From 14475c77a61795ab706ddca41be955ea8b8c9eeb Mon Sep 17 00:00:00 2001 From: Ryan Dahl Date: Wed, 2 Mar 2011 21:04:37 -0800 Subject: [PATCH] Upgrade V8 to 3.1.8 --- deps/v8/ChangeLog | 15 +- deps/v8/SConstruct | 121 +- deps/v8/src/SConscript | 3 +- deps/v8/src/api.cc | 6 +- deps/v8/src/arm/assembler-arm.h | 1 + deps/v8/src/arm/code-stubs-arm.cc | 706 ++++- deps/v8/src/arm/codegen-arm.cc | 47 +- deps/v8/src/arm/constants-arm.h | 8 + deps/v8/src/arm/full-codegen-arm.cc | 582 ++-- deps/v8/src/arm/ic-arm.cc | 27 +- deps/v8/src/arm/lithium-arm.cc | 3 +- deps/v8/src/arm/lithium-codegen-arm.cc | 85 +- deps/v8/src/arm/lithium-codegen-arm.h | 5 - deps/v8/src/arm/macro-assembler-arm.cc | 129 +- deps/v8/src/arm/macro-assembler-arm.h | 49 +- deps/v8/src/arm/simulator-arm.cc | 30 +- deps/v8/src/arm/stub-cache-arm.cc | 12 +- deps/v8/src/arm/virtual-frame-arm.cc | 12 +- deps/v8/src/arm/virtual-frame-arm.h | 2 +- deps/v8/src/builtins.cc | 20 +- deps/v8/src/builtins.h | 215 +- deps/v8/src/compiler.cc | 7 +- deps/v8/src/d8.cc | 8 +- deps/v8/src/d8.js | 618 ++++ deps/v8/src/debug-debugger.js | 121 + deps/v8/src/debug.cc | 3 +- deps/v8/src/flag-definitions.h | 9 +- deps/v8/src/full-codegen.cc | 20 +- deps/v8/src/full-codegen.h | 48 +- deps/v8/src/handles-inl.h | 10 - deps/v8/src/handles.cc | 18 +- deps/v8/src/handles.h | 106 +- deps/v8/src/heap-profiler.cc | 45 +- deps/v8/src/heap-profiler.h | 4 +- deps/v8/src/heap.cc | 15 +- deps/v8/src/hydrogen.cc | 269 +- deps/v8/src/hydrogen.h | 15 +- deps/v8/src/ia32/code-stubs-ia32.cc | 5 +- deps/v8/src/ia32/codegen-ia32.cc | 29 +- deps/v8/src/ia32/full-codegen-ia32.cc | 360 +-- deps/v8/src/ia32/ic-ia32.cc | 27 +- deps/v8/src/ia32/lithium-codegen-ia32.cc | 18 +- deps/v8/src/ia32/lithium-ia32.cc | 19 +- deps/v8/src/ia32/stub-cache-ia32.cc | 10 +- deps/v8/src/ia32/virtual-frame-ia32.cc | 26 +- deps/v8/src/ia32/virtual-frame-ia32.h | 4 +- deps/v8/src/ic-inl.h | 9 + deps/v8/src/ic.cc | 102 +- deps/v8/src/ic.h | 45 +- deps/v8/src/liveobjectlist-inl.h | 90 + deps/v8/src/liveobjectlist.cc | 2476 ++++++++++++++++- deps/v8/src/liveobjectlist.h | 280 +- deps/v8/src/mark-compact.cc | 7 + deps/v8/src/messages.js | 8 +- deps/v8/src/objects-inl.h | 6 +- deps/v8/src/objects.cc | 41 +- deps/v8/src/objects.h | 12 +- deps/v8/src/parser.cc | 88 +- deps/v8/src/profile-generator-inl.h | 28 - deps/v8/src/profile-generator.cc | 805 +++--- deps/v8/src/profile-generator.h | 184 +- deps/v8/src/runtime-profiler.cc | 71 +- deps/v8/src/runtime-profiler.h | 5 +- deps/v8/src/runtime.cc | 401 ++- deps/v8/src/runtime.h | 27 +- deps/v8/src/stub-cache.cc | 78 +- deps/v8/src/stub-cache.h | 33 +- deps/v8/src/version.cc | 2 +- deps/v8/src/virtual-frame-heavy-inl.h | 8 - deps/v8/src/x64/code-stubs-x64.cc | 214 +- deps/v8/src/x64/codegen-x64.cc | 40 +- deps/v8/src/x64/full-codegen-x64.cc | 187 +- deps/v8/src/x64/ic-x64.cc | 25 +- deps/v8/src/x64/lithium-codegen-x64.cc | 110 +- deps/v8/src/x64/lithium-codegen-x64.h | 11 + deps/v8/src/x64/lithium-x64.cc | 28 +- deps/v8/src/x64/stub-cache-x64.cc | 8 +- deps/v8/src/x64/virtual-frame-x64.cc | 30 +- deps/v8/src/x64/virtual-frame-x64.h | 4 +- deps/v8/test/cctest/test-api.cc | 9 +- deps/v8/test/cctest/test-compiler.cc | 2 +- deps/v8/test/cctest/test-debug.cc | 3 +- deps/v8/test/cctest/test-heap.cc | 63 +- deps/v8/test/cctest/test-mark-compact.cc | 11 +- deps/v8/test/es5conform/es5conform.status | 66 - deps/v8/test/mjsunit/array-join.js | 25 + deps/v8/test/mjsunit/mjsunit.status | 12 - .../override-eval-with-non-function.js | 36 + deps/v8/test/mjsunit/regress/regress-1207.js | 35 + deps/v8/test/mjsunit/regress/regress-1209.js | 34 + deps/v8/test/mjsunit/regress/regress-1210.js | 48 + deps/v8/test/mjsunit/regress/regress-1213.js | 43 + deps/v8/test/mjsunit/regress/regress-1218.js | 29 + deps/v8/test/mjsunit/strict-mode.js | 128 + 94 files changed, 7645 insertions(+), 2254 deletions(-) create mode 100644 deps/v8/test/mjsunit/override-eval-with-non-function.js create mode 100644 deps/v8/test/mjsunit/regress/regress-1207.js create mode 100644 deps/v8/test/mjsunit/regress/regress-1209.js create mode 100644 deps/v8/test/mjsunit/regress/regress-1210.js create mode 100644 deps/v8/test/mjsunit/regress/regress-1213.js create mode 100644 deps/v8/test/mjsunit/regress/regress-1218.js diff --git a/deps/v8/ChangeLog b/deps/v8/ChangeLog index e4b018c833..7936058e26 100644 --- a/deps/v8/ChangeLog +++ b/deps/v8/ChangeLog @@ -1,10 +1,23 @@ +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 + Fixed implementation of indexOf/lastIndexOf for sparse arrays (http://crbug.com/73940). Fixed bug in map space compaction (http://crbug.com/59688). diff --git a/deps/v8/SConstruct b/deps/v8/SConstruct index 436581a9ba..2b0ce2412d 100644 --- a/deps/v8/SConstruct +++ b/deps/v8/SConstruct @@ -27,7 +27,6 @@ import platform import re -import subprocess import sys import os from os.path import join, dirname, abspath @@ -143,9 +142,6 @@ LIBRARY_FLAGS = { # Use visibility=default to disable this. 'CXXFLAGS': ['-fvisibility=hidden'] }, - 'strictaliasing:off': { - 'CCFLAGS': ['-fno-strict-aliasing'] - }, 'mode:debug': { 'CCFLAGS': ['-g', '-O0'], 'CPPDEFINES': ['ENABLE_DISASSEMBLER', 'DEBUG'], @@ -655,16 +651,8 @@ def Abort(message): sys.exit(1) -def GuessOS(env): - return utils.GuessOS() - - -def GuessArch(env): - return utils.GuessArchitecture() - - -def GuessToolchain(env): - tools = env['TOOLS'] +def GuessToolchain(os): + tools = Environment()['TOOLS'] if 'gcc' in tools: return 'gcc' elif 'msvc' in tools: @@ -673,9 +661,7 @@ def GuessToolchain(env): return None -def GuessVisibility(env): - os = env['os'] - toolchain = env['toolchain']; +def GuessVisibility(os, toolchain): if (os == 'win32' or os == 'cygwin') and toolchain == 'gcc': # MinGW / Cygwin can't do it. return 'default' @@ -685,35 +671,27 @@ def GuessVisibility(env): return 'hidden' -def GuessStrictAliasing(env): - # There seems to be a problem with gcc 4.5.x - # see http://code.google.com/p/v8/issues/detail?id=884 - # it can be worked around by disabling strict aliasing - toolchain = env['toolchain']; - if toolchain == 'gcc': - env = Environment(tools=['gcc']) - version = subprocess.Popen([env['CC'], '-dumpversion'], - stdout=subprocess.PIPE).communicate()[0] - if version.find('4.5.') == 0: - return 'off' - return 'default' +OS_GUESS = utils.GuessOS() +TOOLCHAIN_GUESS = GuessToolchain(OS_GUESS) +ARCH_GUESS = utils.GuessArchitecture() +VISIBILITY_GUESS = GuessVisibility(OS_GUESS, TOOLCHAIN_GUESS) SIMPLE_OPTIONS = { 'toolchain': { 'values': ['gcc', 'msvc'], - 'guess': GuessToolchain, - 'help': 'the toolchain to use' + 'default': TOOLCHAIN_GUESS, + 'help': 'the toolchain to use (%s)' % TOOLCHAIN_GUESS }, 'os': { 'values': ['freebsd', 'linux', 'macos', 'win32', 'android', 'openbsd', 'solaris', 'cygwin'], - 'guess': GuessOS, - 'help': 'the os to build for' + 'default': OS_GUESS, + 'help': 'the os to build for (%s)' % OS_GUESS }, 'arch': { 'values':['arm', 'ia32', 'x64', 'mips'], - 'guess': GuessArch, - 'help': 'the architecture to build for' + 'default': ARCH_GUESS, + 'help': 'the architecture to build for (%s)' % ARCH_GUESS }, 'regexp': { 'values': ['native', 'interpreted'], @@ -822,15 +800,8 @@ SIMPLE_OPTIONS = { }, 'visibility': { 'values': ['default', 'hidden'], - 'guess': GuessVisibility, - 'depends': ['os', 'toolchain'], - 'help': 'shared library symbol visibility' - }, - 'strictaliasing': { - 'values': ['default', 'off'], - 'guess': GuessStrictAliasing, - 'depends': ['toolchain'], - 'help': 'assume strict aliasing while optimizing' + 'default': VISIBILITY_GUESS, + 'help': 'shared library symbol visibility (%s)' % VISIBILITY_GUESS }, 'pgo': { 'values': ['off', 'instrument', 'optimize'], @@ -840,26 +811,6 @@ SIMPLE_OPTIONS = { } -def AddOption(result, name, option): - if 'guess' in option: - # Option has a guess function - guess = option.get('guess') - guess_env = Environment(options=result) - # Check if all options that the guess function depends on are set - if 'depends' in option: - for dependency in option.get('depends'): - if not dependency in guess_env: - return False - default = guess(guess_env) - else: - # Option has a fixed default - default = option.get('default') - - help = '%s (%s)' % (option.get('help'), ", ".join(option['values'])) - result.Add(name, help, default) - return True - - def GetOptions(): result = Options() result.Add('mode', 'compilation mode (debug, release)', 'release') @@ -867,28 +818,12 @@ def GetOptions(): result.Add('cache', 'directory to use for scons build cache', '') result.Add('env', 'override environment settings (NAME0:value0,NAME1:value1,...)', '') result.Add('importenv', 'import environment settings (NAME0,NAME1,...)', '') - options = SIMPLE_OPTIONS - while len(options): - postpone = {} - for (name, option) in options.iteritems(): - if not AddOption(result, name, option): - postpone[name] = option - options = postpone + for (name, option) in SIMPLE_OPTIONS.iteritems(): + help = '%s (%s)' % (name, ", ".join(option['values'])) + result.Add(name, help, option.get('default')) return result -def GetTools(opts): - env = Environment(options=opts) - os = env['os'] - toolchain = env['toolchain'] - if os == 'win32' and toolchain == 'gcc': - return ['mingw'] - elif os == 'win32' and toolchain == 'msvc': - return ['msvc', 'mslink', 'mslib', 'msvs'] - else: - return ['default'] - - def GetVersionComponents(): MAJOR_VERSION_PATTERN = re.compile(r"#define\s+MAJOR_VERSION\s+(.*)") MINOR_VERSION_PATTERN = re.compile(r"#define\s+MINOR_VERSION\s+(.*)") @@ -969,7 +904,7 @@ def VerifyOptions(env): print env['simulator'] Abort("Option unalignedaccesses only supported for the ARM architecture.") for (name, option) in SIMPLE_OPTIONS.iteritems(): - if (not name in env): + if (not option.get('default')) and (name not in ARGUMENTS): message = ("A value for option %s must be specified (%s)." % (name, ", ".join(option['values']))) Abort(message) @@ -1097,7 +1032,7 @@ def ParseEnvOverrides(arg, imports): return overrides -def BuildSpecific(env, mode, env_overrides, tools): +def BuildSpecific(env, mode, env_overrides): options = {'mode': mode} for option in SIMPLE_OPTIONS: options[option] = env[option] @@ -1150,7 +1085,7 @@ def BuildSpecific(env, mode, env_overrides, tools): (object_files, shell_files, mksnapshot) = env.SConscript( join('src', 'SConscript'), build_dir=join('obj', target_id), - exports='context tools', + exports='context', duplicate=False ) @@ -1170,21 +1105,21 @@ def BuildSpecific(env, mode, env_overrides, tools): library = env.SharedLibrary(library_name, object_files, PDB=pdb_name) context.library_targets.append(library) - d8_env = Environment(tools=tools) + d8_env = Environment() d8_env.Replace(**context.flags['d8']) context.ApplyEnvOverrides(d8_env) shell = d8_env.Program('d8' + suffix, object_files + shell_files) context.d8_targets.append(shell) for sample in context.samples: - sample_env = Environment(tools=tools) + sample_env = Environment() sample_env.Replace(**context.flags['sample']) sample_env.Prepend(LIBS=[library_name]) context.ApplyEnvOverrides(sample_env) sample_object = sample_env.SConscript( join('samples', 'SConscript'), build_dir=join('obj', 'sample', sample, target_id), - exports='sample context tools', + exports='sample context', duplicate=False ) sample_name = sample + suffix @@ -1197,7 +1132,7 @@ def BuildSpecific(env, mode, env_overrides, tools): cctest_program = cctest_env.SConscript( join('test', 'cctest', 'SConscript'), build_dir=join('obj', 'test', target_id), - exports='context object_files tools', + exports='context object_files', duplicate=False ) context.cctest_targets.append(cctest_program) @@ -1207,9 +1142,7 @@ def BuildSpecific(env, mode, env_overrides, tools): def Build(): opts = GetOptions() - tools = GetTools(opts) - env = Environment(options=opts, tools=tools) - + env = Environment(options=opts) Help(opts.GenerateHelpText(env)) VerifyOptions(env) env_overrides = ParseEnvOverrides(env['env'], env['importenv']) @@ -1223,7 +1156,7 @@ def Build(): d8s = [] modes = SplitList(env['mode']) for mode in modes: - context = BuildSpecific(env.Copy(), mode, env_overrides, tools) + context = BuildSpecific(env.Copy(), mode, env_overrides) libraries += context.library_targets mksnapshots += context.mksnapshot_targets cctests += context.cctest_targets diff --git a/deps/v8/src/SConscript b/deps/v8/src/SConscript index 598e4af56e..34ca91ca65 100755 --- a/deps/v8/src/SConscript +++ b/deps/v8/src/SConscript @@ -31,7 +31,6 @@ root_dir = dirname(File('SConstruct').rfile().abspath) sys.path.append(join(root_dir, 'tools')) import js2c Import('context') -Import('tools') SOURCES = { @@ -306,7 +305,7 @@ def Abort(message): def ConfigureObjectFiles(): - env = Environment(tools=tools) + env = Environment() env.Replace(**context.flags['v8']) context.ApplyEnvOverrides(env) env['BUILDERS']['JS2C'] = Builder(action=js2c.JS2C) diff --git a/deps/v8/src/api.cc b/deps/v8/src/api.cc index d718c8875b..b77e450cff 100644 --- a/deps/v8/src/api.cc +++ b/deps/v8/src/api.cc @@ -2286,7 +2286,8 @@ bool v8::Object::Set(v8::Handle key, v8::Handle value, self, key_obj, value_obj, - static_cast(attribs)); + static_cast(attribs), + i::kNonStrictMode); has_pending_exception = obj.is_null(); EXCEPTION_BAILOUT_CHECK(false); return true; @@ -2711,7 +2712,8 @@ bool v8::Object::SetHiddenValue(v8::Handle key, hidden_props, key_obj, value_obj, - static_cast(None)); + static_cast(None), + i::kNonStrictMode); has_pending_exception = obj.is_null(); EXCEPTION_BAILOUT_CHECK(false); return true; diff --git a/deps/v8/src/arm/assembler-arm.h b/deps/v8/src/arm/assembler-arm.h index 954b9cff33..f5eb5075f6 100644 --- a/deps/v8/src/arm/assembler-arm.h +++ b/deps/v8/src/arm/assembler-arm.h @@ -284,6 +284,7 @@ const SwVfpRegister s29 = { 29 }; const SwVfpRegister s30 = { 30 }; const SwVfpRegister s31 = { 31 }; +const DwVfpRegister no_dreg = { -1 }; const DwVfpRegister d0 = { 0 }; const DwVfpRegister d1 = { 1 }; const DwVfpRegister d2 = { 2 }; diff --git a/deps/v8/src/arm/code-stubs-arm.cc b/deps/v8/src/arm/code-stubs-arm.cc index 7d374eecd7..62eb3e6b8e 100644 --- a/deps/v8/src/arm/code-stubs-arm.cc +++ b/deps/v8/src/arm/code-stubs-arm.cc @@ -398,8 +398,11 @@ class FloatingPointHelper : public AllStatic { Label* not_number); // Loads the number from object into dst as a 32-bit integer if possible. If - // the object is not a 32-bit integer control continues at the label - // not_int32. If VFP is supported double_scratch is used but not scratch2. + // the object cannot be converted to a 32-bit integer control continues at + // the label not_int32. If VFP is supported double_scratch is used + // but not scratch2. + // Floating point value in the 32-bit integer range will be rounded + // to an integer. static void LoadNumberAsInteger(MacroAssembler* masm, Register object, Register dst, @@ -409,6 +412,76 @@ class FloatingPointHelper : public AllStatic { DwVfpRegister double_scratch, Label* not_int32); + // 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, + DwVfpRegister double_dst, + Register dst1, + Register dst2, + Register heap_number_map, + Register scratch1, + Register scratch2, + SwVfpRegister 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 VFP3 is supported. + static void LoadNumberAsInt32(MacroAssembler* masm, + Register object, + Register dst, + Register heap_number_map, + Register scratch1, + Register scratch2, + Register scratch3, + DwVfpRegister double_scratch, + Label* not_int32); + + // Generate non VFP3 code to check if a double can be exactly represented by a + // 32-bit integer. This does not check for 0 or -0, which need + // to be checked for separately. + // Control jumps to not_int32 if the value is not a 32-bit integer, and falls + // through otherwise. + // src1 and src2 will be cloberred. + // + // Expected input: + // - src1: higher (exponent) part of the double value. + // - src2: lower (mantissa) part of the double value. + // Output status: + // - dst: 32 higher bits of the mantissa. (mantissa[51:20]) + // - src2: contains 1. + // - other registers are clobbered. + static void DoubleIs32BitInteger(MacroAssembler* masm, + Register src1, + Register src2, + Register dst, + Register scratch, + Label* not_int32); + + // Generates code to call a C function to do a double operation using core + // registers. (Used when VFP3 is not supported.) + // 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: + // r0: Left value (least significant part of mantissa). + // r1: Left value (sign, exponent, top of mantissa). + // r2: Right value (least significant part of mantissa). + // r3: Right value (sign, exponent, top of mantissa). + static void CallCCodeForDoubleOperation(MacroAssembler* masm, + Token::Value op, + Register heap_number_result, + Register scratch); + private: static void LoadNumber(MacroAssembler* masm, FloatingPointHelper::Destination destination, @@ -560,6 +633,319 @@ void FloatingPointHelper::LoadNumberAsInteger(MacroAssembler* masm, } +void FloatingPointHelper::LoadNumberAsInt32Double(MacroAssembler* masm, + Register object, + Destination destination, + DwVfpRegister double_dst, + Register dst1, + Register dst2, + Register heap_number_map, + Register scratch1, + Register scratch2, + SwVfpRegister 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); + if (CpuFeatures::IsSupported(VFP3)) { + CpuFeatures::Scope scope(VFP3); + __ vmov(single_scratch, scratch1); + __ vcvt_f64_s32(double_dst, single_scratch); + if (destination == kCoreRegisters) { + __ vmov(dst1, dst2, double_dst); + } + } else { + Label fewer_than_20_useful_bits; + // Expected output: + // | dst1 | dst2 | + // | s | exp | mantissa | + + // Check for zero. + __ cmp(scratch1, Operand(0)); + __ mov(dst1, scratch1); + __ mov(dst2, scratch1); + __ b(eq, &done); + + // Preload the sign of the value. + __ and_(dst1, scratch1, Operand(HeapNumber::kSignMask), SetCC); + // Get the absolute value of the object (as an unsigned integer). + __ rsb(scratch1, scratch1, Operand(0), SetCC, mi); + + // Get mantisssa[51:20]. + + // Get the position of the first set bit. + __ CountLeadingZeros(dst2, scratch1, scratch2); + __ rsb(dst2, dst2, Operand(31)); + + // Set the exponent. + __ add(scratch2, dst2, Operand(HeapNumber::kExponentBias)); + __ Bfi(dst1, scratch2, scratch2, + HeapNumber::kExponentShift, HeapNumber::kExponentBits); + + // Clear the first non null bit. + __ mov(scratch2, Operand(1)); + __ bic(scratch1, scratch1, Operand(scratch2, LSL, dst2)); + + __ cmp(dst2, Operand(HeapNumber::kMantissaBitsInTopWord)); + // Get the number of bits to set in the lower part of the mantissa. + __ sub(scratch2, dst2, Operand(HeapNumber::kMantissaBitsInTopWord), SetCC); + __ b(mi, &fewer_than_20_useful_bits); + // Set the higher 20 bits of the mantissa. + __ orr(dst1, dst1, Operand(scratch1, LSR, scratch2)); + __ rsb(scratch2, scratch2, Operand(32)); + __ mov(dst2, Operand(scratch1, LSL, scratch2)); + __ b(&done); + + __ bind(&fewer_than_20_useful_bits); + __ rsb(scratch2, dst2, Operand(HeapNumber::kMantissaBitsInTopWord)); + __ mov(scratch2, Operand(scratch1, LSL, scratch2)); + __ orr(dst1, dst1, scratch2); + // Set dst2 to 0. + __ mov(dst2, Operand(0)); + } + + __ b(&done); + + __ bind(&obj_is_not_smi); + if (FLAG_debug_code) { + __ AbortIfNotRootValue(heap_number_map, + Heap::kHeapNumberMapRootIndex, + "HeapNumberMap register clobbered."); + } + __ JumpIfNotHeapNumber(object, heap_number_map, scratch1, not_int32); + + // Load the number. + if (CpuFeatures::IsSupported(VFP3)) { + CpuFeatures::Scope scope(VFP3); + // Load the double value. + __ sub(scratch1, object, Operand(kHeapObjectTag)); + __ vldr(double_dst, scratch1, HeapNumber::kValueOffset); + + __ EmitVFPTruncate(kRoundToZero, + single_scratch, + double_dst, + scratch1, + scratch2, + kCheckForInexactConversion); + + // Jump to not_int32 if the operation did not succeed. + __ b(ne, not_int32); + + if (destination == kCoreRegisters) { + __ vmov(dst1, dst2, double_dst); + } + + } else { + ASSERT(!scratch1.is(object) && !scratch2.is(object)); + // Load the double value in the destination registers.. + __ Ldrd(dst1, dst2, FieldMemOperand(object, HeapNumber::kValueOffset)); + + // Check for 0 and -0. + __ bic(scratch1, dst1, Operand(HeapNumber::kSignMask)); + __ orr(scratch1, scratch1, Operand(dst2)); + __ cmp(scratch1, Operand(0)); + __ b(eq, &done); + + // Check that the value can be exactly represented by a 32-bit integer. + // Jump to not_int32 if that's not the case. + DoubleIs32BitInteger(masm, dst1, dst2, scratch1, scratch2, not_int32); + + // dst1 and dst2 were trashed. Reload the double value. + __ Ldrd(dst1, dst2, FieldMemOperand(object, HeapNumber::kValueOffset)); + } + + __ bind(&done); +} + + +void FloatingPointHelper::LoadNumberAsInt32(MacroAssembler* masm, + Register object, + Register dst, + Register heap_number_map, + Register scratch1, + Register scratch2, + Register scratch3, + DwVfpRegister double_scratch, + 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; + + // Untag the object into the destination register. + __ SmiUntag(dst, object); + // Just return if the object is a smi. + __ JumpIfSmi(object, &done); + + if (FLAG_debug_code) { + __ AbortIfNotRootValue(heap_number_map, + Heap::kHeapNumberMapRootIndex, + "HeapNumberMap register clobbered."); + } + __ JumpIfNotHeapNumber(object, heap_number_map, scratch1, not_int32); + + // Object is a heap number. + // Convert the floating point value to a 32-bit integer. + if (CpuFeatures::IsSupported(VFP3)) { + CpuFeatures::Scope scope(VFP3); + SwVfpRegister single_scratch = double_scratch.low(); + // Load the double value. + __ sub(scratch1, object, Operand(kHeapObjectTag)); + __ vldr(double_scratch, scratch1, HeapNumber::kValueOffset); + + __ EmitVFPTruncate(kRoundToZero, + single_scratch, + double_scratch, + scratch1, + scratch2, + kCheckForInexactConversion); + + // Jump to not_int32 if the operation did not succeed. + __ b(ne, not_int32); + // Get the result in the destination register. + __ vmov(dst, single_scratch); + + } else { + // Load the double value in the destination registers. + __ ldr(scratch1, FieldMemOperand(object, HeapNumber::kExponentOffset)); + __ ldr(scratch2, FieldMemOperand(object, HeapNumber::kMantissaOffset)); + + // Check for 0 and -0. + __ bic(dst, scratch1, Operand(HeapNumber::kSignMask)); + __ orr(dst, scratch2, Operand(dst)); + __ cmp(dst, Operand(0)); + __ b(eq, &done); + + DoubleIs32BitInteger(masm, scratch1, scratch2, dst, scratch3, not_int32); + + // Registers state after DoubleIs32BitInteger. + // dst: mantissa[51:20]. + // scratch2: 1 + + // Shift back the higher bits of the mantissa. + __ mov(dst, Operand(dst, LSR, scratch3)); + // Set the implicit first bit. + __ rsb(scratch3, scratch3, Operand(32)); + __ orr(dst, dst, Operand(scratch2, LSL, scratch3)); + // Set the sign. + __ ldr(scratch1, FieldMemOperand(object, HeapNumber::kExponentOffset)); + __ tst(scratch1, Operand(HeapNumber::kSignMask)); + __ rsb(dst, dst, Operand(0), LeaveCC, mi); + } + + __ bind(&done); +} + + +void FloatingPointHelper::DoubleIs32BitInteger(MacroAssembler* masm, + Register src1, + Register src2, + Register dst, + Register scratch, + Label* not_int32) { + // Get exponent alone in scratch. + __ Ubfx(scratch, + src1, + HeapNumber::kExponentShift, + HeapNumber::kExponentBits); + + // Substract the bias from the exponent. + __ sub(scratch, scratch, Operand(HeapNumber::kExponentBias), SetCC); + + // src1: higher (exponent) part of the double value. + // src2: lower (mantissa) part of the double value. + // scratch: unbiased exponent. + + // Fast cases. Check for obvious non 32-bit integer values. + // Negative exponent cannot yield 32-bit integers. + __ b(mi, not_int32); + // Exponent greater than 31 cannot yield 32-bit integers. + // Also, a positive value with an exponent equal to 31 is outside of the + // signed 32-bit integer range. + __ tst(src1, Operand(HeapNumber::kSignMask)); + __ cmp(scratch, Operand(30), eq); // Executed for positive. If exponent is 30 + // the gt condition will be "correct" and + // the next instruction will be skipped. + __ cmp(scratch, Operand(31), ne); // Executed for negative and positive where + // exponent is not 30. + __ b(gt, not_int32); + // - Bits [21:0] in the mantissa are not null. + __ tst(src2, Operand(0x3fffff)); + __ b(ne, not_int32); + + // Otherwise the exponent needs to be big enough to shift left all the + // non zero bits left. So we need the (30 - exponent) last bits of the + // 31 higher bits of the mantissa to be null. + // Because bits [21:0] are null, we can check instead that the + // (32 - exponent) last bits of the 32 higher bits of the mantisssa are null. + + // Get the 32 higher bits of the mantissa in dst. + __ Ubfx(dst, + src2, + HeapNumber::kMantissaBitsInTopWord, + 32 - HeapNumber::kMantissaBitsInTopWord); + __ orr(dst, + dst, + Operand(src1, LSL, HeapNumber::kNonMantissaBitsInTopWord)); + + // Create the mask and test the lower bits (of the higher bits). + __ rsb(scratch, scratch, Operand(32)); + __ mov(src2, Operand(1)); + __ mov(src1, Operand(src2, LSL, scratch)); + __ sub(src1, src1, Operand(1)); + __ tst(dst, src1); + __ b(ne, not_int32); +} + + +void FloatingPointHelper::CallCCodeForDoubleOperation( + MacroAssembler* masm, + Token::Value op, + Register heap_number_result, + Register scratch) { + // Using core registers: + // r0: Left value (least significant part of mantissa). + // r1: Left value (sign, exponent, top of mantissa). + // r2: Right value (least significant part of mantissa). + // r3: Right value (sign, exponent, top of mantissa). + + // 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(4, scratch); // Two doubles are 4 arguments. + // Call C routine that may not cause GC or other trouble. + __ CallCFunction(ExternalReference::double_fp_operation(op), 4); + // Store answer in the overwritable heap number. +#if !defined(USE_ARM_EABI) + // Double returned in fp coprocessor register 0 and 1, encoded as + // register cr8. Offsets must be divisible by 4 for coprocessor so we + // need to substract the tag from heap_number_result. + __ sub(scratch, heap_number_result, Operand(kHeapObjectTag)); + __ stc(p1, cr8, MemOperand(scratch, HeapNumber::kValueOffset)); +#else + // Double returned in registers 0 and 1. + __ Strd(r0, r1, FieldMemOperand(heap_number_result, + HeapNumber::kValueOffset)); +#endif + // Place heap_number_result in r0 and return to the pushed return address. + __ mov(r0, Operand(heap_number_result)); + __ pop(pc); +} + // See comment for class. void WriteInt32ToHeapNumberStub::Generate(MacroAssembler* masm) { @@ -2707,33 +3093,11 @@ void TypeRecordingBinaryOpStub::GenerateFPOperation(MacroAssembler* masm, __ add(r0, r0, Operand(kHeapObjectTag)); __ Ret(); } else { - // Using core registers: - // r0: Left value (least significant part of mantissa). - // r1: Left value (sign, exponent, top of mantissa). - // r2: Right value (least significant part of mantissa). - // r3: Right value (sign, exponent, top of mantissa). - - // Push the current return address before the C call. Return will be - // through pop(pc) below. - __ push(lr); - __ PrepareCallCFunction(4, scratch1); // Two doubles are 4 arguments. - // Call C routine that may not cause GC or other trouble. r5 is callee - // save. - __ CallCFunction(ExternalReference::double_fp_operation(op_), 4); - // Store answer in the overwritable heap number. -#if !defined(USE_ARM_EABI) - // Double returned in fp coprocessor register 0 and 1, encoded as - // register cr8. Offsets must be divisible by 4 for coprocessor so we - // need to substract the tag from r5. - __ sub(scratch1, result, Operand(kHeapObjectTag)); - __ stc(p1, cr8, MemOperand(scratch1, HeapNumber::kValueOffset)); -#else - // Double returned in registers 0 and 1. - __ Strd(r0, r1, FieldMemOperand(result, HeapNumber::kValueOffset)); -#endif - // Plase result in r0 and return to the pushed return address. - __ mov(r0, Operand(result)); - __ pop(pc); + // Call the C function to handle the double operation. + FloatingPointHelper::CallCCodeForDoubleOperation(masm, + op_, + result, + scratch1); } break; } @@ -2779,7 +3143,6 @@ void TypeRecordingBinaryOpStub::GenerateFPOperation(MacroAssembler* masm, break; case Token::SAR: // Use only the 5 least significant bits of the shift count. - __ and_(r2, r2, Operand(0x1f)); __ GetLeastBitsFromInt32(r2, r2, 5); __ mov(r2, Operand(r3, ASR, r2)); break; @@ -2924,7 +3287,288 @@ void TypeRecordingBinaryOpStub::GenerateStringStub(MacroAssembler* masm) { void TypeRecordingBinaryOpStub::GenerateInt32Stub(MacroAssembler* masm) { ASSERT(operands_type_ == TRBinaryOpIC::INT32); - GenerateTypeTransition(masm); + Register left = r1; + Register right = r0; + Register scratch1 = r7; + Register scratch2 = r9; + DwVfpRegister double_scratch = d0; + SwVfpRegister single_scratch = s3; + + 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); + GenerateSmiSmiOperation(masm); + // 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: { + // 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. + FloatingPointHelper::Destination destination = + CpuFeatures::IsSupported(VFP3) && op_ != Token::MOD ? + FloatingPointHelper::kVFPRegisters : + FloatingPointHelper::kCoreRegisters; + + FloatingPointHelper::LoadNumberAsInt32Double(masm, + right, + destination, + d7, + r2, + r3, + heap_number_map, + scratch1, + scratch2, + s0, + &transition); + FloatingPointHelper::LoadNumberAsInt32Double(masm, + left, + destination, + d6, + r4, + r5, + heap_number_map, + scratch1, + scratch2, + s0, + &transition); + + if (destination == FloatingPointHelper::kVFPRegisters) { + CpuFeatures::Scope scope(VFP3); + Label return_heap_number; + switch (op_) { + case Token::ADD: + __ vadd(d5, d6, d7); + break; + case Token::SUB: + __ vsub(d5, d6, d7); + break; + case Token::MUL: + __ vmul(d5, d6, d7); + break; + case Token::DIV: + __ vdiv(d5, d6, d7); + break; + default: + UNREACHABLE(); + } + + if (op_ != Token::DIV) { + // These operations produce an integer result. + // Try to return a smi if we can. + // Otherwise return a heap number if allowed, or jump to type + // transition. + + __ EmitVFPTruncate(kRoundToZero, + single_scratch, + d5, + scratch1, + scratch2); + + if (result_type_ <= TRBinaryOpIC::INT32) { + // If the ne condition is set, result does + // not fit in a 32-bit integer. + __ b(ne, &transition); + } + + // Check if the result fits in a smi. + __ vmov(scratch1, single_scratch); + __ add(scratch2, scratch1, Operand(0x40000000), SetCC); + // If not try to return a heap number. + __ b(mi, &return_heap_number); + // Tag the result and return. + __ SmiTag(r0, scratch1); + __ Ret(); + } + + if (result_type_ >= (op_ == Token::DIV) ? TRBinaryOpIC::HEAP_NUMBER + : TRBinaryOpIC::INT32) { + __ bind(&return_heap_number); + // We are using vfp registers so r5 is available. + heap_number_result = r5; + GenerateHeapResultAllocation(masm, + heap_number_result, + heap_number_map, + scratch1, + scratch2, + &call_runtime); + __ 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 { + // We preserved r0 and r1 to be able to call runtime. + // Save the left value on the stack. + __ Push(r5, r4); + + // Allocate a heap number to store the result. + heap_number_result = r5; + GenerateHeapResultAllocation(masm, + heap_number_result, + heap_number_map, + scratch1, + scratch2, + &call_runtime); + + // Load the left value from the value saved on the stack. + __ Pop(r1, r0); + + // Call the C function to handle the double operation. + FloatingPointHelper::CallCCodeForDoubleOperation( + masm, op_, heap_number_result, scratch1); + } + + 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 = r5; + // 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. + FloatingPointHelper::LoadNumberAsInt32(masm, + left, + r3, + heap_number_map, + scratch1, + scratch2, + scratch3, + d0, + &transition); + FloatingPointHelper::LoadNumberAsInt32(masm, + right, + r2, + heap_number_map, + scratch1, + scratch2, + scratch3, + d0, + &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. + // The non vfp3 code does not support this special case, so jump to + // runtime if we don't support it. + if (CpuFeatures::IsSupported(VFP3)) { + __ b(mi, + (result_type_ <= TRBinaryOpIC::INT32) ? &transition + : &return_heap_number); + } else { + __ b(mi, (result_type_ <= TRBinaryOpIC::INT32) ? &transition + : &call_runtime); + } + 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. + __ add(scratch1, r2, Operand(0x40000000), SetCC); + // If not try to return a heap number. (We know the result is an int32.) + __ b(mi, &return_heap_number); + // Tag the result and return. + __ SmiTag(r0, r2); + __ Ret(); + + __ bind(&return_heap_number); + if (CpuFeatures::IsSupported(VFP3)) { + CpuFeatures::Scope scope(VFP3); + heap_number_result = r5; + GenerateHeapResultAllocation(masm, + heap_number_result, + heap_number_map, + scratch1, + scratch2, + &call_runtime); + + 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(); + } else { + // Tail call that writes the int32 in r2 to the heap number in r0, using + // r3 as scratch. r0 is preserved and returned. + WriteInt32ToHeapNumberStub stub(r2, r0, r3); + __ TailCallStub(&stub); + } + + break; + } + + default: + UNREACHABLE(); + } + + if (transition.is_linked()) { + __ bind(&transition); + GenerateTypeTransition(masm); + } + + __ bind(&call_runtime); + GenerateCallRuntime(masm); } diff --git a/deps/v8/src/arm/codegen-arm.cc b/deps/v8/src/arm/codegen-arm.cc index 3e125a33fc..8bb576ded7 100644 --- a/deps/v8/src/arm/codegen-arm.cc +++ b/deps/v8/src/arm/codegen-arm.cc @@ -1938,8 +1938,9 @@ void CodeGenerator::DeclareGlobals(Handle pairs) { frame_->EmitPush(cp); frame_->EmitPush(Operand(pairs)); frame_->EmitPush(Operand(Smi::FromInt(is_eval() ? 1 : 0))); + frame_->EmitPush(Operand(Smi::FromInt(strict_mode_flag()))); - frame_->CallRuntime(Runtime::kDeclareGlobals, 3); + frame_->CallRuntime(Runtime::kDeclareGlobals, 4); // The result is discarded. } @@ -3287,7 +3288,8 @@ void CodeGenerator::StoreToSlot(Slot* slot, InitState init_state) { // context slot followed by initialization. frame_->CallRuntime(Runtime::kInitializeConstContextSlot, 3); } else { - frame_->CallRuntime(Runtime::kStoreContextSlot, 3); + frame_->EmitPush(Operand(Smi::FromInt(strict_mode_flag()))); + frame_->CallRuntime(Runtime::kStoreContextSlot, 4); } // Storing a variable must keep the (new) value on the expression // stack. This is necessary for compiling assignment expressions. @@ -3637,7 +3639,8 @@ void CodeGenerator::VisitObjectLiteral(ObjectLiteral* node) { Load(key); Load(value); if (property->emit_store()) { - frame_->CallRuntime(Runtime::kSetProperty, 3); + frame_->EmitPush(Operand(Smi::FromInt(NONE))); // PropertyAttributes + frame_->CallRuntime(Runtime::kSetProperty, 4); } else { frame_->Drop(3); } @@ -5170,11 +5173,11 @@ class DeferredIsStringWrapperSafeForDefaultValueOf : public DeferredCode { // Set the bit in the map to indicate that it has been checked safe for // default valueOf and set true result. - __ ldr(scratch1_, FieldMemOperand(map_result_, Map::kBitField2Offset)); + __ ldrb(scratch1_, FieldMemOperand(map_result_, Map::kBitField2Offset)); __ orr(scratch1_, scratch1_, Operand(1 << Map::kStringWrapperSafeForDefaultValueOf)); - __ str(scratch1_, FieldMemOperand(map_result_, Map::kBitField2Offset)); + __ strb(scratch1_, FieldMemOperand(map_result_, Map::kBitField2Offset)); __ mov(map_result_, Operand(1)); __ jmp(exit_label()); __ bind(&false_result); @@ -6674,8 +6677,12 @@ class DeferredReferenceSetKeyedValue: public DeferredCode { public: DeferredReferenceSetKeyedValue(Register value, Register key, - Register receiver) - : value_(value), key_(key), receiver_(receiver) { + Register receiver, + StrictModeFlag strict_mode) + : value_(value), + key_(key), + receiver_(receiver), + strict_mode_(strict_mode) { set_comment("[ DeferredReferenceSetKeyedValue"); } @@ -6685,6 +6692,7 @@ class DeferredReferenceSetKeyedValue: public DeferredCode { Register value_; Register key_; Register receiver_; + StrictModeFlag strict_mode_; }; @@ -6706,7 +6714,9 @@ void DeferredReferenceSetKeyedValue::Generate() { { Assembler::BlockConstPoolScope block_const_pool(masm_); // Call keyed store IC. It has the arguments value, key and receiver in r0, // r1 and r2. - Handle ic(Builtins::builtin(Builtins::KeyedStoreIC_Initialize)); + Handle ic(Builtins::builtin( + (strict_mode_ == kStrictMode) ? Builtins::KeyedStoreIC_Initialize_Strict + : Builtins::KeyedStoreIC_Initialize)); __ Call(ic, RelocInfo::CODE_TARGET); // The call must be followed by a nop instruction to indicate that the // keyed store has been inlined. @@ -6724,8 +6734,12 @@ class DeferredReferenceSetNamedValue: public DeferredCode { public: DeferredReferenceSetNamedValue(Register value, Register receiver, - Handle name) - : value_(value), receiver_(receiver), name_(name) { + Handle name, + StrictModeFlag strict_mode) + : value_(value), + receiver_(receiver), + name_(name), + strict_mode_(strict_mode) { set_comment("[ DeferredReferenceSetNamedValue"); } @@ -6735,6 +6749,7 @@ class DeferredReferenceSetNamedValue: public DeferredCode { Register value_; Register receiver_; Handle name_; + StrictModeFlag strict_mode_; }; @@ -6754,7 +6769,9 @@ void DeferredReferenceSetNamedValue::Generate() { { Assembler::BlockConstPoolScope block_const_pool(masm_); // Call keyed store IC. It has the arguments value, key and receiver in r0, // r1 and r2. - Handle ic(Builtins::builtin(Builtins::StoreIC_Initialize)); + Handle ic(Builtins::builtin( + (strict_mode_ == kStrictMode) ? Builtins::StoreIC_Initialize_Strict + : Builtins::StoreIC_Initialize)); __ Call(ic, RelocInfo::CODE_TARGET); // The call must be followed by a nop instruction to indicate that the // named store has been inlined. @@ -6943,7 +6960,8 @@ void CodeGenerator::EmitNamedStore(Handle name, bool is_contextual) { Register receiver = r1; DeferredReferenceSetNamedValue* deferred = - new DeferredReferenceSetNamedValue(value, receiver, name); + new DeferredReferenceSetNamedValue( + value, receiver, name, strict_mode_flag()); // Check that the receiver is a heap object. __ tst(receiver, Operand(kSmiTagMask)); @@ -7129,7 +7147,8 @@ void CodeGenerator::EmitKeyedStore(StaticType* key_type, // The deferred code expects value, key and receiver in registers. DeferredReferenceSetKeyedValue* deferred = - new DeferredReferenceSetKeyedValue(value, key, receiver); + new DeferredReferenceSetKeyedValue( + value, key, receiver, strict_mode_flag()); // Check that the value is a smi. As this inlined code does not set the // write barrier it is only possible to store smi values. @@ -7214,7 +7233,7 @@ void CodeGenerator::EmitKeyedStore(StaticType* key_type, deferred->BindExit(); } else { - frame()->CallKeyedStoreIC(); + frame()->CallKeyedStoreIC(strict_mode_flag()); } } diff --git a/deps/v8/src/arm/constants-arm.h b/deps/v8/src/arm/constants-arm.h index 7ac38ed3ea..e6033a8977 100644 --- a/deps/v8/src/arm/constants-arm.h +++ b/deps/v8/src/arm/constants-arm.h @@ -385,7 +385,10 @@ enum VFPConversionMode { 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. static const uint32_t kVFPExceptionMask = 0xf; +static const uint32_t kVFPInexactExceptionBit = 1 << 4; static const uint32_t kVFPFlushToZeroMask = 1 << 24; static const uint32_t kVFPInvalidExceptionBit = 1; @@ -411,6 +414,11 @@ enum VFPRoundingMode { static const uint32_t kVFPRoundingModeMask = 3 << 22; +enum CheckForInexactConversion { + kCheckForInexactConversion, + kDontCheckForInexactConversion +}; + // ----------------------------------------------------------------------------- // Hints. diff --git a/deps/v8/src/arm/full-codegen-arm.cc b/deps/v8/src/arm/full-codegen-arm.cc index 9f521fb31e..7a47644781 100644 --- a/deps/v8/src/arm/full-codegen-arm.cc +++ b/deps/v8/src/arm/full-codegen-arm.cc @@ -339,23 +339,6 @@ void FullCodeGenerator::EmitReturnSequence() { } -FullCodeGenerator::ConstantOperand FullCodeGenerator::GetConstantOperand( - Token::Value op, Expression* left, Expression* right) { - ASSERT(ShouldInlineSmiCase(op)); - if (op == Token::DIV || op == Token::MOD || op == Token::MUL) { - // We never generate inlined constant smi operations for these. - return kNoConstants; - } else if (right->IsSmiLiteral()) { - return kRightConstant; - } else if (left->IsSmiLiteral() && !Token::IsShiftOp(op)) { - // Don't inline shifts with constant left hand side. - return kLeftConstant; - } else { - return kNoConstants; - } -} - - void FullCodeGenerator::EffectContext::Plug(Slot* slot) const { } @@ -793,7 +776,9 @@ void FullCodeGenerator::EmitDeclaration(Variable* variable, prop->key()->AsLiteral()->handle()->IsSmi()); __ mov(r1, Operand(prop->key()->AsLiteral()->handle())); - Handle ic(Builtins::builtin(Builtins::KeyedStoreIC_Initialize)); + Handle ic(Builtins::builtin(is_strict() + ? Builtins::KeyedStoreIC_Initialize_Strict + : Builtins::KeyedStoreIC_Initialize)); EmitCallIC(ic, RelocInfo::CODE_TARGET); // Value in r0 is ignored (declarations are statements). } @@ -809,10 +794,11 @@ void FullCodeGenerator::VisitDeclaration(Declaration* decl) { void FullCodeGenerator::DeclareGlobals(Handle pairs) { // Call the runtime to declare the globals. // The context is the first argument. - __ mov(r1, Operand(pairs)); - __ mov(r0, Operand(Smi::FromInt(is_eval() ? 1 : 0))); - __ Push(cp, r1, r0); - __ CallRuntime(Runtime::kDeclareGlobals, 3); + __ mov(r2, Operand(pairs)); + __ mov(r1, Operand(Smi::FromInt(is_eval() ? 1 : 0))); + __ mov(r0, Operand(Smi::FromInt(strict_mode_flag()))); + __ Push(cp, r2, r1, r0); + __ CallRuntime(Runtime::kDeclareGlobals, 4); // Return value is ignored. } @@ -1456,7 +1442,9 @@ void FullCodeGenerator::VisitObjectLiteral(ObjectLiteral* expr) { VisitForStackValue(key); VisitForStackValue(value); if (property->emit_store()) { - __ CallRuntime(Runtime::kSetProperty, 3); + __ mov(r0, Operand(Smi::FromInt(NONE))); // PropertyAttributes + __ push(r0); + __ CallRuntime(Runtime::kSetProperty, 4); } else { __ Drop(3); } @@ -1634,14 +1622,8 @@ void FullCodeGenerator::VisitAssignment(Assignment* expr) { } Token::Value op = expr->binary_op(); - ConstantOperand constant = ShouldInlineSmiCase(op) - ? GetConstantOperand(op, expr->target(), expr->value()) - : kNoConstants; - ASSERT(constant == kRightConstant || constant == kNoConstants); - if (constant == kNoConstants) { - __ push(r0); // Left operand goes on the stack. - VisitForAccumulatorValue(expr->value()); - } + __ push(r0); // Left operand goes on the stack. + VisitForAccumulatorValue(expr->value()); OverwriteMode mode = expr->value()->ResultOverwriteAllowed() ? OVERWRITE_RIGHT @@ -1653,8 +1635,7 @@ void FullCodeGenerator::VisitAssignment(Assignment* expr) { op, mode, expr->target(), - expr->value(), - constant); + expr->value()); } else { EmitBinaryOp(op, mode); } @@ -1704,217 +1685,11 @@ void FullCodeGenerator::EmitKeyedPropertyLoad(Property* prop) { } -void FullCodeGenerator::EmitConstantSmiAdd(Expression* expr, - OverwriteMode mode, - bool left_is_constant_smi, - Smi* value) { - Label call_stub, done; - // Optimistically add smi value with unknown object. If result overflows or is - // not a smi then we had either a smi overflow or added a smi with a tagged - // pointer. - __ mov(r1, Operand(value)); - __ add(r2, r0, r1, SetCC); - __ b(vs, &call_stub); - JumpPatchSite patch_site(masm_); - patch_site.EmitJumpIfNotSmi(r2, &call_stub); - __ mov(r0, r2); - __ b(&done); - - // Call the shared stub. - __ bind(&call_stub); - if (!left_is_constant_smi) { - __ Swap(r0, r1, r2); - } - TypeRecordingBinaryOpStub stub(Token::ADD, mode); - EmitCallIC(stub.GetCode(), &patch_site); - - __ bind(&done); - context()->Plug(r0); -} - - -void FullCodeGenerator::EmitConstantSmiSub(Expression* expr, - OverwriteMode mode, - bool left_is_constant_smi, - Smi* value) { - Label call_stub, done; - // Optimistically subtract smi value and unknown object. If result overflows - // or is not a smi then we had either a smi overflow or subtraction between a - // smi and a tagged pointer. - __ mov(r1, Operand(value)); - if (left_is_constant_smi) { - __ sub(r2, r1, r0, SetCC); - } else { - __ sub(r2, r0, r1, SetCC); - } - __ b(vs, &call_stub); - JumpPatchSite patch_site(masm_); - patch_site.EmitJumpIfNotSmi(r2, &call_stub); - __ mov(r0, r2); - __ b(&done); - - // Call the shared stub. - __ bind(&call_stub); - if (!left_is_constant_smi) { - __ Swap(r0, r1, r2); - } - TypeRecordingBinaryOpStub stub(Token::SUB, mode); - EmitCallIC(stub.GetCode(), &patch_site); - - __ bind(&done); - context()->Plug(r0); -} - - -void FullCodeGenerator::EmitConstantSmiShiftOp(Expression* expr, - Token::Value op, - OverwriteMode mode, - Smi* value) { - Label call_stub, smi_case, done; - int shift_value = value->value() & 0x1f; - - JumpPatchSite patch_site(masm_); - patch_site.EmitJumpIfSmi(r0, &smi_case); - - // Call stub. - __ bind(&call_stub); - __ mov(r1, r0); - __ mov(r0, Operand(value)); - TypeRecordingBinaryOpStub stub(op, mode); - EmitCallIC(stub.GetCode(), &patch_site); - __ b(&done); - - // Smi case. - __ bind(&smi_case); - switch (op) { - case Token::SHL: - if (shift_value != 0) { - __ mov(r1, r0); - if (shift_value > 1) { - __ mov(r1, Operand(r1, LSL, shift_value - 1)); - } - // Convert int result to smi, checking that it is in int range. - __ SmiTag(r1, SetCC); - __ b(vs, &call_stub); - __ mov(r0, r1); // Put result back into r0. - } - break; - case Token::SAR: - if (shift_value != 0) { - __ mov(r0, Operand(r0, ASR, shift_value)); - __ bic(r0, r0, Operand(kSmiTagMask)); - } - break; - case Token::SHR: - // SHR must return a positive value. When shifting by 0 or 1 we need to - // check that smi tagging the result will not create a negative value. - if (shift_value < 2) { - __ mov(r2, Operand(shift_value)); - __ SmiUntag(r1, r0); - if (shift_value != 0) { - __ mov(r1, Operand(r1, LSR, shift_value)); - } - __ tst(r1, Operand(0xc0000000)); - __ b(ne, &call_stub); - __ SmiTag(r0, r1); // result in r0. - } else { - __ SmiUntag(r0); - __ mov(r0, Operand(r0, LSR, shift_value)); - __ SmiTag(r0); - } - break; - default: - UNREACHABLE(); - } - - __ bind(&done); - context()->Plug(r0); -} - - -void FullCodeGenerator::EmitConstantSmiBitOp(Expression* expr, - Token::Value op, - OverwriteMode mode, - Smi* value) { - Label smi_case, done; - - JumpPatchSite patch_site(masm_); - patch_site.EmitJumpIfSmi(r0, &smi_case); - - // The order of the arguments does not matter for bit-ops with a - // constant operand. - __ mov(r1, Operand(value)); - TypeRecordingBinaryOpStub stub(op, mode); - EmitCallIC(stub.GetCode(), &patch_site); - __ jmp(&done); - - // Smi case. - __ bind(&smi_case); - __ mov(r1, Operand(value)); - switch (op) { - case Token::BIT_OR: - __ orr(r0, r0, Operand(r1)); - break; - case Token::BIT_XOR: - __ eor(r0, r0, Operand(r1)); - break; - case Token::BIT_AND: - __ and_(r0, r0, Operand(r1)); - break; - default: - UNREACHABLE(); - } - - __ bind(&done); - context()->Plug(r0); -} - - -void FullCodeGenerator::EmitConstantSmiBinaryOp(Expression* expr, - Token::Value op, - OverwriteMode mode, - bool left_is_constant_smi, - Smi* value) { - switch (op) { - case Token::BIT_OR: - case Token::BIT_XOR: - case Token::BIT_AND: - EmitConstantSmiBitOp(expr, op, mode, value); - break; - case Token::SHL: - case Token::SAR: - case Token::SHR: - ASSERT(!left_is_constant_smi); - EmitConstantSmiShiftOp(expr, op, mode, value); - break; - case Token::ADD: - EmitConstantSmiAdd(expr, mode, left_is_constant_smi, value); - break; - case Token::SUB: - EmitConstantSmiSub(expr, mode, left_is_constant_smi, value); - break; - default: - UNREACHABLE(); - } -} - - void FullCodeGenerator::EmitInlineSmiBinaryOp(Expression* expr, Token::Value op, OverwriteMode mode, Expression* left_expr, - Expression* right_expr, - ConstantOperand constant) { - if (constant == kRightConstant) { - Smi* value = Smi::cast(*right_expr->AsLiteral()->handle()); - EmitConstantSmiBinaryOp(expr, op, mode, false, value); - return; - } else if (constant == kLeftConstant) { - Smi* value = Smi::cast(*left_expr->AsLiteral()->handle()); - EmitConstantSmiBinaryOp(expr, op, mode, true, value); - return; - } - + Expression* right_expr) { Label done, smi_case, stub_call; Register scratch1 = r2; @@ -2050,7 +1825,9 @@ void FullCodeGenerator::EmitAssignment(Expression* expr, int bailout_ast_id) { __ mov(r1, r0); __ pop(r0); // Restore value. __ mov(r2, Operand(prop->key()->AsLiteral()->handle())); - Handle ic(Builtins::builtin(Builtins::StoreIC_Initialize)); + Handle ic(Builtins::builtin( + is_strict() ? Builtins::StoreIC_Initialize_Strict + : Builtins::StoreIC_Initialize)); EmitCallIC(ic, RelocInfo::CODE_TARGET); break; } @@ -2071,7 +1848,9 @@ void FullCodeGenerator::EmitAssignment(Expression* expr, int bailout_ast_id) { __ pop(r2); } __ pop(r0); // Restore value. - Handle ic(Builtins::builtin(Builtins::KeyedStoreIC_Initialize)); + Handle ic(Builtins::builtin( + is_strict() ? Builtins::KeyedStoreIC_Initialize_Strict + : Builtins::KeyedStoreIC_Initialize)); EmitCallIC(ic, RelocInfo::CODE_TARGET); break; } @@ -2095,9 +1874,9 @@ void FullCodeGenerator::EmitVariableAssignment(Variable* var, // r2, and the global object in r1. __ mov(r2, Operand(var->name())); __ ldr(r1, GlobalObjectOperand()); - Handle ic(Builtins::builtin(is_strict() - ? Builtins::StoreIC_Initialize_Strict - : Builtins::StoreIC_Initialize)); + Handle ic(Builtins::builtin( + is_strict() ? Builtins::StoreIC_Initialize_Strict + : Builtins::StoreIC_Initialize)); EmitCallIC(ic, RelocInfo::CODE_TARGET_CONTEXT); } else if (op == Token::INIT_CONST) { @@ -2166,9 +1945,10 @@ void FullCodeGenerator::EmitVariableAssignment(Variable* var, case Slot::LOOKUP: // Call the runtime for the assignment. __ push(r0); // Value. - __ mov(r0, Operand(slot->var()->name())); - __ Push(cp, r0); // Context and name. - __ CallRuntime(Runtime::kStoreContextSlot, 3); + __ mov(r1, Operand(slot->var()->name())); + __ mov(r0, Operand(Smi::FromInt(strict_mode_flag()))); + __ Push(cp, r1, r0); // Context, name, strict mode. + __ CallRuntime(Runtime::kStoreContextSlot, 4); break; } } @@ -2203,7 +1983,9 @@ void FullCodeGenerator::EmitNamedPropertyAssignment(Assignment* expr) { __ pop(r1); } - Handle ic(Builtins::builtin(Builtins::StoreIC_Initialize)); + Handle ic(Builtins::builtin( + is_strict() ? Builtins::StoreIC_Initialize_Strict + : Builtins::StoreIC_Initialize)); EmitCallIC(ic, RelocInfo::CODE_TARGET); // If the assignment ends an initialization block, revert to fast case. @@ -2247,7 +2029,9 @@ void FullCodeGenerator::EmitKeyedPropertyAssignment(Assignment* expr) { __ pop(r2); } - Handle ic(Builtins::builtin(Builtins::KeyedStoreIC_Initialize)); + Handle ic(Builtins::builtin( + is_strict() ? Builtins::KeyedStoreIC_Initialize_Strict + : Builtins::KeyedStoreIC_Initialize)); EmitCallIC(ic, RelocInfo::CODE_TARGET); // If the assignment ends an initialization block, revert to fast case. @@ -2362,6 +2146,29 @@ void FullCodeGenerator::EmitCallWithStub(Call* expr) { } +void FullCodeGenerator::EmitResolvePossiblyDirectEval(ResolveEvalFlag flag, + 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 and do runtime call. + __ ldr(r1, MemOperand(fp, (2 + scope()->num_parameters()) * kPointerSize)); + __ push(r1); + // Push the strict mode flag. + __ mov(r1, Operand(Smi::FromInt(strict_mode_flag()))); + __ push(r1); + + __ CallRuntime(flag == SKIP_CONTEXT_LOOKUP + ? Runtime::kResolvePossiblyDirectEvalNoLookup + : Runtime::kResolvePossiblyDirectEval, 4); +} + + void FullCodeGenerator::VisitCall(Call* expr) { #ifdef DEBUG // We want to verify that RecordJSReturnSite gets called on all paths @@ -2391,26 +2198,31 @@ void FullCodeGenerator::VisitCall(Call* expr) { VisitForStackValue(args->at(i)); } - // Push copy of the function - found below the arguments. - __ ldr(r1, MemOperand(sp, (arg_count + 1) * kPointerSize)); - __ push(r1); - - // Push copy of the first argument or undefined if it doesn't exist. - if (arg_count > 0) { - __ ldr(r1, MemOperand(sp, arg_count * kPointerSize)); - __ push(r1); - } else { - __ push(r2); + // If we know that eval can only be shadowed by eval-introduced + // variables we attempt to load the global eval function directly + // in generated code. If we succeed, there is no need to perform a + // context lookup in the runtime system. + Label done; + if (var->AsSlot() != NULL && var->mode() == Variable::DYNAMIC_GLOBAL) { + Label slow; + EmitLoadGlobalSlotCheckExtensions(var->AsSlot(), + NOT_INSIDE_TYPEOF, + &slow); + // Push the function and resolve eval. + __ push(r0); + EmitResolvePossiblyDirectEval(SKIP_CONTEXT_LOOKUP, arg_count); + __ jmp(&done); + __ bind(&slow); } - // Push the receiver of the enclosing function and do runtime call. - __ ldr(r1, - MemOperand(fp, (2 + scope()->num_parameters()) * kPointerSize)); - __ push(r1); - // Push the strict mode flag. - __ mov(r1, Operand(Smi::FromInt(strict_mode_flag()))); + // Push copy of the function (found below the arguments) and + // resolve eval. + __ ldr(r1, MemOperand(sp, (arg_count + 1) * kPointerSize)); __ push(r1); - __ CallRuntime(Runtime::kResolvePossiblyDirectEval, 4); + EmitResolvePossiblyDirectEval(PERFORM_CONTEXT_LOOKUP, arg_count); + if (done.is_linked()) { + __ bind(&done); + } // The runtime call returns a pair of values in r0 (function) and // r1 (receiver). Touch up the stack with the right values. @@ -3430,9 +3242,235 @@ void FullCodeGenerator::EmitGetCachedArrayIndex(ZoneList* args) { void FullCodeGenerator::EmitFastAsciiArrayJoin(ZoneList* args) { + 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; + + 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. + __ ldrb(scratch2, FieldMemOperand(scratch1, Map::kBitField2Offset)); + __ tst(scratch2, Operand(1 << Map::kHasFastElements)); + __ b(eq, &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::kEmptyStringRootIndex); + __ 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(0)); + __ 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 (FLAG_debug_code) { + __ cmp(array_length, Operand(0)); + __ Assert(gt, "No empty arrays here in EmitFastAsciiArrayJoin"); + } + __ 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, SeqAsciiString::kLengthOffset)); + __ add(string_length, string_length, Operand(scratch1)); + __ 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, ¬_size_one_array); + __ ldr(r0, FieldMemOperand(elements, FixedArray::kHeaderSize)); + __ b(&done); + + __ bind(¬_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, SeqAsciiString::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(0)); + __ b(ne, &bailout); + __ tst(scratch2, Operand(0x80000000)); + __ b(ne, &bailout); + __ add(string_length, string_length, Operand(scratch2)); + __ 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(SeqAsciiString::kHeaderSize - kHeapObjectTag)); + + // Check the length of the separator. + __ ldr(scratch1, FieldMemOperand(separator, SeqAsciiString::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(SeqAsciiString::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, SeqAsciiString::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(SeqAsciiString::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(SeqAsciiString::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(SeqAsciiString::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); - return; } @@ -3767,7 +3805,9 @@ void FullCodeGenerator::VisitCountOperation(CountOperation* expr) { case NAMED_PROPERTY: { __ mov(r2, Operand(prop->key()->AsLiteral()->handle())); __ pop(r1); - Handle ic(Builtins::builtin(Builtins::StoreIC_Initialize)); + Handle ic(Builtins::builtin( + is_strict() ? Builtins::StoreIC_Initialize_Strict + : Builtins::StoreIC_Initialize)); EmitCallIC(ic, RelocInfo::CODE_TARGET); PrepareForBailoutForId(expr->AssignmentId(), TOS_REG); if (expr->is_postfix()) { @@ -3782,7 +3822,9 @@ void FullCodeGenerator::VisitCountOperation(CountOperation* expr) { case KEYED_PROPERTY: { __ pop(r1); // Key. __ pop(r2); // Receiver. - Handle ic(Builtins::builtin(Builtins::KeyedStoreIC_Initialize)); + Handle ic(Builtins::builtin( + is_strict() ? Builtins::KeyedStoreIC_Initialize_Strict + : Builtins::KeyedStoreIC_Initialize)); EmitCallIC(ic, RelocInfo::CODE_TARGET); PrepareForBailoutForId(expr->AssignmentId(), TOS_REG); if (expr->is_postfix()) { diff --git a/deps/v8/src/arm/ic-arm.cc b/deps/v8/src/arm/ic-arm.cc index 6c7aa0643a..0fc6818703 100644 --- a/deps/v8/src/arm/ic-arm.cc +++ b/deps/v8/src/arm/ic-arm.cc @@ -1400,7 +1400,8 @@ void KeyedStoreIC::GenerateMiss(MacroAssembler* masm) { } -void KeyedStoreIC::GenerateRuntimeSetProperty(MacroAssembler* masm) { +void KeyedStoreIC::GenerateRuntimeSetProperty(MacroAssembler* masm, + StrictModeFlag strict_mode) { // ---------- S t a t e -------------- // -- r0 : value // -- r1 : key @@ -1411,11 +1412,16 @@ void KeyedStoreIC::GenerateRuntimeSetProperty(MacroAssembler* masm) { // Push receiver, key and value for runtime call. __ Push(r2, r1, r0); - __ TailCallRuntime(Runtime::kSetProperty, 3, 1); + __ mov(r1, Operand(Smi::FromInt(NONE))); // PropertyAttributes + __ mov(r0, Operand(Smi::FromInt(strict_mode))); // Strict mode. + __ Push(r1, r0); + + __ TailCallRuntime(Runtime::kSetProperty, 5, 1); } -void KeyedStoreIC::GenerateGeneric(MacroAssembler* masm) { +void KeyedStoreIC::GenerateGeneric(MacroAssembler* masm, + StrictModeFlag strict_mode) { // ---------- S t a t e -------------- // -- r0 : value // -- r1 : key @@ -1470,7 +1476,7 @@ void KeyedStoreIC::GenerateGeneric(MacroAssembler* masm) { // r0: value. // r1: key. // r2: receiver. - GenerateRuntimeSetProperty(masm); + GenerateRuntimeSetProperty(masm, strict_mode); // Check whether the elements is a pixel array. // r4: elements map. @@ -1540,7 +1546,7 @@ void KeyedStoreIC::GenerateGeneric(MacroAssembler* masm) { void StoreIC::GenerateMegamorphic(MacroAssembler* masm, - Code::ExtraICState extra_ic_state) { + StrictModeFlag strict_mode) { // ----------- S t a t e ------------- // -- r0 : value // -- r1 : receiver @@ -1552,7 +1558,7 @@ void StoreIC::GenerateMegamorphic(MacroAssembler* masm, Code::Flags flags = Code::ComputeFlags(Code::STORE_IC, NOT_IN_LOOP, MONOMORPHIC, - extra_ic_state); + strict_mode); StubCache::GenerateProbe(masm, flags, r1, r2, r3, r4, r5); // Cache miss: Jump to runtime. @@ -1646,7 +1652,8 @@ void StoreIC::GenerateNormal(MacroAssembler* masm) { } -void StoreIC::GenerateGlobalProxy(MacroAssembler* masm) { +void StoreIC::GenerateGlobalProxy(MacroAssembler* masm, + StrictModeFlag strict_mode) { // ----------- S t a t e ------------- // -- r0 : value // -- r1 : receiver @@ -1656,8 +1663,12 @@ void StoreIC::GenerateGlobalProxy(MacroAssembler* masm) { __ 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, 3, 1); + __ TailCallRuntime(Runtime::kSetProperty, 5, 1); } diff --git a/deps/v8/src/arm/lithium-arm.cc b/deps/v8/src/arm/lithium-arm.cc index d3c9fee8e3..54ed4bace3 100644 --- a/deps/v8/src/arm/lithium-arm.cc +++ b/deps/v8/src/arm/lithium-arm.cc @@ -1154,8 +1154,7 @@ LInstruction* LChunkBuilder::DoInstanceOfKnownGlobal( HInstanceOfKnownGlobal* instr) { LInstanceOfKnownGlobal* result = new LInstanceOfKnownGlobal(UseFixed(instr->value(), r0), FixedTemp(r4)); - MarkAsSaveDoubles(result); - return AssignEnvironment(AssignPointerMap(DefineFixed(result, r0))); + return MarkAsCall(DefineFixed(result, r0), instr); } diff --git a/deps/v8/src/arm/lithium-codegen-arm.cc b/deps/v8/src/arm/lithium-codegen-arm.cc index d375617adc..c5e9271760 100644 --- a/deps/v8/src/arm/lithium-codegen-arm.cc +++ b/deps/v8/src/arm/lithium-codegen-arm.cc @@ -573,7 +573,8 @@ void LCodeGen::PopulateDeoptimizationData(Handle code) { Handle data = Factory::NewDeoptimizationInputData(length, TENURED); - data->SetTranslationByteArray(*translations_.CreateByteArray()); + Handle translations = translations_.CreateByteArray(); + data->SetTranslationByteArray(*translations); data->SetInlinedFunctionCount(Smi::FromInt(inlined_function_count_)); Handle literals = @@ -1985,11 +1986,7 @@ void LCodeGen::DoDeferredLInstanceOfKnownGlobal(LInstanceOfKnownGlobal* instr, __ BlockConstPoolFor(kAdditionalDelta); __ mov(temp, Operand(delta * kPointerSize)); __ StoreToSafepointRegisterSlot(temp, temp); - __ Call(stub.GetCode(), RelocInfo::CODE_TARGET); - ASSERT_EQ(kAdditionalDelta, - masm_->InstructionsGeneratedSince(&before_push_delta)); - RecordSafepointWithRegisters( - instr->pointer_map(), 0, Safepoint::kNoDeoptimizationIndex); + CallCode(stub.GetCode(), RelocInfo::CODE_TARGET, instr); // Put the result value into the result register slot and // restore all registers. __ StoreToSafepointRegisterSlot(result, result); @@ -2586,41 +2583,6 @@ void LCodeGen::DoMathAbs(LUnaryMathOperation* instr) { } -// Truncates a double using a specific rounding mode. -// Clears the z flag (ne condition) if an overflow occurs. -void LCodeGen::EmitVFPTruncate(VFPRoundingMode rounding_mode, - SwVfpRegister result, - DwVfpRegister double_input, - Register scratch1, - Register scratch2) { - Register prev_fpscr = scratch1; - Register scratch = scratch2; - - // Set custom FPCSR: - // - Set rounding mode. - // - Clear vfp cumulative exception flags. - // - Make sure Flush-to-zero mode control bit is unset. - __ vmrs(prev_fpscr); - __ bic(scratch, prev_fpscr, Operand(kVFPExceptionMask | - kVFPRoundingModeMask | - kVFPFlushToZeroMask)); - __ orr(scratch, scratch, Operand(rounding_mode)); - __ vmsr(scratch); - - // Convert the argument to an integer. - __ vcvt_s32_f64(result, - double_input, - kFPSCRRounding); - - // Retrieve FPSCR. - __ vmrs(scratch); - // Restore FPSCR. - __ vmsr(prev_fpscr); - // Check for vfp exceptions. - __ tst(scratch, Operand(kVFPExceptionMask)); -} - - void LCodeGen::DoMathFloor(LUnaryMathOperation* instr) { DoubleRegister input = ToDoubleRegister(instr->InputAt(0)); Register result = ToRegister(instr->result()); @@ -2628,11 +2590,11 @@ void LCodeGen::DoMathFloor(LUnaryMathOperation* instr) { Register scratch1 = scratch0(); Register scratch2 = ToRegister(instr->TempAt(0)); - EmitVFPTruncate(kRoundToMinusInf, - single_scratch, - input, - scratch1, - scratch2); + __ EmitVFPTruncate(kRoundToMinusInf, + single_scratch, + input, + scratch1, + scratch2); DeoptimizeIf(ne, instr->environment()); // Move the result back to general purpose register r0. @@ -2654,11 +2616,11 @@ void LCodeGen::DoMathRound(LUnaryMathOperation* instr) { Register result = ToRegister(instr->result()); Register scratch1 = scratch0(); Register scratch2 = result; - EmitVFPTruncate(kRoundToNearest, - double_scratch0().low(), - input, - scratch1, - scratch2); + __ EmitVFPTruncate(kRoundToNearest, + double_scratch0().low(), + input, + scratch1, + scratch2); DeoptimizeIf(ne, instr->environment()); __ vmov(result, double_scratch0().low()); @@ -2863,9 +2825,9 @@ void LCodeGen::DoStoreNamedGeneric(LStoreNamedGeneric* instr) { // Name is always in r2. __ mov(r2, Operand(instr->name())); - Handle ic(Builtins::builtin(info_->is_strict() - ? Builtins::StoreIC_Initialize_Strict - : Builtins::StoreIC_Initialize)); + Handle ic(Builtins::builtin( + info_->is_strict() ? Builtins::StoreIC_Initialize_Strict + : Builtins::StoreIC_Initialize)); CallCode(ic, RelocInfo::CODE_TARGET, instr); } @@ -2907,7 +2869,9 @@ void LCodeGen::DoStoreKeyedGeneric(LStoreKeyedGeneric* instr) { ASSERT(ToRegister(instr->key()).is(r1)); ASSERT(ToRegister(instr->value()).is(r0)); - Handle ic(Builtins::builtin(Builtins::KeyedStoreIC_Initialize)); + Handle ic(Builtins::builtin( + info_->is_strict() ? Builtins::KeyedStoreIC_Initialize_Strict + : Builtins::KeyedStoreIC_Initialize)); CallCode(ic, RelocInfo::CODE_TARGET, instr); } @@ -3371,11 +3335,12 @@ void LCodeGen::DoDoubleToI(LDoubleToI* instr) { Register scratch1 = scratch0(); Register scratch2 = ToRegister(instr->TempAt(0)); - EmitVFPTruncate(kRoundToZero, - single_scratch, - double_input, - scratch1, - scratch2); + __ EmitVFPTruncate(kRoundToZero, + single_scratch, + double_input, + scratch1, + scratch2); + // Deoptimize if we had a vfp invalid exception. DeoptimizeIf(ne, instr->environment()); diff --git a/deps/v8/src/arm/lithium-codegen-arm.h b/deps/v8/src/arm/lithium-codegen-arm.h index 2d9c6edcb6..a26f6311e2 100644 --- a/deps/v8/src/arm/lithium-codegen-arm.h +++ b/deps/v8/src/arm/lithium-codegen-arm.h @@ -206,11 +206,6 @@ class LCodeGen BASE_EMBEDDED { // Specific math operations - used from DoUnaryMathOperation. void EmitIntegerMathAbs(LUnaryMathOperation* instr); void DoMathAbs(LUnaryMathOperation* instr); - void EmitVFPTruncate(VFPRoundingMode rounding_mode, - SwVfpRegister result, - DwVfpRegister double_input, - Register scratch1, - Register scratch2); void DoMathFloor(LUnaryMathOperation* instr); void DoMathRound(LUnaryMathOperation* instr); void DoMathSqrt(LUnaryMathOperation* instr); diff --git a/deps/v8/src/arm/macro-assembler-arm.cc b/deps/v8/src/arm/macro-assembler-arm.cc index e0f2916449..9340b61dd8 100644 --- a/deps/v8/src/arm/macro-assembler-arm.cc +++ b/deps/v8/src/arm/macro-assembler-arm.cc @@ -271,6 +271,29 @@ void MacroAssembler::Sbfx(Register dst, Register src1, int lsb, int width, } +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)) { + 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, int lsb, int width, Condition cond) { ASSERT(lsb < 32); if (!CpuFeatures::IsSupported(ARMv7)) { @@ -1818,9 +1841,9 @@ void MacroAssembler::ConvertToInt32(Register source, ldr(scratch, FieldMemOperand(source, HeapNumber::kExponentOffset)); // Get exponent alone in scratch2. Ubfx(scratch2, - scratch, - HeapNumber::kExponentShift, - HeapNumber::kExponentBits); + scratch, + HeapNumber::kExponentShift, + HeapNumber::kExponentBits); // Load dest with zero. We use this either for the final shift or // for the answer. mov(dest, Operand(0, RelocInfo::NONE)); @@ -1883,6 +1906,52 @@ void MacroAssembler::ConvertToInt32(Register source, } +void MacroAssembler::EmitVFPTruncate(VFPRoundingMode rounding_mode, + SwVfpRegister result, + DwVfpRegister double_input, + Register scratch1, + Register scratch2, + CheckForInexactConversion check_inexact) { + ASSERT(CpuFeatures::IsSupported(VFP3)); + CpuFeatures::Scope scope(VFP3); + Register prev_fpscr = scratch1; + Register scratch = scratch2; + + int32_t check_inexact_conversion = + (check_inexact == kCheckForInexactConversion) ? kVFPInexactExceptionBit : 0; + + // Set custom FPCSR: + // - Set rounding mode. + // - Clear vfp cumulative exception flags. + // - Make sure Flush-to-zero mode control bit is unset. + vmrs(prev_fpscr); + bic(scratch, + prev_fpscr, + Operand(kVFPExceptionMask | + check_inexact_conversion | + kVFPRoundingModeMask | + kVFPFlushToZeroMask)); + // 'Round To Nearest' is encoded by 0b00 so no bits need to be set. + if (rounding_mode != kRoundToNearest) { + orr(scratch, scratch, Operand(rounding_mode)); + } + vmsr(scratch); + + // Convert the argument to an integer. + vcvt_s32_f64(result, + double_input, + (rounding_mode == kRoundToZero) ? kDefaultRoundToZero + : kFPSCRRounding); + + // Retrieve FPSCR. + vmrs(scratch); + // Restore FPSCR. + vmsr(prev_fpscr); + // Check for vfp exceptions. + tst(scratch, Operand(kVFPExceptionMask | check_inexact_conversion)); +} + + void MacroAssembler::GetLeastBitsFromSmi(Register dst, Register src, int num_least_bits) { @@ -2389,6 +2458,60 @@ void MacroAssembler::CopyFields(Register dst, } +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(0)); + 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 (FLAG_debug_code) { + tst(src, Operand(kPointerSize - 1)); + Assert(eq, "Expecting alignment for CopyBytes"); + } + cmp(length, Operand(kPointerSize)); + b(lt, &byte_loop); + ldr(scratch, MemOperand(src, kPointerSize, PostIndex)); +#if CAN_USE_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)); +#endif + sub(length, length, Operand(kPointerSize)); + b(&word_loop); + + // Copy the last bytes if any left. + bind(&byte_loop); + cmp(length, Operand(0)); + 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::CountLeadingZeros(Register zeros, // Answer. Register source, // Input. Register scratch) { diff --git a/deps/v8/src/arm/macro-assembler-arm.h b/deps/v8/src/arm/macro-assembler-arm.h index 3e13c783db..acd1d79b7c 100644 --- a/deps/v8/src/arm/macro-assembler-arm.h +++ b/deps/v8/src/arm/macro-assembler-arm.h @@ -121,6 +121,15 @@ class MacroAssembler: public Assembler { 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, int lsb, int width, Condition cond = al); void Usat(Register dst, int satpos, const Operand& src, Condition cond = al); @@ -234,6 +243,17 @@ class MacroAssembler: public Assembler { } } + // 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); + } + } + // Push and pop the registers that can hold pointers, as defined by the // RegList constant kSafepointSavedRegisters. void PushSafepointRegisters(); @@ -497,6 +517,14 @@ class MacroAssembler: public Assembler { // 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); + // --------------------------------------------------------------------------- // Support functions. @@ -613,6 +641,19 @@ class MacroAssembler: public Assembler { DwVfpRegister double_scratch, Label *not_int32); +// Truncates a double using a specific rounding mode. +// Clears the z flag (ne condition) if an overflow occurs. +// If exact_conversion is true, the z flag is also cleared if the conversion +// was inexact, ie. if the double value could not be converted exactly +// to a 32bit integer. + void EmitVFPTruncate(VFPRoundingMode rounding_mode, + SwVfpRegister result, + DwVfpRegister double_input, + Register scratch1, + Register scratch2, + CheckForInexactConversion check + = kDontCheckForInexactConversion); + // Count leading zeros in a 32 bit word. On ARM5 and later it uses the clz // instruction. On pre-ARM5 hardware this routine gives the wrong answer // for 0 (31 instead of 32). Source and scratch can be the same in which case @@ -777,11 +818,11 @@ class MacroAssembler: public Assembler { mov(reg, scratch); } - void SmiUntag(Register reg) { - mov(reg, Operand(reg, ASR, kSmiTagSize)); + void SmiUntag(Register reg, SBit s = LeaveCC) { + mov(reg, Operand(reg, ASR, kSmiTagSize), s); } - void SmiUntag(Register dst, Register src) { - mov(dst, Operand(src, ASR, kSmiTagSize)); + void SmiUntag(Register dst, Register src, SBit s = LeaveCC) { + mov(dst, Operand(src, ASR, kSmiTagSize), s); } // Jump the register contains a smi. diff --git a/deps/v8/src/arm/simulator-arm.cc b/deps/v8/src/arm/simulator-arm.cc index 20d51c6af0..f475a18b09 100644 --- a/deps/v8/src/arm/simulator-arm.cc +++ b/deps/v8/src/arm/simulator-arm.cc @@ -1005,7 +1005,9 @@ int Simulator::ReadW(int32_t addr, Instruction* instr) { intptr_t* ptr = reinterpret_cast(addr); return *ptr; } - PrintF("Unaligned read at 0x%08x, pc=%p\n", addr, instr); + PrintF("Unaligned read at 0x%08x, pc=0x%08" V8PRIxPTR "\n", + addr, + reinterpret_cast(instr)); UNIMPLEMENTED(); return 0; #endif @@ -1023,7 +1025,9 @@ void Simulator::WriteW(int32_t addr, int value, Instruction* instr) { *ptr = value; return; } - PrintF("Unaligned write at 0x%08x, pc=%p\n", addr, instr); + PrintF("Unaligned write at 0x%08x, pc=0x%08" V8PRIxPTR "\n", + addr, + reinterpret_cast(instr)); UNIMPLEMENTED(); #endif } @@ -1038,7 +1042,9 @@ uint16_t Simulator::ReadHU(int32_t addr, Instruction* instr) { uint16_t* ptr = reinterpret_cast(addr); return *ptr; } - PrintF("Unaligned unsigned halfword read at 0x%08x, pc=%p\n", addr, instr); + PrintF("Unaligned unsigned halfword read at 0x%08x, pc=0x%08" V8PRIxPTR "\n", + addr, + reinterpret_cast(instr)); UNIMPLEMENTED(); return 0; #endif @@ -1072,7 +1078,9 @@ void Simulator::WriteH(int32_t addr, uint16_t value, Instruction* instr) { *ptr = value; return; } - PrintF("Unaligned unsigned halfword write at 0x%08x, pc=%p\n", addr, instr); + PrintF("Unaligned unsigned halfword write at 0x%08x, pc=0x%08" V8PRIxPTR "\n", + addr, + reinterpret_cast(instr)); UNIMPLEMENTED(); #endif } @@ -1089,7 +1097,9 @@ void Simulator::WriteH(int32_t addr, int16_t value, Instruction* instr) { *ptr = value; return; } - PrintF("Unaligned halfword write at 0x%08x, pc=%p\n", addr, instr); + PrintF("Unaligned halfword write at 0x%08x, pc=0x%08" V8PRIxPTR "\n", + addr, + reinterpret_cast(instr)); UNIMPLEMENTED(); #endif } @@ -2554,6 +2564,7 @@ void Simulator::DecodeTypeVFP(Instruction* instr) { 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); set_d_register_from_double(vd, dd_value); } else { UNIMPLEMENTED(); // Not used by V8. @@ -2788,14 +2799,17 @@ void Simulator::DecodeVCVTBetweenFloatingPointAndInteger(Instruction* instr) { inv_op_vfp_flag_ = get_inv_op_vfp_flag(mode, val, unsigned_integer); + double abs_diff = + unsigned_integer ? fabs(val - static_cast(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: { - double abs_diff = - unsigned_integer ? fabs(val - static_cast(temp)) - : fabs(val - temp); int val_sign = (val > 0) ? 1 : -1; if (abs_diff > 0.5) { temp += val_sign; diff --git a/deps/v8/src/arm/stub-cache-arm.cc b/deps/v8/src/arm/stub-cache-arm.cc index e2501125a0..60a11f3ced 100644 --- a/deps/v8/src/arm/stub-cache-arm.cc +++ b/deps/v8/src/arm/stub-cache-arm.cc @@ -2671,10 +2671,13 @@ MaybeObject* StoreStubCompiler::CompileStoreInterceptor(JSObject* receiver, __ Push(r1, r2, r0); // Receiver, name, value. + __ mov(r0, Operand(Smi::FromInt(strict_mode_))); + __ push(r0); // strict mode + // Do tail-call to the runtime system. ExternalReference store_ic_property = ExternalReference(IC_Utility(IC::kStoreInterceptorProperty)); - __ TailCallExternalReference(store_ic_property, 3, 1); + __ TailCallExternalReference(store_ic_property, 4, 1); // Handle store cache miss. __ bind(&miss); @@ -4056,7 +4059,12 @@ MaybeObject* ExternalArrayStubCompiler::CompileKeyedStoreStub( // Push receiver, key and value for runtime call. __ Push(r2, r1, r0); - __ TailCallRuntime(Runtime::kSetProperty, 3, 1); + __ mov(r1, Operand(Smi::FromInt(NONE))); // PropertyAttributes + __ mov(r0, Operand(Smi::FromInt( + Code::ExtractExtraICStateFromFlags(flags) & kStrictMode))); + __ Push(r1, r0); + + __ TailCallRuntime(Runtime::kSetProperty, 5, 1); return GetCode(flags); } diff --git a/deps/v8/src/arm/virtual-frame-arm.cc b/deps/v8/src/arm/virtual-frame-arm.cc index b4b518cff6..544e405dbb 100644 --- a/deps/v8/src/arm/virtual-frame-arm.cc +++ b/deps/v8/src/arm/virtual-frame-arm.cc @@ -332,9 +332,9 @@ void VirtualFrame::CallLoadIC(Handle name, RelocInfo::Mode mode) { void VirtualFrame::CallStoreIC(Handle name, bool is_contextual, StrictModeFlag strict_mode) { - Handle ic(Builtins::builtin(strict_mode == kStrictMode - ? Builtins::StoreIC_Initialize_Strict - : Builtins::StoreIC_Initialize)); + Handle ic(Builtins::builtin( + (strict_mode == kStrictMode) ? Builtins::StoreIC_Initialize_Strict + : Builtins::StoreIC_Initialize)); PopToR0(); RelocInfo::Mode mode; if (is_contextual) { @@ -359,8 +359,10 @@ void VirtualFrame::CallKeyedLoadIC() { } -void VirtualFrame::CallKeyedStoreIC() { - Handle ic(Builtins::builtin(Builtins::KeyedStoreIC_Initialize)); +void VirtualFrame::CallKeyedStoreIC(StrictModeFlag strict_mode) { + Handle ic(Builtins::builtin( + (strict_mode == kStrictMode) ? Builtins::KeyedStoreIC_Initialize_Strict + : Builtins::KeyedStoreIC_Initialize)); PopToR1R0(); SpillAll(); EmitPop(r2); diff --git a/deps/v8/src/arm/virtual-frame-arm.h b/deps/v8/src/arm/virtual-frame-arm.h index b6e794a5c0..76470bdc53 100644 --- a/deps/v8/src/arm/virtual-frame-arm.h +++ b/deps/v8/src/arm/virtual-frame-arm.h @@ -303,7 +303,7 @@ class VirtualFrame : public ZoneObject { // Call keyed store IC. Value, key and receiver are on the stack. All three // are consumed. Result is returned in r0. - void CallKeyedStoreIC(); + void CallKeyedStoreIC(StrictModeFlag strict_mode); // Call into an IC stub given the number of arguments it removes // from the stack. Register arguments to the IC stub are implicit, diff --git a/deps/v8/src/builtins.cc b/deps/v8/src/builtins.cc index 8fdc1b1382..01e8deb4e1 100644 --- a/deps/v8/src/builtins.cc +++ b/deps/v8/src/builtins.cc @@ -1328,12 +1328,12 @@ static void Generate_StoreIC_Normal_Strict(MacroAssembler* masm) { static void Generate_StoreIC_Megamorphic(MacroAssembler* masm) { - StoreIC::GenerateMegamorphic(masm, StoreIC::kStoreICNonStrict); + StoreIC::GenerateMegamorphic(masm, kNonStrictMode); } static void Generate_StoreIC_Megamorphic_Strict(MacroAssembler* masm) { - StoreIC::GenerateMegamorphic(masm, StoreIC::kStoreICStrict); + StoreIC::GenerateMegamorphic(masm, kStrictMode); } @@ -1348,17 +1348,22 @@ static void Generate_StoreIC_ArrayLength_Strict(MacroAssembler* masm) { static void Generate_StoreIC_GlobalProxy(MacroAssembler* masm) { - StoreIC::GenerateGlobalProxy(masm); + StoreIC::GenerateGlobalProxy(masm, kNonStrictMode); } static void Generate_StoreIC_GlobalProxy_Strict(MacroAssembler* masm) { - StoreIC::GenerateGlobalProxy(masm); + StoreIC::GenerateGlobalProxy(masm, kStrictMode); } static void Generate_KeyedStoreIC_Generic(MacroAssembler* masm) { - KeyedStoreIC::GenerateGeneric(masm); + KeyedStoreIC::GenerateGeneric(masm, kNonStrictMode); +} + + +static void Generate_KeyedStoreIC_Generic_Strict(MacroAssembler* masm) { + KeyedStoreIC::GenerateGeneric(masm, kStrictMode); } @@ -1372,6 +1377,11 @@ static void Generate_KeyedStoreIC_Initialize(MacroAssembler* masm) { } +static void Generate_KeyedStoreIC_Initialize_Strict(MacroAssembler* masm) { + KeyedStoreIC::GenerateInitialize(masm); +} + + #ifdef ENABLE_DEBUGGER_SUPPORT static void Generate_LoadIC_DebugBreak(MacroAssembler* masm) { Debug::GenerateLoadICDebugBreak(masm); diff --git a/deps/v8/src/builtins.h b/deps/v8/src/builtins.h index 2733410ea9..5ea466513f 100644 --- a/deps/v8/src/builtins.h +++ b/deps/v8/src/builtins.h @@ -62,111 +62,116 @@ enum BuiltinExtraArguments { // Define list of builtins implemented in assembly. -#define BUILTIN_LIST_A(V) \ - V(ArgumentsAdaptorTrampoline, BUILTIN, UNINITIALIZED, \ - Code::kNoExtraICState) \ - V(JSConstructCall, BUILTIN, UNINITIALIZED, \ - Code::kNoExtraICState) \ - V(JSConstructStubCountdown, BUILTIN, UNINITIALIZED, \ - Code::kNoExtraICState) \ - V(JSConstructStubGeneric, BUILTIN, UNINITIALIZED, \ - 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(NotifyDeoptimized, BUILTIN, UNINITIALIZED, \ - Code::kNoExtraICState) \ - V(NotifyLazyDeoptimized, 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(StoreIC_Miss, BUILTIN, UNINITIALIZED, \ - Code::kNoExtraICState) \ - V(KeyedStoreIC_Miss, 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_ArrayLength, LOAD_IC, MONOMORPHIC, \ - Code::kNoExtraICState) \ - V(LoadIC_StringLength, LOAD_IC, MONOMORPHIC, \ - Code::kNoExtraICState) \ - V(LoadIC_StringWrapperLength, LOAD_IC, MONOMORPHIC, \ - Code::kNoExtraICState) \ - V(LoadIC_FunctionPrototype, LOAD_IC, MONOMORPHIC, \ - Code::kNoExtraICState) \ - V(LoadIC_Megamorphic, LOAD_IC, MEGAMORPHIC, \ - 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, MEGAMORPHIC, \ - Code::kNoExtraICState) \ - V(KeyedLoadIC_String, KEYED_LOAD_IC, MEGAMORPHIC, \ - Code::kNoExtraICState) \ - V(KeyedLoadIC_IndexedInterceptor, KEYED_LOAD_IC, MEGAMORPHIC, \ - Code::kNoExtraICState) \ - \ - V(StoreIC_Initialize, STORE_IC, UNINITIALIZED, \ - Code::kNoExtraICState) \ - V(StoreIC_ArrayLength, STORE_IC, MONOMORPHIC, \ - Code::kNoExtraICState) \ - V(StoreIC_Normal, STORE_IC, MONOMORPHIC, \ - Code::kNoExtraICState) \ - V(StoreIC_Megamorphic, STORE_IC, MEGAMORPHIC, \ - Code::kNoExtraICState) \ - V(StoreIC_GlobalProxy, STORE_IC, MEGAMORPHIC, \ - Code::kNoExtraICState) \ - V(StoreIC_Initialize_Strict, STORE_IC, UNINITIALIZED, \ - StoreIC::kStoreICStrict) \ - V(StoreIC_ArrayLength_Strict, STORE_IC, MONOMORPHIC, \ - StoreIC::kStoreICStrict) \ - V(StoreIC_Normal_Strict, STORE_IC, MONOMORPHIC, \ - StoreIC::kStoreICStrict) \ - V(StoreIC_Megamorphic_Strict, STORE_IC, MEGAMORPHIC, \ - StoreIC::kStoreICStrict) \ - V(StoreIC_GlobalProxy_Strict, STORE_IC, MEGAMORPHIC, \ - StoreIC::kStoreICStrict) \ - \ - V(KeyedStoreIC_Initialize, KEYED_STORE_IC, UNINITIALIZED, \ - Code::kNoExtraICState) \ - V(KeyedStoreIC_Generic, KEYED_STORE_IC, MEGAMORPHIC, \ - Code::kNoExtraICState) \ - \ - /* Uses KeyedLoadIC_Initialize; must be after in list. */ \ - V(FunctionCall, BUILTIN, UNINITIALIZED, \ - Code::kNoExtraICState) \ - V(FunctionApply, BUILTIN, UNINITIALIZED, \ - Code::kNoExtraICState) \ - \ - V(ArrayCode, BUILTIN, UNINITIALIZED, \ - Code::kNoExtraICState) \ - V(ArrayConstructCode, BUILTIN, UNINITIALIZED, \ - Code::kNoExtraICState) \ - \ - V(StringConstructCode, BUILTIN, UNINITIALIZED, \ - Code::kNoExtraICState) \ - \ - V(OnStackReplacement, BUILTIN, UNINITIALIZED, \ - Code::kNoExtraICState) +#define BUILTIN_LIST_A(V) \ + V(ArgumentsAdaptorTrampoline, BUILTIN, UNINITIALIZED, \ + Code::kNoExtraICState) \ + V(JSConstructCall, BUILTIN, UNINITIALIZED, \ + Code::kNoExtraICState) \ + V(JSConstructStubCountdown, BUILTIN, UNINITIALIZED, \ + Code::kNoExtraICState) \ + V(JSConstructStubGeneric, BUILTIN, UNINITIALIZED, \ + 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(NotifyDeoptimized, BUILTIN, UNINITIALIZED, \ + Code::kNoExtraICState) \ + V(NotifyLazyDeoptimized, 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(StoreIC_Miss, BUILTIN, UNINITIALIZED, \ + Code::kNoExtraICState) \ + V(KeyedStoreIC_Miss, 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_ArrayLength, LOAD_IC, MONOMORPHIC, \ + Code::kNoExtraICState) \ + V(LoadIC_StringLength, LOAD_IC, MONOMORPHIC, \ + Code::kNoExtraICState) \ + V(LoadIC_StringWrapperLength, LOAD_IC, MONOMORPHIC, \ + Code::kNoExtraICState) \ + V(LoadIC_FunctionPrototype, LOAD_IC, MONOMORPHIC, \ + Code::kNoExtraICState) \ + V(LoadIC_Megamorphic, LOAD_IC, MEGAMORPHIC, \ + 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, MEGAMORPHIC, \ + Code::kNoExtraICState) \ + V(KeyedLoadIC_String, KEYED_LOAD_IC, MEGAMORPHIC, \ + Code::kNoExtraICState) \ + V(KeyedLoadIC_IndexedInterceptor, KEYED_LOAD_IC, MEGAMORPHIC, \ + Code::kNoExtraICState) \ + \ + V(StoreIC_Initialize, STORE_IC, UNINITIALIZED, \ + Code::kNoExtraICState) \ + V(StoreIC_ArrayLength, STORE_IC, MONOMORPHIC, \ + Code::kNoExtraICState) \ + V(StoreIC_Normal, STORE_IC, MONOMORPHIC, \ + Code::kNoExtraICState) \ + V(StoreIC_Megamorphic, STORE_IC, MEGAMORPHIC, \ + Code::kNoExtraICState) \ + V(StoreIC_GlobalProxy, STORE_IC, MEGAMORPHIC, \ + Code::kNoExtraICState) \ + V(StoreIC_Initialize_Strict, STORE_IC, UNINITIALIZED, \ + kStrictMode) \ + V(StoreIC_ArrayLength_Strict, STORE_IC, MONOMORPHIC, \ + kStrictMode) \ + V(StoreIC_Normal_Strict, STORE_IC, MONOMORPHIC, \ + kStrictMode) \ + V(StoreIC_Megamorphic_Strict, STORE_IC, MEGAMORPHIC, \ + kStrictMode) \ + V(StoreIC_GlobalProxy_Strict, STORE_IC, MEGAMORPHIC, \ + kStrictMode) \ + \ + V(KeyedStoreIC_Initialize, KEYED_STORE_IC, UNINITIALIZED, \ + Code::kNoExtraICState) \ + V(KeyedStoreIC_Generic, KEYED_STORE_IC, MEGAMORPHIC, \ + Code::kNoExtraICState) \ + \ + V(KeyedStoreIC_Initialize_Strict, KEYED_STORE_IC, UNINITIALIZED, \ + kStrictMode) \ + V(KeyedStoreIC_Generic_Strict, KEYED_STORE_IC, MEGAMORPHIC, \ + kStrictMode) \ + \ + /* Uses KeyedLoadIC_Initialize; must be after in list. */ \ + V(FunctionCall, BUILTIN, UNINITIALIZED, \ + Code::kNoExtraICState) \ + V(FunctionApply, BUILTIN, UNINITIALIZED, \ + Code::kNoExtraICState) \ + \ + V(ArrayCode, BUILTIN, UNINITIALIZED, \ + Code::kNoExtraICState) \ + V(ArrayConstructCode, BUILTIN, UNINITIALIZED, \ + Code::kNoExtraICState) \ + \ + V(StringConstructCode, BUILTIN, UNINITIALIZED, \ + Code::kNoExtraICState) \ + \ + V(OnStackReplacement, BUILTIN, UNINITIALIZED, \ + Code::kNoExtraICState) #ifdef ENABLE_DEBUGGER_SUPPORT diff --git a/deps/v8/src/compiler.cc b/deps/v8/src/compiler.cc index f392cceb3c..367de64881 100755 --- a/deps/v8/src/compiler.cc +++ b/deps/v8/src/compiler.cc @@ -221,11 +221,12 @@ static bool MakeCrankshaftCode(CompilationInfo* info) { // or perform on-stack replacement for function with too many // stack-allocated local variables. // - // The encoding is as a signed value, with parameters using the negative - // indices and locals the non-negative ones. + // The encoding is as a signed value, with parameters and receiver using + // the negative indices and locals the non-negative ones. const int limit = LUnallocated::kMaxFixedIndices / 2; Scope* scope = info->scope(); - if (scope->num_parameters() > limit || scope->num_stack_slots() > limit) { + if ((scope->num_parameters() + 1) > limit || + scope->num_stack_slots() > limit) { AbortAndDisable(info); // True indicates the compilation pipeline is still going, not // necessarily that we optimized the code. diff --git a/deps/v8/src/d8.cc b/deps/v8/src/d8.cc index 4dcc794bb3..349ec90410 100644 --- a/deps/v8/src/d8.cc +++ b/deps/v8/src/d8.cc @@ -405,7 +405,7 @@ void Shell::AddHistogramSample(void* histogram, int sample) { void Shell::Initialize() { Shell::counter_map_ = new CounterMap(); // Set up counters - if (i::FLAG_map_counters != NULL) + if (i::StrLength(i::FLAG_map_counters) != 0) MapCounters(i::FLAG_map_counters); if (i::FLAG_dump_counters) { V8::SetCounterFunction(LookupCounter); @@ -425,6 +425,12 @@ void Shell::Initialize() { global_template->Set(String::New("quit"), FunctionTemplate::New(Quit)); global_template->Set(String::New("version"), FunctionTemplate::New(Version)); +#ifdef LIVE_OBJECT_LIST + global_template->Set(String::New("lol_is_enabled"), Boolean::New(true)); +#else + global_template->Set(String::New("lol_is_enabled"), Boolean::New(false)); +#endif + Handle os_templ = ObjectTemplate::New(); AddOSMethods(os_templ); global_template->Set(String::New("os"), os_templ); diff --git a/deps/v8/src/d8.js b/deps/v8/src/d8.js index b0edb706ad..979807888e 100644 --- a/deps/v8/src/d8.js +++ b/deps/v8/src/d8.js @@ -117,6 +117,10 @@ Debug.State = { var trace_compile = false; // Tracing all compile events? var trace_debug_json = false; // Tracing all debug json packets? var last_cmd_line = ''; +//var lol_is_enabled; // Set to true in d8.cc if LIVE_OBJECT_LIST is defined. +var lol_next_dump_index = 0; +const kDefaultLolLinesToPrintAtATime = 10; +const kMaxLolLinesToPrintAtATime = 1000; var repeat_cmd_line = ''; var is_running = true; @@ -495,6 +499,13 @@ function DebugRequest(cmd_line) { this.request_ = void 0; break; + case 'liveobjectlist': + case 'lol': + if (lol_is_enabled) { + this.request_ = this.lolToJSONRequest_(args, is_repeating); + break; + } + default: throw new Error('Unknown command "' + cmd + '"'); } @@ -539,10 +550,54 @@ DebugRequest.prototype.createRequest = function(command) { }; +// Note: we use detected command repetition as a signal for continuation here. +DebugRequest.prototype.createLOLRequest = function(command, + start_index, + lines_to_dump, + is_continuation) { + if (is_continuation) { + start_index = lol_next_dump_index; + } + + if (lines_to_dump) { + lines_to_dump = parseInt(lines_to_dump); + } else { + lines_to_dump = kDefaultLolLinesToPrintAtATime; + } + if (lines_to_dump > kMaxLolLinesToPrintAtATime) { + lines_to_dump = kMaxLolLinesToPrintAtATime; + } + + // Save the next start_index to dump from: + lol_next_dump_index = start_index + lines_to_dump; + + var request = this.createRequest(command); + request.arguments = {}; + request.arguments.start = start_index; + request.arguments.count = lines_to_dump; + + return request; +}; + + // Create a JSON request for the evaluation command. DebugRequest.prototype.makeEvaluateJSONRequest_ = function(expression) { // Global varaible used to store whether a handle was requested. lookup_handle = null; + + if (lol_is_enabled) { + // Check if the expression is a obj id in the form @. + var obj_id_match = expression.match(/^@([0-9]+)$/); + if (obj_id_match) { + var obj_id = parseInt(obj_id_match[1]); + // Build a dump request. + var request = this.createRequest('getobj'); + request.arguments = {}; + request.arguments.obj_id = obj_id; + return request.toJSONProtocol(); + } + } + // Check if the expression is a handle id in the form ##. var handle_match = expression.match(/^#([0-9]*)#$/); if (handle_match) { @@ -1103,6 +1158,10 @@ DebugRequest.prototype.infoCommandToJSONRequest_ = function(args) { // Build a evaluate request from the text command. request = this.createRequest('frame'); last_cmd = 'info args'; + } else if (lol_is_enabled && + args && (args == 'liveobjectlist' || args == 'lol')) { + // Build a evaluate request from the text command. + return this.liveObjectListToJSONRequest_(null); } else { throw new Error('Invalid info arguments.'); } @@ -1153,6 +1212,262 @@ DebugRequest.prototype.gcToJSONRequest_ = function(args) { }; +// Args: [v[erbose]] [] [i[ndex] ] [t[ype] ] [sp[ace] ] +DebugRequest.prototype.lolMakeListRequest = + function(cmd, args, first_arg_index, is_repeating) { + + var request; + var start_index = 0; + var dump_limit = void 0; + var type_filter = void 0; + var space_filter = void 0; + var prop_filter = void 0; + var is_verbose = false; + var i; + + for (i = first_arg_index; i < args.length; i++) { + var arg = args[i]; + // Check for [v[erbose]]: + if (arg === 'verbose' || arg === 'v') { + // Nothing to do. This is already implied by args.length > 3. + is_verbose = true; + + // Check for []: + } else if (arg.match(/^[0-9]+$/)) { + dump_limit = arg; + is_verbose = true; + + // Check for i[ndex] : + } else if (arg === 'index' || arg === 'i') { + i++; + if (args.length < i) { + throw new Error('Missing index after ' + arg + '.'); + } + start_index = parseInt(args[i]); + // The user input start index starts at 1: + if (start_index <= 0) { + throw new Error('Invalid index ' + args[i] + '.'); + } + start_index -= 1; + is_verbose = true; + + // Check for t[ype] : + } else if (arg === 'type' || arg === 't') { + i++; + if (args.length < i) { + throw new Error('Missing type after ' + arg + '.'); + } + type_filter = args[i]; + + // Check for space : + } else if (arg === 'space' || arg === 'sp') { + i++; + if (args.length < i) { + throw new Error('Missing space name after ' + arg + '.'); + } + space_filter = args[i]; + + // Check for property : + } else if (arg === 'property' || arg === 'prop') { + i++; + if (args.length < i) { + throw new Error('Missing property name after ' + arg + '.'); + } + prop_filter = args[i]; + + } else { + throw new Error('Unknown args at ' + arg + '.'); + } + } + + // Build the verbose request: + if (is_verbose) { + request = this.createLOLRequest('lol-'+cmd, + start_index, + dump_limit, + is_repeating); + request.arguments.verbose = true; + } else { + request = this.createRequest('lol-'+cmd); + request.arguments = {}; + } + + request.arguments.filter = {}; + if (type_filter) { + request.arguments.filter.type = type_filter; + } + if (space_filter) { + request.arguments.filter.space = space_filter; + } + if (prop_filter) { + request.arguments.filter.prop = prop_filter; + } + + return request; +} + + +function extractObjId(args) { + var id = args; + id = id.match(/^@([0-9]+)$/); + if (id) { + id = id[1]; + } else { + throw new Error('Invalid obj id ' + args + '.'); + } + return parseInt(id); +} + + +DebugRequest.prototype.lolToJSONRequest_ = function(args, is_repeating) { + var request; + // Use default command if one is not specified: + if (!args) { + args = 'info'; + } + + var orig_args = args; + var first_arg_index; + + var arg, i; + var args = args.split(/\s+/g); + var cmd = args[0]; + var id; + + // Command: [v[erbose]] ... + if (cmd.match(/^[0-9]+$/)) { + // Convert to the padded list command: + // Command: l[ist] [v[erbose]] ... + + // Insert the implicit 'list' in front and process as normal: + cmd = 'list'; + args.unshift(cmd); + } + + switch(cmd) { + // Command: c[apture] + case 'capture': + case 'c': + request = this.createRequest('lol-capture'); + break; + + // Command: clear|d[elete] |all + case 'clear': + case 'delete': + case 'del': { + if (args.length < 2) { + throw new Error('Missing argument after ' + cmd + '.'); + } else if (args.length > 2) { + throw new Error('Too many arguments after ' + cmd + '.'); + } + id = args[1]; + if (id.match(/^[0-9]+$/)) { + // Delete a specific lol record: + request = this.createRequest('lol-delete'); + request.arguments = {}; + request.arguments.id = parseInt(id); + } else if (id === 'all') { + // Delete all: + request = this.createRequest('lol-reset'); + } else { + throw new Error('Invalid argument after ' + cmd + '.'); + } + break; + } + + // Command: diff [] + case 'diff': + first_arg_index = 3; + + // Command: list [] + case 'list': + + // Command: ret[ainers] [] + case 'retainers': + case 'ret': + case 'retaining-paths': + case 'rp': { + if (cmd === 'ret') cmd = 'retainers'; + else if (cmd === 'rp') cmd = 'retaining-paths'; + + if (!first_arg_index) first_arg_index = 2; + + if (args.length < first_arg_index) { + throw new Error('Too few arguments after ' + cmd + '.'); + } + + var request_cmd = (cmd === 'list') ? 'diff':cmd; + request = this.lolMakeListRequest(request_cmd, + args, + first_arg_index, + is_repeating); + + if (cmd === 'diff') { + request.arguments.id1 = parseInt(args[1]); + request.arguments.id2 = parseInt(args[2]); + } else if (cmd == 'list') { + request.arguments.id1 = 0; + request.arguments.id2 = parseInt(args[1]); + } else { + request.arguments.id = extractObjId(args[1]); + } + break; + } + + // Command: getid + case 'getid': { + request = this.createRequest('lol-getid'); + request.arguments = {}; + request.arguments.address = args[1]; + break; + } + + // Command: inf[o] [] + case 'info': + case 'inf': { + if (args.length > 2) { + throw new Error('Too many arguments after ' + cmd + '.'); + } + // Built the info request: + request = this.createLOLRequest('lol-info', 0, args[1], is_repeating); + break; + } + + // Command: path + case 'path': { + request = this.createRequest('lol-path'); + request.arguments = {}; + if (args.length > 2) { + request.arguments.id1 = extractObjId(args[1]); + request.arguments.id2 = extractObjId(args[2]); + } else { + request.arguments.id1 = 0; + request.arguments.id2 = extractObjId(args[1]); + } + break; + } + + // Command: print + case 'print': { + request = this.createRequest('lol-print'); + request.arguments = {}; + request.arguments.id = extractObjId(args[1]); + break; + } + + // Command: reset + case 'reset': { + request = this.createRequest('lol-reset'); + break; + } + + default: + throw new Error('Invalid arguments.'); + } + return request.toJSONProtocol(); +}; + + // Create a JSON request for the threads command. DebugRequest.prototype.threadsCommandToJSONRequest_ = function(args) { // Build a threads request from the text command. @@ -1239,6 +1554,49 @@ DebugRequest.prototype.helpCommand_ = function(args) { print(''); print('gc - runs the garbage collector'); print(''); + + if (lol_is_enabled) { + print('liveobjectlist|lol - live object list tracking.'); + print(' where can be:'); + print(' c[apture] - captures a LOL list.'); + print(' clear|del[ete] |all - clears LOL of id .'); + print(' If \'all\' is unspecified instead, will clear all.'); + print(' diff []'); + print(' - prints the diff between LOLs id1 and id2.'); + print(' - also see below.'); + print(' getid
- gets the obj id for the specified address if available.'); + print(' The address must be in hex form prefixed with 0x.'); + print(' inf[o] [] - lists summary info of all LOL lists.'); + print(' If N is specified, will print N items at a time.'); + print(' [l[ist]] []'); + print(' - prints the listing of objects in LOL id.'); + print(' - also see below.'); + print(' reset - clears all LOL lists.'); + print(' ret[ainers] []'); + print(' - prints the list of retainers of obj id.'); + print(' - also see below.'); + print(' path - prints the retaining path from obj id1 to id2.'); + print(' If only one id is specified, will print the path from'); + print(' roots to the specified object if available.'); + print(' print - prints the obj for the specified obj id if available.'); + print(''); + print(' includes:'); + print(' [v[erbose]] - do verbose dump.'); + print(' [] - dump N items at a time. Implies verbose dump.'); + print(' If unspecified, N will default to '+ + kDefaultLolLinesToPrintAtATime+'. Max N is '+ + kMaxLolLinesToPrintAtATime+'.'); + print(' [i[ndex] ] - start dump from index i. Implies verbose dump.'); + print(' [t[ype] ] - filter by type.'); + print(' [sp[ace] ] - filter by heap space where is one of'); + print(' { cell, code, lo, map, new, old-data, old-pointer }.'); + print(''); + print(' If the verbose option, or an option that implies a verbose dump'); + print(' is specified, then a verbose dump will requested. Else, a summary dump'); + print(' will be requested.'); + print(''); + } + print('trace compile'); // hidden command: trace debug json - toggles tracing of debug json packets print(''); @@ -1339,6 +1697,237 @@ function refObjectToString_(protocolPackage, handle) { } +function decodeLolCaptureResponse(body) { + var result; + result = 'Captured live object list '+ body.id + + ': count '+ body.count + ' size ' + body.size; + return result; +} + + +function decodeLolDeleteResponse(body) { + var result; + result = 'Deleted live object list '+ body.id; + return result; +} + + +function digitsIn(value) { + var digits = 0; + if (value === 0) value = 1; + while (value >= 1) { + digits++; + value /= 10; + } + return digits; +} + + +function padding(value, max_digits) { + var padding_digits = max_digits - digitsIn(value); + var padding = ''; + while (padding_digits > 0) { + padding += ' '; + padding_digits--; + } + return padding; +} + + +function decodeLolInfoResponse(body) { + var result; + var lists = body.lists; + var length = lists.length; + var first_index = body.first_index + 1; + var has_more = ((first_index + length) <= body.count); + result = 'captured live object lists'; + if (has_more || (first_index != 1)) { + result += ' ['+ length +' of '+ body.count + + ': starting from '+ first_index +']'; + } + result += ':\n'; + var max_digits = digitsIn(body.count); + var last_count = 0; + var last_size = 0; + for (var i = 0; i < length; i++) { + var entry = lists[i]; + var count = entry.count; + var size = entry.size; + var index = first_index + i; + result += ' [' + padding(index, max_digits) + index + '] id '+ entry.id + + ': count '+ count; + if (last_count > 0) { + result += '(+' + (count - last_count) + ')'; + } + result += ' size '+ size; + if (last_size > 0) { + result += '(+' + (size - last_size) + ')'; + } + result += '\n'; + last_count = count; + last_size = size; + } + result += ' total: '+length+' lists\n'; + if (has_more) { + result += ' -- press for more --\n'; + } else { + repeat_cmd_line = ''; + } + if (length === 0) result += ' none\n'; + + return result; +} + + +function decodeLolListResponse(body, title) { + + var result; + var total_count = body.count; + var total_size = body.size; + var length; + var max_digits; + var i; + var entry; + var index; + + var max_count_digits = digitsIn(total_count); + var max_size_digits; + + var summary = body.summary; + if (summary) { + + var roots_count = 0; + var found_root = body.found_root || 0; + var found_weak_root = body.found_weak_root || 0; + + // Print the summary result: + result = 'summary of objects:\n'; + length = summary.length; + if (found_root !== 0) { + roots_count++; + } + if (found_weak_root !== 0) { + roots_count++; + } + max_digits = digitsIn(length + roots_count); + max_size_digits = digitsIn(total_size); + + index = 1; + if (found_root !== 0) { + result += ' [' + padding(index, max_digits) + index + '] ' + + ' count '+ 1 + padding(0, max_count_digits) + + ' '+ padding(0, max_size_digits+1) + + ' : \n'; + index++; + } + if (found_weak_root !== 0) { + result += ' [' + padding(index, max_digits) + index + '] ' + + ' count '+ 1 + padding(0, max_count_digits) + + ' '+ padding(0, max_size_digits+1) + + ' : \n'; + index++; + } + + for (i = 0; i < length; i++) { + entry = summary[i]; + var count = entry.count; + var size = entry.size; + result += ' [' + padding(index, max_digits) + index + '] ' + + ' count '+ count + padding(count, max_count_digits) + + ' size '+ size + padding(size, max_size_digits) + + ' : <' + entry.desc + '>\n'; + index++; + } + result += '\n total count: '+(total_count+roots_count)+'\n'; + if (body.size) { + result += ' total size: '+body.size+'\n'; + } + + } else { + // Print the full dump result: + var first_index = body.first_index + 1; + var elements = body.elements; + length = elements.length; + var has_more = ((first_index + length) <= total_count); + result = title; + if (has_more || (first_index != 1)) { + result += ' ['+ length +' of '+ total_count + + ': starting from '+ first_index +']'; + } + result += ':\n'; + if (length === 0) result += ' none\n'; + max_digits = digitsIn(length); + + var max_id = 0; + var max_size = 0; + for (i = 0; i < length; i++) { + entry = elements[i]; + if (entry.id > max_id) max_id = entry.id; + if (entry.size > max_size) max_size = entry.size; + } + var max_id_digits = digitsIn(max_id); + max_size_digits = digitsIn(max_size); + + for (i = 0; i < length; i++) { + entry = elements[i]; + index = first_index + i; + result += ' ['+ padding(index, max_digits) + index +']'; + if (entry.id !== 0) { + result += ' @' + entry.id + padding(entry.id, max_id_digits) + + ': size ' + entry.size + ', ' + + padding(entry.size, max_size_digits) + entry.desc + '\n'; + } else { + // Must be a root or weak root: + result += ' ' + entry.desc + '\n'; + } + } + if (has_more) { + result += ' -- press for more --\n'; + } else { + repeat_cmd_line = ''; + } + if (length === 0) result += ' none\n'; + } + + return result; +} + + +function decodeLolDiffResponse(body) { + var title = 'objects'; + return decodeLolListResponse(body, title); +} + + +function decodeLolRetainersResponse(body) { + var title = 'retainers for @' + body.id; + return decodeLolListResponse(body, title); +} + + +function decodeLolPathResponse(body) { + return body.path; +} + + +function decodeLolResetResponse(body) { + return 'Reset all live object lists.'; +} + + +function decodeLolGetIdResponse(body) { + if (body.id == 0) { + return 'Address is invalid, or object has been moved or collected'; + } + return 'obj id is @' + body.id; +} + + +function decodeLolPrintResponse(body) { + return body.dump; +} + + // Rounds number 'num' to 'length' decimal places. function roundNumber(num, length) { var factor = Math.pow(10, length); @@ -1510,6 +2099,7 @@ function DebugResponseDetails(response) { case 'evaluate': case 'lookup': + case 'getobj': if (last_cmd == 'p' || last_cmd == 'print') { result = body.text; } else { @@ -1671,6 +2261,34 @@ function DebugResponseDetails(response) { } break; + case 'lol-capture': + details.text = decodeLolCaptureResponse(body); + break; + case 'lol-delete': + details.text = decodeLolDeleteResponse(body); + break; + case 'lol-diff': + details.text = decodeLolDiffResponse(body); + break; + case 'lol-getid': + details.text = decodeLolGetIdResponse(body); + break; + case 'lol-info': + details.text = decodeLolInfoResponse(body); + break; + case 'lol-print': + details.text = decodeLolPrintResponse(body); + break; + case 'lol-reset': + details.text = decodeLolResetResponse(body); + break; + case 'lol-retainers': + details.text = decodeLolRetainersResponse(body); + break; + case 'lol-path': + details.text = decodeLolPathResponse(body); + break; + default: details.text = 'Response for unknown command \'' + response.command() + '\'' + diff --git a/deps/v8/src/debug-debugger.js b/deps/v8/src/debug-debugger.js index 1adf73ac71..bc0f966fb1 100644 --- a/deps/v8/src/debug-debugger.js +++ b/deps/v8/src/debug-debugger.js @@ -109,6 +109,7 @@ var debugger_flags = { } }, }; +var lol_is_enabled = %HasLOLEnabled(); // Create a new break point object and add it to the list of break points. @@ -1391,6 +1392,8 @@ DebugCommandProcessor.prototype.processDebugJSONRequest = function(json_request) this.scopeRequest_(request, response); } else if (request.command == 'evaluate') { this.evaluateRequest_(request, response); + } else if (lol_is_enabled && request.command == 'getobj') { + this.getobjRequest_(request, response); } else if (request.command == 'lookup') { this.lookupRequest_(request, response); } else if (request.command == 'references') { @@ -1418,6 +1421,28 @@ DebugCommandProcessor.prototype.processDebugJSONRequest = function(json_request) } else if (request.command == 'gc') { this.gcRequest_(request, response); + // LiveObjectList tools: + } else if (lol_is_enabled && request.command == 'lol-capture') { + this.lolCaptureRequest_(request, response); + } else if (lol_is_enabled && request.command == 'lol-delete') { + this.lolDeleteRequest_(request, response); + } else if (lol_is_enabled && request.command == 'lol-diff') { + this.lolDiffRequest_(request, response); + } else if (lol_is_enabled && request.command == 'lol-getid') { + this.lolGetIdRequest_(request, response); + } else if (lol_is_enabled && request.command == 'lol-info') { + this.lolInfoRequest_(request, response); + } else if (lol_is_enabled && request.command == 'lol-reset') { + this.lolResetRequest_(request, response); + } else if (lol_is_enabled && request.command == 'lol-retainers') { + this.lolRetainersRequest_(request, response); + } else if (lol_is_enabled && request.command == 'lol-path') { + this.lolPathRequest_(request, response); + } else if (lol_is_enabled && request.command == 'lol-print') { + this.lolPrintRequest_(request, response); + } else if (lol_is_enabled && request.command == 'lol-stats') { + this.lolStatsRequest_(request, response); + } else { throw new Error('Unknown command "' + request.command + '" in request'); } @@ -2011,6 +2036,24 @@ DebugCommandProcessor.prototype.evaluateRequest_ = function(request, response) { }; +DebugCommandProcessor.prototype.getobjRequest_ = function(request, response) { + if (!request.arguments) { + return response.failed('Missing arguments'); + } + + // Pull out arguments. + var obj_id = request.arguments.obj_id; + + // Check for legal arguments. + if (IS_UNDEFINED(obj_id)) { + return response.failed('Argument "obj_id" missing'); + } + + // Dump the object. + response.body = MakeMirror(%GetLOLObj(obj_id)); +}; + + DebugCommandProcessor.prototype.lookupRequest_ = function(request, response) { if (!request.arguments) { return response.failed('Missing arguments'); @@ -2341,6 +2384,84 @@ DebugCommandProcessor.prototype.gcRequest_ = function(request, response) { }; +DebugCommandProcessor.prototype.lolCaptureRequest_ = + function(request, response) { + response.body = %CaptureLOL(); +}; + + +DebugCommandProcessor.prototype.lolDeleteRequest_ = + function(request, response) { + var id = request.arguments.id; + var result = %DeleteLOL(id); + if (result) { + response.body = { id: id }; + } else { + response.failed('Failed to delete: live object list ' + id + ' not found.'); + } +}; + + +DebugCommandProcessor.prototype.lolDiffRequest_ = function(request, response) { + var id1 = request.arguments.id1; + var id2 = request.arguments.id2; + var verbose = request.arguments.verbose; + var filter = request.arguments.filter; + if (verbose === true) { + var start = request.arguments.start; + var count = request.arguments.count; + response.body = %DumpLOL(id1, id2, start, count, filter); + } else { + response.body = %SummarizeLOL(id1, id2, filter); + } +}; + + +DebugCommandProcessor.prototype.lolGetIdRequest_ = function(request, response) { + var address = request.arguments.address; + response.body = {}; + response.body.id = %GetLOLObjId(address); +}; + + +DebugCommandProcessor.prototype.lolInfoRequest_ = function(request, response) { + var start = request.arguments.start; + var count = request.arguments.count; + response.body = %InfoLOL(start, count); +}; + + +DebugCommandProcessor.prototype.lolResetRequest_ = function(request, response) { + %ResetLOL(); +}; + + +DebugCommandProcessor.prototype.lolRetainersRequest_ = + function(request, response) { + var id = request.arguments.id; + var verbose = request.arguments.verbose; + var start = request.arguments.start; + var count = request.arguments.count; + var filter = request.arguments.filter; + + response.body = %GetLOLObjRetainers(id, Mirror.prototype, verbose, + start, count, filter); +}; + + +DebugCommandProcessor.prototype.lolPathRequest_ = function(request, response) { + var id1 = request.arguments.id1; + var id2 = request.arguments.id2; + response.body = {}; + response.body.path = %GetLOLPath(id1, id2, Mirror.prototype); +}; + + +DebugCommandProcessor.prototype.lolPrintRequest_ = function(request, response) { + var id = request.arguments.id; + response.body = {}; + response.body.dump = %PrintLOLObj(id); +}; // Check whether the previously processed command caused the VM to become diff --git a/deps/v8/src/debug.cc b/deps/v8/src/debug.cc index d8201a1894..c473941210 100644 --- a/deps/v8/src/debug.cc +++ b/deps/v8/src/debug.cc @@ -836,7 +836,8 @@ bool Debug::Load() { Handle key = Factory::LookupAsciiSymbol("builtins"); Handle global = Handle(context->global()); RETURN_IF_EMPTY_HANDLE_VALUE( - SetProperty(global, key, Handle(global->builtins()), NONE), + SetProperty(global, key, Handle(global->builtins()), + NONE, kNonStrictMode), false); // Compile the JavaScript for the debugger in the debugger context. diff --git a/deps/v8/src/flag-definitions.h b/deps/v8/src/flag-definitions.h index cf13def19f..ea245a44d7 100644 --- a/deps/v8/src/flag-definitions.h +++ b/deps/v8/src/flag-definitions.h @@ -110,7 +110,6 @@ DEFINE_bool(use_lithium, true, "use lithium code generator") DEFINE_bool(use_range, true, "use hydrogen range analysis") DEFINE_bool(eliminate_dead_phis, true, "eliminate dead phis") DEFINE_bool(use_gvn, true, "use hydrogen global value numbering") -DEFINE_bool(use_peeling, false, "use loop peeling") DEFINE_bool(use_canonicalizing, true, "use hydrogen instruction canonicalizing") DEFINE_bool(use_inlining, true, "use function inlining") DEFINE_bool(limit_inlining, true, "limit code size growth from inlining") @@ -267,6 +266,12 @@ DEFINE_bool(use_idle_notification, true, // ic.cc DEFINE_bool(use_ic, true, "use inline caching") +#ifdef LIVE_OBJECT_LIST +// liveobjectlist.cc +DEFINE_string(lol_workdir, NULL, "path for lol temp files") +DEFINE_bool(verify_lol, false, "perform debugging verification for lol") +#endif + // macro-assembler-ia32.cc DEFINE_bool(native_code_counters, false, "generate extra code for manipulating stats counters") @@ -355,7 +360,7 @@ 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") -DEFINE_string(map_counters, NULL, "Map counters to a file") +DEFINE_string(map_counters, "", "Map counters to a file") DEFINE_args(js_arguments, JSArguments(), "Pass all remaining arguments to the script. Alias for \"--\".") diff --git a/deps/v8/src/full-codegen.cc b/deps/v8/src/full-codegen.cc index 252fb9257a..b3dc95bdff 100644 --- a/deps/v8/src/full-codegen.cc +++ b/deps/v8/src/full-codegen.cc @@ -739,25 +739,13 @@ void FullCodeGenerator::VisitBinaryOperation(BinaryOperation* expr) { case Token::SHL: case Token::SHR: case Token::SAR: { - // Figure out if either of the operands is a constant. - ConstantOperand constant = ShouldInlineSmiCase(op) - ? GetConstantOperand(op, left, right) - : kNoConstants; - - // Load only the operands that we need to materialize. - if (constant == kNoConstants) { - VisitForStackValue(left); - VisitForAccumulatorValue(right); - } else if (constant == kRightConstant) { - VisitForAccumulatorValue(left); - } else { - ASSERT(constant == kLeftConstant); - VisitForAccumulatorValue(right); - } + // Load both operands. + VisitForStackValue(left); + VisitForAccumulatorValue(right); SetSourcePosition(expr->position()); if (ShouldInlineSmiCase(op)) { - EmitInlineSmiBinaryOp(expr, op, mode, left, right, constant); + EmitInlineSmiBinaryOp(expr, op, mode, left, right); } else { EmitBinaryOp(op, mode); } diff --git a/deps/v8/src/full-codegen.h b/deps/v8/src/full-codegen.h index 655e560eb5..5fb11b43d9 100644 --- a/deps/v8/src/full-codegen.h +++ b/deps/v8/src/full-codegen.h @@ -274,12 +274,6 @@ class FullCodeGenerator: public AstVisitor { ForwardBailoutStack* const parent_; }; - enum ConstantOperand { - kNoConstants, - kLeftConstant, - kRightConstant - }; - // Type of a member function that generates inline code for a native function. typedef void (FullCodeGenerator::*InlineFunctionGenerator) (ZoneList*); @@ -298,11 +292,6 @@ class FullCodeGenerator: public AstVisitor { // operation. bool ShouldInlineSmiCase(Token::Value op); - // Compute which (if any) of the operands is a compile-time constant. - ConstantOperand GetConstantOperand(Token::Value op, - Expression* left, - Expression* right); - // 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. @@ -432,6 +421,14 @@ class FullCodeGenerator: public AstVisitor { Label* done); void EmitVariableLoad(Variable* expr); + enum ResolveEvalFlag { + SKIP_CONTEXT_LOOKUP, + PERFORM_CONTEXT_LOOKUP + }; + + // Expects the arguments and the function already pushed. + void EmitResolvePossiblyDirectEval(ResolveEvalFlag flag, int arg_count); + // Platform-specific support for allocating a new closure based on // the given function info. void EmitNewClosure(Handle info, bool pretenure); @@ -457,34 +454,7 @@ class FullCodeGenerator: public AstVisitor { Token::Value op, OverwriteMode mode, Expression* left, - Expression* right, - ConstantOperand constant); - - void EmitConstantSmiBinaryOp(Expression* expr, - Token::Value op, - OverwriteMode mode, - bool left_is_constant_smi, - Smi* value); - - void EmitConstantSmiBitOp(Expression* expr, - Token::Value op, - OverwriteMode mode, - Smi* value); - - void EmitConstantSmiShiftOp(Expression* expr, - Token::Value op, - OverwriteMode mode, - Smi* value); - - void EmitConstantSmiAdd(Expression* expr, - OverwriteMode mode, - bool left_is_constant_smi, - Smi* value); - - void EmitConstantSmiSub(Expression* expr, - OverwriteMode mode, - bool left_is_constant_smi, - Smi* value); + Expression* right); // Assign to the given expression as if via '='. The right-hand-side value // is expected in the accumulator. diff --git a/deps/v8/src/handles-inl.h b/deps/v8/src/handles-inl.h index 1811023ffb..c0f2fda929 100644 --- a/deps/v8/src/handles-inl.h +++ b/deps/v8/src/handles-inl.h @@ -51,16 +51,6 @@ inline T* Handle::operator*() const { } -template -HandleCell::HandleCell(T* value) - : location_(HandleScope::CreateHandle(value)) { } - - -template -HandleCell::HandleCell(Handle value) - : location_(HandleScope::CreateHandle(*value)) { } - - #ifdef DEBUG inline NoHandleAllocation::NoHandleAllocation() { v8::ImplementationUtilities::HandleScopeData* current = diff --git a/deps/v8/src/handles.cc b/deps/v8/src/handles.cc index 8b2f95b521..05c81bb3f5 100644 --- a/deps/v8/src/handles.cc +++ b/deps/v8/src/handles.cc @@ -242,17 +242,21 @@ Handle SetPrototype(Handle function, Handle SetProperty(Handle object, Handle key, Handle value, - PropertyAttributes attributes) { - CALL_HEAP_FUNCTION(object->SetProperty(*key, *value, attributes), Object); + PropertyAttributes attributes, + StrictModeFlag strict) { + CALL_HEAP_FUNCTION(object->SetProperty(*key, *value, attributes, strict), + Object); } Handle SetProperty(Handle object, Handle key, Handle value, - PropertyAttributes attributes) { + PropertyAttributes attributes, + StrictModeFlag strict) { CALL_HEAP_FUNCTION( - Runtime::SetObjectProperty(object, key, value, attributes), Object); + Runtime::SetObjectProperty(object, key, value, attributes, strict), + Object); } @@ -304,10 +308,12 @@ void SetLocalPropertyNoThrow(Handle object, Handle SetPropertyWithInterceptor(Handle object, Handle key, Handle value, - PropertyAttributes attributes) { + PropertyAttributes attributes, + StrictModeFlag strict) { CALL_HEAP_FUNCTION(object->SetPropertyWithInterceptor(*key, *value, - attributes), + attributes, + strict), Object); } diff --git a/deps/v8/src/handles.h b/deps/v8/src/handles.h index 8f1664bb4e..9d3588bf9a 100644 --- a/deps/v8/src/handles.h +++ b/deps/v8/src/handles.h @@ -93,55 +93,6 @@ class Handle { }; -// A handle-scope based variable. The value stored in the variable can change -// over time. The value stored in the variable at any time is a root -// for garbage collection. -// The variable is backed by the current HandleScope. -template -class HandleCell { - public: - // Create a new HandleCell holding the given value. - explicit HandleCell(Handle value); - explicit HandleCell(T* value); - - // Create an alias of an existing HandleCell. - explicit HandleCell(const HandleCell& value) - : location_(value.location_) { } - - INLINE(T* operator->() const) { return operator*(); } - INLINE(T* operator*() const) { - return *location_; - } - INLINE(void operator=(T* value)) { - *location_ = value; - } - INLINE(void operator=(Handle value)) { - *location_ = *value; - } - INLINE(void operator=(const HandleCell& value)) { - *location_ = *value.location_; - } - - // Extract the value of the variable and cast it to a give type. - // This is typically used for calling methods on a more specialized type. - template - inline S* cast() { - S::cast(*location_); - return *reinterpret_cast(location_); - } - - Handle ToHandle() const { - return Handle(*location_); - } - - private: - // Prevent implicit constructor from being created. - HandleCell(); - - T** location_; -}; - - // 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 @@ -161,15 +112,7 @@ class HandleScope { } ~HandleScope() { - current_.next = prev_next_; - current_.level--; - if (current_.limit != prev_limit_) { - current_.limit = prev_limit_; - DeleteExtensions(); - } -#ifdef DEBUG - ZapRange(prev_next_, prev_limit_); -#endif + CloseScope(); } // Counts the number of allocated handles. @@ -197,6 +140,26 @@ class HandleScope { static Address current_limit_address(); static Address current_level_address(); + // 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 + Handle CloseAndEscape(Handle handle_value) { + T* value = *handle_value; + // Throw away all handles in the current scope. + CloseScope(); + // Allocate one handle in the parent scope. + ASSERT(current_.level > 0); + Handle result(CreateHandle(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; + } + private: // Prevent heap allocation or illegal handle scopes. HandleScope(const HandleScope&); @@ -204,9 +167,23 @@ class HandleScope { void* operator new(size_t size); void operator delete(void* size_t); + inline void CloseScope() { + current_.next = prev_next_; + current_.level--; + if (current_.limit != prev_limit_) { + current_.limit = prev_limit_; + DeleteExtensions(); + } +#ifdef DEBUG + ZapRange(prev_next_, prev_limit_); +#endif + } + static v8::ImplementationUtilities::HandleScopeData current_; - Object** const prev_next_; - Object** const prev_limit_; + // Holds values on entry. The prev_next_ value is never NULL + // on_entry, but is set to NULL when this scope is closed. + Object** prev_next_; + Object** prev_limit_; // Extend the handle scope making room for more handles. static internal::Object** Extend(); @@ -246,12 +223,14 @@ Handle FlattenGetString(Handle str); Handle SetProperty(Handle object, Handle key, Handle value, - PropertyAttributes attributes); + PropertyAttributes attributes, + StrictModeFlag strict); Handle SetProperty(Handle object, Handle key, Handle value, - PropertyAttributes attributes); + PropertyAttributes attributes, + StrictModeFlag strict); Handle ForceSetProperty(Handle object, Handle key, @@ -282,7 +261,8 @@ void SetLocalPropertyNoThrow(Handle object, Handle SetPropertyWithInterceptor(Handle object, Handle key, Handle value, - PropertyAttributes attributes); + PropertyAttributes attributes, + StrictModeFlag strict); Handle SetElement(Handle object, uint32_t index, diff --git a/deps/v8/src/heap-profiler.cc b/deps/v8/src/heap-profiler.cc index 732d2f4102..07b631fa73 100644 --- a/deps/v8/src/heap-profiler.cc +++ b/deps/v8/src/heap-profiler.cc @@ -911,22 +911,27 @@ static JSObjectsCluster HeapObjectAsCluster(HeapObject* object) { class CountingRetainersIterator { public: CountingRetainersIterator(const JSObjectsCluster& child_cluster, + HeapEntriesAllocator* allocator, HeapEntriesMap* map) - : child_(ClusterAsHeapObject(child_cluster)), map_(map) { + : child_(ClusterAsHeapObject(child_cluster)), + allocator_(allocator), + map_(map) { if (map_->Map(child_) == NULL) - map_->Pair(child_, HeapEntriesMap::kHeapEntryPlaceholder); + map_->Pair(child_, allocator_, HeapEntriesMap::kHeapEntryPlaceholder); } void Call(const JSObjectsCluster& cluster, const NumberAndSizeInfo& number_and_size) { if (map_->Map(ClusterAsHeapObject(cluster)) == NULL) map_->Pair(ClusterAsHeapObject(cluster), + allocator_, HeapEntriesMap::kHeapEntryPlaceholder); map_->CountReference(ClusterAsHeapObject(cluster), child_); } private: HeapObject* child_; + HeapEntriesAllocator* allocator_; HeapEntriesMap* map_; }; @@ -934,6 +939,7 @@ class CountingRetainersIterator { class AllocatingRetainersIterator { public: AllocatingRetainersIterator(const JSObjectsCluster& child_cluster, + HeapEntriesAllocator*, HeapEntriesMap* map) : child_(ClusterAsHeapObject(child_cluster)), map_(map) { child_entry_ = map_->Map(child_); @@ -966,8 +972,9 @@ template class AggregatingRetainerTreeIterator { public: explicit AggregatingRetainerTreeIterator(ClustersCoarser* coarser, + HeapEntriesAllocator* allocator, HeapEntriesMap* map) - : coarser_(coarser), map_(map) { + : coarser_(coarser), allocator_(allocator), map_(map) { } void Call(const JSObjectsCluster& cluster, JSObjectsClusterTree* tree) { @@ -981,25 +988,28 @@ class AggregatingRetainerTreeIterator { tree->ForEach(&retainers_aggregator); tree_to_iterate = &dest_tree_; } - RetainersIterator iterator(cluster, map_); + RetainersIterator iterator(cluster, allocator_, map_); tree_to_iterate->ForEach(&iterator); } private: ClustersCoarser* coarser_; + HeapEntriesAllocator* allocator_; HeapEntriesMap* map_; }; -class AggregatedRetainerTreeAllocator { +class AggregatedRetainerTreeAllocator : public HeapEntriesAllocator { public: AggregatedRetainerTreeAllocator(HeapSnapshot* snapshot, int* root_child_index) : snapshot_(snapshot), root_child_index_(root_child_index) { } + ~AggregatedRetainerTreeAllocator() { } - HeapEntry* GetEntry( - HeapObject* obj, int children_count, int retainers_count) { + HeapEntry* AllocateEntry( + HeapThing ptr, int children_count, int retainers_count) { + HeapObject* obj = reinterpret_cast(ptr); JSObjectsCluster cluster = HeapObjectAsCluster(obj); const char* name = cluster.GetSpecialCaseName(); if (name == NULL) { @@ -1018,12 +1028,13 @@ class AggregatedRetainerTreeAllocator { template void AggregatedHeapSnapshotGenerator::IterateRetainers( - HeapEntriesMap* entries_map) { + HeapEntriesAllocator* allocator, HeapEntriesMap* entries_map) { RetainerHeapProfile* p = agg_snapshot_->js_retainer_profile(); AggregatingRetainerTreeIterator agg_ret_iter_1( - p->coarser(), entries_map); + p->coarser(), allocator, entries_map); p->retainers_tree()->ForEach(&agg_ret_iter_1); - AggregatingRetainerTreeIterator agg_ret_iter_2(NULL, entries_map); + AggregatingRetainerTreeIterator agg_ret_iter_2( + NULL, allocator, entries_map); p->aggregator()->output_tree().ForEach(&agg_ret_iter_2); } @@ -1042,7 +1053,9 @@ void AggregatedHeapSnapshotGenerator::FillHeapSnapshot(HeapSnapshot* snapshot) { agg_snapshot_->js_cons_profile()->ForEach(&counting_cons_iter); histogram_entities_count += counting_cons_iter.entities_count(); HeapEntriesMap entries_map; - IterateRetainers(&entries_map); + int root_child_index = 0; + AggregatedRetainerTreeAllocator allocator(snapshot, &root_child_index); + IterateRetainers(&allocator, &entries_map); histogram_entities_count += entries_map.entries_count(); histogram_children_count += entries_map.total_children_count(); histogram_retainers_count += entries_map.total_retainers_count(); @@ -1056,10 +1069,7 @@ void AggregatedHeapSnapshotGenerator::FillHeapSnapshot(HeapSnapshot* snapshot) { snapshot->AllocateEntries(histogram_entities_count, histogram_children_count, histogram_retainers_count); - snapshot->AddEntry(HeapSnapshot::kInternalRootObject, - root_children_count, - 0); - int root_child_index = 0; + snapshot->AddRootEntry(root_children_count); for (int i = FIRST_NONSTRING_TYPE; i <= kAllStringsType; ++i) { if (agg_snapshot_->info()[i].bytes() > 0) { AddEntryFromAggregatedSnapshot(snapshot, @@ -1075,11 +1085,10 @@ void AggregatedHeapSnapshotGenerator::FillHeapSnapshot(HeapSnapshot* snapshot) { AllocatingConstructorHeapProfileIterator alloc_cons_iter( snapshot, &root_child_index); agg_snapshot_->js_cons_profile()->ForEach(&alloc_cons_iter); - AggregatedRetainerTreeAllocator allocator(snapshot, &root_child_index); - entries_map.UpdateEntries(&allocator); + entries_map.AllocateEntries(); // Fill up references. - IterateRetainers(&entries_map); + IterateRetainers(&allocator, &entries_map); snapshot->SetDominatorsToSelf(); } diff --git a/deps/v8/src/heap-profiler.h b/deps/v8/src/heap-profiler.h index 90c664edee..20ba457c5b 100644 --- a/deps/v8/src/heap-profiler.h +++ b/deps/v8/src/heap-profiler.h @@ -340,6 +340,7 @@ class AggregatedHeapSnapshot { class HeapEntriesMap; +class HeapEntriesAllocator; class HeapSnapshot; class AggregatedHeapSnapshotGenerator { @@ -354,7 +355,8 @@ class AggregatedHeapSnapshotGenerator { void CalculateStringsStats(); void CollectStats(HeapObject* obj); template - void IterateRetainers(HeapEntriesMap* entries_map); + void IterateRetainers( + HeapEntriesAllocator* allocator, HeapEntriesMap* entries_map); AggregatedHeapSnapshot* agg_snapshot_; }; diff --git a/deps/v8/src/heap.cc b/deps/v8/src/heap.cc index 1fadec3831..34ab9aafcf 100644 --- a/deps/v8/src/heap.cc +++ b/deps/v8/src/heap.cc @@ -844,8 +844,6 @@ void Heap::MarkCompactPrologue(bool is_compacting) { ContextSlotCache::Clear(); DescriptorLookupCache::Clear(); - RuntimeProfiler::MarkCompactPrologue(is_compacting); - CompilationCache::MarkCompactPrologue(); CompletelyClearInstanceofCache(); @@ -1056,20 +1054,13 @@ void Heap::Scavenge() { // Scavenge object reachable from the global contexts list directly. scavenge_visitor.VisitPointer(BitCast(&global_contexts_list_)); - // Scavenge objects reachable from the runtime-profiler sampler - // window directly. - Object** sampler_window_address = RuntimeProfiler::SamplerWindowAddress(); - int sampler_window_size = RuntimeProfiler::SamplerWindowSize(); - scavenge_visitor.VisitPointers( - sampler_window_address, - sampler_window_address + sampler_window_size); - new_space_front = DoScavenge(&scavenge_visitor, new_space_front); UpdateNewSpaceReferencesInExternalStringTable( &UpdateNewSpaceReferenceInExternalStringTableEntry); LiveObjectList::UpdateReferencesForScavengeGC(); + RuntimeProfiler::UpdateSamplesAfterScavenge(); ASSERT(new_space_front == new_space_.top()); @@ -5336,7 +5327,11 @@ void PathTracer::ProcessResults() { for (int i = 0; i < object_stack_.length(); i++) { if (i > 0) PrintF("\n |\n |\n V\n\n"); Object* obj = object_stack_[i]; +#ifdef OBJECT_PRINT obj->Print(); +#else + obj->ShortPrint(); +#endif } PrintF("=====================================\n"); } diff --git a/deps/v8/src/hydrogen.cc b/deps/v8/src/hydrogen.cc index 2de70ffa57..e40685cd69 100644 --- a/deps/v8/src/hydrogen.cc +++ b/deps/v8/src/hydrogen.cc @@ -535,24 +535,15 @@ HBasicBlock* HGraphBuilder::CreateDoWhile(IterationStatement* statement, HBasicBlock* HGraphBuilder::CreateWhile(IterationStatement* statement, - HBasicBlock* condition_entry, - HBasicBlock* exit_block, - HBasicBlock* body_exit, - HBasicBlock* break_block, HBasicBlock* loop_entry, - HBasicBlock* loop_exit) { + HBasicBlock* cond_false, + HBasicBlock* body_exit, + HBasicBlock* break_block) { if (break_block != NULL) break_block->SetJoinId(statement->ExitId()); HBasicBlock* new_exit = - CreateJoin(exit_block, break_block, statement->ExitId()); - - if (loop_entry != NULL) { - if (body_exit != NULL) body_exit->Goto(loop_entry, true); - loop_entry->SetJoinId(statement->EntryId()); - new_exit = CreateJoin(new_exit, loop_exit, statement->ExitId()); - } else { - if (body_exit != NULL) body_exit->Goto(condition_entry, true); - } - condition_entry->PostProcessLoopHeader(statement); + CreateJoin(cond_false, break_block, statement->ExitId()); + if (body_exit != NULL) body_exit->Goto(loop_entry, true); + loop_entry->PostProcessLoopHeader(statement); return new_exit; } @@ -2317,14 +2308,12 @@ HSubgraph* HGraphBuilder::CreateBranchSubgraph(HEnvironment* env) { } -HSubgraph* HGraphBuilder::CreateLoopHeaderSubgraph(HEnvironment* env) { - HSubgraph* subgraph = new HSubgraph(graph()); - HBasicBlock* block = graph()->CreateBasicBlock(); - HEnvironment* new_env = env->CopyAsLoopHeader(block); - block->SetInitialEnvironment(new_env); - subgraph->Initialize(block); - subgraph->entry_block()->AttachLoopInformation(); - return subgraph; +HBasicBlock* HGraphBuilder::CreateLoopHeader() { + HBasicBlock* header = graph()->CreateBasicBlock(); + HEnvironment* entry_env = environment()->CopyAsLoopHeader(header); + header->SetInitialEnvironment(entry_env); + header->AttachLoopInformation(); + return header; } @@ -2681,120 +2670,80 @@ void HGraphBuilder::PreProcessOsrEntry(IterationStatement* statement) { void HGraphBuilder::VisitDoWhileStatement(DoWhileStatement* stmt) { ASSERT(current_block() != NULL); PreProcessOsrEntry(stmt); + HBasicBlock* loop_entry = CreateLoopHeader(); + current_block()->Goto(loop_entry, false); + set_current_block(loop_entry); - HSubgraph* body_graph = CreateLoopHeaderSubgraph(environment()); - current_block()->Goto(body_graph->entry_block(), false); BreakAndContinueInfo break_info(stmt); { BreakAndContinueScope push(&break_info, this); - ADD_TO_SUBGRAPH(body_graph, stmt->body()); - } - HBasicBlock* body_exit = JoinContinue(stmt, - body_graph->exit_block(), - break_info.continue_block()); - body_graph->set_exit_block(body_exit); - - if (body_graph->exit_block() == NULL || stmt->cond()->ToBooleanIsTrue()) { - set_current_block(CreateEndless(stmt, - body_graph->entry_block(), - body_graph->exit_block(), - break_info.break_block())); + Visit(stmt->body()); + CHECK_BAILOUT; + } + HBasicBlock* body_exit = + JoinContinue(stmt, current_block(), break_info.continue_block()); + HBasicBlock* loop_exit = NULL; + if (body_exit == NULL || stmt->cond()->ToBooleanIsTrue()) { + loop_exit = CreateEndless(stmt, + loop_entry, + body_exit, + break_info.break_block()); } else { - HSubgraph* go_back = CreateEmptySubgraph(); - HSubgraph* exit = CreateEmptySubgraph(); - { - SubgraphScope scope(this, body_graph); - VISIT_FOR_CONTROL(stmt->cond(), - go_back->entry_block(), - exit->entry_block()); - go_back->entry_block()->SetJoinId(stmt->BackEdgeId()); - exit->entry_block()->SetJoinId(stmt->ExitId()); - } - set_current_block(CreateDoWhile(stmt, - body_graph->entry_block(), - go_back->exit_block(), - exit->exit_block(), - break_info.break_block())); + set_current_block(body_exit); + HBasicBlock* cond_true = graph()->CreateBasicBlock(); + HBasicBlock* cond_false = graph()->CreateBasicBlock(); + VISIT_FOR_CONTROL(stmt->cond(), cond_true, cond_false); + cond_true->SetJoinId(stmt->BackEdgeId()); + cond_false->SetJoinId(stmt->ExitId()); + loop_exit = CreateDoWhile(stmt, + loop_entry, + cond_true, + cond_false, + break_info.break_block()); } + set_current_block(loop_exit); } void HGraphBuilder::VisitWhileStatement(WhileStatement* stmt) { ASSERT(current_block() != NULL); PreProcessOsrEntry(stmt); + HBasicBlock* loop_entry = CreateLoopHeader(); + current_block()->Goto(loop_entry, false); + set_current_block(loop_entry); - HSubgraph* cond_graph = NULL; - HSubgraph* body_graph = NULL; - HSubgraph* exit_graph = NULL; - - // If the condition is constant true, do not generate a condition subgraph. - if (stmt->cond()->ToBooleanIsTrue()) { - body_graph = CreateLoopHeaderSubgraph(environment()); - current_block()->Goto(body_graph->entry_block(), false); - } else { - cond_graph = CreateLoopHeaderSubgraph(environment()); - current_block()->Goto(cond_graph->entry_block(), false); - body_graph = CreateEmptySubgraph(); - exit_graph = CreateEmptySubgraph(); - { - SubgraphScope scope(this, cond_graph); - VISIT_FOR_CONTROL(stmt->cond(), - body_graph->entry_block(), - exit_graph->entry_block()); - body_graph->entry_block()->SetJoinId(stmt->BodyId()); - exit_graph->entry_block()->SetJoinId(stmt->ExitId()); - } + // If the condition is constant true, do not generate a branch. + HBasicBlock* cond_false = NULL; + if (!stmt->cond()->ToBooleanIsTrue()) { + HBasicBlock* cond_true = graph()->CreateBasicBlock(); + cond_false = graph()->CreateBasicBlock(); + VISIT_FOR_CONTROL(stmt->cond(), cond_true, cond_false); + cond_true->SetJoinId(stmt->BodyId()); + cond_false->SetJoinId(stmt->ExitId()); + set_current_block(cond_true); } BreakAndContinueInfo break_info(stmt); { BreakAndContinueScope push(&break_info, this); - ADD_TO_SUBGRAPH(body_graph, stmt->body()); - } - HBasicBlock* body_exit = JoinContinue(stmt, - body_graph->exit_block(), - break_info.continue_block()); - body_graph->set_exit_block(body_exit); - - if (cond_graph != NULL) { - set_current_block(CreatePeeledWhile(stmt, - cond_graph->entry_block(), - exit_graph->exit_block(), - body_graph->exit_block(), - break_info.break_block())); - } else { - // TODO(fschneider): Implement peeling for endless loops as well. - set_current_block(CreateEndless(stmt, - body_graph->entry_block(), - body_graph->exit_block(), - break_info.break_block())); + Visit(stmt->body()); + CHECK_BAILOUT; } -} - - -HBasicBlock* HGraphBuilder::CreatePeeledWhile(IterationStatement* stmt, - HBasicBlock* condition_entry, - HBasicBlock* exit_block, - HBasicBlock* body_exit, - HBasicBlock* break_block) { - HBasicBlock* loop_entry = NULL; + HBasicBlock* body_exit = + JoinContinue(stmt, current_block(), break_info.continue_block()); HBasicBlock* loop_exit = NULL; - if (FLAG_use_peeling && body_exit != NULL && stmt != peeled_statement_) { - // Save the last peeled iteration statement to prevent infinite recursion. - IterationStatement* outer_peeled_statement = peeled_statement_; - peeled_statement_ = stmt; - HSubgraph* loop = CreateGotoSubgraph(body_exit->last_environment()); - AddToSubgraph(loop, stmt); - peeled_statement_ = outer_peeled_statement; - if (HasStackOverflow()) return NULL; - loop_entry = loop->entry_block(); - loop_exit = loop->exit_block(); + if (stmt->cond()->ToBooleanIsTrue()) { + // TODO(fschneider): Implement peeling for endless loops as well. + loop_exit = CreateEndless(stmt, + loop_entry, + body_exit, + break_info.break_block()); + } else { + loop_exit = CreateWhile(stmt, + loop_entry, + cond_false, + body_exit, + break_info.break_block()); } - return CreateWhile(stmt, - condition_entry, - exit_block, - body_exit, - break_block, - loop_entry, - loop_exit); + set_current_block(loop_exit); } @@ -2806,59 +2755,49 @@ void HGraphBuilder::VisitForStatement(ForStatement* stmt) { } ASSERT(current_block() != NULL); PreProcessOsrEntry(stmt); + HBasicBlock* loop_entry = CreateLoopHeader(); + current_block()->Goto(loop_entry, false); + set_current_block(loop_entry); - HSubgraph* cond_graph = NULL; - HSubgraph* body_graph = NULL; - HSubgraph* exit_graph = NULL; + HBasicBlock* cond_false = NULL; if (stmt->cond() != NULL) { - cond_graph = CreateLoopHeaderSubgraph(environment()); - current_block()->Goto(cond_graph->entry_block(), false); - body_graph = CreateEmptySubgraph(); - exit_graph = CreateEmptySubgraph(); - { - SubgraphScope scope(this, cond_graph); - VISIT_FOR_CONTROL(stmt->cond(), - body_graph->entry_block(), - exit_graph->entry_block()); - body_graph->entry_block()->SetJoinId(stmt->BodyId()); - exit_graph->entry_block()->SetJoinId(stmt->ExitId()); - } - } else { - body_graph = CreateLoopHeaderSubgraph(environment()); - current_block()->Goto(body_graph->entry_block(), false); + HBasicBlock* cond_true = graph()->CreateBasicBlock(); + cond_false = graph()->CreateBasicBlock(); + VISIT_FOR_CONTROL(stmt->cond(), cond_true, cond_false); + cond_true->SetJoinId(stmt->BodyId()); + cond_false->SetJoinId(stmt->ExitId()); + set_current_block(cond_true); } + BreakAndContinueInfo break_info(stmt); { BreakAndContinueScope push(&break_info, this); - ADD_TO_SUBGRAPH(body_graph, stmt->body()); - } - - HSubgraph* next_graph = NULL; - HBasicBlock* body_exit = JoinContinue(stmt, - body_graph->exit_block(), - break_info.continue_block()); - body_graph->set_exit_block(body_exit); - - if (stmt->next() != NULL && body_graph->exit_block() != NULL) { - next_graph = - CreateGotoSubgraph(body_graph->exit_block()->last_environment()); - body_graph->exit_block()->Goto(next_graph->entry_block()); - next_graph->entry_block()->SetJoinId(stmt->ContinueId()); - ADD_TO_SUBGRAPH(next_graph, stmt->next()); - body_graph->set_exit_block(next_graph->exit_block()); - } - - if (cond_graph != NULL) { - set_current_block(CreatePeeledWhile(stmt, - cond_graph->entry_block(), - exit_graph->exit_block(), - body_graph->exit_block(), - break_info.break_block())); + Visit(stmt->body()); + CHECK_BAILOUT; + } + HBasicBlock* body_exit = + JoinContinue(stmt, current_block(), break_info.continue_block()); + + if (stmt->next() != NULL && body_exit != NULL) { + set_current_block(body_exit); + Visit(stmt->next()); + CHECK_BAILOUT; + body_exit = current_block(); + } + + HBasicBlock* loop_exit = NULL; + if (stmt->cond() == NULL) { + loop_exit = CreateEndless(stmt, + loop_entry, + body_exit, + break_info.break_block()); } else { - set_current_block(CreateEndless(stmt, - body_graph->entry_block(), - body_graph->exit_block(), - break_info.break_block())); + loop_exit = CreateWhile(stmt, + loop_entry, + cond_false, + body_exit, + break_info.break_block()); } + set_current_block(loop_exit); } @@ -3830,9 +3769,11 @@ bool HGraphBuilder::TryArgumentsAccess(Property* expr) { HInstruction* elements = AddInstruction(new HArgumentsElements); result = new HArgumentsLength(elements); } else { + Push(graph()->GetArgumentsObject()); VisitForValue(expr->key()); if (HasStackOverflow()) return false; HValue* key = Pop(); + Drop(1); // Arguments object. HInstruction* elements = AddInstruction(new HArgumentsElements); HInstruction* length = AddInstruction(new HArgumentsLength(elements)); AddInstruction(new HBoundsCheck(key, length)); diff --git a/deps/v8/src/hydrogen.h b/deps/v8/src/hydrogen.h index bd222f490a..1ac4fc430e 100644 --- a/deps/v8/src/hydrogen.h +++ b/deps/v8/src/hydrogen.h @@ -677,12 +677,10 @@ class HGraphBuilder: public AstVisitor { HBasicBlock* second, int join_id); HBasicBlock* CreateWhile(IterationStatement* statement, - HBasicBlock* condition_entry, - HBasicBlock* exit_block, - HBasicBlock* body_exit, - HBasicBlock* break_block, HBasicBlock* loop_entry, - HBasicBlock* loop_exit); + HBasicBlock* cond_false, + HBasicBlock* body_exit, + HBasicBlock* break_block); HBasicBlock* CreateDoWhile(IterationStatement* statement, HBasicBlock* body_entry, HBasicBlock* go_back, @@ -692,11 +690,6 @@ class HGraphBuilder: public AstVisitor { HBasicBlock* body_entry, HBasicBlock* body_exit, HBasicBlock* break_block); - HBasicBlock* CreatePeeledWhile(IterationStatement* stmt, - HBasicBlock* condition_entry, - HBasicBlock* exit_block, - HBasicBlock* body_exit, - HBasicBlock* break_block); HBasicBlock* JoinContinue(IterationStatement* statement, HBasicBlock* exit_block, HBasicBlock* continue_block); @@ -746,7 +739,7 @@ class HGraphBuilder: public AstVisitor { HSubgraph* CreateEmptySubgraph(); HSubgraph* CreateGotoSubgraph(HEnvironment* env); HSubgraph* CreateBranchSubgraph(HEnvironment* env); - HSubgraph* CreateLoopHeaderSubgraph(HEnvironment* env); + HBasicBlock* CreateLoopHeader(); HSubgraph* CreateInlinedSubgraph(HEnvironment* outer, Handle target, FunctionLiteral* function); diff --git a/deps/v8/src/ia32/code-stubs-ia32.cc b/deps/v8/src/ia32/code-stubs-ia32.cc index cb05c38247..7efa934086 100644 --- a/deps/v8/src/ia32/code-stubs-ia32.cc +++ b/deps/v8/src/ia32/code-stubs-ia32.cc @@ -3399,7 +3399,7 @@ void MathPowStub::Generate(MacroAssembler* masm) { __ test(edx, Immediate(kSmiTagMask)); __ j(not_zero, &base_nonsmi); - // Optimized version when both exponent and base is a smi. + // Optimized version when both exponent and base are smis. Label powi; __ SmiUntag(edx); __ cvtsi2sd(xmm0, Operand(edx)); @@ -3438,7 +3438,6 @@ void MathPowStub::Generate(MacroAssembler* masm) { __ j(not_carry, &no_multiply); __ mulsd(xmm1, xmm0); __ bind(&no_multiply); - __ test(eax, Operand(eax)); __ mulsd(xmm0, xmm0); __ j(not_zero, &while_true); @@ -3525,7 +3524,7 @@ void MathPowStub::Generate(MacroAssembler* masm) { __ AllocateHeapNumber(ecx, eax, edx, &call_runtime); __ movdbl(FieldOperand(ecx, HeapNumber::kValueOffset), xmm1); __ mov(eax, ecx); - __ ret(2); + __ ret(2 * kPointerSize); __ bind(&call_runtime); __ TailCallRuntime(Runtime::kMath_pow_cfunction, 2, 1); diff --git a/deps/v8/src/ia32/codegen-ia32.cc b/deps/v8/src/ia32/codegen-ia32.cc index 770ec0b721..3a2753d27c 100644 --- a/deps/v8/src/ia32/codegen-ia32.cc +++ b/deps/v8/src/ia32/codegen-ia32.cc @@ -3526,7 +3526,8 @@ void CodeGenerator::DeclareGlobals(Handle pairs) { frame_->EmitPush(esi); // The context is the first argument. frame_->EmitPush(Immediate(pairs)); frame_->EmitPush(Immediate(Smi::FromInt(is_eval() ? 1 : 0))); - Result ignored = frame_->CallRuntime(Runtime::kDeclareGlobals, 3); + frame_->EmitPush(Immediate(Smi::FromInt(strict_mode_flag()))); + Result ignored = frame_->CallRuntime(Runtime::kDeclareGlobals, 4); // Return value is ignored. } @@ -5259,7 +5260,8 @@ void CodeGenerator::StoreToSlot(Slot* slot, InitState init_state) { // by initialization. value = frame_->CallRuntime(Runtime::kInitializeConstContextSlot, 3); } else { - value = frame_->CallRuntime(Runtime::kStoreContextSlot, 3); + frame_->Push(Smi::FromInt(strict_mode_flag())); + value = frame_->CallRuntime(Runtime::kStoreContextSlot, 4); } // Storing a variable must keep the (new) value on the expression // stack. This is necessary for compiling chained assignment @@ -5618,8 +5620,9 @@ void CodeGenerator::VisitObjectLiteral(ObjectLiteral* node) { Load(property->key()); Load(property->value()); if (property->emit_store()) { + frame_->Push(Smi::FromInt(NONE)); // PropertyAttributes // Ignore the result. - Result ignored = frame_->CallRuntime(Runtime::kSetProperty, 3); + Result ignored = frame_->CallRuntime(Runtime::kSetProperty, 4); } else { frame_->Drop(3); } @@ -8310,6 +8313,7 @@ void CodeGenerator::VisitUnaryOperation(UnaryOperation* node) { switch (op) { case Token::SUB: { __ neg(value.reg()); + frame_->Push(&value); if (node->no_negative_zero()) { // -MIN_INT is MIN_INT with the overflow flag set. unsafe_bailout_->Branch(overflow); @@ -8322,17 +8326,18 @@ void CodeGenerator::VisitUnaryOperation(UnaryOperation* node) { } case Token::BIT_NOT: { __ not_(value.reg()); + frame_->Push(&value); break; } case Token::ADD: { // Unary plus has no effect on int32 values. + frame_->Push(&value); break; } default: UNREACHABLE(); break; } - frame_->Push(&value); } else { Load(node->expression()); bool can_overwrite = node->expression()->ResultOverwriteAllowed(); @@ -9468,11 +9473,13 @@ class DeferredReferenceSetKeyedValue: public DeferredCode { DeferredReferenceSetKeyedValue(Register value, Register key, Register receiver, - Register scratch) + Register scratch, + StrictModeFlag strict_mode) : value_(value), key_(key), receiver_(receiver), - scratch_(scratch) { + scratch_(scratch), + strict_mode_(strict_mode) { set_comment("[ DeferredReferenceSetKeyedValue"); } @@ -9486,6 +9493,7 @@ class DeferredReferenceSetKeyedValue: public DeferredCode { Register receiver_; Register scratch_; Label patch_site_; + StrictModeFlag strict_mode_; }; @@ -9544,7 +9552,9 @@ void DeferredReferenceSetKeyedValue::Generate() { } // Call the IC stub. - Handle ic(Builtins::builtin(Builtins::KeyedStoreIC_Initialize)); + Handle ic(Builtins::builtin( + (strict_mode_ == kStrictMode) ? Builtins::KeyedStoreIC_Initialize_Strict + : Builtins::KeyedStoreIC_Initialize)); __ call(ic, RelocInfo::CODE_TARGET); // The delta from the start of the map-compare instruction to the // test instruction. We use masm_-> directly here instead of the @@ -9906,7 +9916,8 @@ Result CodeGenerator::EmitKeyedStore(StaticType* key_type) { new DeferredReferenceSetKeyedValue(result.reg(), key.reg(), receiver.reg(), - tmp.reg()); + tmp.reg(), + strict_mode_flag()); // Check that the receiver is not a smi. __ test(receiver.reg(), Immediate(kSmiTagMask)); @@ -9961,7 +9972,7 @@ Result CodeGenerator::EmitKeyedStore(StaticType* key_type) { deferred->BindExit(); } else { - result = frame()->CallKeyedStoreIC(); + result = frame()->CallKeyedStoreIC(strict_mode_flag()); // Make sure 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 diff --git a/deps/v8/src/ia32/full-codegen-ia32.cc b/deps/v8/src/ia32/full-codegen-ia32.cc index 4255347bad..67e0e8fac9 100644 --- a/deps/v8/src/ia32/full-codegen-ia32.cc +++ b/deps/v8/src/ia32/full-codegen-ia32.cc @@ -322,23 +322,6 @@ void FullCodeGenerator::EmitReturnSequence() { } -FullCodeGenerator::ConstantOperand FullCodeGenerator::GetConstantOperand( - Token::Value op, Expression* left, Expression* right) { - ASSERT(ShouldInlineSmiCase(op)); - if (op == Token::DIV || op == Token::MOD || op == Token::MUL) { - // We never generate inlined constant smi operations for these. - return kNoConstants; - } else if (right->IsSmiLiteral()) { - return kRightConstant; - } else if (left->IsSmiLiteral() && !Token::IsShiftOp(op)) { - // Don't inline shifts with constant left hand side. - return kLeftConstant; - } else { - return kNoConstants; - } -} - - void FullCodeGenerator::EffectContext::Plug(Slot* slot) const { } @@ -741,7 +724,9 @@ void FullCodeGenerator::EmitDeclaration(Variable* variable, prop->key()->AsLiteral()->handle()->IsSmi()); __ Set(ecx, Immediate(prop->key()->AsLiteral()->handle())); - Handle ic(Builtins::builtin(Builtins::KeyedStoreIC_Initialize)); + Handle ic(Builtins::builtin(is_strict() + ? Builtins::KeyedStoreIC_Initialize_Strict + : Builtins::KeyedStoreIC_Initialize)); EmitCallIC(ic, RelocInfo::CODE_TARGET); } } @@ -758,7 +743,8 @@ void FullCodeGenerator::DeclareGlobals(Handle pairs) { __ push(esi); // The context is the first argument. __ push(Immediate(pairs)); __ push(Immediate(Smi::FromInt(is_eval() ? 1 : 0))); - __ CallRuntime(Runtime::kDeclareGlobals, 3); + __ push(Immediate(Smi::FromInt(strict_mode_flag()))); + __ CallRuntime(Runtime::kDeclareGlobals, 4); // Return value is ignored. } @@ -1380,7 +1366,9 @@ void FullCodeGenerator::VisitObjectLiteral(ObjectLiteral* expr) { VisitForAccumulatorValue(value); __ mov(ecx, Immediate(key->handle())); __ mov(edx, Operand(esp, 0)); - Handle ic(Builtins::builtin(Builtins::StoreIC_Initialize)); + Handle ic(Builtins::builtin( + is_strict() ? Builtins::StoreIC_Initialize_Strict + : Builtins::StoreIC_Initialize)); EmitCallIC(ic, RelocInfo::CODE_TARGET); PrepareForBailoutForId(key->id(), NO_REGISTERS); } else { @@ -1394,7 +1382,8 @@ void FullCodeGenerator::VisitObjectLiteral(ObjectLiteral* expr) { VisitForStackValue(key); VisitForStackValue(value); if (property->emit_store()) { - __ CallRuntime(Runtime::kSetProperty, 3); + __ push(Immediate(Smi::FromInt(NONE))); // PropertyAttributes + __ CallRuntime(Runtime::kSetProperty, 4); } else { __ Drop(3); } @@ -1572,14 +1561,8 @@ void FullCodeGenerator::VisitAssignment(Assignment* expr) { } Token::Value op = expr->binary_op(); - ConstantOperand constant = ShouldInlineSmiCase(op) - ? GetConstantOperand(op, expr->target(), expr->value()) - : kNoConstants; - ASSERT(constant == kRightConstant || constant == kNoConstants); - if (constant == kNoConstants) { - __ push(eax); // Left operand goes on the stack. - VisitForAccumulatorValue(expr->value()); - } + __ push(eax); // Left operand goes on the stack. + VisitForAccumulatorValue(expr->value()); OverwriteMode mode = expr->value()->ResultOverwriteAllowed() ? OVERWRITE_RIGHT @@ -1591,8 +1574,7 @@ void FullCodeGenerator::VisitAssignment(Assignment* expr) { op, mode, expr->target(), - expr->value(), - constant); + expr->value()); } else { EmitBinaryOp(op, mode); } @@ -1640,220 +1622,11 @@ void FullCodeGenerator::EmitKeyedPropertyLoad(Property* prop) { } -void FullCodeGenerator::EmitConstantSmiAdd(Expression* expr, - OverwriteMode mode, - bool left_is_constant_smi, - Smi* value) { - NearLabel call_stub, done; - // Optimistically add smi value with unknown object. If result overflows or is - // not a smi then we had either a smi overflow or added a smi with a tagged - // pointer. - __ add(Operand(eax), Immediate(value)); - __ j(overflow, &call_stub); - JumpPatchSite patch_site(masm_); - patch_site.EmitJumpIfSmi(eax, &done); - - // Undo the optimistic add operation and call the shared stub. - __ bind(&call_stub); - __ sub(Operand(eax), Immediate(value)); - TypeRecordingBinaryOpStub stub(Token::ADD, mode); - if (left_is_constant_smi) { - __ mov(edx, Immediate(value)); - } else { - __ mov(edx, eax); - __ mov(eax, Immediate(value)); - } - EmitCallIC(stub.GetCode(), &patch_site); - - __ bind(&done); - context()->Plug(eax); -} - - -void FullCodeGenerator::EmitConstantSmiSub(Expression* expr, - OverwriteMode mode, - bool left_is_constant_smi, - Smi* value) { - NearLabel call_stub, done; - // Optimistically subtract smi value with unknown object. If result overflows - // or is not a smi then we had either a smi overflow or added a smi with a - // tagged pointer. - if (left_is_constant_smi) { - __ mov(ecx, eax); - __ mov(eax, Immediate(value)); - __ sub(Operand(eax), ecx); - } else { - __ sub(Operand(eax), Immediate(value)); - } - __ j(overflow, &call_stub); - JumpPatchSite patch_site(masm_); - patch_site.EmitJumpIfSmi(eax, &done); - - __ bind(&call_stub); - if (left_is_constant_smi) { - __ mov(edx, Immediate(value)); - __ mov(eax, ecx); - } else { - __ add(Operand(eax), Immediate(value)); // Undo the subtraction. - __ mov(edx, eax); - __ mov(eax, Immediate(value)); - } - TypeRecordingBinaryOpStub stub(Token::SUB, mode); - EmitCallIC(stub.GetCode(), &patch_site); - - __ bind(&done); - context()->Plug(eax); -} - - -void FullCodeGenerator::EmitConstantSmiShiftOp(Expression* expr, - Token::Value op, - OverwriteMode mode, - Smi* value) { - NearLabel call_stub, smi_case, done; - int shift_value = value->value() & 0x1f; - - JumpPatchSite patch_site(masm_); - patch_site.EmitJumpIfSmi(eax, &smi_case); - - // Call stub. - __ bind(&call_stub); - __ mov(edx, eax); - __ mov(eax, Immediate(value)); - TypeRecordingBinaryOpStub stub(op, mode); - EmitCallIC(stub.GetCode(), &patch_site); - __ jmp(&done); - - // Smi case. - __ bind(&smi_case); - switch (op) { - case Token::SHL: - if (shift_value != 0) { - __ mov(edx, eax); - if (shift_value > 1) { - __ shl(edx, shift_value - 1); - } - // Convert int result to smi, checking that it is in int range. - STATIC_ASSERT(kSmiTagSize == 1); // Adjust code if not the case. - __ add(edx, Operand(edx)); - __ j(overflow, &call_stub); - __ mov(eax, edx); // Put result back into eax. - } - break; - case Token::SAR: - if (shift_value != 0) { - __ sar(eax, shift_value); - __ and_(eax, ~kSmiTagMask); - } - break; - case Token::SHR: - // SHR must return a positive value. When shifting by 0 or 1 we need to - // check that smi tagging the result will not create a negative value. - if (shift_value < 2) { - __ mov(edx, eax); - __ SmiUntag(edx); - __ shr(edx, shift_value); - __ test(edx, Immediate(0xc0000000)); - __ j(not_zero, &call_stub); - __ SmiTag(edx); - __ mov(eax, edx); // Put result back into eax. - } else { - __ SmiUntag(eax); - __ shr(eax, shift_value); - __ SmiTag(eax); - } - break; - default: - UNREACHABLE(); - } - - __ bind(&done); - context()->Plug(eax); -} - - -void FullCodeGenerator::EmitConstantSmiBitOp(Expression* expr, - Token::Value op, - OverwriteMode mode, - Smi* value) { - NearLabel smi_case, done; - - JumpPatchSite patch_site(masm_); - patch_site.EmitJumpIfSmi(eax, &smi_case); - - // The order of the arguments does not matter for bit-ops with a - // constant operand. - __ mov(edx, Immediate(value)); - TypeRecordingBinaryOpStub stub(op, mode); - EmitCallIC(stub.GetCode(), &patch_site); - __ jmp(&done); - - // Smi case. - __ bind(&smi_case); - switch (op) { - case Token::BIT_OR: - __ or_(Operand(eax), Immediate(value)); - break; - case Token::BIT_XOR: - __ xor_(Operand(eax), Immediate(value)); - break; - case Token::BIT_AND: - __ and_(Operand(eax), Immediate(value)); - break; - default: - UNREACHABLE(); - } - - __ bind(&done); - context()->Plug(eax); -} - - -void FullCodeGenerator::EmitConstantSmiBinaryOp(Expression* expr, - Token::Value op, - OverwriteMode mode, - bool left_is_constant_smi, - Smi* value) { - switch (op) { - case Token::BIT_OR: - case Token::BIT_XOR: - case Token::BIT_AND: - EmitConstantSmiBitOp(expr, op, mode, value); - break; - case Token::SHL: - case Token::SAR: - case Token::SHR: - ASSERT(!left_is_constant_smi); - EmitConstantSmiShiftOp(expr, op, mode, value); - break; - case Token::ADD: - EmitConstantSmiAdd(expr, mode, left_is_constant_smi, value); - break; - case Token::SUB: - EmitConstantSmiSub(expr, mode, left_is_constant_smi, value); - break; - default: - UNREACHABLE(); - } -} - - void FullCodeGenerator::EmitInlineSmiBinaryOp(Expression* expr, Token::Value op, OverwriteMode mode, Expression* left, - Expression* right, - ConstantOperand constant) { - if (constant == kRightConstant) { - Smi* value = Smi::cast(*right->AsLiteral()->handle()); - EmitConstantSmiBinaryOp(expr, op, mode, false, value); - return; - } else if (constant == kLeftConstant) { - Smi* value = Smi::cast(*left->AsLiteral()->handle()); - EmitConstantSmiBinaryOp(expr, op, mode, true, value); - return; - } - + Expression* right) { // Do combined smi check of the operands. Left operand is on the // stack. Right operand is in eax. NearLabel done, smi_case, stub_call; @@ -1985,7 +1758,9 @@ void FullCodeGenerator::EmitAssignment(Expression* expr, int bailout_ast_id) { __ mov(edx, eax); __ pop(eax); // Restore value. __ mov(ecx, prop->key()->AsLiteral()->handle()); - Handle ic(Builtins::builtin(Builtins::StoreIC_Initialize)); + Handle ic(Builtins::builtin( + is_strict() ? Builtins::StoreIC_Initialize_Strict + : Builtins::StoreIC_Initialize)); EmitCallIC(ic, RelocInfo::CODE_TARGET); break; } @@ -2006,7 +1781,9 @@ void FullCodeGenerator::EmitAssignment(Expression* expr, int bailout_ast_id) { __ pop(edx); } __ pop(eax); // Restore value. - Handle ic(Builtins::builtin(Builtins::KeyedStoreIC_Initialize)); + Handle ic(Builtins::builtin( + is_strict() ? Builtins::KeyedStoreIC_Initialize_Strict + : Builtins::KeyedStoreIC_Initialize)); EmitCallIC(ic, RelocInfo::CODE_TARGET); break; } @@ -2101,7 +1878,8 @@ void FullCodeGenerator::EmitVariableAssignment(Variable* var, __ push(eax); // Value. __ push(esi); // Context. __ push(Immediate(var->name())); - __ CallRuntime(Runtime::kStoreContextSlot, 3); + __ push(Immediate(Smi::FromInt(strict_mode_flag()))); + __ CallRuntime(Runtime::kStoreContextSlot, 4); break; } } @@ -2132,7 +1910,9 @@ void FullCodeGenerator::EmitNamedPropertyAssignment(Assignment* expr) { } else { __ pop(edx); } - Handle ic(Builtins::builtin(Builtins::StoreIC_Initialize)); + Handle ic(Builtins::builtin( + is_strict() ? Builtins::StoreIC_Initialize_Strict + : Builtins::StoreIC_Initialize)); EmitCallIC(ic, RelocInfo::CODE_TARGET); // If the assignment ends an initialization block, revert to fast case. @@ -2170,7 +1950,9 @@ void FullCodeGenerator::EmitKeyedPropertyAssignment(Assignment* expr) { } // Record source code position before IC call. SetSourcePosition(expr->position()); - Handle ic(Builtins::builtin(Builtins::KeyedStoreIC_Initialize)); + Handle ic(Builtins::builtin( + is_strict() ? Builtins::KeyedStoreIC_Initialize_Strict + : Builtins::KeyedStoreIC_Initialize)); EmitCallIC(ic, RelocInfo::CODE_TARGET); // If the assignment ends an initialization block, revert to fast case. @@ -2283,6 +2065,27 @@ void FullCodeGenerator::EmitCallWithStub(Call* expr) { } +void FullCodeGenerator::EmitResolvePossiblyDirectEval(ResolveEvalFlag flag, + 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(Factory::undefined_value())); + } + + // Push the receiver of the enclosing function. + __ push(Operand(ebp, (2 + scope()->num_parameters()) * kPointerSize)); + + // Push the strict mode flag. + __ push(Immediate(Smi::FromInt(strict_mode_flag()))); + + __ CallRuntime(flag == SKIP_CONTEXT_LOOKUP + ? Runtime::kResolvePossiblyDirectEvalNoLookup + : Runtime::kResolvePossiblyDirectEval, 4); +} + + void FullCodeGenerator::VisitCall(Call* expr) { #ifdef DEBUG // We want to verify that RecordJSReturnSite gets called on all paths @@ -2311,21 +2114,30 @@ void FullCodeGenerator::VisitCall(Call* expr) { VisitForStackValue(args->at(i)); } - // Push copy of the function - found below the arguments. - __ push(Operand(esp, (arg_count + 1) * kPointerSize)); - - // 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(Factory::undefined_value())); + // If we know that eval can only be shadowed by eval-introduced + // variables we attempt to load the global eval function directly + // in generated code. If we succeed, there is no need to perform a + // context lookup in the runtime system. + Label done; + if (var->AsSlot() != NULL && var->mode() == Variable::DYNAMIC_GLOBAL) { + Label slow; + EmitLoadGlobalSlotCheckExtensions(var->AsSlot(), + NOT_INSIDE_TYPEOF, + &slow); + // Push the function and resolve eval. + __ push(eax); + EmitResolvePossiblyDirectEval(SKIP_CONTEXT_LOOKUP, arg_count); + __ jmp(&done); + __ bind(&slow); } - // Push the receiver of the enclosing function and do runtime call. - __ push(Operand(ebp, (2 + scope()->num_parameters()) * kPointerSize)); - // Push the strict mode flag. - __ push(Immediate(Smi::FromInt(strict_mode_flag()))); - __ CallRuntime(Runtime::kResolvePossiblyDirectEval, 4); + // Push copy of the function (found below the arguments) and + // resolve eval. + __ push(Operand(esp, (arg_count + 1) * kPointerSize)); + EmitResolvePossiblyDirectEval(PERFORM_CONTEXT_LOOKUP, arg_count); + if (done.is_linked()) { + __ bind(&done); + } // The runtime call returns a pair of values in eax (function) and // edx (receiver). Touch up the stack with the right values. @@ -3418,7 +3230,7 @@ void FullCodeGenerator::EmitGetCachedArrayIndex(ZoneList* args) { void FullCodeGenerator::EmitFastAsciiArrayJoin(ZoneList* args) { Label bailout, done, one_char_separator, long_separator, - non_trivial_array, not_size_one_array, loop, loop_condition, + non_trivial_array, not_size_one_array, loop, loop_1, loop_1_condition, loop_2, loop_2_entry, loop_3, loop_3_entry; ASSERT(args->length() == 2); @@ -3460,7 +3272,7 @@ void FullCodeGenerator::EmitFastAsciiArrayJoin(ZoneList* args) { // If the array has length zero, return the empty string. __ mov(array_length, FieldOperand(array, JSArray::kLengthOffset)); - __ sar(array_length, 1); + __ SmiUntag(array_length); __ j(not_zero, &non_trivial_array); __ mov(result_operand, Factory::empty_string()); __ jmp(&done); @@ -3483,14 +3295,15 @@ void FullCodeGenerator::EmitFastAsciiArrayJoin(ZoneList* args) { // Loop condition: while (index < length). // Live loop registers: index, array_length, string, // scratch, string_length, elements. - __ jmp(&loop_condition); + if (FLAG_debug_code) { + __ cmp(index, Operand(array_length)); + __ Assert(less, "No empty arrays here in EmitFastAsciiArrayJoin"); + } __ bind(&loop); - __ cmp(index, Operand(array_length)); - __ j(greater_equal, &done); - - __ mov(string, FieldOperand(elements, index, - times_pointer_size, - FixedArray::kHeaderSize)); + __ mov(string, FieldOperand(elements, + index, + times_pointer_size, + FixedArray::kHeaderSize)); __ test(string, Immediate(kSmiTagMask)); __ j(zero, &bailout); __ mov(scratch, FieldOperand(string, HeapObject::kMapOffset)); @@ -3503,7 +3316,6 @@ void FullCodeGenerator::EmitFastAsciiArrayJoin(ZoneList* args) { FieldOperand(string, SeqAsciiString::kLengthOffset)); __ j(overflow, &bailout); __ add(Operand(index), Immediate(1)); - __ bind(&loop_condition); __ cmp(index, Operand(array_length)); __ j(less, &loop); @@ -3532,7 +3344,7 @@ void FullCodeGenerator::EmitFastAsciiArrayJoin(ZoneList* args) { __ movzx_b(scratch, FieldOperand(scratch, Map::kInstanceTypeOffset)); __ and_(scratch, Immediate( kIsNotStringMask | kStringEncodingMask | kStringRepresentationMask)); - __ cmp(scratch, kStringTag | kAsciiStringTag | kSeqStringTag); + __ cmp(scratch, ASCII_STRING_TYPE); __ j(not_equal, &bailout); // Add (separator length times array_length) - separator length @@ -4025,7 +3837,9 @@ void FullCodeGenerator::VisitCountOperation(CountOperation* expr) { case NAMED_PROPERTY: { __ mov(ecx, prop->key()->AsLiteral()->handle()); __ pop(edx); - Handle ic(Builtins::builtin(Builtins::StoreIC_Initialize)); + Handle ic(Builtins::builtin( + is_strict() ? Builtins::StoreIC_Initialize_Strict + : Builtins::StoreIC_Initialize)); EmitCallIC(ic, RelocInfo::CODE_TARGET); PrepareForBailoutForId(expr->AssignmentId(), TOS_REG); if (expr->is_postfix()) { @@ -4040,7 +3854,9 @@ void FullCodeGenerator::VisitCountOperation(CountOperation* expr) { case KEYED_PROPERTY: { __ pop(ecx); __ pop(edx); - Handle ic(Builtins::builtin(Builtins::KeyedStoreIC_Initialize)); + Handle ic(Builtins::builtin( + is_strict() ? Builtins::KeyedStoreIC_Initialize_Strict + : Builtins::KeyedStoreIC_Initialize)); EmitCallIC(ic, RelocInfo::CODE_TARGET); PrepareForBailoutForId(expr->AssignmentId(), TOS_REG); if (expr->is_postfix()) { diff --git a/deps/v8/src/ia32/ic-ia32.cc b/deps/v8/src/ia32/ic-ia32.cc index 73cd60df56..6b9e749629 100644 --- a/deps/v8/src/ia32/ic-ia32.cc +++ b/deps/v8/src/ia32/ic-ia32.cc @@ -761,7 +761,8 @@ void KeyedLoadIC::GenerateIndexedInterceptor(MacroAssembler* masm) { } -void KeyedStoreIC::GenerateGeneric(MacroAssembler* masm) { +void KeyedStoreIC::GenerateGeneric(MacroAssembler* masm, + StrictModeFlag strict_mode) { // ----------- S t a t e ------------- // -- eax : value // -- ecx : key @@ -801,7 +802,7 @@ void KeyedStoreIC::GenerateGeneric(MacroAssembler* masm) { // Slow case: call runtime. __ bind(&slow); - GenerateRuntimeSetProperty(masm); + GenerateRuntimeSetProperty(masm, strict_mode); // Check whether the elements is a pixel array. __ bind(&check_pixel_array); @@ -1488,7 +1489,7 @@ void KeyedLoadIC::GenerateRuntimeGetProperty(MacroAssembler* masm) { void StoreIC::GenerateMegamorphic(MacroAssembler* masm, - Code::ExtraICState extra_ic_state) { + StrictModeFlag strict_mode) { // ----------- S t a t e ------------- // -- eax : value // -- ecx : name @@ -1499,7 +1500,7 @@ void StoreIC::GenerateMegamorphic(MacroAssembler* masm, Code::Flags flags = Code::ComputeFlags(Code::STORE_IC, NOT_IN_LOOP, MONOMORPHIC, - extra_ic_state); + strict_mode); StubCache::GenerateProbe(masm, flags, edx, ecx, ebx, no_reg); // Cache miss: Jump to runtime. @@ -1617,7 +1618,8 @@ void StoreIC::GenerateNormal(MacroAssembler* masm) { } -void StoreIC::GenerateGlobalProxy(MacroAssembler* masm) { +void StoreIC::GenerateGlobalProxy(MacroAssembler* masm, + StrictModeFlag strict_mode) { // ----------- S t a t e ------------- // -- eax : value // -- ecx : name @@ -1628,14 +1630,17 @@ void StoreIC::GenerateGlobalProxy(MacroAssembler* masm) { __ push(edx); __ push(ecx); __ push(eax); - __ push(ebx); + __ 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, 3, 1); + __ TailCallRuntime(Runtime::kSetProperty, 5, 1); } -void KeyedStoreIC::GenerateRuntimeSetProperty(MacroAssembler* masm) { +void KeyedStoreIC::GenerateRuntimeSetProperty(MacroAssembler* masm, + StrictModeFlag strict_mode) { // ----------- S t a t e ------------- // -- eax : value // -- ecx : key @@ -1647,10 +1652,12 @@ void KeyedStoreIC::GenerateRuntimeSetProperty(MacroAssembler* masm) { __ push(edx); __ push(ecx); __ push(eax); - __ push(ebx); + __ 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, 3, 1); + __ TailCallRuntime(Runtime::kSetProperty, 5, 1); } diff --git a/deps/v8/src/ia32/lithium-codegen-ia32.cc b/deps/v8/src/ia32/lithium-codegen-ia32.cc index c7424a586b..0b345d31ad 100644 --- a/deps/v8/src/ia32/lithium-codegen-ia32.cc +++ b/deps/v8/src/ia32/lithium-codegen-ia32.cc @@ -588,7 +588,8 @@ void LCodeGen::PopulateDeoptimizationData(Handle code) { Handle data = Factory::NewDeoptimizationInputData(length, TENURED); - data->SetTranslationByteArray(*translations_.CreateByteArray()); + Handle translations = translations_.CreateByteArray(); + data->SetTranslationByteArray(*translations); data->SetInlinedFunctionCount(Smi::FromInt(inlined_function_count_)); Handle literals = @@ -1912,12 +1913,7 @@ void LCodeGen::DoDeferredLInstanceOfKnownGlobal(LInstanceOfKnownGlobal* instr, __ bind(&before_push_delta); __ mov(temp, Immediate(delta)); __ StoreToSafepointRegisterSlot(temp, temp); - __ mov(esi, Operand(ebp, StandardFrameConstants::kContextOffset)); - __ call(stub.GetCode(), RelocInfo::CODE_TARGET); - ASSERT_EQ(kAdditionalDelta, - masm_->SizeOfCodeGeneratedSince(&before_push_delta)); - RecordSafepointWithRegisters( - instr->pointer_map(), 0, Safepoint::kNoDeoptimizationIndex); + CallCode(stub.GetCode(), RelocInfo::CODE_TARGET, instr, false); // Put the result value into the eax slot and restore all registers. __ StoreToSafepointRegisterSlot(eax, eax); __ PopSafepointRegisters(); @@ -2786,7 +2782,9 @@ void LCodeGen::DoStoreNamedGeneric(LStoreNamedGeneric* instr) { ASSERT(ToRegister(instr->value()).is(eax)); __ mov(ecx, instr->name()); - Handle ic(Builtins::builtin(Builtins::StoreIC_Initialize)); + Handle ic(Builtins::builtin( + info_->is_strict() ? Builtins::StoreIC_Initialize_Strict + : Builtins::StoreIC_Initialize)); CallCode(ic, RelocInfo::CODE_TARGET, instr); } @@ -2854,7 +2852,9 @@ void LCodeGen::DoStoreKeyedGeneric(LStoreKeyedGeneric* instr) { ASSERT(ToRegister(instr->key()).is(ecx)); ASSERT(ToRegister(instr->value()).is(eax)); - Handle ic(Builtins::builtin(Builtins::KeyedStoreIC_Initialize)); + Handle ic(Builtins::builtin( + info_->is_strict() ? Builtins::KeyedStoreIC_Initialize_Strict + : Builtins::KeyedStoreIC_Initialize)); CallCode(ic, RelocInfo::CODE_TARGET, instr); } diff --git a/deps/v8/src/ia32/lithium-ia32.cc b/deps/v8/src/ia32/lithium-ia32.cc index 221a7aa154..ea6d41aa14 100644 --- a/deps/v8/src/ia32/lithium-ia32.cc +++ b/deps/v8/src/ia32/lithium-ia32.cc @@ -870,10 +870,18 @@ LInstruction* LChunkBuilder::DoArithmeticD(Token::Value op, ASSERT(instr->representation().IsDouble()); ASSERT(instr->left()->representation().IsDouble()); ASSERT(instr->right()->representation().IsDouble()); - LOperand* left = UseRegisterAtStart(instr->left()); - LOperand* right = UseRegisterAtStart(instr->right()); - LArithmeticD* result = new LArithmeticD(op, left, right); - return DefineSameAsFirst(result); + if (op == Token::MOD) { + LOperand* left = UseFixedDouble(instr->left(), xmm2); + LOperand* right = UseFixedDouble(instr->right(), xmm1); + LArithmeticD* result = new LArithmeticD(op, left, right); + return MarkAsCall(DefineFixedDouble(result, xmm1), instr); + + } else { + LOperand* left = UseRegisterAtStart(instr->left()); + LOperand* right = UseRegisterAtStart(instr->right()); + LArithmeticD* result = new LArithmeticD(op, left, right); + return DefineSameAsFirst(result); + } } @@ -1165,8 +1173,7 @@ LInstruction* LChunkBuilder::DoInstanceOfKnownGlobal( new LInstanceOfKnownGlobal( UseFixed(instr->value(), InstanceofStub::left()), FixedTemp(edi)); - MarkAsSaveDoubles(result); - return AssignEnvironment(AssignPointerMap(DefineFixed(result, eax))); + return MarkAsCall(DefineFixed(result, eax), instr); } diff --git a/deps/v8/src/ia32/stub-cache-ia32.cc b/deps/v8/src/ia32/stub-cache-ia32.cc index 51cc46a08e..633097af66 100644 --- a/deps/v8/src/ia32/stub-cache-ia32.cc +++ b/deps/v8/src/ia32/stub-cache-ia32.cc @@ -2552,12 +2552,13 @@ MaybeObject* StoreStubCompiler::CompileStoreInterceptor(JSObject* receiver, __ push(edx); // receiver __ push(ecx); // name __ push(eax); // value + __ push(Immediate(Smi::FromInt(strict_mode_))); __ push(ebx); // restore return address // Do tail-call to the runtime system. ExternalReference store_ic_property = ExternalReference(IC_Utility(IC::kStoreInterceptorProperty)); - __ TailCallExternalReference(store_ic_property, 3, 1); + __ TailCallExternalReference(store_ic_property, 4, 1); // Handle store cache miss. __ bind(&miss); @@ -3712,10 +3713,13 @@ MaybeObject* ExternalArrayStubCompiler::CompileKeyedStoreStub( __ push(edx); __ push(ecx); __ push(eax); - __ push(ebx); + __ push(Immediate(Smi::FromInt(NONE))); // PropertyAttributes + __ push(Immediate(Smi::FromInt( + Code::ExtractExtraICStateFromFlags(flags) & kStrictMode))); + __ push(ebx); // return address // Do tail-call to runtime routine. - __ TailCallRuntime(Runtime::kSetProperty, 3, 1); + __ TailCallRuntime(Runtime::kSetProperty, 5, 1); return GetCode(flags); } diff --git a/deps/v8/src/ia32/virtual-frame-ia32.cc b/deps/v8/src/ia32/virtual-frame-ia32.cc index 515a9fe97a..93d711e936 100644 --- a/deps/v8/src/ia32/virtual-frame-ia32.cc +++ b/deps/v8/src/ia32/virtual-frame-ia32.cc @@ -1038,9 +1038,9 @@ Result VirtualFrame::CallStoreIC(Handle name, StrictModeFlag strict_mode) { // Value and (if not contextual) receiver are on top of the frame. // The IC expects name in ecx, value in eax, and receiver in edx. - Handle ic(Builtins::builtin(strict_mode == kStrictMode - ? Builtins::StoreIC_Initialize_Strict - : Builtins::StoreIC_Initialize)); + Handle ic(Builtins::builtin( + (strict_mode == kStrictMode) ? Builtins::StoreIC_Initialize_Strict + : Builtins::StoreIC_Initialize)); Result value = Pop(); RelocInfo::Mode mode; @@ -1061,7 +1061,7 @@ Result VirtualFrame::CallStoreIC(Handle name, } -Result VirtualFrame::CallKeyedStoreIC() { +Result VirtualFrame::CallKeyedStoreIC(StrictModeFlag strict_mode) { // Value, key, and receiver are on the top of the frame. The IC // expects value in eax, key in ecx, and receiver in edx. Result value = Pop(); @@ -1105,7 +1105,9 @@ Result VirtualFrame::CallKeyedStoreIC() { receiver.Unuse(); } - Handle ic(Builtins::builtin(Builtins::KeyedStoreIC_Initialize)); + Handle ic(Builtins::builtin( + (strict_mode == kStrictMode) ? Builtins::KeyedStoreIC_Initialize_Strict + : Builtins::KeyedStoreIC_Initialize)); return RawCallCodeObject(ic, RelocInfo::CODE_TARGET); } @@ -1337,6 +1339,20 @@ void VirtualFrame::Push(Expression* expr) { } +void VirtualFrame::Push(Handle value) { + if (ConstantPoolOverflowed()) { + Result temp = cgen()->allocator()->Allocate(); + ASSERT(temp.is_valid()); + __ Set(temp.reg(), Immediate(value)); + Push(&temp); + } else { + FrameElement element = + FrameElement::ConstantElement(value, FrameElement::NOT_SYNCED); + elements_.Add(element); + } +} + + #undef __ } } // namespace v8::internal diff --git a/deps/v8/src/ia32/virtual-frame-ia32.h b/deps/v8/src/ia32/virtual-frame-ia32.h index 93362b4271..51874309d5 100644 --- a/deps/v8/src/ia32/virtual-frame-ia32.h +++ b/deps/v8/src/ia32/virtual-frame-ia32.h @@ -370,7 +370,7 @@ class VirtualFrame: public ZoneObject { // Call keyed store IC. Value, key, and receiver are found on top // of the frame. All three are dropped. - Result CallKeyedStoreIC(); + Result CallKeyedStoreIC(StrictModeFlag strict_mode); // Call call IC. Function name, arguments, and receiver are found on top // of the frame and dropped by the call. The argument count does not @@ -422,8 +422,8 @@ class VirtualFrame: public ZoneObject { inline bool ConstantPoolOverflowed(); // Push an element on the virtual frame. + void Push(Handle value); inline void Push(Register reg, TypeInfo info = TypeInfo::Unknown()); - inline void Push(Handle value); inline void Push(Smi* value); void PushUntaggedElement(Handle value); diff --git a/deps/v8/src/ic-inl.h b/deps/v8/src/ic-inl.h index 8fbc18436d..9d358eddb3 100644 --- a/deps/v8/src/ic-inl.h +++ b/deps/v8/src/ic-inl.h @@ -76,6 +76,15 @@ Code* IC::GetTargetAtAddress(Address address) { void IC::SetTargetAtAddress(Address address, Code* target) { ASSERT(target->is_inline_cache_stub() || target->is_compare_ic_stub()); +#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. + Code* old_target = GetTargetAtAddress(address); + if (old_target->kind() == Code::STORE_IC || + old_target->kind() == Code::KEYED_STORE_IC) { + ASSERT(old_target->extra_ic_state() == target->extra_ic_state()); + } +#endif Assembler::set_target_address_at(address, target->instruction_start()); } diff --git a/deps/v8/src/ic.cc b/deps/v8/src/ic.cc index 4f75ade0e8..2d666d6e8b 100644 --- a/deps/v8/src/ic.cc +++ b/deps/v8/src/ic.cc @@ -343,7 +343,7 @@ void StoreIC::Clear(Address address, Code* target) { if (target->ic_state() == UNINITIALIZED) return; ClearInlinedVersion(address); SetTargetAtAddress(address, - target->extra_ic_state() == kStoreICStrict + (target->extra_ic_state() == kStrictMode) ? initialize_stub_strict() : initialize_stub()); } @@ -366,7 +366,10 @@ void KeyedStoreIC::RestoreInlinedVersion(Address address) { void KeyedStoreIC::Clear(Address address, Code* target) { if (target->ic_state() == UNINITIALIZED) return; - SetTargetAtAddress(address, initialize_stub()); + SetTargetAtAddress(address, + (target->extra_ic_state() == kStrictMode) + ? initialize_stub_strict() + : initialize_stub()); } @@ -1227,7 +1230,8 @@ MaybeObject* KeyedLoadIC::Load(State state, if (receiver->HasExternalArrayElements()) { MaybeObject* probe = StubCache::ComputeKeyedLoadOrStoreExternalArray(*receiver, - false); + false, + kNonStrictMode); stub = probe->IsFailure() ? NULL : Code::cast(probe->ToObjectUnchecked()); } else if (receiver->HasIndexedInterceptor()) { @@ -1383,7 +1387,7 @@ static bool LookupForWrite(JSObject* object, MaybeObject* StoreIC::Store(State state, - Code::ExtraICState extra_ic_state, + StrictModeFlag strict_mode, Handle object, Handle name, Handle value) { @@ -1413,11 +1417,11 @@ MaybeObject* StoreIC::Store(State state, #ifdef DEBUG if (FLAG_trace_ic) PrintF("[StoreIC : +#length /array]\n"); #endif - Builtins::Name target = (extra_ic_state == kStoreICStrict) + Builtins::Name target = (strict_mode == kStrictMode) ? Builtins::StoreIC_ArrayLength_Strict : Builtins::StoreIC_ArrayLength; set_target(Builtins::builtin(target)); - return receiver->SetProperty(*name, *value, NONE); + return receiver->SetProperty(*name, *value, NONE, strict_mode); } // Lookup the property locally in the receiver. @@ -1441,13 +1445,15 @@ MaybeObject* StoreIC::Store(State state, // Index is an offset from the end of the object. int offset = map->instance_size() + (index * kPointerSize); if (PatchInlinedStore(address(), map, offset)) { - set_target(megamorphic_stub()); + set_target((strict_mode == kStrictMode) + ? megamorphic_stub_strict() + : megamorphic_stub()); #ifdef DEBUG if (FLAG_trace_ic) { PrintF("[StoreIC : inline patch %s]\n", *name->ToCString()); } #endif - return receiver->SetProperty(*name, *value, NONE); + return receiver->SetProperty(*name, *value, NONE, strict_mode); #ifdef DEBUG } else { @@ -1474,11 +1480,16 @@ MaybeObject* StoreIC::Store(State state, // If no inlined store ic was patched, generate a stub for this // store. - UpdateCaches(&lookup, state, extra_ic_state, receiver, name, value); + UpdateCaches(&lookup, state, strict_mode, receiver, name, value); } else { - // Strict mode doesn't allow setting non-existent global property. - if (extra_ic_state == kStoreICStrict && IsContextual(object)) { - return ReferenceError("not_defined", name); + // Strict mode doesn't allow setting non-existent global property + // or an assignment to a read only property. + if (strict_mode == kStrictMode) { + if (lookup.IsFound() && lookup.IsReadOnly()) { + return TypeError("strict_read_only_property", object, name); + } else if (IsContextual(object)) { + return ReferenceError("not_defined", name); + } } } } @@ -1486,7 +1497,7 @@ MaybeObject* StoreIC::Store(State state, if (receiver->IsJSGlobalProxy()) { // Generate a generic stub that goes to the runtime when we see a global // proxy as receiver. - Code* stub = (extra_ic_state == kStoreICStrict) + Code* stub = (strict_mode == kStrictMode) ? global_proxy_stub_strict() : global_proxy_stub(); if (target() != stub) { @@ -1498,13 +1509,13 @@ MaybeObject* StoreIC::Store(State state, } // Set the property. - return receiver->SetProperty(*name, *value, NONE); + return receiver->SetProperty(*name, *value, NONE, strict_mode); } void StoreIC::UpdateCaches(LookupResult* lookup, State state, - Code::ExtraICState extra_ic_state, + StrictModeFlag strict_mode, Handle receiver, Handle name, Handle value) { @@ -1526,7 +1537,7 @@ void StoreIC::UpdateCaches(LookupResult* lookup, switch (type) { case FIELD: { maybe_code = StubCache::ComputeStoreField( - *name, *receiver, lookup->GetFieldIndex(), NULL, extra_ic_state); + *name, *receiver, lookup->GetFieldIndex(), NULL, strict_mode); break; } case MAP_TRANSITION: { @@ -1536,7 +1547,7 @@ void StoreIC::UpdateCaches(LookupResult* lookup, Handle transition(lookup->GetTransitionMap()); int index = transition->PropertyIndexFor(*name); maybe_code = StubCache::ComputeStoreField( - *name, *receiver, index, *transition, extra_ic_state); + *name, *receiver, index, *transition, strict_mode); break; } case NORMAL: { @@ -1548,10 +1559,10 @@ void StoreIC::UpdateCaches(LookupResult* lookup, JSGlobalPropertyCell* cell = JSGlobalPropertyCell::cast(global->GetPropertyCell(lookup)); maybe_code = StubCache::ComputeStoreGlobal( - *name, *global, cell, extra_ic_state); + *name, *global, cell, strict_mode); } else { if (lookup->holder() != *receiver) return; - maybe_code = StubCache::ComputeStoreNormal(extra_ic_state); + maybe_code = StubCache::ComputeStoreNormal(strict_mode); } break; } @@ -1560,13 +1571,13 @@ void StoreIC::UpdateCaches(LookupResult* lookup, AccessorInfo* callback = AccessorInfo::cast(lookup->GetCallbackObject()); if (v8::ToCData
(callback->setter()) == 0) return; maybe_code = StubCache::ComputeStoreCallback( - *name, *receiver, callback, extra_ic_state); + *name, *receiver, callback, strict_mode); break; } case INTERCEPTOR: { ASSERT(!receiver->GetNamedInterceptor()->setter()->IsUndefined()); maybe_code = StubCache::ComputeStoreInterceptor( - *name, *receiver, extra_ic_state); + *name, *receiver, strict_mode); break; } default: @@ -1583,7 +1594,7 @@ void StoreIC::UpdateCaches(LookupResult* lookup, } else if (state == MONOMORPHIC) { // Only move to megamorphic if the target changes. if (target() != Code::cast(code)) { - set_target(extra_ic_state == kStoreICStrict + set_target((strict_mode == kStrictMode) ? megamorphic_stub_strict() : megamorphic_stub()); } @@ -1599,6 +1610,7 @@ void StoreIC::UpdateCaches(LookupResult* lookup, MaybeObject* KeyedStoreIC::Store(State state, + StrictModeFlag strict_mode, Handle object, Handle key, Handle value) { @@ -1630,11 +1642,11 @@ MaybeObject* KeyedStoreIC::Store(State state, // Update inline cache and stub cache. if (FLAG_use_ic) { - UpdateCaches(&lookup, state, receiver, name, value); + UpdateCaches(&lookup, state, strict_mode, receiver, name, value); } // Set the property. - return receiver->SetProperty(*name, *value, NONE); + return receiver->SetProperty(*name, *value, NONE, strict_mode); } // Do not use ICs for objects that require access checks (including @@ -1643,23 +1655,25 @@ MaybeObject* KeyedStoreIC::Store(State state, ASSERT(!(use_ic && object->IsJSGlobalProxy())); if (use_ic) { - Code* stub = generic_stub(); + Code* stub = + (strict_mode == kStrictMode) ? generic_stub_strict() : generic_stub(); if (state == UNINITIALIZED) { if (object->IsJSObject()) { Handle receiver = Handle::cast(object); if (receiver->HasExternalArrayElements()) { MaybeObject* probe = - StubCache::ComputeKeyedLoadOrStoreExternalArray(*receiver, true); + StubCache::ComputeKeyedLoadOrStoreExternalArray( + *receiver, true, strict_mode); stub = probe->IsFailure() ? NULL : Code::cast(probe->ToObjectUnchecked()); } else if (receiver->HasPixelElements()) { MaybeObject* probe = - StubCache::ComputeKeyedStorePixelArray(*receiver); + StubCache::ComputeKeyedStorePixelArray(*receiver, strict_mode); stub = probe->IsFailure() ? NULL : Code::cast(probe->ToObjectUnchecked()); } else if (key->IsSmi() && receiver->map()->has_fast_elements()) { MaybeObject* probe = - StubCache::ComputeKeyedStoreSpecialized(*receiver); + StubCache::ComputeKeyedStoreSpecialized(*receiver, strict_mode); stub = probe->IsFailure() ? NULL : Code::cast(probe->ToObjectUnchecked()); } @@ -1669,12 +1683,13 @@ MaybeObject* KeyedStoreIC::Store(State state, } // Set the property. - return Runtime::SetObjectProperty(object, key, value, NONE); + return Runtime::SetObjectProperty(object, key, value, NONE, strict_mode); } void KeyedStoreIC::UpdateCaches(LookupResult* lookup, State state, + StrictModeFlag strict_mode, Handle receiver, Handle name, Handle value) { @@ -1701,8 +1716,8 @@ void KeyedStoreIC::UpdateCaches(LookupResult* lookup, switch (type) { case FIELD: { - maybe_code = StubCache::ComputeKeyedStoreField(*name, *receiver, - lookup->GetFieldIndex()); + maybe_code = StubCache::ComputeKeyedStoreField( + *name, *receiver, lookup->GetFieldIndex(), NULL, strict_mode); break; } case MAP_TRANSITION: { @@ -1711,8 +1726,8 @@ void KeyedStoreIC::UpdateCaches(LookupResult* lookup, ASSERT(type == MAP_TRANSITION); Handle transition(lookup->GetTransitionMap()); int index = transition->PropertyIndexFor(*name); - maybe_code = StubCache::ComputeKeyedStoreField(*name, *receiver, - index, *transition); + maybe_code = StubCache::ComputeKeyedStoreField( + *name, *receiver, index, *transition, strict_mode); break; } // fall through. @@ -1720,7 +1735,9 @@ void KeyedStoreIC::UpdateCaches(LookupResult* lookup, default: { // Always rewrite to the generic case so that we do not // repeatedly try to rewrite. - maybe_code = generic_stub(); + maybe_code = (strict_mode == kStrictMode) + ? generic_stub_strict() + : generic_stub(); break; } } @@ -1735,7 +1752,9 @@ void KeyedStoreIC::UpdateCaches(LookupResult* lookup, if (state == UNINITIALIZED || state == PREMONOMORPHIC) { set_target(Code::cast(code)); } else if (state == MONOMORPHIC) { - set_target(megamorphic_stub()); + set_target((strict_mode == kStrictMode) + ? megamorphic_stub_strict() + : megamorphic_stub()); } #ifdef DEBUG @@ -1836,8 +1855,11 @@ MUST_USE_RESULT MaybeObject* StoreIC_Miss(Arguments args) { StoreIC ic; 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, extra_ic_state, args.at(0), - args.at(1), args.at(2)); + return ic.Store(state, + static_cast(extra_ic_state & kStrictMode), + args.at(0), + args.at(1), + args.at(2)); } @@ -1901,7 +1923,11 @@ MUST_USE_RESULT MaybeObject* KeyedStoreIC_Miss(Arguments args) { ASSERT(args.length() == 3); KeyedStoreIC ic; IC::State state = IC::StateFrom(ic.target(), args[0], args[1]); - return ic.Store(state, args.at(0), args.at(1), + Code::ExtraICState extra_ic_state = ic.target()->extra_ic_state(); + return ic.Store(state, + static_cast(extra_ic_state & kStrictMode), + args.at(0), + args.at(1), args.at(2)); } diff --git a/deps/v8/src/ic.h b/deps/v8/src/ic.h index 96838c7338..e12cbaf8ef 100644 --- a/deps/v8/src/ic.h +++ b/deps/v8/src/ic.h @@ -398,16 +398,10 @@ class KeyedLoadIC: public IC { class StoreIC: public IC { public: - - enum StoreICStrictMode { - kStoreICNonStrict = kNonStrictMode, - kStoreICStrict = kStrictMode - }; - StoreIC() : IC(NO_EXTRA_FRAME) { ASSERT(target()->is_store_stub()); } MUST_USE_RESULT MaybeObject* Store(State state, - Code::ExtraICState extra_ic_state, + StrictModeFlag strict_mode, Handle object, Handle name, Handle value); @@ -416,10 +410,11 @@ class StoreIC: public IC { static void GenerateInitialize(MacroAssembler* masm) { GenerateMiss(masm); } static void GenerateMiss(MacroAssembler* masm); static void GenerateMegamorphic(MacroAssembler* masm, - Code::ExtraICState extra_ic_state); + StrictModeFlag strict_mode); static void GenerateArrayLength(MacroAssembler* masm); static void GenerateNormal(MacroAssembler* masm); - static void GenerateGlobalProxy(MacroAssembler* masm); + static void GenerateGlobalProxy(MacroAssembler* masm, + StrictModeFlag strict_mode); // Clear the use of an inlined version. static void ClearInlinedVersion(Address address); @@ -433,11 +428,18 @@ class StoreIC: public IC { // lookup result. void UpdateCaches(LookupResult* lookup, State state, - Code::ExtraICState extra_ic_state, + StrictModeFlag strict_mode, Handle receiver, Handle name, Handle value); + void set_target(Code* code) { + // Strict mode must be preserved across IC patching. + ASSERT((code->extra_ic_state() & kStrictMode) == + (target()->extra_ic_state() & kStrictMode)); + IC::set_target(code); + } + // Stub accessors. static Code* megamorphic_stub() { return Builtins::builtin(Builtins::StoreIC_Megamorphic); @@ -473,6 +475,7 @@ class KeyedStoreIC: public IC { KeyedStoreIC() : IC(NO_EXTRA_FRAME) { } MUST_USE_RESULT MaybeObject* Store(State state, + StrictModeFlag strict_mode, Handle object, Handle name, Handle value); @@ -480,8 +483,9 @@ class KeyedStoreIC: public IC { // Code generators for stub routines. Only called once at startup. static void GenerateInitialize(MacroAssembler* masm) { GenerateMiss(masm); } static void GenerateMiss(MacroAssembler* masm); - static void GenerateRuntimeSetProperty(MacroAssembler* masm); - static void GenerateGeneric(MacroAssembler* masm); + static void GenerateRuntimeSetProperty(MacroAssembler* masm, + StrictModeFlag strict_mode); + static void GenerateGeneric(MacroAssembler* masm, StrictModeFlag strict_mode); // Clear the inlined version so the IC is always hit. static void ClearInlinedVersion(Address address); @@ -493,20 +497,37 @@ class KeyedStoreIC: public IC { // Update the inline cache. void UpdateCaches(LookupResult* lookup, State state, + StrictModeFlag strict_mode, Handle receiver, Handle name, Handle value); + void set_target(Code* code) { + // Strict mode must be preserved across IC patching. + ASSERT((code->extra_ic_state() & kStrictMode) == + (target()->extra_ic_state() & kStrictMode)); + IC::set_target(code); + } + // Stub accessors. static Code* initialize_stub() { return Builtins::builtin(Builtins::KeyedStoreIC_Initialize); } + static Code* initialize_stub_strict() { + return Builtins::builtin(Builtins::KeyedStoreIC_Initialize_Strict); + } static Code* megamorphic_stub() { return Builtins::builtin(Builtins::KeyedStoreIC_Generic); } + static Code* megamorphic_stub_strict() { + return Builtins::builtin(Builtins::KeyedStoreIC_Generic_Strict); + } static Code* generic_stub() { return Builtins::builtin(Builtins::KeyedStoreIC_Generic); } + static Code* generic_stub_strict() { + return Builtins::builtin(Builtins::KeyedStoreIC_Generic_Strict); + } static void Clear(Address address, Code* target); diff --git a/deps/v8/src/liveobjectlist-inl.h b/deps/v8/src/liveobjectlist-inl.h index 997da4ee95..f742de3a03 100644 --- a/deps/v8/src/liveobjectlist-inl.h +++ b/deps/v8/src/liveobjectlist-inl.h @@ -32,5 +32,95 @@ #include "liveobjectlist.h" +namespace v8 { +namespace internal { + +#ifdef LIVE_OBJECT_LIST + +void LiveObjectList::GCEpilogue() { + if (!NeedLOLProcessing()) return; + GCEpiloguePrivate(); +} + + +void LiveObjectList::GCPrologue() { + if (!NeedLOLProcessing()) return; +#ifdef VERIFY_LOL + if (FLAG_verify_lol) { + Verify(); + } +#endif +} + + +void LiveObjectList::IterateElements(ObjectVisitor* v) { + if (!NeedLOLProcessing()) return; + IterateElementsPrivate(v); +} + + +void LiveObjectList::ProcessNonLive(HeapObject *obj) { + // Only do work if we have at least one list to process. + if (last()) DoProcessNonLive(obj); +} + + +void LiveObjectList::UpdateReferencesForScavengeGC() { + if (LiveObjectList::NeedLOLProcessing()) { + UpdateLiveObjectListVisitor update_visitor; + LiveObjectList::IterateElements(&update_visitor); + } +} + + +LiveObjectList* LiveObjectList::FindLolForId(int id, + LiveObjectList* start_lol) { + if (id != 0) { + LiveObjectList* lol = start_lol; + while (lol != NULL) { + if (lol->id() == id) { + return lol; + } + lol = lol->prev_; + } + } + return NULL; +} + + +// Iterates the elements in every lol and returns the one that matches the +// specified key. If no matching element is found, then it returns NULL. +template +inline LiveObjectList::Element* +LiveObjectList::FindElementFor(T (*GetValue)(LiveObjectList::Element*), T key) { + LiveObjectList *lol = last(); + while (lol != NULL) { + Element* elements = lol->elements_; + for (int i = 0; i < lol->obj_count_; i++) { + Element* element = &elements[i]; + if (GetValue(element) == key) { + return element; + } + } + lol = lol->prev_; + } + return NULL; +} + + +inline int LiveObjectList::GetElementId(LiveObjectList::Element* element) { + return element->id_; +} + + +inline HeapObject* +LiveObjectList::GetElementObj(LiveObjectList::Element* element) { + return element->obj_; +} + +#endif // LIVE_OBJECT_LIST + +} } // namespace v8::internal + #endif // V8_LIVEOBJECTLIST_INL_H_ diff --git a/deps/v8/src/liveobjectlist.cc b/deps/v8/src/liveobjectlist.cc index 28a3d6d6ec..cd6fcf99ef 100644 --- a/deps/v8/src/liveobjectlist.cc +++ b/deps/v8/src/liveobjectlist.cc @@ -37,7 +37,7 @@ #include "heap.h" #include "inspector.h" #include "list-inl.h" -#include "liveobjectlist.h" +#include "liveobjectlist-inl.h" #include "string-stream.h" #include "top.h" #include "v8utils.h" @@ -46,6 +46,2480 @@ namespace v8 { namespace internal { +typedef int (*RawComparer)(const void*, const void*); + + +#ifdef CHECK_ALL_OBJECT_TYPES + +#define DEBUG_LIVE_OBJECT_TYPES(v) \ + v(Smi, "unexpected: Smi") \ + \ + v(CodeCache, "unexpected: CodeCache") \ + v(BreakPointInfo, "unexpected: BreakPointInfo") \ + v(DebugInfo, "unexpected: DebugInfo") \ + v(TypeSwitchInfo, "unexpected: TypeSwitchInfo") \ + v(SignatureInfo, "unexpected: SignatureInfo") \ + v(Script, "unexpected: Script") \ + v(ObjectTemplateInfo, "unexpected: ObjectTemplateInfo") \ + v(FunctionTemplateInfo, "unexpected: FunctionTemplateInfo") \ + v(CallHandlerInfo, "unexpected: CallHandlerInfo") \ + v(InterceptorInfo, "unexpected: InterceptorInfo") \ + v(AccessCheckInfo, "unexpected: AccessCheckInfo") \ + v(AccessorInfo, "unexpected: AccessorInfo") \ + v(ExternalTwoByteString, "unexpected: ExternalTwoByteString") \ + v(ExternalAsciiString, "unexpected: ExternalAsciiString") \ + v(ExternalString, "unexpected: ExternalString") \ + v(SeqTwoByteString, "unexpected: SeqTwoByteString") \ + v(SeqAsciiString, "unexpected: SeqAsciiString") \ + v(SeqString, "unexpected: SeqString") \ + v(JSFunctionResultCache, "unexpected: JSFunctionResultCache") \ + v(GlobalContext, "unexpected: GlobalContext") \ + v(MapCache, "unexpected: MapCache") \ + v(CodeCacheHashTable, "unexpected: CodeCacheHashTable") \ + v(CompilationCacheTable, "unexpected: CompilationCacheTable") \ + v(SymbolTable, "unexpected: SymbolTable") \ + v(Dictionary, "unexpected: Dictionary") \ + v(HashTable, "unexpected: HashTable") \ + v(DescriptorArray, "unexpected: DescriptorArray") \ + v(ExternalFloatArray, "unexpected: ExternalFloatArray") \ + v(ExternalUnsignedIntArray, "unexpected: ExternalUnsignedIntArray") \ + v(ExternalIntArray, "unexpected: ExternalIntArray") \ + v(ExternalUnsignedShortArray, "unexpected: ExternalUnsignedShortArray") \ + v(ExternalShortArray, "unexpected: ExternalShortArray") \ + v(ExternalUnsignedByteArray, "unexpected: ExternalUnsignedByteArray") \ + v(ExternalByteArray, "unexpected: ExternalByteArray") \ + v(JSValue, "unexpected: JSValue") + +#else +#define DEBUG_LIVE_OBJECT_TYPES(v) +#endif + + +#define FOR_EACH_LIVE_OBJECT_TYPE(v) \ + DEBUG_LIVE_OBJECT_TYPES(v) \ + \ + v(JSArray, "JSArray") \ + v(JSRegExp, "JSRegExp") \ + v(JSFunction, "JSFunction") \ + v(JSGlobalObject, "JSGlobal") \ + v(JSBuiltinsObject, "JSBuiltins") \ + v(GlobalObject, "Global") \ + v(JSGlobalProxy, "JSGlobalProxy") \ + v(JSObject, "JSObject") \ + \ + v(Context, "meta: Context") \ + v(ByteArray, "meta: ByteArray") \ + v(PixelArray, "meta: PixelArray") \ + v(ExternalArray, "meta: ExternalArray") \ + v(FixedArray, "meta: FixedArray") \ + v(String, "String") \ + v(HeapNumber, "HeapNumber") \ + \ + v(Code, "meta: Code") \ + v(Map, "meta: Map") \ + v(Oddball, "Oddball") \ + v(Proxy, "meta: Proxy") \ + v(SharedFunctionInfo, "meta: SharedFunctionInfo") \ + v(Struct, "meta: Struct") \ + \ + v(HeapObject, "HeapObject") + + +enum /* LiveObjectType */ { +#define DECLARE_OBJECT_TYPE_ENUM(type, name) kType##type, + FOR_EACH_LIVE_OBJECT_TYPE(DECLARE_OBJECT_TYPE_ENUM) + kInvalidLiveObjType, + kNumberOfTypes +#undef DECLARE_OBJECT_TYPE_ENUM +}; + + +LiveObjectType GetObjectType(HeapObject* heap_obj) { + // TODO(mlam): investigate usint Map::instance_type() instead. +#define CHECK_FOR_OBJECT_TYPE(type, name) \ + if (heap_obj->Is##type()) return kType##type; + FOR_EACH_LIVE_OBJECT_TYPE(CHECK_FOR_OBJECT_TYPE) +#undef CHECK_FOR_OBJECT_TYPE + + UNREACHABLE(); + return kInvalidLiveObjType; +} + + +inline const char* GetObjectTypeDesc(LiveObjectType type) { + static const char* const name[kNumberOfTypes] = { + #define DEFINE_OBJECT_TYPE_NAME(type, name) name, + FOR_EACH_LIVE_OBJECT_TYPE(DEFINE_OBJECT_TYPE_NAME) + "invalid" + #undef DEFINE_OBJECT_TYPE_NAME + }; + ASSERT(type < kNumberOfTypes); + return name[type]; +} + + +const char* GetObjectTypeDesc(HeapObject* heap_obj) { + LiveObjectType type = GetObjectType(heap_obj); + return GetObjectTypeDesc(type); +} + + +bool IsOfType(LiveObjectType type, HeapObject *obj) { + // Note: there are types that are more general (e.g. JSObject) that would + // have passed the Is##type_() test for more specialized types (e.g. + // JSFunction). If we find a more specialized match but we're looking for + // the general type, then we should reject the ones that matches the + // specialized type. +#define CHECK_OBJECT_TYPE(type_, name) \ + if (obj->Is##type_()) return (type == kType##type_); + + FOR_EACH_LIVE_OBJECT_TYPE(CHECK_OBJECT_TYPE) +#undef CHECK_OBJECT_TYPE + + return false; +} + + +const AllocationSpace kInvalidSpace = static_cast(-1); + +static AllocationSpace FindSpaceFor(String* space_str) { + SmartPointer s = + space_str->ToCString(DISALLOW_NULLS, ROBUST_STRING_TRAVERSAL); + + const char* key_str = *s; + switch (key_str[0]) { + case 'c': + if (strcmp(key_str, "cell") == 0) return CELL_SPACE; + if (strcmp(key_str, "code") == 0) return CODE_SPACE; + break; + case 'l': + if (strcmp(key_str, "lo") == 0) return LO_SPACE; + break; + case 'm': + if (strcmp(key_str, "map") == 0) return MAP_SPACE; + break; + case 'n': + if (strcmp(key_str, "new") == 0) return NEW_SPACE; + break; + case 'o': + if (strcmp(key_str, "old-pointer") == 0) return OLD_POINTER_SPACE; + if (strcmp(key_str, "old-data") == 0) return OLD_DATA_SPACE; + break; + } + return kInvalidSpace; +} + + +static bool InSpace(AllocationSpace space, HeapObject *heap_obj) { + if (space != LO_SPACE) { + return Heap::InSpace(heap_obj, space); + } + + // This is an optimization to speed up the check for an object in the LO + // space by exclusion because we know that all object pointers passed in + // here are guaranteed to be in the heap. Hence, it is safe to infer + // using an exclusion test. + // Note: calling Heap::InSpace(heap_obj, LO_SPACE) is too slow for our + // filters. + int first_space = static_cast(FIRST_SPACE); + int last_space = static_cast(LO_SPACE); + for (int sp = first_space; sp < last_space; sp++) { + if (Heap::InSpace(heap_obj, static_cast(sp))) { + return false; + } + } + SLOW_ASSERT(Heap::InSpace(heap_obj, LO_SPACE)); + return true; +} + + +static LiveObjectType FindTypeFor(String* type_str) { + SmartPointer s = + type_str->ToCString(DISALLOW_NULLS, ROBUST_STRING_TRAVERSAL); + +#define CHECK_OBJECT_TYPE(type_, name) { \ + const char* type_desc = GetObjectTypeDesc(kType##type_); \ + const char* key_str = *s; \ + if (strstr(type_desc, key_str) != NULL) return kType##type_; \ + } + FOR_EACH_LIVE_OBJECT_TYPE(CHECK_OBJECT_TYPE) +#undef CHECK_OBJECT_TYPE + + return kInvalidLiveObjType; +} + + +class LolFilter { + public: + explicit LolFilter(Handle filter_obj); + + inline bool is_active() const { return is_active_; } + inline bool Matches(HeapObject* obj) { + return !is_active() || MatchesSlow(obj); + } + + private: + void InitTypeFilter(Handle filter_obj); + void InitSpaceFilter(Handle filter_obj); + void InitPropertyFilter(Handle filter_obj); + bool MatchesSlow(HeapObject* obj); + + bool is_active_; + LiveObjectType type_; + AllocationSpace space_; + Handle prop_; +}; + + +LolFilter::LolFilter(Handle filter_obj) + : is_active_(false), + type_(kInvalidLiveObjType), + space_(kInvalidSpace), + prop_() { + if (filter_obj.is_null()) return; + + InitTypeFilter(filter_obj); + InitSpaceFilter(filter_obj); + InitPropertyFilter(filter_obj); +} + + +void LolFilter::InitTypeFilter(Handle filter_obj) { + Handle type_sym = Factory::LookupAsciiSymbol("type"); + MaybeObject* maybe_result = filter_obj->GetProperty(*type_sym); + Object* type_obj; + if (maybe_result->ToObject(&type_obj)) { + if (type_obj->IsString()) { + String* type_str = String::cast(type_obj); + type_ = FindTypeFor(type_str); + if (type_ != kInvalidLiveObjType) { + is_active_ = true; + } + } + } +} + + +void LolFilter::InitSpaceFilter(Handle filter_obj) { + Handle space_sym = Factory::LookupAsciiSymbol("space"); + MaybeObject* maybe_result = filter_obj->GetProperty(*space_sym); + Object* space_obj; + if (maybe_result->ToObject(&space_obj)) { + if (space_obj->IsString()) { + String* space_str = String::cast(space_obj); + space_ = FindSpaceFor(space_str); + if (space_ != kInvalidSpace) { + is_active_ = true; + } + } + } +} + + +void LolFilter::InitPropertyFilter(Handle filter_obj) { + Handle prop_sym = Factory::LookupAsciiSymbol("prop"); + MaybeObject* maybe_result = filter_obj->GetProperty(*prop_sym); + Object* prop_obj; + if (maybe_result->ToObject(&prop_obj)) { + if (prop_obj->IsString()) { + prop_ = Handle(String::cast(prop_obj)); + is_active_ = true; + } + } +} + + +bool LolFilter::MatchesSlow(HeapObject* obj) { + if ((type_ != kInvalidLiveObjType) && !IsOfType(type_, obj)) { + return false; // Fail because obj is not of the type of interest. + } + if ((space_ != kInvalidSpace) && !InSpace(space_, obj)) { + return false; // Fail because obj is not in the space of interest. + } + if (!prop_.is_null() && obj->IsJSObject()) { + LookupResult result; + obj->Lookup(*prop_, &result); + if (!result.IsProperty()) { + return false; // Fail because obj does not have the property of interest. + } + } + return true; +} + + +class LolIterator { + public: + LolIterator(LiveObjectList* older, LiveObjectList* newer) + : older_(older), + newer_(newer), + curr_(0), + elements_(0), + count_(0), + index_(0) { } + + inline void Init() { + SetCurrent(newer_); + // If the elements_ list is empty, then move on to the next list as long + // as we're not at the last list (indicated by done()). + while ((elements_ == NULL) && !Done()) { + SetCurrent(curr_->prev_); + } + } + + inline bool Done() const { + return (curr_ == older_); + } + + // Object level iteration. + inline void Next() { + index_++; + if (index_ >= count_) { + // Iterate backwards until we get to the oldest list. + while (!Done()) { + SetCurrent(curr_->prev_); + // If we have elements to process, we're good to go. + if (elements_ != NULL) break; + + // Else, we should advance to the next older list. + } + } + } + + inline int Id() const { + return elements_[index_].id_; + } + inline HeapObject* Obj() const { + return elements_[index_].obj_; + } + + inline int LolObjCount() const { + if (curr_ != NULL) return curr_->obj_count_; + return 0; + } + + protected: + inline void SetCurrent(LiveObjectList* new_curr) { + curr_ = new_curr; + if (curr_ != NULL) { + elements_ = curr_->elements_; + count_ = curr_->obj_count_; + index_ = 0; + } + } + + LiveObjectList* older_; + LiveObjectList* newer_; + LiveObjectList* curr_; + LiveObjectList::Element* elements_; + int count_; + int index_; +}; + + +class LolForwardIterator : public LolIterator { + public: + LolForwardIterator(LiveObjectList* first, LiveObjectList* last) + : LolIterator(first, last) { + } + + inline void Init() { + SetCurrent(older_); + // If the elements_ list is empty, then move on to the next list as long + // as we're not at the last list (indicated by Done()). + while ((elements_ == NULL) && !Done()) { + SetCurrent(curr_->next_); + } + } + + inline bool Done() const { + return (curr_ == newer_); + } + + // Object level iteration. + inline void Next() { + index_++; + if (index_ >= count_) { + // Done with current list. Move on to the next. + while (!Done()) { // If not at the last list already, ... + SetCurrent(curr_->next_); + // If we have elements to process, we're good to go. + if (elements_ != NULL) break; + + // Else, we should advance to the next list. + } + } + } +}; + + +// Minimizes the white space in a string. Tabs and newlines are replaced +// with a space where appropriate. +static int CompactString(char* str) { + char* src = str; + char* dst = str; + char prev_ch = 0; + while (*dst != '\0') { + char ch = *src++; + // We will treat non-ascii chars as '?'. + if ((ch & 0x80) != 0) { + ch = '?'; + } + // Compact contiguous whitespace chars into a single ' '. + if (isspace(ch)) { + if (prev_ch != ' ') *dst++ = ' '; + prev_ch = ' '; + continue; + } + *dst++ = ch; + prev_ch = ch; + } + return (dst - str); +} + + +// Generates a custom description based on the specific type of +// object we're looking at. We only generate specialized +// descriptions where we can. In all other cases, we emit the +// generic info. +static void GenerateObjectDesc(HeapObject* obj, + char* buffer, + int buffer_size) { + Vector buffer_v(buffer, buffer_size); + ASSERT(obj != NULL); + if (obj->IsJSArray()) { + JSArray* jsarray = JSArray::cast(obj); + double length = jsarray->length()->Number(); + OS::SNPrintF(buffer_v, + "%p <%s> len %g", + reinterpret_cast(obj), + GetObjectTypeDesc(obj), + length); + + } else if (obj->IsString()) { + String *str = String::cast(obj); + // Only grab up to 160 chars in case they are double byte. + // We'll only dump 80 of them after we compact them. + const int kMaxCharToDump = 80; + const int kMaxBufferSize = kMaxCharToDump * 2; + SmartPointer str_sp = str->ToCString(DISALLOW_NULLS, + ROBUST_STRING_TRAVERSAL, + 0, + kMaxBufferSize); + char* str_cstr = *str_sp; + int length = CompactString(str_cstr); + OS::SNPrintF(buffer_v, + "%p <%s> '%.80s%s'", + reinterpret_cast(obj), + GetObjectTypeDesc(obj), + str_cstr, + (length > kMaxCharToDump) ? "..." : ""); + + } else if (obj->IsJSFunction() || obj->IsSharedFunctionInfo()) { + SharedFunctionInfo* sinfo; + if (obj->IsJSFunction()) { + JSFunction* func = JSFunction::cast(obj); + sinfo = func->shared(); + } else { + sinfo = SharedFunctionInfo::cast(obj); + } + + String* name = sinfo->DebugName(); + SmartPointer name_sp = + name->ToCString(DISALLOW_NULLS, ROBUST_STRING_TRAVERSAL); + char* name_cstr = *name_sp; + + HeapStringAllocator string_allocator; + StringStream stream(&string_allocator); + sinfo->SourceCodePrint(&stream, 50); + SmartPointer source_sp = stream.ToCString(); + const char* source_cstr = *source_sp; + + OS::SNPrintF(buffer_v, + "%p <%s> '%s' %s", + reinterpret_cast(obj), + GetObjectTypeDesc(obj), + name_cstr, + source_cstr); + + } else if (obj->IsFixedArray()) { + FixedArray* fixed = FixedArray::cast(obj); + + OS::SNPrintF(buffer_v, + "%p <%s> len %d", + reinterpret_cast(obj), + GetObjectTypeDesc(obj), + fixed->length()); + + } else { + OS::SNPrintF(buffer_v, + "%p <%s>", + reinterpret_cast(obj), + GetObjectTypeDesc(obj)); + } +} + + +// Utility function for filling in a line of detail in a verbose dump. +static bool AddObjDetail(Handle arr, + int index, + int obj_id, + Handle target, + const char* desc_str, + Handle id_sym, + Handle desc_sym, + Handle size_sym, + Handle detail, + Handle desc, + Handle error) { + detail = Factory::NewJSObject(Top::object_function()); + if (detail->IsFailure()) { + error = detail; + return false; + } + + int size = 0; + char buffer[512]; + if (desc_str == NULL) { + ASSERT(!target.is_null()); + HeapObject* obj = *target; + GenerateObjectDesc(obj, buffer, sizeof(buffer)); + desc_str = buffer; + size = obj->Size(); + } + desc = Factory::NewStringFromAscii(CStrVector(desc_str)); + if (desc->IsFailure()) { + error = desc; + return false; + } + + { MaybeObject* maybe_result = + detail->SetProperty(*id_sym, Smi::FromInt(obj_id), NONE); + if (maybe_result->IsFailure()) return false; + } + { MaybeObject* maybe_result = + detail->SetProperty(*desc_sym, *desc, NONE); + if (maybe_result->IsFailure()) return false; + } + { MaybeObject* maybe_result = + detail->SetProperty(*size_sym, Smi::FromInt(size), NONE); + if (maybe_result->IsFailure()) return false; + } + + arr->set(index, *detail); + return true; +} + + +class DumpWriter { + public: + virtual ~DumpWriter() {} + + virtual void ComputeTotalCountAndSize(LolFilter* filter, + int* count, + int* size) = 0; + virtual bool Write(Handle elements_arr, + int start, + int dump_limit, + LolFilter* filter, + Handle error) = 0; +}; + + +class LolDumpWriter: public DumpWriter { + public: + LolDumpWriter(LiveObjectList* older, LiveObjectList* newer) + : older_(older), newer_(newer) { + } + + void ComputeTotalCountAndSize(LolFilter* filter, int* count, int* size) { + *count = 0; + *size = 0; + + LolIterator it(older_, newer_); + for (it.Init(); !it.Done(); it.Next()) { + HeapObject* heap_obj = it.Obj(); + if (!filter->Matches(heap_obj)) { + continue; + } + + *size += heap_obj->Size(); + (*count)++; + } + } + + bool Write(Handle elements_arr, + int start, + int dump_limit, + LolFilter* filter, + Handle error) { + // The lols are listed in latest to earliest. We want to dump from + // earliest to latest. So, compute the last element to start with. + int index = 0; + int count = 0; + + // Prefetch some needed symbols. + Handle id_sym = Factory::LookupAsciiSymbol("id"); + Handle desc_sym = Factory::LookupAsciiSymbol("desc"); + Handle size_sym = Factory::LookupAsciiSymbol("size"); + + // Fill the array with the lol object details. + Handle detail; + Handle desc; + Handle target; + + LiveObjectList* first_lol = (older_ != NULL) ? + older_->next_ : LiveObjectList::first_; + LiveObjectList* last_lol = (newer_ != NULL) ? newer_->next_ : NULL; + + LolForwardIterator it(first_lol, last_lol); + for (it.Init(); !it.Done() && (index < dump_limit); it.Next()) { + HeapObject* heap_obj = it.Obj(); + + // Skip objects that have been filtered out. + if (!filter->Matches(heap_obj)) { + continue; + } + + // Only report objects that are in the section of interest. + if (count >= start) { + target = Handle(heap_obj); + bool success = AddObjDetail(elements_arr, + index++, + it.Id(), + target, + NULL, + id_sym, + desc_sym, + size_sym, + detail, + desc, + error); + if (!success) return false; + } + count++; + } + return true; + } + + private: + LiveObjectList* older_; + LiveObjectList* newer_; +}; + + +class RetainersDumpWriter: public DumpWriter { + public: + RetainersDumpWriter(Handle target, + Handle instance_filter, + Handle args_function) + : target_(target), + instance_filter_(instance_filter), + args_function_(args_function) { + } + + void ComputeTotalCountAndSize(LolFilter* filter, int* count, int* size) { + Handle retainers_arr; + Handle error; + + *size = -1; + LiveObjectList::GetRetainers(target_, + instance_filter_, + retainers_arr, + 0, + Smi::kMaxValue, + count, + filter, + NULL, + *args_function_, + error); + } + + bool Write(Handle elements_arr, + int start, + int dump_limit, + LolFilter* filter, + Handle error) { + int dummy; + int count; + + // Fill the retainer objects. + count = LiveObjectList::GetRetainers(target_, + instance_filter_, + elements_arr, + start, + dump_limit, + &dummy, + filter, + NULL, + *args_function_, + error); + if (count < 0) { + return false; + } + return true; + } + + private: + Handle target_; + Handle instance_filter_; + Handle args_function_; +}; + + +class LiveObjectSummary { + public: + explicit LiveObjectSummary(LolFilter* filter) + : total_count_(0), + total_size_(0), + found_root_(false), + found_weak_root_(false), + filter_(filter) { + memset(counts_, 0, sizeof(counts_[0]) * kNumberOfEntries); + memset(sizes_, 0, sizeof(sizes_[0]) * kNumberOfEntries); + } + + void Add(HeapObject* heap_obj) { + int size = heap_obj->Size(); + LiveObjectType type = GetObjectType(heap_obj); + ASSERT(type != kInvalidLiveObjType); + counts_[type]++; + sizes_[type] += size; + total_count_++; + total_size_ += size; + } + + void set_found_root() { found_root_ = true; } + void set_found_weak_root() { found_weak_root_ = true; } + + inline int Count(LiveObjectType type) { + return counts_[type]; + } + inline int Size(LiveObjectType type) { + return sizes_[type]; + } + inline int total_count() { + return total_count_; + } + inline int total_size() { + return total_size_; + } + inline bool found_root() { + return found_root_; + } + inline bool found_weak_root() { + return found_weak_root_; + } + int GetNumberOfEntries() { + int entries = 0; + for (int i = 0; i < kNumberOfEntries; i++) { + if (counts_[i]) entries++; + } + return entries; + } + + inline LolFilter* filter() { return filter_; } + + static const int kNumberOfEntries = kNumberOfTypes; + + private: + int counts_[kNumberOfEntries]; + int sizes_[kNumberOfEntries]; + int total_count_; + int total_size_; + bool found_root_; + bool found_weak_root_; + + LolFilter *filter_; +}; + + +// Abstraction for a summary writer. +class SummaryWriter { + public: + virtual ~SummaryWriter() {} + virtual void Write(LiveObjectSummary* summary) = 0; +}; + + +// A summary writer for filling in a summary of lol lists and diffs. +class LolSummaryWriter: public SummaryWriter { + public: + LolSummaryWriter(LiveObjectList *older_lol, + LiveObjectList *newer_lol) + : older_(older_lol), newer_(newer_lol) { + } + + void Write(LiveObjectSummary* summary) { + LolFilter* filter = summary->filter(); + + // Fill the summary with the lol object details. + LolIterator it(older_, newer_); + for (it.Init(); !it.Done(); it.Next()) { + HeapObject* heap_obj = it.Obj(); + if (!filter->Matches(heap_obj)) { + continue; + } + summary->Add(heap_obj); + } + } + + private: + LiveObjectList* older_; + LiveObjectList* newer_; +}; + + +// A summary writer for filling in a retainers list. +class RetainersSummaryWriter: public SummaryWriter { + public: + RetainersSummaryWriter(Handle target, + Handle instance_filter, + Handle args_function) + : target_(target), + instance_filter_(instance_filter), + args_function_(args_function) { + } + + void Write(LiveObjectSummary* summary) { + Handle retainers_arr; + Handle error; + int dummy_total_count; + LiveObjectList::GetRetainers(target_, + instance_filter_, + retainers_arr, + 0, + Smi::kMaxValue, + &dummy_total_count, + summary->filter(), + summary, + *args_function_, + error); + } + + private: + Handle target_; + Handle instance_filter_; + Handle args_function_; +}; + + +uint32_t LiveObjectList::next_element_id_ = 1; +int LiveObjectList::list_count_ = 0; +int LiveObjectList::last_id_ = 0; +LiveObjectList* LiveObjectList::first_ = NULL; +LiveObjectList* LiveObjectList::last_ = NULL; + + +LiveObjectList::LiveObjectList(LiveObjectList* prev, int capacity) + : prev_(prev), + next_(NULL), + capacity_(capacity), + obj_count_(0) { + elements_ = NewArray(capacity); + id_ = ++last_id_; + + list_count_++; +} + + +LiveObjectList::~LiveObjectList() { + DeleteArray(elements_); + delete prev_; +} + + +int LiveObjectList::GetTotalObjCountAndSize(int* size_p) { + int size = 0; + int count = 0; + LiveObjectList *lol = this; + do { + // Only compute total size if requested i.e. when size_p is not null. + if (size_p != NULL) { + Element* elements = lol->elements_; + for (int i = 0; i < lol->obj_count_; i++) { + HeapObject* heap_obj = elements[i].obj_; + size += heap_obj->Size(); + } + } + count += lol->obj_count_; + lol = lol->prev_; + } while (lol != NULL); + + if (size_p != NULL) { + *size_p = size; + } + return count; +} + + +// Adds an object to the lol. +// Returns true if successful, else returns false. +bool LiveObjectList::Add(HeapObject* obj) { + // If the object is already accounted for in the prev list which we inherit + // from, then no need to add it to this list. + if ((prev() != NULL) && (prev()->Find(obj) != NULL)) { + return true; + } + ASSERT(obj_count_ <= capacity_); + if (obj_count_ == capacity_) { + // The heap must have grown and we have more objects than capacity to store + // them. + return false; // Fail this addition. + } + Element& element = elements_[obj_count_++]; + element.id_ = next_element_id_++; + element.obj_ = obj; + return true; +} + + +// Comparator used for sorting and searching the lol. +int LiveObjectList::CompareElement(const Element* a, const Element* b) { + const HeapObject* obj1 = a->obj_; + const HeapObject* obj2 = b->obj_; + // For lol elements, it doesn't matter which comes first if 2 elements point + // to the same object (which gets culled later). Hence, we only care about + // the the greater than / less than relationships. + return (obj1 > obj2) ? 1 : (obj1 == obj2) ? 0 : -1; +} + + +// Looks for the specified object in the lol, and returns its element if found. +LiveObjectList::Element* LiveObjectList::Find(HeapObject* obj) { + LiveObjectList* lol = this; + Element key; + Element* result = NULL; + + key.obj_ = obj; + // Iterate through the chain of lol's to look for the object. + while ((result == NULL) && (lol != NULL)) { + result = reinterpret_cast( + bsearch(&key, lol->elements_, lol->obj_count_, + sizeof(Element), + reinterpret_cast(CompareElement))); + lol = lol->prev_; + } + return result; +} + + +// "Nullifies" (convert the HeapObject* into an SMI) so that it will get cleaned +// up in the GCEpilogue, while preserving the sort order of the lol. +// NOTE: the lols need to be already sorted before NullifyMostRecent() is +// called. +void LiveObjectList::NullifyMostRecent(HeapObject* obj) { + LiveObjectList* lol = last(); + Element key; + Element* result = NULL; + + key.obj_ = obj; + // Iterate through the chain of lol's to look for the object. + while (lol != NULL) { + result = reinterpret_cast( + bsearch(&key, lol->elements_, lol->obj_count_, + sizeof(Element), + reinterpret_cast(CompareElement))); + if (result != NULL) { + // Since there may be more than one (we are nullifying dup's after all), + // find the first in the current lol, and nullify that. The lol should + // be sorted already to make this easy (see the use of SortAll()). + int i = result - lol->elements_; + + // NOTE: we sort the lol in increasing order. So, if an object has been + // "nullified" (its lowest bit will be cleared to make it look like an + // SMI), it would/should show up before the equivalent dups that have not + // yet been "nullified". Hence, we should be searching backwards for the + // first occurence of a matching object and nullify that instance. This + // will ensure that we preserve the expected sorting order. + for (i--; i > 0; i--) { + Element* element = &lol->elements_[i]; + HeapObject* curr_obj = element->obj_; + if (curr_obj != obj) { + break; // No more matches. Let's move on. + } + result = element; // Let this earlier match be the result. + } + + // Nullify the object. + NullifyNonLivePointer(&result->obj_); + return; + } + lol = lol->prev_; + } +} + + +// Sorts the lol. +void LiveObjectList::Sort() { + if (obj_count_ > 0) { + Vector elements_v(elements_, obj_count_); + elements_v.Sort(CompareElement); + } +} + + +// Sorts all captured lols starting from the latest. +void LiveObjectList::SortAll() { + LiveObjectList* lol = last(); + while (lol != NULL) { + lol->Sort(); + lol = lol->prev_; + } +} + + +// Counts the number of objects in the heap. +static int CountHeapObjects() { + int count = 0; + // Iterate over all the heap spaces and count the number of objects. + HeapIterator iterator(HeapIterator::kFilterFreeListNodes); + HeapObject* heap_obj = NULL; + while ((heap_obj = iterator.next()) != NULL) { + count++; + } + return count; +} + + +// Captures a current snapshot of all objects in the heap. +MaybeObject* LiveObjectList::Capture() { + HandleScope scope; + + // Count the number of objects in the heap. + int total_count = CountHeapObjects(); + int count = total_count; + int size = 0; + + LiveObjectList* last_lol = last(); + if (last_lol != NULL) { + count -= last_lol->TotalObjCount(); + } + + LiveObjectList* lol; + + // Create a lol large enough to track all the objects. + lol = new LiveObjectList(last_lol, count); + if (lol == NULL) { + return NULL; // No memory to proceed. + } + + // The HeapIterator needs to be in its own scope because it disables + // allocation, and we need allocate below. + { + // Iterate over all the heap spaces and add the objects. + HeapIterator iterator(HeapIterator::kFilterFreeListNodes); + HeapObject* heap_obj = NULL; + bool failed = false; + while (!failed && (heap_obj = iterator.next()) != NULL) { + failed = !lol->Add(heap_obj); + size += heap_obj->Size(); + } + ASSERT(!failed); + + lol->Sort(); + + // Add the current lol to the list of lols. + if (last_ != NULL) { + last_->next_ = lol; + } else { + first_ = lol; + } + last_ = lol; + +#ifdef VERIFY_LOL + if (FLAG_verify_lol) { + Verify(true); + } +#endif + } + + Handle id_sym = Factory::LookupAsciiSymbol("id"); + Handle count_sym = Factory::LookupAsciiSymbol("count"); + Handle size_sym = Factory::LookupAsciiSymbol("size"); + + Handle result = Factory::NewJSObject(Top::object_function()); + if (result->IsFailure()) return Object::cast(*result); + + { MaybeObject* maybe_result = + result->SetProperty(*id_sym, Smi::FromInt(lol->id()), NONE); + if (maybe_result->IsFailure()) return maybe_result; + } + { MaybeObject* maybe_result = + result->SetProperty(*count_sym, Smi::FromInt(total_count), NONE); + if (maybe_result->IsFailure()) return maybe_result; + } + { MaybeObject* maybe_result = + result->SetProperty(*size_sym, Smi::FromInt(size), NONE); + if (maybe_result->IsFailure()) return maybe_result; + } + + return *result; +} + + +// Delete doesn't actually deletes an lol. It just marks it as invisible since +// its contents are considered to be part of subsequent lists as well. The +// only time we'll actually delete the lol is when we Reset() or if the lol is +// invisible, and its element count reaches 0. +bool LiveObjectList::Delete(int id) { + LiveObjectList *lol = last(); + while (lol != NULL) { + if (lol->id() == id) { + break; + } + lol = lol->prev_; + } + + // If no lol is found for this id, then we fail to delete. + if (lol == NULL) return false; + + // Else, mark the lol as invisible i.e. id == 0. + lol->id_ = 0; + list_count_--; + ASSERT(list_count_ >= 0); + if (lol->obj_count_ == 0) { + // Point the next lol's prev to this lol's prev. + LiveObjectList* next = lol->next_; + LiveObjectList* prev = lol->prev_; + // Point next's prev to prev. + if (next != NULL) { + next->prev_ = lol->prev_; + } else { + last_ = lol->prev_; + } + // Point prev's next to next. + if (prev != NULL) { + prev->next_ = lol->next_; + } else { + first_ = lol->next_; + } + + lol->prev_ = NULL; + lol->next_ = NULL; + + // Delete this now empty and invisible lol. + delete lol; + } + + // Just in case we've marked everything invisible, then clean up completely. + if (list_count_ == 0) { + Reset(); + } + + return true; +} + + +MaybeObject* LiveObjectList::Dump(int older_id, + int newer_id, + int start_idx, + int dump_limit, + Handle filter_obj) { + if ((older_id < 0) || (newer_id < 0) || (last() == NULL)) { + return Failure::Exception(); // Fail: 0 is not a valid lol id. + } + if (newer_id < older_id) { + // They are not in the expected order. Swap them. + int temp = older_id; + older_id = newer_id; + newer_id = temp; + } + + LiveObjectList *newer_lol = FindLolForId(newer_id, last()); + LiveObjectList *older_lol = FindLolForId(older_id, newer_lol); + + // If the id is defined, and we can't find a LOL for it, then we have an + // invalid id. + if ((newer_id != 0) && (newer_lol == NULL)) { + return Failure::Exception(); // Fail: the newer lol id is invalid. + } + if ((older_id != 0) && (older_lol == NULL)) { + return Failure::Exception(); // Fail: the older lol id is invalid. + } + + LolFilter filter(filter_obj); + LolDumpWriter writer(older_lol, newer_lol); + return DumpPrivate(&writer, start_idx, dump_limit, &filter); +} + + +MaybeObject* LiveObjectList::DumpPrivate(DumpWriter* writer, + int start, + int dump_limit, + LolFilter* filter) { + HandleScope scope; + + // Calculate the number of entries of the dump. + int count = -1; + int size = -1; + writer->ComputeTotalCountAndSize(filter, &count, &size); + + // Adjust for where to start the dump. + if ((start < 0) || (start >= count)) { + return Failure::Exception(); // invalid start. + } + + int remaining_count = count - start; + if (dump_limit > remaining_count) { + dump_limit = remaining_count; + } + + // Allocate an array to hold the result. + Handle elements_arr = Factory::NewFixedArray(dump_limit); + if (elements_arr->IsFailure()) return Object::cast(*elements_arr); + + // Fill in the dump. + Handle error; + bool success = writer->Write(elements_arr, + start, + dump_limit, + filter, + error); + if (!success) return Object::cast(*error); + + MaybeObject* maybe_result; + + // Allocate the result body. + Handle body = Factory::NewJSObject(Top::object_function()); + if (body->IsFailure()) return Object::cast(*body); + + // Set the updated body.count. + Handle count_sym = Factory::LookupAsciiSymbol("count"); + maybe_result = body->SetProperty(*count_sym, Smi::FromInt(count), NONE); + if (maybe_result->IsFailure()) return maybe_result; + + // Set the updated body.size if appropriate. + if (size >= 0) { + Handle size_sym = Factory::LookupAsciiSymbol("size"); + maybe_result = body->SetProperty(*size_sym, Smi::FromInt(size), NONE); + if (maybe_result->IsFailure()) return maybe_result; + } + + // Set body.first_index. + Handle first_sym = Factory::LookupAsciiSymbol("first_index"); + maybe_result = body->SetProperty(*first_sym, Smi::FromInt(start), NONE); + if (maybe_result->IsFailure()) return maybe_result; + + // Allocate the JSArray of the elements. + Handle elements = Factory::NewJSObject(Top::array_function()); + if (elements->IsFailure()) return Object::cast(*elements); + Handle::cast(elements)->SetContent(*elements_arr); + + // Set body.elements. + Handle elements_sym = Factory::LookupAsciiSymbol("elements"); + maybe_result = body->SetProperty(*elements_sym, *elements, NONE); + if (maybe_result->IsFailure()) return maybe_result; + + return *body; +} + + +MaybeObject* LiveObjectList::Summarize(int older_id, + int newer_id, + Handle filter_obj) { + if ((older_id < 0) || (newer_id < 0) || (last() == NULL)) { + return Failure::Exception(); // Fail: 0 is not a valid lol id. + } + if (newer_id < older_id) { + // They are not in the expected order. Swap them. + int temp = older_id; + older_id = newer_id; + newer_id = temp; + } + + LiveObjectList *newer_lol = FindLolForId(newer_id, last()); + LiveObjectList *older_lol = FindLolForId(older_id, newer_lol); + + // If the id is defined, and we can't find a LOL for it, then we have an + // invalid id. + if ((newer_id != 0) && (newer_lol == NULL)) { + return Failure::Exception(); // Fail: the newer lol id is invalid. + } + if ((older_id != 0) && (older_lol == NULL)) { + return Failure::Exception(); // Fail: the older lol id is invalid. + } + + LolFilter filter(filter_obj); + LolSummaryWriter writer(older_lol, newer_lol); + return SummarizePrivate(&writer, &filter, false); +} + + +// Creates a summary report for the debugger. +// Note: the SummaryWriter takes care of iterating over objects and filling in +// the summary. +MaybeObject* LiveObjectList::SummarizePrivate(SummaryWriter* writer, + LolFilter* filter, + bool is_tracking_roots) { + HandleScope scope; + MaybeObject* maybe_result; + + LiveObjectSummary summary(filter); + writer->Write(&summary); + + // The result body will look like this: + // body: { + // count: , + // size: , + // found_root: , // optional. + // found_weak_root: , // optional. + // summary: [ + // { + // desc: "", + // count: , + // size: size + // }, + // ... + // ] + // } + + // Prefetch some needed symbols. + Handle desc_sym = Factory::LookupAsciiSymbol("desc"); + Handle count_sym = Factory::LookupAsciiSymbol("count"); + Handle size_sym = Factory::LookupAsciiSymbol("size"); + Handle summary_sym = Factory::LookupAsciiSymbol("summary"); + + // Allocate the summary array. + int entries_count = summary.GetNumberOfEntries(); + Handle summary_arr = + Factory::NewFixedArray(entries_count); + if (summary_arr->IsFailure()) return Object::cast(*summary_arr); + + int idx = 0; + for (int i = 0; i < LiveObjectSummary::kNumberOfEntries; i++) { + // Allocate the summary record. + Handle detail = Factory::NewJSObject(Top::object_function()); + if (detail->IsFailure()) return Object::cast(*detail); + + // Fill in the summary record. + LiveObjectType type = static_cast(i); + int count = summary.Count(type); + if (count) { + const char* desc_cstr = GetObjectTypeDesc(type); + Handle desc = Factory::LookupAsciiSymbol(desc_cstr); + int size = summary.Size(type); + + maybe_result = detail->SetProperty(*desc_sym, *desc, NONE); + if (maybe_result->IsFailure()) return maybe_result; + maybe_result = detail->SetProperty(*count_sym, Smi::FromInt(count), NONE); + if (maybe_result->IsFailure()) return maybe_result; + maybe_result = detail->SetProperty(*size_sym, Smi::FromInt(size), NONE); + if (maybe_result->IsFailure()) return maybe_result; + + summary_arr->set(idx++, *detail); + } + } + + // Wrap the summary fixed array in a JS array. + Handle summary_obj = Factory::NewJSObject(Top::array_function()); + if (summary_obj->IsFailure()) return Object::cast(*summary_obj); + Handle::cast(summary_obj)->SetContent(*summary_arr); + + // Create the body object. + Handle body = Factory::NewJSObject(Top::object_function()); + if (body->IsFailure()) return Object::cast(*body); + + // Fill out the body object. + int total_count = summary.total_count(); + int total_size = summary.total_size(); + maybe_result = + body->SetProperty(*count_sym, Smi::FromInt(total_count), NONE); + if (maybe_result->IsFailure()) return maybe_result; + + maybe_result = body->SetProperty(*size_sym, Smi::FromInt(total_size), NONE); + if (maybe_result->IsFailure()) return maybe_result; + + if (is_tracking_roots) { + int found_root = summary.found_root(); + int found_weak_root = summary.found_weak_root(); + Handle root_sym = Factory::LookupAsciiSymbol("found_root"); + Handle weak_root_sym = + Factory::LookupAsciiSymbol("found_weak_root"); + maybe_result = + body->SetProperty(*root_sym, Smi::FromInt(found_root), NONE); + if (maybe_result->IsFailure()) return maybe_result; + maybe_result = + body->SetProperty(*weak_root_sym, Smi::FromInt(found_weak_root), NONE); + if (maybe_result->IsFailure()) return maybe_result; + } + + maybe_result = body->SetProperty(*summary_sym, *summary_obj, NONE); + if (maybe_result->IsFailure()) return maybe_result; + + return *body; +} + + +// Returns an array listing the captured lols. +// Note: only dumps the section starting at start_idx and only up to +// dump_limit entries. +MaybeObject* LiveObjectList::Info(int start_idx, int dump_limit) { + HandleScope scope; + MaybeObject* maybe_result; + + int total_count = LiveObjectList::list_count(); + int dump_count = total_count; + + // Adjust for where to start the dump. + if (total_count == 0) { + start_idx = 0; // Ensure this to get an empty list. + } else if ((start_idx < 0) || (start_idx >= total_count)) { + return Failure::Exception(); // invalid start. + } + dump_count -= start_idx; + + // Adjust for the dump limit. + if (dump_count > dump_limit) { + dump_count = dump_limit; + } + + // Allocate an array to hold the result. + Handle list = Factory::NewFixedArray(dump_count); + if (list->IsFailure()) return Object::cast(*list); + + // Prefetch some needed symbols. + Handle id_sym = Factory::LookupAsciiSymbol("id"); + Handle count_sym = Factory::LookupAsciiSymbol("count"); + Handle size_sym = Factory::LookupAsciiSymbol("size"); + + // Fill the array with the lol details. + int idx = 0; + LiveObjectList* lol = first_; + while ((lol != NULL) && (idx < start_idx)) { // Skip tail entries. + if (lol->id() != 0) { + idx++; + } + lol = lol->next(); + } + idx = 0; + while ((lol != NULL) && (dump_limit != 0)) { + if (lol->id() != 0) { + int count; + int size; + count = lol->GetTotalObjCountAndSize(&size); + + Handle detail = Factory::NewJSObject(Top::object_function()); + if (detail->IsFailure()) return Object::cast(*detail); + + maybe_result = + detail->SetProperty(*id_sym, Smi::FromInt(lol->id()), NONE); + if (maybe_result->IsFailure()) return maybe_result; + maybe_result = + detail->SetProperty(*count_sym, Smi::FromInt(count), NONE); + if (maybe_result->IsFailure()) return maybe_result; + maybe_result = detail->SetProperty(*size_sym, Smi::FromInt(size), NONE); + if (maybe_result->IsFailure()) return maybe_result; + list->set(idx++, *detail); + dump_limit--; + } + lol = lol->next(); + } + + // Return the result as a JS array. + Handle lols = Factory::NewJSObject(Top::array_function()); + Handle::cast(lols)->SetContent(*list); + + Handle result = Factory::NewJSObject(Top::object_function()); + if (result->IsFailure()) return Object::cast(*result); + + maybe_result = + result->SetProperty(*count_sym, Smi::FromInt(total_count), NONE); + if (maybe_result->IsFailure()) return maybe_result; + + Handle first_sym = Factory::LookupAsciiSymbol("first_index"); + maybe_result = + result->SetProperty(*first_sym, Smi::FromInt(start_idx), NONE); + if (maybe_result->IsFailure()) return maybe_result; + + Handle lists_sym = Factory::LookupAsciiSymbol("lists"); + maybe_result = result->SetProperty(*lists_sym, *lols, NONE); + if (maybe_result->IsFailure()) return maybe_result; + + return *result; +} + + +// Deletes all captured lols. +void LiveObjectList::Reset() { + LiveObjectList *lol = last(); + // Just delete the last. Each lol will delete it's prev automatically. + delete lol; + + next_element_id_ = 1; + list_count_ = 0; + last_id_ = 0; + first_ = NULL; + last_ = NULL; +} + + +// Gets the object for the specified obj id. +Object* LiveObjectList::GetObj(int obj_id) { + Element* element = FindElementFor(GetElementId, obj_id); + if (element != NULL) { + return Object::cast(element->obj_); + } + return Heap::undefined_value(); +} + + +// Gets the obj id for the specified address if valid. +int LiveObjectList::GetObjId(Object* obj) { + // Make a heap object pointer from the address. + HeapObject* hobj = HeapObject::cast(obj); + Element* element = FindElementFor(GetElementObj, hobj); + if (element != NULL) { + return element->id_; + } + return 0; // Invalid address. +} + + +// Gets the obj id for the specified address if valid. +Object* LiveObjectList::GetObjId(Handle address) { + SmartPointer addr_str = + address->ToCString(DISALLOW_NULLS, ROBUST_STRING_TRAVERSAL); + + // Extract the address value from the string. + int value = static_cast(StringToInt(*address, 16)); + Object* obj = reinterpret_cast(value); + return Smi::FromInt(GetObjId(obj)); +} + + +// Helper class for copying HeapObjects. +class LolVisitor: public ObjectVisitor { + public: + + LolVisitor(HeapObject* target, Handle handle_to_skip) + : target_(target), handle_to_skip_(handle_to_skip), found_(false) {} + + void VisitPointer(Object** p) { CheckPointer(p); } + + void VisitPointers(Object** start, Object** end) { + // Check all HeapObject pointers in [start, end). + for (Object** p = start; !found() && p < end; p++) CheckPointer(p); + } + + inline bool found() const { return found_; } + inline bool reset() { return found_ = false; } + + private: + inline void CheckPointer(Object** p) { + Object* object = *p; + if (HeapObject::cast(object) == target_) { + // We may want to skip this handle because the handle may be a local + // handle in a handle scope in one of our callers. Once we return, + // that handle will be popped. Hence, we don't want to count it as + // a root that would have kept the target object alive. + if (!handle_to_skip_.is_null() && + handle_to_skip_.location() == reinterpret_cast(p)) { + return; // Skip this handle. + } + found_ = true; + } + } + + HeapObject* target_; + Handle handle_to_skip_; + bool found_; +}; + + +inline bool AddRootRetainerIfFound(const LolVisitor& visitor, + LolFilter* filter, + LiveObjectSummary *summary, + void (*SetRootFound)(LiveObjectSummary *s), + int start, + int dump_limit, + int* total_count, + Handle retainers_arr, + int* count, + int* index, + const char* root_name, + Handle id_sym, + Handle desc_sym, + Handle size_sym, + Handle error) { + HandleScope scope; + + // Scratch handles. + Handle detail; + Handle desc; + Handle retainer; + + if (visitor.found()) { + if (!filter->is_active()) { + (*total_count)++; + if (summary) { + SetRootFound(summary); + } else if ((*total_count > start) && ((*index) < dump_limit)) { + (*count)++; + if (!retainers_arr.is_null()) { + return AddObjDetail(retainers_arr, + (*index)++, + 0, + retainer, + root_name, + id_sym, + desc_sym, + size_sym, + detail, + desc, + error); + } + } + } + } + return true; +} + + +inline void SetFoundRoot(LiveObjectSummary *summary) { + summary->set_found_root(); +} + + +inline void SetFoundWeakRoot(LiveObjectSummary *summary) { + summary->set_found_weak_root(); +} + + +int LiveObjectList::GetRetainers(Handle target, + Handle instance_filter, + Handle retainers_arr, + int start, + int dump_limit, + int* total_count, + LolFilter* filter, + LiveObjectSummary *summary, + JSFunction* arguments_function, + Handle error) { + HandleScope scope; + + // Scratch handles. + Handle detail; + Handle desc; + Handle retainer; + + // Prefetch some needed symbols. + Handle id_sym = Factory::LookupAsciiSymbol("id"); + Handle desc_sym = Factory::LookupAsciiSymbol("desc"); + Handle size_sym = Factory::LookupAsciiSymbol("size"); + + NoHandleAllocation ha; + int count = 0; + int index = 0; + Handle last_obj; + + *total_count = 0; + + // Iterate roots. + LolVisitor lol_visitor(*target, target); + Heap::IterateStrongRoots(&lol_visitor, VISIT_ALL); + if (!AddRootRetainerIfFound(lol_visitor, + filter, + summary, + SetFoundRoot, + start, + dump_limit, + total_count, + retainers_arr, + &count, + &index, + "", + id_sym, + desc_sym, + size_sym, + error)) { + return -1; + } + + lol_visitor.reset(); + Heap::IterateWeakRoots(&lol_visitor, VISIT_ALL); + if (!AddRootRetainerIfFound(lol_visitor, + filter, + summary, + SetFoundWeakRoot, + start, + dump_limit, + total_count, + retainers_arr, + &count, + &index, + "", + id_sym, + desc_sym, + size_sym, + error)) { + return -1; + } + + // Iterate the live object lists. + LolIterator it(NULL, last()); + for (it.Init(); !it.Done() && (index < dump_limit); it.Next()) { + HeapObject* heap_obj = it.Obj(); + + // 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(); + if (prototype->IsNull()) { + break; + } + if (*instance_filter == prototype) { + obj = NULL; // Don't add this object. + break; + } + V = prototype; + } + } + + if (obj != NULL) { + // Skip objects that have been filtered out. + if (filter->Matches(heap_obj)) { + continue; + } + + // Valid reference found add to instance array if supplied an update + // count. + last_obj = Handle(obj); + (*total_count)++; + + if (summary != NULL) { + summary->Add(heap_obj); + } else if ((*total_count > start) && (index < dump_limit)) { + count++; + if (!retainers_arr.is_null()) { + retainer = Handle(heap_obj); + bool success = AddObjDetail(retainers_arr, + index++, + it.Id(), + retainer, + NULL, + id_sym, + desc_sym, + size_sym, + detail, + desc, + error); + if (!success) return -1; + } + } + } + } + } + } + + // 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 (*total_count == 1 && !last_obj.is_null() && *last_obj == *target) { + count = 0; + *total_count = 0; + } + + return count; +} + + +MaybeObject* LiveObjectList::GetObjRetainers(int obj_id, + Handle instance_filter, + bool verbose, + int start, + int dump_limit, + Handle filter_obj) { + HandleScope scope; + + // Get the target object. + HeapObject* heap_obj = HeapObject::cast(GetObj(obj_id)); + if (heap_obj == Heap::undefined_value()) { + return heap_obj; + } + + Handle target = Handle(heap_obj); + + // Get the constructor function for context extension and arguments array. + JSObject* arguments_boilerplate = + Top::context()->global_context()->arguments_boilerplate(); + JSFunction* arguments_function = + JSFunction::cast(arguments_boilerplate->map()->constructor()); + + Handle args_function = Handle(arguments_function); + LolFilter filter(filter_obj); + + if (!verbose) { + RetainersSummaryWriter writer(target, instance_filter, args_function); + return SummarizePrivate(&writer, &filter, true); + + } else { + RetainersDumpWriter writer(target, instance_filter, args_function); + Object* body_obj; + MaybeObject* maybe_result = + DumpPrivate(&writer, start, dump_limit, &filter); + if (!maybe_result->ToObject(&body_obj)) { + return maybe_result; + } + + // Set body.id. + Handle body = Handle(JSObject::cast(body_obj)); + Handle id_sym = Factory::LookupAsciiSymbol("id"); + maybe_result = body->SetProperty(*id_sym, Smi::FromInt(obj_id), NONE); + if (maybe_result->IsFailure()) return maybe_result; + + return *body; + } +} + + +Object* LiveObjectList::PrintObj(int obj_id) { + Object* obj = GetObj(obj_id); + if (!obj) { + return Heap::undefined_value(); + } + + EmbeddedVector temp_filename; + static int temp_count = 0; + const char* path_prefix = "."; + + if (FLAG_lol_workdir) { + path_prefix = FLAG_lol_workdir; + } + OS::SNPrintF(temp_filename, "%s/lol-print-%d", path_prefix, ++temp_count); + + FILE* f = OS::FOpen(temp_filename.start(), "w+"); + + PrintF(f, "@%d ", LiveObjectList::GetObjId(obj)); +#ifdef OBJECT_PRINT +#ifdef INSPECTOR + Inspector::DumpObjectType(f, obj); +#endif // INSPECTOR + PrintF(f, "\n"); + obj->Print(f); +#else // !OBJECT_PRINT + obj->ShortPrint(f); +#endif // !OBJECT_PRINT + PrintF(f, "\n"); + Flush(f); + fclose(f); + + // Create a string from the temp_file. + // Note: the mmapped resource will take care of closing the file. + MemoryMappedExternalResource* resource = + new MemoryMappedExternalResource(temp_filename.start(), true); + if (resource->exists() && !resource->is_empty()) { + ASSERT(resource->IsAscii()); + Handle dump_string = + Factory::NewExternalStringFromAscii(resource); + ExternalStringTable::AddString(*dump_string); + return *dump_string; + } else { + delete resource; + } + return Heap::undefined_value(); +} + + +class LolPathTracer: public PathTracer { + public: + LolPathTracer(FILE* out, + Object* search_target, + WhatToFind what_to_find) + : PathTracer(search_target, what_to_find, VISIT_ONLY_STRONG), out_(out) {} + + private: + void ProcessResults(); + + FILE* out_; +}; + + +void LolPathTracer::ProcessResults() { + if (found_target_) { + PrintF(out_, "=====================================\n"); + PrintF(out_, "==== Path to object ====\n"); + PrintF(out_, "=====================================\n\n"); + + ASSERT(!object_stack_.is_empty()); + Object* prev = NULL; + for (int i = 0, index = 0; i < object_stack_.length(); i++) { + Object* obj = object_stack_[i]; + + // Skip this object if it is basically the internals of the + // previous object (which would have dumped its details already). + if (prev && prev->IsJSObject() && + (obj != search_target_)) { + JSObject* jsobj = JSObject::cast(prev); + if (obj->IsFixedArray() && + jsobj->properties() == FixedArray::cast(obj)) { + // Skip this one because it would have been printed as the + // properties of the last object already. + continue; + } else if (obj->IsHeapObject() && + jsobj->elements() == HeapObject::cast(obj)) { + // Skip this one because it would have been printed as the + // elements of the last object already. + continue; + } + } + + // Print a connecting arrow. + if (i > 0) PrintF(out_, "\n |\n |\n V\n\n"); + + // Print the object index. + PrintF(out_, "[%d] ", ++index); + + // Print the LOL object ID: + int id = LiveObjectList::GetObjId(obj); + if (id > 0) PrintF(out_, "@%d ", id); + +#ifdef OBJECT_PRINT +#ifdef INSPECTOR + Inspector::DumpObjectType(out_, obj); +#endif // INSPECTOR + PrintF(out_, "\n"); + obj->Print(out_); +#else // !OBJECT_PRINT + obj->ShortPrint(out_); + PrintF(out_, "\n"); +#endif // !OBJECT_PRINT + Flush(out_); + } + PrintF(out_, "\n"); + PrintF(out_, "=====================================\n\n"); + Flush(out_); + } +} + + +Object* LiveObjectList::GetPathPrivate(HeapObject* obj1, HeapObject* obj2) { + EmbeddedVector temp_filename; + static int temp_count = 0; + const char* path_prefix = "."; + + if (FLAG_lol_workdir) { + path_prefix = FLAG_lol_workdir; + } + OS::SNPrintF(temp_filename, "%s/lol-getpath-%d", path_prefix, ++temp_count); + + FILE* f = OS::FOpen(temp_filename.start(), "w+"); + + // Save the previous verbosity. + bool prev_verbosity = FLAG_use_verbose_printer; + FLAG_use_verbose_printer = false; + + // Dump the paths. + { + // The tracer needs to be scoped because its usage asserts no allocation, + // and we need to allocate the result string below. + LolPathTracer tracer(f, obj2, LolPathTracer::FIND_FIRST); + + bool found = false; + if (obj1 == NULL) { + // Check for ObjectGroups that references this object. + // TODO(mlam): refactor this to be more modular. + { + List* groups = GlobalHandles::ObjectGroups(); + for (int i = 0; i < groups->length(); i++) { + ObjectGroup* group = groups->at(i); + if (group == NULL) continue; + + bool found_group = false; + List& objects = group->objects_; + for (int j = 0; j < objects.length(); j++) { + Object* object = *objects[j]; + HeapObject* hobj = HeapObject::cast(object); + if (obj2 == hobj) { + found_group = true; + break; + } + } + + if (found_group) { + PrintF(f, + "obj %p is a member of object group %p {\n", + reinterpret_cast(obj2), + reinterpret_cast(group)); + for (int j = 0; j < objects.length(); j++) { + Object* object = *objects[j]; + if (!object->IsHeapObject()) continue; + + HeapObject* hobj = HeapObject::cast(object); + int id = GetObjId(hobj); + if (id != 0) { + PrintF(f, " @%d:", id); + } else { + PrintF(f, " :"); + } + + char buffer[512]; + GenerateObjectDesc(hobj, buffer, sizeof(buffer)); + PrintF(f, " %s", buffer); + if (hobj == obj2) { + PrintF(f, " <==="); + } + PrintF(f, "\n"); + } + PrintF(f, "}\n"); + } + } + } + + PrintF(f, "path from roots to obj %p\n", reinterpret_cast(obj2)); + Heap::IterateRoots(&tracer, VISIT_ONLY_STRONG); + found = tracer.found(); + + if (!found) { + PrintF(f, " No paths found. Checking symbol tables ...\n"); + SymbolTable* symbol_table = Heap::raw_unchecked_symbol_table(); + tracer.VisitPointers(reinterpret_cast(&symbol_table), + reinterpret_cast(&symbol_table)+1); + found = tracer.found(); + if (!found) { + symbol_table->IteratePrefix(&tracer); + found = tracer.found(); + } + } + + if (!found) { + PrintF(f, " No paths found. Checking weak roots ...\n"); + // Check weak refs next. + GlobalHandles::IterateWeakRoots(&tracer); + found = tracer.found(); + } + + } else { + PrintF(f, "path from obj %p to obj %p:\n", + reinterpret_cast(obj1), reinterpret_cast(obj2)); + tracer.TracePathFrom(reinterpret_cast(&obj1)); + found = tracer.found(); + } + + if (!found) { + PrintF(f, " No paths found\n\n"); + } + } + + // Flush and clean up the dumped file. + Flush(f); + fclose(f); + + // Restore the previous verbosity. + FLAG_use_verbose_printer = prev_verbosity; + + // Create a string from the temp_file. + // Note: the mmapped resource will take care of closing the file. + MemoryMappedExternalResource* resource = + new MemoryMappedExternalResource(temp_filename.start(), true); + if (resource->exists() && !resource->is_empty()) { + ASSERT(resource->IsAscii()); + Handle path_string = + Factory::NewExternalStringFromAscii(resource); + ExternalStringTable::AddString(*path_string); + return *path_string; + } else { + delete resource; + } + return Heap::undefined_value(); +} + + +Object* LiveObjectList::GetPath(int obj_id1, + int obj_id2, + Handle instance_filter) { + HandleScope scope; + + // Get the target object. + HeapObject* obj1 = NULL; + if (obj_id1 != 0) { + obj1 = HeapObject::cast(GetObj(obj_id1)); + if (obj1 == Heap::undefined_value()) { + return obj1; + } + } + + HeapObject* obj2 = HeapObject::cast(GetObj(obj_id2)); + if (obj2 == Heap::undefined_value()) { + return obj2; + } + + return GetPathPrivate(obj1, obj2); +} + + +void LiveObjectList::DoProcessNonLive(HeapObject *obj) { + // We should only be called if we have at least one lol to search. + ASSERT(last() != NULL); + Element* element = last()->Find(obj); + if (element != NULL) { + NullifyNonLivePointer(&element->obj_); + } +} + + +void LiveObjectList::IterateElementsPrivate(ObjectVisitor* v) { + LiveObjectList* lol = last(); + while (lol != NULL) { + Element* elements = lol->elements_; + int count = lol->obj_count_; + for (int i = 0; i < count; i++) { + HeapObject** p = &elements[i].obj_; + v->VisitPointer(reinterpret_cast(p)); + } + lol = lol->prev_; + } +} + + +// Purpose: Called by GCEpilogue to purge duplicates. Not to be called by +// anyone else. +void LiveObjectList::PurgeDuplicates() { + bool is_sorted = false; + LiveObjectList* lol = last(); + if (!lol) { + return; // Nothing to purge. + } + + int total_count = lol->TotalObjCount(); + if (!total_count) { + return; // Nothing to purge. + } + + Element* elements = NewArray(total_count); + int count = 0; + + // Copy all the object elements into a consecutive array. + while (lol) { + memcpy(&elements[count], lol->elements_, lol->obj_count_ * sizeof(Element)); + count += lol->obj_count_; + lol = lol->prev_; + } + qsort(elements, total_count, sizeof(Element), + reinterpret_cast(CompareElement)); + + ASSERT(count == total_count); + + // Iterate over all objects in the consolidated list and check for dups. + total_count--; + for (int i = 0; i < total_count; ) { + Element* curr = &elements[i]; + HeapObject* curr_obj = curr->obj_; + int j = i+1; + bool done = false; + + while (!done && (j < total_count)) { + // Process if the element's object is still live after the current GC. + // Non-live objects will be converted to SMIs i.e. not HeapObjects. + if (curr_obj->IsHeapObject()) { + Element* next = &elements[j]; + HeapObject* next_obj = next->obj_; + if (next_obj->IsHeapObject()) { + if (curr_obj != next_obj) { + done = true; + continue; // Live object but no match. Move on. + } + + // NOTE: we've just GCed the LOLs. Hence, they are no longer sorted. + // Since we detected at least one need to search for entries, we'll + // sort it to enable the use of NullifyMostRecent() below. We only + // need to sort it once (except for one exception ... see below). + if (!is_sorted) { + SortAll(); + is_sorted = true; + } + + // We have a match. Need to nullify the most recent ref to this + // object. We'll keep the oldest ref: + // Note: we will nullify the element record in the LOL + // database, not in the local sorted copy of the elements. + NullifyMostRecent(curr_obj); + } + } + // Either the object was already marked for purging, or we just marked + // it. Either way, if there's more than one dup, then we need to check + // the next element for another possible dup against the current as well + // before we move on. So, here we go. + j++; + } + + // We can move on to checking the match on the next element. + i = j; + } + + DeleteArray(elements); +} + + +// Purpose: Purges dead objects and resorts the LOLs. +void LiveObjectList::GCEpiloguePrivate() { + // Note: During the GC, ConsStrings may be collected and pointers may be + // forwarded to its constituent string. As a result, we may find dupes of + // objects references in the LOL list. + // Another common way we get dups is that free chunks that have been swept + // in the oldGen heap may be kept as ByteArray objects in a free list. + // + // When we promote live objects from the youngGen, the object may be moved + // to the start of these free chunks. Since there is no free or move event + // for the free chunks, their addresses will show up 2 times: once for their + // original free ByteArray selves, and once for the newly promoted youngGen + // object. Hence, we can get a duplicate address in the LOL again. + // + // We need to eliminate these dups because the LOL implementation expects to + // only have at most one unique LOL reference to any object at any time. + PurgeDuplicates(); + + // After the GC, sweep away all free'd Elements and compact. + LiveObjectList *prev = NULL; + LiveObjectList *next = NULL; + + // Iterating from the youngest lol to the oldest lol. + for (LiveObjectList *lol = last(); lol; lol = prev) { + Element* elements = lol->elements_; + prev = lol->prev(); // Save the prev. + + // Remove any references to collected objects. + int i = 0; + while (i < lol->obj_count_) { + Element& element = elements[i]; + if (!element.obj_->IsHeapObject()) { + // If the HeapObject address was converted into a SMI, then this + // is a dead object. Copy the last element over this one. + element = elements[lol->obj_count_ - 1]; + lol->obj_count_--; + // We've just moved the last element into this index. We'll revisit + // this index again. Hence, no need to increment the iterator. + } else { + i++; // Look at the next element next. + } + } + + int new_count = lol->obj_count_; + + // Check if there are any more elements to keep after purging the dead ones. + if (new_count == 0) { + DeleteArray(elements); + lol->elements_ = NULL; + lol->capacity_ = 0; + ASSERT(lol->obj_count_ == 0); + + // If the list is also invisible, the clean up the list as well. + if (lol->id_ == 0) { + // Point the next lol's prev to this lol's prev. + if (next) { + next->prev_ = lol->prev_; + } else { + last_ = lol->prev_; + } + + // Delete this now empty and invisible lol. + delete lol; + + // Don't point the next to this lol since it is now deleted. + // Leave the next pointer pointing to the current lol. + continue; + } + + } else { + // If the obj_count_ is less than the capacity and the difference is + // greater than a specified threshold, then we should shrink the list. + int diff = lol->capacity_ - new_count; + const int kMaxUnusedSpace = 64; + if (diff > kMaxUnusedSpace) { // Threshold for shrinking. + // Shrink the list. + Element *new_elements = NewArray(new_count); + memcpy(new_elements, elements, new_count * sizeof(Element)); + + DeleteArray(elements); + lol->elements_ = new_elements; + lol->capacity_ = new_count; + } + ASSERT(lol->obj_count_ == new_count); + + lol->Sort(); // We've moved objects. Re-sort in case. + } + + // Save the next (for the previous link) in case we need it later. + next = lol; + } + +#ifdef VERIFY_LOL + if (FLAG_verify_lol) { + Verify(); + } +#endif +} + + +#ifdef VERIFY_LOL +void LiveObjectList::Verify(bool match_heap_exactly) { + OS::Print("Verifying the LiveObjectList database:\n"); + + LiveObjectList* lol = last(); + if (lol == NULL) { + OS::Print(" No lol database to verify\n"); + return; + } + + OS::Print(" Preparing the lol database ...\n"); + int total_count = lol->TotalObjCount(); + + Element* elements = NewArray(total_count); + int count = 0; + + // Copy all the object elements into a consecutive array. + OS::Print(" Copying the lol database ...\n"); + while (lol != NULL) { + memcpy(&elements[count], lol->elements_, lol->obj_count_ * sizeof(Element)); + count += lol->obj_count_; + lol = lol->prev_; + } + qsort(elements, total_count, sizeof(Element), + reinterpret_cast(CompareElement)); + + ASSERT(count == total_count); + + // Iterate over all objects in the heap and check for: + // 1. object in LOL but not in heap i.e. error. + // 2. object in heap but not in LOL (possibly not an error). Usually + // just means that we don't have the a capture of the latest heap. + // That is unless we did this verify immediately after a capture, + // and specified match_heap_exactly = true. + + int number_of_heap_objects = 0; + int number_of_matches = 0; + int number_not_in_heap = total_count; + int number_not_in_lol = 0; + + OS::Print(" Start verify ...\n"); + OS::Print(" Verifying ..."); + Flush(); + HeapIterator iterator(HeapIterator::kFilterFreeListNodes); + HeapObject* heap_obj = NULL; + while ((heap_obj = iterator.next()) != NULL) { + number_of_heap_objects++; + + // Check if the heap_obj is in the lol. + Element key; + key.obj_ = heap_obj; + + Element* result = reinterpret_cast( + bsearch(&key, elements, total_count, sizeof(Element), + reinterpret_cast(CompareElement))); + + if (result != NULL) { + number_of_matches++; + number_not_in_heap--; + // Mark it as found by changing it into a SMI (mask off low bit). + // Note: we cannot use HeapObject::cast() here because it asserts that + // the HeapObject bit is set on the address, but we're unsetting it on + // purpose here for our marking. + result->obj_ = reinterpret_cast(heap_obj->address()); + + } else { + number_not_in_lol++; + if (match_heap_exactly) { + OS::Print("heap object %p NOT in lol database\n", heap_obj); + } + } + // Show some sign of life. + if (number_of_heap_objects % 1000 == 0) { + OS::Print("."); + fflush(stdout); + } + } + OS::Print("\n"); + + // Reporting lol objects not found in the heap. + if (number_not_in_heap) { + int found = 0; + for (int i = 0; (i < total_count) && (found < number_not_in_heap); i++) { + Element& element = elements[i]; + if (element.obj_->IsHeapObject()) { + OS::Print("lol database object [%d of %d] %p NOT in heap\n", + i, total_count, element.obj_); + found++; + } + } + } + + DeleteArray(elements); + + OS::Print("number of objects in lol database %d\n", total_count); + OS::Print("number of heap objects .......... %d\n", number_of_heap_objects); + OS::Print("number of matches ............... %d\n", number_of_matches); + OS::Print("number NOT in heap .............. %d\n", number_not_in_heap); + OS::Print("number NOT in lol database ...... %d\n", number_not_in_lol); + + if (number_of_matches != total_count) { + OS::Print(" *** ERROR: " + "NOT all lol database objects match heap objects.\n"); + } + if (number_not_in_heap != 0) { + OS::Print(" *** ERROR: %d lol database objects not found in heap.\n", + number_not_in_heap); + } + if (match_heap_exactly) { + if (!(number_not_in_lol == 0)) { + OS::Print(" *** ERROR: %d heap objects NOT found in lol database.\n", + number_not_in_lol); + } + } + + ASSERT(number_of_matches == total_count); + ASSERT(number_not_in_heap == 0); + ASSERT(number_not_in_lol == (number_of_heap_objects - total_count)); + if (match_heap_exactly) { + ASSERT(total_count == number_of_heap_objects); + ASSERT(number_not_in_lol == 0); + } + + OS::Print(" Verify the lol database is sorted ...\n"); + lol = last(); + while (lol != NULL) { + Element* elements = lol->elements_; + for (int i = 0; i < lol->obj_count_ - 1; i++) { + if (elements[i].obj_ >= elements[i+1].obj_) { + OS::Print(" *** ERROR: lol %p obj[%d] %p > obj[%d] %p\n", + lol, i, elements[i].obj_, i+1, elements[i+1].obj_); + } + } + lol = lol->prev_; + } + + OS::Print(" DONE verifying.\n\n\n"); +} + + +void LiveObjectList::VerifyNotInFromSpace() { + OS::Print("VerifyNotInFromSpace() ...\n"); + LolIterator it(NULL, last()); + int i = 0; + for (it.Init(); !it.Done(); it.Next()) { + HeapObject* heap_obj = it.Obj(); + if (Heap::InFromSpace(heap_obj)) { + OS::Print(" ERROR: VerifyNotInFromSpace: [%d] obj %p in From space %p\n", + i++, heap_obj, Heap::new_space()->FromSpaceLow()); + } + } +} +#endif // VERIFY_LOL + } } // namespace v8::internal diff --git a/deps/v8/src/liveobjectlist.h b/deps/v8/src/liveobjectlist.h index 11f5c45178..423f8f0d74 100644 --- a/deps/v8/src/liveobjectlist.h +++ b/deps/v8/src/liveobjectlist.h @@ -40,54 +40,225 @@ namespace internal { #ifdef LIVE_OBJECT_LIST +#ifdef DEBUG +// The following symbol when defined enables thorough verification of lol data. +// FLAG_verify_lol will also need to set to true to enable the verification. +#define VERIFY_LOL +#endif -// Temporary stubbed out LiveObjectList implementation. + +typedef int LiveObjectType; +class LolFilter; +class LiveObjectSummary; +class DumpWriter; +class SummaryWriter; + + +// The LiveObjectList is both a mechanism for tracking a live capture of +// objects in the JS heap, as well as is the data structure which represents +// each of those captures. Unlike a snapshot, the lol is live. For example, +// if an object in a captured lol dies and is collected by the GC, the lol +// will reflect that the object is no longer available. The term +// LiveObjectList (and lol) is used to describe both the mechanism and the +// data structure depending on context of use. +// +// In captured lols, objects are tracked using their address and an object id. +// The object id is unique. Once assigned to an object, the object id can never +// be assigned to another object. That is unless all captured lols are deleted +// which allows the user to start over with a fresh set of lols and object ids. +// The uniqueness of the object ids allows the user to track specific objects +// and inspect its longevity while debugging JS code in execution. +// +// The lol comes with utility functions to capture, dump, summarize, and diff +// captured lols amongst other functionality. These functionality are +// accessible via the v8 debugger interface. class LiveObjectList { public: - inline static void GCEpilogue() {} - inline static void GCPrologue() {} - inline static void IterateElements(ObjectVisitor* v) {} - inline static void ProcessNonLive(HeapObject *obj) {} - inline static void UpdateReferencesForScavengeGC() {} + inline static void GCEpilogue(); + inline static void GCPrologue(); + inline static void IterateElements(ObjectVisitor* v); + inline static void ProcessNonLive(HeapObject *obj); + inline static void UpdateReferencesForScavengeGC(); + + // Note: LOLs can be listed by calling Dump(0, ), and 2 LOLs can be + // compared/diff'ed using Dump(, , ...). This will yield + // a verbose dump of all the objects in the resultant lists. + // Similarly, a summarized result of a LOL listing or a diff can be + // attained using the Summarize(0, ) and Summarize(, ...) respectively. - static MaybeObject* Capture() { return Heap::undefined_value(); } - static bool Delete(int id) { return false; } + static MaybeObject* Capture(); + static bool Delete(int id); static MaybeObject* Dump(int id1, int id2, int start_idx, int dump_limit, - Handle filter_obj) { - return Heap::undefined_value(); - } - static MaybeObject* Info(int start_idx, int dump_limit) { - return Heap::undefined_value(); - } - static MaybeObject* Summarize(int id1, - int id2, - Handle filter_obj) { - return Heap::undefined_value(); - } + Handle filter_obj); + static MaybeObject* Info(int start_idx, int dump_limit); + static MaybeObject* Summarize(int id1, int id2, Handle filter_obj); - static void Reset() {} - static Object* GetObj(int obj_id) { return Heap::undefined_value(); } - static Object* GetObjId(Handle address) { - return Heap::undefined_value(); - } + static void Reset(); + static Object* GetObj(int obj_id); + static int GetObjId(Object* obj); + static Object* GetObjId(Handle address); static MaybeObject* GetObjRetainers(int obj_id, Handle instance_filter, bool verbose, int start, int count, - Handle filter_obj) { - return Heap::undefined_value(); - } + Handle filter_obj); static Object* GetPath(int obj_id1, int obj_id2, - Handle instance_filter) { - return Heap::undefined_value(); + Handle instance_filter); + static Object* PrintObj(int obj_id); + + private: + + struct Element { + int id_; + HeapObject* obj_; + }; + + explicit LiveObjectList(LiveObjectList* prev, int capacity); + ~LiveObjectList(); + + static void GCEpiloguePrivate(); + static void IterateElementsPrivate(ObjectVisitor* v); + + static void DoProcessNonLive(HeapObject *obj); + + static int CompareElement(const Element* a, const Element* b); + + static Object* GetPathPrivate(HeapObject* obj1, HeapObject* obj2); + + static int GetRetainers(Handle target, + Handle instance_filter, + Handle retainers_arr, + int start, + int dump_limit, + int* total_count, + LolFilter* filter, + LiveObjectSummary *summary, + JSFunction* arguments_function, + Handle error); + + static MaybeObject* DumpPrivate(DumpWriter* writer, + int start, + int dump_limit, + LolFilter* filter); + static MaybeObject* SummarizePrivate(SummaryWriter* writer, + LolFilter* filter, + bool is_tracking_roots); + + static bool NeedLOLProcessing() { return (last() != NULL); } + static void NullifyNonLivePointer(HeapObject **p) { + // Mask out the low bit that marks this as a heap object. We'll use this + // cleared bit as an indicator that this pointer needs to be collected. + // + // Meanwhile, we still preserve its approximate value so that we don't + // have to resort the elements list all the time. + // + // Note: Doing so also makes this HeapObject* look like an SMI. Hence, + // GC pointer updater will ignore it when it gets scanned. + *p = reinterpret_cast((*p)->address()); + } + + LiveObjectList* prev() { return prev_; } + LiveObjectList* next() { return next_; } + int id() { return id_; } + + static int list_count() { return list_count_; } + static LiveObjectList* last() { return last_; } + + inline static LiveObjectList* FindLolForId(int id, LiveObjectList* start_lol); + int TotalObjCount() { return GetTotalObjCountAndSize(NULL); } + int GetTotalObjCountAndSize(int* size_p); + + bool Add(HeapObject* obj); + Element* Find(HeapObject* obj); + static void NullifyMostRecent(HeapObject* obj); + void Sort(); + static void SortAll(); + + static void PurgeDuplicates(); // Only to be called by GCEpilogue. + +#ifdef VERIFY_LOL + static void Verify(bool match_heap_exactly = false); + static void VerifyNotInFromSpace(); +#endif + + // Iterates the elements in every lol and returns the one that matches the + // specified key. If no matching element is found, then it returns NULL. + template + inline static LiveObjectList::Element* + FindElementFor(T (*GetValue)(LiveObjectList::Element*), T key); + + inline static int GetElementId(Element* element); + inline static HeapObject* GetElementObj(Element* element); + + // Instance fields. + LiveObjectList* prev_; + LiveObjectList* next_; + int id_; + int capacity_; + int obj_count_; + Element *elements_; + + // Statics for managing all the lists. + static uint32_t next_element_id_; + static int list_count_; + static int last_id_; + static LiveObjectList* first_; + static LiveObjectList* last_; + + friend class LolIterator; + friend class LolForwardIterator; + friend class LolDumpWriter; + friend class RetainersDumpWriter; + friend class RetainersSummaryWriter; + friend class UpdateLiveObjectListVisitor; +}; + + +// Helper class for updating the LiveObjectList HeapObject pointers. +class UpdateLiveObjectListVisitor: public ObjectVisitor { + public: + + void VisitPointer(Object** p) { UpdatePointer(p); } + + void VisitPointers(Object** start, Object** end) { + // Copy all HeapObject pointers in [start, end). + for (Object** p = start; p < end; p++) UpdatePointer(p); + } + + private: + // Based on Heap::ScavengeObject() but only does forwarding of pointers + // to live new space objects, and not actually keep them alive. + void UpdatePointer(Object** p) { + Object* object = *p; + if (!Heap::InNewSpace(object)) return; + + HeapObject* heap_obj = HeapObject::cast(object); + ASSERT(Heap::InFromSpace(heap_obj)); + + // 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 = heap_obj->map_word(); + + // If the first word is a forwarding address, the object has already been + // copied. + if (first_word.IsForwardingAddress()) { + *p = first_word.ToForwardingAddress(); + return; + + // Else, it's a dead object. + } else { + LiveObjectList::NullifyNonLivePointer(reinterpret_cast(p)); + } } - static Object* PrintObj(int obj_id) { return Heap::undefined_value(); } }; @@ -96,11 +267,50 @@ class LiveObjectList { class LiveObjectList { public: - static void GCEpilogue() {} - static void GCPrologue() {} - static void IterateElements(ObjectVisitor* v) {} - static void ProcessNonLive(HeapObject *obj) {} - static void UpdateReferencesForScavengeGC() {} + inline static void GCEpilogue() {} + inline static void GCPrologue() {} + inline static void IterateElements(ObjectVisitor* v) {} + inline static void ProcessNonLive(HeapObject* obj) {} + inline static void UpdateReferencesForScavengeGC() {} + + inline static MaybeObject* Capture() { return Heap::undefined_value(); } + inline static bool Delete(int id) { return false; } + inline static MaybeObject* Dump(int id1, + int id2, + int start_idx, + int dump_limit, + Handle filter_obj) { + return Heap::undefined_value(); + } + inline static MaybeObject* Info(int start_idx, int dump_limit) { + return Heap::undefined_value(); + } + inline static MaybeObject* Summarize(int id1, + int id2, + Handle filter_obj) { + return Heap::undefined_value(); + } + + inline static void Reset() {} + inline static Object* GetObj(int obj_id) { return Heap::undefined_value(); } + inline static Object* GetObjId(Handle address) { + return Heap::undefined_value(); + } + inline static MaybeObject* GetObjRetainers(int obj_id, + Handle instance_filter, + bool verbose, + int start, + int count, + Handle filter_obj) { + return Heap::undefined_value(); + } + + inline static Object* GetPath(int obj_id1, + int obj_id2, + Handle instance_filter) { + return Heap::undefined_value(); + } + inline static Object* PrintObj(int obj_id) { return Heap::undefined_value(); } }; diff --git a/deps/v8/src/mark-compact.cc b/deps/v8/src/mark-compact.cc index a3b769a8bd..a4c782c59e 100644 --- a/deps/v8/src/mark-compact.cc +++ b/deps/v8/src/mark-compact.cc @@ -1353,6 +1353,9 @@ void MarkCompactCollector::MarkLiveObjects() { // Flush code from collected candidates. FlushCode::ProcessCandidates(); + + // Clean up dead objects from the runtime profiler. + RuntimeProfiler::RemoveDeadSamples(); } @@ -1937,6 +1940,9 @@ static void SweepNewSpace(NewSpace* space) { // All pointers were updated. Update auxiliary allocation info. Heap::IncrementYoungSurvivorsCounter(survivors_size); space->set_age_mark(space->top()); + + // Update JSFunction pointers from the runtime profiler. + RuntimeProfiler::UpdateSamplesAfterScavenge(); } @@ -2535,6 +2541,7 @@ void MarkCompactCollector::UpdatePointers() { state_ = UPDATE_POINTERS; #endif UpdatingVisitor updating_visitor; + RuntimeProfiler::UpdateSamplesAfterCompact(&updating_visitor); Heap::IterateRoots(&updating_visitor, VISIT_ONLY_STRONG); GlobalHandles::IterateWeakRoots(&updating_visitor); diff --git a/deps/v8/src/messages.js b/deps/v8/src/messages.js index b7e57aa228..2c94912fdf 100644 --- a/deps/v8/src/messages.js +++ b/deps/v8/src/messages.js @@ -226,6 +226,10 @@ function FormatMessage(message) { 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"], }; } var message_type = %MessageGetType(message); @@ -1059,8 +1063,8 @@ function errorToString() { } } -%FunctionSetName(errorToString, 'toString'); -%SetProperty($Error.prototype, 'toString', errorToString, DONT_ENUM); + +InstallFunctions($Error.prototype, DONT_ENUM, ['toString', errorToString]); // Boilerplate for exceptions for stack overflows. Used from // Top::StackOverflow(). diff --git a/deps/v8/src/objects-inl.h b/deps/v8/src/objects-inl.h index f955d334d2..dedb199568 100644 --- a/deps/v8/src/objects-inl.h +++ b/deps/v8/src/objects-inl.h @@ -2619,7 +2619,8 @@ Code::Flags Code::ComputeFlags(Kind kind, ASSERT(extra_ic_state == kNoExtraICState || (kind == CALL_IC && (ic_state == MONOMORPHIC || ic_state == MONOMORPHIC_PROTOTYPE_FAILURE)) || - (kind == STORE_IC)); + (kind == STORE_IC) || + (kind == KEYED_STORE_IC)); // Compute the bit mask. int bits = kind << kFlagsKindShift; if (in_loop) bits |= kFlagsICInLoopMask; @@ -3741,7 +3742,8 @@ MaybeObject* JSObject::SetHiddenPropertiesObject(Object* hidden_obj) { ASSERT(!IsJSGlobalProxy()); return SetPropertyPostInterceptor(Heap::hidden_symbol(), hidden_obj, - DONT_ENUM); + DONT_ENUM, + kNonStrictMode); } diff --git a/deps/v8/src/objects.cc b/deps/v8/src/objects.cc index 0b1d72a920..0b7f60a908 100644 --- a/deps/v8/src/objects.cc +++ b/deps/v8/src/objects.cc @@ -1444,14 +1444,15 @@ MaybeObject* JSObject::AddProperty(String* name, MaybeObject* JSObject::SetPropertyPostInterceptor( String* name, Object* value, - PropertyAttributes attributes) { + PropertyAttributes attributes, + StrictModeFlag strict) { // Check local property, ignore interceptor. LookupResult result; LocalLookupRealNamedProperty(name, &result); if (result.IsFound()) { // An existing property, a map transition or a null descriptor was // found. Use set property to handle all these cases. - return SetProperty(&result, name, value, attributes); + return SetProperty(&result, name, value, attributes, strict); } // Add a new real property. return AddProperty(name, value, attributes); @@ -1576,7 +1577,8 @@ MaybeObject* JSObject::ConvertDescriptorToField(String* name, MaybeObject* JSObject::SetPropertyWithInterceptor( String* name, Object* value, - PropertyAttributes attributes) { + PropertyAttributes attributes, + StrictModeFlag strict) { HandleScope scope; Handle this_handle(this); Handle name_handle(name); @@ -1605,7 +1607,8 @@ MaybeObject* JSObject::SetPropertyWithInterceptor( MaybeObject* raw_result = this_handle->SetPropertyPostInterceptor(*name_handle, *value_handle, - attributes); + attributes, + strict); RETURN_IF_SCHEDULED_EXCEPTION(); return raw_result; } @@ -1613,10 +1616,11 @@ MaybeObject* JSObject::SetPropertyWithInterceptor( MaybeObject* JSObject::SetProperty(String* name, Object* value, - PropertyAttributes attributes) { + PropertyAttributes attributes, + StrictModeFlag strict) { LookupResult result; LocalLookup(name, &result); - return SetProperty(&result, name, value, attributes); + return SetProperty(&result, name, value, attributes, strict); } @@ -1896,7 +1900,8 @@ MaybeObject* JSObject::SetPropertyWithFailedAccessCheck(LookupResult* result, MaybeObject* JSObject::SetProperty(LookupResult* result, String* name, Object* value, - PropertyAttributes attributes) { + PropertyAttributes attributes, + StrictModeFlag strict) { // Make sure that the top context does not change when doing callbacks or // interceptor calls. AssertNoContextChange ncc; @@ -1923,7 +1928,8 @@ MaybeObject* JSObject::SetProperty(LookupResult* result, Object* proto = GetPrototype(); if (proto->IsNull()) return value; ASSERT(proto->IsJSGlobalObject()); - return JSObject::cast(proto)->SetProperty(result, name, value, attributes); + return JSObject::cast(proto)->SetProperty( + result, name, value, attributes, strict); } if (!result->IsProperty() && !IsJSContextExtensionObject()) { @@ -1942,7 +1948,19 @@ MaybeObject* JSObject::SetProperty(LookupResult* result, // Neither properties nor transitions found. return AddProperty(name, value, attributes); } - if (result->IsReadOnly() && result->IsProperty()) return value; + if (result->IsReadOnly() && result->IsProperty()) { + if (strict == kStrictMode) { + HandleScope scope; + Handle key(name); + Handle holder(this); + Handle args[2] = { key, holder }; + return Top::Throw(*Factory::NewTypeError("strict_read_only_property", + HandleVector(args, 2))); + + } else { + return value; + } + } // 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. switch (result->type()) { @@ -1970,7 +1988,7 @@ MaybeObject* JSObject::SetProperty(LookupResult* result, value, result->holder()); case INTERCEPTOR: - return SetPropertyWithInterceptor(name, value, attributes); + return SetPropertyWithInterceptor(name, value, attributes, strict); case CONSTANT_TRANSITION: { // If the same constant function is being added we can simply // transition to the target map. @@ -6287,7 +6305,8 @@ void Code::PrintExtraICState(FILE* out, Kind kind, ExtraICState extra) { } break; case STORE_IC: - if (extra == StoreIC::kStoreICStrict) { + case KEYED_STORE_IC: + if (extra == kStrictMode) { name = "STRICT"; } break; diff --git a/deps/v8/src/objects.h b/deps/v8/src/objects.h index fbfc5fdc25..de15a7398d 100644 --- a/deps/v8/src/objects.h +++ b/deps/v8/src/objects.h @@ -1361,11 +1361,13 @@ class JSObject: public HeapObject { MUST_USE_RESULT MaybeObject* SetProperty(String* key, Object* value, - PropertyAttributes attributes); + PropertyAttributes attributes, + StrictModeFlag strict); MUST_USE_RESULT MaybeObject* SetProperty(LookupResult* result, String* key, Object* value, - PropertyAttributes attributes); + PropertyAttributes attributes, + StrictModeFlag strict); MUST_USE_RESULT MaybeObject* SetPropertyWithFailedAccessCheck( LookupResult* result, String* name, @@ -1380,11 +1382,13 @@ class JSObject: public HeapObject { MUST_USE_RESULT MaybeObject* SetPropertyWithInterceptor( String* name, Object* value, - PropertyAttributes attributes); + PropertyAttributes attributes, + StrictModeFlag strict); MUST_USE_RESULT MaybeObject* SetPropertyPostInterceptor( String* name, Object* value, - PropertyAttributes attributes); + PropertyAttributes attributes, + StrictModeFlag strict); MUST_USE_RESULT MaybeObject* SetLocalPropertyIgnoreAttributes( String* key, Object* value, diff --git a/deps/v8/src/parser.cc b/deps/v8/src/parser.cc index 249c9ced35..8560310702 100644 --- a/deps/v8/src/parser.cc +++ b/deps/v8/src/parser.cc @@ -1106,7 +1106,20 @@ void* Parser::ParseSourceElements(ZoneList* processor, } Scanner::Location token_loc = scanner().peek_location(); - Statement* stat = ParseStatement(NULL, CHECK_OK); + + Statement* stat; + if (peek() == Token::FUNCTION) { + // FunctionDeclaration is only allowed in the context of SourceElements + // (Ecma 262 5th Edition, clause 14): + // SourceElement: + // Statement + // FunctionDeclaration + // Common language extension is to allow function declaration in place + // of any statement. This language extension is disabled in strict mode. + stat = ParseFunctionDeclaration(CHECK_OK); + } else { + stat = ParseStatement(NULL, CHECK_OK); + } if (stat == NULL || stat->IsEmpty()) { directive_prologue = false; // End of directive prologue. @@ -1263,8 +1276,17 @@ Statement* Parser::ParseStatement(ZoneStringList* labels, bool* ok) { return result; } - case Token::FUNCTION: + case Token::FUNCTION: { + // In strict mode, FunctionDeclaration is only allowed in the context + // of SourceElements. + if (temp_scope_->StrictMode()) { + ReportMessageAt(scanner().peek_location(), "strict_function", + Vector::empty()); + *ok = false; + return NULL; + } return ParseFunctionDeclaration(ok); + } case Token::NATIVE: return ParseNativeDeclaration(ok); @@ -1515,6 +1537,11 @@ Block* Parser::ParseVariableDeclarations(bool accept_IN, Consume(Token::VAR); } else if (peek() == Token::CONST) { Consume(Token::CONST); + if (temp_scope_->StrictMode()) { + ReportMessage("strict_const", Vector::empty()); + *ok = false; + return NULL; + } mode = Variable::CONST; is_const = true; } else { @@ -1634,34 +1661,49 @@ Block* Parser::ParseVariableDeclarations(bool accept_IN, if (top_scope_->is_global_scope()) { // Compute the arguments for the runtime call. - ZoneList* arguments = new ZoneList(2); - // 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. + ZoneList* arguments = new ZoneList(3); arguments->Add(new Literal(name)); // we have at least 1 parameter - if (is_const || (value != NULL && !inside_with())) { - arguments->Add(value); - value = NULL; // zap the value to avoid the unnecessary assignment - } - // Construct the call to Runtime::DeclareGlobal{Variable,Const}Locally - // and add it to the initialization statement block. Note that - // this function does different things depending on if we have - // 1 or 2 parameters. CallRuntime* initialize; + if (is_const) { + arguments->Add(value); + 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 = - new CallRuntime( - Factory::InitializeConstGlobal_symbol(), - Runtime::FunctionForId(Runtime::kInitializeConstGlobal), - arguments); + new CallRuntime( + Factory::InitializeConstGlobal_symbol(), + Runtime::FunctionForId(Runtime::kInitializeConstGlobal), + arguments); } else { + // Add strict mode. + // We may want to pass singleton to avoid Literal allocations. + arguments->Add(NewNumberLiteral( + temp_scope_->StrictMode() ? kStrictMode : kNonStrictMode)); + + // 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); + 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 = - new CallRuntime( - Factory::InitializeVarGlobal_symbol(), - Runtime::FunctionForId(Runtime::kInitializeVarGlobal), - arguments); + new CallRuntime( + Factory::InitializeVarGlobal_symbol(), + Runtime::FunctionForId(Runtime::kInitializeVarGlobal), + arguments); } + block->AddStatement(new ExpressionStatement(initialize)); } diff --git a/deps/v8/src/profile-generator-inl.h b/deps/v8/src/profile-generator-inl.h index 4bcfa9b1ec..747e5c7271 100644 --- a/deps/v8/src/profile-generator-inl.h +++ b/deps/v8/src/profile-generator-inl.h @@ -121,34 +121,6 @@ uint64_t HeapEntry::id() { return id_adaptor.returned_id; } - -template -void HeapEntriesMap::UpdateEntries(Visitor* visitor) { - for (HashMap::Entry* p = entries_.Start(); - p != NULL; - p = entries_.Next(p)) { - EntryInfo* entry_info = reinterpret_cast(p->value); - entry_info->entry = visitor->GetEntry( - reinterpret_cast(p->key), - entry_info->children_count, - entry_info->retainers_count); - entry_info->children_count = 0; - entry_info->retainers_count = 0; - } -} - - -bool HeapSnapshotGenerator::ReportProgress(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; -} - } } // namespace v8::internal #endif // ENABLE_LOGGING_AND_PROFILING diff --git a/deps/v8/src/profile-generator.cc b/deps/v8/src/profile-generator.cc index 261b3d6ff0..7612eab993 100644 --- a/deps/v8/src/profile-generator.cc +++ b/deps/v8/src/profile-generator.cc @@ -1177,12 +1177,6 @@ void HeapGraphPath::Print() { } -HeapObject *const HeapSnapshot::kInternalRootObject = - reinterpret_cast(1); -HeapObject *const HeapSnapshot::kGcRootsObject = - reinterpret_cast(2); - - // It is very important to keep objects that form a heap snapshot // as small as possible. namespace { // Avoid littering the global namespace. @@ -1253,96 +1247,6 @@ void HeapSnapshot::AllocateEntries(int entries_count, } -HeapEntry* HeapSnapshot::AddEntry(HeapObject* object, - int children_count, - int retainers_count) { - if (object == kInternalRootObject) { - ASSERT(root_entry_ == NULL); - ASSERT(retainers_count == 0); - return (root_entry_ = AddEntry(HeapEntry::kObject, - "", - HeapObjectsMap::kInternalRootObjectId, - 0, - children_count, - retainers_count)); - } else if (object == kGcRootsObject) { - ASSERT(gc_roots_entry_ == NULL); - return (gc_roots_entry_ = AddEntry(HeapEntry::kObject, - "(GC roots)", - HeapObjectsMap::kGcRootsObjectId, - 0, - children_count, - retainers_count)); - } else if (object->IsJSFunction()) { - JSFunction* func = JSFunction::cast(object); - SharedFunctionInfo* shared = func->shared(); - return AddEntry(object, - HeapEntry::kClosure, - collection_->GetName(String::cast(shared->name())), - children_count, - retainers_count); - } else if (object->IsJSRegExp()) { - JSRegExp* re = JSRegExp::cast(object); - return AddEntry(object, - HeapEntry::kRegExp, - collection_->GetName(re->Pattern()), - children_count, - retainers_count); - } else if (object->IsJSObject()) { - return AddEntry(object, - HeapEntry::kObject, - collection_->GetName(GetConstructorNameForHeapProfile( - JSObject::cast(object))), - children_count, - retainers_count); - } else if (object->IsString()) { - return AddEntry(object, - HeapEntry::kString, - collection_->GetName(String::cast(object)), - children_count, - retainers_count); - } else if (object->IsCode()) { - return AddEntry(object, - HeapEntry::kCode, - "", - children_count, - retainers_count); - } else if (object->IsSharedFunctionInfo()) { - SharedFunctionInfo* shared = SharedFunctionInfo::cast(object); - return AddEntry(object, - HeapEntry::kCode, - collection_->GetName(String::cast(shared->name())), - children_count, - retainers_count); - } else if (object->IsScript()) { - Script* script = Script::cast(object); - return AddEntry(object, - HeapEntry::kCode, - script->name()->IsString() ? - collection_->GetName(String::cast(script->name())) : "", - children_count, - retainers_count); - } else if (object->IsFixedArray()) { - return AddEntry(object, - HeapEntry::kArray, - "", - children_count, - retainers_count); - } else if (object->IsHeapNumber()) { - return AddEntry(object, - HeapEntry::kHeapNumber, - "number", - children_count, - retainers_count); - } - return AddEntry(object, - HeapEntry::kHidden, - "system", - children_count, - retainers_count); -} - - static void HeapEntryClearPaint(HeapEntry** entry_ptr) { (*entry_ptr)->clear_paint(); } @@ -1352,17 +1256,26 @@ void HeapSnapshot::ClearPaint() { } -HeapEntry* HeapSnapshot::AddEntry(HeapObject* object, - HeapEntry::Type type, - const char* name, - int children_count, - int retainers_count) { - return AddEntry(type, - name, - collection_->GetObjectId(object->address()), - object->Size(), - children_count, - retainers_count); +HeapEntry* HeapSnapshot::AddRootEntry(int children_count) { + ASSERT(root_entry_ == NULL); + return (root_entry_ = AddEntry(HeapEntry::kObject, + "", + HeapObjectsMap::kInternalRootObjectId, + 0, + children_count, + 0)); +} + + +HeapEntry* HeapSnapshot::AddGcRootsEntry(int children_count, + int retainers_count) { + ASSERT(gc_roots_entry_ == NULL); + return (gc_roots_entry_ = AddEntry(HeapEntry::kObject, + "(GC roots)", + HeapObjectsMap::kGcRootsObjectId, + 0, + children_count, + retainers_count)); } @@ -1615,7 +1528,7 @@ HeapEntry *const HeapEntriesMap::kHeapEntryPlaceholder = reinterpret_cast(1); HeapEntriesMap::HeapEntriesMap() - : entries_(HeapObjectsMatch), + : entries_(HeapThingsMatch), entries_count_(0), total_children_count_(0), total_retainers_count_(0) { @@ -1629,8 +1542,23 @@ HeapEntriesMap::~HeapEntriesMap() { } -HeapEntry* HeapEntriesMap::Map(HeapObject* object) { - HashMap::Entry* cache_entry = entries_.Lookup(object, Hash(object), false); +void HeapEntriesMap::AllocateEntries() { + for (HashMap::Entry* p = entries_.Start(); + p != NULL; + p = entries_.Next(p)) { + EntryInfo* entry_info = reinterpret_cast(p->value); + entry_info->entry = entry_info->allocator->AllocateEntry( + p->key, + entry_info->children_count, + entry_info->retainers_count); + entry_info->children_count = 0; + entry_info->retainers_count = 0; + } +} + + +HeapEntry* HeapEntriesMap::Map(HeapThing thing) { + HashMap::Entry* cache_entry = entries_.Lookup(thing, Hash(thing), false); if (cache_entry != NULL) { EntryInfo* entry_info = reinterpret_cast(cache_entry->value); return entry_info->entry; @@ -1640,15 +1568,16 @@ HeapEntry* HeapEntriesMap::Map(HeapObject* object) { } -void HeapEntriesMap::Pair(HeapObject* object, HeapEntry* entry) { - HashMap::Entry* cache_entry = entries_.Lookup(object, Hash(object), true); +void HeapEntriesMap::Pair( + HeapThing thing, HeapEntriesAllocator* allocator, HeapEntry* entry) { + HashMap::Entry* cache_entry = entries_.Lookup(thing, Hash(thing), true); ASSERT(cache_entry->value == NULL); - cache_entry->value = new EntryInfo(entry); + cache_entry->value = new EntryInfo(entry, allocator); ++entries_count_; } -void HeapEntriesMap::CountReference(HeapObject* from, HeapObject* to, +void HeapEntriesMap::CountReference(HeapThing from, HeapThing to, int* prev_children_count, int* prev_retainers_count) { HashMap::Entry* from_cache_entry = entries_.Lookup(from, Hash(from), false); @@ -1671,7 +1600,7 @@ void HeapEntriesMap::CountReference(HeapObject* from, HeapObject* to, HeapObjectsSet::HeapObjectsSet() - : entries_(HeapEntriesMap::HeapObjectsMatch) { + : entries_(HeapEntriesMap::HeapThingsMatch) { } @@ -1700,206 +1629,144 @@ void HeapObjectsSet::Insert(Object* obj) { } -HeapSnapshotGenerator::HeapSnapshotGenerator(HeapSnapshot* snapshot, - v8::ActivityControl* control) +HeapObject *const V8HeapExplorer::kInternalRootObject = + reinterpret_cast(1); +HeapObject *const V8HeapExplorer::kGcRootsObject = + reinterpret_cast(2); + + +V8HeapExplorer::V8HeapExplorer( + HeapSnapshot* snapshot, + SnapshottingProgressReportingInterface* progress) : snapshot_(snapshot), - control_(control), - collection_(snapshot->collection()), + collection_(snapshot_->collection()), + progress_(progress), filler_(NULL) { } -class SnapshotCounter : public HeapSnapshotGenerator::SnapshotFillerInterface { - public: - explicit SnapshotCounter(HeapEntriesMap* entries) - : entries_(entries) { } - HeapEntry* AddEntry(HeapObject* obj) { - entries_->Pair(obj, HeapEntriesMap::kHeapEntryPlaceholder); - return HeapEntriesMap::kHeapEntryPlaceholder; - } - void SetIndexedReference(HeapGraphEdge::Type, - HeapObject* parent_obj, - HeapEntry*, - int, - Object* child_obj, - HeapEntry*) { - entries_->CountReference(parent_obj, HeapObject::cast(child_obj)); - } - void SetNamedReference(HeapGraphEdge::Type, - HeapObject* parent_obj, - HeapEntry*, - const char*, - Object* child_obj, - HeapEntry*) { - entries_->CountReference(parent_obj, HeapObject::cast(child_obj)); - } - void SetRootShortcutReference(Object* child_obj, HeapEntry*) { - entries_->CountReference( - HeapSnapshot::kInternalRootObject, HeapObject::cast(child_obj)); - } - void SetRootGcRootsReference() { - entries_->CountReference( - HeapSnapshot::kInternalRootObject, HeapSnapshot::kGcRootsObject); - } - void SetStrongRootReference(Object* child_obj, HeapEntry*) { - entries_->CountReference( - HeapSnapshot::kGcRootsObject, HeapObject::cast(child_obj)); - } - private: - HeapEntriesMap* entries_; -}; - - -class SnapshotFiller : public HeapSnapshotGenerator::SnapshotFillerInterface { - public: - explicit SnapshotFiller(HeapSnapshot* snapshot, HeapEntriesMap* entries) - : snapshot_(snapshot), - collection_(snapshot->collection()), - entries_(entries) { } - HeapEntry* AddEntry(HeapObject* obj) { - UNREACHABLE(); - return NULL; - } - void SetIndexedReference(HeapGraphEdge::Type type, - HeapObject* parent_obj, - HeapEntry* parent_entry, - int index, - Object* child_obj, - HeapEntry* child_entry) { - int child_index, retainer_index; - entries_->CountReference(parent_obj, - HeapObject::cast(child_obj), - &child_index, - &retainer_index); - parent_entry->SetIndexedReference( - type, child_index, index, child_entry, retainer_index); - } - void SetNamedReference(HeapGraphEdge::Type type, - HeapObject* parent_obj, - HeapEntry* parent_entry, - const char* reference_name, - Object* child_obj, - HeapEntry* child_entry) { - int child_index, retainer_index; - entries_->CountReference(parent_obj, HeapObject::cast(child_obj), - &child_index, &retainer_index); - parent_entry->SetNamedReference(type, - child_index, - reference_name, - child_entry, - retainer_index); - } - void SetRootGcRootsReference() { - int child_index, retainer_index; - entries_->CountReference(HeapSnapshot::kInternalRootObject, - HeapSnapshot::kGcRootsObject, - &child_index, - &retainer_index); - snapshot_->root()->SetIndexedReference(HeapGraphEdge::kElement, - child_index, - child_index + 1, - snapshot_->gc_roots(), - retainer_index); - } - void SetRootShortcutReference(Object* child_obj, - HeapEntry* child_entry) { - int child_index, retainer_index; - entries_->CountReference(HeapSnapshot::kInternalRootObject, - HeapObject::cast(child_obj), - &child_index, - &retainer_index); - snapshot_->root()->SetNamedReference(HeapGraphEdge::kShortcut, - child_index, - collection_->GetName(child_index + 1), - child_entry, - retainer_index); - } - void SetStrongRootReference(Object* child_obj, - HeapEntry* child_entry) { - int child_index, retainer_index; - entries_->CountReference(HeapSnapshot::kGcRootsObject, - HeapObject::cast(child_obj), - &child_index, - &retainer_index); - snapshot_->gc_roots()->SetIndexedReference(HeapGraphEdge::kElement, - child_index, - child_index + 1, - child_entry, - retainer_index); - } - private: - HeapSnapshot* snapshot_; - HeapSnapshotsCollection* collection_; - HeapEntriesMap* entries_; -}; -class SnapshotAllocator { - public: - explicit SnapshotAllocator(HeapSnapshot* snapshot) - : snapshot_(snapshot) { } - HeapEntry* GetEntry( - HeapObject* obj, int children_count, int retainers_count) { - HeapEntry* entry = - snapshot_->AddEntry(obj, children_count, retainers_count); - ASSERT(entry != NULL); - return entry; - } - private: - HeapSnapshot* snapshot_; -}; - -class RootsReferencesExtractor : public ObjectVisitor { - public: - explicit RootsReferencesExtractor(HeapSnapshotGenerator* generator) - : generator_(generator) { - } - void VisitPointers(Object** start, Object** end) { - for (Object** p = start; p < end; p++) generator_->SetGcRootsReference(*p); - } - private: - HeapSnapshotGenerator* generator_; -}; +V8HeapExplorer::~V8HeapExplorer() { +} -bool HeapSnapshotGenerator::GenerateSnapshot() { - AssertNoAllocation no_alloc; +HeapEntry* V8HeapExplorer::AllocateEntry( + HeapThing ptr, int children_count, int retainers_count) { + return AddEntry( + reinterpret_cast(ptr), children_count, retainers_count); +} - SetProgressTotal(4); // 2 passes + dominators + sizes. - // Pass 1. Iterate heap contents to count entries and references. - if (!CountEntriesAndReferences()) return false; +HeapEntry* V8HeapExplorer::AddEntry(HeapObject* object, + int children_count, + int retainers_count) { + if (object == kInternalRootObject) { + ASSERT(retainers_count == 0); + return snapshot_->AddRootEntry(children_count); + } else if (object == kGcRootsObject) { + return snapshot_->AddGcRootsEntry(children_count, retainers_count); + } else if (object->IsJSFunction()) { + JSFunction* func = JSFunction::cast(object); + SharedFunctionInfo* shared = func->shared(); + return AddEntry(object, + HeapEntry::kClosure, + collection_->GetName(String::cast(shared->name())), + children_count, + retainers_count); + } else if (object->IsJSRegExp()) { + JSRegExp* re = JSRegExp::cast(object); + return AddEntry(object, + HeapEntry::kRegExp, + collection_->GetName(re->Pattern()), + children_count, + retainers_count); + } else if (object->IsJSObject()) { + return AddEntry(object, + HeapEntry::kObject, + collection_->GetName(GetConstructorNameForHeapProfile( + JSObject::cast(object))), + children_count, + retainers_count); + } else if (object->IsString()) { + return AddEntry(object, + HeapEntry::kString, + collection_->GetName(String::cast(object)), + children_count, + retainers_count); + } else if (object->IsCode()) { + return AddEntry(object, + HeapEntry::kCode, + "", + children_count, + retainers_count); + } else if (object->IsSharedFunctionInfo()) { + SharedFunctionInfo* shared = SharedFunctionInfo::cast(object); + return AddEntry(object, + HeapEntry::kCode, + collection_->GetName(String::cast(shared->name())), + children_count, + retainers_count); + } else if (object->IsScript()) { + Script* script = Script::cast(object); + return AddEntry(object, + HeapEntry::kCode, + script->name()->IsString() ? + collection_->GetName(String::cast(script->name())) : "", + children_count, + retainers_count); + } else if (object->IsFixedArray()) { + return AddEntry(object, + HeapEntry::kArray, + "", + children_count, + retainers_count); + } else if (object->IsHeapNumber()) { + return AddEntry(object, + HeapEntry::kHeapNumber, + "number", + children_count, + retainers_count); + } + return AddEntry(object, + HeapEntry::kHidden, + "system", + children_count, + retainers_count); +} - // Allocate and fill entries in the snapshot, allocate references. - snapshot_->AllocateEntries(entries_.entries_count(), - entries_.total_children_count(), - entries_.total_retainers_count()); - SnapshotAllocator allocator(snapshot_); - entries_.UpdateEntries(&allocator); - // Pass 2. Fill references. - if (!FillReferences()) return false; +HeapEntry* V8HeapExplorer::AddEntry(HeapObject* object, + HeapEntry::Type type, + const char* name, + int children_count, + int retainers_count) { + return snapshot_->AddEntry(type, + name, + collection_->GetObjectId(object->address()), + object->Size(), + children_count, + retainers_count); +} - if (!SetEntriesDominators()) return false; - if (!ApproximateRetainedSizes()) return false; - progress_counter_ = progress_total_; - if (!ReportProgress(true)) return false; - return true; +void V8HeapExplorer::AddRootEntries(SnapshotFillerInterface* filler) { + filler->AddEntry(kInternalRootObject); + filler->AddEntry(kGcRootsObject); } -HeapEntry* HeapSnapshotGenerator::GetEntry(Object* obj) { - if (!obj->IsHeapObject()) return NULL; - HeapObject* object = HeapObject::cast(obj); - HeapEntry* entry = entries_.Map(object); - // A new entry. - if (entry == NULL) entry = filler_->AddEntry(object); - return entry; +int V8HeapExplorer::EstimateObjectsCount() { + HeapIterator iterator(HeapIterator::kFilterUnreachable); + 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(HeapSnapshotGenerator* generator, + IndexedReferencesExtractor(V8HeapExplorer* generator, HeapObject* parent_obj, HeapEntry* parent_entry, HeapObjectsSet* known_references = NULL) @@ -1917,7 +1784,7 @@ class IndexedReferencesExtractor : public ObjectVisitor { } } private: - HeapSnapshotGenerator* generator_; + V8HeapExplorer* generator_; HeapObject* parent_obj_; HeapEntry* parent_; HeapObjectsSet* known_references_; @@ -1925,7 +1792,7 @@ class IndexedReferencesExtractor : public ObjectVisitor { }; -void HeapSnapshotGenerator::ExtractReferences(HeapObject* obj) { +void V8HeapExplorer::ExtractReferences(HeapObject* obj) { HeapEntry* entry = GetEntry(obj); if (entry == NULL) return; // No interest in this object. @@ -1969,8 +1836,8 @@ void HeapSnapshotGenerator::ExtractReferences(HeapObject* obj) { } -void HeapSnapshotGenerator::ExtractClosureReferences(JSObject* js_obj, - HeapEntry* entry) { +void V8HeapExplorer::ExtractClosureReferences(JSObject* js_obj, + HeapEntry* entry) { if (js_obj->IsJSFunction()) { HandleScope hs; JSFunction* func = JSFunction::cast(js_obj); @@ -1992,8 +1859,8 @@ void HeapSnapshotGenerator::ExtractClosureReferences(JSObject* js_obj, } -void HeapSnapshotGenerator::ExtractPropertyReferences(JSObject* js_obj, - HeapEntry* entry) { +void V8HeapExplorer::ExtractPropertyReferences(JSObject* js_obj, + HeapEntry* entry) { if (js_obj->HasFastProperties()) { DescriptorArray* descs = js_obj->map()->instance_descriptors(); for (int i = 0; i < descs->number_of_descriptors(); i++) { @@ -2034,8 +1901,8 @@ void HeapSnapshotGenerator::ExtractPropertyReferences(JSObject* js_obj, } -void HeapSnapshotGenerator::ExtractElementReferences(JSObject* js_obj, - HeapEntry* entry) { +void V8HeapExplorer::ExtractElementReferences(JSObject* js_obj, + HeapEntry* entry) { if (js_obj->HasFastElements()) { FixedArray* elements = FixedArray::cast(js_obj->elements()); int length = js_obj->IsJSArray() ? @@ -2061,8 +1928,8 @@ void HeapSnapshotGenerator::ExtractElementReferences(JSObject* js_obj, } -void HeapSnapshotGenerator::ExtractInternalReferences(JSObject* js_obj, - HeapEntry* entry) { +void V8HeapExplorer::ExtractInternalReferences(JSObject* js_obj, + HeapEntry* entry) { int length = js_obj->GetInternalFieldCount(); for (int i = 0; i < length; ++i) { Object* o = js_obj->GetInternalField(i); @@ -2071,10 +1938,55 @@ void HeapSnapshotGenerator::ExtractInternalReferences(JSObject* js_obj, } -void HeapSnapshotGenerator::SetClosureReference(HeapObject* parent_obj, - HeapEntry* parent_entry, - String* reference_name, - Object* child_obj) { +HeapEntry* V8HeapExplorer::GetEntry(Object* obj) { + if (!obj->IsHeapObject()) return NULL; + return filler_->FindOrAddEntry(obj); +} + + +class RootsReferencesExtractor : public ObjectVisitor { + public: + explicit RootsReferencesExtractor(V8HeapExplorer* explorer) + : explorer_(explorer) { + } + void VisitPointers(Object** start, Object** end) { + for (Object** p = start; p < end; p++) explorer_->SetGcRootsReference(*p); + } + private: + V8HeapExplorer* explorer_; +}; + + +bool V8HeapExplorer::IterateAndExtractReferences( + SnapshotFillerInterface* filler) { + filler_ = filler; + HeapIterator iterator(HeapIterator::kFilterUnreachable); + 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(this); + Heap::IterateRoots(&extractor, VISIT_ALL); + filler_ = NULL; + return progress_->ProgressReport(false); +} + + +void V8HeapExplorer::SetClosureReference(HeapObject* parent_obj, + HeapEntry* parent_entry, + String* reference_name, + Object* child_obj) { HeapEntry* child_entry = GetEntry(child_obj); if (child_entry != NULL) { filler_->SetNamedReference(HeapGraphEdge::kContextVariable, @@ -2088,10 +2000,10 @@ void HeapSnapshotGenerator::SetClosureReference(HeapObject* parent_obj, } -void HeapSnapshotGenerator::SetElementReference(HeapObject* parent_obj, - HeapEntry* parent_entry, - int index, - Object* child_obj) { +void V8HeapExplorer::SetElementReference(HeapObject* parent_obj, + HeapEntry* parent_entry, + int index, + Object* child_obj) { HeapEntry* child_entry = GetEntry(child_obj); if (child_entry != NULL) { filler_->SetIndexedReference(HeapGraphEdge::kElement, @@ -2105,10 +2017,10 @@ void HeapSnapshotGenerator::SetElementReference(HeapObject* parent_obj, } -void HeapSnapshotGenerator::SetInternalReference(HeapObject* parent_obj, - HeapEntry* parent_entry, - const char* reference_name, - Object* child_obj) { +void V8HeapExplorer::SetInternalReference(HeapObject* parent_obj, + HeapEntry* parent_entry, + const char* reference_name, + Object* child_obj) { HeapEntry* child_entry = GetEntry(child_obj); if (child_entry != NULL) { filler_->SetNamedReference(HeapGraphEdge::kInternal, @@ -2122,10 +2034,10 @@ void HeapSnapshotGenerator::SetInternalReference(HeapObject* parent_obj, } -void HeapSnapshotGenerator::SetInternalReference(HeapObject* parent_obj, - HeapEntry* parent_entry, - int index, - Object* child_obj) { +void V8HeapExplorer::SetInternalReference(HeapObject* parent_obj, + HeapEntry* parent_entry, + int index, + Object* child_obj) { HeapEntry* child_entry = GetEntry(child_obj); if (child_entry != NULL) { filler_->SetNamedReference(HeapGraphEdge::kInternal, @@ -2139,10 +2051,10 @@ void HeapSnapshotGenerator::SetInternalReference(HeapObject* parent_obj, } -void HeapSnapshotGenerator::SetHiddenReference(HeapObject* parent_obj, - HeapEntry* parent_entry, - int index, - Object* child_obj) { +void V8HeapExplorer::SetHiddenReference(HeapObject* parent_obj, + HeapEntry* parent_entry, + int index, + Object* child_obj) { HeapEntry* child_entry = GetEntry(child_obj); if (child_entry != NULL) { filler_->SetIndexedReference(HeapGraphEdge::kHidden, @@ -2155,10 +2067,10 @@ void HeapSnapshotGenerator::SetHiddenReference(HeapObject* parent_obj, } -void HeapSnapshotGenerator::SetPropertyReference(HeapObject* parent_obj, - HeapEntry* parent_entry, - String* reference_name, - Object* child_obj) { +void V8HeapExplorer::SetPropertyReference(HeapObject* parent_obj, + HeapEntry* parent_entry, + String* reference_name, + Object* child_obj) { HeapEntry* child_entry = GetEntry(child_obj); if (child_entry != NULL) { HeapGraphEdge::Type type = reference_name->length() > 0 ? @@ -2174,7 +2086,7 @@ void HeapSnapshotGenerator::SetPropertyReference(HeapObject* parent_obj, } -void HeapSnapshotGenerator::SetPropertyShortcutReference( +void V8HeapExplorer::SetPropertyShortcutReference( HeapObject* parent_obj, HeapEntry* parent_entry, String* reference_name, @@ -2191,52 +2103,221 @@ void HeapSnapshotGenerator::SetPropertyShortcutReference( } -void HeapSnapshotGenerator::SetRootGcRootsReference() { - filler_->SetRootGcRootsReference(); +void V8HeapExplorer::SetRootGcRootsReference() { + filler_->SetIndexedAutoIndexReference( + HeapGraphEdge::kElement, + kInternalRootObject, snapshot_->root(), + kGcRootsObject, snapshot_->gc_roots()); } -void HeapSnapshotGenerator::SetRootShortcutReference(Object* child_obj) { +void V8HeapExplorer::SetRootShortcutReference(Object* child_obj) { HeapEntry* child_entry = GetEntry(child_obj); ASSERT(child_entry != NULL); - filler_->SetRootShortcutReference(child_obj, child_entry); + filler_->SetNamedAutoIndexReference( + HeapGraphEdge::kShortcut, + kInternalRootObject, snapshot_->root(), + child_obj, child_entry); } -void HeapSnapshotGenerator::SetGcRootsReference(Object* child_obj) { +void V8HeapExplorer::SetGcRootsReference(Object* child_obj) { HeapEntry* child_entry = GetEntry(child_obj); if (child_entry != NULL) { - filler_->SetStrongRootReference(child_obj, child_entry); + filler_->SetIndexedAutoIndexReference( + HeapGraphEdge::kElement, + kGcRootsObject, snapshot_->gc_roots(), + child_obj, child_entry); } } +HeapSnapshotGenerator::HeapSnapshotGenerator(HeapSnapshot* snapshot, + v8::ActivityControl* control) + : snapshot_(snapshot), + control_(control), + v8_heap_explorer_(snapshot_, this) { +} + + +class SnapshotCounter : public SnapshotFillerInterface { + public: + SnapshotCounter(HeapEntriesAllocator* allocator, HeapEntriesMap* entries) + : allocator_(allocator), entries_(entries) { } + HeapEntry* AddEntry(HeapThing ptr) { + entries_->Pair(ptr, allocator_, HeapEntriesMap::kHeapEntryPlaceholder); + return HeapEntriesMap::kHeapEntryPlaceholder; + } + HeapEntry* FindOrAddEntry(HeapThing ptr) { + HeapEntry* entry = entries_->Map(ptr); + return entry != NULL ? entry : AddEntry(ptr); + } + void SetIndexedReference(HeapGraphEdge::Type, + HeapThing parent_ptr, + HeapEntry*, + int, + HeapThing child_ptr, + HeapEntry*) { + entries_->CountReference(parent_ptr, child_ptr); + } + void SetIndexedAutoIndexReference(HeapGraphEdge::Type, + HeapThing parent_ptr, + HeapEntry*, + HeapThing child_ptr, + HeapEntry*) { + entries_->CountReference(parent_ptr, child_ptr); + } + void SetNamedReference(HeapGraphEdge::Type, + HeapThing parent_ptr, + HeapEntry*, + const char*, + HeapThing child_ptr, + HeapEntry*) { + entries_->CountReference(parent_ptr, child_ptr); + } + void SetNamedAutoIndexReference(HeapGraphEdge::Type, + HeapThing parent_ptr, + HeapEntry*, + HeapThing child_ptr, + HeapEntry*) { + entries_->CountReference(parent_ptr, child_ptr); + } + private: + HeapEntriesAllocator* allocator_; + HeapEntriesMap* entries_; +}; + + +class SnapshotFiller : public SnapshotFillerInterface { + public: + explicit SnapshotFiller(HeapSnapshot* snapshot, HeapEntriesMap* entries) + : snapshot_(snapshot), + collection_(snapshot->collection()), + entries_(entries) { } + HeapEntry* AddEntry(HeapThing ptr) { + UNREACHABLE(); + return NULL; + } + HeapEntry* FindOrAddEntry(HeapThing ptr) { + HeapEntry* entry = entries_->Map(ptr); + return entry != NULL ? entry : AddEntry(ptr); + } + void SetIndexedReference(HeapGraphEdge::Type type, + HeapThing parent_ptr, + HeapEntry* parent_entry, + int index, + HeapThing child_ptr, + HeapEntry* child_entry) { + int child_index, retainer_index; + entries_->CountReference( + parent_ptr, child_ptr, &child_index, &retainer_index); + parent_entry->SetIndexedReference( + type, child_index, index, child_entry, retainer_index); + } + void SetIndexedAutoIndexReference(HeapGraphEdge::Type type, + HeapThing parent_ptr, + HeapEntry* parent_entry, + HeapThing child_ptr, + HeapEntry* child_entry) { + int child_index, retainer_index; + entries_->CountReference( + parent_ptr, child_ptr, &child_index, &retainer_index); + parent_entry->SetIndexedReference( + type, child_index, child_index + 1, child_entry, retainer_index); + } + void SetNamedReference(HeapGraphEdge::Type type, + HeapThing parent_ptr, + HeapEntry* parent_entry, + const char* reference_name, + HeapThing child_ptr, + HeapEntry* child_entry) { + int child_index, retainer_index; + entries_->CountReference( + parent_ptr, child_ptr, &child_index, &retainer_index); + parent_entry->SetNamedReference( + type, child_index, reference_name, child_entry, retainer_index); + } + void SetNamedAutoIndexReference(HeapGraphEdge::Type type, + HeapThing parent_ptr, + HeapEntry* parent_entry, + HeapThing child_ptr, + HeapEntry* child_entry) { + int child_index, retainer_index; + entries_->CountReference( + parent_ptr, child_ptr, &child_index, &retainer_index); + parent_entry->SetNamedReference(type, + child_index, + collection_->GetName(child_index + 1), + child_entry, + retainer_index); + } + private: + HeapSnapshot* snapshot_; + HeapSnapshotsCollection* collection_; + HeapEntriesMap* entries_; +}; + + +bool HeapSnapshotGenerator::GenerateSnapshot() { + AssertNoAllocation no_alloc; + + SetProgressTotal(4); // 2 passes + dominators + sizes. + + // Pass 1. Iterate heap contents to count entries and references. + if (!CountEntriesAndReferences()) return false; + + // Allocate and fill entries in the snapshot, allocate references. + snapshot_->AllocateEntries(entries_.entries_count(), + entries_.total_children_count(), + entries_.total_retainers_count()); + entries_.AllocateEntries(); + + // Pass 2. Fill references. + if (!FillReferences()) return false; + + if (!SetEntriesDominators()) return false; + if (!ApproximateRetainedSizes()) return false; + + 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(HeapIterator::kFilterUnreachable); - int objects_count = 0; - for (HeapObject* obj = iterator.next(); - obj != NULL; - obj = iterator.next(), ++objects_count) {} - progress_total_ = objects_count * iterations_count; + progress_total_ = v8_heap_explorer_.EstimateObjectsCount() * iterations_count; progress_counter_ = 0; } bool HeapSnapshotGenerator::CountEntriesAndReferences() { - SnapshotCounter counter(&entries_); - filler_ = &counter; - filler_->AddEntry(HeapSnapshot::kInternalRootObject); - filler_->AddEntry(HeapSnapshot::kGcRootsObject); - return IterateAndExtractReferences(); + SnapshotCounter counter(&v8_heap_explorer_, &entries_); + v8_heap_explorer_.AddRootEntries(&counter); + return v8_heap_explorer_.IterateAndExtractReferences(&counter); } bool HeapSnapshotGenerator::FillReferences() { SnapshotFiller filler(snapshot_, &entries_); - filler_ = &filler; - return IterateAndExtractReferences(); + return v8_heap_explorer_.IterateAndExtractReferences(&filler); } @@ -2322,7 +2403,7 @@ bool HeapSnapshotGenerator::BuildDominatorTree( int remaining = entries_length - changed; if (remaining < 0) remaining = 0; progress_counter_ = base_progress_counter + remaining; - if (!ReportProgress(true)) return false; + if (!ProgressReport(true)) return false; } return true; } @@ -2352,7 +2433,7 @@ bool HeapSnapshotGenerator::ApproximateRetainedSizes() { } for (int i = 0; i < snapshot_->entries()->length(); - ++i, IncProgressCounter()) { + ++i, ProgressStep()) { HeapEntry* entry = snapshot_->entries()->at(i); int entry_size = entry->self_size(); for (HeapEntry* dominator = entry->dominator(); @@ -2360,32 +2441,12 @@ bool HeapSnapshotGenerator::ApproximateRetainedSizes() { entry = dominator, dominator = entry->dominator()) { dominator->add_retained_size(entry_size); } - if (!ReportProgress()) return false; + if (!ProgressReport()) return false; } return true; } -bool HeapSnapshotGenerator::IterateAndExtractReferences() { - HeapIterator iterator(HeapIterator::kFilterUnreachable); - bool interrupted = false; - // Heap iteration with filtering must be finished in any case. - for (HeapObject* obj = iterator.next(); - obj != NULL; - obj = iterator.next(), IncProgressCounter()) { - if (!interrupted) { - ExtractReferences(obj); - if (!ReportProgress()) interrupted = true; - } - } - if (interrupted) return false; - SetRootGcRootsReference(); - RootsReferencesExtractor extractor(this); - Heap::IterateRoots(&extractor, VISIT_ALL); - return ReportProgress(); -} - - void HeapSnapshotsDiff::CreateRoots(int additions_count, int deletions_count) { raw_additions_root_ = NewArray(HeapEntry::EntriesSize(1, additions_count, 0)); diff --git a/deps/v8/src/profile-generator.h b/deps/v8/src/profile-generator.h index 748714dc4d..4762eb6342 100644 --- a/deps/v8/src/profile-generator.h +++ b/deps/v8/src/profile-generator.h @@ -681,14 +681,14 @@ class HeapSnapshot { void AllocateEntries( int entries_count, int children_count, int retainers_count); - HeapEntry* AddEntry( - HeapObject* object, int children_count, int retainers_count); HeapEntry* AddEntry(HeapEntry::Type type, const char* name, uint64_t id, int size, int children_count, int retainers_count); + HeapEntry* AddRootEntry(int children_count); + HeapEntry* AddGcRootsEntry(int children_count, int retainers_count); void ClearPaint(); HeapSnapshotsDiff* CompareWith(HeapSnapshot* snapshot); HeapEntry* GetEntryById(uint64_t id); @@ -701,15 +701,7 @@ class HeapSnapshot { void Print(int max_depth); void PrintEntriesSize(); - static HeapObject* const kInternalRootObject; - static HeapObject* const kGcRootsObject; - private: - HeapEntry* AddEntry(HeapObject* object, - HeapEntry::Type type, - const char* name, - int children_count, - int retainers_count); HeapEntry* GetNextEntryToInit(); HeapSnapshotsCollection* collection_; @@ -873,6 +865,20 @@ class 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, int children_count, int retainers_count) = 0; +}; + + // The HeapEntriesMap instance is used to track a mapping between // real heap objects and their representations in heap snapshots. class HeapEntriesMap { @@ -880,13 +886,12 @@ class HeapEntriesMap { HeapEntriesMap(); ~HeapEntriesMap(); - HeapEntry* Map(HeapObject* object); - void Pair(HeapObject* object, HeapEntry* entry); - void CountReference(HeapObject* from, HeapObject* to, + void AllocateEntries(); + HeapEntry* Map(HeapThing thing); + void Pair(HeapThing thing, HeapEntriesAllocator* allocator, HeapEntry* entry); + void CountReference(HeapThing from, HeapThing to, int* prev_children_count = NULL, int* prev_retainers_count = NULL); - template - void UpdateEntries(Visitor* visitor); int entries_count() { return entries_count_; } int total_children_count() { return total_children_count_; } @@ -896,18 +901,25 @@ class HeapEntriesMap { private: struct EntryInfo { - explicit EntryInfo(HeapEntry* entry) - : entry(entry), children_count(0), retainers_count(0) { } + EntryInfo(HeapEntry* entry, HeapEntriesAllocator* allocator) + : entry(entry), + allocator(allocator), + children_count(0), + retainers_count(0) { + } HeapEntry* entry; + HeapEntriesAllocator* allocator; int children_count; int retainers_count; }; - static uint32_t Hash(HeapObject* object) { + static uint32_t Hash(HeapThing thing) { return ComputeIntegerHash( - static_cast(reinterpret_cast(object))); + static_cast(reinterpret_cast(thing))); + } + static bool HeapThingsMatch(HeapThing key1, HeapThing key2) { + return key1 == key2; } - static bool HeapObjectsMatch(void* key1, void* key2) { return key1 == key2; } HashMap entries_; int entries_count_; @@ -934,52 +946,70 @@ class HeapObjectsSet { }; -class HeapSnapshotGenerator { +// An interface used to populate a snapshot with nodes and edges. +class SnapshotFillerInterface { public: - class SnapshotFillerInterface { - public: - virtual ~SnapshotFillerInterface() { } - virtual HeapEntry* AddEntry(HeapObject* obj) = 0; - virtual void SetIndexedReference(HeapGraphEdge::Type type, - HeapObject* parent_obj, - HeapEntry* parent_entry, - int index, - Object* child_obj, - HeapEntry* child_entry) = 0; - virtual void SetNamedReference(HeapGraphEdge::Type type, - HeapObject* parent_obj, + virtual ~SnapshotFillerInterface() { } + virtual HeapEntry* AddEntry(HeapThing ptr) = 0; + virtual HeapEntry* FindOrAddEntry(HeapThing ptr) = 0; + virtual void SetIndexedReference(HeapGraphEdge::Type type, + HeapThing parent_ptr, HeapEntry* parent_entry, - const char* reference_name, - Object* child_obj, + int index, + HeapThing child_ptr, HeapEntry* child_entry) = 0; - virtual void SetRootGcRootsReference() = 0; - virtual void SetRootShortcutReference(Object* child_obj, + virtual void SetIndexedAutoIndexReference(HeapGraphEdge::Type type, + HeapThing parent_ptr, + HeapEntry* parent_entry, + HeapThing child_ptr, + HeapEntry* child_entry) = 0; + virtual void SetNamedReference(HeapGraphEdge::Type type, + HeapThing parent_ptr, + HeapEntry* parent_entry, + const char* reference_name, + HeapThing child_ptr, + HeapEntry* child_entry) = 0; + virtual void SetNamedAutoIndexReference(HeapGraphEdge::Type type, + HeapThing parent_ptr, + HeapEntry* parent_entry, + HeapThing child_ptr, HeapEntry* child_entry) = 0; - virtual void SetStrongRootReference(Object* child_obj, - HeapEntry* child_entry) = 0; - }; +}; - HeapSnapshotGenerator(HeapSnapshot* snapshot, - v8::ActivityControl* control); - bool GenerateSnapshot(); + +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); + ~V8HeapExplorer(); + virtual HeapEntry* AllocateEntry( + HeapThing ptr, int children_count, int retainers_count); + void AddRootEntries(SnapshotFillerInterface* filler); + int EstimateObjectsCount(); + bool IterateAndExtractReferences(SnapshotFillerInterface* filler); private: - bool ApproximateRetainedSizes(); - bool BuildDominatorTree(const Vector& entries, - Vector* dominators); - bool CountEntriesAndReferences(); - HeapEntry* GetEntry(Object* obj); - void IncProgressCounter() { ++progress_counter_; } + HeapEntry* AddEntry( + HeapObject* object, int children_count, int retainers_count); + HeapEntry* AddEntry(HeapObject* object, + HeapEntry::Type type, + const char* name, + int children_count, + int retainers_count); void ExtractReferences(HeapObject* obj); void ExtractClosureReferences(JSObject* js_obj, HeapEntry* entry); void ExtractPropertyReferences(JSObject* js_obj, HeapEntry* entry); void ExtractElementReferences(JSObject* js_obj, HeapEntry* entry); void ExtractInternalReferences(JSObject* js_obj, HeapEntry* entry); - bool FillReferences(); - void FillReversePostorderIndexes(Vector* entries); - bool IterateAndExtractReferences(); - inline bool ReportProgress(bool force = false); - bool SetEntriesDominators(); void SetClosureReference(HeapObject* parent_obj, HeapEntry* parent, String* reference_name, @@ -1011,24 +1041,54 @@ class HeapSnapshotGenerator { void SetRootShortcutReference(Object* child); void SetRootGcRootsReference(); void SetGcRootsReference(Object* child); - void SetProgressTotal(int iterations_count); + + HeapEntry* GetEntry(Object* obj); HeapSnapshot* snapshot_; - v8::ActivityControl* control_; HeapSnapshotsCollection* collection_; - // Mapping from HeapObject* pointers to HeapEntry* pointers. - HeapEntriesMap entries_; - SnapshotFillerInterface* filler_; + SnapshottingProgressReportingInterface* progress_; // Used during references extraction to mark heap objects that // are references via non-hidden properties. HeapObjectsSet known_references_; - // Used during snapshot generation. - int progress_counter_; - int progress_total_; + SnapshotFillerInterface* filler_; + + static HeapObject* const kInternalRootObject; + static HeapObject* const kGcRootsObject; friend class IndexedReferencesExtractor; friend class RootsReferencesExtractor; + DISALLOW_COPY_AND_ASSIGN(V8HeapExplorer); +}; + + +class HeapSnapshotGenerator : public SnapshottingProgressReportingInterface { + public: + HeapSnapshotGenerator(HeapSnapshot* snapshot, + v8::ActivityControl* control); + bool GenerateSnapshot(); + + private: + bool ApproximateRetainedSizes(); + bool BuildDominatorTree(const Vector& entries, + Vector* dominators); + bool CountEntriesAndReferences(); + bool FillReferences(); + void FillReversePostorderIndexes(Vector* entries); + void ProgressStep(); + bool ProgressReport(bool force = false); + bool SetEntriesDominators(); + void SetProgressTotal(int iterations_count); + + HeapSnapshot* snapshot_; + v8::ActivityControl* control_; + V8HeapExplorer v8_heap_explorer_; + // Mapping from HeapThing pointers to HeapEntry* pointers. + HeapEntriesMap entries_; + // Used during snapshot generation. + int progress_counter_; + int progress_total_; + DISALLOW_COPY_AND_ASSIGN(HeapSnapshotGenerator); }; diff --git a/deps/v8/src/runtime-profiler.cc b/deps/v8/src/runtime-profiler.cc index 3406cdc250..df6471e9d5 100644 --- a/deps/v8/src/runtime-profiler.cc +++ b/deps/v8/src/runtime-profiler.cc @@ -35,6 +35,7 @@ #include "deoptimizer.h" #include "execution.h" #include "global-handles.h" +#include "mark-compact.h" #include "scopeinfo.h" #include "top.h" @@ -100,11 +101,6 @@ static int sampler_ticks_until_threshold_adjustment = // The ratio of ticks spent in JS code in percent. static Atomic32 js_ratio; -// The JSFunctions in the sampler window are not GC safe. Old-space -// pointers are not cleared during mark-sweep collection and therefore -// the window might contain stale pointers. The window is updated on -// scavenges and (parts of it) cleared on mark-sweep and -// mark-sweep-compact. static Object* sampler_window[kSamplerWindowSize] = { NULL, }; static int sampler_window_position = 0; static int sampler_window_weight[kSamplerWindowSize] = { 0, }; @@ -134,7 +130,6 @@ void PendingListNode::WeakCallback(v8::Persistent, void* data) { static bool IsOptimizable(JSFunction* function) { - if (Heap::InNewSpace(function)) return false; Code* code = function->code(); return code->kind() == Code::FUNCTION && code->optimizable(); } @@ -208,16 +203,6 @@ static void ClearSampleBuffer() { } -static void ClearSampleBufferNewSpaceEntries() { - for (int i = 0; i < kSamplerWindowSize; i++) { - if (Heap::InNewSpace(sampler_window[i])) { - sampler_window[i] = NULL; - sampler_window_weight[i] = 0; - } - } -} - - static int LookupSample(JSFunction* function) { int weight = 0; for (int i = 0; i < kSamplerWindowSize; i++) { @@ -372,24 +357,6 @@ void RuntimeProfiler::NotifyTick() { } -void RuntimeProfiler::MarkCompactPrologue(bool is_compacting) { - if (is_compacting) { - // Clear all samples before mark-sweep-compact because every - // function might move. - ClearSampleBuffer(); - } else { - // Clear only new space entries on mark-sweep since none of the - // old-space functions will move. - ClearSampleBufferNewSpaceEntries(); - } -} - - -bool IsEqual(void* first, void* second) { - return first == second; -} - - void RuntimeProfiler::Setup() { ClearSampleBuffer(); // If the ticker hasn't already started, make sure to do so to get @@ -411,13 +378,41 @@ void RuntimeProfiler::TearDown() { } -Object** RuntimeProfiler::SamplerWindowAddress() { - return sampler_window; +int RuntimeProfiler::SamplerWindowSize() { + return kSamplerWindowSize; } -int RuntimeProfiler::SamplerWindowSize() { - return kSamplerWindowSize; +// 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 && 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 && !HeapObject::cast(function)->IsMarked()) { + sampler_window[i] = NULL; + } + } +} + + +void RuntimeProfiler::UpdateSamplesAfterCompact(ObjectVisitor* visitor) { + for (int i = 0; i < kSamplerWindowSize; i++) { + visitor->VisitPointer(&sampler_window[i]); + } } diff --git a/deps/v8/src/runtime-profiler.h b/deps/v8/src/runtime-profiler.h index e041c059b6..02defc9b2c 100644 --- a/deps/v8/src/runtime-profiler.h +++ b/deps/v8/src/runtime-profiler.h @@ -47,9 +47,10 @@ class RuntimeProfiler : public AllStatic { static void Reset(); static void TearDown(); - static void MarkCompactPrologue(bool is_compacting); - static Object** SamplerWindowAddress(); static int SamplerWindowSize(); + static void UpdateSamplesAfterScavenge(); + static void RemoveDeadSamples(); + static void UpdateSamplesAfterCompact(ObjectVisitor* visitor); }; diff --git a/deps/v8/src/runtime.cc b/deps/v8/src/runtime.cc index dce2e15674..0c15f60f30 100644 --- a/deps/v8/src/runtime.cc +++ b/deps/v8/src/runtime.cc @@ -40,8 +40,10 @@ #include "debug.h" #include "deoptimizer.h" #include "execution.h" +#include "global-handles.h" #include "jsregexp.h" #include "liveedit.h" +#include "liveobjectlist-inl.h" #include "parser.h" #include "platform.h" #include "runtime.h" @@ -160,7 +162,8 @@ MUST_USE_RESULT static MaybeObject* DeepCopyBoilerplate(JSObject* boilerplate) { if (!maybe_result->ToObject(&result)) return maybe_result; } { MaybeObject* maybe_result = - copy->SetProperty(key_string, result, NONE); + // Creating object copy for literals. No strict mode needed. + copy->SetProperty(key_string, result, NONE, kNonStrictMode); if (!maybe_result->ToObject(&result)) return maybe_result; } } @@ -546,7 +549,9 @@ static MaybeObject* Runtime_CreateCatchExtensionObject(Arguments args) { // Assign the exception value to the catch variable and make sure // that the catch variable is DontDelete. { MaybeObject* maybe_value = - JSObject::cast(object)->SetProperty(key, value, DONT_DELETE); + // Passing non-strict per ECMA-262 5th Ed. 12.14. Catch, bullet #4. + JSObject::cast(object)->SetProperty( + key, value, DONT_DELETE, kNonStrictMode); if (!maybe_value->ToObject(&value)) return maybe_value; } return object; @@ -783,7 +788,8 @@ static MaybeObject* Runtime_GetOwnProperty(Arguments args) { case JSObject::INTERCEPTED_ELEMENT: case JSObject::FAST_ELEMENT: { elms->set(IS_ACCESSOR_INDEX, Heap::false_value()); - elms->set(VALUE_INDEX, *GetElement(obj, index)); + Handle value = GetElement(obj, index); + elms->set(VALUE_INDEX, *value); elms->set(WRITABLE_INDEX, Heap::true_value()); elms->set(ENUMERABLE_INDEX, Heap::true_value()); elms->set(CONFIGURABLE_INDEX, Heap::true_value()); @@ -816,12 +822,14 @@ static MaybeObject* Runtime_GetOwnProperty(Arguments args) { } break; } - case NORMAL: + case NORMAL: { // This is a data property. elms->set(IS_ACCESSOR_INDEX, Heap::false_value()); - elms->set(VALUE_INDEX, *GetElement(obj, index)); + Handle value = GetElement(obj, index); + elms->set(VALUE_INDEX, *value); elms->set(WRITABLE_INDEX, Heap::ToBoolean(!details.IsReadOnly())); break; + } default: UNREACHABLE(); break; @@ -994,12 +1002,16 @@ static Failure* ThrowRedeclarationError(const char* type, Handle name) { static MaybeObject* Runtime_DeclareGlobals(Arguments args) { + ASSERT(args.length() == 4); HandleScope scope; Handle global = Handle(Top::context()->global()); Handle context = args.at(0); CONVERT_ARG_CHECKED(FixedArray, pairs, 1); bool is_eval = Smi::cast(args[2])->value() == 1; + StrictModeFlag strict_mode = + static_cast(Smi::cast(args[3])->value()); + ASSERT(strict_mode == kStrictMode || strict_mode == kNonStrictMode); // Compute the property attributes. According to ECMA-262, section // 13, page 71, the property must be read-only and @@ -1104,12 +1116,21 @@ static MaybeObject* Runtime_DeclareGlobals(Arguments args) { // onload setter in those case and Safari does not. We follow // Safari for compatibility. if (value->IsJSFunction()) { + // Do not change DONT_DELETE to false from true. + if (lookup.IsProperty() && (lookup.type() != INTERCEPTOR)) { + attributes = static_cast( + attributes | (lookup.GetAttributes() & DONT_DELETE)); + } RETURN_IF_EMPTY_HANDLE(SetLocalPropertyIgnoreAttributes(global, name, value, attributes)); } else { - RETURN_IF_EMPTY_HANDLE(SetProperty(global, name, value, attributes)); + RETURN_IF_EMPTY_HANDLE(SetProperty(global, + name, + value, + attributes, + strict_mode)); } } @@ -1170,7 +1191,8 @@ static MaybeObject* Runtime_DeclareContextSlot(Arguments args) { // Slow case: The property is not in the FixedArray part of the context. Handle context_ext = Handle::cast(holder); RETURN_IF_EMPTY_HANDLE( - SetProperty(context_ext, name, initial_value, mode)); + SetProperty(context_ext, name, initial_value, + mode, kNonStrictMode)); } } @@ -1211,7 +1233,8 @@ static MaybeObject* Runtime_DeclareContextSlot(Arguments args) { return ThrowRedeclarationError("const", name); } } - RETURN_IF_EMPTY_HANDLE(SetProperty(context_ext, name, value, mode)); + RETURN_IF_EMPTY_HANDLE(SetProperty(context_ext, name, value, mode, + kNonStrictMode)); } return Heap::undefined_value(); @@ -1220,14 +1243,21 @@ static MaybeObject* Runtime_DeclareContextSlot(Arguments args) { static MaybeObject* Runtime_InitializeVarGlobal(Arguments args) { NoHandleAllocation nha; + // args[0] == name + // args[1] == strict_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() == 1 || args.length() == 2); - bool assign = args.length() == 2; + RUNTIME_ASSERT(args.length() == 2 || args.length() == 3); + bool assign = args.length() == 3; CONVERT_ARG_CHECKED(String, name, 0); GlobalObject* global = Top::context()->global(); + RUNTIME_ASSERT(args[1]->IsSmi()); + StrictModeFlag strict_mode = + static_cast(Smi::cast(args[1])->value()); + ASSERT(strict_mode == kStrictMode || strict_mode == kNonStrictMode); // According to ECMA-262, section 12.2, page 62, the property must // not be deletable. @@ -1283,8 +1313,9 @@ static MaybeObject* Runtime_InitializeVarGlobal(Arguments args) { } // Assign the value (or undefined) to the property. - Object* value = (assign) ? args[1] : Heap::undefined_value(); - return real_holder->SetProperty(&lookup, *name, value, attributes); + Object* value = (assign) ? args[2] : Heap::undefined_value(); + return real_holder->SetProperty( + &lookup, *name, value, attributes, strict_mode); } Object* proto = real_holder->GetPrototype(); @@ -1298,7 +1329,9 @@ static MaybeObject* Runtime_InitializeVarGlobal(Arguments args) { } global = Top::context()->global(); - if (assign) return global->SetProperty(*name, args[1], attributes); + if (assign) { + return global->SetProperty(*name, args[2], attributes, strict_mode); + } return Heap::undefined_value(); } @@ -1357,13 +1390,19 @@ static MaybeObject* Runtime_InitializeConstGlobal(Arguments args) { // 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... - RETURN_IF_EMPTY_HANDLE(SetProperty(global, name, value, attributes)); + // Passing non-strict mode because the property is writable. + RETURN_IF_EMPTY_HANDLE(SetProperty(global, + name, + value, + attributes, + kNonStrictMode)); return *value; } // Set the value, but only we're assigning the initial value to a // constant. For now, we determine this by checking if the // current value is the hole. + // Strict mode handling not needed (const disallowed in strict mode). PropertyType type = lookup.type(); if (type == FIELD) { FixedArray* properties = global->properties(); @@ -1439,7 +1478,9 @@ static MaybeObject* Runtime_InitializeConstContextSlot(Arguments args) { // context. if (attributes == ABSENT) { Handle global = Handle(Top::context()->global()); - RETURN_IF_EMPTY_HANDLE(SetProperty(global, name, value, NONE)); + // Strict mode not needed (const disallowed in strict mode). + RETURN_IF_EMPTY_HANDLE( + SetProperty(global, name, value, NONE, kNonStrictMode)); return *value; } @@ -1476,8 +1517,9 @@ static MaybeObject* Runtime_InitializeConstContextSlot(Arguments args) { // The property was found in a different context extension 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( - SetProperty(context_ext, name, value, attributes)); + SetProperty(context_ext, name, value, attributes, kNonStrictMode)); } } @@ -1643,7 +1685,7 @@ static Handle InstallBuiltin(Handle holder, code, false); optimized->shared()->DontAdaptArguments(); - SetProperty(holder, key, optimized, NONE); + SetProperty(holder, key, optimized, NONE, kStrictMode); return optimized; } @@ -3739,7 +3781,8 @@ static MaybeObject* Runtime_DefineOrRedefineDataProperty(Arguments args) { MaybeObject* Runtime::SetObjectProperty(Handle object, Handle key, Handle value, - PropertyAttributes attr) { + PropertyAttributes attr, + StrictModeFlag strict) { HandleScope scope; if (object->IsUndefined() || object->IsNull()) { @@ -3769,6 +3812,7 @@ MaybeObject* Runtime::SetObjectProperty(Handle object, return *value; } + // TODO(1220): Implement SetElement strict mode. Handle result = SetElement(js_object, index, value); if (result.is_null()) return Failure::Exception(); return *value; @@ -3781,7 +3825,7 @@ MaybeObject* Runtime::SetObjectProperty(Handle object, } else { Handle key_string = Handle::cast(key); key_string->TryFlatten(); - result = SetProperty(js_object, key_string, value, attr); + result = SetProperty(js_object, key_string, value, attr, strict); } if (result.is_null()) return Failure::Exception(); return *value; @@ -3794,9 +3838,10 @@ MaybeObject* Runtime::SetObjectProperty(Handle object, Handle name = Handle::cast(converted); if (name->AsArrayIndex(&index)) { + // TODO(1220): Implement SetElement strict mode. return js_object->SetElement(index, *value); } else { - return js_object->SetProperty(*name, *value, attr); + return js_object->SetProperty(*name, *value, attr, strict); } } @@ -3888,23 +3933,27 @@ MaybeObject* Runtime::ForceDeleteObjectProperty(Handle js_object, static MaybeObject* Runtime_SetProperty(Arguments args) { NoHandleAllocation ha; - RUNTIME_ASSERT(args.length() == 3 || args.length() == 4); + RUNTIME_ASSERT(args.length() == 4 || args.length() == 5); Handle object = args.at(0); Handle key = args.at(1); Handle value = args.at(2); - + CONVERT_SMI_CHECKED(unchecked_attributes, args[3]); + RUNTIME_ASSERT( + (unchecked_attributes & ~(READ_ONLY | DONT_ENUM | DONT_DELETE)) == 0); // Compute attributes. - PropertyAttributes attributes = NONE; - if (args.length() == 4) { - CONVERT_CHECKED(Smi, value_obj, args[3]); - int unchecked_value = value_obj->value(); - // Only attribute bits should be set. - RUNTIME_ASSERT( - (unchecked_value & ~(READ_ONLY | DONT_ENUM | DONT_DELETE)) == 0); - attributes = static_cast(unchecked_value); + PropertyAttributes attributes = + static_cast(unchecked_attributes); + + StrictModeFlag strict = kNonStrictMode; + if (args.length() == 5) { + CONVERT_SMI_CHECKED(strict_unchecked, args[4]); + RUNTIME_ASSERT(strict_unchecked == kStrictMode || + strict_unchecked == kNonStrictMode); + strict = static_cast(strict_unchecked); } - return Runtime::SetObjectProperty(object, key, value, attributes); + + return Runtime::SetObjectProperty(object, key, value, attributes, strict); } @@ -3938,7 +3987,7 @@ static MaybeObject* Runtime_DeleteProperty(Arguments args) { CONVERT_CHECKED(JSObject, object, args[0]); CONVERT_CHECKED(String, key, args[1]); CONVERT_SMI_CHECKED(strict, args[2]); - return object->DeleteProperty(key, strict == kStrictMode + return object->DeleteProperty(key, (strict == kStrictMode) ? JSObject::STRICT_DELETION : JSObject::NORMAL_DELETION); } @@ -7486,11 +7535,16 @@ static ObjectPair Runtime_LoadContextSlotNoReferenceError(Arguments args) { static MaybeObject* Runtime_StoreContextSlot(Arguments args) { HandleScope scope; - ASSERT(args.length() == 3); + ASSERT(args.length() == 4); Handle value(args[0]); CONVERT_ARG_CHECKED(Context, context, 1); CONVERT_ARG_CHECKED(String, name, 2); + CONVERT_SMI_CHECKED(strict_unchecked, args[3]); + RUNTIME_ASSERT(strict_unchecked == kStrictMode || + strict_unchecked == kNonStrictMode); + StrictModeFlag strict = static_cast(strict_unchecked); + int index; PropertyAttributes attributes; @@ -7534,7 +7588,12 @@ static MaybeObject* Runtime_StoreContextSlot(Arguments args) { // extension object itself. if ((attributes & READ_ONLY) == 0 || (context_ext->GetLocalPropertyAttribute(*name) == ABSENT)) { - RETURN_IF_EMPTY_HANDLE(SetProperty(context_ext, name, value, NONE)); + RETURN_IF_EMPTY_HANDLE(SetProperty(context_ext, name, value, NONE, strict)); + } else if (strict == kStrictMode && (attributes & READ_ONLY) != 0) { + // Setting read only property in strict mode. + Handle error = + Factory::NewTypeError("strict_cannot_assign", HandleVector(&name, 1)); + return Top::Throw(*error); } return *value; } @@ -7863,12 +7922,9 @@ static ObjectPair CompileGlobalEval(Handle source, static ObjectPair Runtime_ResolvePossiblyDirectEval(Arguments args) { ASSERT(args.length() == 4); - if (!args[0]->IsJSFunction()) { - return MakePair(Top::ThrowIllegalOperation(), NULL); - } HandleScope scope; - Handle callee = args.at(0); + Handle callee = args.at(0); Handle receiver; // Will be overwritten. // Compute the calling context. @@ -7936,12 +7992,9 @@ static ObjectPair Runtime_ResolvePossiblyDirectEval(Arguments args) { static ObjectPair Runtime_ResolvePossiblyDirectEvalNoLookup(Arguments args) { ASSERT(args.length() == 4); - if (!args[0]->IsJSFunction()) { - return MakePair(Top::ThrowIllegalOperation(), NULL); - } HandleScope scope; - Handle callee = args.at(0); + Handle callee = args.at(0); // 'eval' is bound in the global context, but it may have been overwritten. // Compare it to the builtin 'GlobalEval' function to make sure. @@ -8033,10 +8086,14 @@ class ArrayConcatVisitor { public: ArrayConcatVisitor(Handle storage, bool fast_elements) : - storage_(storage), + storage_(Handle::cast(GlobalHandles::Create(*storage))), index_offset_(0u), fast_elements_(fast_elements) { } + ~ArrayConcatVisitor() { + clear_storage(); + } + void visit(uint32_t i, Handle elm) { if (i >= JSObject::kMaxElementCount - index_offset_) return; uint32_t index = index_offset_ + i; @@ -8054,11 +8111,13 @@ class ArrayConcatVisitor { // Fall-through to dictionary mode. } ASSERT(!fast_elements_); - Handle dict(storage_.cast()); + Handle dict(NumberDictionary::cast(*storage_)); Handle result = Factory::DictionaryAtNumberPut(dict, index, elm); if (!result.is_identical_to(dict)) { - storage_ = Handle::cast(result); + // Dictionary needed to grow. + clear_storage(); + set_storage(*result); } } @@ -8090,23 +8149,35 @@ class ArrayConcatVisitor { // Convert storage to dictionary mode. void SetDictionaryMode(uint32_t index) { ASSERT(fast_elements_); - Handle current_storage(storage_.ToHandle()); - HandleCell slow_storage( + Handle current_storage(*storage_); + Handle slow_storage( Factory::NewNumberDictionary(current_storage->length())); uint32_t current_length = static_cast(current_storage->length()); for (uint32_t i = 0; i < current_length; i++) { HandleScope loop_scope; Handle element(current_storage->get(i)); if (!element->IsTheHole()) { - slow_storage = - Factory::DictionaryAtNumberPut(slow_storage.ToHandle(), i, element); + Handle new_storage = + Factory::DictionaryAtNumberPut(slow_storage, i, element); + if (!new_storage.is_identical_to(slow_storage)) { + slow_storage = loop_scope.CloseAndEscape(new_storage); + } } } - storage_ = slow_storage.cast(); + clear_storage(); + set_storage(*slow_storage); fast_elements_ = false; } - HandleCell storage_; + inline void clear_storage() { + GlobalHandles::Destroy(Handle::cast(storage_).location()); + } + + inline void set_storage(FixedArray* storage) { + storage_ = Handle::cast(GlobalHandles::Create(storage)); + } + + Handle storage_; // Always a global handle. // Index after last seen index. Always less than or equal to // JSObject::kMaxElementCount. uint32_t index_offset_; @@ -9267,7 +9338,9 @@ static bool CopyContextLocalsToScopeObject( RETURN_IF_EMPTY_HANDLE_VALUE( SetProperty(scope_object, scope_info.context_slot_name(i), - Handle(context->get(context_index)), NONE), + Handle(context->get(context_index)), + NONE, + kNonStrictMode), false); } } @@ -9293,7 +9366,9 @@ static Handle MaterializeLocalScope(JavaScriptFrame* frame) { RETURN_IF_EMPTY_HANDLE_VALUE( SetProperty(local_scope, scope_info.parameter_name(i), - Handle(frame->GetParameter(i)), NONE), + Handle(frame->GetParameter(i)), + NONE, + kNonStrictMode), Handle()); } @@ -9302,7 +9377,9 @@ static Handle MaterializeLocalScope(JavaScriptFrame* frame) { RETURN_IF_EMPTY_HANDLE_VALUE( SetProperty(local_scope, scope_info.stack_slot_name(i), - Handle(frame->GetExpression(i)), NONE), + Handle(frame->GetExpression(i)), + NONE, + kNonStrictMode), Handle()); } @@ -9326,7 +9403,11 @@ static Handle MaterializeLocalScope(JavaScriptFrame* frame) { ASSERT(keys->get(i)->IsString()); Handle key(String::cast(keys->get(i))); RETURN_IF_EMPTY_HANDLE_VALUE( - SetProperty(local_scope, key, GetProperty(ext, key), NONE), + SetProperty(local_scope, + key, + GetProperty(ext, key), + NONE, + kNonStrictMode), Handle()); } } @@ -9364,7 +9445,8 @@ static Handle MaterializeClosure(Handle context) { SetProperty(closure_scope, scope_info.parameter_name(i), Handle(element), - NONE), + NONE, + kNonStrictMode), Handle()); } } @@ -9385,7 +9467,11 @@ static Handle MaterializeClosure(Handle context) { ASSERT(keys->get(i)->IsString()); Handle key(String::cast(keys->get(i))); RETURN_IF_EMPTY_HANDLE_VALUE( - SetProperty(closure_scope, key, GetProperty(ext, key), NONE), + SetProperty(closure_scope, + key, + GetProperty(ext, key), + NONE, + kNonStrictMode), Handle()); } } @@ -10863,6 +10949,207 @@ static MaybeObject* Runtime_GetHeapUsage(Arguments args) { } return Smi::FromInt(usage); } + + +// Captures a live object list from the present heap. +static MaybeObject* Runtime_HasLOLEnabled(Arguments args) { +#ifdef LIVE_OBJECT_LIST + return Heap::true_value(); +#else + return Heap::false_value(); +#endif +} + + +// Captures a live object list from the present heap. +static MaybeObject* Runtime_CaptureLOL(Arguments args) { +#ifdef LIVE_OBJECT_LIST + return LiveObjectList::Capture(); +#else + return Heap::undefined_value(); +#endif +} + + +// Deletes the specified live object list. +static MaybeObject* Runtime_DeleteLOL(Arguments args) { +#ifdef LIVE_OBJECT_LIST + CONVERT_SMI_CHECKED(id, args[0]); + bool success = LiveObjectList::Delete(id); + return success ? Heap::true_value() : Heap::false_value(); +#else + return Heap::undefined_value(); +#endif +} + + +// Generates the response to a debugger request for a dump of the objects +// contained in the difference between the captured live object lists +// specified by id1 and id2. +// If id1 is 0 (i.e. not a valid lol), then the whole of lol id2 will be +// dumped. +static MaybeObject* Runtime_DumpLOL(Arguments args) { +#ifdef LIVE_OBJECT_LIST + HandleScope scope; + CONVERT_SMI_CHECKED(id1, args[0]); + CONVERT_SMI_CHECKED(id2, args[1]); + CONVERT_SMI_CHECKED(start, args[2]); + CONVERT_SMI_CHECKED(count, args[3]); + CONVERT_ARG_CHECKED(JSObject, filter_obj, 4); + EnterDebugger enter_debugger; + return LiveObjectList::Dump(id1, id2, start, count, filter_obj); +#else + return Heap::undefined_value(); +#endif +} + + +// Gets the specified object as requested by the debugger. +// This is only used for obj ids shown in live object lists. +static MaybeObject* Runtime_GetLOLObj(Arguments args) { +#ifdef LIVE_OBJECT_LIST + CONVERT_SMI_CHECKED(obj_id, args[0]); + Object* result = LiveObjectList::GetObj(obj_id); + return result; +#else + return Heap::undefined_value(); +#endif +} + + +// Gets the obj id for the specified address if valid. +// This is only used for obj ids shown in live object lists. +static MaybeObject* Runtime_GetLOLObjId(Arguments args) { +#ifdef LIVE_OBJECT_LIST + HandleScope scope; + CONVERT_ARG_CHECKED(String, address, 0); + Object* result = LiveObjectList::GetObjId(address); + return result; +#else + return Heap::undefined_value(); +#endif +} + + +// Gets the retainers that references the specified object alive. +static MaybeObject* Runtime_GetLOLObjRetainers(Arguments args) { +#ifdef LIVE_OBJECT_LIST + HandleScope scope; + CONVERT_SMI_CHECKED(obj_id, args[0]); + RUNTIME_ASSERT(args[1]->IsUndefined() || args[1]->IsJSObject()); + RUNTIME_ASSERT(args[2]->IsUndefined() || args[2]->IsBoolean()); + RUNTIME_ASSERT(args[3]->IsUndefined() || args[3]->IsSmi()); + RUNTIME_ASSERT(args[4]->IsUndefined() || args[4]->IsSmi()); + CONVERT_ARG_CHECKED(JSObject, filter_obj, 5); + + Handle instance_filter; + if (args[1]->IsJSObject()) { + instance_filter = args.at(1); + } + bool verbose = false; + if (args[2]->IsBoolean()) { + verbose = args[2]->IsTrue(); + } + int start = 0; + if (args[3]->IsSmi()) { + start = Smi::cast(args[3])->value(); + } + int limit = Smi::kMaxValue; + if (args[4]->IsSmi()) { + limit = Smi::cast(args[4])->value(); + } + + return LiveObjectList::GetObjRetainers(obj_id, + instance_filter, + verbose, + start, + limit, + filter_obj); +#else + return Heap::undefined_value(); +#endif +} + + +// Gets the reference path between 2 objects. +static MaybeObject* Runtime_GetLOLPath(Arguments args) { +#ifdef LIVE_OBJECT_LIST + HandleScope scope; + CONVERT_SMI_CHECKED(obj_id1, args[0]); + CONVERT_SMI_CHECKED(obj_id2, args[1]); + RUNTIME_ASSERT(args[2]->IsUndefined() || args[2]->IsJSObject()); + + Handle instance_filter; + if (args[2]->IsJSObject()) { + instance_filter = args.at(2); + } + + Object* result = + LiveObjectList::GetPath(obj_id1, obj_id2, instance_filter); + return result; +#else + return Heap::undefined_value(); +#endif +} + + +// Generates the response to a debugger request for a list of all +// previously captured live object lists. +static MaybeObject* Runtime_InfoLOL(Arguments args) { +#ifdef LIVE_OBJECT_LIST + CONVERT_SMI_CHECKED(start, args[0]); + CONVERT_SMI_CHECKED(count, args[1]); + return LiveObjectList::Info(start, count); +#else + return Heap::undefined_value(); +#endif +} + + +// Gets a dump of the specified object as requested by the debugger. +// This is only used for obj ids shown in live object lists. +static MaybeObject* Runtime_PrintLOLObj(Arguments args) { +#ifdef LIVE_OBJECT_LIST + HandleScope scope; + CONVERT_SMI_CHECKED(obj_id, args[0]); + Object* result = LiveObjectList::PrintObj(obj_id); + return result; +#else + return Heap::undefined_value(); +#endif +} + + +// Resets and releases all previously captured live object lists. +static MaybeObject* Runtime_ResetLOL(Arguments args) { +#ifdef LIVE_OBJECT_LIST + LiveObjectList::Reset(); + return Heap::undefined_value(); +#else + return Heap::undefined_value(); +#endif +} + + +// Generates the response to a debugger request for a summary of the types +// of objects in the difference between the captured live object lists +// specified by id1 and id2. +// If id1 is 0 (i.e. not a valid lol), then the whole of lol id2 will be +// summarized. +static MaybeObject* Runtime_SummarizeLOL(Arguments args) { +#ifdef LIVE_OBJECT_LIST + HandleScope scope; + CONVERT_SMI_CHECKED(id1, args[0]); + CONVERT_SMI_CHECKED(id2, args[1]); + CONVERT_ARG_CHECKED(JSObject, filter_obj, 2); + + EnterDebugger enter_debugger; + return LiveObjectList::Summarize(id1, id2, filter_obj); +#else + return Heap::undefined_value(); +#endif +} + #endif // ENABLE_DEBUGGER_SUPPORT diff --git a/deps/v8/src/runtime.h b/deps/v8/src/runtime.h index 06437ef9fb..9dd6eda0a9 100644 --- a/deps/v8/src/runtime.h +++ b/deps/v8/src/runtime.h @@ -241,7 +241,7 @@ namespace internal { F(ResolvePossiblyDirectEval, 4, 2) \ F(ResolvePossiblyDirectEvalNoLookup, 4, 2) \ \ - F(SetProperty, -1 /* 3 or 4 */, 1) \ + F(SetProperty, -1 /* 4 or 5 */, 1) \ F(DefineOrRedefineDataProperty, 4, 1) \ F(DefineOrRedefineAccessorProperty, 5, 1) \ F(IgnoreAttributesAndSetProperty, -1 /* 3 or 4 */, 1) \ @@ -288,12 +288,12 @@ namespace internal { F(DeleteContextSlot, 2, 1) \ F(LoadContextSlot, 2, 2) \ F(LoadContextSlotNoReferenceError, 2, 2) \ - F(StoreContextSlot, 3, 1) \ + F(StoreContextSlot, 4, 1) \ \ /* Declarations and initialization */ \ - F(DeclareGlobals, 3, 1) \ + F(DeclareGlobals, 4, 1) \ F(DeclareContextSlot, 4, 1) \ - F(InitializeVarGlobal, -1 /* 1 or 2 */, 1) \ + F(InitializeVarGlobal, -1 /* 2 or 3 */, 1) \ F(InitializeConstGlobal, 2, 1) \ F(InitializeConstContextSlot, 3, 1) \ F(OptimizeObjectForAddingMultipleProperties, 2, 1) \ @@ -376,7 +376,21 @@ namespace internal { \ F(SetFlags, 1, 1) \ F(CollectGarbage, 1, 1) \ - F(GetHeapUsage, 0, 1) + F(GetHeapUsage, 0, 1) \ + \ + /* LiveObjectList support*/ \ + F(HasLOLEnabled, 0, 1) \ + F(CaptureLOL, 0, 1) \ + F(DeleteLOL, 1, 1) \ + F(DumpLOL, 5, 1) \ + F(GetLOLObj, 1, 1) \ + F(GetLOLObjId, 1, 1) \ + F(GetLOLObjRetainers, 6, 1) \ + F(GetLOLPath, 3, 1) \ + F(InfoLOL, 2, 1) \ + F(PrintLOLObj, 1, 1) \ + F(ResetLOL, 0, 1) \ + F(SummarizeLOL, 3, 1) #else #define RUNTIME_FUNCTION_LIST_DEBUGGER_SUPPORT(F) @@ -538,7 +552,8 @@ class Runtime : public AllStatic { Handle object, Handle key, Handle value, - PropertyAttributes attr); + PropertyAttributes attr, + StrictModeFlag strict); MUST_USE_RESULT static MaybeObject* ForceSetObjectProperty( Handle object, diff --git a/deps/v8/src/stub-cache.cc b/deps/v8/src/stub-cache.cc index abb26d6ed4..360f0b743e 100644 --- a/deps/v8/src/stub-cache.cc +++ b/deps/v8/src/stub-cache.cc @@ -498,13 +498,13 @@ MaybeObject* StubCache::ComputeStoreField(String* name, JSObject* receiver, int field_index, Map* transition, - Code::ExtraICState extra_ic_state) { + StrictModeFlag strict_mode) { PropertyType type = (transition == NULL) ? FIELD : MAP_TRANSITION; Code::Flags flags = Code::ComputeMonomorphicFlags( - Code::STORE_IC, type, extra_ic_state); + Code::STORE_IC, type, strict_mode); Object* code = receiver->map()->FindInCodeCache(name, flags); if (code->IsUndefined()) { - StoreStubCompiler compiler(extra_ic_state); + StoreStubCompiler compiler(strict_mode); { MaybeObject* maybe_code = compiler.CompileStoreField(receiver, field_index, transition, name); if (!maybe_code->ToObject(&code)) return maybe_code; @@ -521,13 +521,15 @@ MaybeObject* StubCache::ComputeStoreField(String* name, } -MaybeObject* StubCache::ComputeKeyedStoreSpecialized(JSObject* receiver) { +MaybeObject* StubCache::ComputeKeyedStoreSpecialized( + JSObject* receiver, + StrictModeFlag strict_mode) { Code::Flags flags = - Code::ComputeMonomorphicFlags(Code::KEYED_STORE_IC, NORMAL); + Code::ComputeMonomorphicFlags(Code::KEYED_STORE_IC, NORMAL, strict_mode); String* name = Heap::KeyedStoreSpecialized_symbol(); Object* code = receiver->map()->FindInCodeCache(name, flags); if (code->IsUndefined()) { - KeyedStoreStubCompiler compiler; + KeyedStoreStubCompiler compiler(strict_mode); { MaybeObject* maybe_code = compiler.CompileStoreSpecialized(receiver); if (!maybe_code->ToObject(&code)) return maybe_code; } @@ -542,7 +544,9 @@ MaybeObject* StubCache::ComputeKeyedStoreSpecialized(JSObject* receiver) { } -MaybeObject* StubCache::ComputeKeyedStorePixelArray(JSObject* receiver) { +MaybeObject* StubCache::ComputeKeyedStorePixelArray( + JSObject* receiver, + StrictModeFlag strict_mode) { // Using NORMAL as the PropertyType for array element stores is a misuse. The // generated stub always accesses fast elements, not slow-mode fields, but // some property type is required for the stub lookup. Note that overloading @@ -550,11 +554,11 @@ MaybeObject* StubCache::ComputeKeyedStorePixelArray(JSObject* receiver) { // other keyed field stores. This is guaranteed to be the case since all field // keyed stores that are not array elements go through a generic builtin stub. Code::Flags flags = - Code::ComputeMonomorphicFlags(Code::KEYED_STORE_IC, NORMAL); + Code::ComputeMonomorphicFlags(Code::KEYED_STORE_IC, NORMAL, strict_mode); String* name = Heap::KeyedStorePixelArray_symbol(); Object* code = receiver->map()->FindInCodeCache(name, flags); if (code->IsUndefined()) { - KeyedStoreStubCompiler compiler; + KeyedStoreStubCompiler compiler(strict_mode); { MaybeObject* maybe_code = compiler.CompileStorePixelArray(receiver); if (!maybe_code->ToObject(&code)) return maybe_code; } @@ -598,11 +602,13 @@ ExternalArrayType ElementsKindToExternalArrayType(JSObject::ElementsKind kind) { MaybeObject* StubCache::ComputeKeyedLoadOrStoreExternalArray( JSObject* receiver, - bool is_store) { + bool is_store, + StrictModeFlag strict_mode) { Code::Flags flags = Code::ComputeMonomorphicFlags( is_store ? Code::KEYED_STORE_IC : Code::KEYED_LOAD_IC, - NORMAL); + NORMAL, + strict_mode); ExternalArrayType array_type = ElementsKindToExternalArrayType(receiver->GetElementsKind()); String* name = @@ -615,9 +621,9 @@ MaybeObject* StubCache::ComputeKeyedLoadOrStoreExternalArray( Object* code = map->FindInCodeCache(name, flags); if (code->IsUndefined()) { ExternalArrayStubCompiler compiler; - { MaybeObject* maybe_code = - is_store ? compiler.CompileKeyedStoreStub(array_type, flags) : - compiler.CompileKeyedLoadStub(array_type, flags); + { MaybeObject* maybe_code = is_store + ? compiler.CompileKeyedStoreStub(array_type, flags) + : compiler.CompileKeyedLoadStub(array_type, flags); if (!maybe_code->ToObject(&code)) return maybe_code; } if (is_store) { @@ -637,8 +643,8 @@ MaybeObject* StubCache::ComputeKeyedLoadOrStoreExternalArray( } -MaybeObject* StubCache::ComputeStoreNormal(Code::ExtraICState extra_ic_state) { - return Builtins::builtin(extra_ic_state == StoreIC::kStoreICStrict +MaybeObject* StubCache::ComputeStoreNormal(StrictModeFlag strict_mode) { + return Builtins::builtin((strict_mode == kStrictMode) ? Builtins::StoreIC_Normal_Strict : Builtins::StoreIC_Normal); } @@ -647,12 +653,12 @@ MaybeObject* StubCache::ComputeStoreNormal(Code::ExtraICState extra_ic_state) { MaybeObject* StubCache::ComputeStoreGlobal(String* name, GlobalObject* receiver, JSGlobalPropertyCell* cell, - Code::ExtraICState extra_ic_state) { + StrictModeFlag strict_mode) { Code::Flags flags = Code::ComputeMonomorphicFlags( - Code::STORE_IC, NORMAL, extra_ic_state); + Code::STORE_IC, NORMAL, strict_mode); Object* code = receiver->map()->FindInCodeCache(name, flags); if (code->IsUndefined()) { - StoreStubCompiler compiler(extra_ic_state); + StoreStubCompiler compiler(strict_mode); { MaybeObject* maybe_code = compiler.CompileStoreGlobal(receiver, cell, name); if (!maybe_code->ToObject(&code)) return maybe_code; @@ -673,13 +679,13 @@ MaybeObject* StubCache::ComputeStoreCallback( String* name, JSObject* receiver, AccessorInfo* callback, - Code::ExtraICState extra_ic_state) { + StrictModeFlag strict_mode) { ASSERT(v8::ToCData
(callback->setter()) != 0); Code::Flags flags = Code::ComputeMonomorphicFlags( - Code::STORE_IC, CALLBACKS, extra_ic_state); + Code::STORE_IC, CALLBACKS, strict_mode); Object* code = receiver->map()->FindInCodeCache(name, flags); if (code->IsUndefined()) { - StoreStubCompiler compiler(extra_ic_state); + StoreStubCompiler compiler(strict_mode); { MaybeObject* maybe_code = compiler.CompileStoreCallback(receiver, callback, name); if (!maybe_code->ToObject(&code)) return maybe_code; @@ -699,12 +705,12 @@ MaybeObject* StubCache::ComputeStoreCallback( MaybeObject* StubCache::ComputeStoreInterceptor( String* name, JSObject* receiver, - Code::ExtraICState extra_ic_state) { + StrictModeFlag strict_mode) { Code::Flags flags = Code::ComputeMonomorphicFlags( - Code::STORE_IC, INTERCEPTOR, extra_ic_state); + Code::STORE_IC, INTERCEPTOR, strict_mode); Object* code = receiver->map()->FindInCodeCache(name, flags); if (code->IsUndefined()) { - StoreStubCompiler compiler(extra_ic_state); + StoreStubCompiler compiler(strict_mode); { MaybeObject* maybe_code = compiler.CompileStoreInterceptor(receiver, name); if (!maybe_code->ToObject(&code)) return maybe_code; @@ -724,12 +730,14 @@ MaybeObject* StubCache::ComputeStoreInterceptor( MaybeObject* StubCache::ComputeKeyedStoreField(String* name, JSObject* receiver, int field_index, - Map* transition) { + Map* transition, + StrictModeFlag strict_mode) { PropertyType type = (transition == NULL) ? FIELD : MAP_TRANSITION; - Code::Flags flags = Code::ComputeMonomorphicFlags(Code::KEYED_STORE_IC, type); + Code::Flags flags = Code::ComputeMonomorphicFlags( + Code::KEYED_STORE_IC, type, strict_mode); Object* code = receiver->map()->FindInCodeCache(name, flags); if (code->IsUndefined()) { - KeyedStoreStubCompiler compiler; + KeyedStoreStubCompiler compiler(strict_mode); { MaybeObject* maybe_code = compiler.CompileStoreField(receiver, field_index, transition, name); if (!maybe_code->ToObject(&code)) return maybe_code; @@ -1417,12 +1425,17 @@ MaybeObject* LoadPropertyWithInterceptorForCall(Arguments args) { MaybeObject* StoreInterceptorProperty(Arguments args) { + ASSERT(args.length() == 4); JSObject* recv = JSObject::cast(args[0]); String* name = String::cast(args[1]); Object* value = args[2]; + StrictModeFlag strict = + static_cast(Smi::cast(args[3])->value()); + ASSERT(strict == kStrictMode || strict == kNonStrictMode); ASSERT(recv->HasNamedInterceptor()); PropertyAttributes attr = NONE; - MaybeObject* result = recv->SetPropertyWithInterceptor(name, value, attr); + MaybeObject* result = recv->SetPropertyWithInterceptor( + name, value, attr, strict); return result; } @@ -1675,8 +1688,8 @@ MaybeObject* KeyedLoadStubCompiler::GetCode(PropertyType type, String* name) { MaybeObject* StoreStubCompiler::GetCode(PropertyType type, String* name) { - Code::Flags flags = Code::ComputeMonomorphicFlags(Code::STORE_IC, type, - extra_ic_state_); + Code::Flags flags = Code::ComputeMonomorphicFlags( + Code::STORE_IC, type, strict_mode_); MaybeObject* result = GetCodeWithFlags(flags, name); if (!result->IsFailure()) { PROFILE(CodeCreateEvent(Logger::STORE_IC_TAG, @@ -1691,7 +1704,8 @@ MaybeObject* StoreStubCompiler::GetCode(PropertyType type, String* name) { MaybeObject* KeyedStoreStubCompiler::GetCode(PropertyType type, String* name) { - Code::Flags flags = Code::ComputeMonomorphicFlags(Code::KEYED_STORE_IC, type); + Code::Flags flags = Code::ComputeMonomorphicFlags( + Code::KEYED_STORE_IC, type, strict_mode_); MaybeObject* result = GetCodeWithFlags(flags, name); if (!result->IsFailure()) { PROFILE(CodeCreateEvent(Logger::KEYED_STORE_IC_TAG, diff --git a/deps/v8/src/stub-cache.h b/deps/v8/src/stub-cache.h index 4638da2ad7..6927076c16 100644 --- a/deps/v8/src/stub-cache.h +++ b/deps/v8/src/stub-cache.h @@ -143,27 +143,27 @@ class StubCache : public AllStatic { JSObject* receiver, int field_index, Map* transition, - Code::ExtraICState extra_ic_state); + StrictModeFlag strict_mode); MUST_USE_RESULT static MaybeObject* ComputeStoreNormal( - Code::ExtraICState extra_ic_state); + StrictModeFlag strict_mode); MUST_USE_RESULT static MaybeObject* ComputeStoreGlobal( String* name, GlobalObject* receiver, JSGlobalPropertyCell* cell, - Code::ExtraICState extra_ic_state); + StrictModeFlag strict_mode); MUST_USE_RESULT static MaybeObject* ComputeStoreCallback( String* name, JSObject* receiver, AccessorInfo* callback, - Code::ExtraICState extra_ic_state); + StrictModeFlag strict_mode); MUST_USE_RESULT static MaybeObject* ComputeStoreInterceptor( String* name, JSObject* receiver, - Code::ExtraICState extra_ic_state); + StrictModeFlag strict_mode); // --- @@ -171,17 +171,21 @@ class StubCache : public AllStatic { String* name, JSObject* receiver, int field_index, - Map* transition = NULL); + Map* transition, + StrictModeFlag strict_mode); MUST_USE_RESULT static MaybeObject* ComputeKeyedStoreSpecialized( - JSObject* receiver); + JSObject* receiver, + StrictModeFlag strict_mode); MUST_USE_RESULT static MaybeObject* ComputeKeyedStorePixelArray( - JSObject* receiver); + JSObject* receiver, + StrictModeFlag strict_mode); MUST_USE_RESULT static MaybeObject* ComputeKeyedLoadOrStoreExternalArray( JSObject* receiver, - bool is_store); + bool is_store, + StrictModeFlag strict_mode); // --- @@ -628,8 +632,8 @@ class KeyedLoadStubCompiler: public StubCompiler { class StoreStubCompiler: public StubCompiler { public: - explicit StoreStubCompiler(Code::ExtraICState extra_ic_state) - : extra_ic_state_(extra_ic_state) { } + explicit StoreStubCompiler(StrictModeFlag strict_mode) + : strict_mode_(strict_mode) { } MUST_USE_RESULT MaybeObject* CompileStoreField(JSObject* object, int index, @@ -649,12 +653,15 @@ class StoreStubCompiler: public StubCompiler { private: MaybeObject* GetCode(PropertyType type, String* name); - Code::ExtraICState extra_ic_state_; + StrictModeFlag strict_mode_; }; class KeyedStoreStubCompiler: public StubCompiler { public: + explicit KeyedStoreStubCompiler(StrictModeFlag strict_mode) + : strict_mode_(strict_mode) { } + MUST_USE_RESULT MaybeObject* CompileStoreField(JSObject* object, int index, Map* transition, @@ -666,6 +673,8 @@ class KeyedStoreStubCompiler: public StubCompiler { private: MaybeObject* GetCode(PropertyType type, String* name); + + StrictModeFlag strict_mode_; }; diff --git a/deps/v8/src/version.cc b/deps/v8/src/version.cc index 2475fb2f13..391addcbad 100644 --- a/deps/v8/src/version.cc +++ b/deps/v8/src/version.cc @@ -34,7 +34,7 @@ // cannot be changed without changing the SCons build script. #define MAJOR_VERSION 3 #define MINOR_VERSION 1 -#define BUILD_NUMBER 7 +#define BUILD_NUMBER 8 #define PATCH_LEVEL 0 #define CANDIDATE_VERSION false diff --git a/deps/v8/src/virtual-frame-heavy-inl.h b/deps/v8/src/virtual-frame-heavy-inl.h index ac844b44c7..cf12eca623 100644 --- a/deps/v8/src/virtual-frame-heavy-inl.h +++ b/deps/v8/src/virtual-frame-heavy-inl.h @@ -87,14 +87,6 @@ bool VirtualFrame::ConstantPoolOverflowed() { } -void VirtualFrame::Push(Handle value) { - ASSERT(!ConstantPoolOverflowed()); - FrameElement element = - FrameElement::ConstantElement(value, FrameElement::NOT_SYNCED); - elements_.Add(element); -} - - bool VirtualFrame::Equals(VirtualFrame* other) { #ifdef DEBUG for (int i = 0; i < RegisterAllocator::kNumRegisters; i++) { diff --git a/deps/v8/src/x64/code-stubs-x64.cc b/deps/v8/src/x64/code-stubs-x64.cc index 6cfeed3606..42373e3deb 100644 --- a/deps/v8/src/x64/code-stubs-x64.cc +++ b/deps/v8/src/x64/code-stubs-x64.cc @@ -2017,8 +2017,8 @@ void GenericUnaryOpStub::Generate(MacroAssembler* masm) { __ AbortIfSmi(rax); } - __ movq(rdx, FieldOperand(rax, HeapObject::kMapOffset)); - __ CompareRoot(rdx, Heap::kHeapNumberMapRootIndex); + __ CompareRoot(FieldOperand(rax, HeapObject::kMapOffset), + Heap::kHeapNumberMapRootIndex); __ j(not_equal, &slow); // Operand is a float, negate its value by flipping sign bit. __ movq(rdx, FieldOperand(rax, HeapNumber::kValueOffset)); @@ -2047,8 +2047,8 @@ void GenericUnaryOpStub::Generate(MacroAssembler* masm) { } // Check if the operand is a heap number. - __ movq(rdx, FieldOperand(rax, HeapObject::kMapOffset)); - __ CompareRoot(rdx, Heap::kHeapNumberMapRootIndex); + __ CompareRoot(FieldOperand(rax, HeapObject::kMapOffset), + Heap::kHeapNumberMapRootIndex); __ j(not_equal, &slow); // Convert the heap number in rax to an untagged integer in rcx. @@ -2081,6 +2081,157 @@ void GenericUnaryOpStub::Generate(MacroAssembler* masm) { } +void MathPowStub::Generate(MacroAssembler* masm) { + // Registers are used as follows: + // rdx = base + // rax = exponent + // rcx = temporary, result + + Label allocate_return, call_runtime; + + // Load input parameters. + __ movq(rdx, Operand(rsp, 2 * kPointerSize)); + __ movq(rax, Operand(rsp, 1 * kPointerSize)); + + // Save 1 in xmm3 - we need this several times later on. + __ movl(rcx, Immediate(1)); + __ cvtlsi2sd(xmm3, rcx); + + Label exponent_nonsmi; + Label base_nonsmi; + // If the exponent is a heap number go to that specific case. + __ JumpIfNotSmi(rax, &exponent_nonsmi); + __ JumpIfNotSmi(rdx, &base_nonsmi); + + // Optimized version when both exponent and base are smis. + Label powi; + __ SmiToInteger32(rdx, rdx); + __ cvtlsi2sd(xmm0, rdx); + __ jmp(&powi); + // Exponent is a smi and base is a heapnumber. + __ bind(&base_nonsmi); + __ CompareRoot(FieldOperand(rdx, HeapObject::kMapOffset), + Heap::kHeapNumberMapRootIndex); + __ j(not_equal, &call_runtime); + + __ movsd(xmm0, FieldOperand(rdx, HeapNumber::kValueOffset)); + + // Optimized version of pow if exponent is a smi. + // xmm0 contains the base. + __ bind(&powi); + __ SmiToInteger32(rax, rax); + + // Save exponent in base as we need to check if exponent is negative later. + // We know that base and exponent are in different registers. + __ movq(rdx, rax); + + // Get absolute value of exponent. + NearLabel no_neg; + __ cmpl(rax, Immediate(0)); + __ j(greater_equal, &no_neg); + __ negl(rax); + __ bind(&no_neg); + + // Load xmm1 with 1. + __ movsd(xmm1, xmm3); + NearLabel while_true; + NearLabel no_multiply; + + __ bind(&while_true); + __ shrl(rax, Immediate(1)); + __ j(not_carry, &no_multiply); + __ mulsd(xmm1, xmm0); + __ bind(&no_multiply); + __ mulsd(xmm0, xmm0); + __ j(not_zero, &while_true); + + // Base has the original value of the exponent - if the exponent is + // negative return 1/result. + __ testl(rdx, rdx); + __ j(positive, &allocate_return); + // Special case if xmm1 has reached infinity. + __ divsd(xmm3, xmm1); + __ movsd(xmm1, xmm3); + __ xorpd(xmm0, xmm0); + __ ucomisd(xmm0, xmm1); + __ j(equal, &call_runtime); + + __ jmp(&allocate_return); + + // Exponent (or both) is a heapnumber - no matter what we should now work + // on doubles. + __ bind(&exponent_nonsmi); + __ CompareRoot(FieldOperand(rax, HeapObject::kMapOffset), + Heap::kHeapNumberMapRootIndex); + __ j(not_equal, &call_runtime); + __ movsd(xmm1, FieldOperand(rax, HeapNumber::kValueOffset)); + // Test if exponent is nan. + __ ucomisd(xmm1, xmm1); + __ j(parity_even, &call_runtime); + + NearLabel base_not_smi; + NearLabel handle_special_cases; + __ JumpIfNotSmi(rdx, &base_not_smi); + __ SmiToInteger32(rdx, rdx); + __ cvtlsi2sd(xmm0, rdx); + __ jmp(&handle_special_cases); + + __ bind(&base_not_smi); + __ CompareRoot(FieldOperand(rdx, HeapObject::kMapOffset), + Heap::kHeapNumberMapRootIndex); + __ j(not_equal, &call_runtime); + __ movl(rcx, FieldOperand(rdx, HeapNumber::kExponentOffset)); + __ andl(rcx, Immediate(HeapNumber::kExponentMask)); + __ cmpl(rcx, Immediate(HeapNumber::kExponentMask)); + // base is NaN or +/-Infinity + __ j(greater_equal, &call_runtime); + __ movsd(xmm0, FieldOperand(rdx, HeapNumber::kValueOffset)); + + // base is in xmm0 and exponent is in xmm1. + __ bind(&handle_special_cases); + NearLabel not_minus_half; + // Test for -0.5. + // Load xmm2 with -0.5. + __ movq(rcx, V8_UINT64_C(0xBFE0000000000000), RelocInfo::NONE); + __ movq(xmm2, rcx); + // xmm2 now has -0.5. + __ ucomisd(xmm2, xmm1); + __ j(not_equal, ¬_minus_half); + + // Calculates reciprocal of square root. + // sqrtsd returns -0 when input is -0. ECMA spec requires +0. + __ xorpd(xmm1, xmm1); + __ addsd(xmm1, xmm0); + __ sqrtsd(xmm1, xmm1); + __ divsd(xmm3, xmm1); + __ movsd(xmm1, xmm3); + __ jmp(&allocate_return); + + // Test for 0.5. + __ bind(¬_minus_half); + // Load xmm2 with 0.5. + // Since xmm3 is 1 and xmm2 is -0.5 this is simply xmm2 + xmm3. + __ addsd(xmm2, xmm3); + // xmm2 now has 0.5. + __ ucomisd(xmm2, xmm1); + __ j(not_equal, &call_runtime); + // Calculates square root. + // sqrtsd returns -0 when input is -0. ECMA spec requires +0. + __ xorpd(xmm1, xmm1); + __ addsd(xmm1, xmm0); + __ sqrtsd(xmm1, xmm1); + + __ bind(&allocate_return); + __ AllocateHeapNumber(rcx, rax, &call_runtime); + __ movsd(FieldOperand(rcx, HeapNumber::kValueOffset), xmm1); + __ movq(rax, rcx); + __ ret(2 * kPointerSize); + + __ bind(&call_runtime); + __ TailCallRuntime(Runtime::kMath_pow_cfunction, 2, 1); +} + + void ArgumentsAccessStub::GenerateReadElement(MacroAssembler* masm) { // The key is in rdx and the parameter count is in rax. @@ -4682,6 +4833,61 @@ void StringCompareStub::Generate(MacroAssembler* masm) { __ TailCallRuntime(Runtime::kStringCompare, 2, 1); } + +void StringCharAtStub::Generate(MacroAssembler* masm) { + // Expects two arguments (object, index) on the stack: + + // Stack frame on entry. + // rsp[0]: return address + // rsp[8]: index + // rsp[16]: object + + Register object = rbx; + Register index = rax; + Register scratch1 = rcx; + Register scratch2 = rdx; + Register result = rax; + + __ pop(scratch1); // Return address. + __ pop(index); + __ pop(object); + __ push(scratch1); + + Label need_conversion; + Label index_out_of_range; + Label done; + StringCharAtGenerator generator(object, + index, + scratch1, + scratch2, + 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. + __ Move(result, Factory::empty_string()); + __ jmp(&done); + + __ bind(&need_conversion); + // Move smi zero into the result register, which will trigger + // conversion. + __ Move(result, Smi::FromInt(0)); + __ jmp(&done); + + StubRuntimeCallHelper call_helper; + generator.GenerateSlow(masm, call_helper); + + __ bind(&done); + __ ret(0); +} + + void ICCompareStub::GenerateSmis(MacroAssembler* masm) { ASSERT(state_ == CompareIC::SMIS); NearLabel miss; diff --git a/deps/v8/src/x64/codegen-x64.cc b/deps/v8/src/x64/codegen-x64.cc index dfee36ed76..ad114c2431 100644 --- a/deps/v8/src/x64/codegen-x64.cc +++ b/deps/v8/src/x64/codegen-x64.cc @@ -2747,7 +2747,8 @@ void CodeGenerator::DeclareGlobals(Handle pairs) { frame_->EmitPush(rsi); // The context is the first argument. frame_->EmitPush(kScratchRegister); frame_->EmitPush(Smi::FromInt(is_eval() ? 1 : 0)); - Result ignored = frame_->CallRuntime(Runtime::kDeclareGlobals, 3); + frame_->EmitPush(Smi::FromInt(strict_mode_flag())); + Result ignored = frame_->CallRuntime(Runtime::kDeclareGlobals, 4); // Return value is ignored. } @@ -4605,7 +4606,8 @@ void CodeGenerator::StoreToSlot(Slot* slot, InitState init_state) { // by initialization. value = frame_->CallRuntime(Runtime::kInitializeConstContextSlot, 3); } else { - value = frame_->CallRuntime(Runtime::kStoreContextSlot, 3); + frame_->Push(Smi::FromInt(strict_mode_flag())); + value = frame_->CallRuntime(Runtime::kStoreContextSlot, 4); } // Storing a variable must keep the (new) value on the expression // stack. This is necessary for compiling chained assignment @@ -4694,18 +4696,7 @@ void CodeGenerator::VisitVariableProxy(VariableProxy* node) { void CodeGenerator::VisitLiteral(Literal* node) { Comment cmnt(masm_, "[ Literal"); - if (frame_->ConstantPoolOverflowed()) { - Result temp = allocator_->Allocate(); - ASSERT(temp.is_valid()); - if (node->handle()->IsSmi()) { - __ Move(temp.reg(), Smi::cast(*node->handle())); - } else { - __ movq(temp.reg(), node->handle(), RelocInfo::EMBEDDED_OBJECT); - } - frame_->Push(&temp); - } else { - frame_->Push(node->handle()); - } + frame_->Push(node->handle()); } @@ -4925,8 +4916,9 @@ void CodeGenerator::VisitObjectLiteral(ObjectLiteral* node) { Load(property->key()); Load(property->value()); if (property->emit_store()) { + frame_->Push(Smi::FromInt(NONE)); // PropertyAttributes // Ignore the result. - Result ignored = frame_->CallRuntime(Runtime::kSetProperty, 3); + Result ignored = frame_->CallRuntime(Runtime::kSetProperty, 4); } else { frame_->Drop(3); } @@ -8086,8 +8078,12 @@ class DeferredReferenceSetKeyedValue: public DeferredCode { public: DeferredReferenceSetKeyedValue(Register value, Register key, - Register receiver) - : value_(value), key_(key), receiver_(receiver) { + Register receiver, + StrictModeFlag strict_mode) + : value_(value), + key_(key), + receiver_(receiver), + strict_mode_(strict_mode) { set_comment("[ DeferredReferenceSetKeyedValue"); } @@ -8100,6 +8096,7 @@ class DeferredReferenceSetKeyedValue: public DeferredCode { Register key_; Register receiver_; Label patch_site_; + StrictModeFlag strict_mode_; }; @@ -8151,7 +8148,9 @@ void DeferredReferenceSetKeyedValue::Generate() { } // Call the IC stub. - Handle ic(Builtins::builtin(Builtins::KeyedStoreIC_Initialize)); + Handle ic(Builtins::builtin( + (strict_mode_ == kStrictMode) ? Builtins::KeyedStoreIC_Initialize_Strict + : Builtins::KeyedStoreIC_Initialize)); __ Call(ic, RelocInfo::CODE_TARGET); // The delta from the start of the map-compare instructions (initial movq) // to the test instruction. We use masm_-> directly here instead of the @@ -8492,7 +8491,8 @@ Result CodeGenerator::EmitKeyedStore(StaticType* key_type) { DeferredReferenceSetKeyedValue* deferred = new DeferredReferenceSetKeyedValue(result.reg(), key.reg(), - receiver.reg()); + receiver.reg(), + strict_mode_flag()); // Check that the receiver is not a smi. __ JumpIfSmi(receiver.reg(), deferred->entry_label()); @@ -8554,7 +8554,7 @@ Result CodeGenerator::EmitKeyedStore(StaticType* key_type) { deferred->BindExit(); } else { - result = frame()->CallKeyedStoreIC(); + result = frame()->CallKeyedStoreIC(strict_mode_flag()); // Make sure 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 diff --git a/deps/v8/src/x64/full-codegen-x64.cc b/deps/v8/src/x64/full-codegen-x64.cc index b8d7e5019d..0ad6ec2376 100644 --- a/deps/v8/src/x64/full-codegen-x64.cc +++ b/deps/v8/src/x64/full-codegen-x64.cc @@ -327,13 +327,6 @@ void FullCodeGenerator::EmitReturnSequence() { } -FullCodeGenerator::ConstantOperand FullCodeGenerator::GetConstantOperand( - Token::Value op, Expression* left, Expression* right) { - ASSERT(ShouldInlineSmiCase(op)); - return kNoConstants; -} - - void FullCodeGenerator::EffectContext::Plug(Slot* slot) const { } @@ -742,7 +735,9 @@ void FullCodeGenerator::EmitDeclaration(Variable* variable, prop->key()->AsLiteral()->handle()->IsSmi()); __ Move(rcx, prop->key()->AsLiteral()->handle()); - Handle ic(Builtins::builtin(Builtins::KeyedStoreIC_Initialize)); + Handle ic(Builtins::builtin(is_strict() + ? Builtins::KeyedStoreIC_Initialize_Strict + : Builtins::KeyedStoreIC_Initialize)); EmitCallIC(ic, RelocInfo::CODE_TARGET); } } @@ -759,7 +754,8 @@ void FullCodeGenerator::DeclareGlobals(Handle pairs) { __ push(rsi); // The context is the first argument. __ Push(pairs); __ Push(Smi::FromInt(is_eval() ? 1 : 0)); - __ CallRuntime(Runtime::kDeclareGlobals, 3); + __ Push(Smi::FromInt(strict_mode_flag())); + __ CallRuntime(Runtime::kDeclareGlobals, 4); // Return value is ignored. } @@ -1403,7 +1399,8 @@ void FullCodeGenerator::VisitObjectLiteral(ObjectLiteral* expr) { VisitForStackValue(key); VisitForStackValue(value); if (property->emit_store()) { - __ CallRuntime(Runtime::kSetProperty, 3); + __ Push(Smi::FromInt(NONE)); // PropertyAttributes + __ CallRuntime(Runtime::kSetProperty, 4); } else { __ Drop(3); } @@ -1579,14 +1576,8 @@ void FullCodeGenerator::VisitAssignment(Assignment* expr) { } Token::Value op = expr->binary_op(); - ConstantOperand constant = ShouldInlineSmiCase(op) - ? GetConstantOperand(op, expr->target(), expr->value()) - : kNoConstants; - ASSERT(constant == kRightConstant || constant == kNoConstants); - if (constant == kNoConstants) { - __ push(rax); // Left operand goes on the stack. - VisitForAccumulatorValue(expr->value()); - } + __ push(rax); // Left operand goes on the stack. + VisitForAccumulatorValue(expr->value()); OverwriteMode mode = expr->value()->ResultOverwriteAllowed() ? OVERWRITE_RIGHT @@ -1598,8 +1589,7 @@ void FullCodeGenerator::VisitAssignment(Assignment* expr) { op, mode, expr->target(), - expr->value(), - constant); + expr->value()); } else { EmitBinaryOp(op, mode); } @@ -1650,10 +1640,7 @@ void FullCodeGenerator::EmitInlineSmiBinaryOp(Expression* expr, Token::Value op, OverwriteMode mode, Expression* left, - Expression* right, - ConstantOperand constant) { - ASSERT(constant == kNoConstants); // Only handled case. - + 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. @@ -1750,7 +1737,9 @@ void FullCodeGenerator::EmitAssignment(Expression* expr, int bailout_ast_id) { __ movq(rdx, rax); __ pop(rax); // Restore value. __ Move(rcx, prop->key()->AsLiteral()->handle()); - Handle ic(Builtins::builtin(Builtins::StoreIC_Initialize)); + Handle ic(Builtins::builtin( + is_strict() ? Builtins::StoreIC_Initialize_Strict + : Builtins::StoreIC_Initialize)); EmitCallIC(ic, RelocInfo::CODE_TARGET); break; } @@ -1771,7 +1760,9 @@ void FullCodeGenerator::EmitAssignment(Expression* expr, int bailout_ast_id) { __ pop(rdx); } __ pop(rax); // Restore value. - Handle ic(Builtins::builtin(Builtins::KeyedStoreIC_Initialize)); + Handle ic(Builtins::builtin( + is_strict() ? Builtins::KeyedStoreIC_Initialize_Strict + : Builtins::KeyedStoreIC_Initialize)); EmitCallIC(ic, RelocInfo::CODE_TARGET); break; } @@ -1866,7 +1857,8 @@ void FullCodeGenerator::EmitVariableAssignment(Variable* var, __ push(rax); // Value. __ push(rsi); // Context. __ Push(var->name()); - __ CallRuntime(Runtime::kStoreContextSlot, 3); + __ Push(Smi::FromInt(strict_mode_flag())); + __ CallRuntime(Runtime::kStoreContextSlot, 4); break; } } @@ -1897,7 +1889,9 @@ void FullCodeGenerator::EmitNamedPropertyAssignment(Assignment* expr) { } else { __ pop(rdx); } - Handle ic(Builtins::builtin(Builtins::StoreIC_Initialize)); + Handle ic(Builtins::builtin( + is_strict() ? Builtins::StoreIC_Initialize_Strict + : Builtins::StoreIC_Initialize)); EmitCallIC(ic, RelocInfo::CODE_TARGET); // If the assignment ends an initialization block, revert to fast case. @@ -1935,7 +1929,9 @@ void FullCodeGenerator::EmitKeyedPropertyAssignment(Assignment* expr) { } // Record source code position before IC call. SetSourcePosition(expr->position()); - Handle ic(Builtins::builtin(Builtins::KeyedStoreIC_Initialize)); + Handle ic(Builtins::builtin( + is_strict() ? Builtins::KeyedStoreIC_Initialize_Strict + : Builtins::KeyedStoreIC_Initialize)); EmitCallIC(ic, RelocInfo::CODE_TARGET); // If the assignment ends an initialization block, revert to fast case. @@ -2051,6 +2047,27 @@ void FullCodeGenerator::EmitCallWithStub(Call* expr) { } +void FullCodeGenerator::EmitResolvePossiblyDirectEval(ResolveEvalFlag flag, + 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 + scope()->num_parameters()) * kPointerSize)); + + // Push the strict mode flag. + __ Push(Smi::FromInt(strict_mode_flag())); + + __ CallRuntime(flag == SKIP_CONTEXT_LOOKUP + ? Runtime::kResolvePossiblyDirectEvalNoLookup + : Runtime::kResolvePossiblyDirectEval, 4); +} + + void FullCodeGenerator::VisitCall(Call* expr) { #ifdef DEBUG // We want to verify that RecordJSReturnSite gets called on all paths @@ -2078,21 +2095,30 @@ void FullCodeGenerator::VisitCall(Call* expr) { VisitForStackValue(args->at(i)); } - // Push copy of the function - found below the arguments. - __ push(Operand(rsp, (arg_count + 1) * kPointerSize)); - - // 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); + // If we know that eval can only be shadowed by eval-introduced + // variables we attempt to load the global eval function directly + // in generated code. If we succeed, there is no need to perform a + // context lookup in the runtime system. + Label done; + if (var->AsSlot() != NULL && var->mode() == Variable::DYNAMIC_GLOBAL) { + Label slow; + EmitLoadGlobalSlotCheckExtensions(var->AsSlot(), + NOT_INSIDE_TYPEOF, + &slow); + // Push the function and resolve eval. + __ push(rax); + EmitResolvePossiblyDirectEval(SKIP_CONTEXT_LOOKUP, arg_count); + __ jmp(&done); + __ bind(&slow); } - // Push the receiver of the enclosing function and do runtime call. - __ push(Operand(rbp, (2 + scope()->num_parameters()) * kPointerSize)); - // Push the strict mode flag. - __ Push(Smi::FromInt(strict_mode_flag())); - __ CallRuntime(Runtime::kResolvePossiblyDirectEval, 4); + // Push copy of the function (found below the arguments) and + // resolve eval. + __ push(Operand(rsp, (arg_count + 1) * kPointerSize)); + EmitResolvePossiblyDirectEval(PERFORM_CONTEXT_LOOKUP, arg_count); + if (done.is_linked()) { + __ bind(&done); + } // The runtime call returns a pair of values in rax (function) and // rdx (receiver). Touch up the stack with the right values. @@ -2709,7 +2735,8 @@ void FullCodeGenerator::EmitMathPow(ZoneList* args) { ASSERT(args->length() == 2); VisitForStackValue(args->at(0)); VisitForStackValue(args->at(1)); - __ CallRuntime(Runtime::kMath_pow, 2); + MathPowStub stub; + __ CallStub(&stub); context()->Plug(rax); } @@ -2968,7 +2995,73 @@ void FullCodeGenerator::EmitSwapElements(ZoneList* args) { VisitForStackValue(args->at(0)); VisitForStackValue(args->at(1)); VisitForStackValue(args->at(2)); + Label done; + Label slow_case; + Register object = rax; + Register index_1 = rbx; + Register index_2 = rcx; + Register elements = rdi; + Register temp = rdx; + __ movq(object, Operand(rsp, 2 * kPointerSize)); + // Fetch the map and check if array is in fast case. + // Check that object doesn't require security checks and + // has no indexed interceptor. + __ CmpObjectType(object, FIRST_JS_OBJECT_TYPE, temp); + __ j(below, &slow_case); + __ testb(FieldOperand(temp, Map::kBitFieldOffset), + Immediate(KeyedLoadIC::kSlowCaseBitFieldMask)); + __ j(not_zero, &slow_case); + + // Check the object's elements are in fast case and writable. + __ movq(elements, FieldOperand(object, JSObject::kElementsOffset)); + __ CompareRoot(FieldOperand(elements, HeapObject::kMapOffset), + Heap::kFixedArrayMapRootIndex); + __ j(not_equal, &slow_case); + + // Check that both indices are smis. + __ movq(index_1, Operand(rsp, 1 * kPointerSize)); + __ movq(index_2, Operand(rsp, 0 * kPointerSize)); + __ JumpIfNotBothSmi(index_1, index_2, &slow_case); + + // Check that both indices are valid. + // The JSArray length field is a smi since the array is in fast case mode. + __ movq(temp, FieldOperand(object, JSArray::kLengthOffset)); + __ SmiCompare(temp, index_1); + __ j(below_equal, &slow_case); + __ SmiCompare(temp, index_2); + __ j(below_equal, &slow_case); + + __ SmiToInteger32(index_1, index_1); + __ SmiToInteger32(index_2, index_2); + // Bring addresses into index1 and index2. + __ lea(index_1, FieldOperand(elements, index_1, times_pointer_size, + FixedArray::kHeaderSize)); + __ lea(index_2, FieldOperand(elements, index_2, times_pointer_size, + FixedArray::kHeaderSize)); + + // Swap elements. Use object and temp as scratch registers. + __ movq(object, Operand(index_1, 0)); + __ movq(temp, Operand(index_2, 0)); + __ movq(Operand(index_2, 0), object); + __ movq(Operand(index_1, 0), temp); + + Label new_space; + __ InNewSpace(elements, temp, equal, &new_space); + + __ movq(object, elements); + __ RecordWriteHelper(object, index_1, temp); + __ RecordWriteHelper(elements, index_2, temp); + + __ bind(&new_space); + // We are done. Drop elements from the stack, and return undefined. + __ addq(rsp, Immediate(3 * kPointerSize)); + __ LoadRoot(rax, Heap::kUndefinedValueRootIndex); + __ jmp(&done); + + __ bind(&slow_case); __ CallRuntime(Runtime::kSwapElements, 3); + + __ bind(&done); context()->Plug(rax); } @@ -3449,7 +3542,9 @@ void FullCodeGenerator::VisitCountOperation(CountOperation* expr) { case NAMED_PROPERTY: { __ Move(rcx, prop->key()->AsLiteral()->handle()); __ pop(rdx); - Handle ic(Builtins::builtin(Builtins::StoreIC_Initialize)); + Handle ic(Builtins::builtin( + is_strict() ? Builtins::StoreIC_Initialize_Strict + : Builtins::StoreIC_Initialize)); EmitCallIC(ic, RelocInfo::CODE_TARGET); PrepareForBailoutForId(expr->AssignmentId(), TOS_REG); if (expr->is_postfix()) { @@ -3464,7 +3559,9 @@ void FullCodeGenerator::VisitCountOperation(CountOperation* expr) { case KEYED_PROPERTY: { __ pop(rcx); __ pop(rdx); - Handle ic(Builtins::builtin(Builtins::KeyedStoreIC_Initialize)); + Handle ic(Builtins::builtin( + is_strict() ? Builtins::KeyedStoreIC_Initialize_Strict + : Builtins::KeyedStoreIC_Initialize)); EmitCallIC(ic, RelocInfo::CODE_TARGET); PrepareForBailoutForId(expr->AssignmentId(), TOS_REG); if (expr->is_postfix()) { diff --git a/deps/v8/src/x64/ic-x64.cc b/deps/v8/src/x64/ic-x64.cc index 55d837c6b6..b3243cf48a 100644 --- a/deps/v8/src/x64/ic-x64.cc +++ b/deps/v8/src/x64/ic-x64.cc @@ -766,7 +766,8 @@ void KeyedLoadIC::GenerateIndexedInterceptor(MacroAssembler* masm) { } -void KeyedStoreIC::GenerateGeneric(MacroAssembler* masm) { +void KeyedStoreIC::GenerateGeneric(MacroAssembler* masm, + StrictModeFlag strict_mode) { // ----------- S t a t e ------------- // -- rax : value // -- rcx : key @@ -813,7 +814,7 @@ void KeyedStoreIC::GenerateGeneric(MacroAssembler* masm) { __ bind(&slow); __ Integer32ToSmi(rcx, rcx); __ bind(&slow_with_tagged_index); - GenerateRuntimeSetProperty(masm); + GenerateRuntimeSetProperty(masm, strict_mode); // Never returns to here. // Check whether the elements is a pixel array. @@ -1474,7 +1475,7 @@ void KeyedLoadIC::GenerateRuntimeGetProperty(MacroAssembler* masm) { void StoreIC::GenerateMegamorphic(MacroAssembler* masm, - Code::ExtraICState extra_ic_state) { + StrictModeFlag strict_mode) { // ----------- S t a t e ------------- // -- rax : value // -- rcx : name @@ -1486,7 +1487,7 @@ void StoreIC::GenerateMegamorphic(MacroAssembler* masm, Code::Flags flags = Code::ComputeFlags(Code::STORE_IC, NOT_IN_LOOP, MONOMORPHIC, - extra_ic_state); + strict_mode); StubCache::GenerateProbe(masm, flags, rdx, rcx, rbx, no_reg); // Cache miss: Jump to runtime. @@ -1593,7 +1594,8 @@ void StoreIC::GenerateNormal(MacroAssembler* masm) { } -void StoreIC::GenerateGlobalProxy(MacroAssembler* masm) { +void StoreIC::GenerateGlobalProxy(MacroAssembler* masm, + StrictModeFlag strict_mode) { // ----------- S t a t e ------------- // -- rax : value // -- rcx : name @@ -1604,14 +1606,17 @@ void StoreIC::GenerateGlobalProxy(MacroAssembler* masm) { __ push(rdx); __ push(rcx); __ push(rax); - __ push(rbx); + __ Push(Smi::FromInt(NONE)); // PropertyAttributes + __ Push(Smi::FromInt(strict_mode)); + __ push(rbx); // return address // Do tail-call to runtime routine. - __ TailCallRuntime(Runtime::kSetProperty, 3, 1); + __ TailCallRuntime(Runtime::kSetProperty, 5, 1); } -void KeyedStoreIC::GenerateRuntimeSetProperty(MacroAssembler* masm) { +void KeyedStoreIC::GenerateRuntimeSetProperty(MacroAssembler* masm, + StrictModeFlag strict_mode) { // ----------- S t a t e ------------- // -- rax : value // -- rcx : key @@ -1623,10 +1628,12 @@ void KeyedStoreIC::GenerateRuntimeSetProperty(MacroAssembler* masm) { __ push(rdx); // receiver __ push(rcx); // key __ push(rax); // value + __ Push(Smi::FromInt(NONE)); // PropertyAttributes + __ Push(Smi::FromInt(strict_mode)); // Strict mode. __ push(rbx); // return address // Do tail-call to runtime routine. - __ TailCallRuntime(Runtime::kSetProperty, 3, 1); + __ TailCallRuntime(Runtime::kSetProperty, 5, 1); } diff --git a/deps/v8/src/x64/lithium-codegen-x64.cc b/deps/v8/src/x64/lithium-codegen-x64.cc index 90244f1c21..0ae8a003c9 100644 --- a/deps/v8/src/x64/lithium-codegen-x64.cc +++ b/deps/v8/src/x64/lithium-codegen-x64.cc @@ -77,6 +77,7 @@ bool LCodeGen::GenerateCode() { return GeneratePrologue() && GenerateBody() && GenerateDeferredCode() && + GenerateJumpTable() && GenerateSafepointTable(); } @@ -240,6 +241,16 @@ LInstruction* LCodeGen::GetNextInstruction() { } +bool LCodeGen::GenerateJumpTable() { + for (int i = 0; i < jump_table_.length(); i++) { + JumpTableEntry* info = jump_table_[i]; + __ bind(&(info->label_)); + __ Jump(info->address_, RelocInfo::RUNTIME_ENTRY); + } + return !is_aborted(); +} + + bool LCodeGen::GenerateDeferredCode() { ASSERT(is_generating()); for (int i = 0; !is_aborted() && i < deferred_.length(); i++) { @@ -512,10 +523,17 @@ void LCodeGen::DeoptimizeIf(Condition cc, LEnvironment* environment) { if (cc == no_condition) { __ Jump(entry, RelocInfo::RUNTIME_ENTRY); } else { - NearLabel done; - __ j(NegateCondition(cc), &done); - __ Jump(entry, RelocInfo::RUNTIME_ENTRY); - __ bind(&done); + JumpTableEntry* jump_info = NULL; + // We often have several deopts to the same entry, reuse the last + // jump entry if this is the case. + if (jump_table_.length() > 0 && + jump_table_[jump_table_.length() - 1]->address_ == entry) { + jump_info = jump_table_[jump_table_.length() - 1]; + } else { + jump_info = new JumpTableEntry(entry); + jump_table_.Add(jump_info); + } + __ j(cc, &jump_info->label_); } } @@ -527,7 +545,8 @@ void LCodeGen::PopulateDeoptimizationData(Handle code) { Handle data = Factory::NewDeoptimizationInputData(length, TENURED); - data->SetTranslationByteArray(*translations_.CreateByteArray()); + Handle translations = translations_.CreateByteArray(); + data->SetTranslationByteArray(*translations); data->SetInlinedFunctionCount(Smi::FromInt(inlined_function_count_)); Handle literals = @@ -686,13 +705,13 @@ void LCodeGen::DoCallStub(LCallStub* instr) { break; } case CodeStub::StringCharAt: { - // TODO(1116): Add StringCharAt stub to x64. - Abort("Unimplemented: %s", "StringCharAt Stub"); + StringCharAtStub stub; + CallCode(stub.GetCode(), RelocInfo::CODE_TARGET, instr); break; } case CodeStub::MathPow: { - // TODO(1115): Add MathPow stub to x64. - Abort("Unimplemented: %s", "MathPow Stub"); + MathPowStub stub; + CallCode(stub.GetCode(), RelocInfo::CODE_TARGET, instr); break; } case CodeStub::NumberToString: { @@ -1613,7 +1632,17 @@ void LCodeGen::DoHasInstanceTypeAndBranch(LHasInstanceTypeAndBranch* instr) { void LCodeGen::DoHasCachedArrayIndex(LHasCachedArrayIndex* instr) { - Abort("Unimplemented: %s", "DoHasCachedArrayIndex"); + Register input = ToRegister(instr->InputAt(0)); + Register result = ToRegister(instr->result()); + + ASSERT(instr->hydrogen()->value()->representation().IsTagged()); + __ LoadRoot(result, Heap::kTrueValueRootIndex); + __ testl(FieldOperand(input, String::kHashFieldOffset), + Immediate(String::kContainsCachedArrayIndexMask)); + NearLabel done; + __ j(not_zero, &done); + __ LoadRoot(result, Heap::kFalseValueRootIndex); + __ bind(&done); } @@ -1809,9 +1838,7 @@ void LCodeGen::DoDeferredLInstanceOfKnownGlobal(LInstanceOfKnownGlobal* instr) { __ push(ToRegister(instr->InputAt(0))); __ Push(instr->function()); __ movq(rsi, Operand(rbp, StandardFrameConstants::kContextOffset)); - __ Call(stub.GetCode(), RelocInfo::CODE_TARGET); - RecordSafepointWithRegisters( - instr->pointer_map(), 0, Safepoint::kNoDeoptimizationIndex); + CallCode(stub.GetCode(), RelocInfo::CODE_TARGET, instr); __ movq(kScratchRegister, rax); __ PopSafepointRegisters(); __ testq(kScratchRegister, kScratchRegister); @@ -2462,7 +2489,54 @@ void LCodeGen::DoMathPowHalf(LUnaryMathOperation* instr) { void LCodeGen::DoPower(LPower* instr) { - Abort("Unimplemented: %s", "DoPower"); + LOperand* left = instr->InputAt(0); + XMMRegister left_reg = ToDoubleRegister(left); + ASSERT(!left_reg.is(xmm1)); + LOperand* right = instr->InputAt(1); + XMMRegister result_reg = ToDoubleRegister(instr->result()); + Representation exponent_type = instr->hydrogen()->right()->representation(); + if (exponent_type.IsDouble()) { + __ PrepareCallCFunction(2); + // Move arguments to correct registers + __ movsd(xmm0, left_reg); + ASSERT(ToDoubleRegister(right).is(xmm1)); + __ CallCFunction(ExternalReference::power_double_double_function(), 2); + } else if (exponent_type.IsInteger32()) { + __ PrepareCallCFunction(2); + // Move arguments to correct registers: xmm0 and edi (not rdi). + // On Windows, the registers are xmm0 and edx. + __ movsd(xmm0, left_reg); +#ifdef _WIN64 + ASSERT(ToRegister(right).is(rdx)); +#else + ASSERT(ToRegister(right).is(rdi)); +#endif + __ CallCFunction(ExternalReference::power_double_int_function(), 2); + } else { + ASSERT(exponent_type.IsTagged()); + CpuFeatures::Scope scope(SSE2); + Register right_reg = ToRegister(right); + + Label non_smi, call; + __ JumpIfNotSmi(right_reg, &non_smi); + __ SmiToInteger32(right_reg, right_reg); + __ cvtlsi2sd(xmm1, right_reg); + __ jmp(&call); + + __ bind(&non_smi); + __ CmpObjectType(right_reg, HEAP_NUMBER_TYPE , kScratchRegister); + DeoptimizeIf(not_equal, instr->environment()); + __ movsd(xmm1, FieldOperand(right_reg, HeapNumber::kValueOffset)); + + __ bind(&call); + __ PrepareCallCFunction(2); + // Move arguments to correct registers xmm0 and xmm1. + __ movsd(xmm0, left_reg); + // Right argument is already in xmm1. + __ CallCFunction(ExternalReference::power_double_double_function(), 2); + } + // Return value is in xmm0. + __ movsd(result_reg, xmm0); } @@ -2623,7 +2697,9 @@ void LCodeGen::DoStoreNamedGeneric(LStoreNamedGeneric* instr) { ASSERT(ToRegister(instr->value()).is(rax)); __ Move(rcx, instr->hydrogen()->name()); - Handle ic(Builtins::builtin(Builtins::StoreIC_Initialize)); + Handle ic(Builtins::builtin( + info_->is_strict() ? Builtins::StoreIC_Initialize_Strict + : Builtins::StoreIC_Initialize)); CallCode(ic, RelocInfo::CODE_TARGET, instr); } @@ -2692,7 +2768,9 @@ void LCodeGen::DoStoreKeyedGeneric(LStoreKeyedGeneric* instr) { ASSERT(ToRegister(instr->key()).is(rcx)); ASSERT(ToRegister(instr->value()).is(rax)); - Handle ic(Builtins::builtin(Builtins::KeyedStoreIC_Initialize)); + Handle ic(Builtins::builtin( + info_->is_strict() ? Builtins::KeyedStoreIC_Initialize_Strict + : Builtins::KeyedStoreIC_Initialize)); CallCode(ic, RelocInfo::CODE_TARGET, instr); } diff --git a/deps/v8/src/x64/lithium-codegen-x64.h b/deps/v8/src/x64/lithium-codegen-x64.h index 1cac4e9df6..420556a585 100644 --- a/deps/v8/src/x64/lithium-codegen-x64.h +++ b/deps/v8/src/x64/lithium-codegen-x64.h @@ -53,6 +53,7 @@ class LCodeGen BASE_EMBEDDED { current_instruction_(-1), instructions_(chunk->instructions()), deoptimizations_(4), + jump_table_(4), deoptimization_literals_(8), inlined_function_count_(0), scope_(chunk->graph()->info()->scope()), @@ -147,6 +148,7 @@ class LCodeGen BASE_EMBEDDED { bool GeneratePrologue(); bool GenerateBody(); bool GenerateDeferredCode(); + bool GenerateJumpTable(); bool GenerateSafepointTable(); void CallCode(Handle code, @@ -234,6 +236,14 @@ class LCodeGen BASE_EMBEDDED { // Emits code for pushing a constant operand. void EmitPushConstantOperand(LOperand* operand); + struct JumpTableEntry { + inline JumpTableEntry(Address address) + : label_(), + address_(address) { } + Label label_; + Address address_; + }; + LChunk* const chunk_; MacroAssembler* const masm_; CompilationInfo* const info_; @@ -242,6 +252,7 @@ class LCodeGen BASE_EMBEDDED { int current_instruction_; const ZoneList* instructions_; ZoneList deoptimizations_; + ZoneList jump_table_; ZoneList > deoptimization_literals_; int inlined_function_count_; Scope* const scope_; diff --git a/deps/v8/src/x64/lithium-x64.cc b/deps/v8/src/x64/lithium-x64.cc index 8db1ba9065..18b38e2481 100644 --- a/deps/v8/src/x64/lithium-x64.cc +++ b/deps/v8/src/x64/lithium-x64.cc @@ -1158,9 +1158,8 @@ LInstruction* LChunkBuilder::DoInstanceOf(HInstanceOf* instr) { LInstruction* LChunkBuilder::DoInstanceOfKnownGlobal( HInstanceOfKnownGlobal* instr) { LInstanceOfKnownGlobal* result = - new LInstanceOfKnownGlobal(UseRegisterAtStart(instr->value())); - MarkAsSaveDoubles(result); - return AssignEnvironment(AssignPointerMap(DefineFixed(result, rax))); + new LInstanceOfKnownGlobal(UseFixed(instr->value(), rax)); + return MarkAsCall(DefineFixed(result, rax), instr); } @@ -1436,8 +1435,22 @@ LInstruction* LChunkBuilder::DoAdd(HAdd* instr) { LInstruction* LChunkBuilder::DoPower(HPower* instr) { - Abort("Unimplemented: %s", "DoPower"); - return NULL; + 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) : +#ifdef _WIN64 + UseFixed(instr->right(), rdx); +#else + UseFixed(instr->right(), rdi); +#endif + LPower* result = new LPower(left, right); + return MarkAsCall(DefineFixedDouble(result, xmm1), instr, + CAN_DEOPTIMIZE_EAGERLY); } @@ -1518,8 +1531,9 @@ LInstruction* LChunkBuilder::DoGetCachedArrayIndex( LInstruction* LChunkBuilder::DoHasCachedArrayIndex( HHasCachedArrayIndex* instr) { - Abort("Unimplemented: %s", "DoHasCachedArrayIndex"); - return NULL; + ASSERT(instr->value()->representation().IsTagged()); + LOperand* value = UseRegister(instr->value()); + return DefineAsRegister(new LHasCachedArrayIndex(value)); } diff --git a/deps/v8/src/x64/stub-cache-x64.cc b/deps/v8/src/x64/stub-cache-x64.cc index 774de71844..109985c726 100644 --- a/deps/v8/src/x64/stub-cache-x64.cc +++ b/deps/v8/src/x64/stub-cache-x64.cc @@ -2408,12 +2408,13 @@ MaybeObject* StoreStubCompiler::CompileStoreInterceptor(JSObject* receiver, __ push(rdx); // receiver __ push(rcx); // name __ push(rax); // value + __ Push(Smi::FromInt(strict_mode_)); __ push(rbx); // restore return address // Do tail-call to the runtime system. ExternalReference store_ic_property = ExternalReference(IC_Utility(IC::kStoreInterceptorProperty)); - __ TailCallExternalReference(store_ic_property, 3, 1); + __ TailCallExternalReference(store_ic_property, 4, 1); // Handle store cache miss. __ bind(&miss); @@ -3490,10 +3491,13 @@ MaybeObject* ExternalArrayStubCompiler::CompileKeyedStoreStub( __ push(rdx); // receiver __ push(rcx); // key __ push(rax); // value + __ Push(Smi::FromInt(NONE)); // PropertyAttributes + __ Push(Smi::FromInt( + Code::ExtractExtraICStateFromFlags(flags) & kStrictMode)); __ push(rbx); // return address // Do tail-call to runtime routine. - __ TailCallRuntime(Runtime::kSetProperty, 3, 1); + __ TailCallRuntime(Runtime::kSetProperty, 5, 1); return GetCode(flags); } diff --git a/deps/v8/src/x64/virtual-frame-x64.cc b/deps/v8/src/x64/virtual-frame-x64.cc index ea115f28a2..c4d7e65663 100644 --- a/deps/v8/src/x64/virtual-frame-x64.cc +++ b/deps/v8/src/x64/virtual-frame-x64.cc @@ -274,6 +274,24 @@ void VirtualFrame::Push(Expression* expr) { } +void VirtualFrame::Push(Handle value) { + if (ConstantPoolOverflowed()) { + Result temp = cgen()->allocator()->Allocate(); + ASSERT(temp.is_valid()); + if (value->IsSmi()) { + __ Move(temp.reg(), Smi::cast(*value)); + } else { + __ movq(temp.reg(), value, RelocInfo::EMBEDDED_OBJECT); + } + Push(&temp); + } else { + FrameElement element = + FrameElement::ConstantElement(value, FrameElement::NOT_SYNCED); + elements_.Add(element); + } +} + + void VirtualFrame::Drop(int count) { ASSERT(count >= 0); ASSERT(height() >= count); @@ -1124,9 +1142,9 @@ Result VirtualFrame::CallStoreIC(Handle name, StrictModeFlag strict_mode) { // Value and (if not contextual) receiver are on top of the frame. // The IC expects name in rcx, value in rax, and receiver in rdx. - Handle ic(Builtins::builtin(strict_mode == kStrictMode - ? Builtins::StoreIC_Initialize_Strict - : Builtins::StoreIC_Initialize)); + Handle ic(Builtins::builtin( + (strict_mode == kStrictMode) ? Builtins::StoreIC_Initialize_Strict + : Builtins::StoreIC_Initialize)); Result value = Pop(); RelocInfo::Mode mode; if (is_contextual) { @@ -1146,7 +1164,7 @@ Result VirtualFrame::CallStoreIC(Handle name, } -Result VirtualFrame::CallKeyedStoreIC() { +Result VirtualFrame::CallKeyedStoreIC(StrictModeFlag strict_mode) { // Value, key, and receiver are on the top of the frame. The IC // expects value in rax, key in rcx, and receiver in rdx. Result value = Pop(); @@ -1190,7 +1208,9 @@ Result VirtualFrame::CallKeyedStoreIC() { receiver.Unuse(); } - Handle ic(Builtins::builtin(Builtins::KeyedStoreIC_Initialize)); + Handle ic(Builtins::builtin( + (strict_mode == kStrictMode) ? Builtins::KeyedStoreIC_Initialize_Strict + : Builtins::KeyedStoreIC_Initialize)); return RawCallCodeObject(ic, RelocInfo::CODE_TARGET); } diff --git a/deps/v8/src/x64/virtual-frame-x64.h b/deps/v8/src/x64/virtual-frame-x64.h index 824743d1d0..7396db17f6 100644 --- a/deps/v8/src/x64/virtual-frame-x64.h +++ b/deps/v8/src/x64/virtual-frame-x64.h @@ -343,7 +343,7 @@ class VirtualFrame : public ZoneObject { // Call keyed store IC. Value, key, and receiver are found on top // of the frame. All three are dropped. - Result CallKeyedStoreIC(); + Result CallKeyedStoreIC(StrictModeFlag strict_mode); // Call call IC. Function name, arguments, and receiver are found on top // of the frame and dropped by the call. @@ -403,8 +403,8 @@ class VirtualFrame : public ZoneObject { inline bool ConstantPoolOverflowed(); // Push an element on the virtual frame. + void Push(Handle value); inline void Push(Register reg, TypeInfo info = TypeInfo::Unknown()); - inline void Push(Handle value); inline void Push(Smi* value); // Pushing a result invalidates it (its contents become owned by the diff --git a/deps/v8/test/cctest/test-api.cc b/deps/v8/test/cctest/test-api.cc index b3c52f1f1f..f450a34597 100644 --- a/deps/v8/test/cctest/test-api.cc +++ b/deps/v8/test/cctest/test-api.cc @@ -10091,10 +10091,11 @@ class RegExpStringModificationTest { // Inject the input as a global variable. i::Handle input_name = i::Factory::NewStringFromAscii(i::Vector("input", 5)); - i::Top::global_context()->global()->SetProperty(*input_name, - *input_, - NONE)->ToObjectChecked(); - + i::Top::global_context()->global()->SetProperty( + *input_name, + *input_, + NONE, + i::kNonStrictMode)->ToObjectChecked(); MorphThread morph_thread(this); morph_thread.Start(); diff --git a/deps/v8/test/cctest/test-compiler.cc b/deps/v8/test/cctest/test-compiler.cc index b424b7f9e1..9f21b78dd6 100644 --- a/deps/v8/test/cctest/test-compiler.cc +++ b/deps/v8/test/cctest/test-compiler.cc @@ -108,7 +108,7 @@ static void SetGlobalProperty(const char* name, Object* value) { Handle object(value); Handle symbol = Factory::LookupAsciiSymbol(name); Handle global(Top::context()->global()); - SetProperty(global, symbol, object, NONE); + SetProperty(global, symbol, object, NONE, kNonStrictMode); } diff --git a/deps/v8/test/cctest/test-debug.cc b/deps/v8/test/cctest/test-debug.cc index 441aae63c6..7245e54be7 100644 --- a/deps/v8/test/cctest/test-debug.cc +++ b/deps/v8/test/cctest/test-debug.cc @@ -153,7 +153,8 @@ class DebugLocalContext { Handle debug_string = v8::internal::Factory::LookupAsciiSymbol("debug"); SetProperty(global, debug_string, - Handle(Debug::debug_context()->global_proxy()), DONT_ENUM); + Handle(Debug::debug_context()->global_proxy()), DONT_ENUM, + ::v8::internal::kNonStrictMode); } private: v8::Persistent context_; diff --git a/deps/v8/test/cctest/test-heap.cc b/deps/v8/test/cctest/test-heap.cc index a23ee171f2..9cce01eaea 100644 --- a/deps/v8/test/cctest/test-heap.cc +++ b/deps/v8/test/cctest/test-heap.cc @@ -212,13 +212,14 @@ TEST(GarbageCollection) { Handle initial_map = Factory::NewMap(JS_OBJECT_TYPE, JSObject::kHeaderSize); function->set_initial_map(*initial_map); - Top::context()->global()->SetProperty(*name, - *function, - NONE)->ToObjectChecked(); + Top::context()->global()->SetProperty( + *name, *function, NONE, kNonStrictMode)->ToObjectChecked(); // Allocate an object. Unrooted after leaving the scope. Handle obj = Factory::NewJSObject(function); - obj->SetProperty(*prop_name, Smi::FromInt(23), NONE)->ToObjectChecked(); - obj->SetProperty(*prop_namex, Smi::FromInt(24), NONE)->ToObjectChecked(); + obj->SetProperty( + *prop_name, Smi::FromInt(23), NONE, kNonStrictMode)->ToObjectChecked(); + obj->SetProperty( + *prop_namex, Smi::FromInt(24), NONE, kNonStrictMode)->ToObjectChecked(); CHECK_EQ(Smi::FromInt(23), obj->GetProperty(*prop_name)); CHECK_EQ(Smi::FromInt(24), obj->GetProperty(*prop_namex)); @@ -238,10 +239,10 @@ TEST(GarbageCollection) { HandleScope inner_scope; // Allocate another object, make it reachable from global. Handle obj = Factory::NewJSObject(function); - Top::context()->global()->SetProperty(*obj_name, - *obj, - NONE)->ToObjectChecked(); - obj->SetProperty(*prop_name, Smi::FromInt(23), NONE)->ToObjectChecked(); + Top::context()->global()->SetProperty( + *obj_name, *obj, NONE, kNonStrictMode)->ToObjectChecked(); + obj->SetProperty( + *prop_name, Smi::FromInt(23), NONE, kNonStrictMode)->ToObjectChecked(); } // After gc, it should survive. @@ -540,12 +541,12 @@ TEST(FunctionAllocation) { Handle prop_name = Factory::LookupAsciiSymbol("theSlot"); Handle obj = Factory::NewJSObject(function); - obj->SetProperty(*prop_name, Smi::FromInt(23), NONE)->ToObjectChecked(); + obj->SetProperty( + *prop_name, Smi::FromInt(23), NONE, kNonStrictMode)->ToObjectChecked(); CHECK_EQ(Smi::FromInt(23), obj->GetProperty(*prop_name)); // Check that we can add properties to function objects. - function->SetProperty(*prop_name, - Smi::FromInt(24), - NONE)->ToObjectChecked(); + function->SetProperty( + *prop_name, Smi::FromInt(24), NONE, kNonStrictMode)->ToObjectChecked(); CHECK_EQ(Smi::FromInt(24), function->GetProperty(*prop_name)); } @@ -567,7 +568,8 @@ TEST(ObjectProperties) { CHECK(!obj->HasLocalProperty(*first)); // add first - obj->SetProperty(*first, Smi::FromInt(1), NONE)->ToObjectChecked(); + obj->SetProperty( + *first, Smi::FromInt(1), NONE, kNonStrictMode)->ToObjectChecked(); CHECK(obj->HasLocalProperty(*first)); // delete first @@ -575,8 +577,10 @@ TEST(ObjectProperties) { CHECK(!obj->HasLocalProperty(*first)); // add first and then second - obj->SetProperty(*first, Smi::FromInt(1), NONE)->ToObjectChecked(); - obj->SetProperty(*second, Smi::FromInt(2), NONE)->ToObjectChecked(); + obj->SetProperty( + *first, Smi::FromInt(1), NONE, kNonStrictMode)->ToObjectChecked(); + obj->SetProperty( + *second, Smi::FromInt(2), NONE, kNonStrictMode)->ToObjectChecked(); CHECK(obj->HasLocalProperty(*first)); CHECK(obj->HasLocalProperty(*second)); @@ -588,8 +592,10 @@ TEST(ObjectProperties) { CHECK(!obj->HasLocalProperty(*second)); // add first and then second - obj->SetProperty(*first, Smi::FromInt(1), NONE)->ToObjectChecked(); - obj->SetProperty(*second, Smi::FromInt(2), NONE)->ToObjectChecked(); + obj->SetProperty( + *first, Smi::FromInt(1), NONE, kNonStrictMode)->ToObjectChecked(); + obj->SetProperty( + *second, Smi::FromInt(2), NONE, kNonStrictMode)->ToObjectChecked(); CHECK(obj->HasLocalProperty(*first)); CHECK(obj->HasLocalProperty(*second)); @@ -603,14 +609,16 @@ TEST(ObjectProperties) { // check string and symbol match static const char* string1 = "fisk"; Handle s1 = Factory::NewStringFromAscii(CStrVector(string1)); - obj->SetProperty(*s1, Smi::FromInt(1), NONE)->ToObjectChecked(); + obj->SetProperty( + *s1, Smi::FromInt(1), NONE, kNonStrictMode)->ToObjectChecked(); Handle s1_symbol = Factory::LookupAsciiSymbol(string1); CHECK(obj->HasLocalProperty(*s1_symbol)); // check symbol and string match static const char* string2 = "fugl"; Handle s2_symbol = Factory::LookupAsciiSymbol(string2); - obj->SetProperty(*s2_symbol, Smi::FromInt(1), NONE)->ToObjectChecked(); + obj->SetProperty( + *s2_symbol, Smi::FromInt(1), NONE, kNonStrictMode)->ToObjectChecked(); Handle s2 = Factory::NewStringFromAscii(CStrVector(string2)); CHECK(obj->HasLocalProperty(*s2)); } @@ -631,7 +639,8 @@ TEST(JSObjectMaps) { Handle obj = Factory::NewJSObject(function); // Set a propery - obj->SetProperty(*prop_name, Smi::FromInt(23), NONE)->ToObjectChecked(); + obj->SetProperty( + *prop_name, Smi::FromInt(23), NONE, kNonStrictMode)->ToObjectChecked(); CHECK_EQ(Smi::FromInt(23), obj->GetProperty(*prop_name)); // Check the map has changed @@ -698,8 +707,10 @@ TEST(JSObjectCopy) { Handle first = Factory::LookupAsciiSymbol("first"); Handle second = Factory::LookupAsciiSymbol("second"); - obj->SetProperty(*first, Smi::FromInt(1), NONE)->ToObjectChecked(); - obj->SetProperty(*second, Smi::FromInt(2), NONE)->ToObjectChecked(); + obj->SetProperty( + *first, Smi::FromInt(1), NONE, kNonStrictMode)->ToObjectChecked(); + obj->SetProperty( + *second, Smi::FromInt(2), NONE, kNonStrictMode)->ToObjectChecked(); Object* ok = obj->SetElement(0, *first)->ToObjectChecked(); @@ -716,8 +727,10 @@ TEST(JSObjectCopy) { CHECK_EQ(obj->GetProperty(*second), clone->GetProperty(*second)); // Flip the values. - clone->SetProperty(*first, Smi::FromInt(2), NONE)->ToObjectChecked(); - clone->SetProperty(*second, Smi::FromInt(1), NONE)->ToObjectChecked(); + clone->SetProperty( + *first, Smi::FromInt(2), NONE, kNonStrictMode)->ToObjectChecked(); + clone->SetProperty( + *second, Smi::FromInt(1), NONE, kNonStrictMode)->ToObjectChecked(); ok = clone->SetElement(0, *second)->ToObjectChecked(); ok = clone->SetElement(1, *first)->ToObjectChecked(); diff --git a/deps/v8/test/cctest/test-mark-compact.cc b/deps/v8/test/cctest/test-mark-compact.cc index 86f105f2c9..3e3175e735 100644 --- a/deps/v8/test/cctest/test-mark-compact.cc +++ b/deps/v8/test/cctest/test-mark-compact.cc @@ -189,7 +189,8 @@ TEST(MarkCompactCollector) { function->set_initial_map(initial_map); Top::context()->global()->SetProperty(func_name, function, - NONE)->ToObjectChecked(); + NONE, + kNonStrictMode)->ToObjectChecked(); JSObject* obj = JSObject::cast(Heap::AllocateJSObject(function)->ToObjectChecked()); @@ -208,10 +209,14 @@ TEST(MarkCompactCollector) { String::cast(Heap::LookupAsciiSymbol("theObject")->ToObjectChecked()); Top::context()->global()->SetProperty(obj_name, obj, - NONE)->ToObjectChecked(); + NONE, + kNonStrictMode)->ToObjectChecked(); String* prop_name = String::cast(Heap::LookupAsciiSymbol("theSlot")->ToObjectChecked()); - obj->SetProperty(prop_name, Smi::FromInt(23), NONE)->ToObjectChecked(); + obj->SetProperty(prop_name, + Smi::FromInt(23), + NONE, + kNonStrictMode)->ToObjectChecked(); Heap::CollectGarbage(OLD_POINTER_SPACE); diff --git a/deps/v8/test/es5conform/es5conform.status b/deps/v8/test/es5conform/es5conform.status index e021fc54dc..d6f7caf581 100644 --- a/deps/v8/test/es5conform/es5conform.status +++ b/deps/v8/test/es5conform/es5conform.status @@ -269,72 +269,6 @@ chapter11/11.13/11.13.1/11.13.1-4-3-s: FAIL # in strict mode (Global.length) chapter11/11.13/11.13.1/11.13.1-4-4-s: FAIL # simple assignment throws TypeError if LeftHandSide is a readonly property -# in strict mode (Object.length) -chapter11/11.13/11.13.1/11.13.1-4-5-s: FAIL -# simple assignment throws TypeError if LeftHandSide is a readonly property -# in strict mode (Function.length) -chapter11/11.13/11.13.1/11.13.1-4-6-s: FAIL -# simple assignment throws TypeError if LeftHandSide is a readonly property -# in strict mode (Array.length) -chapter11/11.13/11.13.1/11.13.1-4-7-s: FAIL -# simple assignment throws TypeError if LeftHandSide is a readonly property -# in strict mode (String.length) -chapter11/11.13/11.13.1/11.13.1-4-8-s: FAIL -# simple assignment throws TypeError if LeftHandSide is a readonly property -# in strict mode (Boolean.length) -chapter11/11.13/11.13.1/11.13.1-4-9-s: FAIL -# simple assignment throws TypeError if LeftHandSide is a readonly property -# in strict mode (Number.length) -chapter11/11.13/11.13.1/11.13.1-4-10-s: FAIL -# simple assignment throws TypeError if LeftHandSide is a readonly property -# in strict mode (Date.length) -chapter11/11.13/11.13.1/11.13.1-4-11-s: FAIL -# simple assignment throws TypeError if LeftHandSide is a readonly property -# in strict mode (RegExp.length) -chapter11/11.13/11.13.1/11.13.1-4-12-s: FAIL -# simple assignment throws TypeError if LeftHandSide is a readonly property -# in strict mode (Error.length) -chapter11/11.13/11.13.1/11.13.1-4-13-s: FAIL -# simple assignment throws TypeError if LeftHandSide is a readonly property -# in strict mode (Number.MAX_VALUE) -chapter11/11.13/11.13.1/11.13.1-4-14-s: FAIL -# simple assignment throws TypeError if LeftHandSide is a readonly property -# in strict mode (Number.MIN_VALUE) -chapter11/11.13/11.13.1/11.13.1-4-15-s: FAIL -# simple assignment throws TypeError if LeftHandSide is a readonly property -# in strict mode (Number.NaN) -chapter11/11.13/11.13.1/11.13.1-4-16-s: FAIL -# simple assignment throws TypeError if LeftHandSide is a readonly property -# in strict mode (Number.NEGATIVE_INFINITY) -chapter11/11.13/11.13.1/11.13.1-4-17-s: FAIL -# simple assignment throws TypeError if LeftHandSide is a readonly property -# in strict mode (Number.POSITIVE_INFINITY) -chapter11/11.13/11.13.1/11.13.1-4-18-s: FAIL -# simple assignment throws TypeError if LeftHandSide is a readonly property -# in strict mode (Math.E) -chapter11/11.13/11.13.1/11.13.1-4-19-s: FAIL -# simple assignment throws TypeError if LeftHandSide is a readonly property -# in strict mode (Math.LN10) -chapter11/11.13/11.13.1/11.13.1-4-20-s: FAIL -# simple assignment throws TypeError if LeftHandSide is a readonly property -# in strict mode (Math.LN2) -chapter11/11.13/11.13.1/11.13.1-4-21-s: FAIL -# simple assignment throws TypeError if LeftHandSide is a readonly property -# in strict mode (Math.LOG2E) -chapter11/11.13/11.13.1/11.13.1-4-22-s: FAIL -# simple assignment throws TypeError if LeftHandSide is a readonly property -# in strict mode (Math.LOG10E) -chapter11/11.13/11.13.1/11.13.1-4-23-s: FAIL -# simple assignment throws TypeError if LeftHandSide is a readonly property -# in strict mode (Math.PI) -chapter11/11.13/11.13.1/11.13.1-4-24-s: FAIL -# simple assignment throws TypeError if LeftHandSide is a readonly property -# in strict mode (Math.SQRT1_2) -chapter11/11.13/11.13.1/11.13.1-4-25-s: FAIL -# simple assignment throws TypeError if LeftHandSide is a readonly property -# in strict mode (Math.SQRT2) -chapter11/11.13/11.13.1/11.13.1-4-26-s: FAIL -# simple assignment throws TypeError if LeftHandSide is a readonly property # in strict mode (Global.undefined) chapter11/11.13/11.13.1/11.13.1-4-27-s: FAIL diff --git a/deps/v8/test/mjsunit/array-join.js b/deps/v8/test/mjsunit/array-join.js index c66e4626e7..ddd14967f0 100644 --- a/deps/v8/test/mjsunit/array-join.js +++ b/deps/v8/test/mjsunit/array-join.js @@ -27,19 +27,44 @@ // Test that array join calls toString on subarrays. var a = [[1,2],3,4,[5,6]]; +assertEquals('1,2345,6', a.join('')); assertEquals('1,2*3*4*5,6', a.join('*')); +assertEquals('1,2**3**4**5,6', a.join('**')); +assertEquals('1,2****3****4****5,6', a.join('****')); +assertEquals('1,2********3********4********5,6', a.join('********')); +assertEquals('1,2**********3**********4**********5,6', a.join('**********')); // Create a cycle. a.push(a); +assertEquals('1,2345,6', a.join('')); assertEquals('1,2*3*4*5,6*', a.join('*')); +assertEquals('1,2**3**4**5,6**', a.join('**')); +assertEquals('1,2****3****4****5,6****', a.join('****')); +assertEquals('1,2********3********4********5,6********', a.join('********')); +assertEquals('1,2**********3**********4**********5,6**********', a.join('**********')); // Replace array.prototype.toString. Array.prototype.toString = function() { return "array"; } +assertEquals('array34arrayarray', a.join('')); assertEquals('array*3*4*array*array', a.join('*')); +assertEquals('array**3**4**array**array', a.join('**')); +assertEquals('array****3****4****array****array', a.join('****')); +assertEquals('array********3********4********array********array', a.join('********')); +assertEquals('array**********3**********4**********array**********array', a.join('**********')); Array.prototype.toString = function() { throw 42; } +assertThrows("a.join('')"); assertThrows("a.join('*')"); +assertThrows("a.join('**')"); +assertThrows("a.join('****')"); +assertThrows("a.join('********')"); +assertThrows("a.join('**********')"); Array.prototype.toString = function() { return "array"; } +assertEquals('array34arrayarray', a.join('')); assertEquals('array*3*4*array*array', a.join('*')); +assertEquals('array**3**4**array**array', a.join('**')); +assertEquals('array****3****4****array****array', a.join('****')); +assertEquals('array********3********4********array********array', a.join('********')); +assertEquals('array**********3**********4**********array**********array', a.join('**********')); diff --git a/deps/v8/test/mjsunit/mjsunit.status b/deps/v8/test/mjsunit/mjsunit.status index c10281fc19..8f042ced7a 100644 --- a/deps/v8/test/mjsunit/mjsunit.status +++ b/deps/v8/test/mjsunit/mjsunit.status @@ -110,18 +110,6 @@ regress/regress-3247124: SKIP # should be platform-independent. regress/regress-1132: SKIP -############################################################################## -[ $arch == arm && $crankshaft ] - -# BUG (1094) -regress/regress-deopt-gc: SKIP - -############################################################################## -[ $arch == x64 && $crankshaft ] - -# BUG (1094) -regress/regress-deopt-gc: SKIP - ############################################################################## [ $arch == mips ] diff --git a/deps/v8/test/mjsunit/override-eval-with-non-function.js b/deps/v8/test/mjsunit/override-eval-with-non-function.js new file mode 100644 index 0000000000..aa93b25305 --- /dev/null +++ b/deps/v8/test/mjsunit/override-eval-with-non-function.js @@ -0,0 +1,36 @@ +// Copyright 2011 the V8 project authors. All rights reserved. +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following +// disclaimer in the documentation and/or other materials provided +// with the distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +// When 'eval' is overridden with a non-function object we should +// check whether the object is callable. + +function test() { + eval = /foo/; + assertEquals("foo", eval("foobar")); +} + +test(); diff --git a/deps/v8/test/mjsunit/regress/regress-1207.js b/deps/v8/test/mjsunit/regress/regress-1207.js new file mode 100644 index 0000000000..102178abba --- /dev/null +++ b/deps/v8/test/mjsunit/regress/regress-1207.js @@ -0,0 +1,35 @@ +// Copyright 2011 the V8 project authors. All rights reserved. +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following +// disclaimer in the documentation and/or other materials provided +// with the distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +// Test throwing an exception from instanceof. +try { +var object = { }; +function fib(n) { + var f0 = (object instanceof encodeURI)('#2: var x = 1; x <= 1 === true'), f1 = 1; +} +fib(75); +} catch (o) { } diff --git a/deps/v8/test/mjsunit/regress/regress-1209.js b/deps/v8/test/mjsunit/regress/regress-1209.js new file mode 100644 index 0000000000..c017fb5107 --- /dev/null +++ b/deps/v8/test/mjsunit/regress/regress-1209.js @@ -0,0 +1,34 @@ +// Copyright 2011 the V8 project authors. All rights reserved. +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following +// disclaimer in the documentation and/or other materials provided +// with the distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +function crashMe(n) { + var nasty = []; + while (n--) + nasty.push("a" + 0); + return Function.apply(null, nasty); +} +crashMe(64 + 1).length; diff --git a/deps/v8/test/mjsunit/regress/regress-1210.js b/deps/v8/test/mjsunit/regress/regress-1210.js new file mode 100644 index 0000000000..9c708a5805 --- /dev/null +++ b/deps/v8/test/mjsunit/regress/regress-1210.js @@ -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. + +// Deoptimization of the key expression in an arguments access should see +// the arguments object as the value of the receiver. + +var a = 0; + +function observe(x, y) { return x; } + +function side_effect(x) { a = x; } + +function test() { + // We will trigger deoptimization of 'a + 0' which should bail out to + // immediately after the call to 'side_effect' (i.e., still in the key + // subexpression of the arguments access). + return observe(a, arguments[side_effect(a), a + 0]); +} + +// Run enough to optimize assuming global 'a' is a smi. +for (var i = 0; i < 1000000; ++i) test(0); + +a = "hello"; +test(0); diff --git a/deps/v8/test/mjsunit/regress/regress-1213.js b/deps/v8/test/mjsunit/regress/regress-1213.js new file mode 100644 index 0000000000..d66e3cefc4 --- /dev/null +++ b/deps/v8/test/mjsunit/regress/regress-1213.js @@ -0,0 +1,43 @@ +// Copyright 2011 the V8 project authors. All rights reserved. +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following +// disclaimer in the documentation and/or other materials provided +// with the distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +// Test that we do not allow overwriting a global property with a +// redeclaration that makes the property configurable (and hence +// deletable). + +var x = 0; + +function TestGlobal() { + for (var i = 0; i < 2; i++) { + x = x + 1; + } + this.eval('function x() {};'); + delete this['x']; +} + +TestGlobal(); +TestGlobal(); diff --git a/deps/v8/test/mjsunit/regress/regress-1218.js b/deps/v8/test/mjsunit/regress/regress-1218.js new file mode 100644 index 0000000000..dd036edbd7 --- /dev/null +++ b/deps/v8/test/mjsunit/regress/regress-1218.js @@ -0,0 +1,29 @@ +// Copyright 2011 the V8 project authors. All rights reserved. +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following +// disclaimer in the documentation and/or other materials provided +// with the distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +// Builtin functions should not have prototype objects. +assertFalse(Error.prototype.toString.hasOwnProperty("prototype")); diff --git a/deps/v8/test/mjsunit/strict-mode.js b/deps/v8/test/mjsunit/strict-mode.js index ab3e535ec3..4071232ebf 100644 --- a/deps/v8/test/mjsunit/strict-mode.js +++ b/deps/v8/test/mjsunit/strict-mode.js @@ -280,6 +280,19 @@ CheckStrictMode("function strict() { print(--arguments); }", SyntaxError); CheckStrictMode("function strict() { var x = --eval; }", SyntaxError); CheckStrictMode("function strict() { var x = --arguments; }", SyntaxError); +// Use of const in strict mode is disallowed in anticipation of ES Harmony. +CheckStrictMode("const x = 0;", SyntaxError); +CheckStrictMode("for (const x = 0; false;) {}", SyntaxError); +CheckStrictMode("function strict() { const x = 0; }", SyntaxError); + +// Strict mode only allows functions in SourceElements +CheckStrictMode("if (true) { function invalid() {} }", SyntaxError); +CheckStrictMode("for (;false;) { function invalid() {} }", SyntaxError); +CheckStrictMode("{ function invalid() {} }", SyntaxError); +CheckStrictMode("try { function invalid() {} } catch(e) {}", SyntaxError); +CheckStrictMode("try { } catch(e) { function invalid() {} }", SyntaxError); +CheckStrictMode("function outer() {{ function invalid() {} }}", SyntaxError); + // Delete of an unqualified identifier CheckStrictMode("delete unqualified;", SyntaxError); CheckStrictMode("function strict() { delete unqualified; }", SyntaxError); @@ -700,3 +713,118 @@ repeat(10, function() { testAssignToUndefined(false); }); cleanup(Boolean); } })(); + + +(function ObjectEnvironment() { + var o = {}; + Object.defineProperty(o, "foo", { value: "FOO", writable: false }); + assertThrows( + function () { + with (o) { + (function() { + "use strict"; + foo = "Hello"; + })(); + } + }, + TypeError); +})(); + + +(function TestSetPropertyWithoutSetter() { + var o = { get foo() { return "Yey"; } }; + assertThrows( + function broken() { + "use strict"; + o.foo = (0xBADBAD00 >> 1); + }, + TypeError); +})(); + + +(function TestSetPropertyNonConfigurable() { + var frozen = Object.freeze({}); + var sealed = Object.seal({}); + + function strict(o) { + "use strict"; + o.property = "value"; + } + + assertThrows(function() { strict(frozen); }, TypeError); + assertThrows(function() { strict(sealed); }, TypeError); +})(); + + +(function TestAssignmentToReadOnlyProperty() { + "use strict"; + + var o = {}; + Object.defineProperty(o, "property", { value: 7 }); + + assertThrows(function() { o.property = "new value"; }, TypeError); + assertThrows(function() { o.property += 10; }, TypeError); + assertThrows(function() { o.property -= 10; }, TypeError); + assertThrows(function() { o.property *= 10; }, TypeError); + assertThrows(function() { o.property /= 10; }, TypeError); + assertThrows(function() { o.property++; }, TypeError); + assertThrows(function() { o.property--; }, TypeError); + assertThrows(function() { ++o.property; }, TypeError); + assertThrows(function() { --o.property; }, TypeError); + + var name = "prop" + "erty"; // to avoid symbol path. + assertThrows(function() { o[name] = "new value"; }, TypeError); + assertThrows(function() { o[name] += 10; }, TypeError); + assertThrows(function() { o[name] -= 10; }, TypeError); + assertThrows(function() { o[name] *= 10; }, TypeError); + assertThrows(function() { o[name] /= 10; }, TypeError); + assertThrows(function() { o[name]++; }, TypeError); + assertThrows(function() { o[name]--; }, TypeError); + assertThrows(function() { ++o[name]; }, TypeError); + assertThrows(function() { --o[name]; }, TypeError); + + assertEquals(o.property, 7); +})(); + + +(function TestAssignmentToReadOnlyLoop() { + var name = "prop" + "erty"; // to avoid symbol path. + var o = {}; + Object.defineProperty(o, "property", { value: 7 }); + + function strict(o, name) { + "use strict"; + o[name] = "new value"; + } + + for (var i = 0; i < 10; i ++) { + try { + strict(o, name); + assertUnreachable(); + } catch(e) { + assertInstanceof(e, TypeError); + } + } +})(); + + +// Specialized KeyedStoreIC experiencing miss. +(function testKeyedStoreICStrict() { + var o = [9,8,7,6,5,4,3,2,1]; + + function test(o, i, v) { + "use strict"; + o[i] = v; + } + + for (var i = 0; i < 10; i ++) { + test(o, 5, 17); // start specialized for smi indices + assertEquals(o[5], 17); + test(o, "a", 19); + assertEquals(o["a"], 19); + test(o, "5", 29); + assertEquals(o[5], 29); + test(o, 100000, 31); + assertEquals(o[100000], 31); + } +})();