Upgrade V8 to 3.8.1
This commit is contained in:
parent
07b1997388
commit
21e7292ea0
1
deps/v8/AUTHORS
vendored
1
deps/v8/AUTHORS
vendored
@ -42,6 +42,7 @@ Rodolph Perfetta <rodolph.perfetta@arm.com>
|
|||||||
Ryan Dahl <coldredlemur@gmail.com>
|
Ryan Dahl <coldredlemur@gmail.com>
|
||||||
Sanjoy Das <sanjoy@playingwithpointers.com>
|
Sanjoy Das <sanjoy@playingwithpointers.com>
|
||||||
Subrato K De <subratokde@codeaurora.org>
|
Subrato K De <subratokde@codeaurora.org>
|
||||||
|
Tobias Burnus <burnus@net-b.de>
|
||||||
Vlad Burlik <vladbph@gmail.com>
|
Vlad Burlik <vladbph@gmail.com>
|
||||||
Yuqiang Xian <yuqiang.xian@intel.com>
|
Yuqiang Xian <yuqiang.xian@intel.com>
|
||||||
Zaheer Ahmad <zahmad@codeaurora.org>
|
Zaheer Ahmad <zahmad@codeaurora.org>
|
||||||
|
7
deps/v8/ChangeLog
vendored
7
deps/v8/ChangeLog
vendored
@ -1,3 +1,10 @@
|
|||||||
|
2011-12-19: Version 3.8.1
|
||||||
|
|
||||||
|
Fixed GCC 4.7 warnings. Patch from Tobias Burnus.
|
||||||
|
|
||||||
|
Stability improvements on all platforms.
|
||||||
|
|
||||||
|
|
||||||
2011-12-13: Version 3.8.0
|
2011-12-13: Version 3.8.0
|
||||||
|
|
||||||
Fixed handling of arrays in DefineOwnProperty. (issue 1756)
|
Fixed handling of arrays in DefineOwnProperty. (issue 1756)
|
||||||
|
1
deps/v8/build/common.gypi
vendored
1
deps/v8/build/common.gypi
vendored
@ -303,6 +303,7 @@
|
|||||||
}],
|
}],
|
||||||
['OS=="win"', {
|
['OS=="win"', {
|
||||||
'msvs_configuration_attributes': {
|
'msvs_configuration_attributes': {
|
||||||
|
'OutputDirectory': '<(DEPTH)\\build\\$(ConfigurationName)',
|
||||||
'IntermediateDirectory': '$(OutDir)\\obj\\$(ProjectName)',
|
'IntermediateDirectory': '$(OutDir)\\obj\\$(ProjectName)',
|
||||||
'CharacterSet': '1',
|
'CharacterSet': '1',
|
||||||
},
|
},
|
||||||
|
437
deps/v8/src/arm/code-stubs-arm.cc
vendored
437
deps/v8/src/arm/code-stubs-arm.cc
vendored
@ -5785,37 +5785,23 @@ void SubStringStub::Generate(MacroAssembler* masm) {
|
|||||||
static const int kFromOffset = 1 * kPointerSize;
|
static const int kFromOffset = 1 * kPointerSize;
|
||||||
static const int kStringOffset = 2 * kPointerSize;
|
static const int kStringOffset = 2 * kPointerSize;
|
||||||
|
|
||||||
// Check bounds and smi-ness.
|
__ Ldrd(r2, r3, MemOperand(sp, kToOffset));
|
||||||
Register to = r6;
|
|
||||||
Register from = r7;
|
|
||||||
|
|
||||||
__ Ldrd(to, from, MemOperand(sp, kToOffset));
|
|
||||||
STATIC_ASSERT(kFromOffset == kToOffset + 4);
|
STATIC_ASSERT(kFromOffset == kToOffset + 4);
|
||||||
STATIC_ASSERT(kSmiTag == 0);
|
STATIC_ASSERT(kSmiTag == 0);
|
||||||
STATIC_ASSERT(kSmiTagSize + kSmiShiftSize == 1);
|
STATIC_ASSERT(kSmiTagSize + kSmiShiftSize == 1);
|
||||||
|
|
||||||
// I.e., arithmetic shift right by one un-smi-tags.
|
// I.e., arithmetic shift right by one un-smi-tags.
|
||||||
__ mov(r2, Operand(to, ASR, 1), SetCC);
|
__ mov(r2, Operand(r2, ASR, 1), SetCC);
|
||||||
__ mov(r3, Operand(from, ASR, 1), SetCC, cc);
|
__ mov(r3, Operand(r3, ASR, 1), SetCC, cc);
|
||||||
// If either to or from had the smi tag bit set, then carry is set now.
|
// If either to or from had the smi tag bit set, then carry is set now.
|
||||||
__ b(cs, &runtime); // Either "from" or "to" is not a smi.
|
__ b(cs, &runtime); // Either "from" or "to" is not a smi.
|
||||||
__ b(mi, &runtime); // From is negative.
|
__ b(mi, &runtime); // From is negative.
|
||||||
|
|
||||||
// Both to and from are smis.
|
// Both r2 and r3 are untagged integers.
|
||||||
__ sub(r2, r2, Operand(r3), SetCC);
|
__ sub(r2, r2, Operand(r3), SetCC);
|
||||||
__ b(mi, &runtime); // Fail if from > to.
|
__ b(mi, &runtime); // Fail if from > to.
|
||||||
// Special handling of sub-strings of length 1 and 2. One character strings
|
|
||||||
// are handled in the runtime system (looked up in the single character
|
|
||||||
// cache). Two character strings are looked for in the symbol cache in
|
|
||||||
// generated code.
|
|
||||||
__ cmp(r2, Operand(2));
|
|
||||||
__ b(lt, &runtime);
|
|
||||||
|
|
||||||
// r2: result string length
|
// Make sure first argument is a string.
|
||||||
// r3: from index (untagged smi)
|
|
||||||
// r6 (a.k.a. to): to (smi)
|
|
||||||
// r7 (a.k.a. from): from offset (smi)
|
|
||||||
// Make sure first argument is a sequential (or flat) string.
|
|
||||||
__ ldr(r0, MemOperand(sp, kStringOffset));
|
__ ldr(r0, MemOperand(sp, kStringOffset));
|
||||||
STATIC_ASSERT(kSmiTag == 0);
|
STATIC_ASSERT(kSmiTag == 0);
|
||||||
__ JumpIfSmi(r0, &runtime);
|
__ JumpIfSmi(r0, &runtime);
|
||||||
@ -5830,67 +5816,15 @@ void SubStringStub::Generate(MacroAssembler* masm) {
|
|||||||
__ cmp(r2, Operand(r4, ASR, 1));
|
__ cmp(r2, Operand(r4, ASR, 1));
|
||||||
__ b(eq, &return_r0);
|
__ b(eq, &return_r0);
|
||||||
|
|
||||||
Label create_slice;
|
|
||||||
if (FLAG_string_slices) {
|
|
||||||
__ cmp(r2, Operand(SlicedString::kMinLength));
|
|
||||||
__ b(ge, &create_slice);
|
|
||||||
}
|
|
||||||
|
|
||||||
// r0: original string
|
|
||||||
// r1: instance type
|
|
||||||
// r2: result string length
|
|
||||||
// r3: from index (untagged smi)
|
|
||||||
// r6 (a.k.a. to): to (smi)
|
|
||||||
// r7 (a.k.a. from): from offset (smi)
|
|
||||||
Label seq_string;
|
|
||||||
__ and_(r4, r1, Operand(kStringRepresentationMask));
|
|
||||||
STATIC_ASSERT(kSeqStringTag < kConsStringTag);
|
|
||||||
STATIC_ASSERT(kConsStringTag < kExternalStringTag);
|
|
||||||
STATIC_ASSERT(kConsStringTag < kSlicedStringTag);
|
|
||||||
__ cmp(r4, Operand(kConsStringTag));
|
|
||||||
__ b(gt, &runtime); // Slices and external strings go to runtime.
|
|
||||||
__ b(lt, &seq_string); // Sequential strings are handled directly.
|
|
||||||
|
|
||||||
// Cons string. Try to recurse (once) on the first substring.
|
|
||||||
// (This adds a little more generality than necessary to handle flattened
|
|
||||||
// cons strings, but not much).
|
|
||||||
__ ldr(r0, FieldMemOperand(r0, ConsString::kFirstOffset));
|
|
||||||
__ ldr(r4, FieldMemOperand(r0, HeapObject::kMapOffset));
|
|
||||||
__ ldrb(r1, FieldMemOperand(r4, Map::kInstanceTypeOffset));
|
|
||||||
__ tst(r1, Operand(kStringRepresentationMask));
|
|
||||||
STATIC_ASSERT(kSeqStringTag == 0);
|
|
||||||
__ b(ne, &runtime); // Cons, slices and external strings go to runtime.
|
|
||||||
|
|
||||||
// Definitly a sequential string.
|
|
||||||
__ bind(&seq_string);
|
|
||||||
|
|
||||||
// r0: original string
|
|
||||||
// r1: instance type
|
|
||||||
// r2: result string length
|
|
||||||
// r3: from index (untagged smi)
|
|
||||||
// r6 (a.k.a. to): to (smi)
|
|
||||||
// r7 (a.k.a. from): from offset (smi)
|
|
||||||
__ ldr(r4, FieldMemOperand(r0, String::kLengthOffset));
|
|
||||||
__ cmp(r4, Operand(to));
|
|
||||||
__ b(lt, &runtime); // Fail if to > length.
|
|
||||||
to = no_reg;
|
|
||||||
|
|
||||||
// r0: original string or left hand side of the original cons string.
|
|
||||||
// r1: instance type
|
|
||||||
// r2: result string length
|
|
||||||
// r3: from index (untagged smi)
|
|
||||||
// r7 (a.k.a. from): from offset (smi)
|
|
||||||
// Check for flat ASCII string.
|
|
||||||
Label non_ascii_flat;
|
|
||||||
__ tst(r1, Operand(kStringEncodingMask));
|
|
||||||
STATIC_ASSERT(kTwoByteStringTag == 0);
|
|
||||||
__ b(eq, &non_ascii_flat);
|
|
||||||
|
|
||||||
Label result_longer_than_two;
|
Label result_longer_than_two;
|
||||||
|
// Check for special case of two character ascii string, in which case
|
||||||
|
// we do a lookup in the symbol table first.
|
||||||
__ cmp(r2, Operand(2));
|
__ cmp(r2, Operand(2));
|
||||||
__ b(gt, &result_longer_than_two);
|
__ b(gt, &result_longer_than_two);
|
||||||
|
__ b(lt, &runtime);
|
||||||
|
|
||||||
|
__ JumpIfInstanceTypeIsNotSequentialAscii(r1, r1, &runtime);
|
||||||
|
|
||||||
// Sub string of length 2 requested.
|
|
||||||
// Get the two characters forming the sub string.
|
// Get the two characters forming the sub string.
|
||||||
__ add(r0, r0, Operand(r3));
|
__ add(r0, r0, Operand(r3));
|
||||||
__ ldrb(r3, FieldMemOperand(r0, SeqAsciiString::kHeaderSize));
|
__ ldrb(r3, FieldMemOperand(r0, SeqAsciiString::kHeaderSize));
|
||||||
@ -5900,7 +5834,6 @@ void SubStringStub::Generate(MacroAssembler* masm) {
|
|||||||
Label make_two_character_string;
|
Label make_two_character_string;
|
||||||
StringHelper::GenerateTwoCharacterSymbolTableProbe(
|
StringHelper::GenerateTwoCharacterSymbolTableProbe(
|
||||||
masm, r3, r4, r1, r5, r6, r7, r9, &make_two_character_string);
|
masm, r3, r4, r1, r5, r6, r7, r9, &make_two_character_string);
|
||||||
Counters* counters = masm->isolate()->counters();
|
|
||||||
__ jmp(&return_r0);
|
__ jmp(&return_r0);
|
||||||
|
|
||||||
// r2: result string length.
|
// r2: result string length.
|
||||||
@ -5911,18 +5844,114 @@ void SubStringStub::Generate(MacroAssembler* masm) {
|
|||||||
__ jmp(&return_r0);
|
__ jmp(&return_r0);
|
||||||
|
|
||||||
__ bind(&result_longer_than_two);
|
__ bind(&result_longer_than_two);
|
||||||
|
// Deal with different string types: update the index if necessary
|
||||||
|
// and put the underlying string into r5.
|
||||||
|
// r0: original string
|
||||||
|
// r1: instance type
|
||||||
|
// r2: length
|
||||||
|
// r3: from index (untagged)
|
||||||
|
Label underlying_unpacked, sliced_string, seq_or_external_string;
|
||||||
|
// If the string is not indirect, it can only be sequential or external.
|
||||||
|
STATIC_ASSERT(kIsIndirectStringMask == (kSlicedStringTag & kConsStringTag));
|
||||||
|
STATIC_ASSERT(kIsIndirectStringMask != 0);
|
||||||
|
__ tst(r1, Operand(kIsIndirectStringMask));
|
||||||
|
__ b(eq, &seq_or_external_string);
|
||||||
|
|
||||||
// Locate 'from' character of string.
|
__ tst(r1, Operand(kSlicedNotConsMask));
|
||||||
__ add(r5, r0, Operand(SeqAsciiString::kHeaderSize - kHeapObjectTag));
|
__ b(ne, &sliced_string);
|
||||||
__ add(r5, r5, Operand(from, ASR, 1));
|
// Cons string. Check whether it is flat, then fetch first part.
|
||||||
|
__ ldr(r5, FieldMemOperand(r0, ConsString::kSecondOffset));
|
||||||
|
__ CompareRoot(r5, Heap::kEmptyStringRootIndex);
|
||||||
|
__ b(ne, &runtime);
|
||||||
|
__ ldr(r5, FieldMemOperand(r0, ConsString::kFirstOffset));
|
||||||
|
// Update instance type.
|
||||||
|
__ ldr(r1, FieldMemOperand(r5, HeapObject::kMapOffset));
|
||||||
|
__ ldrb(r1, FieldMemOperand(r1, Map::kInstanceTypeOffset));
|
||||||
|
__ jmp(&underlying_unpacked);
|
||||||
|
|
||||||
// Allocate the result.
|
__ bind(&sliced_string);
|
||||||
__ AllocateAsciiString(r0, r2, r3, r4, r1, &runtime);
|
// Sliced string. Fetch parent and correct start index by offset.
|
||||||
|
__ ldr(r5, FieldMemOperand(r0, SlicedString::kOffsetOffset));
|
||||||
|
__ add(r3, r3, Operand(r5, ASR, 1));
|
||||||
|
__ ldr(r5, FieldMemOperand(r0, SlicedString::kParentOffset));
|
||||||
|
// Update instance type.
|
||||||
|
__ ldr(r1, FieldMemOperand(r5, HeapObject::kMapOffset));
|
||||||
|
__ ldrb(r1, FieldMemOperand(r1, Map::kInstanceTypeOffset));
|
||||||
|
__ jmp(&underlying_unpacked);
|
||||||
|
|
||||||
// r0: result string
|
__ bind(&seq_or_external_string);
|
||||||
// r2: result string length
|
// Sequential or external string. Just move string to the expected register.
|
||||||
// r5: first character of substring to copy
|
__ mov(r5, r0);
|
||||||
// r7 (a.k.a. from): from offset (smi)
|
|
||||||
|
__ bind(&underlying_unpacked);
|
||||||
|
|
||||||
|
if (FLAG_string_slices) {
|
||||||
|
Label copy_routine;
|
||||||
|
// r5: underlying subject string
|
||||||
|
// r1: instance type of underlying subject string
|
||||||
|
// r2: length
|
||||||
|
// r3: adjusted start index (untagged)
|
||||||
|
__ cmp(r2, Operand(SlicedString::kMinLength));
|
||||||
|
// Short slice. Copy instead of slicing.
|
||||||
|
__ b(lt, ©_routine);
|
||||||
|
// Allocate new sliced string. At this point we do not reload the instance
|
||||||
|
// type including the string encoding because we simply rely on the info
|
||||||
|
// provided by the original string. It does not matter if the original
|
||||||
|
// string's encoding is wrong because we always have to recheck encoding of
|
||||||
|
// the newly created string's parent anyways due to externalized strings.
|
||||||
|
Label two_byte_slice, set_slice_header;
|
||||||
|
STATIC_ASSERT((kStringEncodingMask & kAsciiStringTag) != 0);
|
||||||
|
STATIC_ASSERT((kStringEncodingMask & kTwoByteStringTag) == 0);
|
||||||
|
__ tst(r1, Operand(kStringEncodingMask));
|
||||||
|
__ b(eq, &two_byte_slice);
|
||||||
|
__ AllocateAsciiSlicedString(r0, r2, r6, r7, &runtime);
|
||||||
|
__ jmp(&set_slice_header);
|
||||||
|
__ bind(&two_byte_slice);
|
||||||
|
__ AllocateTwoByteSlicedString(r0, r2, r6, r7, &runtime);
|
||||||
|
__ bind(&set_slice_header);
|
||||||
|
__ mov(r3, Operand(r3, LSL, 1));
|
||||||
|
__ str(r3, FieldMemOperand(r0, SlicedString::kOffsetOffset));
|
||||||
|
__ str(r5, FieldMemOperand(r0, SlicedString::kParentOffset));
|
||||||
|
__ jmp(&return_r0);
|
||||||
|
|
||||||
|
__ bind(©_routine);
|
||||||
|
}
|
||||||
|
|
||||||
|
// r5: underlying subject string
|
||||||
|
// r1: instance type of underlying subject string
|
||||||
|
// r2: length
|
||||||
|
// r3: adjusted start index (untagged)
|
||||||
|
Label two_byte_sequential, sequential_string, allocate_result;
|
||||||
|
STATIC_ASSERT(kExternalStringTag != 0);
|
||||||
|
STATIC_ASSERT(kSeqStringTag == 0);
|
||||||
|
__ tst(r1, Operand(kExternalStringTag));
|
||||||
|
__ b(eq, &sequential_string);
|
||||||
|
|
||||||
|
// Handle external string.
|
||||||
|
// Rule out short external strings.
|
||||||
|
STATIC_CHECK(kShortExternalStringTag != 0);
|
||||||
|
__ tst(r1, Operand(kShortExternalStringTag));
|
||||||
|
__ b(ne, &runtime);
|
||||||
|
__ ldr(r5, FieldMemOperand(r5, ExternalString::kResourceDataOffset));
|
||||||
|
// r5 already points to the first character of underlying string.
|
||||||
|
__ jmp(&allocate_result);
|
||||||
|
|
||||||
|
__ bind(&sequential_string);
|
||||||
|
// Locate first character of underlying subject string.
|
||||||
|
STATIC_ASSERT(SeqTwoByteString::kHeaderSize == SeqAsciiString::kHeaderSize);
|
||||||
|
__ add(r5, r5, Operand(SeqAsciiString::kHeaderSize - kHeapObjectTag));
|
||||||
|
|
||||||
|
__ bind(&allocate_result);
|
||||||
|
// Sequential acii string. Allocate the result.
|
||||||
|
STATIC_ASSERT((kAsciiStringTag & kStringEncodingMask) != 0);
|
||||||
|
__ tst(r1, Operand(kStringEncodingMask));
|
||||||
|
__ b(eq, &two_byte_sequential);
|
||||||
|
|
||||||
|
// Allocate and copy the resulting ascii string.
|
||||||
|
__ AllocateAsciiString(r0, r2, r4, r6, r7, &runtime);
|
||||||
|
|
||||||
|
// Locate first character of substring to copy.
|
||||||
|
__ add(r5, r5, r3);
|
||||||
// Locate first character of result.
|
// Locate first character of result.
|
||||||
__ add(r1, r0, Operand(SeqAsciiString::kHeaderSize - kHeapObjectTag));
|
__ add(r1, r0, Operand(SeqAsciiString::kHeaderSize - kHeapObjectTag));
|
||||||
|
|
||||||
@ -5935,30 +5964,16 @@ void SubStringStub::Generate(MacroAssembler* masm) {
|
|||||||
COPY_ASCII | DEST_ALWAYS_ALIGNED);
|
COPY_ASCII | DEST_ALWAYS_ALIGNED);
|
||||||
__ jmp(&return_r0);
|
__ jmp(&return_r0);
|
||||||
|
|
||||||
__ bind(&non_ascii_flat);
|
// Allocate and copy the resulting two-byte string.
|
||||||
// r0: original string
|
__ bind(&two_byte_sequential);
|
||||||
// r2: result string length
|
__ AllocateTwoByteString(r0, r2, r4, r6, r7, &runtime);
|
||||||
// r7 (a.k.a. from): from offset (smi)
|
|
||||||
// Check for flat two byte string.
|
|
||||||
|
|
||||||
// Locate 'from' character of string.
|
// Locate first character of substring to copy.
|
||||||
__ add(r5, r0, Operand(SeqTwoByteString::kHeaderSize - kHeapObjectTag));
|
|
||||||
// As "from" is a smi it is 2 times the value which matches the size of a two
|
|
||||||
// byte character.
|
|
||||||
STATIC_ASSERT(kSmiTagSize == 1 && kSmiTag == 0);
|
STATIC_ASSERT(kSmiTagSize == 1 && kSmiTag == 0);
|
||||||
__ add(r5, r5, Operand(from));
|
__ add(r5, r5, Operand(r3, LSL, 1));
|
||||||
|
|
||||||
// Allocate the result.
|
|
||||||
__ AllocateTwoByteString(r0, r2, r1, r3, r4, &runtime);
|
|
||||||
|
|
||||||
// r0: result string
|
|
||||||
// r2: result string length
|
|
||||||
// r5: first character of substring to copy
|
|
||||||
// Locate first character of result.
|
// Locate first character of result.
|
||||||
__ add(r1, r0, Operand(SeqTwoByteString::kHeaderSize - kHeapObjectTag));
|
__ add(r1, r0, Operand(SeqTwoByteString::kHeaderSize - kHeapObjectTag));
|
||||||
|
|
||||||
from = no_reg;
|
|
||||||
|
|
||||||
// r0: result string.
|
// r0: result string.
|
||||||
// r1: first character of result.
|
// r1: first character of result.
|
||||||
// r2: result length.
|
// r2: result length.
|
||||||
@ -5966,69 +5981,9 @@ void SubStringStub::Generate(MacroAssembler* masm) {
|
|||||||
STATIC_ASSERT((SeqTwoByteString::kHeaderSize & kObjectAlignmentMask) == 0);
|
STATIC_ASSERT((SeqTwoByteString::kHeaderSize & kObjectAlignmentMask) == 0);
|
||||||
StringHelper::GenerateCopyCharactersLong(
|
StringHelper::GenerateCopyCharactersLong(
|
||||||
masm, r1, r5, r2, r3, r4, r6, r7, r9, DEST_ALWAYS_ALIGNED);
|
masm, r1, r5, r2, r3, r4, r6, r7, r9, DEST_ALWAYS_ALIGNED);
|
||||||
__ jmp(&return_r0);
|
|
||||||
|
|
||||||
if (FLAG_string_slices) {
|
|
||||||
__ bind(&create_slice);
|
|
||||||
// r0: original string
|
|
||||||
// r1: instance type
|
|
||||||
// r2: length
|
|
||||||
// r3: from index (untagged smi)
|
|
||||||
// r6 (a.k.a. to): to (smi)
|
|
||||||
// r7 (a.k.a. from): from offset (smi)
|
|
||||||
Label allocate_slice, sliced_string, seq_or_external_string;
|
|
||||||
// If the string is not indirect, it can only be sequential or external.
|
|
||||||
STATIC_ASSERT(kIsIndirectStringMask == (kSlicedStringTag & kConsStringTag));
|
|
||||||
STATIC_ASSERT(kIsIndirectStringMask != 0);
|
|
||||||
__ tst(r1, Operand(kIsIndirectStringMask));
|
|
||||||
__ b(eq, &seq_or_external_string);
|
|
||||||
|
|
||||||
__ tst(r1, Operand(kSlicedNotConsMask));
|
|
||||||
__ b(ne, &sliced_string);
|
|
||||||
// Cons string. Check whether it is flat, then fetch first part.
|
|
||||||
__ ldr(r5, FieldMemOperand(r0, ConsString::kSecondOffset));
|
|
||||||
__ LoadRoot(r9, Heap::kEmptyStringRootIndex);
|
|
||||||
__ cmp(r5, r9);
|
|
||||||
__ b(ne, &runtime);
|
|
||||||
__ ldr(r5, FieldMemOperand(r0, ConsString::kFirstOffset));
|
|
||||||
__ jmp(&allocate_slice);
|
|
||||||
|
|
||||||
__ bind(&sliced_string);
|
|
||||||
// Sliced string. Fetch parent and correct start index by offset.
|
|
||||||
__ ldr(r5, FieldMemOperand(r0, SlicedString::kOffsetOffset));
|
|
||||||
__ add(r7, r7, r5);
|
|
||||||
__ ldr(r5, FieldMemOperand(r0, SlicedString::kParentOffset));
|
|
||||||
__ jmp(&allocate_slice);
|
|
||||||
|
|
||||||
__ bind(&seq_or_external_string);
|
|
||||||
// Sequential or external string. Just move string to the correct register.
|
|
||||||
__ mov(r5, r0);
|
|
||||||
|
|
||||||
__ bind(&allocate_slice);
|
|
||||||
// r1: instance type of original string
|
|
||||||
// r2: length
|
|
||||||
// r5: underlying subject string
|
|
||||||
// r7 (a.k.a. from): from offset (smi)
|
|
||||||
// Allocate new sliced string. At this point we do not reload the instance
|
|
||||||
// type including the string encoding because we simply rely on the info
|
|
||||||
// provided by the original string. It does not matter if the original
|
|
||||||
// string's encoding is wrong because we always have to recheck encoding of
|
|
||||||
// the newly created string's parent anyways due to externalized strings.
|
|
||||||
Label two_byte_slice, set_slice_header;
|
|
||||||
STATIC_ASSERT((kStringEncodingMask & kAsciiStringTag) != 0);
|
|
||||||
STATIC_ASSERT((kStringEncodingMask & kTwoByteStringTag) == 0);
|
|
||||||
__ tst(r1, Operand(kStringEncodingMask));
|
|
||||||
__ b(eq, &two_byte_slice);
|
|
||||||
__ AllocateAsciiSlicedString(r0, r2, r3, r4, &runtime);
|
|
||||||
__ jmp(&set_slice_header);
|
|
||||||
__ bind(&two_byte_slice);
|
|
||||||
__ AllocateTwoByteSlicedString(r0, r2, r3, r4, &runtime);
|
|
||||||
__ bind(&set_slice_header);
|
|
||||||
__ str(r7, FieldMemOperand(r0, SlicedString::kOffsetOffset));
|
|
||||||
__ str(r5, FieldMemOperand(r0, SlicedString::kParentOffset));
|
|
||||||
}
|
|
||||||
|
|
||||||
__ bind(&return_r0);
|
__ bind(&return_r0);
|
||||||
|
Counters* counters = masm->isolate()->counters();
|
||||||
__ IncrementCounter(counters->sub_string_native(), 1, r3, r4);
|
__ IncrementCounter(counters->sub_string_native(), 1, r3, r4);
|
||||||
__ add(sp, sp, Operand(3 * kPointerSize));
|
__ add(sp, sp, Operand(3 * kPointerSize));
|
||||||
__ Ret();
|
__ Ret();
|
||||||
@ -6185,7 +6140,7 @@ void StringCompareStub::Generate(MacroAssembler* masm) {
|
|||||||
|
|
||||||
|
|
||||||
void StringAddStub::Generate(MacroAssembler* masm) {
|
void StringAddStub::Generate(MacroAssembler* masm) {
|
||||||
Label string_add_runtime, call_builtin;
|
Label call_runtime, call_builtin;
|
||||||
Builtins::JavaScript builtin_id = Builtins::ADD;
|
Builtins::JavaScript builtin_id = Builtins::ADD;
|
||||||
|
|
||||||
Counters* counters = masm->isolate()->counters();
|
Counters* counters = masm->isolate()->counters();
|
||||||
@ -6200,7 +6155,7 @@ void StringAddStub::Generate(MacroAssembler* masm) {
|
|||||||
|
|
||||||
// Make sure that both arguments are strings if not known in advance.
|
// Make sure that both arguments are strings if not known in advance.
|
||||||
if (flags_ == NO_STRING_ADD_FLAGS) {
|
if (flags_ == NO_STRING_ADD_FLAGS) {
|
||||||
__ JumpIfEitherSmi(r0, r1, &string_add_runtime);
|
__ JumpIfEitherSmi(r0, r1, &call_runtime);
|
||||||
// Load instance types.
|
// Load instance types.
|
||||||
__ ldr(r4, FieldMemOperand(r0, HeapObject::kMapOffset));
|
__ ldr(r4, FieldMemOperand(r0, HeapObject::kMapOffset));
|
||||||
__ ldr(r5, FieldMemOperand(r1, HeapObject::kMapOffset));
|
__ ldr(r5, FieldMemOperand(r1, HeapObject::kMapOffset));
|
||||||
@ -6210,7 +6165,7 @@ void StringAddStub::Generate(MacroAssembler* masm) {
|
|||||||
// If either is not a string, go to runtime.
|
// If either is not a string, go to runtime.
|
||||||
__ tst(r4, Operand(kIsNotStringMask));
|
__ tst(r4, Operand(kIsNotStringMask));
|
||||||
__ tst(r5, Operand(kIsNotStringMask), eq);
|
__ tst(r5, Operand(kIsNotStringMask), eq);
|
||||||
__ b(ne, &string_add_runtime);
|
__ b(ne, &call_runtime);
|
||||||
} else {
|
} else {
|
||||||
// Here at least one of the arguments is definitely a string.
|
// Here at least one of the arguments is definitely a string.
|
||||||
// We convert the one that is not known to be a string.
|
// We convert the one that is not known to be a string.
|
||||||
@ -6279,7 +6234,7 @@ void StringAddStub::Generate(MacroAssembler* masm) {
|
|||||||
__ ldrb(r5, FieldMemOperand(r5, Map::kInstanceTypeOffset));
|
__ ldrb(r5, FieldMemOperand(r5, Map::kInstanceTypeOffset));
|
||||||
}
|
}
|
||||||
__ JumpIfBothInstanceTypesAreNotSequentialAscii(r4, r5, r6, r7,
|
__ JumpIfBothInstanceTypesAreNotSequentialAscii(r4, r5, r6, r7,
|
||||||
&string_add_runtime);
|
&call_runtime);
|
||||||
|
|
||||||
// Get the two characters forming the sub string.
|
// Get the two characters forming the sub string.
|
||||||
__ ldrb(r2, FieldMemOperand(r0, SeqAsciiString::kHeaderSize));
|
__ ldrb(r2, FieldMemOperand(r0, SeqAsciiString::kHeaderSize));
|
||||||
@ -6301,7 +6256,7 @@ void StringAddStub::Generate(MacroAssembler* masm) {
|
|||||||
// halfword store instruction (which assumes that processor is
|
// halfword store instruction (which assumes that processor is
|
||||||
// in a little endian mode)
|
// in a little endian mode)
|
||||||
__ mov(r6, Operand(2));
|
__ mov(r6, Operand(2));
|
||||||
__ AllocateAsciiString(r0, r6, r4, r5, r9, &string_add_runtime);
|
__ AllocateAsciiString(r0, r6, r4, r5, r9, &call_runtime);
|
||||||
__ strh(r2, FieldMemOperand(r0, SeqAsciiString::kHeaderSize));
|
__ strh(r2, FieldMemOperand(r0, SeqAsciiString::kHeaderSize));
|
||||||
__ IncrementCounter(counters->string_add_native(), 1, r2, r3);
|
__ IncrementCounter(counters->string_add_native(), 1, r2, r3);
|
||||||
__ add(sp, sp, Operand(2 * kPointerSize));
|
__ add(sp, sp, Operand(2 * kPointerSize));
|
||||||
@ -6316,7 +6271,7 @@ void StringAddStub::Generate(MacroAssembler* masm) {
|
|||||||
ASSERT(IsPowerOf2(String::kMaxLength + 1));
|
ASSERT(IsPowerOf2(String::kMaxLength + 1));
|
||||||
// kMaxLength + 1 is representable as shifted literal, kMaxLength is not.
|
// kMaxLength + 1 is representable as shifted literal, kMaxLength is not.
|
||||||
__ cmp(r6, Operand(String::kMaxLength + 1));
|
__ cmp(r6, Operand(String::kMaxLength + 1));
|
||||||
__ b(hs, &string_add_runtime);
|
__ b(hs, &call_runtime);
|
||||||
|
|
||||||
// If result is not supposed to be flat, allocate a cons string object.
|
// If result is not supposed to be flat, allocate a cons string object.
|
||||||
// If both strings are ASCII the result is an ASCII cons string.
|
// If both strings are ASCII the result is an ASCII cons string.
|
||||||
@ -6334,7 +6289,7 @@ void StringAddStub::Generate(MacroAssembler* masm) {
|
|||||||
|
|
||||||
// Allocate an ASCII cons string.
|
// Allocate an ASCII cons string.
|
||||||
__ bind(&ascii_data);
|
__ bind(&ascii_data);
|
||||||
__ AllocateAsciiConsString(r7, r6, r4, r5, &string_add_runtime);
|
__ AllocateAsciiConsString(r7, r6, r4, r5, &call_runtime);
|
||||||
__ bind(&allocated);
|
__ bind(&allocated);
|
||||||
// Fill the fields of the cons string.
|
// Fill the fields of the cons string.
|
||||||
__ str(r0, FieldMemOperand(r7, ConsString::kFirstOffset));
|
__ str(r0, FieldMemOperand(r7, ConsString::kFirstOffset));
|
||||||
@ -6359,11 +6314,13 @@ void StringAddStub::Generate(MacroAssembler* masm) {
|
|||||||
__ b(eq, &ascii_data);
|
__ b(eq, &ascii_data);
|
||||||
|
|
||||||
// Allocate a two byte cons string.
|
// Allocate a two byte cons string.
|
||||||
__ AllocateTwoByteConsString(r7, r6, r4, r5, &string_add_runtime);
|
__ AllocateTwoByteConsString(r7, r6, r4, r5, &call_runtime);
|
||||||
__ jmp(&allocated);
|
__ jmp(&allocated);
|
||||||
|
|
||||||
// Handle creating a flat result. First check that both strings are
|
// We cannot encounter sliced strings or cons strings here since:
|
||||||
// sequential and that they have the same encoding.
|
STATIC_ASSERT(SlicedString::kMinLength >= String::kMinNonFlatLength);
|
||||||
|
// Handle creating a flat result from either external or sequential strings.
|
||||||
|
// Locate the first characters' locations.
|
||||||
// r0: first string
|
// r0: first string
|
||||||
// r1: second string
|
// r1: second string
|
||||||
// r2: length of first string
|
// r2: length of first string
|
||||||
@ -6371,6 +6328,7 @@ void StringAddStub::Generate(MacroAssembler* masm) {
|
|||||||
// r4: first string instance type (if flags_ == NO_STRING_ADD_FLAGS)
|
// r4: first string instance type (if flags_ == NO_STRING_ADD_FLAGS)
|
||||||
// r5: second string instance type (if flags_ == NO_STRING_ADD_FLAGS)
|
// r5: second string instance type (if flags_ == NO_STRING_ADD_FLAGS)
|
||||||
// r6: sum of lengths.
|
// r6: sum of lengths.
|
||||||
|
Label first_prepared, second_prepared;
|
||||||
__ bind(&string_add_flat_result);
|
__ bind(&string_add_flat_result);
|
||||||
if (flags_ != NO_STRING_ADD_FLAGS) {
|
if (flags_ != NO_STRING_ADD_FLAGS) {
|
||||||
__ ldr(r4, FieldMemOperand(r0, HeapObject::kMapOffset));
|
__ ldr(r4, FieldMemOperand(r0, HeapObject::kMapOffset));
|
||||||
@ -6378,97 +6336,88 @@ void StringAddStub::Generate(MacroAssembler* masm) {
|
|||||||
__ ldrb(r4, FieldMemOperand(r4, Map::kInstanceTypeOffset));
|
__ ldrb(r4, FieldMemOperand(r4, Map::kInstanceTypeOffset));
|
||||||
__ ldrb(r5, FieldMemOperand(r5, Map::kInstanceTypeOffset));
|
__ ldrb(r5, FieldMemOperand(r5, Map::kInstanceTypeOffset));
|
||||||
}
|
}
|
||||||
// Check that both strings are sequential.
|
|
||||||
STATIC_ASSERT(kSeqStringTag == 0);
|
// Check whether both strings have same encoding
|
||||||
__ tst(r4, Operand(kStringRepresentationMask));
|
|
||||||
__ tst(r5, Operand(kStringRepresentationMask), eq);
|
|
||||||
__ b(ne, &string_add_runtime);
|
|
||||||
// Now check if both strings have the same encoding (ASCII/Two-byte).
|
|
||||||
// r0: first string.
|
|
||||||
// r1: second string.
|
|
||||||
// r2: length of first string.
|
|
||||||
// r3: length of second string.
|
|
||||||
// r6: sum of lengths..
|
|
||||||
Label non_ascii_string_add_flat_result;
|
|
||||||
ASSERT(IsPowerOf2(kStringEncodingMask)); // Just one bit to test.
|
|
||||||
__ eor(r7, r4, Operand(r5));
|
__ eor(r7, r4, Operand(r5));
|
||||||
__ tst(r7, Operand(kStringEncodingMask));
|
__ tst(r7, Operand(kStringEncodingMask));
|
||||||
__ b(ne, &string_add_runtime);
|
__ b(ne, &call_runtime);
|
||||||
// And see if it's ASCII or two-byte.
|
|
||||||
__ tst(r4, Operand(kStringEncodingMask));
|
STATIC_ASSERT(kSeqStringTag == 0);
|
||||||
|
__ tst(r4, Operand(kStringRepresentationMask));
|
||||||
|
STATIC_ASSERT(SeqAsciiString::kHeaderSize == SeqTwoByteString::kHeaderSize);
|
||||||
|
__ add(r7,
|
||||||
|
r0,
|
||||||
|
Operand(SeqAsciiString::kHeaderSize - kHeapObjectTag),
|
||||||
|
LeaveCC,
|
||||||
|
eq);
|
||||||
|
__ b(eq, &first_prepared);
|
||||||
|
// External string: rule out short external string and load string resource.
|
||||||
|
STATIC_ASSERT(kShortExternalStringTag != 0);
|
||||||
|
__ tst(r4, Operand(kShortExternalStringMask));
|
||||||
|
__ b(ne, &call_runtime);
|
||||||
|
__ ldr(r7, FieldMemOperand(r0, ExternalString::kResourceDataOffset));
|
||||||
|
__ bind(&first_prepared);
|
||||||
|
|
||||||
|
STATIC_ASSERT(kSeqStringTag == 0);
|
||||||
|
__ tst(r5, Operand(kStringRepresentationMask));
|
||||||
|
STATIC_ASSERT(SeqAsciiString::kHeaderSize == SeqTwoByteString::kHeaderSize);
|
||||||
|
__ add(r1,
|
||||||
|
r1,
|
||||||
|
Operand(SeqAsciiString::kHeaderSize - kHeapObjectTag),
|
||||||
|
LeaveCC,
|
||||||
|
eq);
|
||||||
|
__ b(eq, &second_prepared);
|
||||||
|
// External string: rule out short external string and load string resource.
|
||||||
|
STATIC_ASSERT(kShortExternalStringTag != 0);
|
||||||
|
__ tst(r5, Operand(kShortExternalStringMask));
|
||||||
|
__ b(ne, &call_runtime);
|
||||||
|
__ ldr(r1, FieldMemOperand(r1, ExternalString::kResourceDataOffset));
|
||||||
|
__ bind(&second_prepared);
|
||||||
|
|
||||||
|
Label non_ascii_string_add_flat_result;
|
||||||
|
// r7: first character of first string
|
||||||
|
// r1: first character of second string
|
||||||
|
// r2: length of first string.
|
||||||
|
// r3: length of second string.
|
||||||
|
// r6: sum of lengths.
|
||||||
|
// Both strings have the same encoding.
|
||||||
|
STATIC_ASSERT(kTwoByteStringTag == 0);
|
||||||
|
__ tst(r5, Operand(kStringEncodingMask));
|
||||||
__ b(eq, &non_ascii_string_add_flat_result);
|
__ b(eq, &non_ascii_string_add_flat_result);
|
||||||
|
|
||||||
// Both strings are sequential ASCII strings. We also know that they are
|
__ AllocateAsciiString(r0, r6, r4, r5, r9, &call_runtime);
|
||||||
// short (since the sum of the lengths is less than kMinNonFlatLength).
|
__ add(r6, r0, Operand(SeqAsciiString::kHeaderSize - kHeapObjectTag));
|
||||||
// r6: length of resulting flat string
|
// r0: result string.
|
||||||
__ AllocateAsciiString(r7, r6, r4, r5, r9, &string_add_runtime);
|
// r7: first character of first string.
|
||||||
// Locate first character of result.
|
// r1: first character of second string.
|
||||||
__ add(r6, r7, Operand(SeqAsciiString::kHeaderSize - kHeapObjectTag));
|
|
||||||
// Locate first character of first argument.
|
|
||||||
__ add(r0, r0, Operand(SeqAsciiString::kHeaderSize - kHeapObjectTag));
|
|
||||||
// r0: first character of first string.
|
|
||||||
// r1: second string.
|
|
||||||
// r2: length of first string.
|
// r2: length of first string.
|
||||||
// r3: length of second string.
|
// r3: length of second string.
|
||||||
// r6: first character of result.
|
// r6: first character of result.
|
||||||
// r7: result string.
|
StringHelper::GenerateCopyCharacters(masm, r6, r7, r2, r4, true);
|
||||||
StringHelper::GenerateCopyCharacters(masm, r6, r0, r2, r4, true);
|
|
||||||
|
|
||||||
// Load second argument and locate first character.
|
|
||||||
__ add(r1, r1, Operand(SeqAsciiString::kHeaderSize - kHeapObjectTag));
|
|
||||||
// r1: first character of second string.
|
|
||||||
// r3: length of second string.
|
|
||||||
// r6: next character of result.
|
// r6: next character of result.
|
||||||
// r7: result string.
|
|
||||||
StringHelper::GenerateCopyCharacters(masm, r6, r1, r3, r4, true);
|
StringHelper::GenerateCopyCharacters(masm, r6, r1, r3, r4, true);
|
||||||
__ mov(r0, Operand(r7));
|
|
||||||
__ IncrementCounter(counters->string_add_native(), 1, r2, r3);
|
__ IncrementCounter(counters->string_add_native(), 1, r2, r3);
|
||||||
__ add(sp, sp, Operand(2 * kPointerSize));
|
__ add(sp, sp, Operand(2 * kPointerSize));
|
||||||
__ Ret();
|
__ Ret();
|
||||||
|
|
||||||
__ bind(&non_ascii_string_add_flat_result);
|
__ bind(&non_ascii_string_add_flat_result);
|
||||||
// Both strings are sequential two byte strings.
|
__ AllocateTwoByteString(r0, r6, r4, r5, r9, &call_runtime);
|
||||||
// r0: first string.
|
__ add(r6, r0, Operand(SeqTwoByteString::kHeaderSize - kHeapObjectTag));
|
||||||
// r1: second string.
|
// r0: result string.
|
||||||
// r2: length of first string.
|
// r7: first character of first string.
|
||||||
// r3: length of second string.
|
// r1: first character of second string.
|
||||||
// r6: sum of length of strings.
|
|
||||||
__ AllocateTwoByteString(r7, r6, r4, r5, r9, &string_add_runtime);
|
|
||||||
// r0: first string.
|
|
||||||
// r1: second string.
|
|
||||||
// r2: length of first string.
|
|
||||||
// r3: length of second string.
|
|
||||||
// r7: result string.
|
|
||||||
|
|
||||||
// Locate first character of result.
|
|
||||||
__ add(r6, r7, Operand(SeqTwoByteString::kHeaderSize - kHeapObjectTag));
|
|
||||||
// Locate first character of first argument.
|
|
||||||
__ add(r0, r0, Operand(SeqTwoByteString::kHeaderSize - kHeapObjectTag));
|
|
||||||
|
|
||||||
// r0: first character of first string.
|
|
||||||
// r1: second string.
|
|
||||||
// r2: length of first string.
|
// r2: length of first string.
|
||||||
// r3: length of second string.
|
// r3: length of second string.
|
||||||
// r6: first character of result.
|
// r6: first character of result.
|
||||||
// r7: result string.
|
StringHelper::GenerateCopyCharacters(masm, r6, r7, r2, r4, false);
|
||||||
StringHelper::GenerateCopyCharacters(masm, r6, r0, r2, r4, false);
|
// r6: next character of result.
|
||||||
|
|
||||||
// Locate first character of second argument.
|
|
||||||
__ add(r1, r1, Operand(SeqTwoByteString::kHeaderSize - kHeapObjectTag));
|
|
||||||
|
|
||||||
// r1: first character of second string.
|
|
||||||
// r3: length of second string.
|
|
||||||
// r6: next character of result (after copy of first string).
|
|
||||||
// r7: result string.
|
|
||||||
StringHelper::GenerateCopyCharacters(masm, r6, r1, r3, r4, false);
|
StringHelper::GenerateCopyCharacters(masm, r6, r1, r3, r4, false);
|
||||||
|
|
||||||
__ mov(r0, Operand(r7));
|
|
||||||
__ IncrementCounter(counters->string_add_native(), 1, r2, r3);
|
__ IncrementCounter(counters->string_add_native(), 1, r2, r3);
|
||||||
__ add(sp, sp, Operand(2 * kPointerSize));
|
__ add(sp, sp, Operand(2 * kPointerSize));
|
||||||
__ Ret();
|
__ Ret();
|
||||||
|
|
||||||
// Just jump to runtime to add the two strings.
|
// Just jump to runtime to add the two strings.
|
||||||
__ bind(&string_add_runtime);
|
__ bind(&call_runtime);
|
||||||
__ TailCallRuntime(Runtime::kStringAdd, 2, 1);
|
__ TailCallRuntime(Runtime::kStringAdd, 2, 1);
|
||||||
|
|
||||||
if (call_builtin.is_linked()) {
|
if (call_builtin.is_linked()) {
|
||||||
|
16
deps/v8/src/arm/ic-arm.cc
vendored
16
deps/v8/src/arm/ic-arm.cc
vendored
@ -1469,11 +1469,10 @@ void StoreIC::GenerateArrayLength(MacroAssembler* masm) {
|
|||||||
// -- lr : return address
|
// -- lr : return address
|
||||||
// -----------------------------------
|
// -----------------------------------
|
||||||
//
|
//
|
||||||
// This accepts as a receiver anything JSObject::SetElementsLength accepts
|
// This accepts as a receiver anything JSArray::SetElementsLength accepts
|
||||||
// (currently anything except for external and pixel arrays which means
|
// (currently anything except for external arrays which means anything with
|
||||||
// anything with elements of FixedArray type.), but currently is restricted
|
// elements of FixedArray type). Value must be a number, but only smis are
|
||||||
// to JSArray.
|
// accepted as the most common case.
|
||||||
// Value must be a number, but only smis are accepted as the most common case.
|
|
||||||
|
|
||||||
Label miss;
|
Label miss;
|
||||||
|
|
||||||
@ -1495,6 +1494,13 @@ void StoreIC::GenerateArrayLength(MacroAssembler* masm) {
|
|||||||
__ CompareObjectType(scratch, scratch, scratch, FIXED_ARRAY_TYPE);
|
__ CompareObjectType(scratch, scratch, scratch, FIXED_ARRAY_TYPE);
|
||||||
__ b(ne, &miss);
|
__ b(ne, &miss);
|
||||||
|
|
||||||
|
// Check that the array has fast properties, otherwise the length
|
||||||
|
// property might have been redefined.
|
||||||
|
__ ldr(scratch, FieldMemOperand(receiver, JSArray::kPropertiesOffset));
|
||||||
|
__ ldr(scratch, FieldMemOperand(scratch, FixedArray::kMapOffset));
|
||||||
|
__ CompareRoot(scratch, Heap::kHashTableMapRootIndex);
|
||||||
|
__ b(eq, &miss);
|
||||||
|
|
||||||
// Check that value is a smi.
|
// Check that value is a smi.
|
||||||
__ JumpIfNotSmi(value, &miss);
|
__ JumpIfNotSmi(value, &miss);
|
||||||
|
|
||||||
|
22
deps/v8/src/arm/lithium-codegen-arm.cc
vendored
22
deps/v8/src/arm/lithium-codegen-arm.cc
vendored
@ -2306,7 +2306,11 @@ void LCodeGen::DoLoadContextSlot(LLoadContextSlot* instr) {
|
|||||||
if (instr->hydrogen()->RequiresHoleCheck()) {
|
if (instr->hydrogen()->RequiresHoleCheck()) {
|
||||||
__ LoadRoot(ip, Heap::kTheHoleValueRootIndex);
|
__ LoadRoot(ip, Heap::kTheHoleValueRootIndex);
|
||||||
__ cmp(result, ip);
|
__ cmp(result, ip);
|
||||||
DeoptimizeIf(eq, instr->environment());
|
if (instr->hydrogen()->DeoptimizesOnHole()) {
|
||||||
|
DeoptimizeIf(eq, instr->environment());
|
||||||
|
} else {
|
||||||
|
__ mov(result, Operand(factory()->undefined_value()), LeaveCC, eq);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -2314,14 +2318,22 @@ void LCodeGen::DoLoadContextSlot(LLoadContextSlot* instr) {
|
|||||||
void LCodeGen::DoStoreContextSlot(LStoreContextSlot* instr) {
|
void LCodeGen::DoStoreContextSlot(LStoreContextSlot* instr) {
|
||||||
Register context = ToRegister(instr->context());
|
Register context = ToRegister(instr->context());
|
||||||
Register value = ToRegister(instr->value());
|
Register value = ToRegister(instr->value());
|
||||||
|
Register scratch = scratch0();
|
||||||
MemOperand target = ContextOperand(context, instr->slot_index());
|
MemOperand target = ContextOperand(context, instr->slot_index());
|
||||||
|
|
||||||
|
Label skip_assignment;
|
||||||
|
|
||||||
if (instr->hydrogen()->RequiresHoleCheck()) {
|
if (instr->hydrogen()->RequiresHoleCheck()) {
|
||||||
Register scratch = scratch0();
|
|
||||||
__ ldr(scratch, target);
|
__ ldr(scratch, target);
|
||||||
__ LoadRoot(ip, Heap::kTheHoleValueRootIndex);
|
__ LoadRoot(ip, Heap::kTheHoleValueRootIndex);
|
||||||
__ cmp(scratch, ip);
|
__ cmp(scratch, ip);
|
||||||
DeoptimizeIf(eq, instr->environment());
|
if (instr->hydrogen()->DeoptimizesOnHole()) {
|
||||||
|
DeoptimizeIf(eq, instr->environment());
|
||||||
|
} else {
|
||||||
|
__ b(ne, &skip_assignment);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
__ str(value, target);
|
__ str(value, target);
|
||||||
if (instr->hydrogen()->NeedsWriteBarrier()) {
|
if (instr->hydrogen()->NeedsWriteBarrier()) {
|
||||||
HType type = instr->hydrogen()->value()->type();
|
HType type = instr->hydrogen()->value()->type();
|
||||||
@ -2330,12 +2342,14 @@ void LCodeGen::DoStoreContextSlot(LStoreContextSlot* instr) {
|
|||||||
__ RecordWriteContextSlot(context,
|
__ RecordWriteContextSlot(context,
|
||||||
target.offset(),
|
target.offset(),
|
||||||
value,
|
value,
|
||||||
scratch0(),
|
scratch,
|
||||||
kLRHasBeenSaved,
|
kLRHasBeenSaved,
|
||||||
kSaveFPRegs,
|
kSaveFPRegs,
|
||||||
EMIT_REMEMBERED_SET,
|
EMIT_REMEMBERED_SET,
|
||||||
check_needed);
|
check_needed);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
__ bind(&skip_assignment);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
5
deps/v8/src/assembler.cc
vendored
5
deps/v8/src/assembler.cc
vendored
@ -817,11 +817,6 @@ ExternalReference ExternalReference::compute_output_frames_function(
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
ExternalReference ExternalReference::global_contexts_list(Isolate* isolate) {
|
|
||||||
return ExternalReference(isolate->heap()->global_contexts_list_address());
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
ExternalReference ExternalReference::keyed_lookup_cache_keys(Isolate* isolate) {
|
ExternalReference ExternalReference::keyed_lookup_cache_keys(Isolate* isolate) {
|
||||||
return ExternalReference(isolate->keyed_lookup_cache()->keys_address());
|
return ExternalReference(isolate->keyed_lookup_cache()->keys_address());
|
||||||
}
|
}
|
||||||
|
1
deps/v8/src/assembler.h
vendored
1
deps/v8/src/assembler.h
vendored
@ -590,7 +590,6 @@ class ExternalReference BASE_EMBEDDED {
|
|||||||
// Deoptimization support.
|
// Deoptimization support.
|
||||||
static ExternalReference new_deoptimizer_function(Isolate* isolate);
|
static ExternalReference new_deoptimizer_function(Isolate* isolate);
|
||||||
static ExternalReference compute_output_frames_function(Isolate* isolate);
|
static ExternalReference compute_output_frames_function(Isolate* isolate);
|
||||||
static ExternalReference global_contexts_list(Isolate* isolate);
|
|
||||||
|
|
||||||
// Static data in the keyed lookup cache.
|
// Static data in the keyed lookup cache.
|
||||||
static ExternalReference keyed_lookup_cache_keys(Isolate* isolate);
|
static ExternalReference keyed_lookup_cache_keys(Isolate* isolate);
|
||||||
|
18
deps/v8/src/d8.cc
vendored
18
deps/v8/src/d8.cc
vendored
@ -296,14 +296,26 @@ Handle<Value> Shell::CreateExternalArray(const Arguments& args,
|
|||||||
ASSERT(kMaxLength == i::ExternalArray::kMaxLength);
|
ASSERT(kMaxLength == i::ExternalArray::kMaxLength);
|
||||||
#endif // V8_SHARED
|
#endif // V8_SHARED
|
||||||
size_t length = 0;
|
size_t length = 0;
|
||||||
|
TryCatch try_catch;
|
||||||
if (args[0]->IsUint32()) {
|
if (args[0]->IsUint32()) {
|
||||||
length = args[0]->Uint32Value();
|
length = args[0]->Uint32Value();
|
||||||
} else {
|
} else {
|
||||||
Local<Number> number = args[0]->ToNumber();
|
Local<Number> number = args[0]->ToNumber();
|
||||||
if (number.IsEmpty() || !number->IsNumber()) {
|
if (number.IsEmpty()) {
|
||||||
return ThrowException(String::New("Array length must be a number."));
|
ASSERT(try_catch.HasCaught());
|
||||||
|
return try_catch.Exception();
|
||||||
|
}
|
||||||
|
ASSERT(number->IsNumber());
|
||||||
|
Local<Int32> int32 = number->ToInt32();
|
||||||
|
if (int32.IsEmpty()) {
|
||||||
|
if (try_catch.HasCaught()) {
|
||||||
|
return try_catch.Exception();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
int32_t raw_length = int32->Int32Value();
|
||||||
|
if (try_catch.HasCaught()) {
|
||||||
|
return try_catch.Exception();
|
||||||
}
|
}
|
||||||
int32_t raw_length = number->ToInt32()->Int32Value();
|
|
||||||
if (raw_length < 0) {
|
if (raw_length < 0) {
|
||||||
return ThrowException(String::New("Array length must not be negative."));
|
return ThrowException(String::New("Array length must not be negative."));
|
||||||
}
|
}
|
||||||
|
15
deps/v8/src/deoptimizer.cc
vendored
15
deps/v8/src/deoptimizer.cc
vendored
@ -264,11 +264,16 @@ void Deoptimizer::VisitAllOptimizedFunctions(
|
|||||||
AssertNoAllocation no_allocation;
|
AssertNoAllocation no_allocation;
|
||||||
|
|
||||||
// Run through the list of all global contexts and deoptimize.
|
// Run through the list of all global contexts and deoptimize.
|
||||||
Object* global = Isolate::Current()->heap()->global_contexts_list();
|
Object* context = Isolate::Current()->heap()->global_contexts_list();
|
||||||
while (!global->IsUndefined()) {
|
while (!context->IsUndefined()) {
|
||||||
VisitAllOptimizedFunctionsForGlobalObject(Context::cast(global)->global(),
|
// GC can happen when the context is not fully initialized,
|
||||||
visitor);
|
// so the global field of the context can be undefined.
|
||||||
global = Context::cast(global)->get(Context::NEXT_CONTEXT_LINK);
|
Object* global = Context::cast(context)->get(Context::GLOBAL_INDEX);
|
||||||
|
if (!global->IsUndefined()) {
|
||||||
|
VisitAllOptimizedFunctionsForGlobalObject(JSObject::cast(global),
|
||||||
|
visitor);
|
||||||
|
}
|
||||||
|
context = Context::cast(context)->get(Context::NEXT_CONTEXT_LINK);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
30
deps/v8/src/flag-definitions.h
vendored
30
deps/v8/src/flag-definitions.h
vendored
@ -41,6 +41,7 @@
|
|||||||
extern ctype FLAG_##nam;
|
extern ctype FLAG_##nam;
|
||||||
#define FLAG_READONLY(ftype, ctype, nam, def, cmt) \
|
#define FLAG_READONLY(ftype, ctype, nam, def, cmt) \
|
||||||
static ctype const FLAG_##nam = def;
|
static ctype const FLAG_##nam = def;
|
||||||
|
#define DEFINE_implication(whenflag, thenflag)
|
||||||
|
|
||||||
// We want to supply the actual storage and value for the flag variable in the
|
// We want to supply the actual storage and value for the flag variable in the
|
||||||
// .cc file. We only do this for writable flags.
|
// .cc file. We only do this for writable flags.
|
||||||
@ -48,6 +49,7 @@
|
|||||||
#define FLAG_FULL(ftype, ctype, nam, def, cmt) \
|
#define FLAG_FULL(ftype, ctype, nam, def, cmt) \
|
||||||
ctype FLAG_##nam = def;
|
ctype FLAG_##nam = def;
|
||||||
#define FLAG_READONLY(ftype, ctype, nam, def, cmt)
|
#define FLAG_READONLY(ftype, ctype, nam, def, cmt)
|
||||||
|
#define DEFINE_implication(whenflag, thenflag)
|
||||||
|
|
||||||
// We need to define all of our default values so that the Flag structure can
|
// We need to define all of our default values so that the Flag structure can
|
||||||
// access them by pointer. These are just used internally inside of one .cc,
|
// access them by pointer. These are just used internally inside of one .cc,
|
||||||
@ -56,7 +58,7 @@
|
|||||||
#define FLAG_FULL(ftype, ctype, nam, def, cmt) \
|
#define FLAG_FULL(ftype, ctype, nam, def, cmt) \
|
||||||
static ctype const FLAGDEFAULT_##nam = def;
|
static ctype const FLAGDEFAULT_##nam = def;
|
||||||
#define FLAG_READONLY(ftype, ctype, nam, def, cmt)
|
#define FLAG_READONLY(ftype, ctype, nam, def, cmt)
|
||||||
|
#define DEFINE_implication(whenflag, thenflag)
|
||||||
|
|
||||||
// We want to write entries into our meta data table, for internal parsing and
|
// We want to write entries into our meta data table, for internal parsing and
|
||||||
// printing / etc in the flag parser code. We only do this for writable flags.
|
// printing / etc in the flag parser code. We only do this for writable flags.
|
||||||
@ -64,6 +66,14 @@
|
|||||||
#define FLAG_FULL(ftype, ctype, nam, def, cmt) \
|
#define FLAG_FULL(ftype, ctype, nam, def, cmt) \
|
||||||
{ Flag::TYPE_##ftype, #nam, &FLAG_##nam, &FLAGDEFAULT_##nam, cmt, false },
|
{ Flag::TYPE_##ftype, #nam, &FLAG_##nam, &FLAGDEFAULT_##nam, cmt, false },
|
||||||
#define FLAG_READONLY(ftype, ctype, nam, def, cmt)
|
#define FLAG_READONLY(ftype, ctype, nam, def, cmt)
|
||||||
|
#define DEFINE_implication(whenflag, thenflag)
|
||||||
|
|
||||||
|
// We produce the code to set flags when it is implied by another flag.
|
||||||
|
#elif defined(FLAG_MODE_DEFINE_IMPLICATIONS)
|
||||||
|
#define FLAG_FULL(ftype, ctype, nam, def, cmt)
|
||||||
|
#define FLAG_READONLY(ftype, ctype, nam, def, cmt)
|
||||||
|
#define DEFINE_implication(whenflag, thenflag) \
|
||||||
|
if (FLAG_##whenflag) FLAG_##thenflag = true;
|
||||||
|
|
||||||
#else
|
#else
|
||||||
#error No mode supplied when including flags.defs
|
#error No mode supplied when including flags.defs
|
||||||
@ -103,6 +113,10 @@ DEFINE_bool(harmony_proxies, false, "enable harmony proxies")
|
|||||||
DEFINE_bool(harmony_collections, false,
|
DEFINE_bool(harmony_collections, false,
|
||||||
"enable harmony collections (sets, maps, and weak maps)")
|
"enable harmony collections (sets, maps, and weak maps)")
|
||||||
DEFINE_bool(harmony, false, "enable all harmony features")
|
DEFINE_bool(harmony, false, "enable all harmony features")
|
||||||
|
DEFINE_implication(harmony, harmony_typeof)
|
||||||
|
DEFINE_implication(harmony, harmony_scoping)
|
||||||
|
DEFINE_implication(harmony, harmony_proxies)
|
||||||
|
DEFINE_implication(harmony, harmony_collections)
|
||||||
|
|
||||||
// Flags for experimental implementation features.
|
// Flags for experimental implementation features.
|
||||||
DEFINE_bool(unbox_double_arrays, true, "automatically unbox arrays of doubles")
|
DEFINE_bool(unbox_double_arrays, true, "automatically unbox arrays of doubles")
|
||||||
@ -542,6 +556,18 @@ DEFINE_bool(print_unopt_code, false, "print unoptimized code before "
|
|||||||
DEFINE_bool(print_code_verbose, false, "print more information for code")
|
DEFINE_bool(print_code_verbose, false, "print more information for code")
|
||||||
DEFINE_bool(print_builtin_code, false, "print generated code for builtins")
|
DEFINE_bool(print_builtin_code, false, "print generated code for builtins")
|
||||||
|
|
||||||
|
#ifdef ENABLE_DISASSEMBLER
|
||||||
|
DEFINE_bool(print_all_code, false, "enable all flags related to printing code")
|
||||||
|
DEFINE_implication(print_all_code, print_code)
|
||||||
|
DEFINE_implication(print_all_code, print_opt_code)
|
||||||
|
DEFINE_implication(print_all_code, print_unopt_code)
|
||||||
|
DEFINE_implication(print_all_code, print_code_verbose)
|
||||||
|
DEFINE_implication(print_all_code, print_builtin_code)
|
||||||
|
DEFINE_implication(print_all_code, print_code_stubs)
|
||||||
|
DEFINE_implication(print_all_code, trace_codegen)
|
||||||
|
DEFINE_implication(print_all_code, code_comments)
|
||||||
|
#endif
|
||||||
|
|
||||||
// Cleanup...
|
// Cleanup...
|
||||||
#undef FLAG_FULL
|
#undef FLAG_FULL
|
||||||
#undef FLAG_READONLY
|
#undef FLAG_READONLY
|
||||||
@ -550,8 +576,10 @@ DEFINE_bool(print_builtin_code, false, "print generated code for builtins")
|
|||||||
#undef DEFINE_bool
|
#undef DEFINE_bool
|
||||||
#undef DEFINE_int
|
#undef DEFINE_int
|
||||||
#undef DEFINE_string
|
#undef DEFINE_string
|
||||||
|
#undef DEFINE_implication
|
||||||
|
|
||||||
#undef FLAG_MODE_DECLARE
|
#undef FLAG_MODE_DECLARE
|
||||||
#undef FLAG_MODE_DEFINE
|
#undef FLAG_MODE_DEFINE
|
||||||
#undef FLAG_MODE_DEFINE_DEFAULTS
|
#undef FLAG_MODE_DEFINE_DEFAULTS
|
||||||
#undef FLAG_MODE_META
|
#undef FLAG_MODE_META
|
||||||
|
#undef FLAG_MODE_DEFINE_IMPLICATIONS
|
||||||
|
5
deps/v8/src/flags.cc
vendored
5
deps/v8/src/flags.cc
vendored
@ -548,4 +548,9 @@ JSArguments& JSArguments::operator=(JSArguments args) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void FlagList::EnforceFlagImplications() {
|
||||||
|
#define FLAG_MODE_DEFINE_IMPLICATIONS
|
||||||
|
#include "flag-definitions.h"
|
||||||
|
}
|
||||||
|
|
||||||
} } // namespace v8::internal
|
} } // namespace v8::internal
|
||||||
|
3
deps/v8/src/flags.h
vendored
3
deps/v8/src/flags.h
vendored
@ -72,6 +72,9 @@ class FlagList {
|
|||||||
|
|
||||||
// Print help to stdout with flags, types, and default values.
|
// Print help to stdout with flags, types, and default values.
|
||||||
static void PrintHelp();
|
static void PrintHelp();
|
||||||
|
|
||||||
|
// Set flags as consequence of being implied by another flag.
|
||||||
|
static void EnforceFlagImplications();
|
||||||
};
|
};
|
||||||
|
|
||||||
} } // namespace v8::internal
|
} } // namespace v8::internal
|
||||||
|
26
deps/v8/src/heap.cc
vendored
26
deps/v8/src/heap.cc
vendored
@ -642,13 +642,17 @@ void Heap::ClearJSFunctionResultCaches() {
|
|||||||
|
|
||||||
Object* context = global_contexts_list_;
|
Object* context = global_contexts_list_;
|
||||||
while (!context->IsUndefined()) {
|
while (!context->IsUndefined()) {
|
||||||
// Get the caches for this context:
|
// Get the caches for this context. GC can happen when the context
|
||||||
FixedArray* caches =
|
// is not fully initialized, so the caches can be undefined.
|
||||||
Context::cast(context)->jsfunction_result_caches();
|
Object* caches_or_undefined =
|
||||||
// Clear the caches:
|
Context::cast(context)->get(Context::JSFUNCTION_RESULT_CACHES_INDEX);
|
||||||
int length = caches->length();
|
if (!caches_or_undefined->IsUndefined()) {
|
||||||
for (int i = 0; i < length; i++) {
|
FixedArray* caches = FixedArray::cast(caches_or_undefined);
|
||||||
JSFunctionResultCache::cast(caches->get(i))->Clear();
|
// Clear the caches:
|
||||||
|
int length = caches->length();
|
||||||
|
for (int i = 0; i < length; i++) {
|
||||||
|
JSFunctionResultCache::cast(caches->get(i))->Clear();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
// Get the next context:
|
// Get the next context:
|
||||||
context = Context::cast(context)->get(Context::NEXT_CONTEXT_LINK);
|
context = Context::cast(context)->get(Context::NEXT_CONTEXT_LINK);
|
||||||
@ -665,7 +669,13 @@ void Heap::ClearNormalizedMapCaches() {
|
|||||||
|
|
||||||
Object* context = global_contexts_list_;
|
Object* context = global_contexts_list_;
|
||||||
while (!context->IsUndefined()) {
|
while (!context->IsUndefined()) {
|
||||||
Context::cast(context)->normalized_map_cache()->Clear();
|
// GC can happen when the context is not fully initialized,
|
||||||
|
// so the cache can be undefined.
|
||||||
|
Object* cache =
|
||||||
|
Context::cast(context)->get(Context::NORMALIZED_MAP_CACHE_INDEX);
|
||||||
|
if (!cache->IsUndefined()) {
|
||||||
|
NormalizedMapCache::cast(cache)->Clear();
|
||||||
|
}
|
||||||
context = Context::cast(context)->get(Context::NEXT_CONTEXT_LINK);
|
context = Context::cast(context)->get(Context::NEXT_CONTEXT_LINK);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
4
deps/v8/src/hydrogen-instructions.cc
vendored
4
deps/v8/src/hydrogen-instructions.cc
vendored
@ -1227,7 +1227,9 @@ void HConstant::PrintDataTo(StringStream* stream) {
|
|||||||
|
|
||||||
|
|
||||||
bool HArrayLiteral::IsCopyOnWrite() const {
|
bool HArrayLiteral::IsCopyOnWrite() const {
|
||||||
return boilerplate_object_->elements()->map() == HEAP->fixed_cow_array_map();
|
if (!boilerplate_object_->IsJSObject()) return false;
|
||||||
|
return Handle<JSObject>::cast(boilerplate_object_)->elements()->map() ==
|
||||||
|
HEAP->fixed_cow_array_map();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
50
deps/v8/src/hydrogen-instructions.h
vendored
50
deps/v8/src/hydrogen-instructions.h
vendored
@ -3449,28 +3449,45 @@ class HLoadContextSlot: public HUnaryOperation {
|
|||||||
public:
|
public:
|
||||||
enum Mode {
|
enum Mode {
|
||||||
// Perform a normal load of the context slot without checking its value.
|
// Perform a normal load of the context slot without checking its value.
|
||||||
kLoad,
|
kNoCheck,
|
||||||
// Load and check the value of the context slot. Deoptimize if it's the
|
// Load and check the value of the context slot. Deoptimize if it's the
|
||||||
// hole value. This is used for checking for loading of uninitialized
|
// hole value. This is used for checking for loading of uninitialized
|
||||||
// harmony bindings where we deoptimize into full-codegen generated code
|
// harmony bindings where we deoptimize into full-codegen generated code
|
||||||
// which will subsequently throw a reference error.
|
// which will subsequently throw a reference error.
|
||||||
kLoadCheck
|
kCheckDeoptimize,
|
||||||
|
// Load and check the value of the context slot. Return undefined if it's
|
||||||
|
// the hole value. This is used for non-harmony const assignments
|
||||||
|
kCheckReturnUndefined
|
||||||
};
|
};
|
||||||
|
|
||||||
HLoadContextSlot(HValue* context, Variable* var)
|
HLoadContextSlot(HValue* context, Variable* var)
|
||||||
: HUnaryOperation(context), slot_index_(var->index()) {
|
: HUnaryOperation(context), slot_index_(var->index()) {
|
||||||
ASSERT(var->IsContextSlot());
|
ASSERT(var->IsContextSlot());
|
||||||
mode_ = (var->mode() == LET || var->mode() == CONST_HARMONY)
|
switch (var->mode()) {
|
||||||
? kLoadCheck : kLoad;
|
case LET:
|
||||||
|
case CONST_HARMONY:
|
||||||
|
mode_ = kCheckDeoptimize;
|
||||||
|
break;
|
||||||
|
case CONST:
|
||||||
|
mode_ = kCheckReturnUndefined;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
mode_ = kNoCheck;
|
||||||
|
}
|
||||||
set_representation(Representation::Tagged());
|
set_representation(Representation::Tagged());
|
||||||
SetFlag(kUseGVN);
|
SetFlag(kUseGVN);
|
||||||
SetFlag(kDependsOnContextSlots);
|
SetFlag(kDependsOnContextSlots);
|
||||||
}
|
}
|
||||||
|
|
||||||
int slot_index() const { return slot_index_; }
|
int slot_index() const { return slot_index_; }
|
||||||
|
Mode mode() const { return mode_; }
|
||||||
|
|
||||||
|
bool DeoptimizesOnHole() {
|
||||||
|
return mode_ == kCheckDeoptimize;
|
||||||
|
}
|
||||||
|
|
||||||
bool RequiresHoleCheck() {
|
bool RequiresHoleCheck() {
|
||||||
return mode_ == kLoadCheck;
|
return mode_ != kNoCheck;
|
||||||
}
|
}
|
||||||
|
|
||||||
virtual Representation RequiredInputRepresentation(int index) {
|
virtual Representation RequiredInputRepresentation(int index) {
|
||||||
@ -3498,12 +3515,14 @@ class HStoreContextSlot: public HTemplateInstruction<2> {
|
|||||||
enum Mode {
|
enum Mode {
|
||||||
// Perform a normal store to the context slot without checking its previous
|
// Perform a normal store to the context slot without checking its previous
|
||||||
// value.
|
// value.
|
||||||
kAssign,
|
kNoCheck,
|
||||||
// Check the previous value of the context slot and deoptimize if it's the
|
// Check the previous value of the context slot and deoptimize if it's the
|
||||||
// hole value. This is used for checking for assignments to uninitialized
|
// hole value. This is used for checking for assignments to uninitialized
|
||||||
// harmony bindings where we deoptimize into full-codegen generated code
|
// harmony bindings where we deoptimize into full-codegen generated code
|
||||||
// which will subsequently throw a reference error.
|
// which will subsequently throw a reference error.
|
||||||
kAssignCheck
|
kCheckDeoptimize,
|
||||||
|
// Check the previous value and ignore assignment if it isn't a hole value
|
||||||
|
kCheckIgnoreAssignment
|
||||||
};
|
};
|
||||||
|
|
||||||
HStoreContextSlot(HValue* context, int slot_index, Mode mode, HValue* value)
|
HStoreContextSlot(HValue* context, int slot_index, Mode mode, HValue* value)
|
||||||
@ -3522,8 +3541,12 @@ class HStoreContextSlot: public HTemplateInstruction<2> {
|
|||||||
return StoringValueNeedsWriteBarrier(value());
|
return StoringValueNeedsWriteBarrier(value());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool DeoptimizesOnHole() {
|
||||||
|
return mode_ == kCheckDeoptimize;
|
||||||
|
}
|
||||||
|
|
||||||
bool RequiresHoleCheck() {
|
bool RequiresHoleCheck() {
|
||||||
return mode_ == kAssignCheck;
|
return mode_ != kNoCheck;
|
||||||
}
|
}
|
||||||
|
|
||||||
virtual Representation RequiredInputRepresentation(int index) {
|
virtual Representation RequiredInputRepresentation(int index) {
|
||||||
@ -4202,7 +4225,7 @@ class HMaterializedLiteral: public HTemplateInstruction<V> {
|
|||||||
class HArrayLiteral: public HMaterializedLiteral<1> {
|
class HArrayLiteral: public HMaterializedLiteral<1> {
|
||||||
public:
|
public:
|
||||||
HArrayLiteral(HValue* context,
|
HArrayLiteral(HValue* context,
|
||||||
Handle<JSObject> boilerplate_object,
|
Handle<HeapObject> boilerplate_object,
|
||||||
int length,
|
int length,
|
||||||
int literal_index,
|
int literal_index,
|
||||||
int depth)
|
int depth)
|
||||||
@ -4214,9 +4237,12 @@ class HArrayLiteral: public HMaterializedLiteral<1> {
|
|||||||
|
|
||||||
HValue* context() { return OperandAt(0); }
|
HValue* context() { return OperandAt(0); }
|
||||||
ElementsKind boilerplate_elements_kind() const {
|
ElementsKind boilerplate_elements_kind() const {
|
||||||
return boilerplate_object_->GetElementsKind();
|
if (!boilerplate_object_->IsJSObject()) {
|
||||||
|
return FAST_ELEMENTS;
|
||||||
|
}
|
||||||
|
return Handle<JSObject>::cast(boilerplate_object_)->GetElementsKind();
|
||||||
}
|
}
|
||||||
Handle<JSObject> boilerplate_object() const { return boilerplate_object_; }
|
Handle<HeapObject> boilerplate_object() const { return boilerplate_object_; }
|
||||||
int length() const { return length_; }
|
int length() const { return length_; }
|
||||||
|
|
||||||
bool IsCopyOnWrite() const;
|
bool IsCopyOnWrite() const;
|
||||||
@ -4230,7 +4256,7 @@ class HArrayLiteral: public HMaterializedLiteral<1> {
|
|||||||
|
|
||||||
private:
|
private:
|
||||||
int length_;
|
int length_;
|
||||||
Handle<JSObject> boilerplate_object_;
|
Handle<HeapObject> boilerplate_object_;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
97
deps/v8/src/hydrogen.cc
vendored
97
deps/v8/src/hydrogen.cc
vendored
@ -3285,9 +3285,6 @@ void HGraphBuilder::VisitVariableProxy(VariableProxy* expr) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
case Variable::CONTEXT: {
|
case Variable::CONTEXT: {
|
||||||
if (variable->mode() == CONST) {
|
|
||||||
return Bailout("reference to const context slot");
|
|
||||||
}
|
|
||||||
HValue* context = BuildContextChainWalk(variable);
|
HValue* context = BuildContextChainWalk(variable);
|
||||||
HLoadContextSlot* instr = new(zone()) HLoadContextSlot(context, variable);
|
HLoadContextSlot* instr = new(zone()) HLoadContextSlot(context, variable);
|
||||||
return ast_context()->ReturnInstruction(instr, expr->id());
|
return ast_context()->ReturnInstruction(instr, expr->id());
|
||||||
@ -3467,14 +3464,22 @@ void HGraphBuilder::VisitArrayLiteral(ArrayLiteral* expr) {
|
|||||||
Handle<FixedArray> literals(environment()->closure()->literals());
|
Handle<FixedArray> literals(environment()->closure()->literals());
|
||||||
Handle<Object> raw_boilerplate(literals->get(expr->literal_index()));
|
Handle<Object> raw_boilerplate(literals->get(expr->literal_index()));
|
||||||
|
|
||||||
// For now, no boilerplate causes a deopt.
|
|
||||||
if (raw_boilerplate->IsUndefined()) {
|
if (raw_boilerplate->IsUndefined()) {
|
||||||
AddInstruction(new(zone()) HSoftDeoptimize);
|
raw_boilerplate = Runtime::CreateArrayLiteralBoilerplate(
|
||||||
return ast_context()->ReturnValue(graph()->GetConstantUndefined());
|
isolate(), literals, expr->constant_elements());
|
||||||
|
if (raw_boilerplate.is_null()) {
|
||||||
|
return Bailout("array boilerplate creation failed");
|
||||||
|
}
|
||||||
|
literals->set(expr->literal_index(), *raw_boilerplate);
|
||||||
|
if (JSObject::cast(*raw_boilerplate)->elements()->map() ==
|
||||||
|
isolate()->heap()->fixed_cow_array_map()) {
|
||||||
|
isolate()->counters()->cow_arrays_created_runtime()->Increment();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Handle<JSObject> boilerplate(Handle<JSObject>::cast(raw_boilerplate));
|
Handle<JSObject> boilerplate = Handle<JSObject>::cast(raw_boilerplate);
|
||||||
ElementsKind boilerplate_elements_kind = boilerplate->GetElementsKind();
|
ElementsKind boilerplate_elements_kind =
|
||||||
|
Handle<JSObject>::cast(boilerplate)->GetElementsKind();
|
||||||
|
|
||||||
HArrayLiteral* literal = new(zone()) HArrayLiteral(
|
HArrayLiteral* literal = new(zone()) HArrayLiteral(
|
||||||
context,
|
context,
|
||||||
@ -3805,8 +3810,8 @@ void HGraphBuilder::HandleCompoundAssignment(Assignment* expr) {
|
|||||||
|
|
||||||
if (proxy != NULL) {
|
if (proxy != NULL) {
|
||||||
Variable* var = proxy->var();
|
Variable* var = proxy->var();
|
||||||
if (var->mode() == CONST || var->mode() == LET) {
|
if (var->mode() == LET) {
|
||||||
return Bailout("unsupported let or const compound assignment");
|
return Bailout("unsupported let compound assignment");
|
||||||
}
|
}
|
||||||
|
|
||||||
CHECK_ALIVE(VisitForValue(operation));
|
CHECK_ALIVE(VisitForValue(operation));
|
||||||
@ -3821,6 +3826,9 @@ void HGraphBuilder::HandleCompoundAssignment(Assignment* expr) {
|
|||||||
|
|
||||||
case Variable::PARAMETER:
|
case Variable::PARAMETER:
|
||||||
case Variable::LOCAL:
|
case Variable::LOCAL:
|
||||||
|
if (var->mode() == CONST) {
|
||||||
|
return Bailout("unsupported const compound assignment");
|
||||||
|
}
|
||||||
Bind(var, Top());
|
Bind(var, Top());
|
||||||
break;
|
break;
|
||||||
|
|
||||||
@ -3841,10 +3849,23 @@ void HGraphBuilder::HandleCompoundAssignment(Assignment* expr) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
HStoreContextSlot::Mode mode;
|
||||||
|
|
||||||
|
switch (var->mode()) {
|
||||||
|
case LET:
|
||||||
|
mode = HStoreContextSlot::kCheckDeoptimize;
|
||||||
|
break;
|
||||||
|
case CONST:
|
||||||
|
return ast_context()->ReturnValue(Pop());
|
||||||
|
case CONST_HARMONY:
|
||||||
|
// This case is checked statically so no need to
|
||||||
|
// perform checks here
|
||||||
|
UNREACHABLE();
|
||||||
|
default:
|
||||||
|
mode = HStoreContextSlot::kNoCheck;
|
||||||
|
}
|
||||||
|
|
||||||
HValue* context = BuildContextChainWalk(var);
|
HValue* context = BuildContextChainWalk(var);
|
||||||
HStoreContextSlot::Mode mode =
|
|
||||||
(var->mode() == LET || var->mode() == CONST_HARMONY)
|
|
||||||
? HStoreContextSlot::kAssignCheck : HStoreContextSlot::kAssign;
|
|
||||||
HStoreContextSlot* instr =
|
HStoreContextSlot* instr =
|
||||||
new(zone()) HStoreContextSlot(context, var->index(), mode, Top());
|
new(zone()) HStoreContextSlot(context, var->index(), mode, Top());
|
||||||
AddInstruction(instr);
|
AddInstruction(instr);
|
||||||
@ -3955,17 +3976,19 @@ void HGraphBuilder::VisitAssignment(Assignment* expr) {
|
|||||||
HandlePropertyAssignment(expr);
|
HandlePropertyAssignment(expr);
|
||||||
} else if (proxy != NULL) {
|
} else if (proxy != NULL) {
|
||||||
Variable* var = proxy->var();
|
Variable* var = proxy->var();
|
||||||
|
|
||||||
if (var->mode() == CONST) {
|
if (var->mode() == CONST) {
|
||||||
if (expr->op() != Token::INIT_CONST) {
|
if (expr->op() != Token::INIT_CONST) {
|
||||||
return Bailout("non-initializer assignment to const");
|
CHECK_ALIVE(VisitForValue(expr->value()));
|
||||||
|
return ast_context()->ReturnValue(Pop());
|
||||||
}
|
}
|
||||||
if (!var->IsStackAllocated()) {
|
|
||||||
return Bailout("assignment to const context slot");
|
if (var->IsStackAllocated()) {
|
||||||
|
// We insert a use of the old value to detect unsupported uses of const
|
||||||
|
// variables (e.g. initialization inside a loop).
|
||||||
|
HValue* old_value = environment()->Lookup(var);
|
||||||
|
AddInstruction(new HUseConst(old_value));
|
||||||
}
|
}
|
||||||
// We insert a use of the old value to detect unsupported uses of const
|
|
||||||
// variables (e.g. initialization inside a loop).
|
|
||||||
HValue* old_value = environment()->Lookup(var);
|
|
||||||
AddInstruction(new HUseConst(old_value));
|
|
||||||
} else if (var->mode() == CONST_HARMONY) {
|
} else if (var->mode() == CONST_HARMONY) {
|
||||||
if (expr->op() != Token::INIT_CONST_HARMONY) {
|
if (expr->op() != Token::INIT_CONST_HARMONY) {
|
||||||
return Bailout("non-initializer assignment to const");
|
return Bailout("non-initializer assignment to const");
|
||||||
@ -4004,7 +4027,6 @@ void HGraphBuilder::VisitAssignment(Assignment* expr) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
case Variable::CONTEXT: {
|
case Variable::CONTEXT: {
|
||||||
ASSERT(var->mode() != CONST);
|
|
||||||
// Bail out if we try to mutate a parameter value in a function using
|
// Bail out if we try to mutate a parameter value in a function using
|
||||||
// the arguments object. We do not (yet) correctly handle the
|
// the arguments object. We do not (yet) correctly handle the
|
||||||
// arguments property of the function.
|
// arguments property of the function.
|
||||||
@ -4020,17 +4042,32 @@ void HGraphBuilder::VisitAssignment(Assignment* expr) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
CHECK_ALIVE(VisitForValue(expr->value()));
|
CHECK_ALIVE(VisitForValue(expr->value()));
|
||||||
HValue* context = BuildContextChainWalk(var);
|
|
||||||
HStoreContextSlot::Mode mode;
|
HStoreContextSlot::Mode mode;
|
||||||
if (expr->op() == Token::ASSIGN) {
|
if (expr->op() == Token::ASSIGN) {
|
||||||
mode = (var->mode() == LET || var->mode() == CONST_HARMONY)
|
switch (var->mode()) {
|
||||||
? HStoreContextSlot::kAssignCheck : HStoreContextSlot::kAssign;
|
case LET:
|
||||||
|
mode = HStoreContextSlot::kCheckDeoptimize;
|
||||||
|
break;
|
||||||
|
case CONST:
|
||||||
|
return ast_context()->ReturnValue(Pop());
|
||||||
|
case CONST_HARMONY:
|
||||||
|
// This case is checked statically so no need to
|
||||||
|
// perform checks here
|
||||||
|
UNREACHABLE();
|
||||||
|
default:
|
||||||
|
mode = HStoreContextSlot::kNoCheck;
|
||||||
|
}
|
||||||
|
} else if (expr->op() == Token::INIT_VAR ||
|
||||||
|
expr->op() == Token::INIT_LET ||
|
||||||
|
expr->op() == Token::INIT_CONST_HARMONY) {
|
||||||
|
mode = HStoreContextSlot::kNoCheck;
|
||||||
} else {
|
} else {
|
||||||
ASSERT(expr->op() == Token::INIT_VAR ||
|
ASSERT(expr->op() == Token::INIT_CONST);
|
||||||
expr->op() == Token::INIT_LET ||
|
|
||||||
expr->op() == Token::INIT_CONST_HARMONY);
|
mode = HStoreContextSlot::kCheckIgnoreAssignment;
|
||||||
mode = HStoreContextSlot::kAssign;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
HValue* context = BuildContextChainWalk(var);
|
||||||
HStoreContextSlot* instr = new(zone()) HStoreContextSlot(
|
HStoreContextSlot* instr = new(zone()) HStoreContextSlot(
|
||||||
context, var->index(), mode, Top());
|
context, var->index(), mode, Top());
|
||||||
AddInstruction(instr);
|
AddInstruction(instr);
|
||||||
@ -5643,7 +5680,7 @@ void HGraphBuilder::VisitCountOperation(CountOperation* expr) {
|
|||||||
HValue* context = BuildContextChainWalk(var);
|
HValue* context = BuildContextChainWalk(var);
|
||||||
HStoreContextSlot::Mode mode =
|
HStoreContextSlot::Mode mode =
|
||||||
(var->mode() == LET || var->mode() == CONST_HARMONY)
|
(var->mode() == LET || var->mode() == CONST_HARMONY)
|
||||||
? HStoreContextSlot::kAssignCheck : HStoreContextSlot::kAssign;
|
? HStoreContextSlot::kCheckDeoptimize : HStoreContextSlot::kNoCheck;
|
||||||
HStoreContextSlot* instr =
|
HStoreContextSlot* instr =
|
||||||
new(zone()) HStoreContextSlot(context, var->index(), mode, after);
|
new(zone()) HStoreContextSlot(context, var->index(), mode, after);
|
||||||
AddInstruction(instr);
|
AddInstruction(instr);
|
||||||
@ -6251,7 +6288,7 @@ void HGraphBuilder::HandleDeclaration(VariableProxy* proxy,
|
|||||||
if (var->IsContextSlot()) {
|
if (var->IsContextSlot()) {
|
||||||
HValue* context = environment()->LookupContext();
|
HValue* context = environment()->LookupContext();
|
||||||
HStoreContextSlot* store = new HStoreContextSlot(
|
HStoreContextSlot* store = new HStoreContextSlot(
|
||||||
context, var->index(), HStoreContextSlot::kAssign, value);
|
context, var->index(), HStoreContextSlot::kNoCheck, value);
|
||||||
AddInstruction(store);
|
AddInstruction(store);
|
||||||
if (store->HasObservableSideEffects()) AddSimulate(proxy->id());
|
if (store->HasObservableSideEffects()) AddSimulate(proxy->id());
|
||||||
} else {
|
} else {
|
||||||
|
176
deps/v8/src/ia32/code-stubs-ia32.cc
vendored
176
deps/v8/src/ia32/code-stubs-ia32.cc
vendored
@ -5474,7 +5474,7 @@ void StringCharAtGenerator::GenerateSlow(
|
|||||||
|
|
||||||
|
|
||||||
void StringAddStub::Generate(MacroAssembler* masm) {
|
void StringAddStub::Generate(MacroAssembler* masm) {
|
||||||
Label string_add_runtime, call_builtin;
|
Label call_runtime, call_builtin;
|
||||||
Builtins::JavaScript builtin_id = Builtins::ADD;
|
Builtins::JavaScript builtin_id = Builtins::ADD;
|
||||||
|
|
||||||
// Load the two arguments.
|
// Load the two arguments.
|
||||||
@ -5483,14 +5483,14 @@ void StringAddStub::Generate(MacroAssembler* masm) {
|
|||||||
|
|
||||||
// Make sure that both arguments are strings if not known in advance.
|
// Make sure that both arguments are strings if not known in advance.
|
||||||
if (flags_ == NO_STRING_ADD_FLAGS) {
|
if (flags_ == NO_STRING_ADD_FLAGS) {
|
||||||
__ JumpIfSmi(eax, &string_add_runtime);
|
__ JumpIfSmi(eax, &call_runtime);
|
||||||
__ CmpObjectType(eax, FIRST_NONSTRING_TYPE, ebx);
|
__ CmpObjectType(eax, FIRST_NONSTRING_TYPE, ebx);
|
||||||
__ j(above_equal, &string_add_runtime);
|
__ j(above_equal, &call_runtime);
|
||||||
|
|
||||||
// First argument is a a string, test second.
|
// First argument is a a string, test second.
|
||||||
__ JumpIfSmi(edx, &string_add_runtime);
|
__ JumpIfSmi(edx, &call_runtime);
|
||||||
__ CmpObjectType(edx, FIRST_NONSTRING_TYPE, ebx);
|
__ CmpObjectType(edx, FIRST_NONSTRING_TYPE, ebx);
|
||||||
__ j(above_equal, &string_add_runtime);
|
__ j(above_equal, &call_runtime);
|
||||||
} else {
|
} else {
|
||||||
// Here at least one of the arguments is definitely a string.
|
// Here at least one of the arguments is definitely a string.
|
||||||
// We convert the one that is not known to be a string.
|
// We convert the one that is not known to be a string.
|
||||||
@ -5541,15 +5541,14 @@ void StringAddStub::Generate(MacroAssembler* masm) {
|
|||||||
__ add(ebx, ecx);
|
__ add(ebx, ecx);
|
||||||
STATIC_ASSERT(Smi::kMaxValue == String::kMaxLength);
|
STATIC_ASSERT(Smi::kMaxValue == String::kMaxLength);
|
||||||
// Handle exceptionally long strings in the runtime system.
|
// Handle exceptionally long strings in the runtime system.
|
||||||
__ j(overflow, &string_add_runtime);
|
__ j(overflow, &call_runtime);
|
||||||
// Use the symbol table when adding two one character strings, as it
|
// Use the symbol table when adding two one character strings, as it
|
||||||
// helps later optimizations to return a symbol here.
|
// helps later optimizations to return a symbol here.
|
||||||
__ cmp(ebx, Immediate(Smi::FromInt(2)));
|
__ cmp(ebx, Immediate(Smi::FromInt(2)));
|
||||||
__ j(not_equal, &longer_than_two);
|
__ j(not_equal, &longer_than_two);
|
||||||
|
|
||||||
// Check that both strings are non-external ascii strings.
|
// Check that both strings are non-external ascii strings.
|
||||||
__ JumpIfNotBothSequentialAsciiStrings(eax, edx, ebx, ecx,
|
__ JumpIfNotBothSequentialAsciiStrings(eax, edx, ebx, ecx, &call_runtime);
|
||||||
&string_add_runtime);
|
|
||||||
|
|
||||||
// Get the two characters forming the new string.
|
// Get the two characters forming the new string.
|
||||||
__ movzx_b(ebx, FieldOperand(eax, SeqAsciiString::kHeaderSize));
|
__ movzx_b(ebx, FieldOperand(eax, SeqAsciiString::kHeaderSize));
|
||||||
@ -5574,11 +5573,7 @@ void StringAddStub::Generate(MacroAssembler* masm) {
|
|||||||
__ movzx_b(ecx, FieldOperand(edx, SeqAsciiString::kHeaderSize));
|
__ movzx_b(ecx, FieldOperand(edx, SeqAsciiString::kHeaderSize));
|
||||||
__ bind(&make_two_character_string_no_reload);
|
__ bind(&make_two_character_string_no_reload);
|
||||||
__ IncrementCounter(counters->string_add_make_two_char(), 1);
|
__ IncrementCounter(counters->string_add_make_two_char(), 1);
|
||||||
__ AllocateAsciiString(eax, // Result.
|
__ AllocateAsciiString(eax, 2, edi, edx, &call_runtime);
|
||||||
2, // Length.
|
|
||||||
edi, // Scratch 1.
|
|
||||||
edx, // Scratch 2.
|
|
||||||
&string_add_runtime);
|
|
||||||
// Pack both characters in ebx.
|
// Pack both characters in ebx.
|
||||||
__ shl(ecx, kBitsPerByte);
|
__ shl(ecx, kBitsPerByte);
|
||||||
__ or_(ebx, ecx);
|
__ or_(ebx, ecx);
|
||||||
@ -5606,7 +5601,7 @@ void StringAddStub::Generate(MacroAssembler* masm) {
|
|||||||
__ j(zero, &non_ascii);
|
__ j(zero, &non_ascii);
|
||||||
__ bind(&ascii_data);
|
__ bind(&ascii_data);
|
||||||
// Allocate an acsii cons string.
|
// Allocate an acsii cons string.
|
||||||
__ AllocateAsciiConsString(ecx, edi, no_reg, &string_add_runtime);
|
__ AllocateAsciiConsString(ecx, edi, no_reg, &call_runtime);
|
||||||
__ bind(&allocated);
|
__ bind(&allocated);
|
||||||
// Fill the fields of the cons string.
|
// Fill the fields of the cons string.
|
||||||
if (FLAG_debug_code) __ AbortIfNotSmi(ebx);
|
if (FLAG_debug_code) __ AbortIfNotSmi(ebx);
|
||||||
@ -5633,64 +5628,93 @@ void StringAddStub::Generate(MacroAssembler* masm) {
|
|||||||
__ cmp(edi, kAsciiStringTag | kAsciiDataHintTag);
|
__ cmp(edi, kAsciiStringTag | kAsciiDataHintTag);
|
||||||
__ j(equal, &ascii_data);
|
__ j(equal, &ascii_data);
|
||||||
// Allocate a two byte cons string.
|
// Allocate a two byte cons string.
|
||||||
__ AllocateTwoByteConsString(ecx, edi, no_reg, &string_add_runtime);
|
__ AllocateTwoByteConsString(ecx, edi, no_reg, &call_runtime);
|
||||||
__ jmp(&allocated);
|
__ jmp(&allocated);
|
||||||
|
|
||||||
// Handle creating a flat result. First check that both strings are not
|
// We cannot encounter sliced strings or cons strings here since:
|
||||||
// external strings.
|
STATIC_ASSERT(SlicedString::kMinLength >= String::kMinNonFlatLength);
|
||||||
|
// Handle creating a flat result from either external or sequential strings.
|
||||||
|
// Locate the first characters' locations.
|
||||||
// eax: first string
|
// eax: first string
|
||||||
// ebx: length of resulting flat string as a smi
|
// ebx: length of resulting flat string as a smi
|
||||||
// edx: second string
|
// edx: second string
|
||||||
|
Label first_prepared, second_prepared;
|
||||||
|
Label first_is_sequential, second_is_sequential;
|
||||||
__ bind(&string_add_flat_result);
|
__ bind(&string_add_flat_result);
|
||||||
__ mov(ecx, FieldOperand(eax, HeapObject::kMapOffset));
|
__ mov(ecx, FieldOperand(eax, HeapObject::kMapOffset));
|
||||||
__ movzx_b(ecx, FieldOperand(ecx, Map::kInstanceTypeOffset));
|
__ movzx_b(ecx, FieldOperand(ecx, Map::kInstanceTypeOffset));
|
||||||
__ and_(ecx, kStringRepresentationMask);
|
// ecx: instance type of first string
|
||||||
__ cmp(ecx, kExternalStringTag);
|
STATIC_ASSERT(kSeqStringTag == 0);
|
||||||
__ j(equal, &string_add_runtime);
|
__ test_b(ecx, kStringRepresentationMask);
|
||||||
__ mov(ecx, FieldOperand(edx, HeapObject::kMapOffset));
|
__ j(zero, &first_is_sequential, Label::kNear);
|
||||||
__ movzx_b(ecx, FieldOperand(ecx, Map::kInstanceTypeOffset));
|
// Rule out short external string and load string resource.
|
||||||
__ and_(ecx, kStringRepresentationMask);
|
STATIC_ASSERT(kShortExternalStringTag != 0);
|
||||||
__ cmp(ecx, kExternalStringTag);
|
__ test_b(ecx, kShortExternalStringMask);
|
||||||
__ j(equal, &string_add_runtime);
|
__ j(not_zero, &call_runtime);
|
||||||
// We cannot encounter sliced strings here since:
|
__ mov(eax, FieldOperand(eax, ExternalString::kResourceDataOffset));
|
||||||
STATIC_ASSERT(SlicedString::kMinLength >= String::kMinNonFlatLength);
|
STATIC_ASSERT(SeqAsciiString::kHeaderSize == SeqTwoByteString::kHeaderSize);
|
||||||
// Now check if both strings are ascii strings.
|
__ jmp(&first_prepared, Label::kNear);
|
||||||
// eax: first string
|
__ bind(&first_is_sequential);
|
||||||
// ebx: length of resulting flat string as a smi
|
__ add(eax, Immediate(SeqAsciiString::kHeaderSize - kHeapObjectTag));
|
||||||
// edx: second string
|
__ bind(&first_prepared);
|
||||||
Label non_ascii_string_add_flat_result;
|
|
||||||
STATIC_ASSERT((kStringEncodingMask & kAsciiStringTag) != 0);
|
|
||||||
STATIC_ASSERT((kStringEncodingMask & kTwoByteStringTag) == 0);
|
|
||||||
__ mov(ecx, FieldOperand(eax, HeapObject::kMapOffset));
|
|
||||||
__ test_b(FieldOperand(ecx, Map::kInstanceTypeOffset), kStringEncodingMask);
|
|
||||||
__ j(zero, &non_ascii_string_add_flat_result);
|
|
||||||
__ mov(ecx, FieldOperand(edx, HeapObject::kMapOffset));
|
|
||||||
__ test_b(FieldOperand(ecx, Map::kInstanceTypeOffset), kStringEncodingMask);
|
|
||||||
__ j(zero, &string_add_runtime);
|
|
||||||
|
|
||||||
// Both strings are ascii strings. As they are short they are both flat.
|
__ mov(edi, FieldOperand(edx, HeapObject::kMapOffset));
|
||||||
|
__ movzx_b(edi, FieldOperand(edi, Map::kInstanceTypeOffset));
|
||||||
|
// Check whether both strings have same encoding.
|
||||||
|
// edi: instance type of second string
|
||||||
|
__ xor_(ecx, edi);
|
||||||
|
__ test_b(ecx, kStringEncodingMask);
|
||||||
|
__ j(not_zero, &call_runtime);
|
||||||
|
STATIC_ASSERT(kSeqStringTag == 0);
|
||||||
|
__ test_b(edi, kStringRepresentationMask);
|
||||||
|
__ j(zero, &second_is_sequential, Label::kNear);
|
||||||
|
// Rule out short external string and load string resource.
|
||||||
|
STATIC_ASSERT(kShortExternalStringTag != 0);
|
||||||
|
__ test_b(edi, kShortExternalStringMask);
|
||||||
|
__ j(not_zero, &call_runtime);
|
||||||
|
__ mov(edx, FieldOperand(edx, ExternalString::kResourceDataOffset));
|
||||||
|
STATIC_ASSERT(SeqAsciiString::kHeaderSize == SeqTwoByteString::kHeaderSize);
|
||||||
|
__ jmp(&second_prepared, Label::kNear);
|
||||||
|
__ bind(&second_is_sequential);
|
||||||
|
__ add(edx, Immediate(SeqAsciiString::kHeaderSize - kHeapObjectTag));
|
||||||
|
__ bind(&second_prepared);
|
||||||
|
|
||||||
|
// Push the addresses of both strings' first characters onto the stack.
|
||||||
|
__ push(edx);
|
||||||
|
__ push(eax);
|
||||||
|
|
||||||
|
Label non_ascii_string_add_flat_result, call_runtime_drop_two;
|
||||||
|
// edi: instance type of second string
|
||||||
|
// First string and second string have the same encoding.
|
||||||
|
STATIC_ASSERT(kTwoByteStringTag == 0);
|
||||||
|
__ test_b(edi, kStringEncodingMask);
|
||||||
|
__ j(zero, &non_ascii_string_add_flat_result);
|
||||||
|
|
||||||
|
// Both strings are ascii strings.
|
||||||
// ebx: length of resulting flat string as a smi
|
// ebx: length of resulting flat string as a smi
|
||||||
__ SmiUntag(ebx);
|
__ SmiUntag(ebx);
|
||||||
__ AllocateAsciiString(eax, ebx, ecx, edx, edi, &string_add_runtime);
|
__ AllocateAsciiString(eax, ebx, ecx, edx, edi, &call_runtime_drop_two);
|
||||||
// eax: result string
|
// eax: result string
|
||||||
__ mov(ecx, eax);
|
__ mov(ecx, eax);
|
||||||
// Locate first character of result.
|
// Locate first character of result.
|
||||||
__ add(ecx, Immediate(SeqAsciiString::kHeaderSize - kHeapObjectTag));
|
__ add(ecx, Immediate(SeqAsciiString::kHeaderSize - kHeapObjectTag));
|
||||||
// Load first argument and locate first character.
|
// Load first argument's length and first character location. Account for
|
||||||
__ mov(edx, Operand(esp, 2 * kPointerSize));
|
// values currently on the stack when fetching arguments from it.
|
||||||
|
__ mov(edx, Operand(esp, 4 * kPointerSize));
|
||||||
__ mov(edi, FieldOperand(edx, String::kLengthOffset));
|
__ mov(edi, FieldOperand(edx, String::kLengthOffset));
|
||||||
__ SmiUntag(edi);
|
__ SmiUntag(edi);
|
||||||
__ add(edx, Immediate(SeqAsciiString::kHeaderSize - kHeapObjectTag));
|
__ pop(edx);
|
||||||
// eax: result string
|
// eax: result string
|
||||||
// ecx: first character of result
|
// ecx: first character of result
|
||||||
// edx: first char of first argument
|
// edx: first char of first argument
|
||||||
// edi: length of first argument
|
// edi: length of first argument
|
||||||
StringHelper::GenerateCopyCharacters(masm, ecx, edx, edi, ebx, true);
|
StringHelper::GenerateCopyCharacters(masm, ecx, edx, edi, ebx, true);
|
||||||
// Load second argument and locate first character.
|
// Load second argument's length and first character location. Account for
|
||||||
__ mov(edx, Operand(esp, 1 * kPointerSize));
|
// values currently on the stack when fetching arguments from it.
|
||||||
|
__ mov(edx, Operand(esp, 2 * kPointerSize));
|
||||||
__ mov(edi, FieldOperand(edx, String::kLengthOffset));
|
__ mov(edi, FieldOperand(edx, String::kLengthOffset));
|
||||||
__ SmiUntag(edi);
|
__ SmiUntag(edi);
|
||||||
__ add(edx, Immediate(SeqAsciiString::kHeaderSize - kHeapObjectTag));
|
__ pop(edx);
|
||||||
// eax: result string
|
// eax: result string
|
||||||
// ecx: next character of result
|
// ecx: next character of result
|
||||||
// edx: first char of second argument
|
// edx: first char of second argument
|
||||||
@ -5704,34 +5728,30 @@ void StringAddStub::Generate(MacroAssembler* masm) {
|
|||||||
// ebx: length of resulting flat string as a smi
|
// ebx: length of resulting flat string as a smi
|
||||||
// edx: second string
|
// edx: second string
|
||||||
__ bind(&non_ascii_string_add_flat_result);
|
__ bind(&non_ascii_string_add_flat_result);
|
||||||
__ mov(ecx, FieldOperand(edx, HeapObject::kMapOffset));
|
// Both strings are two byte strings.
|
||||||
__ test_b(FieldOperand(ecx, Map::kInstanceTypeOffset), kStringEncodingMask);
|
|
||||||
__ j(not_zero, &string_add_runtime);
|
|
||||||
// Both strings are two byte strings. As they are short they are both
|
|
||||||
// flat.
|
|
||||||
__ SmiUntag(ebx);
|
__ SmiUntag(ebx);
|
||||||
__ AllocateTwoByteString(eax, ebx, ecx, edx, edi, &string_add_runtime);
|
__ AllocateTwoByteString(eax, ebx, ecx, edx, edi, &call_runtime_drop_two);
|
||||||
// eax: result string
|
// eax: result string
|
||||||
__ mov(ecx, eax);
|
__ mov(ecx, eax);
|
||||||
// Locate first character of result.
|
// Locate first character of result.
|
||||||
__ add(ecx,
|
__ add(ecx, Immediate(SeqTwoByteString::kHeaderSize - kHeapObjectTag));
|
||||||
Immediate(SeqTwoByteString::kHeaderSize - kHeapObjectTag));
|
// Load second argument's length and first character location. Account for
|
||||||
// Load first argument and locate first character.
|
// values currently on the stack when fetching arguments from it.
|
||||||
__ mov(edx, Operand(esp, 2 * kPointerSize));
|
__ mov(edx, Operand(esp, 4 * kPointerSize));
|
||||||
__ mov(edi, FieldOperand(edx, String::kLengthOffset));
|
__ mov(edi, FieldOperand(edx, String::kLengthOffset));
|
||||||
__ SmiUntag(edi);
|
__ SmiUntag(edi);
|
||||||
__ add(edx,
|
__ pop(edx);
|
||||||
Immediate(SeqTwoByteString::kHeaderSize - kHeapObjectTag));
|
|
||||||
// eax: result string
|
// eax: result string
|
||||||
// ecx: first character of result
|
// ecx: first character of result
|
||||||
// edx: first char of first argument
|
// edx: first char of first argument
|
||||||
// edi: length of first argument
|
// edi: length of first argument
|
||||||
StringHelper::GenerateCopyCharacters(masm, ecx, edx, edi, ebx, false);
|
StringHelper::GenerateCopyCharacters(masm, ecx, edx, edi, ebx, false);
|
||||||
// Load second argument and locate first character.
|
// Load second argument's length and first character location. Account for
|
||||||
__ mov(edx, Operand(esp, 1 * kPointerSize));
|
// values currently on the stack when fetching arguments from it.
|
||||||
|
__ mov(edx, Operand(esp, 2 * kPointerSize));
|
||||||
__ mov(edi, FieldOperand(edx, String::kLengthOffset));
|
__ mov(edi, FieldOperand(edx, String::kLengthOffset));
|
||||||
__ SmiUntag(edi);
|
__ SmiUntag(edi);
|
||||||
__ add(edx, Immediate(SeqAsciiString::kHeaderSize - kHeapObjectTag));
|
__ pop(edx);
|
||||||
// eax: result string
|
// eax: result string
|
||||||
// ecx: next character of result
|
// ecx: next character of result
|
||||||
// edx: first char of second argument
|
// edx: first char of second argument
|
||||||
@ -5740,8 +5760,11 @@ void StringAddStub::Generate(MacroAssembler* masm) {
|
|||||||
__ IncrementCounter(counters->string_add_native(), 1);
|
__ IncrementCounter(counters->string_add_native(), 1);
|
||||||
__ ret(2 * kPointerSize);
|
__ ret(2 * kPointerSize);
|
||||||
|
|
||||||
|
// Recover stack pointer before jumping to runtime.
|
||||||
|
__ bind(&call_runtime_drop_two);
|
||||||
|
__ Drop(2);
|
||||||
// Just jump to runtime to add the two strings.
|
// Just jump to runtime to add the two strings.
|
||||||
__ bind(&string_add_runtime);
|
__ bind(&call_runtime);
|
||||||
__ TailCallRuntime(Runtime::kStringAdd, 2, 1);
|
__ TailCallRuntime(Runtime::kStringAdd, 2, 1);
|
||||||
|
|
||||||
if (call_builtin.is_linked()) {
|
if (call_builtin.is_linked()) {
|
||||||
@ -6120,20 +6143,20 @@ void SubStringStub::Generate(MacroAssembler* masm) {
|
|||||||
FieldOperand(eax, edx, times_1, SeqAsciiString::kHeaderSize + 1));
|
FieldOperand(eax, edx, times_1, SeqAsciiString::kHeaderSize + 1));
|
||||||
|
|
||||||
// Try to lookup two character string in symbol table.
|
// Try to lookup two character string in symbol table.
|
||||||
Label make_two_character_string;
|
Label combine_two_char, save_two_char;
|
||||||
StringHelper::GenerateTwoCharacterSymbolTableProbe(
|
StringHelper::GenerateTwoCharacterSymbolTableProbe(
|
||||||
masm, ebx, ecx, eax, edx, edi,
|
masm, ebx, ecx, eax, edx, edi, &combine_two_char, &save_two_char);
|
||||||
&make_two_character_string, &make_two_character_string);
|
|
||||||
__ IncrementCounter(counters->sub_string_native(), 1);
|
__ IncrementCounter(counters->sub_string_native(), 1);
|
||||||
__ ret(3 * kPointerSize);
|
__ ret(3 * kPointerSize);
|
||||||
|
|
||||||
__ bind(&make_two_character_string);
|
__ bind(&combine_two_char);
|
||||||
// Setup registers for allocating the two character string.
|
__ shl(ecx, kBitsPerByte);
|
||||||
__ mov(eax, Operand(esp, 3 * kPointerSize));
|
__ or_(ebx, ecx);
|
||||||
__ mov(ebx, FieldOperand(eax, HeapObject::kMapOffset));
|
__ bind(&save_two_char);
|
||||||
__ movzx_b(ebx, FieldOperand(ebx, Map::kInstanceTypeOffset));
|
__ AllocateAsciiString(eax, 2, ecx, edx, &runtime);
|
||||||
__ Set(ecx, Immediate(Smi::FromInt(2)));
|
__ mov_w(FieldOperand(eax, SeqAsciiString::kHeaderSize), ebx);
|
||||||
__ mov(edx, Operand(esp, 2 * kPointerSize)); // Load index.
|
__ IncrementCounter(counters->sub_string_native(), 1);
|
||||||
|
__ ret(3 * kPointerSize);
|
||||||
|
|
||||||
__ bind(&result_longer_than_two);
|
__ bind(&result_longer_than_two);
|
||||||
// eax: string
|
// eax: string
|
||||||
@ -6181,7 +6204,7 @@ void SubStringStub::Generate(MacroAssembler* masm) {
|
|||||||
if (FLAG_string_slices) {
|
if (FLAG_string_slices) {
|
||||||
Label copy_routine;
|
Label copy_routine;
|
||||||
// edi: underlying subject string
|
// edi: underlying subject string
|
||||||
// ebx: instance type of original subject string
|
// ebx: instance type of underlying subject string
|
||||||
// edx: adjusted start index (smi)
|
// edx: adjusted start index (smi)
|
||||||
// ecx: length (smi)
|
// ecx: length (smi)
|
||||||
__ cmp(ecx, Immediate(Smi::FromInt(SlicedString::kMinLength)));
|
__ cmp(ecx, Immediate(Smi::FromInt(SlicedString::kMinLength)));
|
||||||
@ -6214,7 +6237,7 @@ void SubStringStub::Generate(MacroAssembler* masm) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// edi: underlying subject string
|
// edi: underlying subject string
|
||||||
// ebx: instance type of original subject string
|
// ebx: instance type of underlying subject string
|
||||||
// edx: adjusted start index (smi)
|
// edx: adjusted start index (smi)
|
||||||
// ecx: length (smi)
|
// ecx: length (smi)
|
||||||
// The subject string can only be external or sequential string of either
|
// The subject string can only be external or sequential string of either
|
||||||
@ -6226,7 +6249,6 @@ void SubStringStub::Generate(MacroAssembler* masm) {
|
|||||||
__ j(zero, &sequential_string);
|
__ j(zero, &sequential_string);
|
||||||
|
|
||||||
// Handle external string.
|
// Handle external string.
|
||||||
Label ascii_external, done;
|
|
||||||
// Rule out short external strings.
|
// Rule out short external strings.
|
||||||
STATIC_CHECK(kShortExternalStringTag != 0);
|
STATIC_CHECK(kShortExternalStringTag != 0);
|
||||||
__ test_b(ebx, kShortExternalStringMask);
|
__ test_b(ebx, kShortExternalStringMask);
|
||||||
|
13
deps/v8/src/ia32/ic-ia32.cc
vendored
13
deps/v8/src/ia32/ic-ia32.cc
vendored
@ -1374,10 +1374,10 @@ void StoreIC::GenerateArrayLength(MacroAssembler* masm) {
|
|||||||
// -- esp[0] : return address
|
// -- esp[0] : return address
|
||||||
// -----------------------------------
|
// -----------------------------------
|
||||||
//
|
//
|
||||||
// This accepts as a receiver anything JSObject::SetElementsLength accepts
|
// This accepts as a receiver anything JSArray::SetElementsLength accepts
|
||||||
// (currently anything except for external arrays which means anything with
|
// (currently anything except for external arrays which means anything with
|
||||||
// elements of FixedArray type.), but currently is restricted to JSArray.
|
// elements of FixedArray type). Value must be a number, but only smis are
|
||||||
// Value must be a number, but only smis are accepted as the most common case.
|
// accepted as the most common case.
|
||||||
|
|
||||||
Label miss;
|
Label miss;
|
||||||
|
|
||||||
@ -1399,6 +1399,13 @@ void StoreIC::GenerateArrayLength(MacroAssembler* masm) {
|
|||||||
__ CmpObjectType(scratch, FIXED_ARRAY_TYPE, scratch);
|
__ CmpObjectType(scratch, FIXED_ARRAY_TYPE, scratch);
|
||||||
__ j(not_equal, &miss);
|
__ j(not_equal, &miss);
|
||||||
|
|
||||||
|
// Check that the array has fast properties, otherwise the length
|
||||||
|
// property might have been redefined.
|
||||||
|
__ mov(scratch, FieldOperand(receiver, JSArray::kPropertiesOffset));
|
||||||
|
__ CompareRoot(FieldOperand(scratch, FixedArray::kMapOffset),
|
||||||
|
Heap::kHashTableMapRootIndex);
|
||||||
|
__ j(equal, &miss);
|
||||||
|
|
||||||
// Check that value is a smi.
|
// Check that value is a smi.
|
||||||
__ JumpIfNotSmi(value, &miss);
|
__ JumpIfNotSmi(value, &miss);
|
||||||
|
|
||||||
|
22
deps/v8/src/ia32/lithium-codegen-ia32.cc
vendored
22
deps/v8/src/ia32/lithium-codegen-ia32.cc
vendored
@ -2165,9 +2165,17 @@ void LCodeGen::DoLoadContextSlot(LLoadContextSlot* instr) {
|
|||||||
Register context = ToRegister(instr->context());
|
Register context = ToRegister(instr->context());
|
||||||
Register result = ToRegister(instr->result());
|
Register result = ToRegister(instr->result());
|
||||||
__ mov(result, ContextOperand(context, instr->slot_index()));
|
__ mov(result, ContextOperand(context, instr->slot_index()));
|
||||||
|
|
||||||
if (instr->hydrogen()->RequiresHoleCheck()) {
|
if (instr->hydrogen()->RequiresHoleCheck()) {
|
||||||
__ cmp(result, factory()->the_hole_value());
|
__ cmp(result, factory()->the_hole_value());
|
||||||
DeoptimizeIf(equal, instr->environment());
|
if (instr->hydrogen()->DeoptimizesOnHole()) {
|
||||||
|
DeoptimizeIf(equal, instr->environment());
|
||||||
|
} else {
|
||||||
|
Label is_not_hole;
|
||||||
|
__ j(not_equal, &is_not_hole, Label::kNear);
|
||||||
|
__ mov(result, factory()->undefined_value());
|
||||||
|
__ bind(&is_not_hole);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -2175,11 +2183,19 @@ void LCodeGen::DoLoadContextSlot(LLoadContextSlot* instr) {
|
|||||||
void LCodeGen::DoStoreContextSlot(LStoreContextSlot* instr) {
|
void LCodeGen::DoStoreContextSlot(LStoreContextSlot* instr) {
|
||||||
Register context = ToRegister(instr->context());
|
Register context = ToRegister(instr->context());
|
||||||
Register value = ToRegister(instr->value());
|
Register value = ToRegister(instr->value());
|
||||||
|
|
||||||
|
Label skip_assignment;
|
||||||
|
|
||||||
Operand target = ContextOperand(context, instr->slot_index());
|
Operand target = ContextOperand(context, instr->slot_index());
|
||||||
if (instr->hydrogen()->RequiresHoleCheck()) {
|
if (instr->hydrogen()->RequiresHoleCheck()) {
|
||||||
__ cmp(target, factory()->the_hole_value());
|
__ cmp(target, factory()->the_hole_value());
|
||||||
DeoptimizeIf(equal, instr->environment());
|
if (instr->hydrogen()->DeoptimizesOnHole()) {
|
||||||
|
DeoptimizeIf(equal, instr->environment());
|
||||||
|
} else {
|
||||||
|
__ j(not_equal, &skip_assignment, Label::kNear);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
__ mov(target, value);
|
__ mov(target, value);
|
||||||
if (instr->hydrogen()->NeedsWriteBarrier()) {
|
if (instr->hydrogen()->NeedsWriteBarrier()) {
|
||||||
HType type = instr->hydrogen()->value()->type();
|
HType type = instr->hydrogen()->value()->type();
|
||||||
@ -2195,6 +2211,8 @@ void LCodeGen::DoStoreContextSlot(LStoreContextSlot* instr) {
|
|||||||
EMIT_REMEMBERED_SET,
|
EMIT_REMEMBERED_SET,
|
||||||
check_needed);
|
check_needed);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
__ bind(&skip_assignment);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
8
deps/v8/src/ia32/macro-assembler-ia32.cc
vendored
8
deps/v8/src/ia32/macro-assembler-ia32.cc
vendored
@ -357,6 +357,14 @@ void MacroAssembler::CompareRoot(Register with, Heap::RootListIndex index) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void MacroAssembler::CompareRoot(const Operand& with,
|
||||||
|
Heap::RootListIndex index) {
|
||||||
|
// see ROOT_ACCESSOR macro in factory.h
|
||||||
|
Handle<Object> value(&isolate()->heap()->roots_array_start()[index]);
|
||||||
|
cmp(with, value);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
void MacroAssembler::CmpObjectType(Register heap_object,
|
void MacroAssembler::CmpObjectType(Register heap_object,
|
||||||
InstanceType type,
|
InstanceType type,
|
||||||
Register map) {
|
Register map) {
|
||||||
|
3
deps/v8/src/ia32/macro-assembler-ia32.h
vendored
3
deps/v8/src/ia32/macro-assembler-ia32.h
vendored
@ -308,8 +308,9 @@ class MacroAssembler: public Assembler {
|
|||||||
void SafeSet(Register dst, const Immediate& x);
|
void SafeSet(Register dst, const Immediate& x);
|
||||||
void SafePush(const Immediate& x);
|
void SafePush(const Immediate& x);
|
||||||
|
|
||||||
// Compare a register against a known root, e.g. undefined, null, true, ...
|
// Compare against a known root, e.g. undefined, null, true, ...
|
||||||
void CompareRoot(Register with, Heap::RootListIndex index);
|
void CompareRoot(Register with, Heap::RootListIndex index);
|
||||||
|
void CompareRoot(const Operand& with, Heap::RootListIndex index);
|
||||||
|
|
||||||
// Compare object type for heap object.
|
// Compare object type for heap object.
|
||||||
// Incoming register is heap_object and outgoing register is map.
|
// Incoming register is heap_object and outgoing register is map.
|
||||||
|
20
deps/v8/src/ic.cc
vendored
20
deps/v8/src/ic.cc
vendored
@ -1272,10 +1272,13 @@ MaybeObject* StoreIC::Store(State state,
|
|||||||
return *value;
|
return *value;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Use specialized code for setting the length of arrays.
|
// Use specialized code for setting the length of arrays with fast
|
||||||
if (receiver->IsJSArray()
|
// properties. Slow properties might indicate redefinition of the
|
||||||
&& name->Equals(isolate()->heap()->length_symbol())
|
// length property.
|
||||||
&& Handle<JSArray>::cast(receiver)->AllowsSetElementsLength()) {
|
if (receiver->IsJSArray() &&
|
||||||
|
name->Equals(isolate()->heap()->length_symbol()) &&
|
||||||
|
Handle<JSArray>::cast(receiver)->AllowsSetElementsLength() &&
|
||||||
|
receiver->HasFastProperties()) {
|
||||||
#ifdef DEBUG
|
#ifdef DEBUG
|
||||||
if (FLAG_trace_ic) PrintF("[StoreIC : +#length /array]\n");
|
if (FLAG_trace_ic) PrintF("[StoreIC : +#length /array]\n");
|
||||||
#endif
|
#endif
|
||||||
@ -1879,12 +1882,19 @@ RUNTIME_FUNCTION(MaybeObject*, StoreIC_ArrayLength) {
|
|||||||
NoHandleAllocation nha;
|
NoHandleAllocation nha;
|
||||||
|
|
||||||
ASSERT(args.length() == 2);
|
ASSERT(args.length() == 2);
|
||||||
JSObject* receiver = JSObject::cast(args[0]);
|
JSArray* receiver = JSArray::cast(args[0]);
|
||||||
Object* len = args[1];
|
Object* len = args[1];
|
||||||
|
|
||||||
// The generated code should filter out non-Smis before we get here.
|
// The generated code should filter out non-Smis before we get here.
|
||||||
ASSERT(len->IsSmi());
|
ASSERT(len->IsSmi());
|
||||||
|
|
||||||
|
#ifdef DEBUG
|
||||||
|
// The length property has to be a writable callback property.
|
||||||
|
LookupResult debug_lookup(isolate);
|
||||||
|
receiver->LocalLookup(isolate->heap()->length_symbol(), &debug_lookup);
|
||||||
|
ASSERT(debug_lookup.type() == CALLBACKS && !debug_lookup.IsReadOnly());
|
||||||
|
#endif
|
||||||
|
|
||||||
Object* result;
|
Object* result;
|
||||||
{ MaybeObject* maybe_result = receiver->SetElementsLength(len);
|
{ MaybeObject* maybe_result = receiver->SetElementsLength(len);
|
||||||
if (!maybe_result->ToObject(&result)) return maybe_result;
|
if (!maybe_result->ToObject(&result)) return maybe_result;
|
||||||
|
15
deps/v8/src/incremental-marking.cc
vendored
15
deps/v8/src/incremental-marking.cc
vendored
@ -677,11 +677,16 @@ void IncrementalMarking::Hurry() {
|
|||||||
|
|
||||||
Object* context = heap_->global_contexts_list();
|
Object* context = heap_->global_contexts_list();
|
||||||
while (!context->IsUndefined()) {
|
while (!context->IsUndefined()) {
|
||||||
NormalizedMapCache* cache = Context::cast(context)->normalized_map_cache();
|
// GC can happen when the context is not fully initialized,
|
||||||
MarkBit mark_bit = Marking::MarkBitFrom(cache);
|
// so the cache can be undefined.
|
||||||
if (Marking::IsGrey(mark_bit)) {
|
HeapObject* cache = HeapObject::cast(
|
||||||
Marking::GreyToBlack(mark_bit);
|
Context::cast(context)->get(Context::NORMALIZED_MAP_CACHE_INDEX));
|
||||||
MemoryChunk::IncrementLiveBytes(cache->address(), cache->Size());
|
if (!cache->IsUndefined()) {
|
||||||
|
MarkBit mark_bit = Marking::MarkBitFrom(cache);
|
||||||
|
if (Marking::IsGrey(mark_bit)) {
|
||||||
|
Marking::GreyToBlack(mark_bit);
|
||||||
|
MemoryChunk::IncrementLiveBytes(cache->address(), cache->Size());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
context = Context::cast(context)->get(Context::NEXT_CONTEXT_LINK);
|
context = Context::cast(context)->get(Context::NEXT_CONTEXT_LINK);
|
||||||
}
|
}
|
||||||
|
16
deps/v8/src/mark-compact.cc
vendored
16
deps/v8/src/mark-compact.cc
vendored
@ -3623,14 +3623,6 @@ void MarkCompactCollector::SweepSpace(PagedSpace* space, SweeperType sweeper) {
|
|||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (lazy_sweeping_active) {
|
|
||||||
if (FLAG_gc_verbose) {
|
|
||||||
PrintF("Sweeping 0x%" V8PRIxPTR " lazily postponed.\n",
|
|
||||||
reinterpret_cast<intptr_t>(p));
|
|
||||||
}
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
// One unused page is kept, all further are released before sweeping them.
|
// One unused page is kept, all further are released before sweeping them.
|
||||||
if (p->LiveBytes() == 0) {
|
if (p->LiveBytes() == 0) {
|
||||||
if (unused_page_present) {
|
if (unused_page_present) {
|
||||||
@ -3644,6 +3636,14 @@ void MarkCompactCollector::SweepSpace(PagedSpace* space, SweeperType sweeper) {
|
|||||||
unused_page_present = true;
|
unused_page_present = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (lazy_sweeping_active) {
|
||||||
|
if (FLAG_gc_verbose) {
|
||||||
|
PrintF("Sweeping 0x%" V8PRIxPTR " lazily postponed.\n",
|
||||||
|
reinterpret_cast<intptr_t>(p));
|
||||||
|
}
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
switch (sweeper) {
|
switch (sweeper) {
|
||||||
case CONSERVATIVE: {
|
case CONSERVATIVE: {
|
||||||
if (FLAG_gc_verbose) {
|
if (FLAG_gc_verbose) {
|
||||||
|
506
deps/v8/src/mips/code-stubs-mips.cc
vendored
506
deps/v8/src/mips/code-stubs-mips.cc
vendored
@ -5972,7 +5972,7 @@ void StringHelper::GenerateHashGetHash(MacroAssembler* masm,
|
|||||||
|
|
||||||
|
|
||||||
void SubStringStub::Generate(MacroAssembler* masm) {
|
void SubStringStub::Generate(MacroAssembler* masm) {
|
||||||
Label sub_string_runtime;
|
Label runtime;
|
||||||
// Stack frame on entry.
|
// Stack frame on entry.
|
||||||
// ra: return address
|
// ra: return address
|
||||||
// sp[0]: to
|
// sp[0]: to
|
||||||
@ -5990,53 +5990,35 @@ void SubStringStub::Generate(MacroAssembler* masm) {
|
|||||||
static const int kFromOffset = 1 * kPointerSize;
|
static const int kFromOffset = 1 * kPointerSize;
|
||||||
static const int kStringOffset = 2 * kPointerSize;
|
static const int kStringOffset = 2 * kPointerSize;
|
||||||
|
|
||||||
Register to = t2;
|
__ lw(a2, MemOperand(sp, kToOffset));
|
||||||
Register from = t3;
|
__ lw(a3, MemOperand(sp, kFromOffset));
|
||||||
|
|
||||||
// Check bounds and smi-ness.
|
|
||||||
__ lw(to, MemOperand(sp, kToOffset));
|
|
||||||
__ lw(from, MemOperand(sp, kFromOffset));
|
|
||||||
STATIC_ASSERT(kFromOffset == kToOffset + 4);
|
STATIC_ASSERT(kFromOffset == kToOffset + 4);
|
||||||
STATIC_ASSERT(kSmiTag == 0);
|
STATIC_ASSERT(kSmiTag == 0);
|
||||||
STATIC_ASSERT(kSmiTagSize + kSmiShiftSize == 1);
|
STATIC_ASSERT(kSmiTagSize + kSmiShiftSize == 1);
|
||||||
|
|
||||||
__ JumpIfNotSmi(from, &sub_string_runtime);
|
// Utilize delay slots. SmiUntag doesn't emit a jump, everything else is
|
||||||
__ JumpIfNotSmi(to, &sub_string_runtime);
|
// safe in this case.
|
||||||
|
__ JumpIfSmi(a2, &runtime, at, USE_DELAY_SLOT);
|
||||||
|
__ SmiUntag(a2);
|
||||||
|
__ JumpIfSmi(a3, &runtime, at, USE_DELAY_SLOT);
|
||||||
|
__ SmiUntag(a3);
|
||||||
|
|
||||||
__ sra(a3, from, kSmiTagSize); // Remove smi tag.
|
// Both a2 and a3 are untagged integers.
|
||||||
__ sra(t5, to, kSmiTagSize); // Remove smi tag.
|
|
||||||
|
|
||||||
// a3: from index (untagged smi)
|
__ Branch(&runtime, lt, a3, Operand(zero_reg)); // From < 0.
|
||||||
// t5: to index (untagged smi)
|
|
||||||
|
|
||||||
__ Branch(&sub_string_runtime, lt, a3, Operand(zero_reg)); // From < 0.
|
|
||||||
|
|
||||||
__ subu(a2, t5, a3);
|
__ subu(a2, t5, a3);
|
||||||
__ Branch(&sub_string_runtime, gt, a3, Operand(t5)); // Fail if from > to.
|
__ Branch(&runtime, gt, a3, Operand(t5)); // Fail if from > to.
|
||||||
|
|
||||||
// Special handling of sub-strings of length 1 and 2. One character strings
|
// Make sure first argument is a string.
|
||||||
// are handled in the runtime system (looked up in the single character
|
|
||||||
// cache). Two character strings are looked for in the symbol cache in
|
|
||||||
// generated code.
|
|
||||||
__ Branch(&sub_string_runtime, lt, a2, Operand(2));
|
|
||||||
|
|
||||||
// Both to and from are smis.
|
|
||||||
|
|
||||||
// a2: result string length
|
|
||||||
// a3: from index (untagged smi)
|
|
||||||
// t2: (a.k.a. to): to (smi)
|
|
||||||
// t3: (a.k.a. from): from offset (smi)
|
|
||||||
// t5: to index (untagged smi)
|
|
||||||
|
|
||||||
// Make sure first argument is a sequential (or flat) string.
|
|
||||||
__ lw(v0, MemOperand(sp, kStringOffset));
|
__ lw(v0, MemOperand(sp, kStringOffset));
|
||||||
__ Branch(&sub_string_runtime, eq, v0, Operand(kSmiTagMask));
|
__ Branch(&runtime, eq, v0, Operand(kSmiTagMask));
|
||||||
|
|
||||||
__ lw(a1, FieldMemOperand(v0, HeapObject::kMapOffset));
|
__ lw(a1, FieldMemOperand(v0, HeapObject::kMapOffset));
|
||||||
__ lbu(a1, FieldMemOperand(a1, Map::kInstanceTypeOffset));
|
__ lbu(a1, FieldMemOperand(a1, Map::kInstanceTypeOffset));
|
||||||
__ And(t4, v0, Operand(kIsNotStringMask));
|
__ And(t4, v0, Operand(kIsNotStringMask));
|
||||||
|
|
||||||
__ Branch(&sub_string_runtime, ne, t4, Operand(zero_reg));
|
__ Branch(&runtime, ne, t4, Operand(zero_reg));
|
||||||
|
|
||||||
// Short-cut for the case of trivial substring.
|
// Short-cut for the case of trivial substring.
|
||||||
Label return_v0;
|
Label return_v0;
|
||||||
@ -6046,74 +6028,16 @@ void SubStringStub::Generate(MacroAssembler* masm) {
|
|||||||
__ sra(t0, t0, 1);
|
__ sra(t0, t0, 1);
|
||||||
__ Branch(&return_v0, eq, a2, Operand(t0));
|
__ Branch(&return_v0, eq, a2, Operand(t0));
|
||||||
|
|
||||||
Label create_slice;
|
|
||||||
if (FLAG_string_slices) {
|
|
||||||
__ Branch(&create_slice, ge, a2, Operand(SlicedString::kMinLength));
|
|
||||||
}
|
|
||||||
|
|
||||||
// v0: original string
|
|
||||||
// a1: instance type
|
|
||||||
// a2: result string length
|
|
||||||
// a3: from index (untagged smi)
|
|
||||||
// t2: (a.k.a. to): to (smi)
|
|
||||||
// t3: (a.k.a. from): from offset (smi)
|
|
||||||
// t5: to index (untagged smi)
|
|
||||||
|
|
||||||
Label seq_string;
|
|
||||||
__ And(t0, a1, Operand(kStringRepresentationMask));
|
|
||||||
STATIC_ASSERT(kSeqStringTag < kConsStringTag);
|
|
||||||
STATIC_ASSERT(kConsStringTag < kExternalStringTag);
|
|
||||||
STATIC_ASSERT(kConsStringTag < kSlicedStringTag);
|
|
||||||
|
|
||||||
// Slices and external strings go to runtime.
|
|
||||||
__ Branch(&sub_string_runtime, gt, t0, Operand(kConsStringTag));
|
|
||||||
|
|
||||||
// Sequential strings are handled directly.
|
|
||||||
__ Branch(&seq_string, lt, t0, Operand(kConsStringTag));
|
|
||||||
|
|
||||||
// Cons string. Try to recurse (once) on the first substring.
|
|
||||||
// (This adds a little more generality than necessary to handle flattened
|
|
||||||
// cons strings, but not much).
|
|
||||||
__ lw(v0, FieldMemOperand(v0, ConsString::kFirstOffset));
|
|
||||||
__ lw(t0, FieldMemOperand(v0, HeapObject::kMapOffset));
|
|
||||||
__ lbu(a1, FieldMemOperand(t0, Map::kInstanceTypeOffset));
|
|
||||||
STATIC_ASSERT(kSeqStringTag == 0);
|
|
||||||
// Cons, slices and external strings go to runtime.
|
|
||||||
__ Branch(&sub_string_runtime, ne, a1, Operand(kStringRepresentationMask));
|
|
||||||
|
|
||||||
// Definitly a sequential string.
|
|
||||||
__ bind(&seq_string);
|
|
||||||
|
|
||||||
// v0: original string
|
|
||||||
// a1: instance type
|
|
||||||
// a2: result string length
|
|
||||||
// a3: from index (untagged smi)
|
|
||||||
// t2: (a.k.a. to): to (smi)
|
|
||||||
// t3: (a.k.a. from): from offset (smi)
|
|
||||||
// t5: to index (untagged smi)
|
|
||||||
|
|
||||||
__ lw(t0, FieldMemOperand(v0, String::kLengthOffset));
|
|
||||||
__ Branch(&sub_string_runtime, lt, t0, Operand(to)); // Fail if to > length.
|
|
||||||
to = no_reg;
|
|
||||||
|
|
||||||
// v0: original string or left hand side of the original cons string.
|
|
||||||
// a1: instance type
|
|
||||||
// a2: result string length
|
|
||||||
// a3: from index (untagged smi)
|
|
||||||
// t3: (a.k.a. from): from offset (smi)
|
|
||||||
// t5: to index (untagged smi)
|
|
||||||
|
|
||||||
// Check for flat ASCII string.
|
|
||||||
Label non_ascii_flat;
|
|
||||||
STATIC_ASSERT(kTwoByteStringTag == 0);
|
|
||||||
|
|
||||||
__ And(t4, a1, Operand(kStringEncodingMask));
|
|
||||||
__ Branch(&non_ascii_flat, eq, t4, Operand(zero_reg));
|
|
||||||
|
|
||||||
Label result_longer_than_two;
|
Label result_longer_than_two;
|
||||||
__ Branch(&result_longer_than_two, gt, a2, Operand(2));
|
// Check for special case of two character ascii string, in which case
|
||||||
|
// we do a lookup in the symbol table first.
|
||||||
|
__ li(t0, 2);
|
||||||
|
__ Branch(&result_longer_than_two, gt, a2, Operand(t0));
|
||||||
|
__ Branch(&runtime, lt, a2, Operand(t0));
|
||||||
|
|
||||||
|
__ JumpIfInstanceTypeIsNotSequentialAscii(a1, a1, &runtime);
|
||||||
|
|
||||||
// Sub string of length 2 requested.
|
|
||||||
// Get the two characters forming the sub string.
|
// Get the two characters forming the sub string.
|
||||||
__ Addu(v0, v0, Operand(a3));
|
__ Addu(v0, v0, Operand(a3));
|
||||||
__ lbu(a3, FieldMemOperand(v0, SeqAsciiString::kHeaderSize));
|
__ lbu(a3, FieldMemOperand(v0, SeqAsciiString::kHeaderSize));
|
||||||
@ -6123,31 +6047,126 @@ void SubStringStub::Generate(MacroAssembler* masm) {
|
|||||||
Label make_two_character_string;
|
Label make_two_character_string;
|
||||||
StringHelper::GenerateTwoCharacterSymbolTableProbe(
|
StringHelper::GenerateTwoCharacterSymbolTableProbe(
|
||||||
masm, a3, t0, a1, t1, t2, t3, t4, &make_two_character_string);
|
masm, a3, t0, a1, t1, t2, t3, t4, &make_two_character_string);
|
||||||
Counters* counters = masm->isolate()->counters();
|
|
||||||
__ jmp(&return_v0);
|
__ jmp(&return_v0);
|
||||||
|
|
||||||
// a2: result string length.
|
// a2: result string length.
|
||||||
// a3: two characters combined into halfword in little endian byte order.
|
// a3: two characters combined into halfword in little endian byte order.
|
||||||
__ bind(&make_two_character_string);
|
__ bind(&make_two_character_string);
|
||||||
__ AllocateAsciiString(v0, a2, t0, t1, t4, &sub_string_runtime);
|
__ AllocateAsciiString(v0, a2, t0, t1, t4, &runtime);
|
||||||
__ sh(a3, FieldMemOperand(v0, SeqAsciiString::kHeaderSize));
|
__ sh(a3, FieldMemOperand(v0, SeqAsciiString::kHeaderSize));
|
||||||
__ jmp(&return_v0);
|
__ jmp(&return_v0);
|
||||||
|
|
||||||
__ bind(&result_longer_than_two);
|
__ bind(&result_longer_than_two);
|
||||||
|
|
||||||
// Locate 'from' character of string.
|
// Deal with different string types: update the index if necessary
|
||||||
__ Addu(t1, v0, Operand(SeqAsciiString::kHeaderSize - kHeapObjectTag));
|
// and put the underlying string into t1.
|
||||||
__ sra(t4, from, 1);
|
// v0: original string
|
||||||
__ Addu(t1, t1, t4);
|
// a1: instance type
|
||||||
|
// a2: length
|
||||||
|
// a3: from index (untagged)
|
||||||
|
Label underlying_unpacked, sliced_string, seq_or_external_string;
|
||||||
|
// If the string is not indirect, it can only be sequential or external.
|
||||||
|
STATIC_ASSERT(kIsIndirectStringMask == (kSlicedStringTag & kConsStringTag));
|
||||||
|
STATIC_ASSERT(kIsIndirectStringMask != 0);
|
||||||
|
__ And(t0, a1, Operand(kIsIndirectStringMask));
|
||||||
|
__ Branch(USE_DELAY_SLOT, &seq_or_external_string, eq, t0, Operand(zero_reg));
|
||||||
|
|
||||||
// Allocate the result.
|
__ And(t0, a1, Operand(kSlicedNotConsMask));
|
||||||
__ AllocateAsciiString(v0, a2, t4, t0, a1, &sub_string_runtime);
|
__ Branch(&sliced_string, ne, t0, Operand(zero_reg));
|
||||||
|
// Cons string. Check whether it is flat, then fetch first part.
|
||||||
|
__ lw(t1, FieldMemOperand(v0, ConsString::kSecondOffset));
|
||||||
|
__ LoadRoot(t0, Heap::kEmptyStringRootIndex);
|
||||||
|
__ Branch(&runtime, ne, t1, Operand(t0));
|
||||||
|
__ lw(t1, FieldMemOperand(v0, ConsString::kFirstOffset));
|
||||||
|
// Update instance type.
|
||||||
|
__ lw(a1, FieldMemOperand(t1, HeapObject::kMapOffset));
|
||||||
|
__ lbu(a1, FieldMemOperand(a1, Map::kInstanceTypeOffset));
|
||||||
|
__ jmp(&underlying_unpacked);
|
||||||
|
|
||||||
|
__ bind(&sliced_string);
|
||||||
|
// Sliced string. Fetch parent and correct start index by offset.
|
||||||
|
__ lw(t1, FieldMemOperand(v0, SlicedString::kOffsetOffset));
|
||||||
|
__ sra(t1, t1, 1);
|
||||||
|
__ Addu(a3, a3, t1);
|
||||||
|
__ lw(t1, FieldMemOperand(v0, SlicedString::kParentOffset));
|
||||||
|
// Update instance type.
|
||||||
|
__ lw(a1, FieldMemOperand(t1, HeapObject::kMapOffset));
|
||||||
|
__ lbu(a1, FieldMemOperand(a1, Map::kInstanceTypeOffset));
|
||||||
|
__ jmp(&underlying_unpacked);
|
||||||
|
|
||||||
|
__ bind(&seq_or_external_string);
|
||||||
|
// Sequential or external string. Just move string to the expected register.
|
||||||
|
__ mov(t1, v0);
|
||||||
|
|
||||||
|
__ bind(&underlying_unpacked);
|
||||||
|
|
||||||
|
if (FLAG_string_slices) {
|
||||||
|
Label copy_routine;
|
||||||
|
// t1: underlying subject string
|
||||||
|
// a1: instance type of underlying subject string
|
||||||
|
// a2: length
|
||||||
|
// a3: adjusted start index (untagged)
|
||||||
|
// Short slice. Copy instead of slicing.
|
||||||
|
__ Branch(©_routine, lt, a2, Operand(SlicedString::kMinLength));
|
||||||
|
// Allocate new sliced string. At this point we do not reload the instance
|
||||||
|
// type including the string encoding because we simply rely on the info
|
||||||
|
// provided by the original string. It does not matter if the original
|
||||||
|
// string's encoding is wrong because we always have to recheck encoding of
|
||||||
|
// the newly created string's parent anyways due to externalized strings.
|
||||||
|
Label two_byte_slice, set_slice_header;
|
||||||
|
STATIC_ASSERT((kStringEncodingMask & kAsciiStringTag) != 0);
|
||||||
|
STATIC_ASSERT((kStringEncodingMask & kTwoByteStringTag) == 0);
|
||||||
|
__ And(t0, a1, Operand(kStringEncodingMask));
|
||||||
|
__ Branch(&two_byte_slice, eq, t0, Operand(zero_reg));
|
||||||
|
__ AllocateAsciiSlicedString(v0, a2, t2, t3, &runtime);
|
||||||
|
__ jmp(&set_slice_header);
|
||||||
|
__ bind(&two_byte_slice);
|
||||||
|
__ AllocateTwoByteSlicedString(v0, a2, t2, t3, &runtime);
|
||||||
|
__ bind(&set_slice_header);
|
||||||
|
__ sll(a3, a3, 1);
|
||||||
|
__ sw(a3, FieldMemOperand(v0, SlicedString::kOffsetOffset));
|
||||||
|
__ sw(t1, FieldMemOperand(v0, SlicedString::kParentOffset));
|
||||||
|
__ jmp(&return_v0);
|
||||||
|
|
||||||
|
__ bind(©_routine);
|
||||||
|
}
|
||||||
|
|
||||||
|
// t1: underlying subject string
|
||||||
|
// a1: instance type of underlying subject string
|
||||||
|
// a2: length
|
||||||
|
// a3: adjusted start index (untagged)
|
||||||
|
Label two_byte_sequential, sequential_string, allocate_result;
|
||||||
|
STATIC_ASSERT(kExternalStringTag != 0);
|
||||||
|
STATIC_ASSERT(kSeqStringTag == 0);
|
||||||
|
__ And(t0, a1, Operand(kExternalStringTag));
|
||||||
|
__ Branch(&sequential_string, eq, t0, Operand(zero_reg));
|
||||||
|
|
||||||
|
// Handle external string.
|
||||||
|
// Rule out short external strings.
|
||||||
|
STATIC_CHECK(kShortExternalStringTag != 0);
|
||||||
|
__ And(t0, a1, Operand(kShortExternalStringTag));
|
||||||
|
__ Branch(&runtime, ne, t0, Operand(zero_reg));
|
||||||
|
__ lw(t1, FieldMemOperand(t1, ExternalString::kResourceDataOffset));
|
||||||
|
// t1 already points to the first character of underlying string.
|
||||||
|
__ jmp(&allocate_result);
|
||||||
|
|
||||||
|
__ bind(&sequential_string);
|
||||||
|
// Locate first character of underlying subject string.
|
||||||
|
STATIC_ASSERT(SeqTwoByteString::kHeaderSize == SeqAsciiString::kHeaderSize);
|
||||||
|
__ Addu(t1, t1, Operand(SeqAsciiString::kHeaderSize - kHeapObjectTag));
|
||||||
|
|
||||||
|
__ bind(&allocate_result);
|
||||||
|
// Sequential acii string. Allocate the result.
|
||||||
|
STATIC_ASSERT((kAsciiStringTag & kStringEncodingMask) != 0);
|
||||||
|
__ And(t0, a1, Operand(kStringEncodingMask));
|
||||||
|
__ Branch(&two_byte_sequential, eq, t0, Operand(zero_reg));
|
||||||
|
|
||||||
|
// Allocate and copy the resulting ascii string.
|
||||||
|
__ AllocateAsciiString(v0, a2, t0, t2, t3, &runtime);
|
||||||
|
|
||||||
|
// Locate first character of substring to copy.
|
||||||
|
__ Addu(t1, t1, a3);
|
||||||
|
|
||||||
// v0: result string
|
|
||||||
// a2: result string length
|
|
||||||
// a3: from index (untagged smi)
|
|
||||||
// t1: first character of substring to copy
|
|
||||||
// t3: (a.k.a. from): from offset (smi)
|
|
||||||
// Locate first character of result.
|
// Locate first character of result.
|
||||||
__ Addu(a1, v0, Operand(SeqAsciiString::kHeaderSize - kHeapObjectTag));
|
__ Addu(a1, v0, Operand(SeqAsciiString::kHeaderSize - kHeapObjectTag));
|
||||||
|
|
||||||
@ -6160,30 +6179,17 @@ void SubStringStub::Generate(MacroAssembler* masm) {
|
|||||||
masm, a1, t1, a2, a3, t0, t2, t3, t4, COPY_ASCII | DEST_ALWAYS_ALIGNED);
|
masm, a1, t1, a2, a3, t0, t2, t3, t4, COPY_ASCII | DEST_ALWAYS_ALIGNED);
|
||||||
__ jmp(&return_v0);
|
__ jmp(&return_v0);
|
||||||
|
|
||||||
__ bind(&non_ascii_flat);
|
// Allocate and copy the resulting two-byte string.
|
||||||
// a2: result string length
|
__ bind(&two_byte_sequential);
|
||||||
// t1: string
|
__ AllocateTwoByteString(v0, a2, t0, t2, t3, &runtime);
|
||||||
// t3: (a.k.a. from): from offset (smi)
|
|
||||||
// Check for flat two byte string.
|
|
||||||
|
|
||||||
// Locate 'from' character of string.
|
// Locate first character of substring to copy.
|
||||||
__ Addu(t1, v0, Operand(SeqTwoByteString::kHeaderSize - kHeapObjectTag));
|
|
||||||
// As "from" is a smi it is 2 times the value which matches the size of a two
|
|
||||||
// byte character.
|
|
||||||
STATIC_ASSERT(kSmiTagSize == 1 && kSmiTag == 0);
|
STATIC_ASSERT(kSmiTagSize == 1 && kSmiTag == 0);
|
||||||
__ Addu(t1, t1, Operand(from));
|
__ sll(t0, a3, 1);
|
||||||
|
__ Addu(t1, t1, t0);
|
||||||
// Allocate the result.
|
|
||||||
__ AllocateTwoByteString(v0, a2, a1, a3, t0, &sub_string_runtime);
|
|
||||||
|
|
||||||
// v0: result string
|
|
||||||
// a2: result string length
|
|
||||||
// t1: first character of substring to copy
|
|
||||||
// Locate first character of result.
|
// Locate first character of result.
|
||||||
__ Addu(a1, v0, Operand(SeqTwoByteString::kHeaderSize - kHeapObjectTag));
|
__ Addu(a1, v0, Operand(SeqTwoByteString::kHeaderSize - kHeapObjectTag));
|
||||||
|
|
||||||
from = no_reg;
|
|
||||||
|
|
||||||
// v0: result string.
|
// v0: result string.
|
||||||
// a1: first character of result.
|
// a1: first character of result.
|
||||||
// a2: result length.
|
// a2: result length.
|
||||||
@ -6191,75 +6197,14 @@ void SubStringStub::Generate(MacroAssembler* masm) {
|
|||||||
STATIC_ASSERT((SeqTwoByteString::kHeaderSize & kObjectAlignmentMask) == 0);
|
STATIC_ASSERT((SeqTwoByteString::kHeaderSize & kObjectAlignmentMask) == 0);
|
||||||
StringHelper::GenerateCopyCharactersLong(
|
StringHelper::GenerateCopyCharactersLong(
|
||||||
masm, a1, t1, a2, a3, t0, t2, t3, t4, DEST_ALWAYS_ALIGNED);
|
masm, a1, t1, a2, a3, t0, t2, t3, t4, DEST_ALWAYS_ALIGNED);
|
||||||
__ jmp(&return_v0);
|
|
||||||
|
|
||||||
if (FLAG_string_slices) {
|
|
||||||
__ bind(&create_slice);
|
|
||||||
// v0: original string
|
|
||||||
// a1: instance type
|
|
||||||
// a2: length
|
|
||||||
// a3: from index (untagged smi)
|
|
||||||
// t2 (a.k.a. to): to (smi)
|
|
||||||
// t3 (a.k.a. from): from offset (smi)
|
|
||||||
Label allocate_slice, sliced_string, seq_or_external_string;
|
|
||||||
// If the string is not indirect, it can only be sequential or external.
|
|
||||||
STATIC_ASSERT(kIsIndirectStringMask == (kSlicedStringTag & kConsStringTag));
|
|
||||||
STATIC_ASSERT(kIsIndirectStringMask != 0);
|
|
||||||
__ And(t4, a1, Operand(kIsIndirectStringMask));
|
|
||||||
// External string. Jump to runtime.
|
|
||||||
__ Branch(&seq_or_external_string, eq, t4, Operand(zero_reg));
|
|
||||||
|
|
||||||
__ And(t4, a1, Operand(kSlicedNotConsMask));
|
|
||||||
__ Branch(&sliced_string, ne, t4, Operand(zero_reg));
|
|
||||||
// Cons string. Check whether it is flat, then fetch first part.
|
|
||||||
__ lw(t1, FieldMemOperand(v0, ConsString::kSecondOffset));
|
|
||||||
__ LoadRoot(t5, Heap::kEmptyStringRootIndex);
|
|
||||||
__ Branch(&sub_string_runtime, ne, t1, Operand(t5));
|
|
||||||
__ lw(t1, FieldMemOperand(v0, ConsString::kFirstOffset));
|
|
||||||
__ jmp(&allocate_slice);
|
|
||||||
|
|
||||||
__ bind(&sliced_string);
|
|
||||||
// Sliced string. Fetch parent and correct start index by offset.
|
|
||||||
__ lw(t1, FieldMemOperand(v0, SlicedString::kOffsetOffset));
|
|
||||||
__ addu(t3, t3, t1);
|
|
||||||
__ lw(t1, FieldMemOperand(v0, SlicedString::kParentOffset));
|
|
||||||
__ jmp(&allocate_slice);
|
|
||||||
|
|
||||||
__ bind(&seq_or_external_string);
|
|
||||||
// Sequential or external string. Just move string to the correct register.
|
|
||||||
__ mov(t1, v0);
|
|
||||||
|
|
||||||
__ bind(&allocate_slice);
|
|
||||||
// a1: instance type of original string
|
|
||||||
// a2: length
|
|
||||||
// t1: underlying subject string
|
|
||||||
// t3 (a.k.a. from): from offset (smi)
|
|
||||||
// Allocate new sliced string. At this point we do not reload the instance
|
|
||||||
// type including the string encoding because we simply rely on the info
|
|
||||||
// provided by the original string. It does not matter if the original
|
|
||||||
// string's encoding is wrong because we always have to recheck encoding of
|
|
||||||
// the newly created string's parent anyways due to externalized strings.
|
|
||||||
Label two_byte_slice, set_slice_header;
|
|
||||||
STATIC_ASSERT((kStringEncodingMask & kAsciiStringTag) != 0);
|
|
||||||
STATIC_ASSERT((kStringEncodingMask & kTwoByteStringTag) == 0);
|
|
||||||
__ And(t4, a1, Operand(kStringEncodingMask));
|
|
||||||
__ Branch(&two_byte_slice, eq, t4, Operand(zero_reg));
|
|
||||||
__ AllocateAsciiSlicedString(v0, a2, a3, t0, &sub_string_runtime);
|
|
||||||
__ jmp(&set_slice_header);
|
|
||||||
__ bind(&two_byte_slice);
|
|
||||||
__ AllocateTwoByteSlicedString(v0, a2, a3, t0, &sub_string_runtime);
|
|
||||||
__ bind(&set_slice_header);
|
|
||||||
__ sw(t3, FieldMemOperand(v0, SlicedString::kOffsetOffset));
|
|
||||||
__ sw(t1, FieldMemOperand(v0, SlicedString::kParentOffset));
|
|
||||||
}
|
|
||||||
|
|
||||||
__ bind(&return_v0);
|
__ bind(&return_v0);
|
||||||
|
Counters* counters = masm->isolate()->counters();
|
||||||
__ IncrementCounter(counters->sub_string_native(), 1, a3, t0);
|
__ IncrementCounter(counters->sub_string_native(), 1, a3, t0);
|
||||||
__ Addu(sp, sp, Operand(3 * kPointerSize));
|
__ DropAndRet(3);
|
||||||
__ Ret();
|
|
||||||
|
|
||||||
// Just jump to runtime to create the sub string.
|
// Just jump to runtime to create the sub string.
|
||||||
__ bind(&sub_string_runtime);
|
__ bind(&runtime);
|
||||||
__ TailCallRuntime(Runtime::kSubString, 3, 1);
|
__ TailCallRuntime(Runtime::kSubString, 3, 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -6417,7 +6362,7 @@ void StringCompareStub::Generate(MacroAssembler* masm) {
|
|||||||
|
|
||||||
|
|
||||||
void StringAddStub::Generate(MacroAssembler* masm) {
|
void StringAddStub::Generate(MacroAssembler* masm) {
|
||||||
Label string_add_runtime, call_builtin;
|
Label call_runtime, call_builtin;
|
||||||
Builtins::JavaScript builtin_id = Builtins::ADD;
|
Builtins::JavaScript builtin_id = Builtins::ADD;
|
||||||
|
|
||||||
Counters* counters = masm->isolate()->counters();
|
Counters* counters = masm->isolate()->counters();
|
||||||
@ -6432,7 +6377,7 @@ void StringAddStub::Generate(MacroAssembler* masm) {
|
|||||||
|
|
||||||
// Make sure that both arguments are strings if not known in advance.
|
// Make sure that both arguments are strings if not known in advance.
|
||||||
if (flags_ == NO_STRING_ADD_FLAGS) {
|
if (flags_ == NO_STRING_ADD_FLAGS) {
|
||||||
__ JumpIfEitherSmi(a0, a1, &string_add_runtime);
|
__ JumpIfEitherSmi(a0, a1, &call_runtime);
|
||||||
// Load instance types.
|
// Load instance types.
|
||||||
__ lw(t0, FieldMemOperand(a0, HeapObject::kMapOffset));
|
__ lw(t0, FieldMemOperand(a0, HeapObject::kMapOffset));
|
||||||
__ lw(t1, FieldMemOperand(a1, HeapObject::kMapOffset));
|
__ lw(t1, FieldMemOperand(a1, HeapObject::kMapOffset));
|
||||||
@ -6442,7 +6387,7 @@ void StringAddStub::Generate(MacroAssembler* masm) {
|
|||||||
// If either is not a string, go to runtime.
|
// If either is not a string, go to runtime.
|
||||||
__ Or(t4, t0, Operand(t1));
|
__ Or(t4, t0, Operand(t1));
|
||||||
__ And(t4, t4, Operand(kIsNotStringMask));
|
__ And(t4, t4, Operand(kIsNotStringMask));
|
||||||
__ Branch(&string_add_runtime, ne, t4, Operand(zero_reg));
|
__ Branch(&call_runtime, ne, t4, Operand(zero_reg));
|
||||||
} else {
|
} else {
|
||||||
// Here at least one of the arguments is definitely a string.
|
// Here at least one of the arguments is definitely a string.
|
||||||
// We convert the one that is not known to be a string.
|
// We convert the one that is not known to be a string.
|
||||||
@ -6481,8 +6426,7 @@ void StringAddStub::Generate(MacroAssembler* masm) {
|
|||||||
__ Branch(&strings_not_empty, ne, t4, Operand(zero_reg));
|
__ Branch(&strings_not_empty, ne, t4, Operand(zero_reg));
|
||||||
|
|
||||||
__ IncrementCounter(counters->string_add_native(), 1, a2, a3);
|
__ IncrementCounter(counters->string_add_native(), 1, a2, a3);
|
||||||
__ Addu(sp, sp, Operand(2 * kPointerSize));
|
__ DropAndRet(2);
|
||||||
__ Ret();
|
|
||||||
|
|
||||||
__ bind(&strings_not_empty);
|
__ bind(&strings_not_empty);
|
||||||
}
|
}
|
||||||
@ -6515,7 +6459,7 @@ void StringAddStub::Generate(MacroAssembler* masm) {
|
|||||||
__ lbu(t1, FieldMemOperand(t1, Map::kInstanceTypeOffset));
|
__ lbu(t1, FieldMemOperand(t1, Map::kInstanceTypeOffset));
|
||||||
}
|
}
|
||||||
__ JumpIfBothInstanceTypesAreNotSequentialAscii(t0, t1, t2, t3,
|
__ JumpIfBothInstanceTypesAreNotSequentialAscii(t0, t1, t2, t3,
|
||||||
&string_add_runtime);
|
&call_runtime);
|
||||||
|
|
||||||
// Get the two characters forming the sub string.
|
// Get the two characters forming the sub string.
|
||||||
__ lbu(a2, FieldMemOperand(a0, SeqAsciiString::kHeaderSize));
|
__ lbu(a2, FieldMemOperand(a0, SeqAsciiString::kHeaderSize));
|
||||||
@ -6525,10 +6469,9 @@ void StringAddStub::Generate(MacroAssembler* masm) {
|
|||||||
// just allocate a new one.
|
// just allocate a new one.
|
||||||
Label make_two_character_string;
|
Label make_two_character_string;
|
||||||
StringHelper::GenerateTwoCharacterSymbolTableProbe(
|
StringHelper::GenerateTwoCharacterSymbolTableProbe(
|
||||||
masm, a2, a3, t2, t3, t0, t1, t4, &make_two_character_string);
|
masm, a2, a3, t2, t3, t0, t1, t5, &make_two_character_string);
|
||||||
__ IncrementCounter(counters->string_add_native(), 1, a2, a3);
|
__ IncrementCounter(counters->string_add_native(), 1, a2, a3);
|
||||||
__ Addu(sp, sp, Operand(2 * kPointerSize));
|
__ DropAndRet(2);
|
||||||
__ Ret();
|
|
||||||
|
|
||||||
__ bind(&make_two_character_string);
|
__ bind(&make_two_character_string);
|
||||||
// Resulting string has length 2 and first chars of two strings
|
// Resulting string has length 2 and first chars of two strings
|
||||||
@ -6537,11 +6480,10 @@ void StringAddStub::Generate(MacroAssembler* masm) {
|
|||||||
// halfword store instruction (which assumes that processor is
|
// halfword store instruction (which assumes that processor is
|
||||||
// in a little endian mode).
|
// in a little endian mode).
|
||||||
__ li(t2, Operand(2));
|
__ li(t2, Operand(2));
|
||||||
__ AllocateAsciiString(v0, t2, t0, t1, t4, &string_add_runtime);
|
__ AllocateAsciiString(v0, t2, t0, t1, t5, &call_runtime);
|
||||||
__ sh(a2, FieldMemOperand(v0, SeqAsciiString::kHeaderSize));
|
__ sh(a2, FieldMemOperand(v0, SeqAsciiString::kHeaderSize));
|
||||||
__ IncrementCounter(counters->string_add_native(), 1, a2, a3);
|
__ IncrementCounter(counters->string_add_native(), 1, a2, a3);
|
||||||
__ Addu(sp, sp, Operand(2 * kPointerSize));
|
__ DropAndRet(2);
|
||||||
__ Ret();
|
|
||||||
|
|
||||||
__ bind(&longer_than_two);
|
__ bind(&longer_than_two);
|
||||||
// Check if resulting string will be flat.
|
// Check if resulting string will be flat.
|
||||||
@ -6551,7 +6493,7 @@ void StringAddStub::Generate(MacroAssembler* masm) {
|
|||||||
STATIC_ASSERT((String::kMaxLength & 0x80000000) == 0);
|
STATIC_ASSERT((String::kMaxLength & 0x80000000) == 0);
|
||||||
ASSERT(IsPowerOf2(String::kMaxLength + 1));
|
ASSERT(IsPowerOf2(String::kMaxLength + 1));
|
||||||
// kMaxLength + 1 is representable as shifted literal, kMaxLength is not.
|
// kMaxLength + 1 is representable as shifted literal, kMaxLength is not.
|
||||||
__ Branch(&string_add_runtime, hs, t2, Operand(String::kMaxLength + 1));
|
__ Branch(&call_runtime, hs, t2, Operand(String::kMaxLength + 1));
|
||||||
|
|
||||||
// If result is not supposed to be flat, allocate a cons string object.
|
// If result is not supposed to be flat, allocate a cons string object.
|
||||||
// If both strings are ASCII the result is an ASCII cons string.
|
// If both strings are ASCII the result is an ASCII cons string.
|
||||||
@ -6570,15 +6512,13 @@ void StringAddStub::Generate(MacroAssembler* masm) {
|
|||||||
|
|
||||||
// Allocate an ASCII cons string.
|
// Allocate an ASCII cons string.
|
||||||
__ bind(&ascii_data);
|
__ bind(&ascii_data);
|
||||||
__ AllocateAsciiConsString(t3, t2, t0, t1, &string_add_runtime);
|
__ AllocateAsciiConsString(v0, t2, t0, t1, &call_runtime);
|
||||||
__ bind(&allocated);
|
__ bind(&allocated);
|
||||||
// Fill the fields of the cons string.
|
// Fill the fields of the cons string.
|
||||||
__ sw(a0, FieldMemOperand(t3, ConsString::kFirstOffset));
|
__ sw(a0, FieldMemOperand(v0, ConsString::kFirstOffset));
|
||||||
__ sw(a1, FieldMemOperand(t3, ConsString::kSecondOffset));
|
__ sw(a1, FieldMemOperand(v0, ConsString::kSecondOffset));
|
||||||
__ mov(v0, t3);
|
|
||||||
__ IncrementCounter(counters->string_add_native(), 1, a2, a3);
|
__ IncrementCounter(counters->string_add_native(), 1, a2, a3);
|
||||||
__ Addu(sp, sp, Operand(2 * kPointerSize));
|
__ DropAndRet(2);
|
||||||
__ Ret();
|
|
||||||
|
|
||||||
__ bind(&non_ascii);
|
__ bind(&non_ascii);
|
||||||
// At least one of the strings is two-byte. Check whether it happens
|
// At least one of the strings is two-byte. Check whether it happens
|
||||||
@ -6596,11 +6536,13 @@ void StringAddStub::Generate(MacroAssembler* masm) {
|
|||||||
__ Branch(&ascii_data, eq, t0, Operand(kAsciiStringTag | kAsciiDataHintTag));
|
__ Branch(&ascii_data, eq, t0, Operand(kAsciiStringTag | kAsciiDataHintTag));
|
||||||
|
|
||||||
// Allocate a two byte cons string.
|
// Allocate a two byte cons string.
|
||||||
__ AllocateTwoByteConsString(t3, t2, t0, t1, &string_add_runtime);
|
__ AllocateTwoByteConsString(v0, t2, t0, t1, &call_runtime);
|
||||||
__ Branch(&allocated);
|
__ Branch(&allocated);
|
||||||
|
|
||||||
// Handle creating a flat result. First check that both strings are
|
// We cannot encounter sliced strings or cons strings here since:
|
||||||
// sequential and that they have the same encoding.
|
STATIC_ASSERT(SlicedString::kMinLength >= String::kMinNonFlatLength);
|
||||||
|
// Handle creating a flat result from either external or sequential strings.
|
||||||
|
// Locate the first characters' locations.
|
||||||
// a0: first string
|
// a0: first string
|
||||||
// a1: second string
|
// a1: second string
|
||||||
// a2: length of first string
|
// a2: length of first string
|
||||||
@ -6608,6 +6550,7 @@ void StringAddStub::Generate(MacroAssembler* masm) {
|
|||||||
// t0: first string instance type (if flags_ == NO_STRING_ADD_FLAGS)
|
// t0: first string instance type (if flags_ == NO_STRING_ADD_FLAGS)
|
||||||
// t1: second string instance type (if flags_ == NO_STRING_ADD_FLAGS)
|
// t1: second string instance type (if flags_ == NO_STRING_ADD_FLAGS)
|
||||||
// t2: sum of lengths.
|
// t2: sum of lengths.
|
||||||
|
Label first_prepared, second_prepared;
|
||||||
__ bind(&string_add_flat_result);
|
__ bind(&string_add_flat_result);
|
||||||
if (flags_ != NO_STRING_ADD_FLAGS) {
|
if (flags_ != NO_STRING_ADD_FLAGS) {
|
||||||
__ lw(t0, FieldMemOperand(a0, HeapObject::kMapOffset));
|
__ lw(t0, FieldMemOperand(a0, HeapObject::kMapOffset));
|
||||||
@ -6615,101 +6558,86 @@ void StringAddStub::Generate(MacroAssembler* masm) {
|
|||||||
__ lbu(t0, FieldMemOperand(t0, Map::kInstanceTypeOffset));
|
__ lbu(t0, FieldMemOperand(t0, Map::kInstanceTypeOffset));
|
||||||
__ lbu(t1, FieldMemOperand(t1, Map::kInstanceTypeOffset));
|
__ lbu(t1, FieldMemOperand(t1, Map::kInstanceTypeOffset));
|
||||||
}
|
}
|
||||||
// Check that both strings are sequential, meaning that we
|
// Check whether both strings have same encoding
|
||||||
// branch to runtime if either string tag is non-zero.
|
__ Xor(t3, t0, Operand(t1));
|
||||||
STATIC_ASSERT(kSeqStringTag == 0);
|
__ And(t3, t3, Operand(kStringEncodingMask));
|
||||||
__ Or(t4, t0, Operand(t1));
|
__ Branch(&call_runtime, ne, t3, Operand(zero_reg));
|
||||||
__ And(t4, t4, Operand(kStringRepresentationMask));
|
|
||||||
__ Branch(&string_add_runtime, ne, t4, Operand(zero_reg));
|
|
||||||
|
|
||||||
// Now check if both strings have the same encoding (ASCII/Two-byte).
|
STATIC_ASSERT(kSeqStringTag == 0);
|
||||||
// a0: first string
|
__ And(t4, t0, Operand(kStringRepresentationMask));
|
||||||
// a1: second string
|
|
||||||
|
STATIC_ASSERT(SeqAsciiString::kHeaderSize == SeqTwoByteString::kHeaderSize);
|
||||||
|
Label skip_first_add;
|
||||||
|
__ Branch(&skip_first_add, ne, t4, Operand(zero_reg));
|
||||||
|
__ Branch(USE_DELAY_SLOT, &first_prepared);
|
||||||
|
__ addiu(t3, a0, SeqAsciiString::kHeaderSize - kHeapObjectTag);
|
||||||
|
__ bind(&skip_first_add);
|
||||||
|
// External string: rule out short external string and load string resource.
|
||||||
|
STATIC_ASSERT(kShortExternalStringTag != 0);
|
||||||
|
__ And(t4, t0, Operand(kShortExternalStringMask));
|
||||||
|
__ Branch(&call_runtime, ne, t4, Operand(zero_reg));
|
||||||
|
__ lw(t3, FieldMemOperand(a0, ExternalString::kResourceDataOffset));
|
||||||
|
__ bind(&first_prepared);
|
||||||
|
|
||||||
|
STATIC_ASSERT(kSeqStringTag == 0);
|
||||||
|
__ And(t4, t1, Operand(kStringRepresentationMask));
|
||||||
|
STATIC_ASSERT(SeqAsciiString::kHeaderSize == SeqTwoByteString::kHeaderSize);
|
||||||
|
Label skip_second_add;
|
||||||
|
__ Branch(&skip_second_add, ne, t4, Operand(zero_reg));
|
||||||
|
__ Branch(USE_DELAY_SLOT, &second_prepared);
|
||||||
|
__ addiu(a1, a1, SeqAsciiString::kHeaderSize - kHeapObjectTag);
|
||||||
|
__ bind(&skip_second_add);
|
||||||
|
// External string: rule out short external string and load string resource.
|
||||||
|
STATIC_ASSERT(kShortExternalStringTag != 0);
|
||||||
|
__ And(t4, t1, Operand(kShortExternalStringMask));
|
||||||
|
__ Branch(&call_runtime, ne, t4, Operand(zero_reg));
|
||||||
|
__ lw(a1, FieldMemOperand(a1, ExternalString::kResourceDataOffset));
|
||||||
|
__ bind(&second_prepared);
|
||||||
|
|
||||||
|
Label non_ascii_string_add_flat_result;
|
||||||
|
// t3: first character of first string
|
||||||
|
// a1: first character of second string
|
||||||
// a2: length of first string
|
// a2: length of first string
|
||||||
// a3: length of second string
|
// a3: length of second string
|
||||||
// t0: first string instance type
|
|
||||||
// t1: second string instance type
|
|
||||||
// t2: sum of lengths.
|
// t2: sum of lengths.
|
||||||
Label non_ascii_string_add_flat_result;
|
// Both strings have the same encoding.
|
||||||
ASSERT(IsPowerOf2(kStringEncodingMask)); // Just one bit to test.
|
STATIC_ASSERT(kTwoByteStringTag == 0);
|
||||||
__ xor_(t3, t1, t0);
|
__ And(t4, t1, Operand(kStringEncodingMask));
|
||||||
__ And(t3, t3, Operand(kStringEncodingMask));
|
__ Branch(&non_ascii_string_add_flat_result, eq, t4, Operand(zero_reg));
|
||||||
__ Branch(&string_add_runtime, ne, t3, Operand(zero_reg));
|
|
||||||
// And see if it's ASCII (0) or two-byte (1).
|
|
||||||
__ And(t3, t0, Operand(kStringEncodingMask));
|
|
||||||
__ Branch(&non_ascii_string_add_flat_result, eq, t3, Operand(zero_reg));
|
|
||||||
|
|
||||||
// Both strings are sequential ASCII strings. We also know that they are
|
__ AllocateAsciiString(v0, t2, t0, t1, t5, &call_runtime);
|
||||||
// short (since the sum of the lengths is less than kMinNonFlatLength).
|
__ Addu(t2, v0, Operand(SeqAsciiString::kHeaderSize - kHeapObjectTag));
|
||||||
// t2: length of resulting flat string
|
// v0: result string.
|
||||||
__ AllocateAsciiString(t3, t2, t0, t1, t4, &string_add_runtime);
|
// t3: first character of first string.
|
||||||
// Locate first character of result.
|
// a1: first character of second string
|
||||||
__ Addu(t2, t3, Operand(SeqAsciiString::kHeaderSize - kHeapObjectTag));
|
|
||||||
// Locate first character of first argument.
|
|
||||||
__ Addu(a0, a0, Operand(SeqAsciiString::kHeaderSize - kHeapObjectTag));
|
|
||||||
// a0: first character of first string.
|
|
||||||
// a1: second string.
|
|
||||||
// a2: length of first string.
|
// a2: length of first string.
|
||||||
// a3: length of second string.
|
// a3: length of second string.
|
||||||
// t2: first character of result.
|
// t2: first character of result.
|
||||||
// t3: result string.
|
|
||||||
StringHelper::GenerateCopyCharacters(masm, t2, a0, a2, t0, true);
|
|
||||||
|
|
||||||
// Load second argument and locate first character.
|
StringHelper::GenerateCopyCharacters(masm, t2, t3, a2, t0, true);
|
||||||
__ Addu(a1, a1, Operand(SeqAsciiString::kHeaderSize - kHeapObjectTag));
|
|
||||||
// a1: first character of second string.
|
|
||||||
// a3: length of second string.
|
|
||||||
// t2: next character of result.
|
// t2: next character of result.
|
||||||
// t3: result string.
|
|
||||||
StringHelper::GenerateCopyCharacters(masm, t2, a1, a3, t0, true);
|
StringHelper::GenerateCopyCharacters(masm, t2, a1, a3, t0, true);
|
||||||
__ mov(v0, t3);
|
|
||||||
__ IncrementCounter(counters->string_add_native(), 1, a2, a3);
|
__ IncrementCounter(counters->string_add_native(), 1, a2, a3);
|
||||||
__ Addu(sp, sp, Operand(2 * kPointerSize));
|
__ DropAndRet(2);
|
||||||
__ Ret();
|
|
||||||
|
|
||||||
__ bind(&non_ascii_string_add_flat_result);
|
__ bind(&non_ascii_string_add_flat_result);
|
||||||
// Both strings are sequential two byte strings.
|
__ AllocateTwoByteString(v0, t2, t0, t1, t5, &call_runtime);
|
||||||
// a0: first string.
|
__ Addu(t2, v0, Operand(SeqTwoByteString::kHeaderSize - kHeapObjectTag));
|
||||||
// a1: second string.
|
// v0: result string.
|
||||||
// a2: length of first string.
|
// t3: first character of first string.
|
||||||
// a3: length of second string.
|
// a1: first character of second string.
|
||||||
// t2: sum of length of strings.
|
|
||||||
__ AllocateTwoByteString(t3, t2, t0, t1, t4, &string_add_runtime);
|
|
||||||
// a0: first string.
|
|
||||||
// a1: second string.
|
|
||||||
// a2: length of first string.
|
|
||||||
// a3: length of second string.
|
|
||||||
// t3: result string.
|
|
||||||
|
|
||||||
// Locate first character of result.
|
|
||||||
__ Addu(t2, t3, Operand(SeqTwoByteString::kHeaderSize - kHeapObjectTag));
|
|
||||||
// Locate first character of first argument.
|
|
||||||
__ Addu(a0, a0, Operand(SeqTwoByteString::kHeaderSize - kHeapObjectTag));
|
|
||||||
|
|
||||||
// a0: first character of first string.
|
|
||||||
// a1: second string.
|
|
||||||
// a2: length of first string.
|
// a2: length of first string.
|
||||||
// a3: length of second string.
|
// a3: length of second string.
|
||||||
// t2: first character of result.
|
// t2: first character of result.
|
||||||
// t3: result string.
|
StringHelper::GenerateCopyCharacters(masm, t2, t3, a2, t0, false);
|
||||||
StringHelper::GenerateCopyCharacters(masm, t2, a0, a2, t0, false);
|
// t2: next character of result.
|
||||||
|
|
||||||
// Locate first character of second argument.
|
|
||||||
__ Addu(a1, a1, Operand(SeqTwoByteString::kHeaderSize - kHeapObjectTag));
|
|
||||||
|
|
||||||
// a1: first character of second string.
|
|
||||||
// a3: length of second string.
|
|
||||||
// t2: next character of result (after copy of first string).
|
|
||||||
// t3: result string.
|
|
||||||
StringHelper::GenerateCopyCharacters(masm, t2, a1, a3, t0, false);
|
StringHelper::GenerateCopyCharacters(masm, t2, a1, a3, t0, false);
|
||||||
|
|
||||||
__ mov(v0, t3);
|
|
||||||
__ IncrementCounter(counters->string_add_native(), 1, a2, a3);
|
__ IncrementCounter(counters->string_add_native(), 1, a2, a3);
|
||||||
__ Addu(sp, sp, Operand(2 * kPointerSize));
|
__ DropAndRet(2);
|
||||||
__ Ret();
|
|
||||||
|
|
||||||
// Just jump to runtime to add the two strings.
|
// Just jump to runtime to add the two strings.
|
||||||
__ bind(&string_add_runtime);
|
__ bind(&call_runtime);
|
||||||
__ TailCallRuntime(Runtime::kStringAdd, 2, 1);
|
__ TailCallRuntime(Runtime::kStringAdd, 2, 1);
|
||||||
|
|
||||||
if (call_builtin.is_linked()) {
|
if (call_builtin.is_linked()) {
|
||||||
|
13
deps/v8/src/mips/ic-mips.cc
vendored
13
deps/v8/src/mips/ic-mips.cc
vendored
@ -1470,11 +1470,10 @@ void StoreIC::GenerateArrayLength(MacroAssembler* masm) {
|
|||||||
// -- ra : return address
|
// -- ra : return address
|
||||||
// -----------------------------------
|
// -----------------------------------
|
||||||
//
|
//
|
||||||
// This accepts as a receiver anything JSObject::SetElementsLength accepts
|
// This accepts as a receiver anything JSArray::SetElementsLength accepts
|
||||||
// (currently anything except for external and pixel arrays which means
|
// (currently anything except for external arrays which means anything with
|
||||||
// anything with elements of FixedArray type.), but currently is restricted
|
// elements of FixedArray type). Value must be a number, but only smis are
|
||||||
// to JSArray.
|
// accepted as the most common case.
|
||||||
// Value must be a number, but only smis are accepted as the most common case.
|
|
||||||
|
|
||||||
Label miss;
|
Label miss;
|
||||||
|
|
||||||
@ -1496,6 +1495,10 @@ void StoreIC::GenerateArrayLength(MacroAssembler* masm) {
|
|||||||
__ GetObjectType(scratch, scratch, scratch);
|
__ GetObjectType(scratch, scratch, scratch);
|
||||||
__ Branch(&miss, ne, scratch, Operand(FIXED_ARRAY_TYPE));
|
__ Branch(&miss, ne, scratch, Operand(FIXED_ARRAY_TYPE));
|
||||||
|
|
||||||
|
// Check that the array has fast properties, otherwise the length
|
||||||
|
// property might have been redefined.
|
||||||
|
// TODO(mstarzinger): Port this check to MIPS.
|
||||||
|
|
||||||
// Check that value is a smi.
|
// Check that value is a smi.
|
||||||
__ JumpIfNotSmi(value, &miss);
|
__ JumpIfNotSmi(value, &miss);
|
||||||
|
|
||||||
|
26
deps/v8/src/mips/lithium-codegen-mips.cc
vendored
26
deps/v8/src/mips/lithium-codegen-mips.cc
vendored
@ -2180,10 +2180,19 @@ void LCodeGen::DoStoreGlobalGeneric(LStoreGlobalGeneric* instr) {
|
|||||||
void LCodeGen::DoLoadContextSlot(LLoadContextSlot* instr) {
|
void LCodeGen::DoLoadContextSlot(LLoadContextSlot* instr) {
|
||||||
Register context = ToRegister(instr->context());
|
Register context = ToRegister(instr->context());
|
||||||
Register result = ToRegister(instr->result());
|
Register result = ToRegister(instr->result());
|
||||||
|
|
||||||
__ lw(result, ContextOperand(context, instr->slot_index()));
|
__ lw(result, ContextOperand(context, instr->slot_index()));
|
||||||
if (instr->hydrogen()->RequiresHoleCheck()) {
|
if (instr->hydrogen()->RequiresHoleCheck()) {
|
||||||
__ LoadRoot(at, Heap::kTheHoleValueRootIndex);
|
__ LoadRoot(at, Heap::kTheHoleValueRootIndex);
|
||||||
DeoptimizeIf(eq, instr->environment(), result, Operand(at));
|
|
||||||
|
if (instr->hydrogen()->DeoptimizesOnHole()) {
|
||||||
|
DeoptimizeIf(eq, instr->environment(), result, Operand(at));
|
||||||
|
} else {
|
||||||
|
Label is_not_hole;
|
||||||
|
__ Branch(&is_not_hole, ne, result, Operand(at));
|
||||||
|
__ LoadRoot(result, Heap::kUndefinedValueRootIndex);
|
||||||
|
__ bind(&is_not_hole);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -2191,13 +2200,22 @@ void LCodeGen::DoLoadContextSlot(LLoadContextSlot* instr) {
|
|||||||
void LCodeGen::DoStoreContextSlot(LStoreContextSlot* instr) {
|
void LCodeGen::DoStoreContextSlot(LStoreContextSlot* instr) {
|
||||||
Register context = ToRegister(instr->context());
|
Register context = ToRegister(instr->context());
|
||||||
Register value = ToRegister(instr->value());
|
Register value = ToRegister(instr->value());
|
||||||
|
Register scratch = scratch0();
|
||||||
MemOperand target = ContextOperand(context, instr->slot_index());
|
MemOperand target = ContextOperand(context, instr->slot_index());
|
||||||
|
|
||||||
|
Label skip_assignment;
|
||||||
|
|
||||||
if (instr->hydrogen()->RequiresHoleCheck()) {
|
if (instr->hydrogen()->RequiresHoleCheck()) {
|
||||||
Register scratch = scratch0();
|
|
||||||
__ lw(scratch, target);
|
__ lw(scratch, target);
|
||||||
__ LoadRoot(at, Heap::kTheHoleValueRootIndex);
|
__ LoadRoot(at, Heap::kTheHoleValueRootIndex);
|
||||||
DeoptimizeIf(eq, instr->environment(), scratch, Operand(at));
|
|
||||||
|
if (instr->hydrogen()->DeoptimizesOnHole()) {
|
||||||
|
DeoptimizeIf(eq, instr->environment(), scratch, Operand(at));
|
||||||
|
} else {
|
||||||
|
__ Branch(&skip_assignment, ne, scratch, Operand(at));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
__ sw(value, target);
|
__ sw(value, target);
|
||||||
if (instr->hydrogen()->NeedsWriteBarrier()) {
|
if (instr->hydrogen()->NeedsWriteBarrier()) {
|
||||||
HType type = instr->hydrogen()->value()->type();
|
HType type = instr->hydrogen()->value()->type();
|
||||||
@ -2212,6 +2230,8 @@ void LCodeGen::DoStoreContextSlot(LStoreContextSlot* instr) {
|
|||||||
EMIT_REMEMBERED_SET,
|
EMIT_REMEMBERED_SET,
|
||||||
check_needed);
|
check_needed);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
__ bind(&skip_assignment);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
2
deps/v8/src/mksnapshot.cc
vendored
2
deps/v8/src/mksnapshot.cc
vendored
@ -109,7 +109,7 @@ class PartialSnapshotSink : public i::SnapshotByteSink {
|
|||||||
if (j != 0) {
|
if (j != 0) {
|
||||||
fprintf(fp, ",");
|
fprintf(fp, ",");
|
||||||
}
|
}
|
||||||
fprintf(fp, "%d", at(j));
|
fprintf(fp, "%u", static_cast<unsigned char>(at(j)));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
char at(int i) { return data_[i]; }
|
char at(int i) { return data_[i]; }
|
||||||
|
15
deps/v8/src/objects-inl.h
vendored
15
deps/v8/src/objects-inl.h
vendored
@ -4256,14 +4256,6 @@ bool JSObject::HasIndexedInterceptor() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
bool JSObject::AllowsSetElementsLength() {
|
|
||||||
bool result = elements()->IsFixedArray() ||
|
|
||||||
elements()->IsFixedDoubleArray();
|
|
||||||
ASSERT(result == !HasExternalArrayElements());
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
MaybeObject* JSObject::EnsureWritableFastElements() {
|
MaybeObject* JSObject::EnsureWritableFastElements() {
|
||||||
ASSERT(HasFastTypeElements());
|
ASSERT(HasFastTypeElements());
|
||||||
FixedArray* elems = FixedArray::cast(elements());
|
FixedArray* elems = FixedArray::cast(elements());
|
||||||
@ -4624,6 +4616,13 @@ void JSArray::set_length(Smi* length) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
bool JSArray::AllowsSetElementsLength() {
|
||||||
|
bool result = elements()->IsFixedArray() || elements()->IsFixedDoubleArray();
|
||||||
|
ASSERT(result == !HasExternalArrayElements());
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
MaybeObject* JSArray::SetContent(FixedArrayBase* storage) {
|
MaybeObject* JSArray::SetContent(FixedArrayBase* storage) {
|
||||||
MaybeObject* maybe_result = EnsureCanContainElements(
|
MaybeObject* maybe_result = EnsureCanContainElements(
|
||||||
storage, ALLOW_COPIED_DOUBLE_ELEMENTS);
|
storage, ALLOW_COPIED_DOUBLE_ELEMENTS);
|
||||||
|
16
deps/v8/src/objects.cc
vendored
16
deps/v8/src/objects.cc
vendored
@ -4257,10 +4257,10 @@ void JSObject::LookupCallback(String* name, LookupResult* result) {
|
|||||||
|
|
||||||
|
|
||||||
// Search for a getter or setter in an elements dictionary and update its
|
// Search for a getter or setter in an elements dictionary and update its
|
||||||
// attributes. Returns either undefined if the element is read-only, or the
|
// attributes. Returns either undefined if the element is non-deletable, or
|
||||||
// getter/setter pair (fixed array) if there is an existing one, or the hole
|
// the getter/setter pair (fixed array) if there is an existing one, or the
|
||||||
// value if the element does not exist or is a normal non-getter/setter data
|
// hole value if the element does not exist or is a normal non-getter/setter
|
||||||
// element.
|
// data element.
|
||||||
static Object* UpdateGetterSetterInDictionary(NumberDictionary* dictionary,
|
static Object* UpdateGetterSetterInDictionary(NumberDictionary* dictionary,
|
||||||
uint32_t index,
|
uint32_t index,
|
||||||
PropertyAttributes attributes,
|
PropertyAttributes attributes,
|
||||||
@ -4269,7 +4269,8 @@ static Object* UpdateGetterSetterInDictionary(NumberDictionary* dictionary,
|
|||||||
if (entry != NumberDictionary::kNotFound) {
|
if (entry != NumberDictionary::kNotFound) {
|
||||||
Object* result = dictionary->ValueAt(entry);
|
Object* result = dictionary->ValueAt(entry);
|
||||||
PropertyDetails details = dictionary->DetailsAt(entry);
|
PropertyDetails details = dictionary->DetailsAt(entry);
|
||||||
if (details.IsReadOnly()) return heap->undefined_value();
|
// TODO(mstarzinger): We should check for details.IsDontDelete() here once
|
||||||
|
// we only call into the runtime once to set both getter and setter.
|
||||||
if (details.type() == CALLBACKS && result->IsFixedArray()) {
|
if (details.type() == CALLBACKS && result->IsFixedArray()) {
|
||||||
if (details.attributes() != attributes) {
|
if (details.attributes() != attributes) {
|
||||||
dictionary->DetailsAtPut(entry,
|
dictionary->DetailsAtPut(entry,
|
||||||
@ -4353,7 +4354,8 @@ MaybeObject* JSObject::DefineGetterSetter(String* name,
|
|||||||
LookupResult result(heap->isolate());
|
LookupResult result(heap->isolate());
|
||||||
LocalLookup(name, &result);
|
LocalLookup(name, &result);
|
||||||
if (result.IsProperty()) {
|
if (result.IsProperty()) {
|
||||||
if (result.IsReadOnly()) return heap->undefined_value();
|
// TODO(mstarzinger): We should check for result.IsDontDelete() here once
|
||||||
|
// we only call into the runtime once to set both getter and setter.
|
||||||
if (result.type() == CALLBACKS) {
|
if (result.type() == CALLBACKS) {
|
||||||
Object* obj = result.GetCallbackObject();
|
Object* obj = result.GetCallbackObject();
|
||||||
// Need to preserve old getters/setters.
|
// Need to preserve old getters/setters.
|
||||||
@ -8376,7 +8378,7 @@ void JSArray::Expand(int required_size) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
MaybeObject* JSObject::SetElementsLength(Object* len) {
|
MaybeObject* JSArray::SetElementsLength(Object* len) {
|
||||||
// We should never end in here with a pixel or external array.
|
// We should never end in here with a pixel or external array.
|
||||||
ASSERT(AllowsSetElementsLength());
|
ASSERT(AllowsSetElementsLength());
|
||||||
return GetElementsAccessor()->SetLength(this, len);
|
return GetElementsAccessor()->SetLength(this, len);
|
||||||
|
8
deps/v8/src/objects.h
vendored
8
deps/v8/src/objects.h
vendored
@ -1471,7 +1471,6 @@ class JSObject: public JSReceiver {
|
|||||||
inline bool HasExternalDoubleElements();
|
inline bool HasExternalDoubleElements();
|
||||||
bool HasFastArgumentsElements();
|
bool HasFastArgumentsElements();
|
||||||
bool HasDictionaryArgumentsElements();
|
bool HasDictionaryArgumentsElements();
|
||||||
inline bool AllowsSetElementsLength();
|
|
||||||
inline NumberDictionary* element_dictionary(); // Gets slow elements.
|
inline NumberDictionary* element_dictionary(); // Gets slow elements.
|
||||||
|
|
||||||
// Requires: HasFastElements().
|
// Requires: HasFastElements().
|
||||||
@ -1733,9 +1732,6 @@ class JSObject: public JSReceiver {
|
|||||||
bool HasRealElementProperty(uint32_t index);
|
bool HasRealElementProperty(uint32_t index);
|
||||||
bool HasRealNamedCallbackProperty(String* key);
|
bool HasRealNamedCallbackProperty(String* key);
|
||||||
|
|
||||||
// Initializes the array to a certain length
|
|
||||||
MUST_USE_RESULT MaybeObject* SetElementsLength(Object* length);
|
|
||||||
|
|
||||||
// Get the header size for a JSObject. Used to compute the index of
|
// Get the header size for a JSObject. Used to compute the index of
|
||||||
// internal fields as well as the number of internal fields.
|
// internal fields as well as the number of internal fields.
|
||||||
inline int GetHeaderSize();
|
inline int GetHeaderSize();
|
||||||
@ -7398,6 +7394,10 @@ class JSArray: public JSObject {
|
|||||||
// capacity is non-zero.
|
// capacity is non-zero.
|
||||||
MUST_USE_RESULT MaybeObject* Initialize(int capacity);
|
MUST_USE_RESULT MaybeObject* Initialize(int capacity);
|
||||||
|
|
||||||
|
// Initializes the array to a certain length.
|
||||||
|
inline bool AllowsSetElementsLength();
|
||||||
|
MUST_USE_RESULT MaybeObject* SetElementsLength(Object* length);
|
||||||
|
|
||||||
// Set the content of the array to the content of storage.
|
// Set the content of the array to the content of storage.
|
||||||
inline MaybeObject* SetContent(FixedArrayBase* storage);
|
inline MaybeObject* SetContent(FixedArrayBase* storage);
|
||||||
|
|
||||||
|
1
deps/v8/src/prettyprinter.cc
vendored
1
deps/v8/src/prettyprinter.cc
vendored
@ -447,6 +447,7 @@ void PrettyPrinter::Print(const char* format, ...) {
|
|||||||
|
|
||||||
|
|
||||||
void PrettyPrinter::PrintStatements(ZoneList<Statement*>* statements) {
|
void PrettyPrinter::PrintStatements(ZoneList<Statement*>* statements) {
|
||||||
|
if (statements == NULL) return;
|
||||||
for (int i = 0; i < statements->length(); i++) {
|
for (int i = 0; i < statements->length(); i++) {
|
||||||
if (i != 0) Print(" ");
|
if (i != 0) Print(" ");
|
||||||
Visit(statements->at(i));
|
Visit(statements->at(i));
|
||||||
|
36
deps/v8/src/runtime.cc
vendored
36
deps/v8/src/runtime.cc
vendored
@ -434,7 +434,8 @@ static Handle<Object> CreateObjectLiteralBoilerplate(
|
|||||||
static const int kSmiOnlyLiteralMinimumLength = 1024;
|
static const int kSmiOnlyLiteralMinimumLength = 1024;
|
||||||
|
|
||||||
|
|
||||||
static Handle<Object> CreateArrayLiteralBoilerplate(
|
// static
|
||||||
|
Handle<Object> Runtime::CreateArrayLiteralBoilerplate(
|
||||||
Isolate* isolate,
|
Isolate* isolate,
|
||||||
Handle<FixedArray> literals,
|
Handle<FixedArray> literals,
|
||||||
Handle<FixedArray> elements) {
|
Handle<FixedArray> elements) {
|
||||||
@ -536,7 +537,8 @@ static Handle<Object> CreateLiteralBoilerplate(
|
|||||||
false,
|
false,
|
||||||
kHasNoFunctionLiteral);
|
kHasNoFunctionLiteral);
|
||||||
case CompileTimeValue::ARRAY_LITERAL:
|
case CompileTimeValue::ARRAY_LITERAL:
|
||||||
return CreateArrayLiteralBoilerplate(isolate, literals, elements);
|
return Runtime::CreateArrayLiteralBoilerplate(
|
||||||
|
isolate, literals, elements);
|
||||||
default:
|
default:
|
||||||
UNREACHABLE();
|
UNREACHABLE();
|
||||||
return Handle<Object>::null();
|
return Handle<Object>::null();
|
||||||
@ -606,7 +608,8 @@ RUNTIME_FUNCTION(MaybeObject*, Runtime_CreateArrayLiteral) {
|
|||||||
// Check if boilerplate exists. If not, create it first.
|
// Check if boilerplate exists. If not, create it first.
|
||||||
Handle<Object> boilerplate(literals->get(literals_index), isolate);
|
Handle<Object> boilerplate(literals->get(literals_index), isolate);
|
||||||
if (*boilerplate == isolate->heap()->undefined_value()) {
|
if (*boilerplate == isolate->heap()->undefined_value()) {
|
||||||
boilerplate = CreateArrayLiteralBoilerplate(isolate, literals, elements);
|
boilerplate =
|
||||||
|
Runtime::CreateArrayLiteralBoilerplate(isolate, literals, elements);
|
||||||
if (boilerplate.is_null()) return Failure::Exception();
|
if (boilerplate.is_null()) return Failure::Exception();
|
||||||
// Update the functions literal and return the boilerplate.
|
// Update the functions literal and return the boilerplate.
|
||||||
literals->set(literals_index, *boilerplate);
|
literals->set(literals_index, *boilerplate);
|
||||||
@ -626,7 +629,8 @@ RUNTIME_FUNCTION(MaybeObject*, Runtime_CreateArrayLiteralShallow) {
|
|||||||
Handle<Object> boilerplate(literals->get(literals_index), isolate);
|
Handle<Object> boilerplate(literals->get(literals_index), isolate);
|
||||||
if (*boilerplate == isolate->heap()->undefined_value()) {
|
if (*boilerplate == isolate->heap()->undefined_value()) {
|
||||||
ASSERT(*elements != isolate->heap()->empty_fixed_array());
|
ASSERT(*elements != isolate->heap()->empty_fixed_array());
|
||||||
boilerplate = CreateArrayLiteralBoilerplate(isolate, literals, elements);
|
boilerplate =
|
||||||
|
Runtime::CreateArrayLiteralBoilerplate(isolate, literals, elements);
|
||||||
if (boilerplate.is_null()) return Failure::Exception();
|
if (boilerplate.is_null()) return Failure::Exception();
|
||||||
// Update the functions literal and return the boilerplate.
|
// Update the functions literal and return the boilerplate.
|
||||||
literals->set(literals_index, *boilerplate);
|
literals->set(literals_index, *boilerplate);
|
||||||
@ -4244,27 +4248,14 @@ RUNTIME_FUNCTION(MaybeObject*, Runtime_DefineOrRedefineAccessorProperty) {
|
|||||||
CONVERT_CHECKED(String, name, args[1]);
|
CONVERT_CHECKED(String, name, args[1]);
|
||||||
CONVERT_CHECKED(Smi, flag_setter, args[2]);
|
CONVERT_CHECKED(Smi, flag_setter, args[2]);
|
||||||
Object* fun = args[3];
|
Object* fun = args[3];
|
||||||
RUNTIME_ASSERT(fun->IsSpecFunction() || fun->IsUndefined());
|
|
||||||
CONVERT_CHECKED(Smi, flag_attr, args[4]);
|
CONVERT_CHECKED(Smi, flag_attr, args[4]);
|
||||||
|
|
||||||
int unchecked = flag_attr->value();
|
int unchecked = flag_attr->value();
|
||||||
RUNTIME_ASSERT((unchecked & ~(READ_ONLY | DONT_ENUM | DONT_DELETE)) == 0);
|
RUNTIME_ASSERT((unchecked & ~(READ_ONLY | DONT_ENUM | DONT_DELETE)) == 0);
|
||||||
RUNTIME_ASSERT(!obj->IsNull());
|
|
||||||
LookupResult result(isolate);
|
|
||||||
obj->LocalLookupRealNamedProperty(name, &result);
|
|
||||||
|
|
||||||
PropertyAttributes attr = static_cast<PropertyAttributes>(unchecked);
|
PropertyAttributes attr = static_cast<PropertyAttributes>(unchecked);
|
||||||
// If an existing property is either FIELD, NORMAL or CONSTANT_FUNCTION
|
|
||||||
// delete it to avoid running into trouble in DefineAccessor, which
|
RUNTIME_ASSERT(!obj->IsNull());
|
||||||
// handles this incorrectly if the property is readonly (does nothing)
|
RUNTIME_ASSERT(fun->IsSpecFunction() || fun->IsUndefined());
|
||||||
if (result.IsProperty() &&
|
|
||||||
(result.type() == FIELD || result.type() == NORMAL
|
|
||||||
|| result.type() == CONSTANT_FUNCTION)) {
|
|
||||||
Object* ok;
|
|
||||||
{ MaybeObject* maybe_ok =
|
|
||||||
obj->DeleteProperty(name, JSReceiver::NORMAL_DELETION);
|
|
||||||
if (!maybe_ok->ToObject(&ok)) return maybe_ok;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return obj->DefineAccessor(name, flag_setter->value() == 0, fun, attr);
|
return obj->DefineAccessor(name, flag_setter->value() == 0, fun, attr);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -4280,11 +4271,10 @@ RUNTIME_FUNCTION(MaybeObject*, Runtime_DefineOrRedefineDataProperty) {
|
|||||||
CONVERT_ARG_CHECKED(JSObject, js_object, 0);
|
CONVERT_ARG_CHECKED(JSObject, js_object, 0);
|
||||||
CONVERT_ARG_CHECKED(String, name, 1);
|
CONVERT_ARG_CHECKED(String, name, 1);
|
||||||
Handle<Object> obj_value = args.at<Object>(2);
|
Handle<Object> obj_value = args.at<Object>(2);
|
||||||
|
|
||||||
CONVERT_CHECKED(Smi, flag, args[3]);
|
CONVERT_CHECKED(Smi, flag, args[3]);
|
||||||
|
|
||||||
int unchecked = flag->value();
|
int unchecked = flag->value();
|
||||||
RUNTIME_ASSERT((unchecked & ~(READ_ONLY | DONT_ENUM | DONT_DELETE)) == 0);
|
RUNTIME_ASSERT((unchecked & ~(READ_ONLY | DONT_ENUM | DONT_DELETE)) == 0);
|
||||||
|
|
||||||
PropertyAttributes attr = static_cast<PropertyAttributes>(unchecked);
|
PropertyAttributes attr = static_cast<PropertyAttributes>(unchecked);
|
||||||
|
|
||||||
// Check if this is an element.
|
// Check if this is an element.
|
||||||
|
6
deps/v8/src/runtime.h
vendored
6
deps/v8/src/runtime.h
vendored
@ -679,6 +679,12 @@ class Runtime : public AllStatic {
|
|||||||
|
|
||||||
// Helper functions used stubs.
|
// Helper functions used stubs.
|
||||||
static void PerformGC(Object* result);
|
static void PerformGC(Object* result);
|
||||||
|
|
||||||
|
// Used in runtime.cc and hydrogen's VisitArrayLiteral.
|
||||||
|
static Handle<Object> CreateArrayLiteralBoilerplate(
|
||||||
|
Isolate* isolate,
|
||||||
|
Handle<FixedArray> literals,
|
||||||
|
Handle<FixedArray> elements);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
2
deps/v8/src/spaces.cc
vendored
2
deps/v8/src/spaces.cc
vendored
@ -752,7 +752,7 @@ int PagedSpace::CountTotalPages() {
|
|||||||
void PagedSpace::ReleasePage(Page* page) {
|
void PagedSpace::ReleasePage(Page* page) {
|
||||||
ASSERT(page->LiveBytes() == 0);
|
ASSERT(page->LiveBytes() == 0);
|
||||||
|
|
||||||
// Adjust list of unswept pages if the page is it's head or tail.
|
// Adjust list of unswept pages if the page is the head of the list.
|
||||||
if (first_unswept_page_ == page) {
|
if (first_unswept_page_ == page) {
|
||||||
first_unswept_page_ = page->next_page();
|
first_unswept_page_ = page->next_page();
|
||||||
if (first_unswept_page_ == anchor()) {
|
if (first_unswept_page_ == anchor()) {
|
||||||
|
9
deps/v8/src/stub-cache.cc
vendored
9
deps/v8/src/stub-cache.cc
vendored
@ -877,7 +877,8 @@ void StubCache::Clear() {
|
|||||||
|
|
||||||
void StubCache::CollectMatchingMaps(SmallMapList* types,
|
void StubCache::CollectMatchingMaps(SmallMapList* types,
|
||||||
String* name,
|
String* name,
|
||||||
Code::Flags flags) {
|
Code::Flags flags,
|
||||||
|
Handle<Context> global_context) {
|
||||||
for (int i = 0; i < kPrimaryTableSize; i++) {
|
for (int i = 0; i < kPrimaryTableSize; i++) {
|
||||||
if (primary_[i].key == name) {
|
if (primary_[i].key == name) {
|
||||||
Map* map = primary_[i].value->FindFirstMap();
|
Map* map = primary_[i].value->FindFirstMap();
|
||||||
@ -886,7 +887,8 @@ void StubCache::CollectMatchingMaps(SmallMapList* types,
|
|||||||
if (map == NULL) continue;
|
if (map == NULL) continue;
|
||||||
|
|
||||||
int offset = PrimaryOffset(name, flags, map);
|
int offset = PrimaryOffset(name, flags, map);
|
||||||
if (entry(primary_, offset) == &primary_[i]) {
|
if (entry(primary_, offset) == &primary_[i] &&
|
||||||
|
!TypeFeedbackOracle::CanRetainOtherContext(map, *global_context)) {
|
||||||
types->Add(Handle<Map>(map));
|
types->Add(Handle<Map>(map));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -909,7 +911,8 @@ void StubCache::CollectMatchingMaps(SmallMapList* types,
|
|||||||
|
|
||||||
// Lookup in secondary table and add matches.
|
// Lookup in secondary table and add matches.
|
||||||
int offset = SecondaryOffset(name, flags, primary_offset);
|
int offset = SecondaryOffset(name, flags, primary_offset);
|
||||||
if (entry(secondary_, offset) == &secondary_[i]) {
|
if (entry(secondary_, offset) == &secondary_[i] &&
|
||||||
|
!TypeFeedbackOracle::CanRetainOtherContext(map, *global_context)) {
|
||||||
types->Add(Handle<Map>(map));
|
types->Add(Handle<Map>(map));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
3
deps/v8/src/stub-cache.h
vendored
3
deps/v8/src/stub-cache.h
vendored
@ -248,7 +248,8 @@ class StubCache {
|
|||||||
// Collect all maps that match the name and flags.
|
// Collect all maps that match the name and flags.
|
||||||
void CollectMatchingMaps(SmallMapList* types,
|
void CollectMatchingMaps(SmallMapList* types,
|
||||||
String* name,
|
String* name,
|
||||||
Code::Flags flags);
|
Code::Flags flags,
|
||||||
|
Handle<Context> global_context);
|
||||||
|
|
||||||
// Generate code for probing the stub cache table.
|
// Generate code for probing the stub cache table.
|
||||||
// Arguments extra and extra2 may be used to pass additional scratch
|
// Arguments extra and extra2 may be used to pass additional scratch
|
||||||
|
47
deps/v8/src/type-info.cc
vendored
47
deps/v8/src/type-info.cc
vendored
@ -438,11 +438,45 @@ void TypeFeedbackOracle::CollectReceiverTypes(unsigned ast_id,
|
|||||||
Handle<Code>::cast(object)->ic_state() == MEGAMORPHIC) {
|
Handle<Code>::cast(object)->ic_state() == MEGAMORPHIC) {
|
||||||
types->Reserve(4);
|
types->Reserve(4);
|
||||||
ASSERT(object->IsCode());
|
ASSERT(object->IsCode());
|
||||||
isolate_->stub_cache()->CollectMatchingMaps(types, *name, flags);
|
isolate_->stub_cache()->CollectMatchingMaps(types,
|
||||||
|
*name,
|
||||||
|
flags,
|
||||||
|
global_context_);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// Check if a map originates from a given global context. We use this
|
||||||
|
// information to filter out maps from different context to avoid
|
||||||
|
// retaining objects from different tabs in Chrome via optimized code.
|
||||||
|
bool TypeFeedbackOracle::CanRetainOtherContext(Map* map,
|
||||||
|
Context* global_context) {
|
||||||
|
Object* constructor = map->constructor();
|
||||||
|
ASSERT(constructor != NULL);
|
||||||
|
while (!constructor->IsJSFunction()) {
|
||||||
|
// If the constructor is not null or a JSFunction, we have to
|
||||||
|
// conservatively assume that it may retain a global context.
|
||||||
|
if (!constructor->IsNull()) return true;
|
||||||
|
|
||||||
|
// If both, constructor and prototype are null, we conclude
|
||||||
|
// that no global context will be retained by this map.
|
||||||
|
if (map->prototype()->IsNull()) return false;
|
||||||
|
|
||||||
|
map = JSObject::cast(map->prototype())->map();
|
||||||
|
constructor = map->constructor();
|
||||||
|
}
|
||||||
|
JSFunction* function = JSFunction::cast(constructor);
|
||||||
|
return CanRetainOtherContext(function, global_context);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
bool TypeFeedbackOracle::CanRetainOtherContext(JSFunction* function,
|
||||||
|
Context* global_context) {
|
||||||
|
return function->context()->global() != global_context->global()
|
||||||
|
&& function->context()->global() != global_context->builtins();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
static void AddMapIfMissing(Handle<Map> map, SmallMapList* list) {
|
static void AddMapIfMissing(Handle<Map> map, SmallMapList* list) {
|
||||||
for (int i = 0; i < list->length(); ++i) {
|
for (int i = 0; i < list->length(); ++i) {
|
||||||
if (list->at(i).is_identical_to(map)) return;
|
if (list->at(i).is_identical_to(map)) return;
|
||||||
@ -539,7 +573,12 @@ void TypeFeedbackOracle::ProcessRelocInfos(ZoneList<RelocInfo>* infos) {
|
|||||||
SetInfo(ast_id, Smi::FromInt(target->check_type()));
|
SetInfo(ast_id, Smi::FromInt(target->check_type()));
|
||||||
} else {
|
} else {
|
||||||
Object* map = target->FindFirstMap();
|
Object* map = target->FindFirstMap();
|
||||||
SetInfo(ast_id, map == NULL ? static_cast<Object*>(target) : map);
|
if (map == NULL) {
|
||||||
|
SetInfo(ast_id, static_cast<Object*>(target));
|
||||||
|
} else if (!CanRetainOtherContext(Map::cast(map),
|
||||||
|
*global_context_)) {
|
||||||
|
SetInfo(ast_id, map);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
} else if (target->ic_state() == MEGAMORPHIC) {
|
} else if (target->ic_state() == MEGAMORPHIC) {
|
||||||
SetInfo(ast_id, target);
|
SetInfo(ast_id, target);
|
||||||
@ -565,7 +604,9 @@ void TypeFeedbackOracle::ProcessRelocInfos(ZoneList<RelocInfo>* infos) {
|
|||||||
if (target->major_key() == CodeStub::CallFunction &&
|
if (target->major_key() == CodeStub::CallFunction &&
|
||||||
target->has_function_cache()) {
|
target->has_function_cache()) {
|
||||||
Object* value = CallFunctionStub::GetCachedValue(reloc_entry.pc());
|
Object* value = CallFunctionStub::GetCachedValue(reloc_entry.pc());
|
||||||
if (value->IsJSFunction()) {
|
if (value->IsJSFunction() &&
|
||||||
|
!CanRetainOtherContext(JSFunction::cast(value),
|
||||||
|
*global_context_)) {
|
||||||
SetInfo(ast_id, value);
|
SetInfo(ast_id, value);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
4
deps/v8/src/type-info.h
vendored
4
deps/v8/src/type-info.h
vendored
@ -256,6 +256,10 @@ class TypeFeedbackOracle BASE_EMBEDDED {
|
|||||||
void CollectKeyedReceiverTypes(unsigned ast_id,
|
void CollectKeyedReceiverTypes(unsigned ast_id,
|
||||||
SmallMapList* types);
|
SmallMapList* types);
|
||||||
|
|
||||||
|
static bool CanRetainOtherContext(Map* map, Context* global_context);
|
||||||
|
static bool CanRetainOtherContext(JSFunction* function,
|
||||||
|
Context* global_context);
|
||||||
|
|
||||||
CheckType GetCallCheckType(Call* expr);
|
CheckType GetCallCheckType(Call* expr);
|
||||||
Handle<JSObject> GetPrototypeForPrimitiveCheck(CheckType check);
|
Handle<JSObject> GetPrototypeForPrimitiveCheck(CheckType check);
|
||||||
|
|
||||||
|
9
deps/v8/src/v8.cc
vendored
9
deps/v8/src/v8.cc
vendored
@ -57,14 +57,7 @@ static EntropySource entropy_source;
|
|||||||
|
|
||||||
|
|
||||||
bool V8::Initialize(Deserializer* des) {
|
bool V8::Initialize(Deserializer* des) {
|
||||||
// Setting --harmony implies all other harmony flags.
|
FlagList::EnforceFlagImplications();
|
||||||
// TODO(rossberg): Is there a better place to put this?
|
|
||||||
if (FLAG_harmony) {
|
|
||||||
FLAG_harmony_typeof = true;
|
|
||||||
FLAG_harmony_scoping = true;
|
|
||||||
FLAG_harmony_proxies = true;
|
|
||||||
FLAG_harmony_collections = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
InitializeOncePerProcess();
|
InitializeOncePerProcess();
|
||||||
|
|
||||||
|
2
deps/v8/src/version.cc
vendored
2
deps/v8/src/version.cc
vendored
@ -34,7 +34,7 @@
|
|||||||
// cannot be changed without changing the SCons build script.
|
// cannot be changed without changing the SCons build script.
|
||||||
#define MAJOR_VERSION 3
|
#define MAJOR_VERSION 3
|
||||||
#define MINOR_VERSION 8
|
#define MINOR_VERSION 8
|
||||||
#define BUILD_NUMBER 0
|
#define BUILD_NUMBER 1
|
||||||
#define PATCH_LEVEL 0
|
#define PATCH_LEVEL 0
|
||||||
// Use 1 for candidates and 0 otherwise.
|
// Use 1 for candidates and 0 otherwise.
|
||||||
// (Boolean macro values are not supported by all preprocessors.)
|
// (Boolean macro values are not supported by all preprocessors.)
|
||||||
|
386
deps/v8/src/x64/code-stubs-x64.cc
vendored
386
deps/v8/src/x64/code-stubs-x64.cc
vendored
@ -4434,7 +4434,7 @@ void StringCharAtGenerator::GenerateSlow(
|
|||||||
|
|
||||||
|
|
||||||
void StringAddStub::Generate(MacroAssembler* masm) {
|
void StringAddStub::Generate(MacroAssembler* masm) {
|
||||||
Label string_add_runtime, call_builtin;
|
Label call_runtime, call_builtin;
|
||||||
Builtins::JavaScript builtin_id = Builtins::ADD;
|
Builtins::JavaScript builtin_id = Builtins::ADD;
|
||||||
|
|
||||||
// Load the two arguments.
|
// Load the two arguments.
|
||||||
@ -4443,14 +4443,14 @@ void StringAddStub::Generate(MacroAssembler* masm) {
|
|||||||
|
|
||||||
// Make sure that both arguments are strings if not known in advance.
|
// Make sure that both arguments are strings if not known in advance.
|
||||||
if (flags_ == NO_STRING_ADD_FLAGS) {
|
if (flags_ == NO_STRING_ADD_FLAGS) {
|
||||||
__ JumpIfSmi(rax, &string_add_runtime);
|
__ JumpIfSmi(rax, &call_runtime);
|
||||||
__ CmpObjectType(rax, FIRST_NONSTRING_TYPE, r8);
|
__ CmpObjectType(rax, FIRST_NONSTRING_TYPE, r8);
|
||||||
__ j(above_equal, &string_add_runtime);
|
__ j(above_equal, &call_runtime);
|
||||||
|
|
||||||
// First argument is a a string, test second.
|
// First argument is a a string, test second.
|
||||||
__ JumpIfSmi(rdx, &string_add_runtime);
|
__ JumpIfSmi(rdx, &call_runtime);
|
||||||
__ CmpObjectType(rdx, FIRST_NONSTRING_TYPE, r9);
|
__ CmpObjectType(rdx, FIRST_NONSTRING_TYPE, r9);
|
||||||
__ j(above_equal, &string_add_runtime);
|
__ j(above_equal, &call_runtime);
|
||||||
} else {
|
} else {
|
||||||
// Here at least one of the arguments is definitely a string.
|
// Here at least one of the arguments is definitely a string.
|
||||||
// We convert the one that is not known to be a string.
|
// We convert the one that is not known to be a string.
|
||||||
@ -4518,7 +4518,7 @@ void StringAddStub::Generate(MacroAssembler* masm) {
|
|||||||
|
|
||||||
// Check that both strings are non-external ascii strings.
|
// Check that both strings are non-external ascii strings.
|
||||||
__ JumpIfBothInstanceTypesAreNotSequentialAscii(r8, r9, rbx, rcx,
|
__ JumpIfBothInstanceTypesAreNotSequentialAscii(r8, r9, rbx, rcx,
|
||||||
&string_add_runtime);
|
&call_runtime);
|
||||||
|
|
||||||
// Get the two characters forming the sub string.
|
// Get the two characters forming the sub string.
|
||||||
__ movzxbq(rbx, FieldOperand(rax, SeqAsciiString::kHeaderSize));
|
__ movzxbq(rbx, FieldOperand(rax, SeqAsciiString::kHeaderSize));
|
||||||
@ -4533,8 +4533,18 @@ void StringAddStub::Generate(MacroAssembler* masm) {
|
|||||||
__ ret(2 * kPointerSize);
|
__ ret(2 * kPointerSize);
|
||||||
|
|
||||||
__ bind(&make_two_character_string);
|
__ bind(&make_two_character_string);
|
||||||
__ Set(rbx, 2);
|
__ Set(rdi, 2);
|
||||||
__ jmp(&make_flat_ascii_string);
|
__ AllocateAsciiString(rax, rdi, r8, r9, r11, &call_runtime);
|
||||||
|
// rbx - first byte: first character
|
||||||
|
// rbx - second byte: *maybe* second character
|
||||||
|
// Make sure that the second byte of rbx contains the second character.
|
||||||
|
__ movzxbq(rcx, FieldOperand(rdx, SeqAsciiString::kHeaderSize));
|
||||||
|
__ shll(rcx, Immediate(kBitsPerByte));
|
||||||
|
__ orl(rbx, rcx);
|
||||||
|
// Write both characters to the new string.
|
||||||
|
__ movw(FieldOperand(rax, SeqAsciiString::kHeaderSize), rbx);
|
||||||
|
__ IncrementCounter(counters->string_add_native(), 1);
|
||||||
|
__ ret(2 * kPointerSize);
|
||||||
|
|
||||||
__ bind(&longer_than_two);
|
__ bind(&longer_than_two);
|
||||||
// Check if resulting string will be flat.
|
// Check if resulting string will be flat.
|
||||||
@ -4543,7 +4553,7 @@ void StringAddStub::Generate(MacroAssembler* masm) {
|
|||||||
// Handle exceptionally long strings in the runtime system.
|
// Handle exceptionally long strings in the runtime system.
|
||||||
STATIC_ASSERT((String::kMaxLength & 0x80000000) == 0);
|
STATIC_ASSERT((String::kMaxLength & 0x80000000) == 0);
|
||||||
__ SmiCompare(rbx, Smi::FromInt(String::kMaxLength));
|
__ SmiCompare(rbx, Smi::FromInt(String::kMaxLength));
|
||||||
__ j(above, &string_add_runtime);
|
__ j(above, &call_runtime);
|
||||||
|
|
||||||
// If result is not supposed to be flat, allocate a cons string object. If
|
// If result is not supposed to be flat, allocate a cons string object. If
|
||||||
// both strings are ascii the result is an ascii cons string.
|
// both strings are ascii the result is an ascii cons string.
|
||||||
@ -4561,7 +4571,7 @@ void StringAddStub::Generate(MacroAssembler* masm) {
|
|||||||
__ j(zero, &non_ascii);
|
__ j(zero, &non_ascii);
|
||||||
__ bind(&ascii_data);
|
__ bind(&ascii_data);
|
||||||
// Allocate an acsii cons string.
|
// Allocate an acsii cons string.
|
||||||
__ AllocateAsciiConsString(rcx, rdi, no_reg, &string_add_runtime);
|
__ AllocateAsciiConsString(rcx, rdi, no_reg, &call_runtime);
|
||||||
__ bind(&allocated);
|
__ bind(&allocated);
|
||||||
// Fill the fields of the cons string.
|
// Fill the fields of the cons string.
|
||||||
__ movq(FieldOperand(rcx, ConsString::kLengthOffset), rbx);
|
__ movq(FieldOperand(rcx, ConsString::kLengthOffset), rbx);
|
||||||
@ -4586,111 +4596,103 @@ void StringAddStub::Generate(MacroAssembler* masm) {
|
|||||||
__ cmpb(r8, Immediate(kAsciiStringTag | kAsciiDataHintTag));
|
__ cmpb(r8, Immediate(kAsciiStringTag | kAsciiDataHintTag));
|
||||||
__ j(equal, &ascii_data);
|
__ j(equal, &ascii_data);
|
||||||
// Allocate a two byte cons string.
|
// Allocate a two byte cons string.
|
||||||
__ AllocateTwoByteConsString(rcx, rdi, no_reg, &string_add_runtime);
|
__ AllocateTwoByteConsString(rcx, rdi, no_reg, &call_runtime);
|
||||||
__ jmp(&allocated);
|
__ jmp(&allocated);
|
||||||
|
|
||||||
// Handle creating a flat result. First check that both strings are not
|
// We cannot encounter sliced strings or cons strings here since:
|
||||||
// external strings.
|
STATIC_ASSERT(SlicedString::kMinLength >= String::kMinNonFlatLength);
|
||||||
|
// Handle creating a flat result from either external or sequential strings.
|
||||||
|
// Locate the first characters' locations.
|
||||||
// rax: first string
|
// rax: first string
|
||||||
// rbx: length of resulting flat string as smi
|
// rbx: length of resulting flat string as smi
|
||||||
// rdx: second string
|
// rdx: second string
|
||||||
// r8: instance type of first string
|
// r8: instance type of first string
|
||||||
// r9: instance type of first string
|
// r9: instance type of first string
|
||||||
|
Label first_prepared, second_prepared;
|
||||||
|
Label first_is_sequential, second_is_sequential;
|
||||||
__ bind(&string_add_flat_result);
|
__ bind(&string_add_flat_result);
|
||||||
__ SmiToInteger32(rbx, rbx);
|
|
||||||
__ movl(rcx, r8);
|
__ SmiToInteger32(r14, FieldOperand(rax, SeqString::kLengthOffset));
|
||||||
__ and_(rcx, Immediate(kStringRepresentationMask));
|
// r14: length of first string
|
||||||
__ cmpl(rcx, Immediate(kExternalStringTag));
|
STATIC_ASSERT(kSeqStringTag == 0);
|
||||||
__ j(equal, &string_add_runtime);
|
__ testb(r8, Immediate(kStringRepresentationMask));
|
||||||
__ movl(rcx, r9);
|
__ j(zero, &first_is_sequential, Label::kNear);
|
||||||
__ and_(rcx, Immediate(kStringRepresentationMask));
|
// Rule out short external string and load string resource.
|
||||||
__ cmpl(rcx, Immediate(kExternalStringTag));
|
STATIC_ASSERT(kShortExternalStringTag != 0);
|
||||||
__ j(equal, &string_add_runtime);
|
__ testb(r8, Immediate(kShortExternalStringMask));
|
||||||
// We cannot encounter sliced strings here since:
|
__ j(not_zero, &call_runtime);
|
||||||
STATIC_ASSERT(SlicedString::kMinLength >= String::kMinNonFlatLength);
|
__ movq(rcx, FieldOperand(rax, ExternalString::kResourceDataOffset));
|
||||||
// Now check if both strings are ascii strings.
|
__ jmp(&first_prepared, Label::kNear);
|
||||||
// rax: first string
|
__ bind(&first_is_sequential);
|
||||||
// rbx: length of resulting flat string
|
STATIC_ASSERT(SeqAsciiString::kHeaderSize == SeqTwoByteString::kHeaderSize);
|
||||||
// rdx: second string
|
__ lea(rcx, FieldOperand(rax, SeqAsciiString::kHeaderSize));
|
||||||
// r8: instance type of first string
|
__ bind(&first_prepared);
|
||||||
// r9: instance type of second string
|
|
||||||
|
// Check whether both strings have same encoding.
|
||||||
|
__ xorl(r8, r9);
|
||||||
|
__ testb(r8, Immediate(kStringEncodingMask));
|
||||||
|
__ j(not_zero, &call_runtime);
|
||||||
|
|
||||||
|
__ SmiToInteger32(r15, FieldOperand(rdx, SeqString::kLengthOffset));
|
||||||
|
// r15: length of second string
|
||||||
|
STATIC_ASSERT(kSeqStringTag == 0);
|
||||||
|
__ testb(r9, Immediate(kStringRepresentationMask));
|
||||||
|
__ j(zero, &second_is_sequential, Label::kNear);
|
||||||
|
// Rule out short external string and load string resource.
|
||||||
|
STATIC_ASSERT(kShortExternalStringTag != 0);
|
||||||
|
__ testb(r9, Immediate(kShortExternalStringMask));
|
||||||
|
__ j(not_zero, &call_runtime);
|
||||||
|
__ movq(rdx, FieldOperand(rdx, ExternalString::kResourceDataOffset));
|
||||||
|
__ jmp(&second_prepared, Label::kNear);
|
||||||
|
__ bind(&second_is_sequential);
|
||||||
|
STATIC_ASSERT(SeqAsciiString::kHeaderSize == SeqTwoByteString::kHeaderSize);
|
||||||
|
__ lea(rdx, FieldOperand(rdx, SeqAsciiString::kHeaderSize));
|
||||||
|
__ bind(&second_prepared);
|
||||||
|
|
||||||
Label non_ascii_string_add_flat_result;
|
Label non_ascii_string_add_flat_result;
|
||||||
STATIC_ASSERT((kStringEncodingMask & kAsciiStringTag) != 0);
|
// r9: instance type of second string
|
||||||
STATIC_ASSERT((kStringEncodingMask & kTwoByteStringTag) == 0);
|
// First string and second string have the same encoding.
|
||||||
__ testl(r8, Immediate(kStringEncodingMask));
|
STATIC_ASSERT(kTwoByteStringTag == 0);
|
||||||
|
__ SmiToInteger32(rbx, rbx);
|
||||||
|
__ testb(r9, Immediate(kStringEncodingMask));
|
||||||
__ j(zero, &non_ascii_string_add_flat_result);
|
__ j(zero, &non_ascii_string_add_flat_result);
|
||||||
__ testl(r9, Immediate(kStringEncodingMask));
|
|
||||||
__ j(zero, &string_add_runtime);
|
|
||||||
|
|
||||||
__ bind(&make_flat_ascii_string);
|
__ bind(&make_flat_ascii_string);
|
||||||
// Both strings are ascii strings. As they are short they are both flat.
|
// Both strings are ascii strings. As they are short they are both flat.
|
||||||
__ AllocateAsciiString(rcx, rbx, rdi, r14, r11, &string_add_runtime);
|
__ AllocateAsciiString(rax, rbx, rdi, r8, r9, &call_runtime);
|
||||||
// rcx: result string
|
// rax: result string
|
||||||
__ movq(rbx, rcx);
|
|
||||||
// Locate first character of result.
|
// Locate first character of result.
|
||||||
__ addq(rcx, Immediate(SeqAsciiString::kHeaderSize - kHeapObjectTag));
|
__ lea(rbx, FieldOperand(rax, SeqAsciiString::kHeaderSize));
|
||||||
// Locate first character of first argument
|
// rcx: first char of first string
|
||||||
__ SmiToInteger32(rdi, FieldOperand(rax, String::kLengthOffset));
|
// rbx: first character of result
|
||||||
__ addq(rax, Immediate(SeqAsciiString::kHeaderSize - kHeapObjectTag));
|
// r14: length of first string
|
||||||
// rax: first char of first argument
|
StringHelper::GenerateCopyCharacters(masm, rbx, rcx, r14, true);
|
||||||
// rbx: result string
|
// rbx: next character of result
|
||||||
// rcx: first character of result
|
// rdx: first char of second string
|
||||||
// rdx: second string
|
// r15: length of second string
|
||||||
// rdi: length of first argument
|
StringHelper::GenerateCopyCharacters(masm, rbx, rdx, r15, true);
|
||||||
StringHelper::GenerateCopyCharacters(masm, rcx, rax, rdi, true);
|
|
||||||
// Locate first character of second argument.
|
|
||||||
__ SmiToInteger32(rdi, FieldOperand(rdx, String::kLengthOffset));
|
|
||||||
__ addq(rdx, Immediate(SeqAsciiString::kHeaderSize - kHeapObjectTag));
|
|
||||||
// rbx: result string
|
|
||||||
// rcx: next character of result
|
|
||||||
// rdx: first char of second argument
|
|
||||||
// rdi: length of second argument
|
|
||||||
StringHelper::GenerateCopyCharacters(masm, rcx, rdx, rdi, true);
|
|
||||||
__ movq(rax, rbx);
|
|
||||||
__ IncrementCounter(counters->string_add_native(), 1);
|
__ IncrementCounter(counters->string_add_native(), 1);
|
||||||
__ ret(2 * kPointerSize);
|
__ ret(2 * kPointerSize);
|
||||||
|
|
||||||
// Handle creating a flat two byte result.
|
|
||||||
// rax: first string - known to be two byte
|
|
||||||
// rbx: length of resulting flat string
|
|
||||||
// rdx: second string
|
|
||||||
// r8: instance type of first string
|
|
||||||
// r9: instance type of first string
|
|
||||||
__ bind(&non_ascii_string_add_flat_result);
|
__ bind(&non_ascii_string_add_flat_result);
|
||||||
STATIC_ASSERT((kStringEncodingMask & kAsciiStringTag) != 0);
|
// Both strings are ascii strings. As they are short they are both flat.
|
||||||
STATIC_ASSERT((kStringEncodingMask & kTwoByteStringTag) == 0);
|
__ AllocateTwoByteString(rax, rbx, rdi, r8, r9, &call_runtime);
|
||||||
__ and_(r9, Immediate(kStringEncodingMask));
|
// rax: result string
|
||||||
__ j(not_zero, &string_add_runtime);
|
|
||||||
// Both strings are two byte strings. As they are short they are both
|
|
||||||
// flat.
|
|
||||||
__ AllocateTwoByteString(rcx, rbx, rdi, r14, r11, &string_add_runtime);
|
|
||||||
// rcx: result string
|
|
||||||
__ movq(rbx, rcx);
|
|
||||||
// Locate first character of result.
|
// Locate first character of result.
|
||||||
__ addq(rcx, Immediate(SeqTwoByteString::kHeaderSize - kHeapObjectTag));
|
__ lea(rbx, FieldOperand(rax, SeqTwoByteString::kHeaderSize));
|
||||||
// Locate first character of first argument.
|
// rcx: first char of first string
|
||||||
__ SmiToInteger32(rdi, FieldOperand(rax, String::kLengthOffset));
|
// rbx: first character of result
|
||||||
__ addq(rax, Immediate(SeqTwoByteString::kHeaderSize - kHeapObjectTag));
|
// r14: length of first string
|
||||||
// rax: first char of first argument
|
StringHelper::GenerateCopyCharacters(masm, rbx, rcx, r14, false);
|
||||||
// rbx: result string
|
// rbx: next character of result
|
||||||
// rcx: first character of result
|
// rdx: first char of second string
|
||||||
// rdx: second argument
|
// r15: length of second string
|
||||||
// rdi: length of first argument
|
StringHelper::GenerateCopyCharacters(masm, rbx, rdx, r15, false);
|
||||||
StringHelper::GenerateCopyCharacters(masm, rcx, rax, rdi, false);
|
|
||||||
// Locate first character of second argument.
|
|
||||||
__ SmiToInteger32(rdi, FieldOperand(rdx, String::kLengthOffset));
|
|
||||||
__ addq(rdx, Immediate(SeqTwoByteString::kHeaderSize - kHeapObjectTag));
|
|
||||||
// rbx: result string
|
|
||||||
// rcx: next character of result
|
|
||||||
// rdx: first char of second argument
|
|
||||||
// rdi: length of second argument
|
|
||||||
StringHelper::GenerateCopyCharacters(masm, rcx, rdx, rdi, false);
|
|
||||||
__ movq(rax, rbx);
|
|
||||||
__ IncrementCounter(counters->string_add_native(), 1);
|
__ IncrementCounter(counters->string_add_native(), 1);
|
||||||
__ ret(2 * kPointerSize);
|
__ ret(2 * kPointerSize);
|
||||||
|
|
||||||
// Just jump to runtime to add the two strings.
|
// Just jump to runtime to add the two strings.
|
||||||
__ bind(&string_add_runtime);
|
__ bind(&call_runtime);
|
||||||
__ TailCallRuntime(Runtime::kStringAdd, 2, 1);
|
__ TailCallRuntime(Runtime::kStringAdd, 2, 1);
|
||||||
|
|
||||||
if (call_builtin.is_linked()) {
|
if (call_builtin.is_linked()) {
|
||||||
@ -5040,8 +5042,12 @@ void SubStringStub::Generate(MacroAssembler* masm) {
|
|||||||
|
|
||||||
__ SmiSub(rcx, rcx, rdx); // Overflow doesn't happen.
|
__ SmiSub(rcx, rcx, rdx); // Overflow doesn't happen.
|
||||||
__ cmpq(FieldOperand(rax, String::kLengthOffset), rcx);
|
__ cmpq(FieldOperand(rax, String::kLengthOffset), rcx);
|
||||||
Label return_rax;
|
Label not_original_string;
|
||||||
__ j(equal, &return_rax);
|
__ j(not_equal, ¬_original_string, Label::kNear);
|
||||||
|
Counters* counters = masm->isolate()->counters();
|
||||||
|
__ IncrementCounter(counters->sub_string_native(), 1);
|
||||||
|
__ ret(kArgumentsSize);
|
||||||
|
__ bind(¬_original_string);
|
||||||
// Special handling of sub-strings of length 1 and 2. One character strings
|
// Special handling of sub-strings of length 1 and 2. One character strings
|
||||||
// are handled in the runtime system (looked up in the single character
|
// are handled in the runtime system (looked up in the single character
|
||||||
// cache). Two character strings are looked for in the symbol cache.
|
// cache). Two character strings are looked for in the symbol cache.
|
||||||
@ -5060,68 +5066,77 @@ void SubStringStub::Generate(MacroAssembler* masm) {
|
|||||||
// Get the two characters forming the sub string.
|
// Get the two characters forming the sub string.
|
||||||
__ SmiToInteger32(rdx, rdx); // From index is no longer smi.
|
__ SmiToInteger32(rdx, rdx); // From index is no longer smi.
|
||||||
__ movzxbq(rbx, FieldOperand(rax, rdx, times_1, SeqAsciiString::kHeaderSize));
|
__ movzxbq(rbx, FieldOperand(rax, rdx, times_1, SeqAsciiString::kHeaderSize));
|
||||||
__ movzxbq(rcx,
|
__ movzxbq(rdi,
|
||||||
FieldOperand(rax, rdx, times_1, SeqAsciiString::kHeaderSize + 1));
|
FieldOperand(rax, rdx, times_1, SeqAsciiString::kHeaderSize + 1));
|
||||||
|
|
||||||
// Try to lookup two character string in symbol table.
|
// Try to lookup two character string in symbol table.
|
||||||
Label make_two_character_string;
|
Label make_two_character_string;
|
||||||
StringHelper::GenerateTwoCharacterSymbolTableProbe(
|
StringHelper::GenerateTwoCharacterSymbolTableProbe(
|
||||||
masm, rbx, rcx, rax, rdx, rdi, r14, &make_two_character_string);
|
masm, rbx, rdi, r9, r11, r14, r15, &make_two_character_string);
|
||||||
|
__ IncrementCounter(counters->sub_string_native(), 1);
|
||||||
__ ret(3 * kPointerSize);
|
__ ret(3 * kPointerSize);
|
||||||
|
|
||||||
__ bind(&make_two_character_string);
|
__ bind(&make_two_character_string);
|
||||||
// Setup registers for allocating the two character string.
|
// Setup registers for allocating the two character string.
|
||||||
__ movq(rax, Operand(rsp, kStringOffset));
|
__ movzxwq(rbx, FieldOperand(rax, rdx, times_1, SeqAsciiString::kHeaderSize));
|
||||||
__ movq(rbx, FieldOperand(rax, HeapObject::kMapOffset));
|
__ AllocateAsciiString(rax, rcx, r11, r14, r15, &runtime);
|
||||||
|
__ movw(FieldOperand(rax, SeqAsciiString::kHeaderSize), rbx);
|
||||||
|
__ IncrementCounter(counters->sub_string_native(), 1);
|
||||||
|
__ ret(3 * kPointerSize);
|
||||||
|
|
||||||
|
__ bind(&result_longer_than_two);
|
||||||
|
// rax: string
|
||||||
|
// rbx: instance type
|
||||||
|
// rcx: sub string length
|
||||||
|
// rdx: from index (smi)
|
||||||
|
// Deal with different string types: update the index if necessary
|
||||||
|
// and put the underlying string into edi.
|
||||||
|
Label underlying_unpacked, sliced_string, seq_or_external_string;
|
||||||
|
// If the string is not indirect, it can only be sequential or external.
|
||||||
|
STATIC_ASSERT(kIsIndirectStringMask == (kSlicedStringTag & kConsStringTag));
|
||||||
|
STATIC_ASSERT(kIsIndirectStringMask != 0);
|
||||||
|
__ testb(rbx, Immediate(kIsIndirectStringMask));
|
||||||
|
__ j(zero, &seq_or_external_string, Label::kNear);
|
||||||
|
|
||||||
|
__ testb(rbx, Immediate(kSlicedNotConsMask));
|
||||||
|
__ j(not_zero, &sliced_string, Label::kNear);
|
||||||
|
// Cons string. Check whether it is flat, then fetch first part.
|
||||||
|
// Flat cons strings have an empty second part.
|
||||||
|
__ CompareRoot(FieldOperand(rax, ConsString::kSecondOffset),
|
||||||
|
Heap::kEmptyStringRootIndex);
|
||||||
|
__ j(not_equal, &runtime);
|
||||||
|
__ movq(rdi, FieldOperand(rax, ConsString::kFirstOffset));
|
||||||
|
// Update instance type.
|
||||||
|
__ movq(rbx, FieldOperand(rdi, HeapObject::kMapOffset));
|
||||||
__ movzxbl(rbx, FieldOperand(rbx, Map::kInstanceTypeOffset));
|
__ movzxbl(rbx, FieldOperand(rbx, Map::kInstanceTypeOffset));
|
||||||
__ Set(rcx, 2);
|
__ jmp(&underlying_unpacked, Label::kNear);
|
||||||
|
|
||||||
|
__ bind(&sliced_string);
|
||||||
|
// Sliced string. Fetch parent and correct start index by offset.
|
||||||
|
__ addq(rdx, FieldOperand(rax, SlicedString::kOffsetOffset));
|
||||||
|
__ movq(rdi, FieldOperand(rax, SlicedString::kParentOffset));
|
||||||
|
// Update instance type.
|
||||||
|
__ movq(rbx, FieldOperand(rdi, HeapObject::kMapOffset));
|
||||||
|
__ movzxbl(rbx, FieldOperand(rbx, Map::kInstanceTypeOffset));
|
||||||
|
__ jmp(&underlying_unpacked, Label::kNear);
|
||||||
|
|
||||||
|
__ bind(&seq_or_external_string);
|
||||||
|
// Sequential or external string. Just move string to the correct register.
|
||||||
|
__ movq(rdi, rax);
|
||||||
|
|
||||||
|
__ bind(&underlying_unpacked);
|
||||||
|
|
||||||
if (FLAG_string_slices) {
|
if (FLAG_string_slices) {
|
||||||
Label copy_routine;
|
Label copy_routine;
|
||||||
|
// rdi: underlying subject string
|
||||||
|
// rbx: instance type of underlying subject string
|
||||||
|
// rdx: adjusted start index (smi)
|
||||||
|
// rcx: length
|
||||||
// If coming from the make_two_character_string path, the string
|
// If coming from the make_two_character_string path, the string
|
||||||
// is too short to be sliced anyways.
|
// is too short to be sliced anyways.
|
||||||
STATIC_ASSERT(2 < SlicedString::kMinLength);
|
|
||||||
__ jmp(©_routine);
|
|
||||||
__ bind(&result_longer_than_two);
|
|
||||||
|
|
||||||
// rax: string
|
|
||||||
// rbx: instance type
|
|
||||||
// rcx: sub string length
|
|
||||||
// rdx: from index (smi)
|
|
||||||
Label allocate_slice, sliced_string, seq_or_external_string;
|
|
||||||
__ cmpq(rcx, Immediate(SlicedString::kMinLength));
|
__ cmpq(rcx, Immediate(SlicedString::kMinLength));
|
||||||
// Short slice. Copy instead of slicing.
|
// Short slice. Copy instead of slicing.
|
||||||
__ j(less, ©_routine);
|
__ j(less, ©_routine);
|
||||||
// If the string is not indirect, it can only be sequential or external.
|
|
||||||
STATIC_ASSERT(kIsIndirectStringMask == (kSlicedStringTag & kConsStringTag));
|
|
||||||
STATIC_ASSERT(kIsIndirectStringMask != 0);
|
|
||||||
__ testb(rbx, Immediate(kIsIndirectStringMask));
|
|
||||||
__ j(zero, &seq_or_external_string, Label::kNear);
|
|
||||||
|
|
||||||
__ testb(rbx, Immediate(kSlicedNotConsMask));
|
|
||||||
__ j(not_zero, &sliced_string, Label::kNear);
|
|
||||||
// Cons string. Check whether it is flat, then fetch first part.
|
|
||||||
__ CompareRoot(FieldOperand(rax, ConsString::kSecondOffset),
|
|
||||||
Heap::kEmptyStringRootIndex);
|
|
||||||
__ j(not_equal, &runtime);
|
|
||||||
__ movq(rdi, FieldOperand(rax, ConsString::kFirstOffset));
|
|
||||||
__ jmp(&allocate_slice, Label::kNear);
|
|
||||||
|
|
||||||
__ bind(&sliced_string);
|
|
||||||
// Sliced string. Fetch parent and correct start index by offset.
|
|
||||||
__ addq(rdx, FieldOperand(rax, SlicedString::kOffsetOffset));
|
|
||||||
__ movq(rdi, FieldOperand(rax, SlicedString::kParentOffset));
|
|
||||||
__ jmp(&allocate_slice, Label::kNear);
|
|
||||||
|
|
||||||
__ bind(&seq_or_external_string);
|
|
||||||
// Sequential or external string. Just move string to the correct register.
|
|
||||||
__ movq(rdi, rax);
|
|
||||||
|
|
||||||
__ bind(&allocate_slice);
|
|
||||||
// edi: underlying subject string
|
|
||||||
// ebx: instance type of original subject string
|
|
||||||
// edx: offset
|
|
||||||
// ecx: length
|
|
||||||
// Allocate new sliced string. At this point we do not reload the instance
|
// Allocate new sliced string. At this point we do not reload the instance
|
||||||
// type including the string encoding because we simply rely on the info
|
// type including the string encoding because we simply rely on the info
|
||||||
// provided by the original string. It does not matter if the original
|
// provided by the original string. It does not matter if the original
|
||||||
@ -5132,10 +5147,10 @@ void SubStringStub::Generate(MacroAssembler* masm) {
|
|||||||
STATIC_ASSERT((kStringEncodingMask & kTwoByteStringTag) == 0);
|
STATIC_ASSERT((kStringEncodingMask & kTwoByteStringTag) == 0);
|
||||||
__ testb(rbx, Immediate(kStringEncodingMask));
|
__ testb(rbx, Immediate(kStringEncodingMask));
|
||||||
__ j(zero, &two_byte_slice, Label::kNear);
|
__ j(zero, &two_byte_slice, Label::kNear);
|
||||||
__ AllocateAsciiSlicedString(rax, rbx, no_reg, &runtime);
|
__ AllocateAsciiSlicedString(rax, rbx, r14, &runtime);
|
||||||
__ jmp(&set_slice_header, Label::kNear);
|
__ jmp(&set_slice_header, Label::kNear);
|
||||||
__ bind(&two_byte_slice);
|
__ bind(&two_byte_slice);
|
||||||
__ AllocateTwoByteSlicedString(rax, rbx, no_reg, &runtime);
|
__ AllocateTwoByteSlicedString(rax, rbx, r14, &runtime);
|
||||||
__ bind(&set_slice_header);
|
__ bind(&set_slice_header);
|
||||||
__ movq(FieldOperand(rax, SlicedString::kOffsetOffset), rdx);
|
__ movq(FieldOperand(rax, SlicedString::kOffsetOffset), rdx);
|
||||||
__ Integer32ToSmi(rcx, rcx);
|
__ Integer32ToSmi(rcx, rcx);
|
||||||
@ -5143,82 +5158,85 @@ void SubStringStub::Generate(MacroAssembler* masm) {
|
|||||||
__ movq(FieldOperand(rax, SlicedString::kParentOffset), rdi);
|
__ movq(FieldOperand(rax, SlicedString::kParentOffset), rdi);
|
||||||
__ movq(FieldOperand(rax, SlicedString::kHashFieldOffset),
|
__ movq(FieldOperand(rax, SlicedString::kHashFieldOffset),
|
||||||
Immediate(String::kEmptyHashField));
|
Immediate(String::kEmptyHashField));
|
||||||
__ jmp(&return_rax);
|
__ IncrementCounter(counters->sub_string_native(), 1);
|
||||||
|
__ ret(kArgumentsSize);
|
||||||
|
|
||||||
__ bind(©_routine);
|
__ bind(©_routine);
|
||||||
} else {
|
|
||||||
__ bind(&result_longer_than_two);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// rax: string
|
// rdi: underlying subject string
|
||||||
// rbx: instance type
|
// rbx: instance type of underlying subject string
|
||||||
// rcx: result string length
|
// rdx: adjusted start index (smi)
|
||||||
// Check for flat ascii string
|
// rcx: length
|
||||||
Label non_ascii_flat;
|
// The subject string can only be external or sequential string of either
|
||||||
__ JumpIfInstanceTypeIsNotSequentialAscii(rbx, rbx, &non_ascii_flat);
|
// encoding at this point.
|
||||||
|
Label two_byte_sequential, sequential_string;
|
||||||
|
STATIC_ASSERT(kExternalStringTag != 0);
|
||||||
|
STATIC_ASSERT(kSeqStringTag == 0);
|
||||||
|
__ testb(rbx, Immediate(kExternalStringTag));
|
||||||
|
__ j(zero, &sequential_string);
|
||||||
|
|
||||||
|
// Handle external string.
|
||||||
|
// Rule out short external strings.
|
||||||
|
STATIC_CHECK(kShortExternalStringTag != 0);
|
||||||
|
__ testb(rbx, Immediate(kShortExternalStringMask));
|
||||||
|
__ j(not_zero, &runtime);
|
||||||
|
__ movq(rdi, FieldOperand(rdi, ExternalString::kResourceDataOffset));
|
||||||
|
// Move the pointer so that offset-wise, it looks like a sequential string.
|
||||||
|
STATIC_ASSERT(SeqTwoByteString::kHeaderSize == SeqAsciiString::kHeaderSize);
|
||||||
|
__ subq(rdi, Immediate(SeqTwoByteString::kHeaderSize - kHeapObjectTag));
|
||||||
|
|
||||||
|
__ bind(&sequential_string);
|
||||||
|
STATIC_ASSERT((kAsciiStringTag & kStringEncodingMask) != 0);
|
||||||
|
__ testb(rbx, Immediate(kStringEncodingMask));
|
||||||
|
__ j(zero, &two_byte_sequential);
|
||||||
|
|
||||||
// Allocate the result.
|
// Allocate the result.
|
||||||
__ AllocateAsciiString(rax, rcx, rbx, rdx, rdi, &runtime);
|
__ AllocateAsciiString(rax, rcx, r11, r14, r15, &runtime);
|
||||||
|
|
||||||
// rax: result string
|
// rax: result string
|
||||||
// rcx: result string length
|
// rcx: result string length
|
||||||
__ movq(rdx, rsi); // esi used by following code.
|
__ movq(r14, rsi); // esi used by following code.
|
||||||
// Locate first character of result.
|
{ // Locate character of sub string start.
|
||||||
__ lea(rdi, FieldOperand(rax, SeqAsciiString::kHeaderSize));
|
SmiIndex smi_as_index = masm->SmiToIndex(rdx, rdx, times_1);
|
||||||
// Load string argument and locate character of sub string start.
|
__ lea(rsi, Operand(rdi, smi_as_index.reg, smi_as_index.scale,
|
||||||
__ movq(rsi, Operand(rsp, kStringOffset));
|
|
||||||
__ movq(rbx, Operand(rsp, kFromOffset));
|
|
||||||
{
|
|
||||||
SmiIndex smi_as_index = masm->SmiToIndex(rbx, rbx, times_1);
|
|
||||||
__ lea(rsi, Operand(rsi, smi_as_index.reg, smi_as_index.scale,
|
|
||||||
SeqAsciiString::kHeaderSize - kHeapObjectTag));
|
SeqAsciiString::kHeaderSize - kHeapObjectTag));
|
||||||
}
|
}
|
||||||
|
// Locate first character of result.
|
||||||
|
__ lea(rdi, FieldOperand(rax, SeqAsciiString::kHeaderSize));
|
||||||
|
|
||||||
// rax: result string
|
// rax: result string
|
||||||
// rcx: result length
|
// rcx: result length
|
||||||
// rdx: original value of rsi
|
|
||||||
// rdi: first character of result
|
// rdi: first character of result
|
||||||
// rsi: character of sub string start
|
// rsi: character of sub string start
|
||||||
|
// r14: original value of rsi
|
||||||
StringHelper::GenerateCopyCharactersREP(masm, rdi, rsi, rcx, true);
|
StringHelper::GenerateCopyCharactersREP(masm, rdi, rsi, rcx, true);
|
||||||
__ movq(rsi, rdx); // Restore rsi.
|
__ movq(rsi, r14); // Restore rsi.
|
||||||
Counters* counters = masm->isolate()->counters();
|
|
||||||
__ IncrementCounter(counters->sub_string_native(), 1);
|
__ IncrementCounter(counters->sub_string_native(), 1);
|
||||||
__ ret(kArgumentsSize);
|
__ ret(kArgumentsSize);
|
||||||
|
|
||||||
__ bind(&non_ascii_flat);
|
__ bind(&two_byte_sequential);
|
||||||
// rax: string
|
|
||||||
// rbx: instance type & kStringRepresentationMask | kStringEncodingMask
|
|
||||||
// rcx: result string length
|
|
||||||
// Check for sequential two byte string
|
|
||||||
__ cmpb(rbx, Immediate(kSeqStringTag | kTwoByteStringTag));
|
|
||||||
__ j(not_equal, &runtime);
|
|
||||||
|
|
||||||
// Allocate the result.
|
// Allocate the result.
|
||||||
__ AllocateTwoByteString(rax, rcx, rbx, rdx, rdi, &runtime);
|
__ AllocateTwoByteString(rax, rcx, r11, r14, r15, &runtime);
|
||||||
|
|
||||||
// rax: result string
|
// rax: result string
|
||||||
// rcx: result string length
|
// rcx: result string length
|
||||||
__ movq(rdx, rsi); // esi used by following code.
|
__ movq(r14, rsi); // esi used by following code.
|
||||||
// Locate first character of result.
|
{ // Locate character of sub string start.
|
||||||
__ lea(rdi, FieldOperand(rax, SeqTwoByteString::kHeaderSize));
|
SmiIndex smi_as_index = masm->SmiToIndex(rdx, rdx, times_2);
|
||||||
// Load string argument and locate character of sub string start.
|
__ lea(rsi, Operand(rdi, smi_as_index.reg, smi_as_index.scale,
|
||||||
__ movq(rsi, Operand(rsp, kStringOffset));
|
|
||||||
__ movq(rbx, Operand(rsp, kFromOffset));
|
|
||||||
{
|
|
||||||
SmiIndex smi_as_index = masm->SmiToIndex(rbx, rbx, times_2);
|
|
||||||
__ lea(rsi, Operand(rsi, smi_as_index.reg, smi_as_index.scale,
|
|
||||||
SeqAsciiString::kHeaderSize - kHeapObjectTag));
|
SeqAsciiString::kHeaderSize - kHeapObjectTag));
|
||||||
}
|
}
|
||||||
|
// Locate first character of result.
|
||||||
|
__ lea(rdi, FieldOperand(rax, SeqTwoByteString::kHeaderSize));
|
||||||
|
|
||||||
// rax: result string
|
// rax: result string
|
||||||
// rcx: result length
|
// rcx: result length
|
||||||
// rdx: original value of rsi
|
|
||||||
// rdi: first character of result
|
// rdi: first character of result
|
||||||
// rsi: character of sub string start
|
// rsi: character of sub string start
|
||||||
|
// r14: original value of rsi
|
||||||
StringHelper::GenerateCopyCharactersREP(masm, rdi, rsi, rcx, false);
|
StringHelper::GenerateCopyCharactersREP(masm, rdi, rsi, rcx, false);
|
||||||
__ movq(rsi, rdx); // Restore esi.
|
__ movq(rsi, r14); // Restore esi.
|
||||||
|
|
||||||
__ bind(&return_rax);
|
|
||||||
__ IncrementCounter(counters->sub_string_native(), 1);
|
__ IncrementCounter(counters->sub_string_native(), 1);
|
||||||
__ ret(kArgumentsSize);
|
__ ret(kArgumentsSize);
|
||||||
|
|
||||||
|
16
deps/v8/src/x64/ic-x64.cc
vendored
16
deps/v8/src/x64/ic-x64.cc
vendored
@ -1397,11 +1397,10 @@ void StoreIC::GenerateArrayLength(MacroAssembler* masm) {
|
|||||||
// -- rsp[0] : return address
|
// -- rsp[0] : return address
|
||||||
// -----------------------------------
|
// -----------------------------------
|
||||||
//
|
//
|
||||||
// This accepts as a receiver anything JSObject::SetElementsLength accepts
|
// This accepts as a receiver anything JSArray::SetElementsLength accepts
|
||||||
// (currently anything except for external and pixel arrays which means
|
// (currently anything except for external arrays which means anything with
|
||||||
// anything with elements of FixedArray type.), but currently is restricted
|
// elements of FixedArray type). Value must be a number, but only smis are
|
||||||
// to JSArray.
|
// accepted as the most common case.
|
||||||
// Value must be a number, but only smis are accepted as the most common case.
|
|
||||||
|
|
||||||
Label miss;
|
Label miss;
|
||||||
|
|
||||||
@ -1423,6 +1422,13 @@ void StoreIC::GenerateArrayLength(MacroAssembler* masm) {
|
|||||||
__ CmpObjectType(scratch, FIXED_ARRAY_TYPE, scratch);
|
__ CmpObjectType(scratch, FIXED_ARRAY_TYPE, scratch);
|
||||||
__ j(not_equal, &miss);
|
__ j(not_equal, &miss);
|
||||||
|
|
||||||
|
// Check that the array has fast properties, otherwise the length
|
||||||
|
// property might have been redefined.
|
||||||
|
__ movq(scratch, FieldOperand(receiver, JSArray::kPropertiesOffset));
|
||||||
|
__ CompareRoot(FieldOperand(scratch, FixedArray::kMapOffset),
|
||||||
|
Heap::kHashTableMapRootIndex);
|
||||||
|
__ j(equal, &miss);
|
||||||
|
|
||||||
// Check that value is a smi.
|
// Check that value is a smi.
|
||||||
__ JumpIfNotSmi(value, &miss);
|
__ JumpIfNotSmi(value, &miss);
|
||||||
|
|
||||||
|
21
deps/v8/src/x64/lithium-codegen-x64.cc
vendored
21
deps/v8/src/x64/lithium-codegen-x64.cc
vendored
@ -2069,7 +2069,14 @@ void LCodeGen::DoLoadContextSlot(LLoadContextSlot* instr) {
|
|||||||
__ movq(result, ContextOperand(context, instr->slot_index()));
|
__ movq(result, ContextOperand(context, instr->slot_index()));
|
||||||
if (instr->hydrogen()->RequiresHoleCheck()) {
|
if (instr->hydrogen()->RequiresHoleCheck()) {
|
||||||
__ CompareRoot(result, Heap::kTheHoleValueRootIndex);
|
__ CompareRoot(result, Heap::kTheHoleValueRootIndex);
|
||||||
DeoptimizeIf(equal, instr->environment());
|
if (instr->hydrogen()->DeoptimizesOnHole()) {
|
||||||
|
DeoptimizeIf(equal, instr->environment());
|
||||||
|
} else {
|
||||||
|
Label is_not_hole;
|
||||||
|
__ j(not_equal, &is_not_hole, Label::kNear);
|
||||||
|
__ LoadRoot(result, Heap::kUndefinedValueRootIndex);
|
||||||
|
__ bind(&is_not_hole);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -2077,12 +2084,20 @@ void LCodeGen::DoLoadContextSlot(LLoadContextSlot* instr) {
|
|||||||
void LCodeGen::DoStoreContextSlot(LStoreContextSlot* instr) {
|
void LCodeGen::DoStoreContextSlot(LStoreContextSlot* instr) {
|
||||||
Register context = ToRegister(instr->context());
|
Register context = ToRegister(instr->context());
|
||||||
Register value = ToRegister(instr->value());
|
Register value = ToRegister(instr->value());
|
||||||
|
|
||||||
Operand target = ContextOperand(context, instr->slot_index());
|
Operand target = ContextOperand(context, instr->slot_index());
|
||||||
|
|
||||||
|
Label skip_assignment;
|
||||||
if (instr->hydrogen()->RequiresHoleCheck()) {
|
if (instr->hydrogen()->RequiresHoleCheck()) {
|
||||||
__ CompareRoot(target, Heap::kTheHoleValueRootIndex);
|
__ CompareRoot(target, Heap::kTheHoleValueRootIndex);
|
||||||
DeoptimizeIf(equal, instr->environment());
|
if (instr->hydrogen()->DeoptimizesOnHole()) {
|
||||||
|
DeoptimizeIf(equal, instr->environment());
|
||||||
|
} else {
|
||||||
|
__ j(not_equal, &skip_assignment);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
__ movq(target, value);
|
__ movq(target, value);
|
||||||
|
|
||||||
if (instr->hydrogen()->NeedsWriteBarrier()) {
|
if (instr->hydrogen()->NeedsWriteBarrier()) {
|
||||||
HType type = instr->hydrogen()->value()->type();
|
HType type = instr->hydrogen()->value()->type();
|
||||||
SmiCheck check_needed =
|
SmiCheck check_needed =
|
||||||
@ -2097,6 +2112,8 @@ void LCodeGen::DoStoreContextSlot(LStoreContextSlot* instr) {
|
|||||||
EMIT_REMEMBERED_SET,
|
EMIT_REMEMBERED_SET,
|
||||||
check_needed);
|
check_needed);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
__ bind(&skip_assignment);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
4
deps/v8/test/cctest/cctest.status
vendored
4
deps/v8/test/cctest/cctest.status
vendored
@ -52,6 +52,10 @@ test-profile-generator/RecordStackTraceAtStartProfiling: PASS || FAIL
|
|||||||
# We do not yet shrink weak maps after they have been emptied by the GC
|
# We do not yet shrink weak maps after they have been emptied by the GC
|
||||||
test-weakmaps/Shrinking: FAIL
|
test-weakmaps/Shrinking: FAIL
|
||||||
|
|
||||||
|
# TODO(1823): Fails without snapshot. Temporarily disabled until fixed.
|
||||||
|
test-heap/LeakGlobalContextViaMap: SKIP
|
||||||
|
test-heap/LeakGlobalContextViaFunction: SKIP
|
||||||
|
|
||||||
##############################################################################
|
##############################################################################
|
||||||
[ $arch == arm ]
|
[ $arch == arm ]
|
||||||
|
|
||||||
|
3
deps/v8/test/cctest/test-hashing.cc
vendored
3
deps/v8/test/cctest/test-hashing.cc
vendored
@ -106,6 +106,7 @@ void generate(MacroAssembler* assm, i::Vector<const char> string) {
|
|||||||
}
|
}
|
||||||
StringHelper::GenerateHashGetHash(assm, v0);
|
StringHelper::GenerateHashGetHash(assm, v0);
|
||||||
__ jr(ra);
|
__ jr(ra);
|
||||||
|
__ nop();
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -125,7 +126,7 @@ void check(i::Vector<const char> string) {
|
|||||||
Handle<Object>(HEAP->undefined_value()))->ToObjectChecked());
|
Handle<Object>(HEAP->undefined_value()))->ToObjectChecked());
|
||||||
CHECK(code->IsCode());
|
CHECK(code->IsCode());
|
||||||
|
|
||||||
HASH_FUNCTION hash = FUNCTION_CAST<HASH_FUNCTION>(Code::cast(code)->entry());
|
HASH_FUNCTION hash = FUNCTION_CAST<HASH_FUNCTION>(code->entry());
|
||||||
Handle<String> v8_string = FACTORY->NewStringFromAscii(string);
|
Handle<String> v8_string = FACTORY->NewStringFromAscii(string);
|
||||||
v8_string->set_hash_field(String::kEmptyHashField);
|
v8_string->set_hash_field(String::kEmptyHashField);
|
||||||
#ifdef USE_SIMULATOR
|
#ifdef USE_SIMULATOR
|
||||||
|
80
deps/v8/test/cctest/test-heap.cc
vendored
80
deps/v8/test/cctest/test-heap.cc
vendored
@ -1318,3 +1318,83 @@ TEST(IdleNotificationAdvancesIncrementalMarking) {
|
|||||||
intptr_t new_size = HEAP->SizeOfObjects();
|
intptr_t new_size = HEAP->SizeOfObjects();
|
||||||
CHECK(no_idle_work || new_size < old_size);
|
CHECK(no_idle_work || new_size < old_size);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static int NumberOfGlobalObjects() {
|
||||||
|
int count = 0;
|
||||||
|
HeapIterator iterator;
|
||||||
|
for (HeapObject* obj = iterator.next(); obj != NULL; obj = iterator.next()) {
|
||||||
|
if (obj->IsGlobalObject()) count++;
|
||||||
|
}
|
||||||
|
return count;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// Test that we don't embed maps from foreign contexts into
|
||||||
|
// optimized code.
|
||||||
|
TEST(LeakGlobalContextViaMap) {
|
||||||
|
v8::HandleScope outer_scope;
|
||||||
|
v8::Persistent<v8::Context> ctx1 = v8::Context::New();
|
||||||
|
v8::Persistent<v8::Context> ctx2 = v8::Context::New();
|
||||||
|
ctx1->Enter();
|
||||||
|
|
||||||
|
HEAP->CollectAllAvailableGarbage();
|
||||||
|
CHECK_EQ(4, NumberOfGlobalObjects());
|
||||||
|
|
||||||
|
{
|
||||||
|
v8::HandleScope inner_scope;
|
||||||
|
CompileRun("var v = {x: 42}");
|
||||||
|
v8::Local<v8::Value> v = ctx1->Global()->Get(v8_str("v"));
|
||||||
|
ctx2->Enter();
|
||||||
|
ctx2->Global()->Set(v8_str("o"), v);
|
||||||
|
v8::Local<v8::Value> res = CompileRun(
|
||||||
|
"function f() { return o.x; }"
|
||||||
|
"for (var i = 0; i < 1000000; ++i) f();"
|
||||||
|
"f();");
|
||||||
|
CHECK_EQ(42, res->Int32Value());
|
||||||
|
ctx2->Global()->Set(v8_str("o"), v8::Int32::New(0));
|
||||||
|
ctx2->Exit();
|
||||||
|
ctx1->Exit();
|
||||||
|
ctx1.Dispose();
|
||||||
|
}
|
||||||
|
HEAP->CollectAllAvailableGarbage();
|
||||||
|
CHECK_EQ(2, NumberOfGlobalObjects());
|
||||||
|
ctx2.Dispose();
|
||||||
|
HEAP->CollectAllAvailableGarbage();
|
||||||
|
CHECK_EQ(0, NumberOfGlobalObjects());
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// Test that we don't embed functions from foreign contexts into
|
||||||
|
// optimized code.
|
||||||
|
TEST(LeakGlobalContextViaFunction) {
|
||||||
|
v8::HandleScope outer_scope;
|
||||||
|
v8::Persistent<v8::Context> ctx1 = v8::Context::New();
|
||||||
|
v8::Persistent<v8::Context> ctx2 = v8::Context::New();
|
||||||
|
ctx1->Enter();
|
||||||
|
|
||||||
|
HEAP->CollectAllAvailableGarbage();
|
||||||
|
CHECK_EQ(4, NumberOfGlobalObjects());
|
||||||
|
|
||||||
|
{
|
||||||
|
v8::HandleScope inner_scope;
|
||||||
|
CompileRun("var v = function() { return 42; }");
|
||||||
|
v8::Local<v8::Value> v = ctx1->Global()->Get(v8_str("v"));
|
||||||
|
ctx2->Enter();
|
||||||
|
ctx2->Global()->Set(v8_str("o"), v);
|
||||||
|
v8::Local<v8::Value> res = CompileRun(
|
||||||
|
"function f(x) { return x(); }"
|
||||||
|
"for (var i = 0; i < 1000000; ++i) f(o);"
|
||||||
|
"f(o);");
|
||||||
|
CHECK_EQ(42, res->Int32Value());
|
||||||
|
ctx2->Global()->Set(v8_str("o"), v8::Int32::New(0));
|
||||||
|
ctx2->Exit();
|
||||||
|
ctx1->Exit();
|
||||||
|
ctx1.Dispose();
|
||||||
|
}
|
||||||
|
HEAP->CollectAllAvailableGarbage();
|
||||||
|
CHECK_EQ(2, NumberOfGlobalObjects());
|
||||||
|
ctx2.Dispose();
|
||||||
|
HEAP->CollectAllAvailableGarbage();
|
||||||
|
CHECK_EQ(0, NumberOfGlobalObjects());
|
||||||
|
}
|
||||||
|
6
deps/v8/test/cctest/test-regexp.cc
vendored
6
deps/v8/test/cctest/test-regexp.cc
vendored
@ -836,7 +836,8 @@ TEST(MacroAssemblerNativeSimpleUC16) {
|
|||||||
Handle<Code> code = Handle<Code>::cast(code_object);
|
Handle<Code> code = Handle<Code>::cast(code_object);
|
||||||
|
|
||||||
int captures[4] = {42, 37, 87, 117};
|
int captures[4] = {42, 37, 87, 117};
|
||||||
const uc16 input_data[6] = {'f', 'o', 'o', 'f', 'o', '\xa0'};
|
const uc16 input_data[6] = {'f', 'o', 'o', 'f', 'o',
|
||||||
|
static_cast<uc16>('\xa0')};
|
||||||
Handle<String> input =
|
Handle<String> input =
|
||||||
factory->NewStringFromTwoByte(Vector<const uc16>(input_data, 6));
|
factory->NewStringFromTwoByte(Vector<const uc16>(input_data, 6));
|
||||||
Handle<SeqTwoByteString> seq_input = Handle<SeqTwoByteString>::cast(input);
|
Handle<SeqTwoByteString> seq_input = Handle<SeqTwoByteString>::cast(input);
|
||||||
@ -856,7 +857,8 @@ TEST(MacroAssemblerNativeSimpleUC16) {
|
|||||||
CHECK_EQ(-1, captures[2]);
|
CHECK_EQ(-1, captures[2]);
|
||||||
CHECK_EQ(-1, captures[3]);
|
CHECK_EQ(-1, captures[3]);
|
||||||
|
|
||||||
const uc16 input_data2[9] = {'b', 'a', 'r', 'b', 'a', 'r', 'b', 'a', '\xa0'};
|
const uc16 input_data2[9] = {'b', 'a', 'r', 'b', 'a', 'r', 'b', 'a',
|
||||||
|
static_cast<uc16>('\xa0')};
|
||||||
input = factory->NewStringFromTwoByte(Vector<const uc16>(input_data2, 9));
|
input = factory->NewStringFromTwoByte(Vector<const uc16>(input_data2, 9));
|
||||||
seq_input = Handle<SeqTwoByteString>::cast(input);
|
seq_input = Handle<SeqTwoByteString>::cast(input);
|
||||||
start_adr = seq_input->GetCharsAddress();
|
start_adr = seq_input->GetCharsAddress();
|
||||||
|
@ -144,9 +144,9 @@ if (support_smi_only_arrays) {
|
|||||||
var array = deopt_array(false);
|
var array = deopt_array(false);
|
||||||
assertTrue(2 != %GetOptimizationStatus(deopt_array));
|
assertTrue(2 != %GetOptimizationStatus(deopt_array));
|
||||||
deopt_array(true);
|
deopt_array(true);
|
||||||
assertTrue(1 != %GetOptimizationStatus(deopt_array));
|
assertTrue(2 != %GetOptimizationStatus(deopt_array));
|
||||||
array = deopt_array(false);
|
array = deopt_array(false);
|
||||||
assertTrue(1 != %GetOptimizationStatus(deopt_array));
|
assertTrue(2 != %GetOptimizationStatus(deopt_array));
|
||||||
|
|
||||||
// Check that unexpected changes in the objects stored into the boilerplate
|
// Check that unexpected changes in the objects stored into the boilerplate
|
||||||
// also force a deopt.
|
// also force a deopt.
|
||||||
@ -201,3 +201,10 @@ if (support_smi_only_arrays) {
|
|||||||
assertEquals(1, array[1]);
|
assertEquals(1, array[1]);
|
||||||
assertEquals(foo, array[2]);
|
assertEquals(foo, array[2]);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
(function literals_after_osr() {
|
||||||
|
var color = [0];
|
||||||
|
// Trigger OSR.
|
||||||
|
while (%GetOptimizationStatus(literals_after_osr) == 2) {}
|
||||||
|
return [color[0]];
|
||||||
|
})();
|
||||||
|
73
deps/v8/test/mjsunit/d8-os.js
vendored
73
deps/v8/test/mjsunit/d8-os.js
vendored
@ -54,6 +54,8 @@ function str_error(str) {
|
|||||||
|
|
||||||
|
|
||||||
if (this.os && os.system) {
|
if (this.os && os.system) {
|
||||||
|
// Ensure that we have a valid working directory.
|
||||||
|
os.chdir("/tmp");
|
||||||
try {
|
try {
|
||||||
// Delete the dir if it is lying around from last time.
|
// Delete the dir if it is lying around from last time.
|
||||||
os.system("ls", [TEST_DIR]);
|
os.system("ls", [TEST_DIR]);
|
||||||
@ -143,42 +145,43 @@ if (this.os && os.system) {
|
|||||||
assertEquals("baz\n", os.system("echo", ["baz"]));
|
assertEquals("baz\n", os.system("echo", ["baz"]));
|
||||||
//}
|
//}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Too few args.
|
||||||
|
arg_error("os.umask();");
|
||||||
|
arg_error("os.system();");
|
||||||
|
arg_error("os.mkdirp();");
|
||||||
|
arg_error("os.chdir();");
|
||||||
|
arg_error("os.setenv();");
|
||||||
|
arg_error("os.rmdir();");
|
||||||
|
|
||||||
|
// Too many args.
|
||||||
|
arg_error("os.setenv('FOO=bar');");
|
||||||
|
arg_error("os.umask(0, 0);");
|
||||||
|
arg_error("os.system('ls', [], -1, -1, -1);");
|
||||||
|
arg_error("os.mkdirp('foo', 0, 0)");
|
||||||
|
arg_error("os.chdir('foo', 'bar')");
|
||||||
|
arg_error("os.rmdir('foo', 'bar');");
|
||||||
|
|
||||||
|
// Wrong kind of args.
|
||||||
|
arg_error("os.umask([]);");
|
||||||
|
arg_error("os.system('ls', 'foo');");
|
||||||
|
arg_error("os.system('ls', 123);");
|
||||||
|
arg_error("os.system('ls', [], 'foo');");
|
||||||
|
arg_error("os.system('ls', [], -1, 'foo');");
|
||||||
|
arg_error("os.mkdirp('foo', 'bar');");
|
||||||
|
|
||||||
|
// Test broken toString().
|
||||||
|
str_error("os.system(e);");
|
||||||
|
str_error("os.system('ls', [e]);");
|
||||||
|
str_error("os.system('ls', ['.', e]);");
|
||||||
|
str_error("os.system('ls', [e, '.']);");
|
||||||
|
str_error("os.mkdirp(e);");
|
||||||
|
str_error("os.setenv(e, 'goo');");
|
||||||
|
str_error("os.setenv('goo', e);");
|
||||||
|
str_error("os.chdir(e);");
|
||||||
|
str_error("os.rmdir(e);");
|
||||||
|
|
||||||
} finally {
|
} finally {
|
||||||
os.system("rm", ["-r", TEST_DIR]);
|
os.system("rm", ["-r", TEST_DIR]);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Too few args.
|
|
||||||
arg_error("os.umask();");
|
|
||||||
arg_error("os.system();");
|
|
||||||
arg_error("os.mkdirp();");
|
|
||||||
arg_error("os.chdir();");
|
|
||||||
arg_error("os.setenv();");
|
|
||||||
arg_error("os.rmdir();");
|
|
||||||
|
|
||||||
// Too many args.
|
|
||||||
arg_error("os.setenv('FOO=bar');");
|
|
||||||
arg_error("os.umask(0, 0);");
|
|
||||||
arg_error("os.system('ls', [], -1, -1, -1);");
|
|
||||||
arg_error("os.mkdirp('foo', 0, 0)");
|
|
||||||
arg_error("os.chdir('foo', 'bar')");
|
|
||||||
arg_error("os.rmdir('foo', 'bar');");
|
|
||||||
|
|
||||||
// Wrong kind of args.
|
|
||||||
arg_error("os.umask([]);");
|
|
||||||
arg_error("os.system('ls', 'foo');");
|
|
||||||
arg_error("os.system('ls', 123);");
|
|
||||||
arg_error("os.system('ls', [], 'foo');");
|
|
||||||
arg_error("os.system('ls', [], -1, 'foo');");
|
|
||||||
arg_error("os.mkdirp('foo', 'bar');");
|
|
||||||
|
|
||||||
// Test broken toString().
|
|
||||||
str_error("os.system(e);");
|
|
||||||
str_error("os.system('ls', [e]);");
|
|
||||||
str_error("os.system('ls', ['.', e]);");
|
|
||||||
str_error("os.system('ls', [e, '.']);");
|
|
||||||
str_error("os.mkdirp(e);");
|
|
||||||
str_error("os.setenv(e, 'goo');");
|
|
||||||
str_error("os.setenv('goo', e);");
|
|
||||||
str_error("os.chdir(e);");
|
|
||||||
str_error("os.rmdir(e);");
|
|
||||||
}
|
}
|
||||||
|
45
deps/v8/test/mjsunit/function-named-self-reference.js
vendored
Normal file
45
deps/v8/test/mjsunit/function-named-self-reference.js
vendored
Normal file
@ -0,0 +1,45 @@
|
|||||||
|
// Copyright 2011 the V8 project authors. All rights reserved.
|
||||||
|
// Redistribution and use in source and binary forms, with or without
|
||||||
|
// modification, are permitted provided that the following conditions are
|
||||||
|
// met:
|
||||||
|
//
|
||||||
|
// * Redistributions of source code must retain the above copyright
|
||||||
|
// notice, this list of conditions and the following disclaimer.
|
||||||
|
// * Redistributions in binary form must reproduce the above
|
||||||
|
// copyright notice, this list of conditions and the following
|
||||||
|
// disclaimer in the documentation and/or other materials provided
|
||||||
|
// with the distribution.
|
||||||
|
// * Neither the name of Google Inc. nor the names of its
|
||||||
|
// contributors may be used to endorse or promote products derived
|
||||||
|
// from this software without specific prior written permission.
|
||||||
|
//
|
||||||
|
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||||
|
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||||
|
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||||
|
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||||
|
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||||
|
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||||
|
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||||
|
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||||
|
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||||
|
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||||
|
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||||
|
|
||||||
|
// Flags: --allow-natives-syntax
|
||||||
|
|
||||||
|
var fn = function fn(val) {
|
||||||
|
if (val) return val;
|
||||||
|
|
||||||
|
%OptimizeFunctionOnNextCall(fn);
|
||||||
|
|
||||||
|
function run(val) {
|
||||||
|
var res = fn((val + 1) << 1);
|
||||||
|
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
|
||||||
|
return run(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
var res = fn();
|
||||||
|
assertEquals(res, 2);
|
39
deps/v8/test/mjsunit/regress/regress-crbug-100859.js
vendored
Normal file
39
deps/v8/test/mjsunit/regress/regress-crbug-100859.js
vendored
Normal file
@ -0,0 +1,39 @@
|
|||||||
|
// Copyright 2011 the V8 project authors. All rights reserved.
|
||||||
|
// Redistribution and use in source and binary forms, with or without
|
||||||
|
// modification, are permitted provided that the following conditions are
|
||||||
|
// met:
|
||||||
|
//
|
||||||
|
// * Redistributions of source code must retain the above copyright
|
||||||
|
// notice, this list of conditions and the following disclaimer.
|
||||||
|
// * Redistributions in binary form must reproduce the above
|
||||||
|
// copyright notice, this list of conditions and the following
|
||||||
|
// disclaimer in the documentation and/or other materials provided
|
||||||
|
// with the distribution.
|
||||||
|
// * Neither the name of Google Inc. nor the names of its
|
||||||
|
// contributors may be used to endorse or promote products derived
|
||||||
|
// from this software without specific prior written permission.
|
||||||
|
//
|
||||||
|
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||||
|
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||||
|
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||||
|
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||||
|
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||||
|
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||||
|
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||||
|
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||||
|
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||||
|
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||||
|
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||||
|
|
||||||
|
// This used to trigger a crash because of an unhandled stack overflow.
|
||||||
|
function setx() {
|
||||||
|
setx(typeof new Uint16Array('x') === 'object');
|
||||||
|
}
|
||||||
|
var exception = false;
|
||||||
|
try {
|
||||||
|
setx();
|
||||||
|
} catch (ex) {
|
||||||
|
assertTrue(ex instanceof RangeError);
|
||||||
|
exception = true;
|
||||||
|
}
|
||||||
|
assertTrue(exception);
|
23
deps/v8/test/mjsunit/string-external-cached.js
vendored
23
deps/v8/test/mjsunit/string-external-cached.js
vendored
@ -86,6 +86,29 @@ function test() {
|
|||||||
assertEquals("DEFG", ascii_cons.substr(3, 4));
|
assertEquals("DEFG", ascii_cons.substr(3, 4));
|
||||||
assertEquals("DEFG", twobyte_cons.substr(4, 4));
|
assertEquals("DEFG", twobyte_cons.substr(4, 4));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Test adding external strings
|
||||||
|
var short_ascii = "E=";
|
||||||
|
var long_ascii = "MCsquared";
|
||||||
|
var short_twobyte = "E\u1234";
|
||||||
|
var long_twobyte = "MCsquare\u1234";
|
||||||
|
try { // String can only be externalized once
|
||||||
|
externalizeString(short_ascii, false);
|
||||||
|
externalizeString(long_ascii, false);
|
||||||
|
externalizeString(short_twobyte, true);
|
||||||
|
externalizeString(long_twobyte, true);
|
||||||
|
assertTrue(isAsciiString(short_asii) && isAsciiString(long_ascii));
|
||||||
|
assertFalse(isAsciiString(short_twobyte) || isAsciiString(long_twobyte));
|
||||||
|
} catch (ex) { }
|
||||||
|
assertEquals("E=MCsquared", short_ascii + long_ascii);
|
||||||
|
assertTrue(isAsciiString(short_ascii + long_ascii));
|
||||||
|
assertEquals("MCsquaredE=", long_ascii + short_ascii);
|
||||||
|
assertEquals("E\u1234MCsquare\u1234", short_twobyte + long_twobyte);
|
||||||
|
assertFalse(isAsciiString(short_twobyte + long_twobyte));
|
||||||
|
assertEquals("E=MCsquared", "E=" + long_ascii);
|
||||||
|
assertEquals("E\u1234MCsquared", short_twobyte + "MCsquared");
|
||||||
|
assertEquals("E\u1234MCsquared", short_twobyte + long_ascii);
|
||||||
|
assertFalse(isAsciiString(short_twobyte + long_ascii));
|
||||||
}
|
}
|
||||||
|
|
||||||
// Run the test many times to ensure IC-s don't break things.
|
// Run the test many times to ensure IC-s don't break things.
|
||||||
|
43
deps/v8/test/test262/test262.status
vendored
43
deps/v8/test/test262/test262.status
vendored
@ -42,13 +42,11 @@ S10.4.2.1_A1: FAIL
|
|||||||
# V8 Bug: http://code.google.com/p/v8/issues/detail?id=1530
|
# V8 Bug: http://code.google.com/p/v8/issues/detail?id=1530
|
||||||
S15.3.3.1_A4: FAIL
|
S15.3.3.1_A4: FAIL
|
||||||
|
|
||||||
# V8 Bug: http://code.google.com/p/v8/issues/detail?id=1756
|
# V8 Bug: http://code.google.com/p/v8/issues/detail?id=1475
|
||||||
15.2.3.6-4-167: FAIL || PASS
|
15.2.3.6-4-405: FAIL
|
||||||
15.2.3.6-4-181: FAIL || PASS
|
15.2.3.6-4-410: FAIL
|
||||||
15.2.3.7-6-a-163: FAIL || PASS
|
15.2.3.6-4-415: FAIL
|
||||||
15.2.3.7-6-a-164: FAIL || PASS
|
15.2.3.6-4-420: FAIL
|
||||||
15.2.3.7-6-a-176: FAIL || PASS
|
|
||||||
15.2.3.7-6-a-177: FAIL || PASS
|
|
||||||
|
|
||||||
# V8 Bug: http://code.google.com/p/v8/issues/detail?id=1772
|
# V8 Bug: http://code.google.com/p/v8/issues/detail?id=1772
|
||||||
15.2.3.6-4-292-1: FAIL
|
15.2.3.6-4-292-1: FAIL
|
||||||
@ -207,37 +205,6 @@ S15.1.1.3_A2_T2: FAIL_OK # undefined
|
|||||||
S15.4.4.2_A2_T1: FAIL_OK
|
S15.4.4.2_A2_T1: FAIL_OK
|
||||||
S15.4.4.3_A2_T1: FAIL_OK
|
S15.4.4.3_A2_T1: FAIL_OK
|
||||||
|
|
||||||
######################### UNANALYZED FAILURES ##########################
|
|
||||||
|
|
||||||
# Bug? ES5 Attributes - Updating indexed data property 'P' whose attributes are
|
|
||||||
# [[Writable]]: false, [[Enumerable]]: true, [[Configurable]]: true to an
|
|
||||||
# accessor property, 'A' is an Array object (8.12.9 - step 9.b.i)
|
|
||||||
15.2.3.6-4-360-1: FAIL
|
|
||||||
# Bug? ES5 Attributes - Updating indexed data property 'P' whose attributes are
|
|
||||||
# [[Writable]]: false, [[Enumerable]]: true, [[Configurable]]: true to an
|
|
||||||
# accessor property, 'O' is an Arguments object (8.12.9 - step 9.b.i)
|
|
||||||
15.2.3.6-4-360-6: FAIL
|
|
||||||
# Bug? ES5 Attributes - Updating indexed data property 'P' whose attributes are
|
|
||||||
# [[Writable]]: false, [[Enumerable]]: true, [[Configurable]]: true to an
|
|
||||||
# accessor property, 'O' is the global object (8.12.9 - step 9.b.i)
|
|
||||||
15.2.3.6-4-360-7: FAIL
|
|
||||||
# Bug? ES5 Attributes - Failed to add a property to an object when the object's
|
|
||||||
# object has a property with same name and [[Writable]] attribute is set to
|
|
||||||
# false (Number instance)
|
|
||||||
15.2.3.6-4-405: FAIL
|
|
||||||
# Bug? ES5 Attributes - Failed to add a property to an object when the object's
|
|
||||||
# prototype has a property with the same name and [[Writable]] set to false
|
|
||||||
# (JSON)
|
|
||||||
15.2.3.6-4-410: FAIL
|
|
||||||
# Bug? ES5 Attributes - Failed to add properties to an object when the object's
|
|
||||||
# prototype has properties with the same name and [[Writable]] set to false
|
|
||||||
# (Object.create)
|
|
||||||
15.2.3.6-4-415: FAIL
|
|
||||||
# Bug? ES5 Attributes - Failed to add a property to an object when the object's
|
|
||||||
# prototype has a property with the same name and [[Writable]] set to
|
|
||||||
# false(Function.prototype.bind)
|
|
||||||
15.2.3.6-4-420: FAIL
|
|
||||||
|
|
||||||
############################ SKIPPED TESTS #############################
|
############################ SKIPPED TESTS #############################
|
||||||
|
|
||||||
# These tests take a looong time to run in debug mode.
|
# These tests take a looong time to run in debug mode.
|
||||||
|
1
deps/v8/test/test262/testcfg.py
vendored
1
deps/v8/test/test262/testcfg.py
vendored
@ -55,7 +55,6 @@ class Test262TestCase(test.TestCase):
|
|||||||
|
|
||||||
def GetCommand(self):
|
def GetCommand(self):
|
||||||
result = self.context.GetVmCommand(self, self.mode)
|
result = self.context.GetVmCommand(self, self.mode)
|
||||||
result += ['-e', 'var window = this']
|
|
||||||
result += self.framework
|
result += self.framework
|
||||||
result.append(self.filename)
|
result.append(self.filename)
|
||||||
return result
|
return result
|
||||||
|
328
deps/v8/tools/grokdump.py
vendored
328
deps/v8/tools/grokdump.py
vendored
@ -52,6 +52,7 @@ Examples:
|
|||||||
$ %prog 12345678-1234-1234-1234-123456789abcd-full.dmp
|
$ %prog 12345678-1234-1234-1234-123456789abcd-full.dmp
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
|
||||||
DEBUG=False
|
DEBUG=False
|
||||||
|
|
||||||
|
|
||||||
@ -233,6 +234,80 @@ MINIDUMP_CONTEXT_X86 = Descriptor([
|
|||||||
MD_CONTEXT_X86_EXTENDED_REGISTERS))
|
MD_CONTEXT_X86_EXTENDED_REGISTERS))
|
||||||
])
|
])
|
||||||
|
|
||||||
|
MD_CONTEXT_AMD64 = 0x00100000
|
||||||
|
MD_CONTEXT_AMD64_CONTROL = (MD_CONTEXT_AMD64 | 0x00000001)
|
||||||
|
MD_CONTEXT_AMD64_INTEGER = (MD_CONTEXT_AMD64 | 0x00000002)
|
||||||
|
MD_CONTEXT_AMD64_SEGMENTS = (MD_CONTEXT_AMD64 | 0x00000004)
|
||||||
|
MD_CONTEXT_AMD64_FLOATING_POINT = (MD_CONTEXT_AMD64 | 0x00000008)
|
||||||
|
MD_CONTEXT_AMD64_DEBUG_REGISTERS = (MD_CONTEXT_AMD64 | 0x00000010)
|
||||||
|
|
||||||
|
MINIDUMP_CONTEXT_AMD64 = Descriptor([
|
||||||
|
("p1_home", ctypes.c_uint64),
|
||||||
|
("p2_home", ctypes.c_uint64),
|
||||||
|
("p3_home", ctypes.c_uint64),
|
||||||
|
("p4_home", ctypes.c_uint64),
|
||||||
|
("p5_home", ctypes.c_uint64),
|
||||||
|
("p6_home", ctypes.c_uint64),
|
||||||
|
("context_flags", ctypes.c_uint32),
|
||||||
|
("mx_csr", ctypes.c_uint32),
|
||||||
|
# MD_CONTEXT_AMD64_CONTROL.
|
||||||
|
("cs", EnableOnFlag(ctypes.c_uint16, MD_CONTEXT_AMD64_CONTROL)),
|
||||||
|
# MD_CONTEXT_AMD64_SEGMENTS
|
||||||
|
("ds", EnableOnFlag(ctypes.c_uint16, MD_CONTEXT_AMD64_SEGMENTS)),
|
||||||
|
("es", EnableOnFlag(ctypes.c_uint16, MD_CONTEXT_AMD64_SEGMENTS)),
|
||||||
|
("fs", EnableOnFlag(ctypes.c_uint16, MD_CONTEXT_AMD64_SEGMENTS)),
|
||||||
|
("gs", EnableOnFlag(ctypes.c_uint16, MD_CONTEXT_AMD64_SEGMENTS)),
|
||||||
|
# MD_CONTEXT_AMD64_CONTROL.
|
||||||
|
("ss", EnableOnFlag(ctypes.c_uint16, MD_CONTEXT_AMD64_CONTROL)),
|
||||||
|
("eflags", EnableOnFlag(ctypes.c_uint32, MD_CONTEXT_AMD64_CONTROL)),
|
||||||
|
# MD_CONTEXT_AMD64_DEBUG_REGISTERS.
|
||||||
|
("dr0", EnableOnFlag(ctypes.c_uint64, MD_CONTEXT_AMD64_DEBUG_REGISTERS)),
|
||||||
|
("dr1", EnableOnFlag(ctypes.c_uint64, MD_CONTEXT_AMD64_DEBUG_REGISTERS)),
|
||||||
|
("dr2", EnableOnFlag(ctypes.c_uint64, MD_CONTEXT_AMD64_DEBUG_REGISTERS)),
|
||||||
|
("dr3", EnableOnFlag(ctypes.c_uint64, MD_CONTEXT_AMD64_DEBUG_REGISTERS)),
|
||||||
|
("dr6", EnableOnFlag(ctypes.c_uint64, MD_CONTEXT_AMD64_DEBUG_REGISTERS)),
|
||||||
|
("dr7", EnableOnFlag(ctypes.c_uint64, MD_CONTEXT_AMD64_DEBUG_REGISTERS)),
|
||||||
|
# MD_CONTEXT_AMD64_INTEGER.
|
||||||
|
("rax", EnableOnFlag(ctypes.c_uint64, MD_CONTEXT_AMD64_INTEGER)),
|
||||||
|
("rcx", EnableOnFlag(ctypes.c_uint64, MD_CONTEXT_AMD64_INTEGER)),
|
||||||
|
("rdx", EnableOnFlag(ctypes.c_uint64, MD_CONTEXT_AMD64_INTEGER)),
|
||||||
|
("rbx", EnableOnFlag(ctypes.c_uint64, MD_CONTEXT_AMD64_INTEGER)),
|
||||||
|
# MD_CONTEXT_AMD64_CONTROL.
|
||||||
|
("rsp", EnableOnFlag(ctypes.c_uint64, MD_CONTEXT_AMD64_CONTROL)),
|
||||||
|
# MD_CONTEXT_AMD64_INTEGER.
|
||||||
|
("rbp", EnableOnFlag(ctypes.c_uint64, MD_CONTEXT_AMD64_INTEGER)),
|
||||||
|
("rsi", EnableOnFlag(ctypes.c_uint64, MD_CONTEXT_AMD64_INTEGER)),
|
||||||
|
("rdi", EnableOnFlag(ctypes.c_uint64, MD_CONTEXT_AMD64_INTEGER)),
|
||||||
|
("r8", EnableOnFlag(ctypes.c_uint64, MD_CONTEXT_AMD64_INTEGER)),
|
||||||
|
("r9", EnableOnFlag(ctypes.c_uint64, MD_CONTEXT_AMD64_INTEGER)),
|
||||||
|
("r10", EnableOnFlag(ctypes.c_uint64, MD_CONTEXT_AMD64_INTEGER)),
|
||||||
|
("r11", EnableOnFlag(ctypes.c_uint64, MD_CONTEXT_AMD64_INTEGER)),
|
||||||
|
("r12", EnableOnFlag(ctypes.c_uint64, MD_CONTEXT_AMD64_INTEGER)),
|
||||||
|
("r13", EnableOnFlag(ctypes.c_uint64, MD_CONTEXT_AMD64_INTEGER)),
|
||||||
|
("r14", EnableOnFlag(ctypes.c_uint64, MD_CONTEXT_AMD64_INTEGER)),
|
||||||
|
("r15", EnableOnFlag(ctypes.c_uint64, MD_CONTEXT_AMD64_INTEGER)),
|
||||||
|
# MD_CONTEXT_AMD64_CONTROL.
|
||||||
|
("rip", EnableOnFlag(ctypes.c_uint64, MD_CONTEXT_AMD64_CONTROL)),
|
||||||
|
# MD_CONTEXT_AMD64_FLOATING_POINT
|
||||||
|
("sse_registers", EnableOnFlag(ctypes.c_uint8 * (16 * 26),
|
||||||
|
MD_CONTEXT_AMD64_FLOATING_POINT)),
|
||||||
|
("vector_registers", EnableOnFlag(ctypes.c_uint8 * (16 * 26),
|
||||||
|
MD_CONTEXT_AMD64_FLOATING_POINT)),
|
||||||
|
("vector_control", EnableOnFlag(ctypes.c_uint64,
|
||||||
|
MD_CONTEXT_AMD64_FLOATING_POINT)),
|
||||||
|
# MD_CONTEXT_AMD64_DEBUG_REGISTERS.
|
||||||
|
("debug_control", EnableOnFlag(ctypes.c_uint64,
|
||||||
|
MD_CONTEXT_AMD64_DEBUG_REGISTERS)),
|
||||||
|
("last_branch_to_rip", EnableOnFlag(ctypes.c_uint64,
|
||||||
|
MD_CONTEXT_AMD64_DEBUG_REGISTERS)),
|
||||||
|
("last_branch_from_rip", EnableOnFlag(ctypes.c_uint64,
|
||||||
|
MD_CONTEXT_AMD64_DEBUG_REGISTERS)),
|
||||||
|
("last_exception_to_rip", EnableOnFlag(ctypes.c_uint64,
|
||||||
|
MD_CONTEXT_AMD64_DEBUG_REGISTERS)),
|
||||||
|
("last_exception_from_rip", EnableOnFlag(ctypes.c_uint64,
|
||||||
|
MD_CONTEXT_AMD64_DEBUG_REGISTERS))
|
||||||
|
])
|
||||||
|
|
||||||
MINIDUMP_MEMORY_DESCRIPTOR = Descriptor([
|
MINIDUMP_MEMORY_DESCRIPTOR = Descriptor([
|
||||||
("start", ctypes.c_uint64),
|
("start", ctypes.c_uint64),
|
||||||
("memory", MINIDUMP_LOCATION_DESCRIPTOR.ctype)
|
("memory", MINIDUMP_LOCATION_DESCRIPTOR.ctype)
|
||||||
@ -269,6 +344,12 @@ MINIDUMP_THREAD_LIST = Descriptor([
|
|||||||
("threads", lambda t: MINIDUMP_THREAD.ctype * t.thread_count)
|
("threads", lambda t: MINIDUMP_THREAD.ctype * t.thread_count)
|
||||||
])
|
])
|
||||||
|
|
||||||
|
MINIDUMP_RAW_SYSTEM_INFO = Descriptor([
|
||||||
|
("processor_architecture", ctypes.c_uint16)
|
||||||
|
])
|
||||||
|
|
||||||
|
MD_CPU_ARCHITECTURE_X86 = 0
|
||||||
|
MD_CPU_ARCHITECTURE_AMD64 = 9
|
||||||
|
|
||||||
class MinidumpReader(object):
|
class MinidumpReader(object):
|
||||||
"""Minidump (.dmp) reader."""
|
"""Minidump (.dmp) reader."""
|
||||||
@ -288,20 +369,34 @@ class MinidumpReader(object):
|
|||||||
for _ in xrange(self.header.stream_count):
|
for _ in xrange(self.header.stream_count):
|
||||||
directories.append(MINIDUMP_DIRECTORY.Read(self.minidump, offset))
|
directories.append(MINIDUMP_DIRECTORY.Read(self.minidump, offset))
|
||||||
offset += MINIDUMP_DIRECTORY.size
|
offset += MINIDUMP_DIRECTORY.size
|
||||||
|
self.arch = None
|
||||||
self.exception = None
|
self.exception = None
|
||||||
self.exception_context = None
|
self.exception_context = None
|
||||||
self.memory_list = None
|
self.memory_list = None
|
||||||
self.memory_list64 = None
|
self.memory_list64 = None
|
||||||
self.thread_map = {}
|
self.thread_map = {}
|
||||||
|
|
||||||
|
# Find MDRawSystemInfo stream and determine arch.
|
||||||
|
for d in directories:
|
||||||
|
if d.stream_type == MD_SYSTEM_INFO_STREAM:
|
||||||
|
system_info = MINIDUMP_RAW_SYSTEM_INFO.Read(
|
||||||
|
self.minidump, d.location.rva)
|
||||||
|
self.arch = system_info.processor_architecture
|
||||||
|
assert self.arch in [MD_CPU_ARCHITECTURE_AMD64, MD_CPU_ARCHITECTURE_X86]
|
||||||
|
assert not self.arch is None
|
||||||
|
|
||||||
for d in directories:
|
for d in directories:
|
||||||
DebugPrint(d)
|
DebugPrint(d)
|
||||||
# TODO(vitalyr): extract system info including CPU features.
|
|
||||||
if d.stream_type == MD_EXCEPTION_STREAM:
|
if d.stream_type == MD_EXCEPTION_STREAM:
|
||||||
self.exception = MINIDUMP_EXCEPTION_STREAM.Read(
|
self.exception = MINIDUMP_EXCEPTION_STREAM.Read(
|
||||||
self.minidump, d.location.rva)
|
self.minidump, d.location.rva)
|
||||||
DebugPrint(self.exception)
|
DebugPrint(self.exception)
|
||||||
self.exception_context = MINIDUMP_CONTEXT_X86.Read(
|
if self.arch == MD_CPU_ARCHITECTURE_X86:
|
||||||
self.minidump, self.exception.thread_context.rva)
|
self.exception_context = MINIDUMP_CONTEXT_X86.Read(
|
||||||
|
self.minidump, self.exception.thread_context.rva)
|
||||||
|
elif self.arch == MD_CPU_ARCHITECTURE_AMD64:
|
||||||
|
self.exception_context = MINIDUMP_CONTEXT_AMD64.Read(
|
||||||
|
self.minidump, self.exception.thread_context.rva)
|
||||||
DebugPrint(self.exception_context)
|
DebugPrint(self.exception_context)
|
||||||
elif d.stream_type == MD_THREAD_LIST_STREAM:
|
elif d.stream_type == MD_THREAD_LIST_STREAM:
|
||||||
thread_list = MINIDUMP_THREAD_LIST.Read(self.minidump, d.location.rva)
|
thread_list = MINIDUMP_THREAD_LIST.Read(self.minidump, d.location.rva)
|
||||||
@ -335,6 +430,16 @@ class MinidumpReader(object):
|
|||||||
location = self.FindLocation(address)
|
location = self.FindLocation(address)
|
||||||
return ctypes.c_uint32.from_buffer(self.minidump, location).value
|
return ctypes.c_uint32.from_buffer(self.minidump, location).value
|
||||||
|
|
||||||
|
def ReadU64(self, address):
|
||||||
|
location = self.FindLocation(address)
|
||||||
|
return ctypes.c_uint64.from_buffer(self.minidump, location).value
|
||||||
|
|
||||||
|
def ReadUIntPtr(self, address):
|
||||||
|
if self.arch == MD_CPU_ARCHITECTURE_AMD64:
|
||||||
|
return self.ReadU64(address)
|
||||||
|
elif self.arch == MD_CPU_ARCHITECTURE_X86:
|
||||||
|
return self.ReadU32(address)
|
||||||
|
|
||||||
def ReadBytes(self, address, size):
|
def ReadBytes(self, address, size):
|
||||||
location = self.FindLocation(address)
|
location = self.FindLocation(address)
|
||||||
return self.minidump[location:location + size]
|
return self.minidump[location:location + size]
|
||||||
@ -355,10 +460,15 @@ class MinidumpReader(object):
|
|||||||
def GetDisasmLines(self, address, size):
|
def GetDisasmLines(self, address, size):
|
||||||
location = self.FindLocation(address)
|
location = self.FindLocation(address)
|
||||||
if location is None: return []
|
if location is None: return []
|
||||||
|
arch = None
|
||||||
|
if self.arch == MD_CPU_ARCHITECTURE_X86:
|
||||||
|
arch = "ia32"
|
||||||
|
elif self.arch == MD_CPU_ARCHITECTURE_AMD64:
|
||||||
|
arch = "x64"
|
||||||
return disasm.GetDisasmLines(self.minidump_name,
|
return disasm.GetDisasmLines(self.minidump_name,
|
||||||
location,
|
location,
|
||||||
size,
|
size,
|
||||||
"ia32",
|
arch,
|
||||||
False)
|
False)
|
||||||
|
|
||||||
|
|
||||||
@ -366,6 +476,33 @@ class MinidumpReader(object):
|
|||||||
self.minidump.close()
|
self.minidump.close()
|
||||||
self.minidump_file.close()
|
self.minidump_file.close()
|
||||||
|
|
||||||
|
def ExceptionIP(self):
|
||||||
|
if self.arch == MD_CPU_ARCHITECTURE_AMD64:
|
||||||
|
return self.exception_context.rip
|
||||||
|
elif self.arch == MD_CPU_ARCHITECTURE_X86:
|
||||||
|
return self.exception_context.eip
|
||||||
|
|
||||||
|
def ExceptionSP(self):
|
||||||
|
if self.arch == MD_CPU_ARCHITECTURE_AMD64:
|
||||||
|
return self.exception_context.rsp
|
||||||
|
elif self.arch == MD_CPU_ARCHITECTURE_X86:
|
||||||
|
return self.exception_context.rbp
|
||||||
|
|
||||||
|
def FormatIntPtr(self, value):
|
||||||
|
if self.arch == MD_CPU_ARCHITECTURE_AMD64:
|
||||||
|
return "%016x" % value
|
||||||
|
elif self.arch == MD_CPU_ARCHITECTURE_X86:
|
||||||
|
return "%08x" % value
|
||||||
|
|
||||||
|
def PointerSize(self):
|
||||||
|
if self.arch == MD_CPU_ARCHITECTURE_AMD64:
|
||||||
|
return 8
|
||||||
|
elif self.arch == MD_CPU_ARCHITECTURE_X86:
|
||||||
|
return 4
|
||||||
|
|
||||||
|
def Register(self, name):
|
||||||
|
return self.exception_context.__getattribute__(name)
|
||||||
|
|
||||||
|
|
||||||
# List of V8 instance types. Obtained by adding the code below to any .cc file.
|
# List of V8 instance types. Obtained by adding the code below to any .cc file.
|
||||||
#
|
#
|
||||||
@ -501,34 +638,36 @@ class HeapObject(object):
|
|||||||
p.Print(str(self))
|
p.Print(str(self))
|
||||||
|
|
||||||
def __str__(self):
|
def __str__(self):
|
||||||
return "HeapObject(%08x, %s)" % (self.address,
|
return "HeapObject(%s, %s)" % (self.heap.reader.FormatIntPtr(self.address),
|
||||||
INSTANCE_TYPES[self.map.instance_type])
|
INSTANCE_TYPES[self.map.instance_type])
|
||||||
|
|
||||||
def ObjectField(self, offset):
|
def ObjectField(self, offset):
|
||||||
field_value = self.heap.reader.ReadU32(self.address + offset)
|
field_value = self.heap.reader.ReadUIntPtr(self.address + offset)
|
||||||
return self.heap.FindObjectOrSmi(field_value)
|
return self.heap.FindObjectOrSmi(field_value)
|
||||||
|
|
||||||
def SmiField(self, offset):
|
def SmiField(self, offset):
|
||||||
field_value = self.heap.reader.ReadU32(self.address + offset)
|
field_value = self.heap.reader.ReadUIntPtr(self.address + offset)
|
||||||
assert (field_value & 1) == 0
|
assert (field_value & 1) == 0
|
||||||
return field_value / 2
|
return field_value / 2
|
||||||
|
|
||||||
|
|
||||||
class Map(HeapObject):
|
class Map(HeapObject):
|
||||||
INSTANCE_TYPE_OFFSET = 8
|
def InstanceTypeOffset():
|
||||||
|
return self.heap.PointerSize() + self.heap.IntSize()
|
||||||
|
|
||||||
def __init__(self, heap, map, address):
|
def __init__(self, heap, map, address):
|
||||||
HeapObject.__init__(self, heap, map, address)
|
HeapObject.__init__(self, heap, map, address)
|
||||||
self.instance_type = \
|
self.instance_type = \
|
||||||
heap.reader.ReadU8(self.address + Map.INSTANCE_TYPE_OFFSET)
|
heap.reader.ReadU8(self.address + self.InstanceTypeOffset())
|
||||||
|
|
||||||
|
|
||||||
class String(HeapObject):
|
class String(HeapObject):
|
||||||
LENGTH_OFFSET = 4
|
def LengthOffset(self):
|
||||||
|
return self.heap.PointerSize()
|
||||||
|
|
||||||
def __init__(self, heap, map, address):
|
def __init__(self, heap, map, address):
|
||||||
HeapObject.__init__(self, heap, map, address)
|
HeapObject.__init__(self, heap, map, address)
|
||||||
self.length = self.SmiField(String.LENGTH_OFFSET)
|
self.length = self.SmiField(self.LengthOffset())
|
||||||
|
|
||||||
def GetChars(self):
|
def GetChars(self):
|
||||||
return "?string?"
|
return "?string?"
|
||||||
@ -541,11 +680,12 @@ class String(HeapObject):
|
|||||||
|
|
||||||
|
|
||||||
class SeqString(String):
|
class SeqString(String):
|
||||||
CHARS_OFFSET = 12
|
def CharsOffset(self):
|
||||||
|
return self.heap.PointerSize() * 3
|
||||||
|
|
||||||
def __init__(self, heap, map, address):
|
def __init__(self, heap, map, address):
|
||||||
String.__init__(self, heap, map, address)
|
String.__init__(self, heap, map, address)
|
||||||
self.chars = heap.reader.ReadBytes(self.address + SeqString.CHARS_OFFSET,
|
self.chars = heap.reader.ReadBytes(self.address + self.CharsOffset(),
|
||||||
self.length)
|
self.length)
|
||||||
|
|
||||||
def GetChars(self):
|
def GetChars(self):
|
||||||
@ -553,6 +693,7 @@ class SeqString(String):
|
|||||||
|
|
||||||
|
|
||||||
class ExternalString(String):
|
class ExternalString(String):
|
||||||
|
# TODO(vegorov) fix ExternalString for X64 architecture
|
||||||
RESOURCE_OFFSET = 12
|
RESOURCE_OFFSET = 12
|
||||||
|
|
||||||
WEBKIT_RESOUCE_STRING_IMPL_OFFSET = 4
|
WEBKIT_RESOUCE_STRING_IMPL_OFFSET = 4
|
||||||
@ -582,24 +723,28 @@ class ExternalString(String):
|
|||||||
|
|
||||||
|
|
||||||
class ConsString(String):
|
class ConsString(String):
|
||||||
LEFT_OFFSET = 12
|
def LeftOffset(self):
|
||||||
RIGHT_OFFSET = 16
|
return self.heap.PointerSize() * 3
|
||||||
|
|
||||||
|
def RightOffset(self):
|
||||||
|
return self.heap.PointerSize() * 4
|
||||||
|
|
||||||
def __init__(self, heap, map, address):
|
def __init__(self, heap, map, address):
|
||||||
String.__init__(self, heap, map, address)
|
String.__init__(self, heap, map, address)
|
||||||
self.left = self.ObjectField(ConsString.LEFT_OFFSET)
|
self.left = self.ObjectField(self.LeftOffset())
|
||||||
self.right = self.ObjectField(ConsString.RIGHT_OFFSET)
|
self.right = self.ObjectField(self.RightOffset())
|
||||||
|
|
||||||
def GetChars(self):
|
def GetChars(self):
|
||||||
return self.left.GetChars() + self.right.GetChars()
|
return self.left.GetChars() + self.right.GetChars()
|
||||||
|
|
||||||
|
|
||||||
class Oddball(HeapObject):
|
class Oddball(HeapObject):
|
||||||
TO_STRING_OFFSET = 4
|
def ToStringOffset(self):
|
||||||
|
return self.heap.PointerSize()
|
||||||
|
|
||||||
def __init__(self, heap, map, address):
|
def __init__(self, heap, map, address):
|
||||||
HeapObject.__init__(self, heap, map, address)
|
HeapObject.__init__(self, heap, map, address)
|
||||||
self.to_string = self.ObjectField(Oddball.TO_STRING_OFFSET)
|
self.to_string = self.ObjectField(self.ToStringOffset())
|
||||||
|
|
||||||
def Print(self, p):
|
def Print(self, p):
|
||||||
p.Print(str(self))
|
p.Print(str(self))
|
||||||
@ -609,19 +754,23 @@ class Oddball(HeapObject):
|
|||||||
|
|
||||||
|
|
||||||
class FixedArray(HeapObject):
|
class FixedArray(HeapObject):
|
||||||
LENGTH_OFFSET = 4
|
def LengthOffset(self):
|
||||||
ELEMENTS_OFFSET = 8
|
return self.heap.PointerSize()
|
||||||
|
|
||||||
|
def ElementsOffset(self):
|
||||||
|
return self.heap.PointerSize() * 2
|
||||||
|
|
||||||
def __init__(self, heap, map, address):
|
def __init__(self, heap, map, address):
|
||||||
HeapObject.__init__(self, heap, map, address)
|
HeapObject.__init__(self, heap, map, address)
|
||||||
self.length = self.SmiField(FixedArray.LENGTH_OFFSET)
|
self.length = self.SmiField(self.LengthOffset())
|
||||||
|
|
||||||
def Print(self, p):
|
def Print(self, p):
|
||||||
p.Print("FixedArray(%08x) {" % self.address)
|
p.Print("FixedArray(%s) {" % self.heap.reader.FormatIntPtr(self.address))
|
||||||
p.Indent()
|
p.Indent()
|
||||||
p.Print("length: %d" % self.length)
|
p.Print("length: %d" % self.length)
|
||||||
|
base_offset = self.ElementsOffset()
|
||||||
for i in xrange(self.length):
|
for i in xrange(self.length):
|
||||||
offset = FixedArray.ELEMENTS_OFFSET + 4 * i
|
offset = base_offset + 4 * i
|
||||||
p.Print("[%08d] = %s" % (i, self.ObjectField(offset)))
|
p.Print("[%08d] = %s" % (i, self.ObjectField(offset)))
|
||||||
p.Dedent()
|
p.Dedent()
|
||||||
p.Print("}")
|
p.Print("}")
|
||||||
@ -631,19 +780,22 @@ class FixedArray(HeapObject):
|
|||||||
|
|
||||||
|
|
||||||
class JSFunction(HeapObject):
|
class JSFunction(HeapObject):
|
||||||
CODE_ENTRY_OFFSET = 12
|
def CodeEntryOffset(self):
|
||||||
SHARED_OFFSET = 20
|
return 3 * self.heap.PointerSize()
|
||||||
|
|
||||||
|
def SharedOffset(self):
|
||||||
|
return 5 * self.heap.PointerSize()
|
||||||
|
|
||||||
def __init__(self, heap, map, address):
|
def __init__(self, heap, map, address):
|
||||||
HeapObject.__init__(self, heap, map, address)
|
HeapObject.__init__(self, heap, map, address)
|
||||||
code_entry = \
|
code_entry = \
|
||||||
heap.reader.ReadU32(self.address + JSFunction.CODE_ENTRY_OFFSET)
|
heap.reader.ReadU32(self.address + self.CodeEntryOffset())
|
||||||
self.code = heap.FindObject(code_entry - Code.ENTRY_OFFSET + 1)
|
self.code = heap.FindObject(code_entry - Code.HeaderSize(heap) + 1)
|
||||||
self.shared = self.ObjectField(JSFunction.SHARED_OFFSET)
|
self.shared = self.ObjectField(self.SharedOffset())
|
||||||
|
|
||||||
def Print(self, p):
|
def Print(self, p):
|
||||||
source = "\n".join(" %s" % line for line in self._GetSource().split("\n"))
|
source = "\n".join(" %s" % line for line in self._GetSource().split("\n"))
|
||||||
p.Print("JSFunction(%08x) {" % self.address)
|
p.Print("JSFunction(%s) {" % self.heap.reader.FormatIntPtr(self.address))
|
||||||
p.Indent()
|
p.Indent()
|
||||||
p.Print("inferred name: %s" % self.shared.inferred_name)
|
p.Print("inferred name: %s" % self.shared.inferred_name)
|
||||||
if self.shared.script.Is(Script) and self.shared.script.name.Is(String):
|
if self.shared.script.Is(Script) and self.shared.script.name.Is(String):
|
||||||
@ -662,7 +814,8 @@ class JSFunction(HeapObject):
|
|||||||
inferred_name = ""
|
inferred_name = ""
|
||||||
if self.shared.Is(SharedFunctionInfo):
|
if self.shared.Is(SharedFunctionInfo):
|
||||||
inferred_name = self.shared.inferred_name
|
inferred_name = self.shared.inferred_name
|
||||||
return "JSFunction(%08x, %s)" % (self.address, inferred_name)
|
return "JSFunction(%s, %s)" % \
|
||||||
|
(self.heap.reader.FormatIntPtr(self.address), inferred_name)
|
||||||
|
|
||||||
def _GetSource(self):
|
def _GetSource(self):
|
||||||
source = "?source?"
|
source = "?source?"
|
||||||
@ -675,47 +828,75 @@ class JSFunction(HeapObject):
|
|||||||
|
|
||||||
|
|
||||||
class SharedFunctionInfo(HeapObject):
|
class SharedFunctionInfo(HeapObject):
|
||||||
CODE_OFFSET = 2 * 4
|
def CodeOffset(self):
|
||||||
SCRIPT_OFFSET = 7 * 4
|
return 2 * self.heap.PointerSize()
|
||||||
INFERRED_NAME_OFFSET = 9 * 4
|
|
||||||
START_POSITION_AND_TYPE_OFFSET = 17 * 4
|
def ScriptOffset(self):
|
||||||
END_POSITION_OFFSET = 18 * 4
|
return 7 * self.heap.PointerSize()
|
||||||
|
|
||||||
|
def InferredNameOffset(self):
|
||||||
|
return 9 * self.heap.PointerSize()
|
||||||
|
|
||||||
|
def EndPositionOffset(self):
|
||||||
|
return 12 * self.heap.PointerSize() + 4 * self.heap.IntSize()
|
||||||
|
|
||||||
|
def StartPositionAndTypeOffset(self):
|
||||||
|
return 12 * self.heap.PointerSize() + 5 * self.heap.IntSize()
|
||||||
|
|
||||||
def __init__(self, heap, map, address):
|
def __init__(self, heap, map, address):
|
||||||
HeapObject.__init__(self, heap, map, address)
|
HeapObject.__init__(self, heap, map, address)
|
||||||
self.code = self.ObjectField(SharedFunctionInfo.CODE_OFFSET)
|
self.code = self.ObjectField(self.CodeOffset())
|
||||||
self.script = self.ObjectField(SharedFunctionInfo.SCRIPT_OFFSET)
|
self.script = self.ObjectField(self.ScriptOffset())
|
||||||
self.inferred_name = \
|
self.inferred_name = self.ObjectField(self.InferredNameOffset())
|
||||||
self.ObjectField(SharedFunctionInfo.INFERRED_NAME_OFFSET)
|
if heap.PointerSize() == 8:
|
||||||
start_position_and_type = \
|
start_position_and_type = \
|
||||||
self.SmiField(SharedFunctionInfo.START_POSITION_AND_TYPE_OFFSET)
|
heap.reader.ReadU32(self.StartPositionAndTypeOffset())
|
||||||
self.start_position = start_position_and_type >> 2
|
self.start_position = start_position_and_type >> 2
|
||||||
self.end_position = self.SmiField(SharedFunctionInfo.END_POSITION_OFFSET)
|
pseudo_smi_end_position = \
|
||||||
|
heap.reader.ReadU32(self.EndPositionOffset())
|
||||||
|
self.end_position = pseudo_smi_end_position >> 2
|
||||||
|
else:
|
||||||
|
start_position_and_type = \
|
||||||
|
self.SmiField(self.StartPositionAndTypeOffset())
|
||||||
|
self.start_position = start_position_and_type >> 2
|
||||||
|
self.end_position = \
|
||||||
|
self.SmiField(self.EndPositionOffset())
|
||||||
|
|
||||||
|
|
||||||
class Script(HeapObject):
|
class Script(HeapObject):
|
||||||
SOURCE_OFFSET = 4
|
def SourceOffset(self):
|
||||||
NAME_OFFSET = 8
|
return self.heap.PointerSize()
|
||||||
|
|
||||||
|
def NameOffset(self):
|
||||||
|
return self.SourceOffset() + self.heap.PointerSize()
|
||||||
|
|
||||||
def __init__(self, heap, map, address):
|
def __init__(self, heap, map, address):
|
||||||
HeapObject.__init__(self, heap, map, address)
|
HeapObject.__init__(self, heap, map, address)
|
||||||
self.source = self.ObjectField(Script.SOURCE_OFFSET)
|
self.source = self.ObjectField(self.SourceOffset())
|
||||||
self.name = self.ObjectField(Script.NAME_OFFSET)
|
self.name = self.ObjectField(self.NameOffset())
|
||||||
|
|
||||||
|
|
||||||
class Code(HeapObject):
|
class Code(HeapObject):
|
||||||
INSTRUCTION_SIZE_OFFSET = 4
|
CODE_ALIGNMENT_MASK = (1 << 5) - 1
|
||||||
ENTRY_OFFSET = 32
|
|
||||||
|
def InstructionSizeOffset(self):
|
||||||
|
return self.heap.PointerSize()
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def HeaderSize(heap):
|
||||||
|
return (heap.PointerSize() + heap.IntSize() + \
|
||||||
|
4 * heap.PointerSize() + 3 * heap.IntSize() + \
|
||||||
|
CODE_ALIGNMENT_MASK) & ~CODE_ALIGNMENT_MASK
|
||||||
|
|
||||||
def __init__(self, heap, map, address):
|
def __init__(self, heap, map, address):
|
||||||
HeapObject.__init__(self, heap, map, address)
|
HeapObject.__init__(self, heap, map, address)
|
||||||
self.entry = self.address + Code.ENTRY_OFFSET
|
self.entry = self.address + Code.HeaderSize(heap)
|
||||||
self.instruction_size = \
|
self.instruction_size = \
|
||||||
heap.reader.ReadU32(self.address + Code.INSTRUCTION_SIZE_OFFSET)
|
heap.reader.ReadU32(self.address + self.InstructionSizeOffset())
|
||||||
|
|
||||||
def Print(self, p):
|
def Print(self, p):
|
||||||
lines = self.heap.reader.GetDisasmLines(self.entry, self.instruction_size)
|
lines = self.heap.reader.GetDisasmLines(self.entry, self.instruction_size)
|
||||||
p.Print("Code(%08x) {" % self.address)
|
p.Print("Code(%s) {" % self.heap.reader.FormatIntPtr(self.address))
|
||||||
p.Indent()
|
p.Indent()
|
||||||
p.Print("instruction_size: %d" % self.instruction_size)
|
p.Print("instruction_size: %d" % self.instruction_size)
|
||||||
p.PrintLines(self._FormatLine(line) for line in lines)
|
p.PrintLines(self._FormatLine(line) for line in lines)
|
||||||
@ -767,7 +948,7 @@ class V8Heap(object):
|
|||||||
if (tagged_address & 1) != 1: return None
|
if (tagged_address & 1) != 1: return None
|
||||||
address = tagged_address - 1
|
address = tagged_address - 1
|
||||||
if not self.reader.IsValidAddress(address): return None
|
if not self.reader.IsValidAddress(address): return None
|
||||||
map_tagged_address = self.reader.ReadU32(address)
|
map_tagged_address = self.reader.ReadUIntPtr(address)
|
||||||
if tagged_address == map_tagged_address:
|
if tagged_address == map_tagged_address:
|
||||||
# Meta map?
|
# Meta map?
|
||||||
meta_map = Map(self, None, address)
|
meta_map = Map(self, None, address)
|
||||||
@ -785,9 +966,19 @@ class V8Heap(object):
|
|||||||
self.objects[tagged_address] = object
|
self.objects[tagged_address] = object
|
||||||
return object
|
return object
|
||||||
|
|
||||||
|
def PointerSize(self):
|
||||||
|
return self.reader.PointerSize()
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
EIP_PROXIMITY = 64
|
EIP_PROXIMITY = 64
|
||||||
|
|
||||||
|
CONTEXT_FOR_ARCH = {
|
||||||
|
MD_CPU_ARCHITECTURE_AMD64:
|
||||||
|
['rax', 'rbx', 'rcx', 'rdx', 'rdi', 'rsi', 'rbp', 'rsp', 'rip'],
|
||||||
|
MD_CPU_ARCHITECTURE_X86:
|
||||||
|
['eax', 'ebx', 'ecx', 'edx', 'edi', 'esi', 'ebp', 'esp', 'eip']
|
||||||
|
}
|
||||||
|
|
||||||
def AnalyzeMinidump(options, minidump_name):
|
def AnalyzeMinidump(options, minidump_name):
|
||||||
reader = MinidumpReader(options, minidump_name)
|
reader = MinidumpReader(options, minidump_name)
|
||||||
@ -800,40 +991,35 @@ def AnalyzeMinidump(options, minidump_name):
|
|||||||
print " thread id: %d" % exception_thread.id
|
print " thread id: %d" % exception_thread.id
|
||||||
print " code: %08X" % reader.exception.exception.code
|
print " code: %08X" % reader.exception.exception.code
|
||||||
print " context:"
|
print " context:"
|
||||||
print " eax: %08x" % reader.exception_context.eax
|
for r in CONTEXT_FOR_ARCH[reader.arch]:
|
||||||
print " ebx: %08x" % reader.exception_context.ebx
|
print " %s: %s" % (r, reader.FormatIntPtr(reader.Register(r)))
|
||||||
print " ecx: %08x" % reader.exception_context.ecx
|
|
||||||
print " edx: %08x" % reader.exception_context.edx
|
|
||||||
print " edi: %08x" % reader.exception_context.edi
|
|
||||||
print " esi: %08x" % reader.exception_context.esi
|
|
||||||
print " ebp: %08x" % reader.exception_context.ebp
|
|
||||||
print " esp: %08x" % reader.exception_context.esp
|
|
||||||
print " eip: %08x" % reader.exception_context.eip
|
|
||||||
# TODO(vitalyr): decode eflags.
|
# TODO(vitalyr): decode eflags.
|
||||||
print " eflags: %s" % bin(reader.exception_context.eflags)[2:]
|
print " eflags: %s" % bin(reader.exception_context.eflags)[2:]
|
||||||
print
|
print
|
||||||
|
|
||||||
|
stack_top = reader.ExceptionSP()
|
||||||
stack_bottom = exception_thread.stack.start + \
|
stack_bottom = exception_thread.stack.start + \
|
||||||
exception_thread.stack.memory.data_size
|
exception_thread.stack.memory.data_size
|
||||||
stack_map = {reader.exception_context.eip: -1}
|
stack_map = {reader.ExceptionIP(): -1}
|
||||||
for slot in xrange(reader.exception_context.esp, stack_bottom, 4):
|
for slot in xrange(stack_top, stack_bottom, reader.PointerSize()):
|
||||||
maybe_address = reader.ReadU32(slot)
|
maybe_address = reader.ReadUIntPtr(slot)
|
||||||
if not maybe_address in stack_map:
|
if not maybe_address in stack_map:
|
||||||
stack_map[maybe_address] = slot
|
stack_map[maybe_address] = slot
|
||||||
heap = V8Heap(reader, stack_map)
|
heap = V8Heap(reader, stack_map)
|
||||||
|
|
||||||
print "Disassembly around exception.eip:"
|
print "Disassembly around exception.eip:"
|
||||||
start = reader.exception_context.eip - EIP_PROXIMITY
|
start = reader.ExceptionIP() - EIP_PROXIMITY
|
||||||
lines = reader.GetDisasmLines(start, 2 * EIP_PROXIMITY)
|
lines = reader.GetDisasmLines(start, 2 * EIP_PROXIMITY)
|
||||||
for line in lines:
|
for line in lines:
|
||||||
print FormatDisasmLine(start, heap, line)
|
print FormatDisasmLine(start, heap, line)
|
||||||
print
|
print
|
||||||
|
|
||||||
print "Annotated stack (from exception.esp to bottom):"
|
print "Annotated stack (from exception.esp to bottom):"
|
||||||
for slot in xrange(reader.exception_context.esp, stack_bottom, 4):
|
for slot in xrange(stack_top, stack_bottom, reader.PointerSize()):
|
||||||
maybe_address = reader.ReadU32(slot)
|
maybe_address = reader.ReadUIntPtr(slot)
|
||||||
heap_object = heap.FindObject(maybe_address)
|
heap_object = heap.FindObject(maybe_address)
|
||||||
print "%08x: %08x" % (slot, maybe_address)
|
print "%s: %s" % (reader.FormatIntPtr(slot),
|
||||||
|
reader.FormatIntPtr(maybe_address))
|
||||||
if heap_object:
|
if heap_object:
|
||||||
heap_object.Print(Printer())
|
heap_object.Print(Printer())
|
||||||
print
|
print
|
||||||
|
Loading…
x
Reference in New Issue
Block a user