diff options
author | Ryan Dahl <ry@tinyclouds.org> | 2010-11-19 10:49:09 -0800 |
---|---|---|
committer | Ryan Dahl <ry@tinyclouds.org> | 2010-11-19 10:49:09 -0800 |
commit | cbdcc1d5f321cfff3d4d3f416e48733089de7e00 (patch) | |
tree | 5d44f3a1fdb3d48326566126eca8e31729f6db90 /deps/v8/src | |
parent | f11291a220136164294e3cac5f09efd6c2b21be6 (diff) | |
download | node-new-cbdcc1d5f321cfff3d4d3f416e48733089de7e00.tar.gz |
Upgrade V8 to 2.5.7
Diffstat (limited to 'deps/v8/src')
119 files changed, 8658 insertions, 3980 deletions
diff --git a/deps/v8/src/SConscript b/deps/v8/src/SConscript index ef5485d854..316387fa23 100755 --- a/deps/v8/src/SConscript +++ b/deps/v8/src/SConscript @@ -40,6 +40,8 @@ SOURCES = { api.cc assembler.cc ast.cc + bignum.cc + bignum-dtoa.cc bootstrapper.cc builtins.cc cached-powers.cc @@ -95,6 +97,7 @@ SOURCES = { register-allocator.cc rewriter.cc runtime.cc + scanner-base.cc scanner.cc scopeinfo.cc scopes.cc @@ -117,6 +120,8 @@ SOURCES = { version.cc virtual-frame.cc zone.cc + extensions/gc-extension.cc + extensions/externalize-string-extension.cc """), 'arch:arm': Split(""" jump-target-light.cc diff --git a/deps/v8/src/accessors.cc b/deps/v8/src/accessors.cc index 7c21659ebc..08ef41b9f7 100644 --- a/deps/v8/src/accessors.cc +++ b/deps/v8/src/accessors.cc @@ -316,8 +316,10 @@ MaybeObject* Accessors::ScriptGetLineEnds(Object* object, void*) { InitScriptLineEnds(script); ASSERT(script->line_ends()->IsFixedArray()); Handle<FixedArray> line_ends(FixedArray::cast(script->line_ends())); - Handle<FixedArray> copy = Factory::CopyFixedArray(line_ends); - Handle<JSArray> js_array = Factory::NewJSArrayWithElements(copy); + // We do not want anyone to modify this array from JS. + ASSERT(*line_ends == Heap::empty_fixed_array() || + line_ends->map() == Heap::fixed_cow_array_map()); + Handle<JSArray> js_array = Factory::NewJSArrayWithElements(line_ends); return *js_array; } diff --git a/deps/v8/src/allocation.cc b/deps/v8/src/allocation.cc index 678f4fd7d2..d74c37cd79 100644 --- a/deps/v8/src/allocation.cc +++ b/deps/v8/src/allocation.cc @@ -27,16 +27,21 @@ #include <stdlib.h> -#include "v8.h" +#include "../include/v8stdint.h" +#include "globals.h" +#include "checks.h" +#include "allocation.h" +#include "utils.h" namespace v8 { namespace internal { - void* Malloced::New(size_t size) { ASSERT(NativeAllocationChecker::allocation_allowed()); void* result = malloc(size); - if (result == NULL) V8::FatalProcessOutOfMemory("Malloced operator new"); + if (result == NULL) { + v8::internal::FatalProcessOutOfMemory("Malloced operator new"); + } return result; } @@ -47,7 +52,7 @@ void Malloced::Delete(void* p) { void Malloced::FatalProcessOutOfMemory() { - V8::FatalProcessOutOfMemory("Out of memory"); + v8::internal::FatalProcessOutOfMemory("Out of memory"); } @@ -82,7 +87,7 @@ void AllStatic::operator delete(void* p) { char* StrDup(const char* str) { int length = StrLength(str); char* result = NewArray<char>(length + 1); - memcpy(result, str, length * kCharSize); + memcpy(result, str, length); result[length] = '\0'; return result; } @@ -92,7 +97,7 @@ char* StrNDup(const char* str, int n) { int length = StrLength(str); if (n < length) length = n; char* result = NewArray<char>(length + 1); - memcpy(result, str, length * kCharSize); + memcpy(result, str, length); result[length] = '\0'; return result; } @@ -124,6 +129,7 @@ void* PreallocatedStorage::New(size_t size) { } ASSERT(free_list_.next_ != &free_list_); ASSERT(free_list_.previous_ != &free_list_); + size = (size + kPointerSize - 1) & ~(kPointerSize - 1); // Search for exact fit. for (PreallocatedStorage* storage = free_list_.next_; diff --git a/deps/v8/src/allocation.h b/deps/v8/src/allocation.h index 70a3a03889..6f4bd2fb54 100644 --- a/deps/v8/src/allocation.h +++ b/deps/v8/src/allocation.h @@ -31,6 +31,10 @@ namespace v8 { namespace internal { +// Called when allocation routines fail to allocate. +// This function should not return, but should terminate the current +// processing. +void FatalProcessOutOfMemory(const char* message); // A class that controls whether allocation is allowed. This is for // the C++ heap only! diff --git a/deps/v8/src/api.cc b/deps/v8/src/api.cc index 617922dd5a..5912449169 100644 --- a/deps/v8/src/api.cc +++ b/deps/v8/src/api.cc @@ -43,7 +43,6 @@ #include "serialize.h" #include "snapshot.h" #include "top.h" -#include "utils.h" #include "v8threads.h" #include "version.h" @@ -116,7 +115,6 @@ static void DefaultFatalErrorHandler(const char* location, } - static FatalErrorCallback& GetFatalErrorHandler() { if (exception_behavior == NULL) { exception_behavior = DefaultFatalErrorHandler; @@ -125,6 +123,10 @@ static FatalErrorCallback& GetFatalErrorHandler() { } +void i::FatalProcessOutOfMemory(const char* location) { + i::V8::FatalProcessOutOfMemory(location, false); +} + // When V8 cannot allocated memory FatalProcessOutOfMemory is called. // The default fatal error handler is called and execution is stopped. @@ -394,14 +396,18 @@ v8::Handle<Boolean> False() { ResourceConstraints::ResourceConstraints() : max_young_space_size_(0), max_old_space_size_(0), + max_executable_size_(0), stack_limit_(NULL) { } bool SetResourceConstraints(ResourceConstraints* constraints) { int young_space_size = constraints->max_young_space_size(); int old_gen_size = constraints->max_old_space_size(); - if (young_space_size != 0 || old_gen_size != 0) { - bool result = i::Heap::ConfigureHeap(young_space_size / 2, old_gen_size); + int max_executable_size = constraints->max_executable_size(); + if (young_space_size != 0 || old_gen_size != 0 || max_executable_size != 0) { + bool result = i::Heap::ConfigureHeap(young_space_size / 2, + old_gen_size, + max_executable_size); if (!result) return false; } if (constraints->stack_limit() != NULL) { @@ -3260,11 +3266,15 @@ bool v8::V8::Dispose() { } -HeapStatistics::HeapStatistics(): total_heap_size_(0), used_heap_size_(0) { } +HeapStatistics::HeapStatistics(): total_heap_size_(0), + total_heap_size_executable_(0), + used_heap_size_(0) { } void v8::V8::GetHeapStatistics(HeapStatistics* heap_statistics) { heap_statistics->set_total_heap_size(i::Heap::CommittedMemory()); + heap_statistics->set_total_heap_size_executable( + i::Heap::CommittedMemoryExecutable()); heap_statistics->set_used_heap_size(i::Heap::SizeOfObjects()); } diff --git a/deps/v8/src/arm/assembler-arm.cc b/deps/v8/src/arm/assembler-arm.cc index ebbd9b1138..4cb421c577 100644 --- a/deps/v8/src/arm/assembler-arm.cc +++ b/deps/v8/src/arm/assembler-arm.cc @@ -317,7 +317,8 @@ static const Instr kLdrStrOffsetMask = 0x00000fff; static const int kMinimalBufferSize = 4*KB; static byte* spare_buffer_ = NULL; -Assembler::Assembler(void* buffer, int buffer_size) { +Assembler::Assembler(void* buffer, int buffer_size) + : positions_recorder_(this) { if (buffer == NULL) { // Do our own buffer management. if (buffer_size <= kMinimalBufferSize) { @@ -354,10 +355,6 @@ Assembler::Assembler(void* buffer, int buffer_size) { no_const_pool_before_ = 0; last_const_pool_end_ = 0; last_bound_pos_ = 0; - current_statement_position_ = RelocInfo::kNoPosition; - current_position_ = RelocInfo::kNoPosition; - written_statement_position_ = current_statement_position_; - written_position_ = current_position_; } @@ -752,15 +749,15 @@ static bool fits_shifter(uint32_t imm32, // if they can be encoded in the ARM's 12 bits of immediate-offset instruction // space. There is no guarantee that the relocated location can be similarly // encoded. -static bool MustUseConstantPool(RelocInfo::Mode rmode) { - if (rmode == RelocInfo::EXTERNAL_REFERENCE) { +bool Operand::must_use_constant_pool() const { + if (rmode_ == RelocInfo::EXTERNAL_REFERENCE) { #ifdef DEBUG if (!Serializer::enabled()) { Serializer::TooLateToEnableNow(); } #endif // def DEBUG return Serializer::enabled(); - } else if (rmode == RelocInfo::NONE) { + } else if (rmode_ == RelocInfo::NONE) { return false; } return true; @@ -769,7 +766,7 @@ static bool MustUseConstantPool(RelocInfo::Mode rmode) { bool Operand::is_single_instruction() const { if (rm_.is_valid()) return true; - if (MustUseConstantPool(rmode_)) return false; + if (must_use_constant_pool()) return false; uint32_t dummy1, dummy2; return fits_shifter(imm32_, &dummy1, &dummy2, NULL); } @@ -785,7 +782,7 @@ void Assembler::addrmod1(Instr instr, // Immediate. uint32_t rotate_imm; uint32_t immed_8; - if (MustUseConstantPool(x.rmode_) || + if (x.must_use_constant_pool() || !fits_shifter(x.imm32_, &rotate_imm, &immed_8, &instr)) { // The immediate operand cannot be encoded as a shifter operand, so load // it first to register ip and change the original instruction to use ip. @@ -794,8 +791,7 @@ void Assembler::addrmod1(Instr instr, CHECK(!rn.is(ip)); // rn should never be ip, or will be trashed Condition cond = static_cast<Condition>(instr & CondMask); if ((instr & ~CondMask) == 13*B21) { // mov, S not set - if (MustUseConstantPool(x.rmode_) || - !CpuFeatures::IsSupported(ARMv7)) { + if (x.must_use_constant_pool() || !CpuFeatures::IsSupported(ARMv7)) { RecordRelocInfo(x.rmode_, x.imm32_); ldr(rd, MemOperand(pc, 0), cond); } else { @@ -806,7 +802,7 @@ void Assembler::addrmod1(Instr instr, } else { // If this is not a mov or mvn instruction we may still be able to avoid // a constant pool entry by using mvn or movw. - if (!MustUseConstantPool(x.rmode_) && + if (!x.must_use_constant_pool() && (instr & kMovMvnMask) != kMovMvnPattern) { mov(ip, x, LeaveCC, cond); } else { @@ -999,7 +995,7 @@ void Assembler::bl(int branch_offset, Condition cond) { void Assembler::blx(int branch_offset) { // v5 and above - WriteRecordedPositions(); + positions_recorder()->WriteRecordedPositions(); ASSERT((branch_offset & 1) == 0); int h = ((branch_offset & 2) >> 1)*B24; int imm24 = branch_offset >> 2; @@ -1009,14 +1005,14 @@ void Assembler::blx(int branch_offset) { // v5 and above void Assembler::blx(Register target, Condition cond) { // v5 and above - WriteRecordedPositions(); + positions_recorder()->WriteRecordedPositions(); ASSERT(!target.is(pc)); emit(cond | B24 | B21 | 15*B16 | 15*B12 | 15*B8 | 3*B4 | target.code()); } void Assembler::bx(Register target, Condition cond) { // v5 and above, plus v4t - WriteRecordedPositions(); + positions_recorder()->WriteRecordedPositions(); ASSERT(!target.is(pc)); // use of pc is actually allowed, but discouraged emit(cond | B24 | B21 | 15*B16 | 15*B12 | 15*B8 | B4 | target.code()); } @@ -1114,7 +1110,7 @@ void Assembler::orr(Register dst, Register src1, const Operand& src2, void Assembler::mov(Register dst, const Operand& src, SBit s, Condition cond) { if (dst.is(pc)) { - WriteRecordedPositions(); + positions_recorder()->WriteRecordedPositions(); } // Don't allow nop instructions in the form mov rn, rn to be generated using // the mov instruction. They must be generated using nop(int) @@ -1339,7 +1335,7 @@ void Assembler::msr(SRegisterFieldMask fields, const Operand& src, // Immediate. uint32_t rotate_imm; uint32_t immed_8; - if (MustUseConstantPool(src.rmode_) || + if (src.must_use_constant_pool() || !fits_shifter(src.imm32_, &rotate_imm, &immed_8, NULL)) { // Immediate operand cannot be encoded, load it first to register ip. RecordRelocInfo(src.rmode_, src.imm32_); @@ -1359,7 +1355,7 @@ void Assembler::msr(SRegisterFieldMask fields, const Operand& src, // Load/Store instructions. void Assembler::ldr(Register dst, const MemOperand& src, Condition cond) { if (dst.is(pc)) { - WriteRecordedPositions(); + positions_recorder()->WriteRecordedPositions(); } addrmod2(cond | B26 | L, dst, src); @@ -2148,6 +2144,7 @@ static Instr EncodeVCVT(const VFPType dst_type, const int dst_code, const VFPType src_type, const int src_code, + Assembler::ConversionMode mode, const Condition cond) { ASSERT(src_type != dst_type); int D, Vd, M, Vm; @@ -2166,7 +2163,7 @@ static Instr EncodeVCVT(const VFPType dst_type, if (IsIntegerVFPType(dst_type)) { opc2 = IsSignedVFPType(dst_type) ? 0x5 : 0x4; sz = IsDoubleVFPType(src_type) ? 0x1 : 0x0; - op = 1; // round towards zero + op = mode; } else { ASSERT(IsIntegerVFPType(src_type)); opc2 = 0x0; @@ -2190,57 +2187,64 @@ static Instr EncodeVCVT(const VFPType dst_type, void Assembler::vcvt_f64_s32(const DwVfpRegister dst, const SwVfpRegister src, + ConversionMode mode, const Condition cond) { ASSERT(CpuFeatures::IsEnabled(VFP3)); - emit(EncodeVCVT(F64, dst.code(), S32, src.code(), cond)); + emit(EncodeVCVT(F64, dst.code(), S32, src.code(), mode, cond)); } void Assembler::vcvt_f32_s32(const SwVfpRegister dst, const SwVfpRegister src, + ConversionMode mode, const Condition cond) { ASSERT(CpuFeatures::IsEnabled(VFP3)); - emit(EncodeVCVT(F32, dst.code(), S32, src.code(), cond)); + emit(EncodeVCVT(F32, dst.code(), S32, src.code(), mode, cond)); } void Assembler::vcvt_f64_u32(const DwVfpRegister dst, const SwVfpRegister src, + ConversionMode mode, const Condition cond) { ASSERT(CpuFeatures::IsEnabled(VFP3)); - emit(EncodeVCVT(F64, dst.code(), U32, src.code(), cond)); + emit(EncodeVCVT(F64, dst.code(), U32, src.code(), mode, cond)); } void Assembler::vcvt_s32_f64(const SwVfpRegister dst, const DwVfpRegister src, + ConversionMode mode, const Condition cond) { ASSERT(CpuFeatures::IsEnabled(VFP3)); - emit(EncodeVCVT(S32, dst.code(), F64, src.code(), cond)); + emit(EncodeVCVT(S32, dst.code(), F64, src.code(), mode, cond)); } void Assembler::vcvt_u32_f64(const SwVfpRegister dst, const DwVfpRegister src, + ConversionMode mode, const Condition cond) { ASSERT(CpuFeatures::IsEnabled(VFP3)); - emit(EncodeVCVT(U32, dst.code(), F64, src.code(), cond)); + emit(EncodeVCVT(U32, dst.code(), F64, src.code(), mode, cond)); } void Assembler::vcvt_f64_f32(const DwVfpRegister dst, const SwVfpRegister src, + ConversionMode mode, const Condition cond) { ASSERT(CpuFeatures::IsEnabled(VFP3)); - emit(EncodeVCVT(F64, dst.code(), F32, src.code(), cond)); + emit(EncodeVCVT(F64, dst.code(), F32, src.code(), mode, cond)); } void Assembler::vcvt_f32_f64(const SwVfpRegister dst, const DwVfpRegister src, + ConversionMode mode, const Condition cond) { ASSERT(CpuFeatures::IsEnabled(VFP3)); - emit(EncodeVCVT(F32, dst.code(), F64, src.code(), cond)); + emit(EncodeVCVT(F32, dst.code(), F64, src.code(), mode, cond)); } @@ -2333,6 +2337,16 @@ void Assembler::vcmp(const DwVfpRegister src1, } +void Assembler::vmsr(Register dst, Condition cond) { + // Instruction details available in ARM DDI 0406A, A8-652. + // cond(31-28) | 1110 (27-24) | 1110(23-20)| 0001 (19-16) | + // Rt(15-12) | 1010 (11-8) | 0(7) | 00 (6-5) | 1(4) | 0000(3-0) + ASSERT(CpuFeatures::IsEnabled(VFP3)); + emit(cond | 0xE*B24 | 0xE*B20 | B16 | + dst.code()*B12 | 0xA*B8 | B4); +} + + void Assembler::vmrs(Register dst, Condition cond) { // Instruction details available in ARM DDI 0406A, A8-652. // cond(31-28) | 1110 (27-24) | 1111(23-20)| 0001 (19-16) | @@ -2343,7 +2357,6 @@ void Assembler::vmrs(Register dst, Condition cond) { } - void Assembler::vsqrt(const DwVfpRegister dst, const DwVfpRegister src, const Condition cond) { @@ -2377,14 +2390,14 @@ void Assembler::BlockConstPoolFor(int instructions) { // Debugging. void Assembler::RecordJSReturn() { - WriteRecordedPositions(); + positions_recorder()->WriteRecordedPositions(); CheckBuffer(); RecordRelocInfo(RelocInfo::JS_RETURN); } void Assembler::RecordDebugBreakSlot() { - WriteRecordedPositions(); + positions_recorder()->WriteRecordedPositions(); CheckBuffer(); RecordRelocInfo(RelocInfo::DEBUG_BREAK_SLOT); } @@ -2398,47 +2411,6 @@ void Assembler::RecordComment(const char* msg) { } -void Assembler::RecordPosition(int pos) { - if (pos == RelocInfo::kNoPosition) return; - ASSERT(pos >= 0); - current_position_ = pos; -} - - -void Assembler::RecordStatementPosition(int pos) { - if (pos == RelocInfo::kNoPosition) return; - ASSERT(pos >= 0); - current_statement_position_ = pos; -} - - -bool Assembler::WriteRecordedPositions() { - bool written = false; - - // Write the statement position if it is different from what was written last - // time. - if (current_statement_position_ != written_statement_position_) { - CheckBuffer(); - RecordRelocInfo(RelocInfo::STATEMENT_POSITION, current_statement_position_); - written_statement_position_ = current_statement_position_; - written = true; - } - - // Write the position if it is different from what was written last time and - // also different from the written statement position. - if (current_position_ != written_position_ && - current_position_ != written_statement_position_) { - CheckBuffer(); - RecordRelocInfo(RelocInfo::POSITION, current_position_); - written_position_ = current_position_; - written = true; - } - - // Return whether something was written. - return written; -} - - void Assembler::GrowBuffer() { if (!own_buffer_) FATAL("external code buffer is too small"); diff --git a/deps/v8/src/arm/assembler-arm.h b/deps/v8/src/arm/assembler-arm.h index 5b647a7537..606ff86340 100644 --- a/deps/v8/src/arm/assembler-arm.h +++ b/deps/v8/src/arm/assembler-arm.h @@ -219,6 +219,11 @@ const DwVfpRegister d13 = { 13 }; const DwVfpRegister d14 = { 14 }; const DwVfpRegister d15 = { 15 }; +// VFP FPSCR constants. +static const uint32_t kVFPExceptionMask = 0xf; +static const uint32_t kVFPRoundingModeMask = 3 << 22; +static const uint32_t kVFPFlushToZeroMask = 1 << 24; +static const uint32_t kVFPRoundToMinusInfinityBits = 2 << 22; // Coprocessor register struct CRegister { @@ -448,6 +453,7 @@ class Operand BASE_EMBEDDED { // Return true of this operand fits in one instruction so that no // 2-instruction solution with a load into the ip register is necessary. bool is_single_instruction() const; + bool must_use_constant_pool() const; inline int32_t immediate() const { ASSERT(!rm_.is_valid()); @@ -1007,26 +1013,37 @@ class Assembler : public Malloced { void vmov(const Register dst, const SwVfpRegister src, const Condition cond = al); + enum ConversionMode { + FPSCRRounding = 0, + RoundToZero = 1 + }; void vcvt_f64_s32(const DwVfpRegister dst, const SwVfpRegister src, + ConversionMode mode = RoundToZero, const Condition cond = al); void vcvt_f32_s32(const SwVfpRegister dst, const SwVfpRegister src, + ConversionMode mode = RoundToZero, const Condition cond = al); void vcvt_f64_u32(const DwVfpRegister dst, const SwVfpRegister src, + ConversionMode mode = RoundToZero, const Condition cond = al); void vcvt_s32_f64(const SwVfpRegister dst, const DwVfpRegister src, + ConversionMode mode = RoundToZero, const Condition cond = al); void vcvt_u32_f64(const SwVfpRegister dst, const DwVfpRegister src, + ConversionMode mode = RoundToZero, const Condition cond = al); void vcvt_f64_f32(const DwVfpRegister dst, const SwVfpRegister src, + ConversionMode mode = RoundToZero, const Condition cond = al); void vcvt_f32_f64(const SwVfpRegister dst, const DwVfpRegister src, + ConversionMode mode = RoundToZero, const Condition cond = al); void vadd(const DwVfpRegister dst, @@ -1055,6 +1072,8 @@ class Assembler : public Malloced { const Condition cond = al); void vmrs(const Register dst, const Condition cond = al); + void vmsr(const Register dst, + const Condition cond = al); void vsqrt(const DwVfpRegister dst, const DwVfpRegister src, const Condition cond = al); @@ -1117,13 +1136,9 @@ class Assembler : public Malloced { // Use --debug_code to enable. void RecordComment(const char* msg); - void RecordPosition(int pos); - void RecordStatementPosition(int pos); - bool WriteRecordedPositions(); - int pc_offset() const { return pc_ - buffer_; } - int current_position() const { return current_position_; } - int current_statement_position() const { return current_statement_position_; } + + PositionsRecorder* positions_recorder() { return &positions_recorder_; } bool can_peephole_optimize(int instructions) { if (!FLAG_peephole_optimization) return false; @@ -1259,12 +1274,6 @@ class Assembler : public Malloced { // The bound position, before this we cannot do instruction elimination. int last_bound_pos_; - // source position information - int current_position_; - int current_statement_position_; - int written_position_; - int written_statement_position_; - // Code emission inline void CheckBuffer(); void GrowBuffer(); @@ -1290,8 +1299,21 @@ class Assembler : public Malloced { friend class RelocInfo; friend class CodePatcher; friend class BlockConstPoolScope; + + PositionsRecorder positions_recorder_; + friend class PositionsRecorder; + friend class EnsureSpace; }; + +class EnsureSpace BASE_EMBEDDED { + public: + explicit EnsureSpace(Assembler* assembler) { + assembler->CheckBuffer(); + } +}; + + } } // namespace v8::internal #endif // V8_ARM_ASSEMBLER_ARM_H_ diff --git a/deps/v8/src/arm/codegen-arm.cc b/deps/v8/src/arm/codegen-arm.cc index 70ff244649..dd0520feed 100644 --- a/deps/v8/src/arm/codegen-arm.cc +++ b/deps/v8/src/arm/codegen-arm.cc @@ -43,6 +43,7 @@ #include "register-allocator-inl.h" #include "runtime.h" #include "scopes.h" +#include "stub-cache.h" #include "virtual-frame-inl.h" #include "virtual-frame-arm-inl.h" @@ -557,7 +558,7 @@ void CodeGenerator::Load(Expression* expr) { void CodeGenerator::LoadGlobal() { Register reg = frame_->GetTOSRegister(); - __ ldr(reg, GlobalObject()); + __ ldr(reg, GlobalObjectOperand()); frame_->EmitPush(reg); } @@ -1891,18 +1892,15 @@ void CodeGenerator::CheckStack() { frame_->SpillAll(); Comment cmnt(masm_, "[ check stack"); __ LoadRoot(ip, Heap::kStackLimitRootIndex); - // Put the lr setup instruction in the delay slot. kInstrSize is added to - // the implicit 8 byte offset that always applies to operations with pc and - // gives a return address 12 bytes down. - masm_->add(lr, pc, Operand(Assembler::kInstrSize)); masm_->cmp(sp, Operand(ip)); StackCheckStub stub; // Call the stub if lower. - masm_->mov(pc, + masm_->mov(ip, Operand(reinterpret_cast<intptr_t>(stub.GetCode().location()), RelocInfo::CODE_TARGET), LeaveCC, lo); + masm_->Call(ip, lo); } @@ -4232,7 +4230,7 @@ void CodeGenerator::VisitCall(Call* node) { // Setup the name register and call the IC initialization code. __ mov(r2, Operand(var->name())); InLoopFlag in_loop = loop_nesting() > 0 ? IN_LOOP : NOT_IN_LOOP; - Handle<Code> stub = ComputeCallInitialize(arg_count, in_loop); + Handle<Code> stub = StubCache::ComputeCallInitialize(arg_count, in_loop); CodeForSourcePosition(node->position()); frame_->CallCodeObject(stub, RelocInfo::CODE_TARGET_CONTEXT, arg_count + 1); @@ -4326,7 +4324,8 @@ void CodeGenerator::VisitCall(Call* node) { // Set the name register and call the IC initialization code. __ mov(r2, Operand(name)); InLoopFlag in_loop = loop_nesting() > 0 ? IN_LOOP : NOT_IN_LOOP; - Handle<Code> stub = ComputeCallInitialize(arg_count, in_loop); + Handle<Code> stub = + StubCache::ComputeCallInitialize(arg_count, in_loop); CodeForSourcePosition(node->position()); frame_->CallCodeObject(stub, RelocInfo::CODE_TARGET, arg_count + 1); __ ldr(cp, frame_->Context()); @@ -4337,9 +4336,12 @@ void CodeGenerator::VisitCall(Call* node) { // ------------------------------------------- // JavaScript example: 'array[index](1, 2, 3)' // ------------------------------------------- + + // Load the receiver and name of the function. Load(property->obj()); + Load(property->key()); + if (property->is_synthetic()) { - Load(property->key()); EmitKeyedLoad(); // Put the function below the receiver. // Use the global receiver. @@ -4349,21 +4351,28 @@ void CodeGenerator::VisitCall(Call* node) { CallWithArguments(args, RECEIVER_MIGHT_BE_VALUE, node->position()); frame_->EmitPush(r0); } else { + // Swap the name of the function and the receiver on the stack to follow + // the calling convention for call ICs. + Register key = frame_->PopToRegister(); + Register receiver = frame_->PopToRegister(key); + frame_->EmitPush(key); + frame_->EmitPush(receiver); + // Load the arguments. int arg_count = args->length(); for (int i = 0; i < arg_count; i++) { Load(args->at(i)); } - // Set the name register and call the IC initialization code. - Load(property->key()); - frame_->SpillAll(); - frame_->EmitPop(r2); // Function name. - + // Load the key into r2 and call the IC initialization code. InLoopFlag in_loop = loop_nesting() > 0 ? IN_LOOP : NOT_IN_LOOP; - Handle<Code> stub = ComputeKeyedCallInitialize(arg_count, in_loop); + Handle<Code> stub = + StubCache::ComputeKeyedCallInitialize(arg_count, in_loop); CodeForSourcePosition(node->position()); + frame_->SpillAll(); + __ ldr(r2, frame_->ElementAt(arg_count + 1)); frame_->CallCodeObject(stub, RelocInfo::CODE_TARGET, arg_count + 1); + frame_->Drop(); // Drop the key still on the stack. __ ldr(cp, frame_->Context()); frame_->EmitPush(r0); } @@ -5135,11 +5144,11 @@ class DeferredIsStringWrapperSafeForDefaultValueOf : public DeferredCode { __ b(eq, &false_result); __ ldr(scratch1_, FieldMemOperand(scratch1_, HeapObject::kMapOffset)); __ ldr(scratch2_, - CodeGenerator::ContextOperand(cp, Context::GLOBAL_INDEX)); + ContextOperand(cp, Context::GLOBAL_INDEX)); __ ldr(scratch2_, FieldMemOperand(scratch2_, GlobalObject::kGlobalContextOffset)); __ ldr(scratch2_, - CodeGenerator::ContextOperand( + ContextOperand( scratch2_, Context::STRING_FUNCTION_PROTOTYPE_MAP_INDEX)); __ cmp(scratch1_, scratch2_); __ b(ne, &false_result); @@ -5496,73 +5505,6 @@ void CodeGenerator::GenerateRegExpConstructResult(ZoneList<Expression*>* args) { } -void CodeGenerator::GenerateRegExpCloneResult(ZoneList<Expression*>* args) { - ASSERT_EQ(1, args->length()); - - Load(args->at(0)); - frame_->PopToR0(); - { - VirtualFrame::SpilledScope spilled_scope(frame_); - - Label done; - Label call_runtime; - __ BranchOnSmi(r0, &done); - - // Load JSRegExp map into r1. Check that argument object has this map. - // Arguments to this function should be results of calling RegExp exec, - // which is either an unmodified JSRegExpResult or null. Anything not having - // the unmodified JSRegExpResult map is returned unmodified. - // This also ensures that elements are fast. - - __ ldr(r1, ContextOperand(cp, Context::GLOBAL_INDEX)); - __ ldr(r1, FieldMemOperand(r1, GlobalObject::kGlobalContextOffset)); - __ ldr(r1, ContextOperand(r1, Context::REGEXP_RESULT_MAP_INDEX)); - __ ldr(ip, FieldMemOperand(r0, HeapObject::kMapOffset)); - __ cmp(r1, Operand(ip)); - __ b(ne, &done); - - if (FLAG_debug_code) { - __ LoadRoot(r2, Heap::kEmptyFixedArrayRootIndex); - __ ldr(ip, FieldMemOperand(r0, JSObject::kPropertiesOffset)); - __ cmp(ip, r2); - __ Check(eq, "JSRegExpResult: default map but non-empty properties."); - } - - // All set, copy the contents to a new object. - __ AllocateInNewSpace(JSRegExpResult::kSize, - r2, - r3, - r4, - &call_runtime, - NO_ALLOCATION_FLAGS); - // Store RegExpResult map as map of allocated object. - ASSERT(JSRegExpResult::kSize == 6 * kPointerSize); - // Copy all fields (map is already in r1) from (untagged) r0 to r2. - // Change map of elements array (ends up in r4) to be a FixedCOWArray. - __ bic(r0, r0, Operand(kHeapObjectTagMask)); - __ ldm(ib, r0, r3.bit() | r4.bit() | r5.bit() | r6.bit() | r7.bit()); - __ stm(ia, r2, - r1.bit() | r3.bit() | r4.bit() | r5.bit() | r6.bit() | r7.bit()); - ASSERT(JSRegExp::kElementsOffset == 2 * kPointerSize); - // Check whether elements array is empty fixed array, and otherwise make - // it copy-on-write (it never should be empty unless someone is messing - // with the arguments to the runtime function). - __ LoadRoot(ip, Heap::kEmptyFixedArrayRootIndex); - __ add(r0, r2, Operand(kHeapObjectTag)); // Tag result and move it to r0. - __ cmp(r4, ip); - __ b(eq, &done); - __ LoadRoot(ip, Heap::kFixedCOWArrayMapRootIndex); - __ str(ip, FieldMemOperand(r4, HeapObject::kMapOffset)); - __ b(&done); - __ bind(&call_runtime); - __ push(r0); - __ CallRuntime(Runtime::kRegExpCloneResult, 1); - __ bind(&done); - } - frame_->EmitPush(r0); -} - - class DeferredSearchCache: public DeferredCode { public: DeferredSearchCache(Register dst, Register cache, Register key) @@ -5892,7 +5834,7 @@ void CodeGenerator::VisitCallRuntime(CallRuntime* node) { // Prepare stack for calling JS runtime function. // Push the builtins object found in the current global object. Register scratch = VirtualFrame::scratch0(); - __ ldr(scratch, GlobalObject()); + __ ldr(scratch, GlobalObjectOperand()); Register builtins = frame_->GetTOSRegister(); __ ldr(builtins, FieldMemOperand(scratch, GlobalObject::kBuiltinsOffset)); frame_->EmitPush(builtins); @@ -5910,7 +5852,7 @@ void CodeGenerator::VisitCallRuntime(CallRuntime* node) { // Call the JS runtime function. __ mov(r2, Operand(node->name())); InLoopFlag in_loop = loop_nesting() > 0 ? IN_LOOP : NOT_IN_LOOP; - Handle<Code> stub = ComputeCallInitialize(arg_count, in_loop); + Handle<Code> stub = StubCache::ComputeCallInitialize(arg_count, in_loop); frame_->CallCodeObject(stub, RelocInfo::CODE_TARGET, arg_count + 1); __ ldr(cp, frame_->Context()); frame_->EmitPush(r0); diff --git a/deps/v8/src/arm/codegen-arm.h b/deps/v8/src/arm/codegen-arm.h index e6fd6071e1..2e8f46668c 100644 --- a/deps/v8/src/arm/codegen-arm.h +++ b/deps/v8/src/arm/codegen-arm.h @@ -279,10 +279,6 @@ class CodeGenerator: public AstVisitor { return inlined_write_barrier_size_ + 4; } - static MemOperand ContextOperand(Register context, int index) { - return MemOperand(context, Context::SlotOffset(index)); - } - private: // Type of a member function that generates inline code for a native function. typedef void (CodeGenerator::*InlineFunctionGenerator) @@ -349,10 +345,6 @@ class CodeGenerator: public AstVisitor { JumpTarget* slow); // Expressions - static MemOperand GlobalObject() { - return ContextOperand(cp, Context::GLOBAL_INDEX); - } - void LoadCondition(Expression* x, JumpTarget* true_target, JumpTarget* false_target, @@ -452,10 +444,6 @@ class CodeGenerator: public AstVisitor { static Handle<Code> ComputeLazyCompile(int argc); void ProcessDeclarations(ZoneList<Declaration*>* declarations); - static Handle<Code> ComputeCallInitialize(int argc, InLoopFlag in_loop); - - static Handle<Code> ComputeKeyedCallInitialize(int argc, InLoopFlag in_loop); - // Declare global variables and functions in the given array of // name/value pairs. void DeclareGlobals(Handle<FixedArray> pairs); @@ -518,8 +506,6 @@ class CodeGenerator: public AstVisitor { void GenerateRegExpConstructResult(ZoneList<Expression*>* args); - void GenerateRegExpCloneResult(ZoneList<Expression*>* args); - // Support for fast native caches. void GenerateGetFromCache(ZoneList<Expression*>* args); diff --git a/deps/v8/src/arm/constants-arm.h b/deps/v8/src/arm/constants-arm.h index 123c5e7972..36f6283c96 100644 --- a/deps/v8/src/arm/constants-arm.h +++ b/deps/v8/src/arm/constants-arm.h @@ -206,6 +206,13 @@ enum VFPRegPrecision { kDoublePrecision = 1 }; +// VFP rounding modes. See ARM DDI 0406B Page A2-29. +enum FPSCRRoundingModes { + RN, // Round to Nearest. + RP, // Round towards Plus Infinity. + RM, // Round towards Minus Infinity. + RZ // Round towards zero. +}; typedef int32_t instr_t; diff --git a/deps/v8/src/arm/disasm-arm.cc b/deps/v8/src/arm/disasm-arm.cc index 4e7580f868..297a2db5b2 100644 --- a/deps/v8/src/arm/disasm-arm.cc +++ b/deps/v8/src/arm/disasm-arm.cc @@ -1046,6 +1046,7 @@ int Decoder::DecodeType7(Instr* instr) { // Dd = vdiv(Dn, Dm) // vcmp(Dd, Dm) // vmrs +// vmsr // Dd = vsqrt(Dm) void Decoder::DecodeTypeVFP(Instr* instr) { ASSERT((instr->TypeField() == 7) && (instr->Bit(24) == 0x0) ); @@ -1111,16 +1112,22 @@ void Decoder::DecodeTypeVFP(Instr* instr) { if ((instr->VCField() == 0x0) && (instr->VAField() == 0x0)) { DecodeVMOVBetweenCoreAndSinglePrecisionRegisters(instr); - } else if ((instr->VLField() == 0x1) && - (instr->VCField() == 0x0) && + } else if ((instr->VCField() == 0x0) && (instr->VAField() == 0x7) && (instr->Bits(19, 16) == 0x1)) { - if (instr->Bits(15, 12) == 0xF) - Format(instr, "vmrs'cond APSR, FPSCR"); - else - Unknown(instr); // Not used by V8. - } else { - Unknown(instr); // Not used by V8. + if (instr->VLField() == 0) { + if (instr->Bits(15, 12) == 0xF) { + Format(instr, "vmsr'cond FPSCR, APSR"); + } else { + Format(instr, "vmsr'cond FPSCR, 'rt"); + } + } else { + if (instr->Bits(15, 12) == 0xF) { + Format(instr, "vmrs'cond APSR, FPSCR"); + } else { + Format(instr, "vmrs'cond 'rt, FPSCR"); + } + } } } } diff --git a/deps/v8/src/arm/full-codegen-arm.cc b/deps/v8/src/arm/full-codegen-arm.cc index 9935e038f5..c50f84ad5e 100644 --- a/deps/v8/src/arm/full-codegen-arm.cc +++ b/deps/v8/src/arm/full-codegen-arm.cc @@ -36,6 +36,7 @@ #include "full-codegen.h" #include "parser.h" #include "scopes.h" +#include "stub-cache.h" namespace v8 { namespace internal { @@ -171,19 +172,16 @@ void FullCodeGenerator::Generate(CompilationInfo* info) { } // Check the stack for overflow or break request. - // Put the lr setup instruction in the delay slot. The kInstrSize is - // added to the implicit 8 byte offset that always applies to operations - // with pc and gives a return address 12 bytes down. { Comment cmnt(masm_, "[ Stack check"); __ LoadRoot(r2, Heap::kStackLimitRootIndex); - __ add(lr, pc, Operand(Assembler::kInstrSize)); __ cmp(sp, Operand(r2)); StackCheckStub stub; - __ mov(pc, + __ mov(ip, Operand(reinterpret_cast<intptr_t>(stub.GetCode().location()), RelocInfo::CODE_TARGET), LeaveCC, lo); + __ Call(ip, lo); } if (FLAG_trace) { @@ -1019,7 +1017,7 @@ void FullCodeGenerator::EmitLoadGlobalSlotCheckExtensions( __ bind(&fast); } - __ ldr(r0, CodeGenerator::GlobalObject()); + __ ldr(r0, GlobalObjectOperand()); __ mov(r2, Operand(slot->var()->name())); RelocInfo::Mode mode = (typeof_state == INSIDE_TYPEOF) ? RelocInfo::CODE_TARGET @@ -1040,7 +1038,7 @@ void FullCodeGenerator::EmitVariableLoad(Variable* var) { Comment cmnt(masm_, "Global variable"); // Use inline caching. Variable name is passed in r2 and the global // object (receiver) in r0. - __ ldr(r0, CodeGenerator::GlobalObject()); + __ ldr(r0, GlobalObjectOperand()); __ mov(r2, Operand(var->name())); Handle<Code> ic(Builtins::builtin(Builtins::LoadIC_Initialize)); EmitCallIC(ic, RelocInfo::CODE_TARGET_CONTEXT); @@ -1514,7 +1512,7 @@ void FullCodeGenerator::EmitVariableAssignment(Variable* var, // assignment. Right-hand-side value is passed in r0, variable name in // r2, and the global object in r1. __ mov(r2, Operand(var->name())); - __ ldr(r1, CodeGenerator::GlobalObject()); + __ ldr(r1, GlobalObjectOperand()); Handle<Code> ic(Builtins::builtin(Builtins::StoreIC_Initialize)); EmitCallIC(ic, RelocInfo::CODE_TARGET); @@ -1688,15 +1686,17 @@ void FullCodeGenerator::EmitCallWithIC(Call* expr, // Code common for calls using the IC. ZoneList<Expression*>* args = expr->arguments(); int arg_count = args->length(); - for (int i = 0; i < arg_count; i++) { - VisitForStackValue(args->at(i)); + { PreserveStatementPositionScope scope(masm()->positions_recorder()); + for (int i = 0; i < arg_count; i++) { + VisitForStackValue(args->at(i)); + } + __ mov(r2, Operand(name)); } - __ mov(r2, Operand(name)); // Record source position for debugger. - SetSourcePosition(expr->position()); + SetSourcePosition(expr->position(), FORCED_POSITION); // Call the IC initialization code. InLoopFlag in_loop = (loop_depth() > 0) ? IN_LOOP : NOT_IN_LOOP; - Handle<Code> ic = CodeGenerator::ComputeCallInitialize(arg_count, in_loop); + Handle<Code> ic = StubCache::ComputeCallInitialize(arg_count, in_loop); EmitCallIC(ic, mode); // Restore context register. __ ldr(cp, MemOperand(fp, StandardFrameConstants::kContextOffset)); @@ -1707,24 +1707,33 @@ void FullCodeGenerator::EmitCallWithIC(Call* expr, void FullCodeGenerator::EmitKeyedCallWithIC(Call* expr, Expression* key, RelocInfo::Mode mode) { + // Load the key. + VisitForAccumulatorValue(key); + + // Swap the name of the function and the receiver on the stack to follow + // the calling convention for call ICs. + __ pop(r1); + __ push(r0); + __ push(r1); + // Code common for calls using the IC. ZoneList<Expression*>* args = expr->arguments(); int arg_count = args->length(); - for (int i = 0; i < arg_count; i++) { - VisitForStackValue(args->at(i)); + { PreserveStatementPositionScope scope(masm()->positions_recorder()); + for (int i = 0; i < arg_count; i++) { + VisitForStackValue(args->at(i)); + } } - VisitForAccumulatorValue(key); - __ mov(r2, r0); // Record source position for debugger. - SetSourcePosition(expr->position()); + SetSourcePosition(expr->position(), FORCED_POSITION); // Call the IC initialization code. InLoopFlag in_loop = (loop_depth() > 0) ? IN_LOOP : NOT_IN_LOOP; - Handle<Code> ic = CodeGenerator::ComputeKeyedCallInitialize(arg_count, - in_loop); + Handle<Code> ic = StubCache::ComputeKeyedCallInitialize(arg_count, in_loop); + __ ldr(r2, MemOperand(sp, (arg_count + 1) * kPointerSize)); // Key. EmitCallIC(ic, mode); // Restore context register. __ ldr(cp, MemOperand(fp, StandardFrameConstants::kContextOffset)); - context()->Plug(r0); + context()->DropAndPlug(1, r0); // Drop the key still on the stack. } @@ -1732,11 +1741,13 @@ void FullCodeGenerator::EmitCallWithStub(Call* expr) { // Code common for calls using the call stub. ZoneList<Expression*>* args = expr->arguments(); int arg_count = args->length(); - for (int i = 0; i < arg_count; i++) { - VisitForStackValue(args->at(i)); + { PreserveStatementPositionScope scope(masm()->positions_recorder()); + for (int i = 0; i < arg_count; i++) { + VisitForStackValue(args->at(i)); + } } // Record source position for debugger. - SetSourcePosition(expr->position()); + SetSourcePosition(expr->position(), FORCED_POSITION); InLoopFlag in_loop = (loop_depth() > 0) ? IN_LOOP : NOT_IN_LOOP; CallFunctionStub stub(arg_count, in_loop, RECEIVER_MIGHT_BE_VALUE); __ CallStub(&stub); @@ -1756,41 +1767,45 @@ void FullCodeGenerator::VisitCall(Call* expr) { // resolve the function we need to call and the receiver of the // call. Then we call the resolved function using the given // arguments. - VisitForStackValue(fun); - __ LoadRoot(r2, Heap::kUndefinedValueRootIndex); - __ push(r2); // Reserved receiver slot. - - // Push the arguments. ZoneList<Expression*>* args = expr->arguments(); int arg_count = args->length(); - for (int i = 0; i < arg_count; i++) { - VisitForStackValue(args->at(i)); - } - // Push copy of the function - found below the arguments. - __ ldr(r1, MemOperand(sp, (arg_count + 1) * kPointerSize)); - __ push(r1); + { PreserveStatementPositionScope pos_scope(masm()->positions_recorder()); + VisitForStackValue(fun); + __ LoadRoot(r2, Heap::kUndefinedValueRootIndex); + __ push(r2); // Reserved receiver slot. + + // Push the arguments. + for (int i = 0; i < arg_count; i++) { + VisitForStackValue(args->at(i)); + } - // Push copy of the first argument or undefined if it doesn't exist. - if (arg_count > 0) { - __ ldr(r1, MemOperand(sp, arg_count * kPointerSize)); + // Push copy of the function - found below the arguments. + __ ldr(r1, MemOperand(sp, (arg_count + 1) * kPointerSize)); __ push(r1); - } else { - __ push(r2); - } - // Push the receiver of the enclosing function and do runtime call. - __ ldr(r1, MemOperand(fp, (2 + scope()->num_parameters()) * kPointerSize)); - __ push(r1); - __ CallRuntime(Runtime::kResolvePossiblyDirectEval, 3); + // Push copy of the first argument or undefined if it doesn't exist. + if (arg_count > 0) { + __ ldr(r1, MemOperand(sp, arg_count * kPointerSize)); + __ push(r1); + } else { + __ push(r2); + } - // The runtime call returns a pair of values in r0 (function) and - // r1 (receiver). Touch up the stack with the right values. - __ str(r0, MemOperand(sp, (arg_count + 1) * kPointerSize)); - __ str(r1, MemOperand(sp, arg_count * kPointerSize)); + // Push the receiver of the enclosing function and do runtime call. + __ ldr(r1, + MemOperand(fp, (2 + scope()->num_parameters()) * kPointerSize)); + __ push(r1); + __ CallRuntime(Runtime::kResolvePossiblyDirectEval, 3); + + // The runtime call returns a pair of values in r0 (function) and + // r1 (receiver). Touch up the stack with the right values. + __ str(r0, MemOperand(sp, (arg_count + 1) * kPointerSize)); + __ str(r1, MemOperand(sp, arg_count * kPointerSize)); + } // Record source position for debugger. - SetSourcePosition(expr->position()); + SetSourcePosition(expr->position(), FORCED_POSITION); InLoopFlag in_loop = (loop_depth() > 0) ? IN_LOOP : NOT_IN_LOOP; CallFunctionStub stub(arg_count, in_loop, RECEIVER_MIGHT_BE_VALUE); __ CallStub(&stub); @@ -1799,7 +1814,7 @@ void FullCodeGenerator::VisitCall(Call* expr) { context()->DropAndPlug(1, r0); } else if (var != NULL && !var->is_this() && var->is_global()) { // Push global object as receiver for the call IC. - __ ldr(r0, CodeGenerator::GlobalObject()); + __ ldr(r0, GlobalObjectOperand()); __ push(r0); EmitCallWithIC(expr, var->name(), RelocInfo::CODE_TARGET_CONTEXT); } else if (var != NULL && var->AsSlot() != NULL && @@ -1807,12 +1822,14 @@ void FullCodeGenerator::VisitCall(Call* expr) { // Call to a lookup slot (dynamically introduced variable). Label slow, done; - // Generate code for loading from variables potentially shadowed - // by eval-introduced variables. - EmitDynamicLoadFromSlotFastCase(var->AsSlot(), - NOT_INSIDE_TYPEOF, - &slow, - &done); + { PreserveStatementPositionScope scope(masm()->positions_recorder()); + // Generate code for loading from variables potentially shadowed + // by eval-introduced variables. + EmitDynamicLoadFromSlotFastCase(var->AsSlot(), + NOT_INSIDE_TYPEOF, + &slow, + &done); + } __ bind(&slow); // Call the runtime to find the function to call (returned in r0) @@ -1833,7 +1850,7 @@ void FullCodeGenerator::VisitCall(Call* expr) { // Push function. __ push(r0); // Push global receiver. - __ ldr(r1, CodeGenerator::GlobalObject()); + __ ldr(r1, GlobalObjectOperand()); __ ldr(r1, FieldMemOperand(r1, GlobalObject::kGlobalReceiverOffset)); __ push(r1); __ bind(&call); @@ -1846,22 +1863,28 @@ void FullCodeGenerator::VisitCall(Call* expr) { Literal* key = prop->key()->AsLiteral(); if (key != NULL && key->handle()->IsSymbol()) { // Call to a named property, use call IC. - VisitForStackValue(prop->obj()); + { PreserveStatementPositionScope scope(masm()->positions_recorder()); + VisitForStackValue(prop->obj()); + } EmitCallWithIC(expr, key->handle(), RelocInfo::CODE_TARGET); } else { // Call to a keyed property. // For a synthetic property use keyed load IC followed by function call, // for a regular property use keyed CallIC. - VisitForStackValue(prop->obj()); + { PreserveStatementPositionScope scope(masm()->positions_recorder()); + VisitForStackValue(prop->obj()); + } if (prop->is_synthetic()) { - VisitForAccumulatorValue(prop->key()); + { PreserveStatementPositionScope scope(masm()->positions_recorder()); + VisitForAccumulatorValue(prop->key()); + } // Record source code position for IC call. - SetSourcePosition(prop->position()); + SetSourcePosition(prop->position(), FORCED_POSITION); __ pop(r1); // We do not need to keep the receiver. Handle<Code> ic(Builtins::builtin(Builtins::KeyedLoadIC_Initialize)); EmitCallIC(ic, RelocInfo::CODE_TARGET); - __ ldr(r1, CodeGenerator::GlobalObject()); + __ ldr(r1, GlobalObjectOperand()); __ ldr(r1, FieldMemOperand(r1, GlobalObject::kGlobalReceiverOffset)); __ Push(r0, r1); // Function, receiver. EmitCallWithStub(expr); @@ -1879,9 +1902,12 @@ void FullCodeGenerator::VisitCall(Call* expr) { loop_depth() == 0) { lit->set_try_full_codegen(true); } - VisitForStackValue(fun); + + { PreserveStatementPositionScope scope(masm()->positions_recorder()); + VisitForStackValue(fun); + } // Load global receiver object. - __ ldr(r1, CodeGenerator::GlobalObject()); + __ ldr(r1, GlobalObjectOperand()); __ ldr(r1, FieldMemOperand(r1, GlobalObject::kGlobalReceiverOffset)); __ push(r1); // Emit function call. @@ -2759,7 +2785,7 @@ void FullCodeGenerator::VisitCallRuntime(CallRuntime* expr) { if (expr->is_jsruntime()) { // Prepare for calling JS runtime function. - __ ldr(r0, CodeGenerator::GlobalObject()); + __ ldr(r0, GlobalObjectOperand()); __ ldr(r0, FieldMemOperand(r0, GlobalObject::kBuiltinsOffset)); __ push(r0); } @@ -2773,8 +2799,7 @@ void FullCodeGenerator::VisitCallRuntime(CallRuntime* expr) { if (expr->is_jsruntime()) { // Call the JS runtime function. __ mov(r2, Operand(expr->name())); - Handle<Code> ic = CodeGenerator::ComputeCallInitialize(arg_count, - NOT_IN_LOOP); + Handle<Code> ic = StubCache::ComputeCallInitialize(arg_count, NOT_IN_LOOP); EmitCallIC(ic, RelocInfo::CODE_TARGET); // Restore context register. __ ldr(cp, MemOperand(fp, StandardFrameConstants::kContextOffset)); @@ -2811,7 +2836,7 @@ void FullCodeGenerator::VisitUnaryOperation(UnaryOperation* expr) { VisitForStackValue(prop->obj()); VisitForStackValue(prop->key()); } else if (var->is_global()) { - __ ldr(r1, CodeGenerator::GlobalObject()); + __ ldr(r1, GlobalObjectOperand()); __ mov(r0, Operand(var->name())); __ Push(r1, r0); } else { @@ -3077,7 +3102,7 @@ void FullCodeGenerator::VisitForTypeofValue(Expression* expr) { VariableProxy* proxy = expr->AsVariableProxy(); if (proxy != NULL && !proxy->var()->is_this() && proxy->var()->is_global()) { Comment cmnt(masm_, "Global variable"); - __ ldr(r0, CodeGenerator::GlobalObject()); + __ ldr(r0, GlobalObjectOperand()); __ mov(r2, Operand(proxy->name())); Handle<Code> ic(Builtins::builtin(Builtins::LoadIC_Initialize)); // Use a regular load, not a contextual load, to avoid a reference diff --git a/deps/v8/src/arm/ic-arm.cc b/deps/v8/src/arm/ic-arm.cc index a09afdf754..4c1f9835f4 100644 --- a/deps/v8/src/arm/ic-arm.cc +++ b/deps/v8/src/arm/ic-arm.cc @@ -1988,9 +1988,9 @@ void KeyedStoreIC::GenerateExternalArray(MacroAssembler* masm, // Not infinity or NaN simply convert to int. if (IsElementTypeSigned(array_type)) { - __ vcvt_s32_f64(s0, d0, ne); + __ vcvt_s32_f64(s0, d0, Assembler::RoundToZero, ne); } else { - __ vcvt_u32_f64(s0, d0, ne); + __ vcvt_u32_f64(s0, d0, Assembler::RoundToZero, ne); } __ vmov(r5, s0, ne); diff --git a/deps/v8/src/arm/macro-assembler-arm.cc b/deps/v8/src/arm/macro-assembler-arm.cc index 7f6090bc50..d2c22af53d 100644 --- a/deps/v8/src/arm/macro-assembler-arm.cc +++ b/deps/v8/src/arm/macro-assembler-arm.cc @@ -129,7 +129,7 @@ void MacroAssembler::Call(intptr_t target, RelocInfo::Mode rmode, // address is loaded. The mov method will automatically record // positions when pc is the target, since this is not the case here // we have to do it explicitly. - WriteRecordedPositions(); + positions_recorder()->WriteRecordedPositions(); mov(ip, Operand(target, rmode), LeaveCC, cond); blx(ip, cond); @@ -220,20 +220,20 @@ void MacroAssembler::Move(Register dst, Register src) { void MacroAssembler::And(Register dst, Register src1, const Operand& src2, Condition cond) { - if (!CpuFeatures::IsSupported(ARMv7) || src2.is_single_instruction()) { - and_(dst, src1, src2, LeaveCC, cond); - return; - } - int32_t immediate = src2.immediate(); - if (immediate == 0) { + if (!src2.is_reg() && + !src2.must_use_constant_pool() && + src2.immediate() == 0) { mov(dst, Operand(0, RelocInfo::NONE), LeaveCC, cond); - return; - } - if (IsPowerOf2(immediate + 1) && ((immediate & 1) != 0)) { - ubfx(dst, src1, 0, WhichPowerOf2(immediate + 1), cond); - return; + + } else if (!src2.is_single_instruction() && + !src2.must_use_constant_pool() && + CpuFeatures::IsSupported(ARMv7) && + IsPowerOf2(src2.immediate() + 1)) { + ubfx(dst, src1, 0, WhichPowerOf2(src2.immediate() + 1), cond); + + } else { + and_(dst, src1, src2, LeaveCC, cond); } - and_(dst, src1, src2, LeaveCC, cond); } diff --git a/deps/v8/src/arm/macro-assembler-arm.h b/deps/v8/src/arm/macro-assembler-arm.h index 48a8059966..8d89d6984c 100644 --- a/deps/v8/src/arm/macro-assembler-arm.h +++ b/deps/v8/src/arm/macro-assembler-arm.h @@ -727,6 +727,16 @@ class CodePatcher { // ----------------------------------------------------------------------------- // Static helper functions. +static MemOperand ContextOperand(Register context, int index) { + return MemOperand(context, Context::SlotOffset(index)); +} + + +static inline MemOperand GlobalObjectOperand() { + return ContextOperand(cp, Context::GLOBAL_INDEX); +} + + #ifdef GENERATED_CODE_COVERAGE #define CODE_COVERAGE_STRINGIFY(x) #x #define CODE_COVERAGE_TOSTRING(x) CODE_COVERAGE_STRINGIFY(x) diff --git a/deps/v8/src/arm/simulator-arm.cc b/deps/v8/src/arm/simulator-arm.cc index cb91520f3a..3ec5f449d8 100644 --- a/deps/v8/src/arm/simulator-arm.cc +++ b/deps/v8/src/arm/simulator-arm.cc @@ -705,6 +705,7 @@ Simulator::Simulator() { z_flag_FPSCR_ = false; c_flag_FPSCR_ = false; v_flag_FPSCR_ = false; + FPSCR_rounding_mode_ = RZ; inv_op_vfp_flag_ = false; div_zero_vfp_flag_ = false; @@ -2501,10 +2502,45 @@ void Simulator::DecodeTypeVFP(Instr* instr) { (instr->VAField() == 0x7) && (instr->Bits(19, 16) == 0x1)) { // vmrs - if (instr->RtField() == 0xF) + uint32_t rt = instr->RtField(); + if (rt == 0xF) { Copy_FPSCR_to_APSR(); - else - UNIMPLEMENTED(); // Not used by V8. + } else { + // Emulate FPSCR from the Simulator flags. + uint32_t fpscr = (n_flag_FPSCR_ << 31) | + (z_flag_FPSCR_ << 30) | + (c_flag_FPSCR_ << 29) | + (v_flag_FPSCR_ << 28) | + (inexact_vfp_flag_ << 4) | + (underflow_vfp_flag_ << 3) | + (overflow_vfp_flag_ << 2) | + (div_zero_vfp_flag_ << 1) | + (inv_op_vfp_flag_ << 0) | + (FPSCR_rounding_mode_ << 22); + set_register(rt, fpscr); + } + } else if ((instr->VLField() == 0x0) && + (instr->VCField() == 0x0) && + (instr->VAField() == 0x7) && + (instr->Bits(19, 16) == 0x1)) { + // vmsr + uint32_t rt = instr->RtField(); + if (rt == pc) { + UNREACHABLE(); + } else { + uint32_t rt_value = get_register(rt); + n_flag_FPSCR_ = (rt_value >> 31) & 1; + z_flag_FPSCR_ = (rt_value >> 30) & 1; + c_flag_FPSCR_ = (rt_value >> 29) & 1; + v_flag_FPSCR_ = (rt_value >> 28) & 1; + inexact_vfp_flag_ = (rt_value >> 4) & 1; + underflow_vfp_flag_ = (rt_value >> 3) & 1; + overflow_vfp_flag_ = (rt_value >> 2) & 1; + div_zero_vfp_flag_ = (rt_value >> 1) & 1; + inv_op_vfp_flag_ = (rt_value >> 0) & 1; + FPSCR_rounding_mode_ = + static_cast<FPSCRRoundingModes>((rt_value >> 22) & 3); + } } else { UNIMPLEMENTED(); // Not used by V8. } @@ -2605,29 +2641,71 @@ void Simulator::DecodeVCVTBetweenFloatingPointAndInteger(Instr* instr) { if (to_integer) { bool unsigned_integer = (instr->Bit(16) == 0); + FPSCRRoundingModes mode; if (instr->Bit(7) != 1) { - // Only rounding towards zero supported. - UNIMPLEMENTED(); // Not used by V8. + // Use FPSCR defined rounding mode. + mode = FPSCR_rounding_mode_; + // Only RZ and RM modes are supported. + ASSERT((mode == RM) || (mode == RZ)); + } else { + // VFP uses round towards zero by default. + mode = RZ; } int dst = instr->VFPDRegCode(kSinglePrecision); int src = instr->VFPMRegCode(src_precision); + int32_t kMaxInt = v8::internal::kMaxInt; + int32_t kMinInt = v8::internal::kMinInt; + switch (mode) { + case RM: + if (src_precision == kDoublePrecision) { + double val = get_double_from_d_register(src); - if (src_precision == kDoublePrecision) { - double val = get_double_from_d_register(src); + inv_op_vfp_flag_ = (val > kMaxInt) || (val < kMinInt) || (val != val); - int sint = unsigned_integer ? static_cast<uint32_t>(val) : - static_cast<int32_t>(val); + int sint = unsigned_integer ? static_cast<uint32_t>(val) : + static_cast<int32_t>(val); + sint = sint > val ? sint - 1 : sint; - set_s_register_from_sinteger(dst, sint); - } else { - float val = get_float_from_s_register(src); + set_s_register_from_sinteger(dst, sint); + } else { + float val = get_float_from_s_register(src); + + inv_op_vfp_flag_ = (val > kMaxInt) || (val < kMinInt) || (val != val); - int sint = unsigned_integer ? static_cast<uint32_t>(val) : - static_cast<int32_t>(val); + int sint = unsigned_integer ? static_cast<uint32_t>(val) : + static_cast<int32_t>(val); + sint = sint > val ? sint - 1 : sint; - set_s_register_from_sinteger(dst, sint); + set_s_register_from_sinteger(dst, sint); + } + break; + case RZ: + if (src_precision == kDoublePrecision) { + double val = get_double_from_d_register(src); + + inv_op_vfp_flag_ = (val > kMaxInt) || (val < kMinInt) || (val != val); + + int sint = unsigned_integer ? static_cast<uint32_t>(val) : + static_cast<int32_t>(val); + + set_s_register_from_sinteger(dst, sint); + } else { + float val = get_float_from_s_register(src); + + inv_op_vfp_flag_ = (val > kMaxInt) || (val < kMinInt) || (val != val); + + int sint = unsigned_integer ? static_cast<uint32_t>(val) : + static_cast<int32_t>(val); + + set_s_register_from_sinteger(dst, sint); + } + break; + + default: + UNREACHABLE(); } + } else { bool unsigned_integer = (instr->Bit(7) == 0); diff --git a/deps/v8/src/arm/simulator-arm.h b/deps/v8/src/arm/simulator-arm.h index 3e023489ee..c37b3f7156 100644 --- a/deps/v8/src/arm/simulator-arm.h +++ b/deps/v8/src/arm/simulator-arm.h @@ -306,6 +306,9 @@ class Simulator { bool c_flag_FPSCR_; bool v_flag_FPSCR_; + // VFP rounding mode. See ARM DDI 0406B Page A2-29. + FPSCRRoundingModes FPSCR_rounding_mode_; + // VFP FP exception flags architecture state. bool inv_op_vfp_flag_; bool div_zero_vfp_flag_; diff --git a/deps/v8/src/arm/stub-cache-arm.cc b/deps/v8/src/arm/stub-cache-arm.cc index 5e29c2e485..a0ef80a0fd 100644 --- a/deps/v8/src/arm/stub-cache-arm.cc +++ b/deps/v8/src/arm/stub-cache-arm.cc @@ -1676,8 +1676,143 @@ MaybeObject* CallStubCompiler::CompileMathFloorCall(Object* object, JSGlobalPropertyCell* cell, JSFunction* function, String* name) { - // TODO(872): implement this. - return Heap::undefined_value(); + // ----------- S t a t e ------------- + // -- r2 : function name + // -- lr : return address + // -- sp[(argc - n - 1) * 4] : arg[n] (zero-based) + // -- ... + // -- sp[argc * 4] : receiver + // ----------------------------------- + + if (!CpuFeatures::IsSupported(VFP3)) return Heap::undefined_value(); + CpuFeatures::Scope scope_vfp3(VFP3); + + const int argc = arguments().immediate(); + + // If the object is not a JSObject or we got an unexpected number of + // arguments, bail out to the regular call. + if (!object->IsJSObject() || argc != 1) return Heap::undefined_value(); + + Label miss, slow; + GenerateNameCheck(name, &miss); + + if (cell == NULL) { + __ ldr(r1, MemOperand(sp, 1 * kPointerSize)); + + STATIC_ASSERT(kSmiTag == 0); + __ BranchOnSmi(r1, &miss); + + CheckPrototypes(JSObject::cast(object), r1, holder, r0, r3, r4, name, + &miss); + } else { + ASSERT(cell->value() == function); + GenerateGlobalReceiverCheck(JSObject::cast(object), holder, name, &miss); + GenerateLoadFunctionFromCell(cell, function, &miss); + } + + // Load the (only) argument into r0. + __ ldr(r0, MemOperand(sp, 0 * kPointerSize)); + + // If the argument is a smi, just return. + STATIC_ASSERT(kSmiTag == 0); + __ tst(r0, Operand(kSmiTagMask)); + __ Drop(argc + 1, eq); + __ Ret(eq); + + __ CheckMap(r0, r1, Heap::kHeapNumberMapRootIndex, &slow, true); + + Label wont_fit_smi, no_vfp_exception, restore_fpscr_and_return; + + // If vfp3 is enabled, we use the fpu rounding with the RM (round towards + // minus infinity) mode. + + // Load the HeapNumber value. + // We will need access to the value in the core registers, so we load it + // with ldrd and move it to the fpu. It also spares a sub instruction for + // updating the HeapNumber value address, as vldr expects a multiple + // of 4 offset. + __ Ldrd(r4, r5, FieldMemOperand(r0, HeapNumber::kValueOffset)); + __ vmov(d1, r4, r5); + + // Backup FPSCR. + __ vmrs(r3); + // Set custom FPCSR: + // - Set rounding mode to "Round towards Minus Infinity" + // (ie bits [23:22] = 0b10). + // - Clear vfp cumulative exception flags (bits [3:0]). + // - Make sure Flush-to-zero mode control bit is unset (bit 22). + __ bic(r9, r3, + Operand(kVFPExceptionMask | kVFPRoundingModeMask | kVFPFlushToZeroMask)); + __ orr(r9, r9, Operand(kVFPRoundToMinusInfinityBits)); + __ vmsr(r9); + + // Convert the argument to an integer. + __ vcvt_s32_f64(s0, d1, Assembler::FPSCRRounding, al); + + // Use vcvt latency to start checking for special cases. + // Get the argument exponent and clear the sign bit. + __ bic(r6, r5, Operand(HeapNumber::kSignMask)); + __ mov(r6, Operand(r6, LSR, HeapNumber::kMantissaBitsInTopWord)); + + // Retrieve FPSCR and check for vfp exceptions. + __ vmrs(r9); + __ tst(r9, Operand(kVFPExceptionMask)); + __ b(&no_vfp_exception, eq); + + // Check for NaN, Infinity, and -Infinity. + // They are invariant through a Math.Floor call, so just + // return the original argument. + __ sub(r7, r6, Operand(HeapNumber::kExponentMask + >> HeapNumber::kMantissaBitsInTopWord), SetCC); + __ b(&restore_fpscr_and_return, eq); + // We had an overflow or underflow in the conversion. Check if we + // have a big exponent. + __ cmp(r7, Operand(HeapNumber::kMantissaBits)); + // If greater or equal, the argument is already round and in r0. + __ b(&restore_fpscr_and_return, ge); + __ b(&slow); + + __ bind(&no_vfp_exception); + // Move the result back to general purpose register r0. + __ vmov(r0, s0); + // Check if the result fits into a smi. + __ add(r1, r0, Operand(0x40000000), SetCC); + __ b(&wont_fit_smi, mi); + // Tag the result. + STATIC_ASSERT(kSmiTag == 0); + __ mov(r0, Operand(r0, LSL, kSmiTagSize)); + + // Check for -0. + __ cmp(r0, Operand(0)); + __ b(&restore_fpscr_and_return, ne); + // r5 already holds the HeapNumber exponent. + __ tst(r5, Operand(HeapNumber::kSignMask)); + // If our HeapNumber is negative it was -0, so load its address and return. + // Else r0 is loaded with 0, so we can also just return. + __ ldr(r0, MemOperand(sp, 0 * kPointerSize), ne); + + __ bind(&restore_fpscr_and_return); + // Restore FPSCR and return. + __ vmsr(r3); + __ Drop(argc + 1); + __ Ret(); + + __ bind(&wont_fit_smi); + __ bind(&slow); + // Restore FPCSR and fall to slow case. + __ vmsr(r3); + + // Tail call the full function. We do not have to patch the receiver + // because the function makes no use of it. + __ InvokeFunction(function, arguments(), JUMP_FUNCTION); + + __ bind(&miss); + // r2: function name. + MaybeObject* obj = GenerateMissBranch(); + if (obj->IsFailure()) return obj; + + // Return the generated code. + return (cell == NULL) ? GetCode(function) : GetCode(NORMAL, name); } diff --git a/deps/v8/src/arm/virtual-frame-arm.cc b/deps/v8/src/arm/virtual-frame-arm.cc index da76edf391..45f48767c4 100644 --- a/deps/v8/src/arm/virtual-frame-arm.cc +++ b/deps/v8/src/arm/virtual-frame-arm.cc @@ -245,18 +245,15 @@ void VirtualFrame::AllocateStackSlots() { __ LoadRoot(r2, Heap::kStackLimitRootIndex); } // Check the stack for overflow or a break request. - // Put the lr setup instruction in the delay slot. The kInstrSize is added - // to the implicit 8 byte offset that always applies to operations with pc - // and gives a return address 12 bytes down. - masm()->add(lr, pc, Operand(Assembler::kInstrSize)); masm()->cmp(sp, Operand(r2)); StackCheckStub stub; // Call the stub if lower. - masm()->mov(pc, + masm()->mov(ip, Operand(reinterpret_cast<intptr_t>(stub.GetCode().location()), RelocInfo::CODE_TARGET), LeaveCC, lo); + masm()->Call(ip, lo); } diff --git a/deps/v8/src/assembler.cc b/deps/v8/src/assembler.cc index ce90dceacb..7493673e81 100644 --- a/deps/v8/src/assembler.cc +++ b/deps/v8/src/assembler.cc @@ -804,4 +804,53 @@ ExternalReference ExternalReference::debug_step_in_fp_address() { } #endif + +void PositionsRecorder::RecordPosition(int pos, + PositionRecordingType recording_type) { + ASSERT(pos != RelocInfo::kNoPosition); + ASSERT(pos >= 0); + current_position_ = pos; + current_position_recording_type_ = recording_type; +} + + +void PositionsRecorder::RecordStatementPosition(int pos) { + ASSERT(pos != RelocInfo::kNoPosition); + ASSERT(pos >= 0); + current_statement_position_ = pos; +} + + +bool PositionsRecorder::WriteRecordedPositions() { + bool written = false; + + // Write the statement position if it is different from what was written last + // time. + if (current_statement_position_ != written_statement_position_) { + EnsureSpace ensure_space(assembler_); + assembler_->RecordRelocInfo(RelocInfo::STATEMENT_POSITION, + current_statement_position_); + written_statement_position_ = current_statement_position_; + written = true; + } + + // Write the position if it is different from what was written last time and + // also different from the written statement position or was forced. + if (current_position_ != written_position_ && + (current_position_ != current_statement_position_ || !written) && + (current_position_ != written_statement_position_ + || current_position_recording_type_ == FORCED_POSITION)) { + EnsureSpace ensure_space(assembler_); + assembler_->RecordRelocInfo(RelocInfo::POSITION, current_position_); + written_position_ = current_position_; + written = true; + } + + current_position_recording_type_ = NORMAL_POSITION; + + // Return whether something was written. + return written; +} + + } } // namespace v8::internal diff --git a/deps/v8/src/assembler.h b/deps/v8/src/assembler.h index 66811777fa..09159fed08 100644 --- a/deps/v8/src/assembler.h +++ b/deps/v8/src/assembler.h @@ -585,6 +585,67 @@ class ExternalReference BASE_EMBEDDED { // ----------------------------------------------------------------------------- +// Position recording support + +enum PositionRecordingType { FORCED_POSITION, NORMAL_POSITION }; + +class PositionsRecorder BASE_EMBEDDED { + public: + explicit PositionsRecorder(Assembler* assembler) + : assembler_(assembler), + current_position_(RelocInfo::kNoPosition), + current_position_recording_type_(NORMAL_POSITION), + written_position_(RelocInfo::kNoPosition), + current_statement_position_(RelocInfo::kNoPosition), + written_statement_position_(RelocInfo::kNoPosition) { } + + // Set current position to pos. If recording_type is FORCED_POSITION then + // WriteRecordedPositions will write this position even if it is equal to + // statement position previously written for another pc. + void RecordPosition(int pos, + PositionRecordingType recording_type = NORMAL_POSITION); + + // Set current statement position to pos. + void RecordStatementPosition(int pos); + + // Write recorded positions to relocation information. + bool WriteRecordedPositions(); + + int current_position() const { return current_position_; } + + int current_statement_position() const { return current_statement_position_; } + + private: + Assembler* assembler_; + + int current_position_; + PositionRecordingType current_position_recording_type_; + int written_position_; + + int current_statement_position_; + int written_statement_position_; +}; + + +class PreserveStatementPositionScope BASE_EMBEDDED { + public: + explicit PreserveStatementPositionScope(PositionsRecorder* positions_recorder) + : positions_recorder_(positions_recorder), + statement_position_(positions_recorder->current_statement_position()) {} + + ~PreserveStatementPositionScope() { + if (statement_position_ != RelocInfo::kNoPosition) { + positions_recorder_->RecordStatementPosition(statement_position_); + } + } + + private: + PositionsRecorder* positions_recorder_; + int statement_position_; +}; + + +// ----------------------------------------------------------------------------- // Utility functions static inline bool is_intn(int x, int n) { diff --git a/deps/v8/src/bignum-dtoa.cc b/deps/v8/src/bignum-dtoa.cc new file mode 100644 index 0000000000..088dd79f55 --- /dev/null +++ b/deps/v8/src/bignum-dtoa.cc @@ -0,0 +1,655 @@ +// Copyright 2010 the V8 project authors. All rights reserved. +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following +// disclaimer in the documentation and/or other materials provided +// with the distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +#include <math.h> + +#include "v8.h" +#include "bignum-dtoa.h" + +#include "bignum.h" +#include "double.h" + +namespace v8 { +namespace internal { + +static int NormalizedExponent(uint64_t significand, int exponent) { + ASSERT(significand != 0); + while ((significand & Double::kHiddenBit) == 0) { + significand = significand << 1; + exponent = exponent - 1; + } + return exponent; +} + + +// Forward declarations: +// Returns an estimation of k such that 10^(k-1) <= v < 10^k. +static int EstimatePower(int exponent); +// Computes v / 10^estimated_power exactly, as a ratio of two bignums, numerator +// and denominator. +static void InitialScaledStartValues(double v, + int estimated_power, + bool need_boundary_deltas, + Bignum* numerator, + Bignum* denominator, + Bignum* delta_minus, + Bignum* delta_plus); +// Multiplies numerator/denominator so that its values lies in the range 1-10. +// Returns decimal_point s.t. +// v = numerator'/denominator' * 10^(decimal_point-1) +// where numerator' and denominator' are the values of numerator and +// denominator after the call to this function. +static void FixupMultiply10(int estimated_power, bool is_even, + int* decimal_point, + Bignum* numerator, Bignum* denominator, + Bignum* delta_minus, Bignum* delta_plus); +// Generates digits from the left to the right and stops when the generated +// digits yield the shortest decimal representation of v. +static void GenerateShortestDigits(Bignum* numerator, Bignum* denominator, + Bignum* delta_minus, Bignum* delta_plus, + bool is_even, + Vector<char> buffer, int* length); +// Generates 'requested_digits' after the decimal point. +static void BignumToFixed(int requested_digits, int* decimal_point, + Bignum* numerator, Bignum* denominator, + Vector<char>(buffer), int* length); +// Generates 'count' digits of numerator/denominator. +// Once 'count' digits have been produced rounds the result depending on the +// remainder (remainders of exactly .5 round upwards). Might update the +// decimal_point when rounding up (for example for 0.9999). +static void GenerateCountedDigits(int count, int* decimal_point, + Bignum* numerator, Bignum* denominator, + Vector<char>(buffer), int* length); + + +void BignumDtoa(double v, BignumDtoaMode mode, int requested_digits, + Vector<char> buffer, int* length, int* decimal_point) { + ASSERT(v > 0); + ASSERT(!Double(v).IsSpecial()); + uint64_t significand = Double(v).Significand(); + bool is_even = (significand & 1) == 0; + int exponent = Double(v).Exponent(); + int normalized_exponent = NormalizedExponent(significand, exponent); + // estimated_power might be too low by 1. + int estimated_power = EstimatePower(normalized_exponent); + + // Shortcut for Fixed. + // The requested digits correspond to the digits after the point. If the + // number is much too small, then there is no need in trying to get any + // digits. + if (mode == BIGNUM_DTOA_FIXED && -estimated_power - 1 > requested_digits) { + buffer[0] = '\0'; + *length = 0; + // Set decimal-point to -requested_digits. This is what Gay does. + // Note that it should not have any effect anyways since the string is + // empty. + *decimal_point = -requested_digits; + return; + } + + Bignum numerator; + Bignum denominator; + Bignum delta_minus; + Bignum delta_plus; + // Make sure the bignum can grow large enough. The smallest double equals + // 4e-324. In this case the denominator needs fewer than 324*4 binary digits. + // The maximum double is 1.7976931348623157e308 which needs fewer than + // 308*4 binary digits. + ASSERT(Bignum::kMaxSignificantBits >= 324*4); + bool need_boundary_deltas = (mode == BIGNUM_DTOA_SHORTEST); + InitialScaledStartValues(v, estimated_power, need_boundary_deltas, + &numerator, &denominator, + &delta_minus, &delta_plus); + // We now have v = (numerator / denominator) * 10^estimated_power. + FixupMultiply10(estimated_power, is_even, decimal_point, + &numerator, &denominator, + &delta_minus, &delta_plus); + // We now have v = (numerator / denominator) * 10^(decimal_point-1), and + // 1 <= (numerator + delta_plus) / denominator < 10 + switch (mode) { + case BIGNUM_DTOA_SHORTEST: + GenerateShortestDigits(&numerator, &denominator, + &delta_minus, &delta_plus, + is_even, buffer, length); + break; + case BIGNUM_DTOA_FIXED: + BignumToFixed(requested_digits, decimal_point, + &numerator, &denominator, + buffer, length); + break; + case BIGNUM_DTOA_PRECISION: + GenerateCountedDigits(requested_digits, decimal_point, + &numerator, &denominator, + buffer, length); + break; + default: + UNREACHABLE(); + } + buffer[*length] = '\0'; +} + + +// The procedure starts generating digits from the left to the right and stops +// when the generated digits yield the shortest decimal representation of v. A +// decimal representation of v is a number lying closer to v than to any other +// double, so it converts to v when read. +// +// This is true if d, the decimal representation, is between m- and m+, the +// upper and lower boundaries. d must be strictly between them if !is_even. +// m- := (numerator - delta_minus) / denominator +// m+ := (numerator + delta_plus) / denominator +// +// Precondition: 0 <= (numerator+delta_plus) / denominator < 10. +// If 1 <= (numerator+delta_plus) / denominator < 10 then no leading 0 digit +// will be produced. This should be the standard precondition. +static void GenerateShortestDigits(Bignum* numerator, Bignum* denominator, + Bignum* delta_minus, Bignum* delta_plus, + bool is_even, + Vector<char> buffer, int* length) { + // Small optimization: if delta_minus and delta_plus are the same just reuse + // one of the two bignums. + if (Bignum::Equal(*delta_minus, *delta_plus)) { + delta_plus = delta_minus; + } + *length = 0; + while (true) { + uint16_t digit; + digit = numerator->DivideModuloIntBignum(*denominator); + ASSERT(digit <= 9); // digit is a uint16_t and therefore always positive. + // digit = numerator / denominator (integer division). + // numerator = numerator % denominator. + buffer[(*length)++] = digit + '0'; + + // Can we stop already? + // If the remainder of the division is less than the distance to the lower + // boundary we can stop. In this case we simply round down (discarding the + // remainder). + // Similarly we test if we can round up (using the upper boundary). + bool in_delta_room_minus; + bool in_delta_room_plus; + if (is_even) { + in_delta_room_minus = Bignum::LessEqual(*numerator, *delta_minus); + } else { + in_delta_room_minus = Bignum::Less(*numerator, *delta_minus); + } + if (is_even) { + in_delta_room_plus = + Bignum::PlusCompare(*numerator, *delta_plus, *denominator) >= 0; + } else { + in_delta_room_plus = + Bignum::PlusCompare(*numerator, *delta_plus, *denominator) > 0; + } + if (!in_delta_room_minus && !in_delta_room_plus) { + // Prepare for next iteration. + numerator->Times10(); + delta_minus->Times10(); + // We optimized delta_plus to be equal to delta_minus (if they share the + // same value). So don't multiply delta_plus if they point to the same + // object. + if (delta_minus != delta_plus) { + delta_plus->Times10(); + } + } else if (in_delta_room_minus && in_delta_room_plus) { + // Let's see if 2*numerator < denominator. + // If yes, then the next digit would be < 5 and we can round down. + int compare = Bignum::PlusCompare(*numerator, *numerator, *denominator); + if (compare < 0) { + // Remaining digits are less than .5. -> Round down (== do nothing). + } else if (compare > 0) { + // Remaining digits are more than .5 of denominator. -> Round up. + // Note that the last digit could not be a '9' as otherwise the whole + // loop would have stopped earlier. + // We still have an assert here in case the preconditions were not + // satisfied. + ASSERT(buffer[(*length) - 1] != '9'); + buffer[(*length) - 1]++; + } else { + // Halfway case. + // TODO(floitsch): need a way to solve half-way cases. + // For now let's round towards even (since this is what Gay seems to + // do). + + if ((buffer[(*length) - 1] - '0') % 2 == 0) { + // Round down => Do nothing. + } else { + ASSERT(buffer[(*length) - 1] != '9'); + buffer[(*length) - 1]++; + } + } + return; + } else if (in_delta_room_minus) { + // Round down (== do nothing). + return; + } else { // in_delta_room_plus + // Round up. + // Note again that the last digit could not be '9' since this would have + // stopped the loop earlier. + // We still have an ASSERT here, in case the preconditions were not + // satisfied. + ASSERT(buffer[(*length) -1] != '9'); + buffer[(*length) - 1]++; + return; + } + } +} + + +// Let v = numerator / denominator < 10. +// Then we generate 'count' digits of d = x.xxxxx... (without the decimal point) +// from left to right. Once 'count' digits have been produced we decide wether +// to round up or down. Remainders of exactly .5 round upwards. Numbers such +// as 9.999999 propagate a carry all the way, and change the +// exponent (decimal_point), when rounding upwards. +static void GenerateCountedDigits(int count, int* decimal_point, + Bignum* numerator, Bignum* denominator, + Vector<char>(buffer), int* length) { + ASSERT(count >= 0); + for (int i = 0; i < count - 1; ++i) { + uint16_t digit; + digit = numerator->DivideModuloIntBignum(*denominator); + ASSERT(digit <= 9); // digit is a uint16_t and therefore always positive. + // digit = numerator / denominator (integer division). + // numerator = numerator % denominator. + buffer[i] = digit + '0'; + // Prepare for next iteration. + numerator->Times10(); + } + // Generate the last digit. + uint16_t digit; + digit = numerator->DivideModuloIntBignum(*denominator); + if (Bignum::PlusCompare(*numerator, *numerator, *denominator) >= 0) { + digit++; + } + buffer[count - 1] = digit + '0'; + // Correct bad digits (in case we had a sequence of '9's). Propagate the + // carry until we hat a non-'9' or til we reach the first digit. + for (int i = count - 1; i > 0; --i) { + if (buffer[i] != '0' + 10) break; + buffer[i] = '0'; + buffer[i - 1]++; + } + if (buffer[0] == '0' + 10) { + // Propagate a carry past the top place. + buffer[0] = '1'; + (*decimal_point)++; + } + *length = count; +} + + +// Generates 'requested_digits' after the decimal point. It might omit +// trailing '0's. If the input number is too small then no digits at all are +// generated (ex.: 2 fixed digits for 0.00001). +// +// Input verifies: 1 <= (numerator + delta) / denominator < 10. +static void BignumToFixed(int requested_digits, int* decimal_point, + Bignum* numerator, Bignum* denominator, + Vector<char>(buffer), int* length) { + // Note that we have to look at more than just the requested_digits, since + // a number could be rounded up. Example: v=0.5 with requested_digits=0. + // Even though the power of v equals 0 we can't just stop here. + if (-(*decimal_point) > requested_digits) { + // The number is definitively too small. + // Ex: 0.001 with requested_digits == 1. + // Set decimal-point to -requested_digits. This is what Gay does. + // Note that it should not have any effect anyways since the string is + // empty. + *decimal_point = -requested_digits; + *length = 0; + return; + } else if (-(*decimal_point) == requested_digits) { + // We only need to verify if the number rounds down or up. + // Ex: 0.04 and 0.06 with requested_digits == 1. + ASSERT(*decimal_point == -requested_digits); + // Initially the fraction lies in range (1, 10]. Multiply the denominator + // by 10 so that we can compare more easily. + denominator->Times10(); + if (Bignum::PlusCompare(*numerator, *numerator, *denominator) >= 0) { + // If the fraction is >= 0.5 then we have to include the rounded + // digit. + buffer[0] = '1'; + *length = 1; + (*decimal_point)++; + } else { + // Note that we caught most of similar cases earlier. + *length = 0; + } + return; + } else { + // The requested digits correspond to the digits after the point. + // The variable 'needed_digits' includes the digits before the point. + int needed_digits = (*decimal_point) + requested_digits; + GenerateCountedDigits(needed_digits, decimal_point, + numerator, denominator, + buffer, length); + } +} + + +// Returns an estimation of k such that 10^(k-1) <= v < 10^k where +// v = f * 2^exponent and 2^52 <= f < 2^53. +// v is hence a normalized double with the given exponent. The output is an +// approximation for the exponent of the decimal approimation .digits * 10^k. +// +// The result might undershoot by 1 in which case 10^k <= v < 10^k+1. +// Note: this property holds for v's upper boundary m+ too. +// 10^k <= m+ < 10^k+1. +// (see explanation below). +// +// Examples: +// EstimatePower(0) => 16 +// EstimatePower(-52) => 0 +// +// Note: e >= 0 => EstimatedPower(e) > 0. No similar claim can be made for e<0. +static int EstimatePower(int exponent) { + // This function estimates log10 of v where v = f*2^e (with e == exponent). + // Note that 10^floor(log10(v)) <= v, but v <= 10^ceil(log10(v)). + // Note that f is bounded by its container size. Let p = 53 (the double's + // significand size). Then 2^(p-1) <= f < 2^p. + // + // Given that log10(v) == log2(v)/log2(10) and e+(len(f)-1) is quite close + // to log2(v) the function is simplified to (e+(len(f)-1)/log2(10)). + // The computed number undershoots by less than 0.631 (when we compute log3 + // and not log10). + // + // Optimization: since we only need an approximated result this computation + // can be performed on 64 bit integers. On x86/x64 architecture the speedup is + // not really measurable, though. + // + // Since we want to avoid overshooting we decrement by 1e10 so that + // floating-point imprecisions don't affect us. + // + // Explanation for v's boundary m+: the computation takes advantage of + // the fact that 2^(p-1) <= f < 2^p. Boundaries still satisfy this requirement + // (even for denormals where the delta can be much more important). + + const double k1Log10 = 0.30102999566398114; // 1/lg(10) + + // For doubles len(f) == 53 (don't forget the hidden bit). + const int kSignificandSize = 53; + double estimate = ceil((exponent + kSignificandSize - 1) * k1Log10 - 1e-10); + return static_cast<int>(estimate); +} + + +// See comments for InitialScaledStartValues. +static void InitialScaledStartValuesPositiveExponent( + double v, int estimated_power, bool need_boundary_deltas, + Bignum* numerator, Bignum* denominator, + Bignum* delta_minus, Bignum* delta_plus) { + // A positive exponent implies a positive power. + ASSERT(estimated_power >= 0); + // Since the estimated_power is positive we simply multiply the denominator + // by 10^estimated_power. + + // numerator = v. + numerator->AssignUInt64(Double(v).Significand()); + numerator->ShiftLeft(Double(v).Exponent()); + // denominator = 10^estimated_power. + denominator->AssignPowerUInt16(10, estimated_power); + + if (need_boundary_deltas) { + // Introduce a common denominator so that the deltas to the boundaries are + // integers. + denominator->ShiftLeft(1); + numerator->ShiftLeft(1); + // Let v = f * 2^e, then m+ - v = 1/2 * 2^e; With the common + // denominator (of 2) delta_plus equals 2^e. + delta_plus->AssignUInt16(1); + delta_plus->ShiftLeft(Double(v).Exponent()); + // Same for delta_minus (with adjustments below if f == 2^p-1). + delta_minus->AssignUInt16(1); + delta_minus->ShiftLeft(Double(v).Exponent()); + + // If the significand (without the hidden bit) is 0, then the lower + // boundary is closer than just half a ulp (unit in the last place). + // There is only one exception: if the next lower number is a denormal then + // the distance is 1 ulp. This cannot be the case for exponent >= 0 (but we + // have to test it in the other function where exponent < 0). + uint64_t v_bits = Double(v).AsUint64(); + if ((v_bits & Double::kSignificandMask) == 0) { + // The lower boundary is closer at half the distance of "normal" numbers. + // Increase the common denominator and adapt all but the delta_minus. + denominator->ShiftLeft(1); // *2 + numerator->ShiftLeft(1); // *2 + delta_plus->ShiftLeft(1); // *2 + } + } +} + + +// See comments for InitialScaledStartValues +static void InitialScaledStartValuesNegativeExponentPositivePower( + double v, int estimated_power, bool need_boundary_deltas, + Bignum* numerator, Bignum* denominator, + Bignum* delta_minus, Bignum* delta_plus) { + uint64_t significand = Double(v).Significand(); + int exponent = Double(v).Exponent(); + // v = f * 2^e with e < 0, and with estimated_power >= 0. + // This means that e is close to 0 (have a look at how estimated_power is + // computed). + + // numerator = significand + // since v = significand * 2^exponent this is equivalent to + // numerator = v * / 2^-exponent + numerator->AssignUInt64(significand); + // denominator = 10^estimated_power * 2^-exponent (with exponent < 0) + denominator->AssignPowerUInt16(10, estimated_power); + denominator->ShiftLeft(-exponent); + + if (need_boundary_deltas) { + // Introduce a common denominator so that the deltas to the boundaries are + // integers. + denominator->ShiftLeft(1); + numerator->ShiftLeft(1); + // Let v = f * 2^e, then m+ - v = 1/2 * 2^e; With the common + // denominator (of 2) delta_plus equals 2^e. + // Given that the denominator already includes v's exponent the distance + // to the boundaries is simply 1. + delta_plus->AssignUInt16(1); + // Same for delta_minus (with adjustments below if f == 2^p-1). + delta_minus->AssignUInt16(1); + + // If the significand (without the hidden bit) is 0, then the lower + // boundary is closer than just one ulp (unit in the last place). + // There is only one exception: if the next lower number is a denormal + // then the distance is 1 ulp. Since the exponent is close to zero + // (otherwise estimated_power would have been negative) this cannot happen + // here either. + uint64_t v_bits = Double(v).AsUint64(); + if ((v_bits & Double::kSignificandMask) == 0) { + // The lower boundary is closer at half the distance of "normal" numbers. + // Increase the denominator and adapt all but the delta_minus. + denominator->ShiftLeft(1); // *2 + numerator->ShiftLeft(1); // *2 + delta_plus->ShiftLeft(1); // *2 + } + } +} + + +// See comments for InitialScaledStartValues +static void InitialScaledStartValuesNegativeExponentNegativePower( + double v, int estimated_power, bool need_boundary_deltas, + Bignum* numerator, Bignum* denominator, + Bignum* delta_minus, Bignum* delta_plus) { + const uint64_t kMinimalNormalizedExponent = + V8_2PART_UINT64_C(0x00100000, 00000000); + uint64_t significand = Double(v).Significand(); + int exponent = Double(v).Exponent(); + // Instead of multiplying the denominator with 10^estimated_power we + // multiply all values (numerator and deltas) by 10^-estimated_power. + + // Use numerator as temporary container for power_ten. + Bignum* power_ten = numerator; + power_ten->AssignPowerUInt16(10, -estimated_power); + + if (need_boundary_deltas) { + // Since power_ten == numerator we must make a copy of 10^estimated_power + // before we complete the computation of the numerator. + // delta_plus = delta_minus = 10^estimated_power + delta_plus->AssignBignum(*power_ten); + delta_minus->AssignBignum(*power_ten); + } + + // numerator = significand * 2 * 10^-estimated_power + // since v = significand * 2^exponent this is equivalent to + // numerator = v * 10^-estimated_power * 2 * 2^-exponent. + // Remember: numerator has been abused as power_ten. So no need to assign it + // to itself. + ASSERT(numerator == power_ten); + numerator->MultiplyByUInt64(significand); + + // denominator = 2 * 2^-exponent with exponent < 0. + denominator->AssignUInt16(1); + denominator->ShiftLeft(-exponent); + + if (need_boundary_deltas) { + // Introduce a common denominator so that the deltas to the boundaries are + // integers. + numerator->ShiftLeft(1); + denominator->ShiftLeft(1); + // With this shift the boundaries have their correct value, since + // delta_plus = 10^-estimated_power, and + // delta_minus = 10^-estimated_power. + // These assignments have been done earlier. + + // The special case where the lower boundary is twice as close. + // This time we have to look out for the exception too. + uint64_t v_bits = Double(v).AsUint64(); + if ((v_bits & Double::kSignificandMask) == 0 && + // The only exception where a significand == 0 has its boundaries at + // "normal" distances: + (v_bits & Double::kExponentMask) != kMinimalNormalizedExponent) { + numerator->ShiftLeft(1); // *2 + denominator->ShiftLeft(1); // *2 + delta_plus->ShiftLeft(1); // *2 + } + } +} + + +// Let v = significand * 2^exponent. +// Computes v / 10^estimated_power exactly, as a ratio of two bignums, numerator +// and denominator. The functions GenerateShortestDigits and +// GenerateCountedDigits will then convert this ratio to its decimal +// representation d, with the required accuracy. +// Then d * 10^estimated_power is the representation of v. +// (Note: the fraction and the estimated_power might get adjusted before +// generating the decimal representation.) +// +// The initial start values consist of: +// - a scaled numerator: s.t. numerator/denominator == v / 10^estimated_power. +// - a scaled (common) denominator. +// optionally (used by GenerateShortestDigits to decide if it has the shortest +// decimal converting back to v): +// - v - m-: the distance to the lower boundary. +// - m+ - v: the distance to the upper boundary. +// +// v, m+, m-, and therefore v - m- and m+ - v all share the same denominator. +// +// Let ep == estimated_power, then the returned values will satisfy: +// v / 10^ep = numerator / denominator. +// v's boundarys m- and m+: +// m- / 10^ep == v / 10^ep - delta_minus / denominator +// m+ / 10^ep == v / 10^ep + delta_plus / denominator +// Or in other words: +// m- == v - delta_minus * 10^ep / denominator; +// m+ == v + delta_plus * 10^ep / denominator; +// +// Since 10^(k-1) <= v < 10^k (with k == estimated_power) +// or 10^k <= v < 10^(k+1) +// we then have 0.1 <= numerator/denominator < 1 +// or 1 <= numerator/denominator < 10 +// +// It is then easy to kickstart the digit-generation routine. +// +// The boundary-deltas are only filled if need_boundary_deltas is set. +static void InitialScaledStartValues(double v, + int estimated_power, + bool need_boundary_deltas, + Bignum* numerator, + Bignum* denominator, + Bignum* delta_minus, + Bignum* delta_plus) { + if (Double(v).Exponent() >= 0) { + InitialScaledStartValuesPositiveExponent( + v, estimated_power, need_boundary_deltas, + numerator, denominator, delta_minus, delta_plus); + } else if (estimated_power >= 0) { + InitialScaledStartValuesNegativeExponentPositivePower( + v, estimated_power, need_boundary_deltas, + numerator, denominator, delta_minus, delta_plus); + } else { + InitialScaledStartValuesNegativeExponentNegativePower( + v, estimated_power, need_boundary_deltas, + numerator, denominator, delta_minus, delta_plus); + } +} + + +// This routine multiplies numerator/denominator so that its values lies in the +// range 1-10. That is after a call to this function we have: +// 1 <= (numerator + delta_plus) /denominator < 10. +// Let numerator the input before modification and numerator' the argument +// after modification, then the output-parameter decimal_point is such that +// numerator / denominator * 10^estimated_power == +// numerator' / denominator' * 10^(decimal_point - 1) +// In some cases estimated_power was too low, and this is already the case. We +// then simply adjust the power so that 10^(k-1) <= v < 10^k (with k == +// estimated_power) but do not touch the numerator or denominator. +// Otherwise the routine multiplies the numerator and the deltas by 10. +static void FixupMultiply10(int estimated_power, bool is_even, + int* decimal_point, + Bignum* numerator, Bignum* denominator, + Bignum* delta_minus, Bignum* delta_plus) { + bool in_range; + if (is_even) { + // For IEEE doubles half-way cases (in decimal system numbers ending with 5) + // are rounded to the closest floating-point number with even significand. + in_range = Bignum::PlusCompare(*numerator, *delta_plus, *denominator) >= 0; + } else { + in_range = Bignum::PlusCompare(*numerator, *delta_plus, *denominator) > 0; + } + if (in_range) { + // Since numerator + delta_plus >= denominator we already have + // 1 <= numerator/denominator < 10. Simply update the estimated_power. + *decimal_point = estimated_power + 1; + } else { + *decimal_point = estimated_power; + numerator->Times10(); + if (Bignum::Equal(*delta_minus, *delta_plus)) { + delta_minus->Times10(); + delta_plus->AssignBignum(*delta_minus); + } else { + delta_minus->Times10(); + delta_plus->Times10(); + } + } +} + +} } // namespace v8::internal diff --git a/deps/v8/src/bignum-dtoa.h b/deps/v8/src/bignum-dtoa.h new file mode 100644 index 0000000000..ea1acbbfc8 --- /dev/null +++ b/deps/v8/src/bignum-dtoa.h @@ -0,0 +1,81 @@ +// Copyright 2010 the V8 project authors. All rights reserved. +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following +// disclaimer in the documentation and/or other materials provided +// with the distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +#ifndef V8_BIGNUM_DTOA_H_ +#define V8_BIGNUM_DTOA_H_ + +namespace v8 { +namespace internal { + +enum BignumDtoaMode { + // Return the shortest correct representation. + // For example the output of 0.299999999999999988897 is (the less accurate but + // correct) 0.3. + BIGNUM_DTOA_SHORTEST, + // Return a fixed number of digits after the decimal point. + // For instance fixed(0.1, 4) becomes 0.1000 + // If the input number is big, the output will be big. + BIGNUM_DTOA_FIXED, + // Return a fixed number of digits, no matter what the exponent is. + BIGNUM_DTOA_PRECISION +}; + +// Converts the given double 'v' to ascii. +// The result should be interpreted as buffer * 10^(point-length). +// The buffer will be null-terminated. +// +// The input v must be > 0 and different from NaN, and Infinity. +// +// The output depends on the given mode: +// - SHORTEST: produce the least amount of digits for which the internal +// identity requirement is still satisfied. If the digits are printed +// (together with the correct exponent) then reading this number will give +// 'v' again. The buffer will choose the representation that is closest to +// 'v'. If there are two at the same distance, than the number is round up. +// In this mode the 'requested_digits' parameter is ignored. +// - FIXED: produces digits necessary to print a given number with +// 'requested_digits' digits after the decimal point. The produced digits +// might be too short in which case the caller has to fill the gaps with '0's. +// Example: toFixed(0.001, 5) is allowed to return buffer="1", point=-2. +// Halfway cases are rounded up. The call toFixed(0.15, 2) thus returns +// buffer="2", point=0. +// Note: the length of the returned buffer has no meaning wrt the significance +// of its digits. That is, just because it contains '0's does not mean that +// any other digit would not satisfy the internal identity requirement. +// - PRECISION: produces 'requested_digits' where the first digit is not '0'. +// Even though the length of produced digits usually equals +// 'requested_digits', the function is allowed to return fewer digits, in +// which case the caller has to fill the missing digits with '0's. +// Halfway cases are again rounded up. +// 'BignumDtoa' expects the given buffer to be big enough to hold all digits +// and a terminating null-character. +void BignumDtoa(double v, BignumDtoaMode mode, int requested_digits, + Vector<char> buffer, int* length, int* point); + +} } // namespace v8::internal + +#endif // V8_BIGNUM_DTOA_H_ diff --git a/deps/v8/src/bignum.cc b/deps/v8/src/bignum.cc new file mode 100644 index 0000000000..dd1537a25a --- /dev/null +++ b/deps/v8/src/bignum.cc @@ -0,0 +1,767 @@ +// Copyright 2010 the V8 project authors. All rights reserved. +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following +// disclaimer in the documentation and/or other materials provided +// with the distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +#include "v8.h" + +#include "bignum.h" +#include "utils.h" + +namespace v8 { +namespace internal { + +Bignum::Bignum() + : bigits_(bigits_buffer_, kBigitCapacity), used_digits_(0), exponent_(0) { + for (int i = 0; i < kBigitCapacity; ++i) { + bigits_[i] = 0; + } +} + + +template<typename S> +static int BitSize(S value) { + return 8 * sizeof(value); +} + +// Guaranteed to lie in one Bigit. +void Bignum::AssignUInt16(uint16_t value) { + ASSERT(kBigitSize >= BitSize(value)); + Zero(); + if (value == 0) return; + + EnsureCapacity(1); + bigits_[0] = value; + used_digits_ = 1; +} + + +void Bignum::AssignUInt64(uint64_t value) { + const int kUInt64Size = 64; + + Zero(); + if (value == 0) return; + + int needed_bigits = kUInt64Size / kBigitSize + 1; + EnsureCapacity(needed_bigits); + for (int i = 0; i < needed_bigits; ++i) { + bigits_[i] = value & kBigitMask; + value = value >> kBigitSize; + } + used_digits_ = needed_bigits; + Clamp(); +} + + +void Bignum::AssignBignum(const Bignum& other) { + exponent_ = other.exponent_; + for (int i = 0; i < other.used_digits_; ++i) { + bigits_[i] = other.bigits_[i]; + } + // Clear the excess digits (if there were any). + for (int i = other.used_digits_; i < used_digits_; ++i) { + bigits_[i] = 0; + } + used_digits_ = other.used_digits_; +} + + +static uint64_t ReadUInt64(Vector<const char> buffer, + int from, + int digits_to_read) { + uint64_t result = 0; + for (int i = from; i < from + digits_to_read; ++i) { + int digit = buffer[i] - '0'; + ASSERT(0 <= digit && digit <= 9); + result = result * 10 + digit; + } + return result; +} + + +void Bignum::AssignDecimalString(Vector<const char> value) { + // 2^64 = 18446744073709551616 > 10^19 + const int kMaxUint64DecimalDigits = 19; + Zero(); + int length = value.length(); + int pos = 0; + // Let's just say that each digit needs 4 bits. + while (length >= kMaxUint64DecimalDigits) { + uint64_t digits = ReadUInt64(value, pos, kMaxUint64DecimalDigits); + pos += kMaxUint64DecimalDigits; + length -= kMaxUint64DecimalDigits; + MultiplyByPowerOfTen(kMaxUint64DecimalDigits); + AddUInt64(digits); + } + uint64_t digits = ReadUInt64(value, pos, length); + MultiplyByPowerOfTen(length); + AddUInt64(digits); + Clamp(); +} + + +static int HexCharValue(char c) { + if ('0' <= c && c <= '9') return c - '0'; + if ('a' <= c && c <= 'f') return 10 + c - 'a'; + if ('A' <= c && c <= 'F') return 10 + c - 'A'; + UNREACHABLE(); + return 0; // To make compiler happy. +} + + +void Bignum::AssignHexString(Vector<const char> value) { + Zero(); + int length = value.length(); + + int needed_bigits = length * 4 / kBigitSize + 1; + EnsureCapacity(needed_bigits); + int string_index = length - 1; + for (int i = 0; i < needed_bigits - 1; ++i) { + // These bigits are guaranteed to be "full". + Chunk current_bigit = 0; + for (int j = 0; j < kBigitSize / 4; j++) { + current_bigit += HexCharValue(value[string_index--]) << (j * 4); + } + bigits_[i] = current_bigit; + } + used_digits_ = needed_bigits - 1; + + Chunk most_significant_bigit = 0; // Could be = 0; + for (int j = 0; j <= string_index; ++j) { + most_significant_bigit <<= 4; + most_significant_bigit += HexCharValue(value[j]); + } + if (most_significant_bigit != 0) { + bigits_[used_digits_] = most_significant_bigit; + used_digits_++; + } + Clamp(); +} + + +void Bignum::AddUInt64(uint64_t operand) { + if (operand == 0) return; + Bignum other; + other.AssignUInt64(operand); + AddBignum(other); +} + + +void Bignum::AddBignum(const Bignum& other) { + ASSERT(IsClamped()); + ASSERT(other.IsClamped()); + + // If this has a greater exponent than other append zero-bigits to this. + // After this call exponent_ <= other.exponent_. + Align(other); + + // There are two possibilities: + // aaaaaaaaaaa 0000 (where the 0s represent a's exponent) + // bbbbb 00000000 + // ---------------- + // ccccccccccc 0000 + // or + // aaaaaaaaaa 0000 + // bbbbbbbbb 0000000 + // ----------------- + // cccccccccccc 0000 + // In both cases we might need a carry bigit. + + EnsureCapacity(1 + Max(BigitLength(), other.BigitLength()) - exponent_); + Chunk carry = 0; + int bigit_pos = other.exponent_ - exponent_; + ASSERT(bigit_pos >= 0); + for (int i = 0; i < other.used_digits_; ++i) { + Chunk sum = bigits_[bigit_pos] + other.bigits_[i] + carry; + bigits_[bigit_pos] = sum & kBigitMask; + carry = sum >> kBigitSize; + bigit_pos++; + } + + while (carry != 0) { + Chunk sum = bigits_[bigit_pos] + carry; + bigits_[bigit_pos] = sum & kBigitMask; + carry = sum >> kBigitSize; + bigit_pos++; + } + used_digits_ = Max(bigit_pos, used_digits_); + ASSERT(IsClamped()); +} + + +void Bignum::SubtractBignum(const Bignum& other) { + ASSERT(IsClamped()); + ASSERT(other.IsClamped()); + // We require this to be bigger than other. + ASSERT(LessEqual(other, *this)); + + Align(other); + + int offset = other.exponent_ - exponent_; + Chunk borrow = 0; + int i; + for (i = 0; i < other.used_digits_; ++i) { + ASSERT((borrow == 0) || (borrow == 1)); + Chunk difference = bigits_[i + offset] - other.bigits_[i] - borrow; + bigits_[i + offset] = difference & kBigitMask; + borrow = difference >> (kChunkSize - 1); + } + while (borrow != 0) { + Chunk difference = bigits_[i + offset] - borrow; + bigits_[i + offset] = difference & kBigitMask; + borrow = difference >> (kChunkSize - 1); + ++i; + } + Clamp(); +} + + +void Bignum::ShiftLeft(int shift_amount) { + if (used_digits_ == 0) return; + exponent_ += shift_amount / kBigitSize; + int local_shift = shift_amount % kBigitSize; + EnsureCapacity(used_digits_ + 1); + BigitsShiftLeft(local_shift); +} + + +void Bignum::MultiplyByUInt32(uint32_t factor) { + if (factor == 1) return; + if (factor == 0) { + Zero(); + return; + } + if (used_digits_ == 0) return; + + // The product of a bigit with the factor is of size kBigitSize + 32. + // Assert that this number + 1 (for the carry) fits into double chunk. + ASSERT(kDoubleChunkSize >= kBigitSize + 32 + 1); + DoubleChunk carry = 0; + for (int i = 0; i < used_digits_; ++i) { + DoubleChunk product = static_cast<DoubleChunk>(factor) * bigits_[i] + carry; + bigits_[i] = static_cast<Chunk>(product & kBigitMask); + carry = (product >> kBigitSize); + } + while (carry != 0) { + EnsureCapacity(used_digits_ + 1); + bigits_[used_digits_] = carry & kBigitMask; + used_digits_++; + carry >>= kBigitSize; + } +} + + +void Bignum::MultiplyByUInt64(uint64_t factor) { + if (factor == 1) return; + if (factor == 0) { + Zero(); + return; + } + ASSERT(kBigitSize < 32); + uint64_t carry = 0; + uint64_t low = factor & 0xFFFFFFFF; + uint64_t high = factor >> 32; + for (int i = 0; i < used_digits_; ++i) { + uint64_t product_low = low * bigits_[i]; + uint64_t product_high = high * bigits_[i]; + uint64_t tmp = (carry & kBigitMask) + product_low; + bigits_[i] = tmp & kBigitMask; + carry = (carry >> kBigitSize) + (tmp >> kBigitSize) + + (product_high << (32 - kBigitSize)); + } + while (carry != 0) { + EnsureCapacity(used_digits_ + 1); + bigits_[used_digits_] = carry & kBigitMask; + used_digits_++; + carry >>= kBigitSize; + } +} + + +void Bignum::MultiplyByPowerOfTen(int exponent) { + const uint64_t kFive27 = V8_2PART_UINT64_C(0x6765c793, fa10079d); + const uint16_t kFive1 = 5; + const uint16_t kFive2 = kFive1 * 5; + const uint16_t kFive3 = kFive2 * 5; + const uint16_t kFive4 = kFive3 * 5; + const uint16_t kFive5 = kFive4 * 5; + const uint16_t kFive6 = kFive5 * 5; + const uint32_t kFive7 = kFive6 * 5; + const uint32_t kFive8 = kFive7 * 5; + const uint32_t kFive9 = kFive8 * 5; + const uint32_t kFive10 = kFive9 * 5; + const uint32_t kFive11 = kFive10 * 5; + const uint32_t kFive12 = kFive11 * 5; + const uint32_t kFive13 = kFive12 * 5; + const uint32_t kFive1_to_12[] = + { kFive1, kFive2, kFive3, kFive4, kFive5, kFive6, + kFive7, kFive8, kFive9, kFive10, kFive11, kFive12 }; + + ASSERT(exponent >= 0); + if (exponent == 0) return; + if (used_digits_ == 0) return; + + // We shift by exponent at the end just before returning. + int remaining_exponent = exponent; + while (remaining_exponent >= 27) { + MultiplyByUInt64(kFive27); + remaining_exponent -= 27; + } + while (remaining_exponent >= 13) { + MultiplyByUInt32(kFive13); + remaining_exponent -= 13; + } + if (remaining_exponent > 0) { + MultiplyByUInt32(kFive1_to_12[remaining_exponent - 1]); + } + ShiftLeft(exponent); +} + + +void Bignum::Square() { + ASSERT(IsClamped()); + int product_length = 2 * used_digits_; + EnsureCapacity(product_length); + + // Comba multiplication: compute each column separately. + // Example: r = a2a1a0 * b2b1b0. + // r = 1 * a0b0 + + // 10 * (a1b0 + a0b1) + + // 100 * (a2b0 + a1b1 + a0b2) + + // 1000 * (a2b1 + a1b2) + + // 10000 * a2b2 + // + // In the worst case we have to accumulate nb-digits products of digit*digit. + // + // Assert that the additional number of bits in a DoubleChunk are enough to + // sum up used_digits of Bigit*Bigit. + if ((1 << (2 * (kChunkSize - kBigitSize))) <= used_digits_) { + UNIMPLEMENTED(); + } + DoubleChunk accumulator = 0; + // First shift the digits so we don't overwrite them. + int copy_offset = used_digits_; + for (int i = 0; i < used_digits_; ++i) { + bigits_[copy_offset + i] = bigits_[i]; + } + // We have two loops to avoid some 'if's in the loop. + for (int i = 0; i < used_digits_; ++i) { + // Process temporary digit i with power i. + // The sum of the two indices must be equal to i. + int bigit_index1 = i; + int bigit_index2 = 0; + // Sum all of the sub-products. + while (bigit_index1 >= 0) { + Chunk chunk1 = bigits_[copy_offset + bigit_index1]; + Chunk chunk2 = bigits_[copy_offset + bigit_index2]; + accumulator += static_cast<DoubleChunk>(chunk1) * chunk2; + bigit_index1--; + bigit_index2++; + } + bigits_[i] = static_cast<Chunk>(accumulator) & kBigitMask; + accumulator >>= kBigitSize; + } + for (int i = used_digits_; i < product_length; ++i) { + int bigit_index1 = used_digits_ - 1; + int bigit_index2 = i - bigit_index1; + // Invariant: sum of both indices is again equal to i. + // Inner loop runs 0 times on last iteration, emptying accumulator. + while (bigit_index2 < used_digits_) { + Chunk chunk1 = bigits_[copy_offset + bigit_index1]; + Chunk chunk2 = bigits_[copy_offset + bigit_index2]; + accumulator += static_cast<DoubleChunk>(chunk1) * chunk2; + bigit_index1--; + bigit_index2++; + } + // The overwritten bigits_[i] will never be read in further loop iterations, + // because bigit_index1 and bigit_index2 are always greater + // than i - used_digits_. + bigits_[i] = static_cast<Chunk>(accumulator) & kBigitMask; + accumulator >>= kBigitSize; + } + // Since the result was guaranteed to lie inside the number the + // accumulator must be 0 now. + ASSERT(accumulator == 0); + + // Don't forget to update the used_digits and the exponent. + used_digits_ = product_length; + exponent_ *= 2; + Clamp(); +} + + +void Bignum::AssignPowerUInt16(uint16_t base, int power_exponent) { + ASSERT(base != 0); + ASSERT(power_exponent >= 0); + if (power_exponent == 0) { + AssignUInt16(1); + return; + } + Zero(); + int shifts = 0; + // We expect base to be in range 2-32, and most often to be 10. + // It does not make much sense to implement different algorithms for counting + // the bits. + while ((base & 1) == 0) { + base >>= 1; + shifts++; + } + int bit_size = 0; + int tmp_base = base; + while (tmp_base != 0) { + tmp_base >>= 1; + bit_size++; + } + int final_size = bit_size * power_exponent; + // 1 extra bigit for the shifting, and one for rounded final_size. + EnsureCapacity(final_size / kBigitSize + 2); + + // Left to Right exponentiation. + int mask = 1; + while (power_exponent >= mask) mask <<= 1; + + // The mask is now pointing to the bit above the most significant 1-bit of + // power_exponent. + // Get rid of first 1-bit; + mask >>= 2; + uint64_t this_value = base; + + bool delayed_multipliciation = false; + const uint64_t max_32bits = 0xFFFFFFFF; + while (mask != 0 && this_value <= max_32bits) { + this_value = this_value * this_value; + // Verify that there is enough space in this_value to perform the + // multiplication. The first bit_size bits must be 0. + if ((power_exponent & mask) != 0) { + uint64_t base_bits_mask = + ~((static_cast<uint64_t>(1) << (64 - bit_size)) - 1); + bool high_bits_zero = (this_value & base_bits_mask) == 0; + if (high_bits_zero) { + this_value *= base; + } else { + delayed_multipliciation = true; + } + } + mask >>= 1; + } + AssignUInt64(this_value); + if (delayed_multipliciation) { + MultiplyByUInt32(base); + } + + // Now do the same thing as a bignum. + while (mask != 0) { + Square(); + if ((power_exponent & mask) != 0) { + MultiplyByUInt32(base); + } + mask >>= 1; + } + + // And finally add the saved shifts. + ShiftLeft(shifts * power_exponent); +} + + +// Precondition: this/other < 16bit. +uint16_t Bignum::DivideModuloIntBignum(const Bignum& other) { + ASSERT(IsClamped()); + ASSERT(other.IsClamped()); + ASSERT(other.used_digits_ > 0); + + // Easy case: if we have less digits than the divisor than the result is 0. + // Note: this handles the case where this == 0, too. + if (BigitLength() < other.BigitLength()) { + return 0; + } + + Align(other); + + uint16_t result = 0; + + // Start by removing multiples of 'other' until both numbers have the same + // number of digits. + while (BigitLength() > other.BigitLength()) { + // This naive approach is extremely inefficient if the this divided other + // might be big. This function is implemented for doubleToString where + // the result should be small (less than 10). + ASSERT(other.bigits_[other.used_digits_ - 1] >= ((1 << kBigitSize) / 16)); + // Remove the multiples of the first digit. + // Example this = 23 and other equals 9. -> Remove 2 multiples. + result += bigits_[used_digits_ - 1]; + SubtractTimes(other, bigits_[used_digits_ - 1]); + } + + ASSERT(BigitLength() == other.BigitLength()); + + // Both bignums are at the same length now. + // Since other has more than 0 digits we know that the access to + // bigits_[used_digits_ - 1] is safe. + Chunk this_bigit = bigits_[used_digits_ - 1]; + Chunk other_bigit = other.bigits_[other.used_digits_ - 1]; + + if (other.used_digits_ == 1) { + // Shortcut for easy (and common) case. + int quotient = this_bigit / other_bigit; + bigits_[used_digits_ - 1] = this_bigit - other_bigit * quotient; + result += quotient; + Clamp(); + return result; + } + + int division_estimate = this_bigit / (other_bigit + 1); + result += division_estimate; + SubtractTimes(other, division_estimate); + + if (other_bigit * (division_estimate + 1) > this_bigit) { + // No need to even try to subtract. Even if other's remaining digits were 0 + // another subtraction would be too much. + return result; + } + + while (LessEqual(other, *this)) { + SubtractBignum(other); + result++; + } + return result; +} + + +template<typename S> +static int SizeInHexChars(S number) { + ASSERT(number > 0); + int result = 0; + while (number != 0) { + number >>= 4; + result++; + } + return result; +} + + +static char HexCharOfValue(int value) { + ASSERT(0 <= value && value <= 16); + if (value < 10) return value + '0'; + return value - 10 + 'A'; +} + + +bool Bignum::ToHexString(char* buffer, int buffer_size) const { + ASSERT(IsClamped()); + // Each bigit must be printable as separate hex-character. + ASSERT(kBigitSize % 4 == 0); + const int kHexCharsPerBigit = kBigitSize / 4; + + if (used_digits_ == 0) { + if (buffer_size < 2) return false; + buffer[0] = '0'; + buffer[1] = '\0'; + return true; + } + // We add 1 for the terminating '\0' character. + int needed_chars = (BigitLength() - 1) * kHexCharsPerBigit + + SizeInHexChars(bigits_[used_digits_ - 1]) + 1; + if (needed_chars > buffer_size) return false; + int string_index = needed_chars - 1; + buffer[string_index--] = '\0'; + for (int i = 0; i < exponent_; ++i) { + for (int j = 0; j < kHexCharsPerBigit; ++j) { + buffer[string_index--] = '0'; + } + } + for (int i = 0; i < used_digits_ - 1; ++i) { + Chunk current_bigit = bigits_[i]; + for (int j = 0; j < kHexCharsPerBigit; ++j) { + buffer[string_index--] = HexCharOfValue(current_bigit & 0xF); + current_bigit >>= 4; + } + } + // And finally the last bigit. + Chunk most_significant_bigit = bigits_[used_digits_ - 1]; + while (most_significant_bigit != 0) { + buffer[string_index--] = HexCharOfValue(most_significant_bigit & 0xF); + most_significant_bigit >>= 4; + } + return true; +} + + +Bignum::Chunk Bignum::BigitAt(int index) const { + if (index >= BigitLength()) return 0; + if (index < exponent_) return 0; + return bigits_[index - exponent_]; +} + + +int Bignum::Compare(const Bignum& a, const Bignum& b) { + ASSERT(a.IsClamped()); + ASSERT(b.IsClamped()); + int bigit_length_a = a.BigitLength(); + int bigit_length_b = b.BigitLength(); + if (bigit_length_a < bigit_length_b) return -1; + if (bigit_length_a > bigit_length_b) return +1; + for (int i = bigit_length_a - 1; i >= Min(a.exponent_, b.exponent_); --i) { + Chunk bigit_a = a.BigitAt(i); + Chunk bigit_b = b.BigitAt(i); + if (bigit_a < bigit_b) return -1; + if (bigit_a > bigit_b) return +1; + // Otherwise they are equal up to this digit. Try the next digit. + } + return 0; +} + + +int Bignum::PlusCompare(const Bignum& a, const Bignum& b, const Bignum& c) { + ASSERT(a.IsClamped()); + ASSERT(b.IsClamped()); + ASSERT(c.IsClamped()); + if (a.BigitLength() < b.BigitLength()) { + return PlusCompare(b, a, c); + } + if (a.BigitLength() + 1 < c.BigitLength()) return -1; + if (a.BigitLength() > c.BigitLength()) return +1; + // The exponent encodes 0-bigits. So if there are more 0-digits in 'a' than + // 'b' has digits, then the bigit-length of 'a'+'b' must be equal to the one + // of 'a'. + if (a.exponent_ >= b.BigitLength() && a.BigitLength() < c.BigitLength()) { + return -1; + } + + Chunk borrow = 0; + // Starting at min_exponent all digits are == 0. So no need to compare them. + int min_exponent = Min(Min(a.exponent_, b.exponent_), c.exponent_); + for (int i = c.BigitLength() - 1; i >= min_exponent; --i) { + Chunk chunk_a = a.BigitAt(i); + Chunk chunk_b = b.BigitAt(i); + Chunk chunk_c = c.BigitAt(i); + Chunk sum = chunk_a + chunk_b; + if (sum > chunk_c + borrow) { + return +1; + } else { + borrow = chunk_c + borrow - sum; + if (borrow > 1) return -1; + borrow <<= kBigitSize; + } + } + if (borrow == 0) return 0; + return -1; +} + + +void Bignum::Clamp() { + while (used_digits_ > 0 && bigits_[used_digits_ - 1] == 0) { + used_digits_--; + } + if (used_digits_ == 0) { + // Zero. + exponent_ = 0; + } +} + + +bool Bignum::IsClamped() const { + return used_digits_ == 0 || bigits_[used_digits_ - 1] != 0; +} + + +void Bignum::Zero() { + for (int i = 0; i < used_digits_; ++i) { + bigits_[i] = 0; + } + used_digits_ = 0; + exponent_ = 0; +} + + +void Bignum::Align(const Bignum& other) { + if (exponent_ > other.exponent_) { + // If "X" represents a "hidden" digit (by the exponent) then we are in the + // following case (a == this, b == other): + // a: aaaaaaXXXX or a: aaaaaXXX + // b: bbbbbbX b: bbbbbbbbXX + // We replace some of the hidden digits (X) of a with 0 digits. + // a: aaaaaa000X or a: aaaaa0XX + int zero_digits = exponent_ - other.exponent_; + EnsureCapacity(used_digits_ + zero_digits); + for (int i = used_digits_ - 1; i >= 0; --i) { + bigits_[i + zero_digits] = bigits_[i]; + } + for (int i = 0; i < zero_digits; ++i) { + bigits_[i] = 0; + } + used_digits_ += zero_digits; + exponent_ -= zero_digits; + ASSERT(used_digits_ >= 0); + ASSERT(exponent_ >= 0); + } +} + + +void Bignum::BigitsShiftLeft(int shift_amount) { + ASSERT(shift_amount < kBigitSize); + ASSERT(shift_amount >= 0); + Chunk carry = 0; + for (int i = 0; i < used_digits_; ++i) { + Chunk new_carry = bigits_[i] >> (kBigitSize - shift_amount); + bigits_[i] = ((bigits_[i] << shift_amount) + carry) & kBigitMask; + carry = new_carry; + } + if (carry != 0) { + bigits_[used_digits_] = carry; + used_digits_++; + } +} + + +void Bignum::SubtractTimes(const Bignum& other, int factor) { + ASSERT(exponent_ <= other.exponent_); + if (factor < 3) { + for (int i = 0; i < factor; ++i) { + SubtractBignum(other); + } + return; + } + Chunk borrow = 0; + int exponent_diff = other.exponent_ - exponent_; + for (int i = 0; i < other.used_digits_; ++i) { + DoubleChunk product = static_cast<DoubleChunk>(factor) * other.bigits_[i]; + DoubleChunk remove = borrow + product; + Chunk difference = bigits_[i + exponent_diff] - (remove & kBigitMask); + bigits_[i + exponent_diff] = difference & kBigitMask; + borrow = static_cast<Chunk>((difference >> (kChunkSize - 1)) + + (remove >> kBigitSize)); + } + for (int i = other.used_digits_ + exponent_diff; i < used_digits_; ++i) { + if (borrow == 0) return; + Chunk difference = bigits_[i] - borrow; + bigits_[i] = difference & kBigitMask; + borrow = difference >> (kChunkSize - 1); + ++i; + } + Clamp(); +} + + +} } // namespace v8::internal diff --git a/deps/v8/src/bignum.h b/deps/v8/src/bignum.h new file mode 100644 index 0000000000..1d2bff61a5 --- /dev/null +++ b/deps/v8/src/bignum.h @@ -0,0 +1,140 @@ +// Copyright 2010 the V8 project authors. All rights reserved. +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following +// disclaimer in the documentation and/or other materials provided +// with the distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +#ifndef V8_BIGNUM_H_ +#define V8_BIGNUM_H_ + +namespace v8 { +namespace internal { + +class Bignum { + public: + // 3584 = 128 * 28. We can represent 2^3584 > 10^1000 accurately. + // This bignum can encode much bigger numbers, since it contains an + // exponent. + static const int kMaxSignificantBits = 3584; + + Bignum(); + void AssignUInt16(uint16_t value); + void AssignUInt64(uint64_t value); + void AssignBignum(const Bignum& other); + + void AssignDecimalString(Vector<const char> value); + void AssignHexString(Vector<const char> value); + + void AssignPowerUInt16(uint16_t base, int exponent); + + void AddUInt16(uint16_t operand); + void AddUInt64(uint64_t operand); + void AddBignum(const Bignum& other); + // Precondition: this >= other. + void SubtractBignum(const Bignum& other); + + void Square(); + void ShiftLeft(int shift_amount); + void MultiplyByUInt32(uint32_t factor); + void MultiplyByUInt64(uint64_t factor); + void MultiplyByPowerOfTen(int exponent); + void Times10() { return MultiplyByUInt32(10); } + // Pseudocode: + // int result = this / other; + // this = this % other; + // In the worst case this function is in O(this/other). + uint16_t DivideModuloIntBignum(const Bignum& other); + + bool ToHexString(char* buffer, int buffer_size) const; + + static int Compare(const Bignum& a, const Bignum& b); + static bool Equal(const Bignum& a, const Bignum& b) { + return Compare(a, b) == 0; + } + static bool LessEqual(const Bignum& a, const Bignum& b) { + return Compare(a, b) <= 0; + } + static bool Less(const Bignum& a, const Bignum& b) { + return Compare(a, b) < 0; + } + // Returns Compare(a + b, c); + static int PlusCompare(const Bignum& a, const Bignum& b, const Bignum& c); + // Returns a + b == c + static bool PlusEqual(const Bignum& a, const Bignum& b, const Bignum& c) { + return PlusCompare(a, b, c) == 0; + } + // Returns a + b <= c + static bool PlusLessEqual(const Bignum& a, const Bignum& b, const Bignum& c) { + return PlusCompare(a, b, c) <= 0; + } + // Returns a + b < c + static bool PlusLess(const Bignum& a, const Bignum& b, const Bignum& c) { + return PlusCompare(a, b, c) < 0; + } + private: + typedef uint32_t Chunk; + typedef uint64_t DoubleChunk; + + static const int kChunkSize = sizeof(Chunk) * 8; + static const int kDoubleChunkSize = sizeof(DoubleChunk) * 8; + // With bigit size of 28 we loose some bits, but a double still fits easily + // into two chunks, and more importantly we can use the Comba multiplication. + static const int kBigitSize = 28; + static const Chunk kBigitMask = (1 << kBigitSize) - 1; + // Every instance allocates kBigitLength chunks on the stack. Bignums cannot + // grow. There are no checks if the stack-allocated space is sufficient. + static const int kBigitCapacity = kMaxSignificantBits / kBigitSize; + + void EnsureCapacity(int size) { + if (size > kBigitCapacity) { + UNREACHABLE(); + } + } + void Align(const Bignum& other); + void Clamp(); + bool IsClamped() const; + void Zero(); + // Requires this to have enough capacity (no tests done). + // Updates used_digits_ if necessary. + // by must be < kBigitSize. + void BigitsShiftLeft(int shift_amount); + // BigitLength includes the "hidden" digits encoded in the exponent. + int BigitLength() const { return used_digits_ + exponent_; } + Chunk BigitAt(int index) const; + void SubtractTimes(const Bignum& other, int factor); + + Chunk bigits_buffer_[kBigitCapacity]; + // A vector backed by bigits_buffer_. This way accesses to the array are + // checked for out-of-bounds errors. + Vector<Chunk> bigits_; + int used_digits_; + // The Bignum's value equals value(bigits_) * 2^(exponent_ * kBigitSize). + int exponent_; + + DISALLOW_COPY_AND_ASSIGN(Bignum); +}; + +} } // namespace v8::internal + +#endif // V8_BIGNUM_H_ diff --git a/deps/v8/src/bootstrapper.cc b/deps/v8/src/bootstrapper.cc index 0e49966bbd..f60a975dc1 100644 --- a/deps/v8/src/bootstrapper.cc +++ b/deps/v8/src/bootstrapper.cc @@ -39,6 +39,8 @@ #include "objects-visiting.h" #include "snapshot.h" #include "stub-cache.h" +#include "extensions/externalize-string-extension.h" +#include "extensions/gc-extension.h" namespace v8 { namespace internal { @@ -137,6 +139,8 @@ Handle<String> Bootstrapper::NativesSourceLookup(int index) { void Bootstrapper::Initialize(bool create_heap_objects) { extensions_cache.Initialize(create_heap_objects); + GCExtension::Register(); + ExternalizeStringExtension::Register(); } diff --git a/deps/v8/src/checks.cc b/deps/v8/src/checks.cc index b5df316d0f..1ab8802ec3 100644 --- a/deps/v8/src/checks.cc +++ b/deps/v8/src/checks.cc @@ -98,3 +98,12 @@ void API_Fatal(const char* location, const char* format, ...) { i::OS::PrintError("\n#\n\n"); i::OS::Abort(); } + + +namespace v8 { namespace internal { + + bool EnableSlowAsserts() { return FLAG_enable_slow_asserts; } + + intptr_t HeapObjectTagMask() { return kHeapObjectTagMask; } + +} } // namespace v8::internal diff --git a/deps/v8/src/checks.h b/deps/v8/src/checks.h index 5ea59920ac..6b493225ad 100644 --- a/deps/v8/src/checks.h +++ b/deps/v8/src/checks.h @@ -30,8 +30,6 @@ #include <string.h> -#include "flags.h" - extern "C" void V8_Fatal(const char* file, int line, const char* format, ...); void API_Fatal(const char* location, const char* format, ...); @@ -279,6 +277,12 @@ template <int> class StaticAssertionHelper { }; SEMI_STATIC_JOIN(__StaticAssertTypedef__, __LINE__) +namespace v8 { namespace internal { + +bool EnableSlowAsserts(); + +} } // namespace v8::internal + // The ASSERT macro is equivalent to CHECK except that it only // generates code in debug builds. #ifdef DEBUG @@ -287,7 +291,7 @@ template <int> class StaticAssertionHelper { }; #define ASSERT_EQ(v1, v2) CHECK_EQ(v1, v2) #define ASSERT_NE(v1, v2) CHECK_NE(v1, v2) #define ASSERT_GE(v1, v2) CHECK_GE(v1, v2) -#define SLOW_ASSERT(condition) if (FLAG_enable_slow_asserts) CHECK(condition) +#define SLOW_ASSERT(condition) if (EnableSlowAsserts()) CHECK(condition) #else #define ASSERT_RESULT(expr) (expr) #define ASSERT(condition) ((void) 0) @@ -303,11 +307,16 @@ template <int> class StaticAssertionHelper { }; // and release compilation modes behaviour. #define STATIC_ASSERT(test) STATIC_CHECK(test) +namespace v8 { namespace internal { + +intptr_t HeapObjectTagMask(); + +} } // namespace v8::internal #define ASSERT_TAG_ALIGNED(address) \ - ASSERT((reinterpret_cast<intptr_t>(address) & kHeapObjectTagMask) == 0) + ASSERT((reinterpret_cast<intptr_t>(address) & HeapObjectTagMask()) == 0) -#define ASSERT_SIZE_TAG_ALIGNED(size) ASSERT((size & kHeapObjectTagMask) == 0) +#define ASSERT_SIZE_TAG_ALIGNED(size) ASSERT((size & HeapObjectTagMask()) == 0) #define ASSERT_NOT_NULL(p) ASSERT_NE(NULL, p) diff --git a/deps/v8/src/code-stubs.cc b/deps/v8/src/code-stubs.cc index 787ec2a7a1..8b9198fb99 100644 --- a/deps/v8/src/code-stubs.cc +++ b/deps/v8/src/code-stubs.cc @@ -37,7 +37,6 @@ namespace v8 { namespace internal { bool CodeStub::FindCodeInCache(Code** code_out) { - if (has_custom_cache()) return GetCustomCache(code_out); int index = Heap::code_stubs()->FindEntry(GetKey()); if (index != NumberDictionary::kNotFound) { *code_out = Code::cast(Heap::code_stubs()->ValueAt(index)); @@ -105,17 +104,14 @@ Handle<Code> CodeStub::GetCode() { Handle<Code> new_object = Factory::NewCode(desc, flags, masm.CodeObject()); RecordCodeGeneration(*new_object, &masm); - if (has_custom_cache()) { - SetCustomCache(*new_object); - } else { - // Update the dictionary and the root in Heap. - Handle<NumberDictionary> dict = - Factory::DictionaryAtNumberPut( - Handle<NumberDictionary>(Heap::code_stubs()), - GetKey(), - new_object); - Heap::public_set_code_stubs(*dict); - } + // Update the dictionary and the root in Heap. + Handle<NumberDictionary> dict = + Factory::DictionaryAtNumberPut( + Handle<NumberDictionary>(Heap::code_stubs()), + GetKey(), + new_object); + Heap::public_set_code_stubs(*dict); + code = *new_object; } @@ -147,15 +143,11 @@ MaybeObject* CodeStub::TryGetCode() { code = Code::cast(new_object); RecordCodeGeneration(code, &masm); - if (has_custom_cache()) { - SetCustomCache(code); - } else { - // Try to update the code cache but do not fail if unable. - MaybeObject* maybe_new_object = - Heap::code_stubs()->AtNumberPut(GetKey(), code); - if (maybe_new_object->ToObject(&new_object)) { - Heap::public_set_code_stubs(NumberDictionary::cast(new_object)); - } + // Try to update the code cache but do not fail if unable. + MaybeObject* maybe_new_object = + Heap::code_stubs()->AtNumberPut(GetKey(), code); + if (maybe_new_object->ToObject(&new_object)) { + Heap::public_set_code_stubs(NumberDictionary::cast(new_object)); } } diff --git a/deps/v8/src/code-stubs.h b/deps/v8/src/code-stubs.h index c0a8d3063c..b156647d5d 100644 --- a/deps/v8/src/code-stubs.h +++ b/deps/v8/src/code-stubs.h @@ -124,12 +124,6 @@ class CodeStub BASE_EMBEDDED { virtual ~CodeStub() {} - // Override these methods to provide a custom caching mechanism for - // an individual type of code stub. - virtual bool GetCustomCache(Code** code_out) { return false; } - virtual void SetCustomCache(Code* value) { } - virtual bool has_custom_cache() { return false; } - protected: static const int kMajorBits = 5; static const int kMinorBits = kBitsPerInt - kSmiTagSize - kMajorBits; @@ -524,32 +518,6 @@ class CEntryStub : public CodeStub { }; -class ApiGetterEntryStub : public CodeStub { - public: - ApiGetterEntryStub(Handle<AccessorInfo> info, - ApiFunction* fun) - : info_(info), - fun_(fun) { } - void Generate(MacroAssembler* masm); - virtual bool has_custom_cache() { return true; } - virtual bool GetCustomCache(Code** code_out); - virtual void SetCustomCache(Code* value); - - static const int kStackSpace = 5; - static const int kArgc = 2; - private: - Handle<AccessorInfo> info() { return info_; } - ApiFunction* fun() { return fun_; } - Major MajorKey() { return NoCache; } - int MinorKey() { return 0; } - const char* GetName() { return "ApiEntryStub"; } - // The accessor info associated with the function. - Handle<AccessorInfo> info_; - // The function to be called. - ApiFunction* fun_; -}; - - class JSEntryStub : public CodeStub { public: JSEntryStub() { } diff --git a/deps/v8/src/codegen.cc b/deps/v8/src/codegen.cc index bda697abaf..fb8c5cd4a3 100644 --- a/deps/v8/src/codegen.cc +++ b/deps/v8/src/codegen.cc @@ -70,9 +70,10 @@ void CodeGenerator::ProcessDeferred() { DeferredCode* code = deferred_.RemoveLast(); ASSERT(masm_ == code->masm()); // Record position of deferred code stub. - masm_->RecordStatementPosition(code->statement_position()); + masm_->positions_recorder()->RecordStatementPosition( + code->statement_position()); if (code->position() != RelocInfo::kNoPosition) { - masm_->RecordPosition(code->position()); + masm_->positions_recorder()->RecordPosition(code->position()); } // Generate the code. Comment cmnt(masm_, code->comment()); @@ -251,39 +252,6 @@ bool CodeGenerator::ShouldGenerateLog(Expression* type) { #endif -Handle<Code> CodeGenerator::ComputeCallInitialize( - int argc, - InLoopFlag in_loop) { - if (in_loop == IN_LOOP) { - // Force the creation of the corresponding stub outside loops, - // because it may be used when clearing the ICs later - it is - // possible for a series of IC transitions to lose the in-loop - // information, and the IC clearing code can't generate a stub - // that it needs so we need to ensure it is generated already. - ComputeCallInitialize(argc, NOT_IN_LOOP); - } - CALL_HEAP_FUNCTION( - StubCache::ComputeCallInitialize(argc, in_loop, Code::CALL_IC), - Code); -} - - -Handle<Code> CodeGenerator::ComputeKeyedCallInitialize( - int argc, - InLoopFlag in_loop) { - if (in_loop == IN_LOOP) { - // Force the creation of the corresponding stub outside loops, - // because it may be used when clearing the ICs later - it is - // possible for a series of IC transitions to lose the in-loop - // information, and the IC clearing code can't generate a stub - // that it needs so we need to ensure it is generated already. - ComputeKeyedCallInitialize(argc, NOT_IN_LOOP); - } - CALL_HEAP_FUNCTION( - StubCache::ComputeCallInitialize(argc, in_loop, Code::KEYED_CALL_IC), - Code); -} - void CodeGenerator::ProcessDeclarations(ZoneList<Declaration*>* declarations) { int length = declarations->length(); int globals = 0; @@ -402,10 +370,10 @@ bool CodeGenerator::RecordPositions(MacroAssembler* masm, int pos, bool right_here) { if (pos != RelocInfo::kNoPosition) { - masm->RecordStatementPosition(pos); - masm->RecordPosition(pos); + masm->positions_recorder()->RecordStatementPosition(pos); + masm->positions_recorder()->RecordPosition(pos); if (right_here) { - return masm->WriteRecordedPositions(); + return masm->positions_recorder()->WriteRecordedPositions(); } } return false; @@ -435,7 +403,7 @@ void CodeGenerator::CodeForDoWhileConditionPosition(DoWhileStatement* stmt) { void CodeGenerator::CodeForSourcePosition(int pos) { if (FLAG_debug_info && pos != RelocInfo::kNoPosition) { - masm()->RecordPosition(pos); + masm()->positions_recorder()->RecordPosition(pos); } } @@ -481,20 +449,4 @@ int CEntryStub::MinorKey() { } -bool ApiGetterEntryStub::GetCustomCache(Code** code_out) { - Object* cache = info()->load_stub_cache(); - if (cache->IsUndefined()) { - return false; - } else { - *code_out = Code::cast(cache); - return true; - } -} - - -void ApiGetterEntryStub::SetCustomCache(Code* value) { - info()->set_load_stub_cache(value); -} - - } } // namespace v8::internal diff --git a/deps/v8/src/codegen.h b/deps/v8/src/codegen.h index 8f923dd34b..66300d6caf 100644 --- a/deps/v8/src/codegen.h +++ b/deps/v8/src/codegen.h @@ -58,8 +58,6 @@ // Generate // ComputeLazyCompile // BuildFunctionInfo -// ComputeCallInitialize -// ComputeCallInitializeInLoop // ProcessDeclarations // DeclareGlobals // CheckForInlineRuntimeCall diff --git a/deps/v8/src/compiler.cc b/deps/v8/src/compiler.cc index 6f02960dda..29bbbc7034 100755 --- a/deps/v8/src/compiler.cc +++ b/deps/v8/src/compiler.cc @@ -279,7 +279,6 @@ Handle<SharedFunctionInfo> Compiler::Compile(Handle<String> source, // in that case too. ScriptDataImpl* pre_data = input_pre_data; if (pre_data == NULL - && FLAG_lazy && source_length >= FLAG_min_preparse_length) { pre_data = ParserApi::PartialPreParse(source, NULL, extension); } diff --git a/deps/v8/src/conversions.cc b/deps/v8/src/conversions.cc index 790e807aef..c0dbf73ad0 100644 --- a/deps/v8/src/conversions.cc +++ b/deps/v8/src/conversions.cc @@ -33,7 +33,7 @@ #include "conversions-inl.h" #include "dtoa.h" #include "factory.h" -#include "scanner.h" +#include "scanner-base.h" #include "strtod.h" namespace v8 { @@ -121,7 +121,7 @@ static const double JUNK_STRING_VALUE = OS::nan_value(); template <class Iterator, class EndMark> static inline bool AdvanceToNonspace(Iterator* current, EndMark end) { while (*current != end) { - if (!Scanner::kIsWhiteSpace.get(**current)) return true; + if (!ScannerConstants::kIsWhiteSpace.get(**current)) return true; ++*current; } return false; @@ -654,7 +654,7 @@ static double InternalStringToDouble(Iterator current, buffer[buffer_pos] = '\0'; double converted = Strtod(Vector<const char>(buffer, buffer_pos), exponent); - return sign? -converted: converted; + return sign ? -converted : converted; } @@ -711,11 +711,6 @@ double StringToDouble(Vector<const char> str, } -extern "C" char* dtoa(double d, int mode, int ndigits, - int* decpt, int* sign, char** rve); - -extern "C" void freedtoa(char* s); - const char* DoubleToCString(double v, Vector<char> buffer) { StringBuilder builder(buffer.start(), buffer.length()); @@ -739,21 +734,13 @@ const char* DoubleToCString(double v, Vector<char> buffer) { default: { int decimal_point; int sign; - char* decimal_rep; - bool used_gay_dtoa = false; const int kV8DtoaBufferCapacity = kBase10MaximalLength + 1; - char v8_dtoa_buffer[kV8DtoaBufferCapacity]; + char decimal_rep[kV8DtoaBufferCapacity]; int length; - if (DoubleToAscii(v, DTOA_SHORTEST, 0, - Vector<char>(v8_dtoa_buffer, kV8DtoaBufferCapacity), - &sign, &length, &decimal_point)) { - decimal_rep = v8_dtoa_buffer; - } else { - decimal_rep = dtoa(v, 0, 0, &decimal_point, &sign, NULL); - used_gay_dtoa = true; - length = StrLength(decimal_rep); - } + DoubleToAscii(v, DTOA_SHORTEST, 0, + Vector<char>(decimal_rep, kV8DtoaBufferCapacity), + &sign, &length, &decimal_point); if (sign) builder.AddCharacter('-'); @@ -787,8 +774,6 @@ const char* DoubleToCString(double v, Vector<char> buffer) { if (exponent < 0) exponent = -exponent; builder.AddFormatted("%d", exponent); } - - if (used_gay_dtoa) freedtoa(decimal_rep); } } return builder.Finalize(); @@ -816,7 +801,7 @@ const char* IntToCString(int n, Vector<char> buffer) { char* DoubleToFixedCString(double value, int f) { - const int kMaxDigitsBeforePoint = 20; + const int kMaxDigitsBeforePoint = 21; const double kFirstNonFixed = 1e21; const int kMaxDigitsAfterPoint = 20; ASSERT(f >= 0); @@ -840,16 +825,14 @@ char* DoubleToFixedCString(double value, int f) { // Find a sufficiently precise decimal representation of n. int decimal_point; int sign; - // Add space for the '.' and the '\0' byte. + // Add space for the '\0' byte. const int kDecimalRepCapacity = - kMaxDigitsBeforePoint + kMaxDigitsAfterPoint + 2; + kMaxDigitsBeforePoint + kMaxDigitsAfterPoint + 1; char decimal_rep[kDecimalRepCapacity]; int decimal_rep_length; - bool status = DoubleToAscii(value, DTOA_FIXED, f, - Vector<char>(decimal_rep, kDecimalRepCapacity), - &sign, &decimal_rep_length, &decimal_point); - USE(status); - ASSERT(status); + DoubleToAscii(value, DTOA_FIXED, f, + Vector<char>(decimal_rep, kDecimalRepCapacity), + &sign, &decimal_rep_length, &decimal_point); // Create a representation that is padded with zeros if needed. int zero_prefix_length = 0; @@ -935,8 +918,6 @@ char* DoubleToExponentialCString(double value, int f) { // Find a sufficiently precise decimal representation of n. int decimal_point; int sign; - char* decimal_rep = NULL; - bool used_gay_dtoa = false; // f corresponds to the digits after the point. There is always one digit // before the point. The number of requested_digits equals hence f + 1. // And we have to add one character for the null-terminator. @@ -944,31 +925,18 @@ char* DoubleToExponentialCString(double value, int f) { // Make sure that the buffer is big enough, even if we fall back to the // shortest representation (which happens when f equals -1). ASSERT(kBase10MaximalLength <= kMaxDigitsAfterPoint + 1); - char v8_dtoa_buffer[kV8DtoaBufferCapacity]; + char decimal_rep[kV8DtoaBufferCapacity]; int decimal_rep_length; if (f == -1) { - if (DoubleToAscii(value, DTOA_SHORTEST, 0, - Vector<char>(v8_dtoa_buffer, kV8DtoaBufferCapacity), - &sign, &decimal_rep_length, &decimal_point)) { - f = decimal_rep_length - 1; - decimal_rep = v8_dtoa_buffer; - } else { - decimal_rep = dtoa(value, 0, 0, &decimal_point, &sign, NULL); - decimal_rep_length = StrLength(decimal_rep); - f = decimal_rep_length - 1; - used_gay_dtoa = true; - } + DoubleToAscii(value, DTOA_SHORTEST, 0, + Vector<char>(decimal_rep, kV8DtoaBufferCapacity), + &sign, &decimal_rep_length, &decimal_point); + f = decimal_rep_length - 1; } else { - if (DoubleToAscii(value, DTOA_PRECISION, f + 1, - Vector<char>(v8_dtoa_buffer, kV8DtoaBufferCapacity), - &sign, &decimal_rep_length, &decimal_point)) { - decimal_rep = v8_dtoa_buffer; - } else { - decimal_rep = dtoa(value, 2, f + 1, &decimal_point, &sign, NULL); - decimal_rep_length = StrLength(decimal_rep); - used_gay_dtoa = true; - } + DoubleToAscii(value, DTOA_PRECISION, f + 1, + Vector<char>(decimal_rep, kV8DtoaBufferCapacity), + &sign, &decimal_rep_length, &decimal_point); } ASSERT(decimal_rep_length > 0); ASSERT(decimal_rep_length <= f + 1); @@ -977,10 +945,6 @@ char* DoubleToExponentialCString(double value, int f) { char* result = CreateExponentialRepresentation(decimal_rep, exponent, negative, f+1); - if (used_gay_dtoa) { - freedtoa(decimal_rep); - } - return result; } @@ -1000,22 +964,14 @@ char* DoubleToPrecisionCString(double value, int p) { // Find a sufficiently precise decimal representation of n. int decimal_point; int sign; - char* decimal_rep = NULL; - bool used_gay_dtoa = false; // Add one for the terminating null character. const int kV8DtoaBufferCapacity = kMaximalDigits + 1; - char v8_dtoa_buffer[kV8DtoaBufferCapacity]; + char decimal_rep[kV8DtoaBufferCapacity]; int decimal_rep_length; - if (DoubleToAscii(value, DTOA_PRECISION, p, - Vector<char>(v8_dtoa_buffer, kV8DtoaBufferCapacity), - &sign, &decimal_rep_length, &decimal_point)) { - decimal_rep = v8_dtoa_buffer; - } else { - decimal_rep = dtoa(value, 2, p, &decimal_point, &sign, NULL); - decimal_rep_length = StrLength(decimal_rep); - used_gay_dtoa = true; - } + DoubleToAscii(value, DTOA_PRECISION, p, + Vector<char>(decimal_rep, kV8DtoaBufferCapacity), + &sign, &decimal_rep_length, &decimal_point); ASSERT(decimal_rep_length <= p); int exponent = decimal_point - 1; @@ -1059,9 +1015,6 @@ char* DoubleToPrecisionCString(double value, int p) { result = builder.Finalize(); } - if (used_gay_dtoa) { - freedtoa(decimal_rep); - } return result; } diff --git a/deps/v8/src/dateparser.h b/deps/v8/src/dateparser.h index cae9b08d5b..28053f46d1 100644 --- a/deps/v8/src/dateparser.h +++ b/deps/v8/src/dateparser.h @@ -28,7 +28,8 @@ #ifndef V8_DATEPARSER_H_ #define V8_DATEPARSER_H_ -#include "scanner.h" +#include "char-predicates-inl.h" +#include "scanner-base.h" namespace v8 { namespace internal { @@ -99,10 +100,20 @@ class DateParser : public AllStatic { } // The skip methods return whether they actually skipped something. - bool Skip(uint32_t c) { return ch_ == c ? (Next(), true) : false; } + bool Skip(uint32_t c) { + if (ch_ == c) { + Next(); + return true; + } + return false; + } bool SkipWhiteSpace() { - return Scanner::kIsWhiteSpace.get(ch_) ? (Next(), true) : false; + if (ScannerConstants::kIsWhiteSpace.get(ch_)) { + Next(); + return true; + } + return false; } bool SkipParentheses() { diff --git a/deps/v8/src/debug-debugger.js b/deps/v8/src/debug-debugger.js index 0eab8d1b83..d091991a11 100644 --- a/deps/v8/src/debug-debugger.js +++ b/deps/v8/src/debug-debugger.js @@ -897,10 +897,6 @@ ExecutionState.prototype.frame = function(opt_index) { return new FrameMirror(this.break_id, opt_index); }; -ExecutionState.prototype.cframesValue = function(opt_from_index, opt_to_index) { - return %GetCFrames(this.break_id); -}; - ExecutionState.prototype.setSelectedFrame = function(index) { var i = %ToNumber(index); if (i < 0 || i >= this.frameCount()) throw new Error('Illegal frame index.'); @@ -1751,11 +1747,6 @@ DebugCommandProcessor.prototype.backtraceRequest_ = function(request, response) }; -DebugCommandProcessor.prototype.backtracec = function(cmd, args) { - return this.exec_state_.cframesValue(); -}; - - DebugCommandProcessor.prototype.frameRequest_ = function(request, response) { // No frames no source. if (this.exec_state_.frameCount() == 0) { @@ -2205,29 +2196,6 @@ function NumberToHex8Str(n) { return r; }; -DebugCommandProcessor.prototype.formatCFrames = function(cframes_value) { - var result = ""; - if (cframes_value == null || cframes_value.length == 0) { - result += "(stack empty)"; - } else { - for (var i = 0; i < cframes_value.length; ++i) { - if (i != 0) result += "\n"; - result += this.formatCFrame(cframes_value[i]); - } - } - return result; -}; - - -DebugCommandProcessor.prototype.formatCFrame = function(cframe_value) { - var result = ""; - result += "0x" + NumberToHex8Str(cframe_value.address); - if (!IS_UNDEFINED(cframe_value.text)) { - result += " " + cframe_value.text; - } - return result; -} - /** * Convert an Object to its debugger protocol representation. The representation diff --git a/deps/v8/src/debug.cc b/deps/v8/src/debug.cc index 24f0409861..f3bf954da9 100644 --- a/deps/v8/src/debug.cc +++ b/deps/v8/src/debug.cc @@ -1839,6 +1839,7 @@ bool Debug::IsDebugGlobal(GlobalObject* global) { void Debug::ClearMirrorCache() { + PostponeInterruptsScope postpone; HandleScope scope; ASSERT(Top::context() == *Debug::debug_context()); diff --git a/deps/v8/src/double.h b/deps/v8/src/double.h index e805173e07..65eded9989 100644 --- a/deps/v8/src/double.h +++ b/deps/v8/src/double.h @@ -54,18 +54,20 @@ class Double { explicit Double(DiyFp diy_fp) : d64_(DiyFpToUint64(diy_fp)) {} + // The value encoded by this Double must be greater or equal to +0.0. + // It must not be special (infinity, or NaN). DiyFp AsDiyFp() const { + ASSERT(Sign() > 0); ASSERT(!IsSpecial()); return DiyFp(Significand(), Exponent()); } - // this->Significand() must not be 0. + // The value encoded by this Double must be strictly greater than 0. DiyFp AsNormalizedDiyFp() const { + ASSERT(value() > 0.0); uint64_t f = Significand(); int e = Exponent(); - ASSERT(f != 0); - // The current double could be a denormal. while ((f & kHiddenBit) == 0) { f <<= 1; @@ -82,6 +84,20 @@ class Double { return d64_; } + // Returns the next greater double. Returns +infinity on input +infinity. + double NextDouble() const { + if (d64_ == kInfinity) return Double(kInfinity).value(); + if (Sign() < 0 && Significand() == 0) { + // -0.0 + return 0.0; + } + if (Sign() < 0) { + return Double(d64_ - 1).value(); + } else { + return Double(d64_ + 1).value(); + } + } + int Exponent() const { if (IsDenormal()) return kDenormalExponent; @@ -120,24 +136,30 @@ class Double { ((d64 & kSignificandMask) != 0); } - bool IsInfinite() const { uint64_t d64 = AsUint64(); return ((d64 & kExponentMask) == kExponentMask) && ((d64 & kSignificandMask) == 0); } - int Sign() const { uint64_t d64 = AsUint64(); return (d64 & kSignMask) == 0? 1: -1; } + // Precondition: the value encoded by this Double must be greater or equal + // than +0.0. + DiyFp UpperBoundary() const { + ASSERT(Sign() > 0); + return DiyFp(Significand() * 2 + 1, Exponent() - 1); + } // Returns the two boundaries of this. // The bigger boundary (m_plus) is normalized. The lower boundary has the same // exponent as m_plus. + // Precondition: the value encoded by this Double must be greater than 0. void NormalizedBoundaries(DiyFp* out_m_minus, DiyFp* out_m_plus) const { + ASSERT(value() > 0.0); DiyFp v = this->AsDiyFp(); bool significand_is_zero = (v.f() == kHiddenBit); DiyFp m_plus = DiyFp::Normalize(DiyFp((v.f() << 1) + 1, v.e() - 1)); diff --git a/deps/v8/src/dtoa.cc b/deps/v8/src/dtoa.cc index f4141eb619..b857a5dc59 100644 --- a/deps/v8/src/dtoa.cc +++ b/deps/v8/src/dtoa.cc @@ -30,6 +30,7 @@ #include "v8.h" #include "dtoa.h" +#include "bignum-dtoa.h" #include "double.h" #include "fast-dtoa.h" #include "fixed-dtoa.h" @@ -37,7 +38,19 @@ namespace v8 { namespace internal { -bool DoubleToAscii(double v, DtoaMode mode, int requested_digits, +static BignumDtoaMode DtoaToBignumDtoaMode(DtoaMode dtoa_mode) { + switch (dtoa_mode) { + case DTOA_SHORTEST: return BIGNUM_DTOA_SHORTEST; + case DTOA_FIXED: return BIGNUM_DTOA_FIXED; + case DTOA_PRECISION: return BIGNUM_DTOA_PRECISION; + default: + UNREACHABLE(); + return BIGNUM_DTOA_SHORTEST; // To silence compiler. + } +} + + +void DoubleToAscii(double v, DtoaMode mode, int requested_digits, Vector<char> buffer, int* sign, int* length, int* point) { ASSERT(!Double(v).IsSpecial()); ASSERT(mode == DTOA_SHORTEST || requested_digits >= 0); @@ -54,25 +67,37 @@ bool DoubleToAscii(double v, DtoaMode mode, int requested_digits, buffer[1] = '\0'; *length = 1; *point = 1; - return true; + return; } if (mode == DTOA_PRECISION && requested_digits == 0) { buffer[0] = '\0'; *length = 0; - return true; + return; } + bool fast_worked; switch (mode) { case DTOA_SHORTEST: - return FastDtoa(v, FAST_DTOA_SHORTEST, 0, buffer, length, point); + fast_worked = FastDtoa(v, FAST_DTOA_SHORTEST, 0, buffer, length, point); + break; case DTOA_FIXED: - return FastFixedDtoa(v, requested_digits, buffer, length, point); + fast_worked = FastFixedDtoa(v, requested_digits, buffer, length, point); + break; case DTOA_PRECISION: - return FastDtoa(v, FAST_DTOA_PRECISION, requested_digits, - buffer, length, point); + fast_worked = FastDtoa(v, FAST_DTOA_PRECISION, requested_digits, + buffer, length, point); + break; + default: + UNREACHABLE(); + fast_worked = false; } - return false; + if (fast_worked) return; + + // If the fast dtoa didn't succeed use the slower bignum version. + BignumDtoaMode bignum_mode = DtoaToBignumDtoaMode(mode); + BignumDtoa(v, bignum_mode, requested_digits, buffer, length, point); + buffer[*length] = '\0'; } } } // namespace v8::internal diff --git a/deps/v8/src/dtoa.h b/deps/v8/src/dtoa.h index be0d5456b2..b3e79afa48 100644 --- a/deps/v8/src/dtoa.h +++ b/deps/v8/src/dtoa.h @@ -32,13 +32,15 @@ namespace v8 { namespace internal { enum DtoaMode { - // 0.9999999999999999 becomes 0.1 + // Return the shortest correct representation. + // For example the output of 0.299999999999999988897 is (the less accurate but + // correct) 0.3. DTOA_SHORTEST, - // Fixed number of digits after the decimal point. + // Return a fixed number of digits after the decimal point. // For instance fixed(0.1, 4) becomes 0.1000 // If the input number is big, the output will be big. DTOA_FIXED, - // Fixed number of digits (independent of the decimal point). + // Return a fixed number of digits, no matter what the exponent is. DTOA_PRECISION }; @@ -72,8 +74,10 @@ static const int kBase10MaximalLength = 17; // which case the caller has to fill the missing digits with '0's. // Halfway cases are again rounded away from 0. // 'DoubleToAscii' expects the given buffer to be big enough to hold all digits -// and a terminating null-character. -bool DoubleToAscii(double v, DtoaMode mode, int requested_digits, +// and a terminating null-character. In SHORTEST-mode it expects a buffer of +// at least kBase10MaximalLength + 1. Otherwise, the size of the output is +// limited to requested_digits digits plus the null terminator. +void DoubleToAscii(double v, DtoaMode mode, int requested_digits, Vector<char> buffer, int* sign, int* length, int* point); } } // namespace v8::internal diff --git a/deps/v8/src/execution.cc b/deps/v8/src/execution.cc index 885bf63cf1..691d56954a 100644 --- a/deps/v8/src/execution.cc +++ b/deps/v8/src/execution.cc @@ -700,135 +700,4 @@ MaybeObject* Execution::HandleStackGuardInterrupt() { return Heap::undefined_value(); } -// --- G C E x t e n s i o n --- - -const char* const GCExtension::kSource = "native function gc();"; - - -v8::Handle<v8::FunctionTemplate> GCExtension::GetNativeFunction( - v8::Handle<v8::String> str) { - return v8::FunctionTemplate::New(GCExtension::GC); -} - - -v8::Handle<v8::Value> GCExtension::GC(const v8::Arguments& args) { - // All allocation spaces other than NEW_SPACE have the same effect. - Heap::CollectAllGarbage(false); - return v8::Undefined(); -} - - -static GCExtension gc_extension; -static v8::DeclareExtension gc_extension_declaration(&gc_extension); - - -// --- E x t e r n a l i z e S t r i n g E x t e n s i o n --- - - -template <typename Char, typename Base> -class SimpleStringResource : public Base { - public: - // Takes ownership of |data|. - SimpleStringResource(Char* data, size_t length) - : data_(data), - length_(length) {} - - virtual ~SimpleStringResource() { delete[] data_; } - - virtual const Char* data() const { return data_; } - - virtual size_t length() const { return length_; } - - private: - Char* const data_; - const size_t length_; -}; - - -typedef SimpleStringResource<char, v8::String::ExternalAsciiStringResource> - SimpleAsciiStringResource; -typedef SimpleStringResource<uc16, v8::String::ExternalStringResource> - SimpleTwoByteStringResource; - - -const char* const ExternalizeStringExtension::kSource = - "native function externalizeString();" - "native function isAsciiString();"; - - -v8::Handle<v8::FunctionTemplate> ExternalizeStringExtension::GetNativeFunction( - v8::Handle<v8::String> str) { - if (strcmp(*v8::String::AsciiValue(str), "externalizeString") == 0) { - return v8::FunctionTemplate::New(ExternalizeStringExtension::Externalize); - } else { - ASSERT(strcmp(*v8::String::AsciiValue(str), "isAsciiString") == 0); - return v8::FunctionTemplate::New(ExternalizeStringExtension::IsAscii); - } -} - - -v8::Handle<v8::Value> ExternalizeStringExtension::Externalize( - const v8::Arguments& args) { - if (args.Length() < 1 || !args[0]->IsString()) { - return v8::ThrowException(v8::String::New( - "First parameter to externalizeString() must be a string.")); - } - bool force_two_byte = false; - if (args.Length() >= 2) { - if (args[1]->IsBoolean()) { - force_two_byte = args[1]->BooleanValue(); - } else { - return v8::ThrowException(v8::String::New( - "Second parameter to externalizeString() must be a boolean.")); - } - } - bool result = false; - Handle<String> string = Utils::OpenHandle(*args[0].As<v8::String>()); - if (string->IsExternalString()) { - return v8::ThrowException(v8::String::New( - "externalizeString() can't externalize twice.")); - } - if (string->IsAsciiRepresentation() && !force_two_byte) { - char* data = new char[string->length()]; - String::WriteToFlat(*string, data, 0, string->length()); - SimpleAsciiStringResource* resource = new SimpleAsciiStringResource( - data, string->length()); - result = string->MakeExternal(resource); - if (result && !string->IsSymbol()) { - i::ExternalStringTable::AddString(*string); - } - if (!result) delete resource; - } else { - uc16* data = new uc16[string->length()]; - String::WriteToFlat(*string, data, 0, string->length()); - SimpleTwoByteStringResource* resource = new SimpleTwoByteStringResource( - data, string->length()); - result = string->MakeExternal(resource); - if (result && !string->IsSymbol()) { - i::ExternalStringTable::AddString(*string); - } - if (!result) delete resource; - } - if (!result) { - return v8::ThrowException(v8::String::New("externalizeString() failed.")); - } - return v8::Undefined(); -} - - -v8::Handle<v8::Value> ExternalizeStringExtension::IsAscii( - const v8::Arguments& args) { - if (args.Length() != 1 || !args[0]->IsString()) { - return v8::ThrowException(v8::String::New( - "isAsciiString() requires a single string argument.")); - } - return Utils::OpenHandle(*args[0].As<v8::String>())->IsAsciiRepresentation() ? - v8::True() : v8::False(); -} - - -static ExternalizeStringExtension externalize_extension; -static v8::DeclareExtension externalize_extension_declaration( - &externalize_extension); - } } // namespace v8::internal diff --git a/deps/v8/src/execution.h b/deps/v8/src/execution.h index 5547803be7..a2ddc41a15 100644 --- a/deps/v8/src/execution.h +++ b/deps/v8/src/execution.h @@ -189,6 +189,9 @@ class StackGuard : public AllStatic { static uintptr_t climit() { return thread_local_.climit_; } + static uintptr_t real_climit() { + return thread_local_.real_climit_; + } static uintptr_t jslimit() { return thread_local_.jslimit_; } @@ -313,29 +316,6 @@ class PostponeInterruptsScope BASE_EMBEDDED { } }; - -class GCExtension : public v8::Extension { - public: - GCExtension() : v8::Extension("v8/gc", kSource) {} - virtual v8::Handle<v8::FunctionTemplate> GetNativeFunction( - v8::Handle<v8::String> name); - static v8::Handle<v8::Value> GC(const v8::Arguments& args); - private: - static const char* const kSource; -}; - - -class ExternalizeStringExtension : public v8::Extension { - public: - ExternalizeStringExtension() : v8::Extension("v8/externalize", kSource) {} - virtual v8::Handle<v8::FunctionTemplate> GetNativeFunction( - v8::Handle<v8::String> name); - static v8::Handle<v8::Value> Externalize(const v8::Arguments& args); - static v8::Handle<v8::Value> IsAscii(const v8::Arguments& args); - private: - static const char* const kSource; -}; - } } // namespace v8::internal #endif // V8_EXECUTION_H_ diff --git a/deps/v8/src/extensions/externalize-string-extension.cc b/deps/v8/src/extensions/externalize-string-extension.cc new file mode 100644 index 0000000000..8b4bdbd88e --- /dev/null +++ b/deps/v8/src/extensions/externalize-string-extension.cc @@ -0,0 +1,141 @@ +// Copyright 2010 the V8 project authors. All rights reserved. +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following +// disclaimer in the documentation and/or other materials provided +// with the distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +#include "externalize-string-extension.h" + +namespace v8 { +namespace internal { + +template <typename Char, typename Base> +class SimpleStringResource : public Base { + public: + // Takes ownership of |data|. + SimpleStringResource(Char* data, size_t length) + : data_(data), + length_(length) {} + + virtual ~SimpleStringResource() { delete[] data_; } + + virtual const Char* data() const { return data_; } + + virtual size_t length() const { return length_; } + + private: + Char* const data_; + const size_t length_; +}; + + +typedef SimpleStringResource<char, v8::String::ExternalAsciiStringResource> + SimpleAsciiStringResource; +typedef SimpleStringResource<uc16, v8::String::ExternalStringResource> + SimpleTwoByteStringResource; + + +const char* const ExternalizeStringExtension::kSource = + "native function externalizeString();" + "native function isAsciiString();"; + + +v8::Handle<v8::FunctionTemplate> ExternalizeStringExtension::GetNativeFunction( + v8::Handle<v8::String> str) { + if (strcmp(*v8::String::AsciiValue(str), "externalizeString") == 0) { + return v8::FunctionTemplate::New(ExternalizeStringExtension::Externalize); + } else { + ASSERT(strcmp(*v8::String::AsciiValue(str), "isAsciiString") == 0); + return v8::FunctionTemplate::New(ExternalizeStringExtension::IsAscii); + } +} + + +v8::Handle<v8::Value> ExternalizeStringExtension::Externalize( + const v8::Arguments& args) { + if (args.Length() < 1 || !args[0]->IsString()) { + return v8::ThrowException(v8::String::New( + "First parameter to externalizeString() must be a string.")); + } + bool force_two_byte = false; + if (args.Length() >= 2) { + if (args[1]->IsBoolean()) { + force_two_byte = args[1]->BooleanValue(); + } else { + return v8::ThrowException(v8::String::New( + "Second parameter to externalizeString() must be a boolean.")); + } + } + bool result = false; + Handle<String> string = Utils::OpenHandle(*args[0].As<v8::String>()); + if (string->IsExternalString()) { + return v8::ThrowException(v8::String::New( + "externalizeString() can't externalize twice.")); + } + if (string->IsAsciiRepresentation() && !force_two_byte) { + char* data = new char[string->length()]; + String::WriteToFlat(*string, data, 0, string->length()); + SimpleAsciiStringResource* resource = new SimpleAsciiStringResource( + data, string->length()); + result = string->MakeExternal(resource); + if (result && !string->IsSymbol()) { + i::ExternalStringTable::AddString(*string); + } + if (!result) delete resource; + } else { + uc16* data = new uc16[string->length()]; + String::WriteToFlat(*string, data, 0, string->length()); + SimpleTwoByteStringResource* resource = new SimpleTwoByteStringResource( + data, string->length()); + result = string->MakeExternal(resource); + if (result && !string->IsSymbol()) { + i::ExternalStringTable::AddString(*string); + } + if (!result) delete resource; + } + if (!result) { + return v8::ThrowException(v8::String::New("externalizeString() failed.")); + } + return v8::Undefined(); +} + + +v8::Handle<v8::Value> ExternalizeStringExtension::IsAscii( + const v8::Arguments& args) { + if (args.Length() != 1 || !args[0]->IsString()) { + return v8::ThrowException(v8::String::New( + "isAsciiString() requires a single string argument.")); + } + return Utils::OpenHandle(*args[0].As<v8::String>())->IsAsciiRepresentation() ? + v8::True() : v8::False(); +} + + +void ExternalizeStringExtension::Register() { + static ExternalizeStringExtension externalize_extension; + static v8::DeclareExtension externalize_extension_declaration( + &externalize_extension); +} + +} } // namespace v8::internal diff --git a/deps/v8/src/extensions/externalize-string-extension.h b/deps/v8/src/extensions/externalize-string-extension.h new file mode 100644 index 0000000000..b97b4962cf --- /dev/null +++ b/deps/v8/src/extensions/externalize-string-extension.h @@ -0,0 +1,50 @@ +// Copyright 2010 the V8 project authors. All rights reserved. +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following +// disclaimer in the documentation and/or other materials provided +// with the distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +#ifndef V8_EXTENSIONS_EXTERNALIZE_STRING_EXTENSION_H_ +#define V8_EXTENSIONS_EXTERNALIZE_STRING_EXTENSION_H_ + +#include "v8.h" + +namespace v8 { +namespace internal { + +class ExternalizeStringExtension : public v8::Extension { + public: + ExternalizeStringExtension() : v8::Extension("v8/externalize", kSource) {} + virtual v8::Handle<v8::FunctionTemplate> GetNativeFunction( + v8::Handle<v8::String> name); + static v8::Handle<v8::Value> Externalize(const v8::Arguments& args); + static v8::Handle<v8::Value> IsAscii(const v8::Arguments& args); + static void Register(); + private: + static const char* const kSource; +}; + +} } // namespace v8::internal + +#endif // V8_EXTENSIONS_EXTERNALIZE_STRING_EXTENSION_H_ diff --git a/deps/v8/src/extensions/gc-extension.cc b/deps/v8/src/extensions/gc-extension.cc new file mode 100644 index 0000000000..b8f081c54d --- /dev/null +++ b/deps/v8/src/extensions/gc-extension.cc @@ -0,0 +1,54 @@ +// Copyright 2010 the V8 project authors. All rights reserved. +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following +// disclaimer in the documentation and/or other materials provided +// with the distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +#include "gc-extension.h" + +namespace v8 { +namespace internal { + +const char* const GCExtension::kSource = "native function gc();"; + + +v8::Handle<v8::FunctionTemplate> GCExtension::GetNativeFunction( + v8::Handle<v8::String> str) { + return v8::FunctionTemplate::New(GCExtension::GC); +} + + +v8::Handle<v8::Value> GCExtension::GC(const v8::Arguments& args) { + // All allocation spaces other than NEW_SPACE have the same effect. + Heap::CollectAllGarbage(false); + return v8::Undefined(); +} + + +void GCExtension::Register() { + static GCExtension gc_extension; + static v8::DeclareExtension gc_extension_declaration(&gc_extension); +} + +} } // namespace v8::internal diff --git a/deps/v8/src/extensions/gc-extension.h b/deps/v8/src/extensions/gc-extension.h new file mode 100644 index 0000000000..06ea4ed21a --- /dev/null +++ b/deps/v8/src/extensions/gc-extension.h @@ -0,0 +1,49 @@ +// Copyright 2010 the V8 project authors. All rights reserved. +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following +// disclaimer in the documentation and/or other materials provided +// with the distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +#ifndef V8_EXTENSIONS_GC_EXTENSION_H_ +#define V8_EXTENSIONS_GC_EXTENSION_H_ + +#include "v8.h" + +namespace v8 { +namespace internal { + +class GCExtension : public v8::Extension { + public: + GCExtension() : v8::Extension("v8/gc", kSource) {} + virtual v8::Handle<v8::FunctionTemplate> GetNativeFunction( + v8::Handle<v8::String> name); + static v8::Handle<v8::Value> GC(const v8::Arguments& args); + static void Register(); + private: + static const char* const kSource; +}; + +} } // namespace v8::internal + +#endif // V8_EXTENSIONS_GC_EXTENSION_H_ diff --git a/deps/v8/src/flag-definitions.h b/deps/v8/src/flag-definitions.h index 54501ec95d..46feea77ac 100644 --- a/deps/v8/src/flag-definitions.h +++ b/deps/v8/src/flag-definitions.h @@ -186,6 +186,7 @@ DEFINE_bool(always_inline_smi_code, false, // heap.cc DEFINE_int(max_new_space_size, 0, "max size of the new generation (in kBytes)") DEFINE_int(max_old_space_size, 0, "max size of the old generation (in Mbytes)") +DEFINE_int(max_executable_size, 0, "max size of executable memory (in Mbytes)") DEFINE_bool(gc_global, false, "always perform global GCs") DEFINE_int(gc_interval, -1, "garbage collect after <n> allocations") DEFINE_bool(trace_gc, false, diff --git a/deps/v8/src/full-codegen.cc b/deps/v8/src/full-codegen.cc index 97987c27a8..8592472524 100644 --- a/deps/v8/src/full-codegen.cc +++ b/deps/v8/src/full-codegen.cc @@ -301,11 +301,6 @@ bool FullCodeGenerator::MakeCode(CompilationInfo* info) { } -MemOperand FullCodeGenerator::ContextOperand(Register context, int index) { - return CodeGenerator::ContextOperand(context, index); -} - - int FullCodeGenerator::SlotOffset(Slot* slot) { ASSERT(slot != NULL); // Offset is negative because higher indexes are at lower addresses. @@ -563,9 +558,10 @@ void FullCodeGenerator::SetStatementPosition(int pos) { } -void FullCodeGenerator::SetSourcePosition(int pos) { +void FullCodeGenerator::SetSourcePosition( + int pos, PositionRecordingType recording_type) { if (FLAG_debug_info && pos != RelocInfo::kNoPosition) { - masm_->RecordPosition(pos); + masm_->positions_recorder()->RecordPosition(pos, recording_type); } } @@ -1225,13 +1221,6 @@ int FullCodeGenerator::TryCatch::Exit(int stack_depth) { } -void FullCodeGenerator::EmitRegExpCloneResult(ZoneList<Expression*>* args) { - ASSERT(args->length() == 1); - VisitForStackValue(args->at(0)); - __ CallRuntime(Runtime::kRegExpCloneResult, 1); - context()->Plug(result_register()); -} - #undef __ diff --git a/deps/v8/src/full-codegen.h b/deps/v8/src/full-codegen.h index 201507b2af..6a1def6ee1 100644 --- a/deps/v8/src/full-codegen.h +++ b/deps/v8/src/full-codegen.h @@ -423,7 +423,9 @@ class FullCodeGenerator: public AstVisitor { void SetStatementPosition(Statement* stmt); void SetExpressionPosition(Expression* expr, int pos); void SetStatementPosition(int pos); - void SetSourcePosition(int pos); + void SetSourcePosition( + int pos, + PositionRecordingType recording_type = NORMAL_POSITION); // Non-local control flow support. void EnterFinallyBlock(); @@ -462,9 +464,6 @@ class FullCodeGenerator: public AstVisitor { // in v8::internal::Context. void LoadContextField(Register dst, int context_index); - // Create an operand for a context field. - MemOperand ContextOperand(Register context, int context_index); - // AST node visit functions. #define DECLARE_VISIT(type) virtual void Visit##type(type* node); AST_NODE_LIST(DECLARE_VISIT) diff --git a/deps/v8/src/global-handles.cc b/deps/v8/src/global-handles.cc index 9ede908528..5339840988 100644 --- a/deps/v8/src/global-handles.cc +++ b/deps/v8/src/global-handles.cc @@ -372,13 +372,14 @@ void GlobalHandles::IdentifyWeakHandles(WeakSlotCallback f) { int post_gc_processing_count = 0; -void GlobalHandles::PostGarbageCollectionProcessing() { +bool GlobalHandles::PostGarbageCollectionProcessing() { // Process weak global handle callbacks. This must be done after the // GC is completely done, because the callbacks may invoke arbitrary // API functions. // At the same time deallocate all DESTROYED nodes. ASSERT(Heap::gc_state() == Heap::NOT_IN_GC); const int initial_post_gc_processing_count = ++post_gc_processing_count; + bool next_gc_likely_to_collect_more = false; Node** p = &head_; while (*p != NULL) { if ((*p)->PostGarbageCollectionProcessing()) { @@ -399,6 +400,7 @@ void GlobalHandles::PostGarbageCollectionProcessing() { } node->set_next_free(first_deallocated()); set_first_deallocated(node); + next_gc_likely_to_collect_more = true; } else { p = (*p)->next_addr(); } @@ -407,6 +409,8 @@ void GlobalHandles::PostGarbageCollectionProcessing() { if (first_deallocated()) { first_deallocated()->set_next(head()); } + + return next_gc_likely_to_collect_more; } diff --git a/deps/v8/src/global-handles.h b/deps/v8/src/global-handles.h index 659f86eca7..37b2b44522 100644 --- a/deps/v8/src/global-handles.h +++ b/deps/v8/src/global-handles.h @@ -96,7 +96,8 @@ class GlobalHandles : public AllStatic { static bool IsWeak(Object** location); // Process pending weak handles. - static void PostGarbageCollectionProcessing(); + // Returns true if next major GC is likely to collect more garbage. + static bool PostGarbageCollectionProcessing(); // Iterates over all strong handles. static void IterateStrongRoots(ObjectVisitor* v); diff --git a/deps/v8/src/globals.h b/deps/v8/src/globals.h index c218f80dc1..88c3e780d9 100644 --- a/deps/v8/src/globals.h +++ b/deps/v8/src/globals.h @@ -1,4 +1,4 @@ -// Copyright 2006-2009 the V8 project authors. All rights reserved. +// Copyright 2010 the V8 project authors. All rights reserved. // Redistribution and use in source and binary forms, with or without // modification, are permitted provided that the following conditions are // met: @@ -195,8 +195,8 @@ const int kCharSize = sizeof(char); // NOLINT const int kShortSize = sizeof(short); // NOLINT const int kIntSize = sizeof(int); // NOLINT const int kDoubleSize = sizeof(double); // NOLINT -const int kPointerSize = sizeof(void*); // NOLINT const int kIntptrSize = sizeof(intptr_t); // NOLINT +const int kPointerSize = sizeof(void*); // NOLINT #if V8_HOST_ARCH_64_BIT const int kPointerSizeLog2 = 3; @@ -208,38 +208,6 @@ const intptr_t kIntptrSignBit = 0x80000000; const uintptr_t kUintptrAllBitsSet = 0xFFFFFFFFu; #endif -// Mask for the sign bit in a smi. -const intptr_t kSmiSignMask = kIntptrSignBit; - -const int kObjectAlignmentBits = kPointerSizeLog2; -const intptr_t kObjectAlignment = 1 << kObjectAlignmentBits; -const intptr_t kObjectAlignmentMask = kObjectAlignment - 1; - -// Desired alignment for pointers. -const intptr_t kPointerAlignment = (1 << kPointerSizeLog2); -const intptr_t kPointerAlignmentMask = kPointerAlignment - 1; - -// Desired alignment for maps. -#if V8_HOST_ARCH_64_BIT -const intptr_t kMapAlignmentBits = kObjectAlignmentBits; -#else -const intptr_t kMapAlignmentBits = kObjectAlignmentBits + 3; -#endif -const intptr_t kMapAlignment = (1 << kMapAlignmentBits); -const intptr_t kMapAlignmentMask = kMapAlignment - 1; - -// Desired alignment for generated code is 32 bytes (to improve cache line -// utilization). -const int kCodeAlignmentBits = 5; -const intptr_t kCodeAlignment = 1 << kCodeAlignmentBits; -const intptr_t kCodeAlignmentMask = kCodeAlignment - 1; - -// Tag information for Failure. -const int kFailureTag = 3; -const int kFailureTagSize = 2; -const intptr_t kFailureTagMask = (1 << kFailureTagSize) - 1; - - const int kBitsPerByte = 8; const int kBitsPerByteLog2 = 3; const int kBitsPerPointer = kPointerSize * kBitsPerByte; @@ -255,364 +223,6 @@ const int kBinary32MinExponent = 0x01; const int kBinary32MantissaBits = 23; const int kBinary32ExponentShift = 23; -// Zap-value: The value used for zapping dead objects. -// Should be a recognizable hex value tagged as a heap object pointer. -#ifdef V8_HOST_ARCH_64_BIT -const Address kZapValue = - reinterpret_cast<Address>(V8_UINT64_C(0xdeadbeedbeadbeed)); -const Address kHandleZapValue = - reinterpret_cast<Address>(V8_UINT64_C(0x1baddead0baddead)); -const Address kFromSpaceZapValue = - reinterpret_cast<Address>(V8_UINT64_C(0x1beefdad0beefdad)); -const uint64_t kDebugZapValue = 0xbadbaddbbadbaddb; -#else -const Address kZapValue = reinterpret_cast<Address>(0xdeadbeed); -const Address kHandleZapValue = reinterpret_cast<Address>(0xbaddead); -const Address kFromSpaceZapValue = reinterpret_cast<Address>(0xbeefdad); -const uint32_t kDebugZapValue = 0xbadbaddb; -#endif - - -// Number of bits to represent the page size for paged spaces. The value of 13 -// gives 8K bytes per page. -const int kPageSizeBits = 13; - -// On Intel architecture, cache line size is 64 bytes. -// On ARM it may be less (32 bytes), but as far this constant is -// used for aligning data, it doesn't hurt to align on a greater value. -const int kProcessorCacheLineSize = 64; - -// Constants relevant to double precision floating point numbers. - -// Quiet NaNs have bits 51 to 62 set, possibly the sign bit, and no -// other bits set. -const uint64_t kQuietNaNMask = static_cast<uint64_t>(0xfff) << 51; -// If looking only at the top 32 bits, the QNaN mask is bits 19 to 30. -const uint32_t kQuietNaNHighBitsMask = 0xfff << (51 - 32); - - -// ----------------------------------------------------------------------------- -// Forward declarations for frequently used classes -// (sorted alphabetically) - -class AccessorInfo; -class Allocation; -class Arguments; -class Assembler; -class AssertNoAllocation; -class BreakableStatement; -class Code; -class CodeGenerator; -class CodeStub; -class Context; -class Debug; -class Debugger; -class DebugInfo; -class Descriptor; -class DescriptorArray; -class Expression; -class ExternalReference; -class FixedArray; -class FunctionEntry; -class FunctionLiteral; -class FunctionTemplateInfo; -class NumberDictionary; -class StringDictionary; -class FreeStoreAllocationPolicy; -template <typename T> class Handle; -class Heap; -class HeapObject; -class IC; -class InterceptorInfo; -class IterationStatement; -class JSArray; -class JSFunction; -class JSObject; -class LargeObjectSpace; -template <typename T, class P = FreeStoreAllocationPolicy> class List; -class LookupResult; -class MacroAssembler; -class Map; -class MapSpace; -class MarkCompactCollector; -class NewSpace; -class NodeVisitor; -class Object; -class MaybeObject; -class OldSpace; -class Property; -class Proxy; -class RegExpNode; -struct RegExpCompileData; -class RegExpTree; -class RegExpCompiler; -class RegExpVisitor; -class Scope; -template<class Allocator = FreeStoreAllocationPolicy> class ScopeInfo; -class SerializedScopeInfo; -class Script; -class Slot; -class Smi; -template <typename Config, class Allocator = FreeStoreAllocationPolicy> - class SplayTree; -class Statement; -class String; -class Struct; -class SwitchStatement; -class AstVisitor; -class Variable; -class VariableProxy; -class RelocInfo; -class Deserializer; -class MessageLocation; -class ObjectGroup; -class TickSample; -class VirtualMemory; -class Mutex; - -typedef bool (*WeakSlotCallback)(Object** pointer); - -// ----------------------------------------------------------------------------- -// Miscellaneous - -// NOTE: SpaceIterator depends on AllocationSpace enumeration values being -// consecutive. -enum AllocationSpace { - NEW_SPACE, // Semispaces collected with copying collector. - OLD_POINTER_SPACE, // May contain pointers to new space. - OLD_DATA_SPACE, // Must not have pointers to new space. - CODE_SPACE, // No pointers to new space, marked executable. - MAP_SPACE, // Only and all map objects. - CELL_SPACE, // Only and all cell objects. - LO_SPACE, // Promoted large objects. - - FIRST_SPACE = NEW_SPACE, - LAST_SPACE = LO_SPACE, - FIRST_PAGED_SPACE = OLD_POINTER_SPACE, - LAST_PAGED_SPACE = CELL_SPACE -}; -const int kSpaceTagSize = 3; -const int kSpaceTagMask = (1 << kSpaceTagSize) - 1; - - -// A flag that indicates whether objects should be pretenured when -// allocated (allocated directly into the old generation) or not -// (allocated in the young generation if the object size and type -// allows). -enum PretenureFlag { NOT_TENURED, TENURED }; - -enum GarbageCollector { SCAVENGER, MARK_COMPACTOR }; - -enum Executability { NOT_EXECUTABLE, EXECUTABLE }; - -enum VisitMode { VISIT_ALL, VISIT_ALL_IN_SCAVENGE, VISIT_ONLY_STRONG }; - -// Flag indicating whether code is built into the VM (one of the natives files). -enum NativesFlag { NOT_NATIVES_CODE, NATIVES_CODE }; - - -// A CodeDesc describes a buffer holding instructions and relocation -// information. The instructions start at the beginning of the buffer -// and grow forward, the relocation information starts at the end of -// the buffer and grows backward. -// -// |<--------------- buffer_size ---------------->| -// |<-- instr_size -->| |<-- reloc_size -->| -// +==================+========+==================+ -// | instructions | free | reloc info | -// +==================+========+==================+ -// ^ -// | -// buffer - -struct CodeDesc { - byte* buffer; - int buffer_size; - int instr_size; - int reloc_size; - Assembler* origin; -}; - - -// Callback function on object slots, used for iterating heap object slots in -// HeapObjects, global pointers to heap objects, etc. The callback allows the -// callback function to change the value of the slot. -typedef void (*ObjectSlotCallback)(HeapObject** pointer); - - -// Callback function used for iterating objects in heap spaces, -// for example, scanning heap objects. -typedef int (*HeapObjectCallback)(HeapObject* obj); - - -// Callback function used for checking constraints when copying/relocating -// objects. Returns true if an object can be copied/relocated from its -// old_addr to a new_addr. -typedef bool (*ConstraintCallback)(Address new_addr, Address old_addr); - - -// Callback function on inline caches, used for iterating over inline caches -// in compiled code. -typedef void (*InlineCacheCallback)(Code* code, Address ic); - - -// State for inline cache call sites. Aliased as IC::State. -enum InlineCacheState { - // Has never been executed. - UNINITIALIZED, - // Has been executed but monomorhic state has been delayed. - PREMONOMORPHIC, - // Has been executed and only one receiver type has been seen. - MONOMORPHIC, - // Like MONOMORPHIC but check failed due to prototype. - MONOMORPHIC_PROTOTYPE_FAILURE, - // Multiple receiver types have been seen. - MEGAMORPHIC, - // Special states for debug break or step in prepare stubs. - DEBUG_BREAK, - DEBUG_PREPARE_STEP_IN -}; - - -enum InLoopFlag { - NOT_IN_LOOP, - IN_LOOP -}; - - -enum CallFunctionFlags { - NO_CALL_FUNCTION_FLAGS = 0, - RECEIVER_MIGHT_BE_VALUE = 1 << 0 // Receiver might not be a JSObject. -}; - - -enum InlineCacheHolderFlag { - OWN_MAP, // For fast properties objects. - PROTOTYPE_MAP // For slow properties objects (except GlobalObjects). -}; - - -// Type of properties. -// Order of properties is significant. -// Must fit in the BitField PropertyDetails::TypeField. -// A copy of this is in mirror-debugger.js. -enum PropertyType { - NORMAL = 0, // only in slow mode - FIELD = 1, // only in fast mode - CONSTANT_FUNCTION = 2, // only in fast mode - CALLBACKS = 3, - INTERCEPTOR = 4, // only in lookup results, not in descriptors. - MAP_TRANSITION = 5, // only in fast mode - CONSTANT_TRANSITION = 6, // only in fast mode - NULL_DESCRIPTOR = 7, // only in fast mode - // All properties before MAP_TRANSITION are real. - FIRST_PHANTOM_PROPERTY_TYPE = MAP_TRANSITION, - // There are no IC stubs for NULL_DESCRIPTORS. Therefore, - // NULL_DESCRIPTOR can be used as the type flag for IC stubs for - // nonexistent properties. - NONEXISTENT = NULL_DESCRIPTOR -}; - - -// Whether to remove map transitions and constant transitions from a -// DescriptorArray. -enum TransitionFlag { - REMOVE_TRANSITIONS, - KEEP_TRANSITIONS -}; - - -// Union used for fast testing of specific double values. -union DoubleRepresentation { - double value; - int64_t bits; - DoubleRepresentation(double x) { value = x; } -}; - - -// Union used for customized checking of the IEEE double types -// inlined within v8 runtime, rather than going to the underlying -// platform headers and libraries -union IeeeDoubleLittleEndianArchType { - double d; - struct { - unsigned int man_low :32; - unsigned int man_high :20; - unsigned int exp :11; - unsigned int sign :1; - } bits; -}; - - -union IeeeDoubleBigEndianArchType { - double d; - struct { - unsigned int sign :1; - unsigned int exp :11; - unsigned int man_high :20; - unsigned int man_low :32; - } bits; -}; - - -// AccessorCallback -struct AccessorDescriptor { - MaybeObject* (*getter)(Object* object, void* data); - MaybeObject* (*setter)(JSObject* object, Object* value, void* data); - void* data; -}; - - -// Logging and profiling. -// A StateTag represents a possible state of the VM. When compiled with -// ENABLE_VMSTATE_TRACKING, the logger maintains a stack of these. -// Creating a VMState object enters a state by pushing on the stack, and -// destroying a VMState object leaves a state by popping the current state -// from the stack. - -#define STATE_TAG_LIST(V) \ - V(JS) \ - V(GC) \ - V(COMPILER) \ - V(OTHER) \ - V(EXTERNAL) - -enum StateTag { -#define DEF_STATE_TAG(name) name, - STATE_TAG_LIST(DEF_STATE_TAG) -#undef DEF_STATE_TAG - // Pseudo-types. - state_tag_count -}; - - -// ----------------------------------------------------------------------------- -// Macros - -// Testers for test. - -#define HAS_SMI_TAG(value) \ - ((reinterpret_cast<intptr_t>(value) & kSmiTagMask) == kSmiTag) - -#define HAS_FAILURE_TAG(value) \ - ((reinterpret_cast<intptr_t>(value) & kFailureTagMask) == kFailureTag) - -// OBJECT_POINTER_ALIGN returns the value aligned as a HeapObject pointer -#define OBJECT_POINTER_ALIGN(value) \ - (((value) + kObjectAlignmentMask) & ~kObjectAlignmentMask) - -// POINTER_SIZE_ALIGN returns the value aligned as a pointer. -#define POINTER_SIZE_ALIGN(value) \ - (((value) + kPointerAlignmentMask) & ~kPointerAlignmentMask) - -// MAP_POINTER_ALIGN returns the value aligned as a map pointer. -#define MAP_POINTER_ALIGN(value) \ - (((value) + kMapAlignmentMask) & ~kMapAlignmentMask) - -// CODE_POINTER_ALIGN returns the value aligned as a generated code segment. -#define CODE_POINTER_ALIGN(value) \ - (((value) + kCodeAlignmentMask) & ~kCodeAlignmentMask) - // The expression OFFSET_OF(type, field) computes the byte-offset // of the specified field relative to the containing type. This // corresponds to 'offsetof' (in stddef.h), except that it doesn't @@ -669,26 +279,6 @@ F FUNCTION_CAST(Address addr) { DISALLOW_COPY_AND_ASSIGN(TypeName) -// Support for tracking C++ memory allocation. Insert TRACK_MEMORY("Fisk") -// inside a C++ class and new and delete will be overloaded so logging is -// performed. -// This file (globals.h) is included before log.h, so we use direct calls to -// the Logger rather than the LOG macro. -#ifdef DEBUG -#define TRACK_MEMORY(name) \ - void* operator new(size_t size) { \ - void* result = ::operator new(size); \ - Logger::NewEvent(name, result, size); \ - return result; \ - } \ - void operator delete(void* object) { \ - Logger::DeleteEvent(name, object); \ - ::operator delete(object); \ - } -#else -#define TRACK_MEMORY(name) -#endif - // Define used for helping GCC to make better inlining. Don't bother for debug // builds. On GCC 3.4.5 using __attribute__((always_inline)) causes compilation // errors in debug build. @@ -712,20 +302,12 @@ F FUNCTION_CAST(Address addr) { #define MUST_USE_RESULT #endif +// ----------------------------------------------------------------------------- +// Forward declarations for frequently used classes +// (sorted alphabetically) -// Feature flags bit positions. They are mostly based on the CPUID spec. -// (We assign CPUID itself to one of the currently reserved bits -- -// feel free to change this if needed.) -// On X86/X64, values below 32 are bits in EDX, values above 32 are bits in ECX. -enum CpuFeature { SSE4_1 = 32 + 19, // x86 - SSE3 = 32 + 0, // x86 - SSE2 = 26, // x86 - CMOV = 15, // x86 - RDTSC = 4, // x86 - CPUID = 10, // x86 - VFP3 = 1, // ARM - ARMv7 = 2, // ARM - SAHF = 0}; // x86 +class FreeStoreAllocationPolicy; +template <typename T, class P = FreeStoreAllocationPolicy> class List; } } // namespace v8::internal diff --git a/deps/v8/src/handles.cc b/deps/v8/src/handles.cc index 7a46bc3ea2..1364951b71 100644 --- a/deps/v8/src/handles.cc +++ b/deps/v8/src/handles.cc @@ -499,6 +499,10 @@ void InitScriptLineEnds(Handle<Script> script) { Handle<FixedArray> array = CalculateLineEnds(src, true); + if (*array != Heap::empty_fixed_array()) { + array->set_map(Heap::fixed_cow_array_map()); + } + script->set_line_ends(*array); ASSERT(script->line_ends()->IsFixedArray()); } diff --git a/deps/v8/src/heap-inl.h b/deps/v8/src/heap-inl.h index 15feb9d5fb..ba50c0f735 100644 --- a/deps/v8/src/heap-inl.h +++ b/deps/v8/src/heap-inl.h @@ -330,6 +330,11 @@ void Heap::ScavengeObject(HeapObject** p, HeapObject* object) { } +bool Heap::CollectGarbage(AllocationSpace space) { + return CollectGarbage(space, SelectGarbageCollector(space)); +} + + MaybeObject* Heap::PrepareForCompare(String* str) { // Always flatten small strings and force flattening of long strings // after we have accumulated a certain amount we failed to flatten. @@ -413,7 +418,7 @@ void Heap::SetLastScriptId(Object* last_script_id) { } \ if (!__maybe_object__->IsRetryAfterGC()) RETURN_EMPTY; \ Counters::gc_last_resort_from_handles.Increment(); \ - Heap::CollectAllGarbage(false); \ + Heap::CollectAllAvailableGarbage(); \ { \ AlwaysAllocateScope __scope__; \ __maybe_object__ = FUNCTION_CALL; \ diff --git a/deps/v8/src/heap-profiler.cc b/deps/v8/src/heap-profiler.cc index e47d66f984..d3cc4ab910 100644 --- a/deps/v8/src/heap-profiler.cc +++ b/deps/v8/src/heap-profiler.cc @@ -788,15 +788,13 @@ void AggregatedHeapSnapshotGenerator::CalculateStringsStats() { void AggregatedHeapSnapshotGenerator::CollectStats(HeapObject* obj) { InstanceType type = obj->map()->instance_type(); ASSERT(0 <= type && type <= LAST_TYPE); - if (!FreeListNode::IsFreeListNode(obj)) { - agg_snapshot_->info()[type].increment_number(1); - agg_snapshot_->info()[type].increment_bytes(obj->Size()); - } + agg_snapshot_->info()[type].increment_number(1); + agg_snapshot_->info()[type].increment_bytes(obj->Size()); } void AggregatedHeapSnapshotGenerator::GenerateSnapshot() { - HeapIterator iterator; + HeapIterator iterator(HeapIterator::kPreciseFiltering); for (HeapObject* obj = iterator.next(); obj != NULL; obj = iterator.next()) { CollectStats(obj); agg_snapshot_->js_cons_profile()->CollectStats(obj); diff --git a/deps/v8/src/heap.cc b/deps/v8/src/heap.cc index b037efd804..26859d7c07 100644 --- a/deps/v8/src/heap.cc +++ b/deps/v8/src/heap.cc @@ -38,7 +38,7 @@ #include "mark-compact.h" #include "natives.h" #include "objects-visiting.h" -#include "scanner.h" +#include "scanner-base.h" #include "scopeinfo.h" #include "snapshot.h" #include "v8threads.h" @@ -79,20 +79,32 @@ int Heap::amount_of_external_allocated_memory_at_last_global_gc_ = 0; // semispace_size_ should be a power of 2 and old_generation_size_ should be // a multiple of Page::kPageSize. #if defined(ANDROID) -int Heap::max_semispace_size_ = 2*MB; +static const int default_max_semispace_size_ = 2*MB; intptr_t Heap::max_old_generation_size_ = 192*MB; int Heap::initial_semispace_size_ = 128*KB; intptr_t Heap::code_range_size_ = 0; +intptr_t Heap::max_executable_size_ = max_old_generation_size_; #elif defined(V8_TARGET_ARCH_X64) -int Heap::max_semispace_size_ = 16*MB; +static const int default_max_semispace_size_ = 16*MB; intptr_t Heap::max_old_generation_size_ = 1*GB; int Heap::initial_semispace_size_ = 1*MB; intptr_t Heap::code_range_size_ = 512*MB; +intptr_t Heap::max_executable_size_ = 256*MB; #else -int Heap::max_semispace_size_ = 8*MB; +static const int default_max_semispace_size_ = 8*MB; intptr_t Heap::max_old_generation_size_ = 512*MB; int Heap::initial_semispace_size_ = 512*KB; intptr_t Heap::code_range_size_ = 0; +intptr_t Heap::max_executable_size_ = 128*MB; +#endif + +// Allow build-time customization of the max semispace size. Building +// V8 with snapshots and a non-default max semispace size is much +// easier if you can define it as part of the build environment. +#if defined(V8_MAX_SEMISPACE_SIZE) +int Heap::max_semispace_size_ = V8_MAX_SEMISPACE_SIZE; +#else +int Heap::max_semispace_size_ = default_max_semispace_size_; #endif // The snapshot semispace size will be the default semispace size if @@ -172,6 +184,12 @@ intptr_t Heap::CommittedMemory() { lo_space_->Size(); } +intptr_t Heap::CommittedMemoryExecutable() { + if (!HasBeenSetup()) return 0; + + return MemoryAllocator::SizeExecutable(); +} + intptr_t Heap::Available() { if (!HasBeenSetup()) return 0; @@ -386,7 +404,7 @@ intptr_t Heap::SizeOfObjects() { intptr_t total = 0; AllSpaces spaces; for (Space* space = spaces.next(); space != NULL; space = spaces.next()) { - total += space->Size(); + total += space->SizeOfObjects(); } return total; } @@ -429,7 +447,31 @@ void Heap::CollectAllGarbage(bool force_compaction) { } -void Heap::CollectGarbage(AllocationSpace space) { +void Heap::CollectAllAvailableGarbage() { + // Since we are ignoring the return value, the exact choice of space does + // not matter, so long as we do not specify NEW_SPACE, which would not + // cause a full GC. + MarkCompactCollector::SetForceCompaction(true); + + // Major GC would invoke weak handle callbacks on weakly reachable + // handles, but won't collect weakly reachable objects until next + // major GC. Therefore if we collect aggressively and weak handle callback + // has been invoked, we rerun major GC to release objects which become + // garbage. + // Note: as weak callbacks can execute arbitrary code, we cannot + // hope that eventually there will be no weak callbacks invocations. + // Therefore stop recollecting after several attempts. + const int kMaxNumberOfAttempts = 7; + for (int attempt = 0; attempt < kMaxNumberOfAttempts; attempt++) { + if (!CollectGarbage(OLD_POINTER_SPACE, MARK_COMPACTOR)) { + break; + } + } + MarkCompactCollector::SetForceCompaction(false); +} + + +bool Heap::CollectGarbage(AllocationSpace space, GarbageCollector collector) { // The VM is in the GC state until exiting this function. VMState state(GC); @@ -442,13 +484,14 @@ void Heap::CollectGarbage(AllocationSpace space) { allocation_timeout_ = Max(6, FLAG_gc_interval); #endif + bool next_gc_likely_to_collect_more = false; + { GCTracer tracer; GarbageCollectionPrologue(); // The GC count was incremented in the prologue. Tell the tracer about // it. tracer.set_gc_count(gc_count_); - GarbageCollector collector = SelectGarbageCollector(space); // Tell the tracer which collector we've selected. tracer.set_collector(collector); @@ -456,7 +499,8 @@ void Heap::CollectGarbage(AllocationSpace space) { ? &Counters::gc_scavenger : &Counters::gc_compactor; rate->Start(); - PerformGarbageCollection(collector, &tracer); + next_gc_likely_to_collect_more = + PerformGarbageCollection(collector, &tracer); rate->Stop(); GarbageCollectionEpilogue(); @@ -467,6 +511,8 @@ void Heap::CollectGarbage(AllocationSpace space) { if (FLAG_log_gc) HeapProfiler::WriteSample(); if (CpuProfiler::is_profiling()) CpuProfiler::ProcessMovedFunctions(); #endif + + return next_gc_likely_to_collect_more; } @@ -653,8 +699,10 @@ void Heap::UpdateSurvivalRateTrend(int start_new_space_size) { survival_rate_ = survival_rate; } -void Heap::PerformGarbageCollection(GarbageCollector collector, +bool Heap::PerformGarbageCollection(GarbageCollector collector, GCTracer* tracer) { + bool next_gc_likely_to_collect_more = false; + if (collector != SCAVENGER) { PROFILE(CodeMovingGCEvent()); } @@ -720,7 +768,8 @@ void Heap::PerformGarbageCollection(GarbageCollector collector, if (collector == MARK_COMPACTOR) { DisableAssertNoAllocation allow_allocation; GCTracer::Scope scope(tracer, GCTracer::Scope::EXTERNAL); - GlobalHandles::PostGarbageCollectionProcessing(); + next_gc_likely_to_collect_more = + GlobalHandles::PostGarbageCollectionProcessing(); } // Update relocatables. @@ -747,6 +796,8 @@ void Heap::PerformGarbageCollection(GarbageCollector collector, global_gc_epilogue_callback_(); } VerifySymbolTable(); + + return next_gc_likely_to_collect_more; } @@ -3198,7 +3249,8 @@ MaybeObject* Heap::AllocateStringFromUtf8(Vector<const char> string, const uc32 kMaxSupportedChar = 0xFFFF; // Count the number of characters in the UTF-8 string and check if // it is an ASCII string. - Access<Scanner::Utf8Decoder> decoder(Scanner::utf8_decoder()); + Access<ScannerConstants::Utf8Decoder> + decoder(ScannerConstants::utf8_decoder()); decoder->Reset(string.start(), string.length()); int chars = 0; bool is_ascii = true; @@ -4280,7 +4332,9 @@ static bool heap_configured = false; // TODO(1236194): Since the heap size is configurable on the command line // and through the API, we should gracefully handle the case that the heap // size is not big enough to fit all the initial objects. -bool Heap::ConfigureHeap(int max_semispace_size, int max_old_gen_size) { +bool Heap::ConfigureHeap(int max_semispace_size, + int max_old_gen_size, + int max_executable_size) { if (HasBeenSetup()) return false; if (max_semispace_size > 0) max_semispace_size_ = max_semispace_size; @@ -4301,6 +4355,15 @@ bool Heap::ConfigureHeap(int max_semispace_size, int max_old_gen_size) { } if (max_old_gen_size > 0) max_old_generation_size_ = max_old_gen_size; + if (max_executable_size > 0) { + max_executable_size_ = RoundUp(max_executable_size, Page::kPageSize); + } + + // The max executable size must be less than or equal to the max old + // generation size. + if (max_executable_size_ > max_old_generation_size_) { + max_executable_size_ = max_old_generation_size_; + } // The new space size must be a power of two to support single-bit testing // for containment. @@ -4318,8 +4381,9 @@ bool Heap::ConfigureHeap(int max_semispace_size, int max_old_gen_size) { bool Heap::ConfigureHeapDefault() { - return ConfigureHeap( - FLAG_max_new_space_size * (KB / 2), FLAG_max_old_space_size * MB); + return ConfigureHeap(FLAG_max_new_space_size / 2 * KB, + FLAG_max_old_space_size * MB, + FLAG_max_executable_size * MB); } @@ -4345,13 +4409,10 @@ void Heap::RecordStats(HeapStats* stats, bool take_snapshot) { MemoryAllocator::Size() + MemoryAllocator::Available(); *stats->os_error = OS::GetLastError(); if (take_snapshot) { - HeapIterator iterator; + HeapIterator iterator(HeapIterator::kPreciseFiltering); for (HeapObject* obj = iterator.next(); obj != NULL; obj = iterator.next()) { - // Note: snapshot won't be precise because IsFreeListNode returns true - // for any bytearray. - if (FreeListNode::IsFreeListNode(obj)) continue; InstanceType type = obj->map()->instance_type(); ASSERT(0 <= type && type <= LAST_TYPE); stats->objects_per_type[type]++; @@ -4402,7 +4463,7 @@ bool Heap::Setup(bool create_heap_objects) { // space. The chunk is double the size of the requested reserved // new space size to ensure that we can find a pair of semispaces that // are contiguous and aligned to their size. - if (!MemoryAllocator::Setup(MaxReserved())) return false; + if (!MemoryAllocator::Setup(MaxReserved(), MaxExecutableSize())) return false; void* chunk = MemoryAllocator::ReserveInitialChunk(4 * reserved_semispace_size_); if (chunk == NULL) return false; @@ -4706,7 +4767,17 @@ OldSpace* OldSpaces::next() { } -SpaceIterator::SpaceIterator() : current_space_(FIRST_SPACE), iterator_(NULL) { +SpaceIterator::SpaceIterator() + : current_space_(FIRST_SPACE), + iterator_(NULL), + size_func_(NULL) { +} + + +SpaceIterator::SpaceIterator(HeapObjectCallback size_func) + : current_space_(FIRST_SPACE), + iterator_(NULL), + size_func_(size_func) { } @@ -4744,25 +4815,25 @@ ObjectIterator* SpaceIterator::CreateIterator() { switch (current_space_) { case NEW_SPACE: - iterator_ = new SemiSpaceIterator(Heap::new_space()); + iterator_ = new SemiSpaceIterator(Heap::new_space(), size_func_); break; case OLD_POINTER_SPACE: - iterator_ = new HeapObjectIterator(Heap::old_pointer_space()); + iterator_ = new HeapObjectIterator(Heap::old_pointer_space(), size_func_); break; case OLD_DATA_SPACE: - iterator_ = new HeapObjectIterator(Heap::old_data_space()); + iterator_ = new HeapObjectIterator(Heap::old_data_space(), size_func_); break; case CODE_SPACE: - iterator_ = new HeapObjectIterator(Heap::code_space()); + iterator_ = new HeapObjectIterator(Heap::code_space(), size_func_); break; case MAP_SPACE: - iterator_ = new HeapObjectIterator(Heap::map_space()); + iterator_ = new HeapObjectIterator(Heap::map_space(), size_func_); break; case CELL_SPACE: - iterator_ = new HeapObjectIterator(Heap::cell_space()); + iterator_ = new HeapObjectIterator(Heap::cell_space(), size_func_); break; case LO_SPACE: - iterator_ = new LargeObjectIterator(Heap::lo_space()); + iterator_ = new LargeObjectIterator(Heap::lo_space(), size_func_); break; } @@ -4772,7 +4843,54 @@ ObjectIterator* SpaceIterator::CreateIterator() { } -HeapIterator::HeapIterator() { +class FreeListNodesFilter { + public: + FreeListNodesFilter() { + MarkFreeListNodes(); + } + + inline bool IsFreeListNode(HeapObject* object) { + if (object->IsMarked()) { + object->ClearMark(); + return true; + } else { + return false; + } + } + + private: + void MarkFreeListNodes() { + Heap::old_pointer_space()->MarkFreeListNodes(); + Heap::old_data_space()->MarkFreeListNodes(); + MarkCodeSpaceFreeListNodes(); + Heap::map_space()->MarkFreeListNodes(); + Heap::cell_space()->MarkFreeListNodes(); + } + + void MarkCodeSpaceFreeListNodes() { + // For code space, using FreeListNode::IsFreeListNode is OK. + HeapObjectIterator iter(Heap::code_space()); + for (HeapObject* obj = iter.next_object(); + obj != NULL; + obj = iter.next_object()) { + if (FreeListNode::IsFreeListNode(obj)) obj->SetMark(); + } + } + + AssertNoAllocation no_alloc; +}; + + +HeapIterator::HeapIterator() + : filtering_(HeapIterator::kNoFiltering), + filter_(NULL) { + Init(); +} + + +HeapIterator::HeapIterator(HeapIterator::FreeListNodesFiltering filtering) + : filtering_(filtering), + filter_(NULL) { Init(); } @@ -4784,20 +4902,44 @@ HeapIterator::~HeapIterator() { void HeapIterator::Init() { // Start the iteration. - space_iterator_ = new SpaceIterator(); + if (filtering_ == kPreciseFiltering) { + filter_ = new FreeListNodesFilter; + space_iterator_ = + new SpaceIterator(MarkCompactCollector::SizeOfMarkedObject); + } else { + space_iterator_ = new SpaceIterator; + } object_iterator_ = space_iterator_->next(); } void HeapIterator::Shutdown() { +#ifdef DEBUG + // Assert that in precise mode we have iterated through all + // objects. Otherwise, heap will be left in an inconsistent state. + if (filtering_ == kPreciseFiltering) { + ASSERT(object_iterator_ == NULL); + } +#endif // Make sure the last iterator is deallocated. delete space_iterator_; space_iterator_ = NULL; object_iterator_ = NULL; + delete filter_; + filter_ = NULL; } HeapObject* HeapIterator::next() { + if (filter_ == NULL) return NextObject(); + + HeapObject* obj = NextObject(); + while (obj != NULL && filter_->IsFreeListNode(obj)) obj = NextObject(); + return obj; +} + + +HeapObject* HeapIterator::NextObject() { // No iterator means we are done. if (object_iterator_ == NULL) return NULL; diff --git a/deps/v8/src/heap.h b/deps/v8/src/heap.h index 8ff2f5f341..93caf3bd4e 100644 --- a/deps/v8/src/heap.h +++ b/deps/v8/src/heap.h @@ -222,7 +222,9 @@ class Heap : public AllStatic { public: // Configure heap size before setup. Return false if the heap has been // setup already. - static bool ConfigureHeap(int max_semispace_size, int max_old_gen_size); + static bool ConfigureHeap(int max_semispace_size, + int max_old_gen_size, + int max_executable_size); static bool ConfigureHeapDefault(); // Initializes the global object heap. If create_heap_objects is true, @@ -253,6 +255,7 @@ class Heap : public AllStatic { static int ReservedSemiSpaceSize() { return reserved_semispace_size_; } static int InitialSemiSpaceSize() { return initial_semispace_size_; } static intptr_t MaxOldGenerationSize() { return max_old_generation_size_; } + static intptr_t MaxExecutableSize() { return max_executable_size_; } // Returns the capacity of the heap in bytes w/o growing. Heap grows when // more spaces are needed until it reaches the limit. @@ -261,6 +264,9 @@ class Heap : public AllStatic { // Returns the amount of memory currently committed for the heap. static intptr_t CommittedMemory(); + // Returns the amount of executable memory currently committed for the heap. + static intptr_t CommittedMemoryExecutable(); + // Returns the available bytes in space w/o growing. // Heap doesn't guarantee that it can allocate an object that requires // all available bytes. Check MaxHeapObjectSize() instead. @@ -705,13 +711,22 @@ class Heap : public AllStatic { static void GarbageCollectionEpilogue(); // Performs garbage collection operation. - // Returns whether required_space bytes are available after the collection. - static void CollectGarbage(AllocationSpace space); + // Returns whether there is a chance that another major GC could + // collect more garbage. + static bool CollectGarbage(AllocationSpace space, GarbageCollector collector); + + // Performs garbage collection operation. + // Returns whether there is a chance that another major GC could + // collect more garbage. + inline static bool CollectGarbage(AllocationSpace space); // Performs a full garbage collection. Force compaction if the // parameter is true. static void CollectAllGarbage(bool force_compaction); + // Last hope GC, should try to squeeze as much as possible. + static void CollectAllAvailableGarbage(); + // Notify the heap that a context has been disposed. static int NotifyContextDisposed() { return ++contexts_disposed_; } @@ -1087,6 +1102,7 @@ class Heap : public AllStatic { static int max_semispace_size_; static int initial_semispace_size_; static intptr_t max_old_generation_size_; + static intptr_t max_executable_size_; static intptr_t code_range_size_; // For keeping track of how much data has survived @@ -1246,7 +1262,9 @@ class Heap : public AllStatic { static GarbageCollector SelectGarbageCollector(AllocationSpace space); // Performs garbage collection - static void PerformGarbageCollection(GarbageCollector collector, + // Returns whether there is a chance another major GC could + // collect more garbage. + static bool PerformGarbageCollection(GarbageCollector collector, GCTracer* tracer); // Allocate an uninitialized object in map space. The behavior is identical @@ -1540,6 +1558,7 @@ class PagedSpaces BASE_EMBEDDED { class SpaceIterator : public Malloced { public: SpaceIterator(); + explicit SpaceIterator(HeapObjectCallback size_func); virtual ~SpaceIterator(); bool has_next(); @@ -1550,17 +1569,31 @@ class SpaceIterator : public Malloced { int current_space_; // from enum AllocationSpace. ObjectIterator* iterator_; // object iterator for the current space. + HeapObjectCallback size_func_; }; -// A HeapIterator provides iteration over the whole heap It aggregates a the -// specific iterators for the different spaces as these can only iterate over -// one space only. +// A HeapIterator provides iteration over the whole heap. It +// aggregates the specific iterators for the different spaces as +// these can only iterate over one space only. +// +// HeapIterator can skip free list nodes (that is, de-allocated heap +// objects that still remain in the heap). As implementation of free +// nodes filtering uses GC marks, it can't be used during MS/MC GC +// phases. Also, it is forbidden to interrupt iteration in this mode, +// as this will leave heap objects marked (and thus, unusable). +class FreeListNodesFilter; class HeapIterator BASE_EMBEDDED { public: - explicit HeapIterator(); - virtual ~HeapIterator(); + enum FreeListNodesFiltering { + kNoFiltering, + kPreciseFiltering + }; + + HeapIterator(); + explicit HeapIterator(FreeListNodesFiltering filtering); + ~HeapIterator(); HeapObject* next(); void reset(); @@ -1568,10 +1601,12 @@ class HeapIterator BASE_EMBEDDED { private: // Perform the initialization. void Init(); - // Perform all necessary shutdown (destruction) work. void Shutdown(); + HeapObject* NextObject(); + FreeListNodesFiltering filtering_; + FreeListNodesFilter* filter_; // Space iterator for iterating all the spaces. SpaceIterator* space_iterator_; // Object iterator for the space currently being iterated. diff --git a/deps/v8/src/ia32/assembler-ia32.cc b/deps/v8/src/ia32/assembler-ia32.cc index 019f478adc..125f503bec 100644 --- a/deps/v8/src/ia32/assembler-ia32.cc +++ b/deps/v8/src/ia32/assembler-ia32.cc @@ -298,7 +298,8 @@ static void InitCoverageLog(); // Spare buffer. byte* Assembler::spare_buffer_ = NULL; -Assembler::Assembler(void* buffer, int buffer_size) { +Assembler::Assembler(void* buffer, int buffer_size) + : positions_recorder_(this) { if (buffer == NULL) { // Do our own buffer management. if (buffer_size <= kMinimalBufferSize) { @@ -339,10 +340,6 @@ Assembler::Assembler(void* buffer, int buffer_size) { reloc_info_writer.Reposition(buffer_ + buffer_size, pc_); last_pc_ = NULL; - current_statement_position_ = RelocInfo::kNoPosition; - current_position_ = RelocInfo::kNoPosition; - written_statement_position_ = current_statement_position_; - written_position_ = current_position_; #ifdef GENERATED_CODE_COVERAGE InitCoverageLog(); #endif @@ -1581,7 +1578,7 @@ void Assembler::call(const Operand& adr) { void Assembler::call(Handle<Code> code, RelocInfo::Mode rmode) { - WriteRecordedPositions(); + positions_recorder()->WriteRecordedPositions(); EnsureSpace ensure_space(this); last_pc_ = pc_; ASSERT(RelocInfo::IsCodeTarget(rmode)); @@ -2464,14 +2461,14 @@ void Assembler::Print() { void Assembler::RecordJSReturn() { - WriteRecordedPositions(); + positions_recorder()->WriteRecordedPositions(); EnsureSpace ensure_space(this); RecordRelocInfo(RelocInfo::JS_RETURN); } void Assembler::RecordDebugBreakSlot() { - WriteRecordedPositions(); + positions_recorder()->WriteRecordedPositions(); EnsureSpace ensure_space(this); RecordRelocInfo(RelocInfo::DEBUG_BREAK_SLOT); } @@ -2485,47 +2482,6 @@ void Assembler::RecordComment(const char* msg) { } -void Assembler::RecordPosition(int pos) { - ASSERT(pos != RelocInfo::kNoPosition); - ASSERT(pos >= 0); - current_position_ = pos; -} - - -void Assembler::RecordStatementPosition(int pos) { - ASSERT(pos != RelocInfo::kNoPosition); - ASSERT(pos >= 0); - current_statement_position_ = pos; -} - - -bool Assembler::WriteRecordedPositions() { - bool written = false; - - // Write the statement position if it is different from what was written last - // time. - if (current_statement_position_ != written_statement_position_) { - EnsureSpace ensure_space(this); - RecordRelocInfo(RelocInfo::STATEMENT_POSITION, current_statement_position_); - written_statement_position_ = current_statement_position_; - written = true; - } - - // Write the position if it is different from what was written last time and - // also different from the written statement position. - if (current_position_ != written_position_ && - current_position_ != written_statement_position_) { - EnsureSpace ensure_space(this); - RecordRelocInfo(RelocInfo::POSITION, current_position_); - written_position_ = current_position_; - written = true; - } - - // Return whether something was written. - return written; -} - - void Assembler::GrowBuffer() { ASSERT(overflow()); if (!own_buffer_) FATAL("external code buffer is too small"); diff --git a/deps/v8/src/ia32/assembler-ia32.h b/deps/v8/src/ia32/assembler-ia32.h index 5286788fa7..79637a1901 100644 --- a/deps/v8/src/ia32/assembler-ia32.h +++ b/deps/v8/src/ia32/assembler-ia32.h @@ -521,7 +521,6 @@ class Assembler : public Malloced { void push(const Immediate& x); void push(Register src); void push(const Operand& src); - void push(Label* label, RelocInfo::Mode relocation_mode); void pop(Register dst); void pop(const Operand& dst); @@ -847,17 +846,11 @@ class Assembler : public Malloced { // Use --debug_code to enable. void RecordComment(const char* msg); - void RecordPosition(int pos); - void RecordStatementPosition(int pos); - bool WriteRecordedPositions(); - // Writes a single word of data in the code stream. // Used for inline tables, e.g., jump-tables. void dd(uint32_t data, RelocInfo::Mode reloc_info); int pc_offset() const { return pc_ - buffer_; } - int current_statement_position() const { return current_statement_position_; } - int current_position() const { return current_position_; } // Check if there is less than kGap bytes available in the buffer. // If this is the case, we need to grow the buffer before emitting @@ -869,6 +862,8 @@ class Assembler : public Malloced { static bool IsNop(Address addr) { return *addr == 0x90; } + PositionsRecorder* positions_recorder() { return &positions_recorder_; } + // Avoid overflows for displacements etc. static const int kMaximalBufferSize = 512*MB; static const int kMinimalBufferSize = 4*KB; @@ -947,11 +942,9 @@ class Assembler : public Malloced { // push-pop elimination byte* last_pc_; - // source position information - int current_statement_position_; - int current_position_; - int written_statement_position_; - int written_position_; + PositionsRecorder positions_recorder_; + + friend class PositionsRecorder; }; diff --git a/deps/v8/src/ia32/code-stubs-ia32.cc b/deps/v8/src/ia32/code-stubs-ia32.cc index b2b73926b9..6ac89bfbac 100644 --- a/deps/v8/src/ia32/code-stubs-ia32.cc +++ b/deps/v8/src/ia32/code-stubs-ia32.cc @@ -3058,15 +3058,6 @@ void CEntryStub::GenerateThrowTOS(MacroAssembler* masm) { } -void ApiGetterEntryStub::Generate(MacroAssembler* masm) { - __ PrepareCallApiFunction(kStackSpace, kArgc); - STATIC_ASSERT(kArgc == 2); - __ mov(ApiParameterOperand(0), ebx); // name. - __ mov(ApiParameterOperand(1), eax); // arguments pointer. - __ CallApiFunctionAndReturn(fun(), kArgc); -} - - void CEntryStub::GenerateCore(MacroAssembler* masm, Label* throw_normal_exception, Label* throw_termination_exception, diff --git a/deps/v8/src/ia32/codegen-ia32.cc b/deps/v8/src/ia32/codegen-ia32.cc index 6d23dd7df9..fb7a138d5c 100644 --- a/deps/v8/src/ia32/codegen-ia32.cc +++ b/deps/v8/src/ia32/codegen-ia32.cc @@ -154,7 +154,7 @@ CodeGenerator::CodeGenerator(MacroAssembler* masm) safe_int32_mode_enabled_(true), function_return_is_shadowed_(false), in_spilled_code_(false), - jit_cookie_((FLAG_mask_constants_with_cookie) ? V8::Random() : 0) { + jit_cookie_((FLAG_mask_constants_with_cookie) ? V8::RandomPrivate() : 0) { } @@ -686,10 +686,10 @@ void CodeGenerator::Load(Expression* expr) { void CodeGenerator::LoadGlobal() { if (in_spilled_code()) { - frame_->EmitPush(GlobalObject()); + frame_->EmitPush(GlobalObjectOperand()); } else { Result temp = allocator_->Allocate(); - __ mov(temp.reg(), GlobalObject()); + __ mov(temp.reg(), GlobalObjectOperand()); frame_->Push(&temp); } } @@ -698,7 +698,7 @@ void CodeGenerator::LoadGlobal() { void CodeGenerator::LoadGlobalReceiver() { Result temp = allocator_->Allocate(); Register reg = temp.reg(); - __ mov(reg, GlobalObject()); + __ mov(reg, GlobalObjectOperand()); __ mov(reg, FieldOperand(reg, GlobalObject::kGlobalReceiverOffset)); frame_->Push(&temp); } @@ -3734,7 +3734,7 @@ void CodeGenerator::VisitReturnStatement(ReturnStatement* node) { CodeForStatementPosition(node); Load(node->expression()); Result return_value = frame_->Pop(); - masm()->WriteRecordedPositions(); + masm()->positions_recorder()->WriteRecordedPositions(); if (function_return_is_shadowed_) { function_return_.Jump(&return_value); } else { @@ -6294,6 +6294,18 @@ void CodeGenerator::VisitCall(Call* node) { // Push the receiver onto the frame. Load(property->obj()); + // Load the name of the function. + Load(property->key()); + + // Swap the name of the function and the receiver on the stack to follow + // the calling convention for call ICs. + Result key = frame_->Pop(); + Result receiver = frame_->Pop(); + frame_->Push(&key); + frame_->Push(&receiver); + key.Unuse(); + receiver.Unuse(); + // Load the arguments. int arg_count = args->length(); for (int i = 0; i < arg_count; i++) { @@ -6301,15 +6313,14 @@ void CodeGenerator::VisitCall(Call* node) { frame_->SpillTop(); } - // Load the name of the function. - Load(property->key()); - - // Call the IC initialization code. + // Place the key on top of stack and call the IC initialization code. + frame_->PushElementAt(arg_count + 1); CodeForSourcePosition(node->position()); Result result = frame_->CallKeyedCallIC(RelocInfo::CODE_TARGET, arg_count, loop_nesting()); + frame_->Drop(); // Drop the key still on the stack. frame_->RestoreContextRegister(); frame_->Push(&result); } @@ -6778,8 +6789,8 @@ class DeferredIsStringWrapperSafeForDefaultValueOf : public DeferredCode { __ mov(scratch2_, FieldOperand(scratch2_, GlobalObject::kGlobalContextOffset)); __ cmp(scratch1_, - CodeGenerator::ContextOperand( - scratch2_, Context::STRING_FUNCTION_PROTOTYPE_MAP_INDEX)); + ContextOperand(scratch2_, + Context::STRING_FUNCTION_PROTOTYPE_MAP_INDEX)); __ j(not_equal, &false_result); // Set the bit in the map to indicate that it has been checked safe for // default valueOf and set true result. @@ -7292,88 +7303,6 @@ void CodeGenerator::GenerateRegExpConstructResult(ZoneList<Expression*>* args) { } -void CodeGenerator::GenerateRegExpCloneResult(ZoneList<Expression*>* args) { - ASSERT_EQ(1, args->length()); - - Load(args->at(0)); - Result object_result = frame_->Pop(); - object_result.ToRegister(eax); - object_result.Unuse(); - { - VirtualFrame::SpilledScope spilled_scope; - - Label done; - - __ test(eax, Immediate(kSmiTagMask)); - __ j(zero, &done); - - // Load JSRegExpResult map into edx. - // Arguments to this function should be results of calling RegExp exec, - // which is either an unmodified JSRegExpResult or null. Anything not having - // the unmodified JSRegExpResult map is returned unmodified. - // This also ensures that elements are fast. - __ mov(edx, ContextOperand(esi, Context::GLOBAL_INDEX)); - __ mov(edx, FieldOperand(edx, GlobalObject::kGlobalContextOffset)); - __ mov(edx, ContextOperand(edx, Context::REGEXP_RESULT_MAP_INDEX)); - __ cmp(edx, FieldOperand(eax, HeapObject::kMapOffset)); - __ j(not_equal, &done); - - if (FLAG_debug_code) { - // Check that object really has empty properties array, as the map - // should guarantee. - __ cmp(FieldOperand(eax, JSObject::kPropertiesOffset), - Immediate(Factory::empty_fixed_array())); - __ Check(equal, "JSRegExpResult: default map but non-empty properties."); - } - - DeferredAllocateInNewSpace* allocate_fallback = - new DeferredAllocateInNewSpace(JSRegExpResult::kSize, - ebx, - edx.bit() | eax.bit()); - - // All set, copy the contents to a new object. - __ AllocateInNewSpace(JSRegExpResult::kSize, - ebx, - ecx, - no_reg, - allocate_fallback->entry_label(), - TAG_OBJECT); - __ bind(allocate_fallback->exit_label()); - - // Copy all fields from eax to ebx. - STATIC_ASSERT(JSRegExpResult::kSize % (2 * kPointerSize) == 0); - // There is an even number of fields, so unroll the loop once - // for efficiency. - for (int i = 0; i < JSRegExpResult::kSize; i += 2 * kPointerSize) { - STATIC_ASSERT(JSObject::kMapOffset % (2 * kPointerSize) == 0); - if (i != JSObject::kMapOffset) { - // The map was already loaded into edx. - __ mov(edx, FieldOperand(eax, i)); - } - __ mov(ecx, FieldOperand(eax, i + kPointerSize)); - - STATIC_ASSERT(JSObject::kElementsOffset % (2 * kPointerSize) == 0); - if (i == JSObject::kElementsOffset) { - // If the elements array isn't empty, make it copy-on-write - // before copying it. - Label empty; - __ cmp(Operand(edx), Immediate(Factory::empty_fixed_array())); - __ j(equal, &empty); - __ mov(FieldOperand(edx, HeapObject::kMapOffset), - Immediate(Factory::fixed_cow_array_map())); - __ bind(&empty); - } - __ mov(FieldOperand(ebx, i), edx); - __ mov(FieldOperand(ebx, i + kPointerSize), ecx); - } - __ mov(eax, ebx); - - __ bind(&done); - } - frame_->Push(eax); -} - - class DeferredSearchCache: public DeferredCode { public: DeferredSearchCache(Register dst, Register cache, Register key) @@ -8016,7 +7945,7 @@ void CodeGenerator::VisitCallRuntime(CallRuntime* node) { // Push the builtins object found in the current global object. Result temp = allocator()->Allocate(); ASSERT(temp.is_valid()); - __ mov(temp.reg(), GlobalObject()); + __ mov(temp.reg(), GlobalObjectOperand()); __ mov(temp.reg(), FieldOperand(temp.reg(), GlobalObject::kBuiltinsOffset)); frame_->Push(&temp); } @@ -8660,9 +8589,11 @@ void CodeGenerator::Int32BinaryOperation(BinaryOperation* node) { } right.Unuse(); frame_->Push(&left); - if (!node->to_int32()) { - // If ToInt32 is called on the result of ADD, SUB, or MUL, we don't + if (!node->to_int32() || op == Token::MUL) { + // If ToInt32 is called on the result of ADD, SUB, we don't // care about overflows. + // Result of MUL can be non-representable precisely in double so + // we have to check for overflow. unsafe_bailout_->Branch(overflow); } break; diff --git a/deps/v8/src/ia32/codegen-ia32.h b/deps/v8/src/ia32/codegen-ia32.h index 4594b19ddd..a945e217ab 100644 --- a/deps/v8/src/ia32/codegen-ia32.h +++ b/deps/v8/src/ia32/codegen-ia32.h @@ -352,10 +352,6 @@ class CodeGenerator: public AstVisitor { return FieldOperand(array, index_as_smi, times_half_pointer_size, offset); } - static Operand ContextOperand(Register context, int index) { - return Operand(context, Context::SlotOffset(index)); - } - private: // Type of a member function that generates inline code for a native function. typedef void (CodeGenerator::*InlineFunctionGenerator) @@ -441,10 +437,6 @@ class CodeGenerator: public AstVisitor { JumpTarget* slow); // Expressions - static Operand GlobalObject() { - return ContextOperand(esi, Context::GLOBAL_INDEX); - } - void LoadCondition(Expression* expr, ControlDestination* destination, bool force_control); @@ -628,10 +620,6 @@ class CodeGenerator: public AstVisitor { void ProcessDeclarations(ZoneList<Declaration*>* declarations); - static Handle<Code> ComputeCallInitialize(int argc, InLoopFlag in_loop); - - static Handle<Code> ComputeKeyedCallInitialize(int argc, InLoopFlag in_loop); - // Declare global variables and functions in the given array of // name/value pairs. void DeclareGlobals(Handle<FixedArray> pairs); @@ -697,11 +685,6 @@ class CodeGenerator: public AstVisitor { // Construct a RegExp exec result with two in-object properties. void GenerateRegExpConstructResult(ZoneList<Expression*>* args); - // Clone the result of a regexp function. - // Must be an object created by GenerateRegExpConstructResult with - // no extra properties. - void GenerateRegExpCloneResult(ZoneList<Expression*>* args); - // Support for fast native caches. void GenerateGetFromCache(ZoneList<Expression*>* args); diff --git a/deps/v8/src/ia32/full-codegen-ia32.cc b/deps/v8/src/ia32/full-codegen-ia32.cc index ee4e6458ae..ad32dc85be 100644 --- a/deps/v8/src/ia32/full-codegen-ia32.cc +++ b/deps/v8/src/ia32/full-codegen-ia32.cc @@ -36,6 +36,7 @@ #include "full-codegen.h" #include "parser.h" #include "scopes.h" +#include "stub-cache.h" namespace v8 { namespace internal { @@ -954,7 +955,7 @@ void FullCodeGenerator::EmitLoadGlobalSlotCheckExtensions( // All extension objects were empty and it is safe to use a global // load IC call. - __ mov(eax, CodeGenerator::GlobalObject()); + __ mov(eax, GlobalObjectOperand()); __ mov(ecx, slot->var()->name()); Handle<Code> ic(Builtins::builtin(Builtins::LoadIC_Initialize)); RelocInfo::Mode mode = (typeof_state == INSIDE_TYPEOF) @@ -1057,7 +1058,7 @@ void FullCodeGenerator::EmitVariableLoad(Variable* var) { Comment cmnt(masm_, "Global variable"); // Use inline caching. Variable name is passed in ecx and the global // object on the stack. - __ mov(eax, CodeGenerator::GlobalObject()); + __ mov(eax, GlobalObjectOperand()); __ mov(ecx, var->name()); Handle<Code> ic(Builtins::builtin(Builtins::LoadIC_Initialize)); EmitCallIC(ic, RelocInfo::CODE_TARGET_CONTEXT); @@ -1834,7 +1835,7 @@ void FullCodeGenerator::EmitVariableAssignment(Variable* var, // assignment. Right-hand-side value is passed in eax, variable name in // ecx, and the global object on the stack. __ mov(ecx, var->name()); - __ mov(edx, CodeGenerator::GlobalObject()); + __ mov(edx, GlobalObjectOperand()); Handle<Code> ic(Builtins::builtin(Builtins::StoreIC_Initialize)); EmitCallIC(ic, RelocInfo::CODE_TARGET); @@ -1996,14 +1997,16 @@ void FullCodeGenerator::EmitCallWithIC(Call* expr, // Code common for calls using the IC. ZoneList<Expression*>* args = expr->arguments(); int arg_count = args->length(); - for (int i = 0; i < arg_count; i++) { - VisitForStackValue(args->at(i)); + { PreserveStatementPositionScope scope(masm()->positions_recorder()); + for (int i = 0; i < arg_count; i++) { + VisitForStackValue(args->at(i)); + } + __ Set(ecx, Immediate(name)); } - __ Set(ecx, Immediate(name)); // Record source position of the IC call. - SetSourcePosition(expr->position()); + SetSourcePosition(expr->position(), FORCED_POSITION); InLoopFlag in_loop = (loop_depth() > 0) ? IN_LOOP : NOT_IN_LOOP; - Handle<Code> ic = CodeGenerator::ComputeCallInitialize(arg_count, in_loop); + Handle<Code> ic = StubCache::ComputeCallInitialize(arg_count, in_loop); EmitCallIC(ic, mode); // Restore context register. __ mov(esi, Operand(ebp, StandardFrameConstants::kContextOffset)); @@ -2014,23 +2017,32 @@ void FullCodeGenerator::EmitCallWithIC(Call* expr, void FullCodeGenerator::EmitKeyedCallWithIC(Call* expr, Expression* key, RelocInfo::Mode mode) { - // Code common for calls using the IC. + // Load the key. + VisitForAccumulatorValue(key); + + // Swap the name of the function and the receiver on the stack to follow + // the calling convention for call ICs. + __ pop(ecx); + __ push(eax); + __ push(ecx); + + // Load the arguments. ZoneList<Expression*>* args = expr->arguments(); int arg_count = args->length(); - for (int i = 0; i < arg_count; i++) { - VisitForStackValue(args->at(i)); + { PreserveStatementPositionScope scope(masm()->positions_recorder()); + for (int i = 0; i < arg_count; i++) { + VisitForStackValue(args->at(i)); + } } - VisitForAccumulatorValue(key); - __ mov(ecx, eax); // Record source position of the IC call. - SetSourcePosition(expr->position()); + SetSourcePosition(expr->position(), FORCED_POSITION); InLoopFlag in_loop = (loop_depth() > 0) ? IN_LOOP : NOT_IN_LOOP; - Handle<Code> ic = CodeGenerator::ComputeKeyedCallInitialize( - arg_count, in_loop); + Handle<Code> ic = StubCache::ComputeKeyedCallInitialize(arg_count, in_loop); + __ mov(ecx, Operand(esp, (arg_count + 1) * kPointerSize)); // Key. EmitCallIC(ic, mode); // Restore context register. __ mov(esi, Operand(ebp, StandardFrameConstants::kContextOffset)); - context()->Plug(eax); + context()->DropAndPlug(1, eax); // Drop the key still on the stack. } @@ -2038,11 +2050,13 @@ void FullCodeGenerator::EmitCallWithStub(Call* expr) { // Code common for calls using the call stub. ZoneList<Expression*>* args = expr->arguments(); int arg_count = args->length(); - for (int i = 0; i < arg_count; i++) { - VisitForStackValue(args->at(i)); + { PreserveStatementPositionScope scope(masm()->positions_recorder()); + for (int i = 0; i < arg_count; i++) { + VisitForStackValue(args->at(i)); + } } // Record source position for debugger. - SetSourcePosition(expr->position()); + SetSourcePosition(expr->position(), FORCED_POSITION); InLoopFlag in_loop = (loop_depth() > 0) ? IN_LOOP : NOT_IN_LOOP; CallFunctionStub stub(arg_count, in_loop, RECEIVER_MIGHT_BE_VALUE); __ CallStub(&stub); @@ -2062,37 +2076,39 @@ void FullCodeGenerator::VisitCall(Call* expr) { // resolve the function we need to call and the receiver of the // call. Then we call the resolved function using the given // arguments. - VisitForStackValue(fun); - __ push(Immediate(Factory::undefined_value())); // Reserved receiver slot. - - // Push the arguments. ZoneList<Expression*>* args = expr->arguments(); int arg_count = args->length(); - for (int i = 0; i < arg_count; i++) { - VisitForStackValue(args->at(i)); - } + { PreserveStatementPositionScope pos_scope(masm()->positions_recorder()); + VisitForStackValue(fun); + // Reserved receiver slot. + __ push(Immediate(Factory::undefined_value())); - // Push copy of the function - found below the arguments. - __ push(Operand(esp, (arg_count + 1) * kPointerSize)); + // Push the arguments. + for (int i = 0; i < arg_count; i++) { + VisitForStackValue(args->at(i)); + } - // Push copy of the first argument or undefined if it doesn't exist. - if (arg_count > 0) { - __ push(Operand(esp, arg_count * kPointerSize)); - } else { - __ push(Immediate(Factory::undefined_value())); - } + // Push copy of the function - found below the arguments. + __ push(Operand(esp, (arg_count + 1) * kPointerSize)); - // Push the receiver of the enclosing function and do runtime call. - __ push(Operand(ebp, (2 + scope()->num_parameters()) * kPointerSize)); - __ CallRuntime(Runtime::kResolvePossiblyDirectEval, 3); + // Push copy of the first argument or undefined if it doesn't exist. + if (arg_count > 0) { + __ push(Operand(esp, arg_count * kPointerSize)); + } else { + __ push(Immediate(Factory::undefined_value())); + } - // The runtime call returns a pair of values in eax (function) and - // edx (receiver). Touch up the stack with the right values. - __ mov(Operand(esp, (arg_count + 0) * kPointerSize), edx); - __ mov(Operand(esp, (arg_count + 1) * kPointerSize), eax); + // Push the receiver of the enclosing function and do runtime call. + __ push(Operand(ebp, (2 + scope()->num_parameters()) * kPointerSize)); + __ CallRuntime(Runtime::kResolvePossiblyDirectEval, 3); + // The runtime call returns a pair of values in eax (function) and + // edx (receiver). Touch up the stack with the right values. + __ mov(Operand(esp, (arg_count + 0) * kPointerSize), edx); + __ mov(Operand(esp, (arg_count + 1) * kPointerSize), eax); + } // Record source position for debugger. - SetSourcePosition(expr->position()); + SetSourcePosition(expr->position(), FORCED_POSITION); InLoopFlag in_loop = (loop_depth() > 0) ? IN_LOOP : NOT_IN_LOOP; CallFunctionStub stub(arg_count, in_loop, RECEIVER_MIGHT_BE_VALUE); __ CallStub(&stub); @@ -2101,19 +2117,21 @@ void FullCodeGenerator::VisitCall(Call* expr) { context()->DropAndPlug(1, eax); } else if (var != NULL && !var->is_this() && var->is_global()) { // Push global object as receiver for the call IC. - __ push(CodeGenerator::GlobalObject()); + __ push(GlobalObjectOperand()); EmitCallWithIC(expr, var->name(), RelocInfo::CODE_TARGET_CONTEXT); } else if (var != NULL && var->AsSlot() != NULL && var->AsSlot()->type() == Slot::LOOKUP) { // Call to a lookup slot (dynamically introduced variable). Label slow, done; - // Generate code for loading from variables potentially shadowed - // by eval-introduced variables. - EmitDynamicLoadFromSlotFastCase(var->AsSlot(), - NOT_INSIDE_TYPEOF, - &slow, - &done); + { PreserveStatementPositionScope scope(masm()->positions_recorder()); + // Generate code for loading from variables potentially shadowed + // by eval-introduced variables. + EmitDynamicLoadFromSlotFastCase(var->AsSlot(), + NOT_INSIDE_TYPEOF, + &slow, + &done); + } __ bind(&slow); // Call the runtime to find the function to call (returned in eax) @@ -2134,7 +2152,7 @@ void FullCodeGenerator::VisitCall(Call* expr) { // Push function. __ push(eax); // Push global receiver. - __ mov(ebx, CodeGenerator::GlobalObject()); + __ mov(ebx, GlobalObjectOperand()); __ push(FieldOperand(ebx, GlobalObject::kGlobalReceiverOffset)); __ bind(&call); } @@ -2152,11 +2170,15 @@ void FullCodeGenerator::VisitCall(Call* expr) { // Call to a keyed property. // For a synthetic property use keyed load IC followed by function call, // for a regular property use keyed EmitCallIC. - VisitForStackValue(prop->obj()); + { PreserveStatementPositionScope scope(masm()->positions_recorder()); + VisitForStackValue(prop->obj()); + } if (prop->is_synthetic()) { - VisitForAccumulatorValue(prop->key()); + { PreserveStatementPositionScope scope(masm()->positions_recorder()); + VisitForAccumulatorValue(prop->key()); + } // Record source code position for IC call. - SetSourcePosition(prop->position()); + SetSourcePosition(prop->position(), FORCED_POSITION); __ pop(edx); // We do not need to keep the receiver. Handle<Code> ic(Builtins::builtin(Builtins::KeyedLoadIC_Initialize)); @@ -2164,7 +2186,7 @@ void FullCodeGenerator::VisitCall(Call* expr) { // Push result (function). __ push(eax); // Push Global receiver. - __ mov(ecx, CodeGenerator::GlobalObject()); + __ mov(ecx, GlobalObjectOperand()); __ push(FieldOperand(ecx, GlobalObject::kGlobalReceiverOffset)); EmitCallWithStub(expr); } else { @@ -2181,9 +2203,11 @@ void FullCodeGenerator::VisitCall(Call* expr) { loop_depth() == 0) { lit->set_try_full_codegen(true); } - VisitForStackValue(fun); + { PreserveStatementPositionScope scope(masm()->positions_recorder()); + VisitForStackValue(fun); + } // Load global receiver object. - __ mov(ebx, CodeGenerator::GlobalObject()); + __ mov(ebx, GlobalObjectOperand()); __ push(FieldOperand(ebx, GlobalObject::kGlobalReceiverOffset)); // Emit function call. EmitCallWithStub(expr); @@ -3073,7 +3097,7 @@ void FullCodeGenerator::VisitCallRuntime(CallRuntime* expr) { if (expr->is_jsruntime()) { // Prepare for calling JS runtime function. - __ mov(eax, CodeGenerator::GlobalObject()); + __ mov(eax, GlobalObjectOperand()); __ push(FieldOperand(eax, GlobalObject::kBuiltinsOffset)); } @@ -3087,7 +3111,7 @@ void FullCodeGenerator::VisitCallRuntime(CallRuntime* expr) { // Call the JS runtime function via a call IC. __ Set(ecx, Immediate(expr->name())); InLoopFlag in_loop = (loop_depth() > 0) ? IN_LOOP : NOT_IN_LOOP; - Handle<Code> ic = CodeGenerator::ComputeCallInitialize(arg_count, in_loop); + Handle<Code> ic = StubCache::ComputeCallInitialize(arg_count, in_loop); EmitCallIC(ic, RelocInfo::CODE_TARGET); // Restore context register. __ mov(esi, Operand(ebp, StandardFrameConstants::kContextOffset)); @@ -3124,7 +3148,7 @@ void FullCodeGenerator::VisitUnaryOperation(UnaryOperation* expr) { VisitForStackValue(prop->obj()); VisitForStackValue(prop->key()); } else if (var->is_global()) { - __ push(CodeGenerator::GlobalObject()); + __ push(GlobalObjectOperand()); __ push(Immediate(var->name())); } else { // Non-global variable. Call the runtime to look up the context @@ -3402,7 +3426,7 @@ void FullCodeGenerator::VisitForTypeofValue(Expression* expr) { if (proxy != NULL && !proxy->var()->is_this() && proxy->var()->is_global()) { Comment cmnt(masm_, "Global variable"); - __ mov(eax, CodeGenerator::GlobalObject()); + __ mov(eax, GlobalObjectOperand()); __ mov(ecx, Immediate(proxy->name())); Handle<Code> ic(Builtins::builtin(Builtins::LoadIC_Initialize)); // Use a regular load, not a contextual load, to avoid a reference diff --git a/deps/v8/src/ia32/ic-ia32.cc b/deps/v8/src/ia32/ic-ia32.cc index b5f4deefeb..a0bc086d87 100644 --- a/deps/v8/src/ia32/ic-ia32.cc +++ b/deps/v8/src/ia32/ic-ia32.cc @@ -33,7 +33,6 @@ #include "ic-inl.h" #include "runtime.h" #include "stub-cache.h" -#include "utils.h" namespace v8 { namespace internal { diff --git a/deps/v8/src/ia32/macro-assembler-ia32.cc b/deps/v8/src/ia32/macro-assembler-ia32.cc index b72f4df0e7..db26c10425 100644 --- a/deps/v8/src/ia32/macro-assembler-ia32.cc +++ b/deps/v8/src/ia32/macro-assembler-ia32.cc @@ -392,13 +392,8 @@ void MacroAssembler::EnterExitFrame() { } -void MacroAssembler::EnterApiExitFrame(int stack_space, - int argc) { +void MacroAssembler::EnterApiExitFrame(int argc) { EnterExitFramePrologue(); - - int offset = StandardFrameConstants::kCallerSPOffset - kPointerSize; - lea(esi, Operand(ebp, (stack_space * kPointerSize) + offset)); - EnterExitFrameEpilogue(argc); } @@ -411,6 +406,13 @@ void MacroAssembler::LeaveExitFrame() { // Pop the arguments and the receiver from the caller stack. lea(esp, Operand(esi, 1 * kPointerSize)); + // Push the return address to get ready to return. + push(ecx); + + LeaveExitFrameEpilogue(); +} + +void MacroAssembler::LeaveExitFrameEpilogue() { // Restore current context from top and clear it in debug mode. ExternalReference context_address(Top::k_context_address); mov(esi, Operand::StaticVariable(context_address)); @@ -418,15 +420,20 @@ void MacroAssembler::LeaveExitFrame() { mov(Operand::StaticVariable(context_address), Immediate(0)); #endif - // Push the return address to get ready to return. - push(ecx); - // Clear the top frame. ExternalReference c_entry_fp_address(Top::k_c_entry_fp_address); mov(Operand::StaticVariable(c_entry_fp_address), Immediate(0)); } +void MacroAssembler::LeaveApiExitFrame() { + mov(esp, Operand(ebp)); + pop(ebp); + + LeaveExitFrameEpilogue(); +} + + void MacroAssembler::PushTryHandler(CodeLocation try_location, HandlerType type) { // Adjust this code if not the case. @@ -1110,6 +1117,17 @@ void MacroAssembler::TailCallExternalReference(const ExternalReference& ext, } +MaybeObject* MacroAssembler::TryTailCallExternalReference( + const ExternalReference& ext, int num_arguments, int result_size) { + // TODO(1236192): Most runtime routines don't need the number of + // arguments passed in because it is constant. At some point we + // should remove this need and make the runtime routine entry code + // smarter. + Set(eax, Immediate(num_arguments)); + return TryJumpToExternalReference(ext); +} + + void MacroAssembler::TailCallRuntime(Runtime::FunctionId fid, int num_arguments, int result_size) { @@ -1117,6 +1135,14 @@ void MacroAssembler::TailCallRuntime(Runtime::FunctionId fid, } +MaybeObject* MacroAssembler::TryTailCallRuntime(Runtime::FunctionId fid, + int num_arguments, + int result_size) { + return TryTailCallExternalReference( + ExternalReference(fid), num_arguments, result_size); +} + + // If true, a Handle<T> passed by value is passed and returned by // using the location_ field directly. If false, it is passed and // returned as a pointer to a handle. @@ -1132,20 +1158,15 @@ Operand ApiParameterOperand(int index) { } -void MacroAssembler::PrepareCallApiFunction(int stack_space, int argc) { +void MacroAssembler::PrepareCallApiFunction(int argc, Register scratch) { if (kPassHandlesDirectly) { - EnterApiExitFrame(stack_space, argc); + EnterApiExitFrame(argc); // When handles as passed directly we don't have to allocate extra // space for and pass an out parameter. } else { // We allocate two additional slots: return value and pointer to it. - EnterApiExitFrame(stack_space, argc + 2); - } -} - + EnterApiExitFrame(argc + 2); -void MacroAssembler::CallApiFunctionAndReturn(ApiFunction* function, int argc) { - if (!kPassHandlesDirectly) { // The argument slots are filled as follows: // // n + 1: output cell @@ -1157,11 +1178,19 @@ void MacroAssembler::CallApiFunctionAndReturn(ApiFunction* function, int argc) { // Note that this is one more "argument" than the function expects // so the out cell will have to be popped explicitly after returning // from the function. The out cell contains Handle. - lea(eax, Operand(esp, (argc + 1) * kPointerSize)); // pointer to out cell. - mov(Operand(esp, 0 * kPointerSize), eax); // output. - mov(Operand(esp, (argc + 1) * kPointerSize), Immediate(0)); // out cell. + + // pointer to out cell. + lea(scratch, Operand(esp, (argc + 1) * kPointerSize)); + mov(Operand(esp, 0 * kPointerSize), scratch); // output. + if (FLAG_debug_code) { + mov(Operand(esp, (argc + 1) * kPointerSize), Immediate(0)); // out cell. + } } +} + +MaybeObject* MacroAssembler::TryCallApiFunctionAndReturn(ApiFunction* function, + int stack_space) { ExternalReference next_address = ExternalReference::handle_scope_next_address(); ExternalReference limit_address = @@ -1210,10 +1239,14 @@ void MacroAssembler::CallApiFunctionAndReturn(ApiFunction* function, int argc) { cmp(Operand::StaticVariable(scheduled_exception_address), Immediate(Factory::the_hole_value())); j(not_equal, &promote_scheduled_exception, not_taken); - LeaveExitFrame(); - ret(0); + LeaveApiExitFrame(); + ret(stack_space * kPointerSize); bind(&promote_scheduled_exception); - TailCallRuntime(Runtime::kPromoteScheduledException, 0, 1); + MaybeObject* result = + TryTailCallRuntime(Runtime::kPromoteScheduledException, 0, 1); + if (result->IsFailure()) { + return result; + } bind(&empty_handle); // It was zero; the result is undefined. mov(eax, Factory::undefined_value()); @@ -1227,6 +1260,8 @@ void MacroAssembler::CallApiFunctionAndReturn(ApiFunction* function, int argc) { call(Operand(eax)); mov(eax, edi); jmp(&leave_exit_frame); + + return result; } @@ -1238,6 +1273,15 @@ void MacroAssembler::JumpToExternalReference(const ExternalReference& ext) { } +MaybeObject* MacroAssembler::TryJumpToExternalReference( + const ExternalReference& ext) { + // Set the entry point and jump to the C entry runtime stub. + mov(ebx, Immediate(ext)); + CEntryStub ces(1); + return TryTailCallStub(&ces); +} + + void MacroAssembler::InvokePrologue(const ParameterCount& expected, const ParameterCount& actual, Handle<Code> code_constant, diff --git a/deps/v8/src/ia32/macro-assembler-ia32.h b/deps/v8/src/ia32/macro-assembler-ia32.h index 7b9b843939..0a6e0ee35b 100644 --- a/deps/v8/src/ia32/macro-assembler-ia32.h +++ b/deps/v8/src/ia32/macro-assembler-ia32.h @@ -123,13 +123,17 @@ class MacroAssembler: public Assembler { // to the first argument in register esi. void EnterExitFrame(); - void EnterApiExitFrame(int stack_space, int argc); + void EnterApiExitFrame(int argc); // Leave the current exit frame. Expects the return value in // register eax:edx (untouched) and the pointer to the first // argument in register esi. void LeaveExitFrame(); + // Leave the current exit frame. Expects the return value in + // register eax (untouched). + void LeaveApiExitFrame(); + // Find the function context up the context chain. void LoadContext(Register dst, int context_chain_length); @@ -460,11 +464,23 @@ class MacroAssembler: public Assembler { int num_arguments, int result_size); + // Tail call of a runtime routine (jump). Try to generate the code if + // necessary. Do not perform a GC but instead return a retry after GC failure. + MUST_USE_RESULT MaybeObject* TryTailCallExternalReference( + const ExternalReference& ext, int num_arguments, int result_size); + // Convenience function: tail call a runtime routine (jump). void TailCallRuntime(Runtime::FunctionId fid, int num_arguments, int result_size); + // Convenience function: tail call a runtime routine (jump). Try to generate + // the code if necessary. Do not perform a GC but instead return a retry after + // GC failure. + MUST_USE_RESULT MaybeObject* TryTailCallRuntime(Runtime::FunctionId fid, + int num_arguments, + int result_size); + // Before calling a C-function from generated code, align arguments on stack. // After aligning the frame, arguments must be stored in esp[0], esp[4], // etc., not pushed. The argument count assumes all arguments are word sized. @@ -485,17 +501,22 @@ class MacroAssembler: public Assembler { // Prepares stack to put arguments (aligns and so on). Reserves // space for return value if needed (assumes the return value is a handle). // Uses callee-saved esi to restore stack state after call. Arguments must be - // stored in ApiParameterOperand(0), ApiParameterOperand(1) etc. - void PrepareCallApiFunction(int stack_space, int argc); + // stored in ApiParameterOperand(0), ApiParameterOperand(1) etc. Saves + // context (esi). + void PrepareCallApiFunction(int argc, Register scratch); - // Tail call an API function (jump). Allocates HandleScope, extracts + // Calls an API function. Allocates HandleScope, extracts // returned value from handle and propagates exceptions. - // Clobbers ebx, esi, edi and caller-save registers. - void CallApiFunctionAndReturn(ApiFunction* function, int argc); + // Clobbers ebx, edi and caller-save registers. Restores context. + // On return removes stack_space * kPointerSize (GCed). + MaybeObject* TryCallApiFunctionAndReturn(ApiFunction* function, + int stack_space); // Jump to a runtime routine. void JumpToExternalReference(const ExternalReference& ext); + MaybeObject* TryJumpToExternalReference(const ExternalReference& ext); + // --------------------------------------------------------------------------- // Utilities @@ -589,6 +610,8 @@ class MacroAssembler: public Assembler { void EnterExitFramePrologue(); void EnterExitFrameEpilogue(int argc); + void LeaveExitFrameEpilogue(); + // Allocation support helpers. void LoadAllocationTopHelper(Register result, Register result_end, @@ -642,6 +665,17 @@ static inline Operand FieldOperand(Register object, return Operand(object, index, scale, offset - kHeapObjectTag); } + +static inline Operand ContextOperand(Register context, int index) { + return Operand(context, Context::SlotOffset(index)); +} + + +static inline Operand GlobalObjectOperand() { + return ContextOperand(esi, Context::GLOBAL_INDEX); +} + + // Generates an Operand for saving parameters after PrepareCallApiFunction. Operand ApiParameterOperand(int index); diff --git a/deps/v8/src/ia32/stub-cache-ia32.cc b/deps/v8/src/ia32/stub-cache-ia32.cc index e387088359..4184d4cde3 100644 --- a/deps/v8/src/ia32/stub-cache-ia32.cc +++ b/deps/v8/src/ia32/stub-cache-ia32.cc @@ -413,6 +413,10 @@ static void CompileCallLoadPropertyWithInterceptor(MacroAssembler* masm, } +// Number of pointers to be reserved on stack for fast API call. +static const int kFastApiCallArguments = 3; + + // Reserves space for the extra arguments to FastHandleApiCall in the // caller's frame. // @@ -423,10 +427,9 @@ static void ReserveSpaceForFastApiCall(MacroAssembler* masm, Register scratch) { // -- esp[4] : last argument in the internal frame of the caller // ----------------------------------- __ pop(scratch); - __ push(Immediate(Smi::FromInt(0))); - __ push(Immediate(Smi::FromInt(0))); - __ push(Immediate(Smi::FromInt(0))); - __ push(Immediate(Smi::FromInt(0))); + for (int i = 0; i < kFastApiCallArguments; i++) { + __ push(Immediate(Smi::FromInt(0))); + } __ push(scratch); } @@ -434,75 +437,92 @@ static void ReserveSpaceForFastApiCall(MacroAssembler* masm, Register scratch) { // Undoes the effects of ReserveSpaceForFastApiCall. static void FreeSpaceForFastApiCall(MacroAssembler* masm, Register scratch) { // ----------- S t a t e ------------- - // -- esp[0] : return address - // -- esp[4] : last fast api call extra argument + // -- esp[0] : return address. + // -- esp[4] : last fast api call extra argument. // -- ... - // -- esp[16] : first fast api call extra argument - // -- esp[20] : last argument in the internal frame + // -- esp[kFastApiCallArguments * 4] : first fast api call extra argument. + // -- esp[kFastApiCallArguments * 4 + 4] : last argument in the internal + // frame. // ----------------------------------- __ pop(scratch); - __ add(Operand(esp), Immediate(kPointerSize * 4)); + __ add(Operand(esp), Immediate(kPointerSize * kFastApiCallArguments)); __ push(scratch); } // Generates call to FastHandleApiCall builtin. -static void GenerateFastApiCall(MacroAssembler* masm, +static bool GenerateFastApiCall(MacroAssembler* masm, const CallOptimization& optimization, - int argc) { + int argc, + Failure** failure) { // ----------- S t a t e ------------- // -- esp[0] : return address // -- esp[4] : object passing the type check // (last fast api call extra argument, // set by CheckPrototypes) - // -- esp[8] : api call data - // -- esp[12] : api callback - // -- esp[16] : api function + // -- esp[8] : api function // (first fast api call extra argument) - // -- esp[20] : last argument + // -- esp[12] : api call data + // -- esp[16] : last argument // -- ... - // -- esp[(argc + 5) * 4] : first argument - // -- esp[(argc + 6) * 4] : receiver + // -- esp[(argc + 3) * 4] : first argument + // -- esp[(argc + 4) * 4] : receiver // ----------------------------------- - // Get the function and setup the context. JSFunction* function = optimization.constant_function(); __ mov(edi, Immediate(Handle<JSFunction>(function))); __ mov(esi, FieldOperand(edi, JSFunction::kContextOffset)); // Pass the additional arguments FastHandleApiCall expects. - __ mov(Operand(esp, 4 * kPointerSize), edi); - bool info_loaded = false; - Object* callback = optimization.api_call_info()->callback(); - if (Heap::InNewSpace(callback)) { - info_loaded = true; - __ mov(ecx, Handle<CallHandlerInfo>(optimization.api_call_info())); - __ mov(ebx, FieldOperand(ecx, CallHandlerInfo::kCallbackOffset)); - __ mov(Operand(esp, 3 * kPointerSize), ebx); - } else { - __ mov(Operand(esp, 3 * kPointerSize), Immediate(Handle<Object>(callback))); - } + __ mov(Operand(esp, 2 * kPointerSize), edi); Object* call_data = optimization.api_call_info()->data(); + Handle<CallHandlerInfo> api_call_info_handle(optimization.api_call_info()); if (Heap::InNewSpace(call_data)) { - if (!info_loaded) { - __ mov(ecx, Handle<CallHandlerInfo>(optimization.api_call_info())); - } + __ mov(ecx, api_call_info_handle); __ mov(ebx, FieldOperand(ecx, CallHandlerInfo::kDataOffset)); - __ mov(Operand(esp, 2 * kPointerSize), ebx); + __ mov(Operand(esp, 3 * kPointerSize), ebx); } else { - __ mov(Operand(esp, 2 * kPointerSize), + __ mov(Operand(esp, 3 * kPointerSize), Immediate(Handle<Object>(call_data))); } - // Set the number of arguments. - __ mov(eax, Immediate(argc + 4)); + // Prepare arguments. + __ lea(eax, Operand(esp, 3 * kPointerSize)); - // Jump to the fast api call builtin (tail call). - Handle<Code> code = Handle<Code>( - Builtins::builtin(Builtins::FastHandleApiCall)); - ParameterCount expected(0); - __ InvokeCode(code, expected, expected, - RelocInfo::CODE_TARGET, JUMP_FUNCTION); + Object* callback = optimization.api_call_info()->callback(); + Address api_function_address = v8::ToCData<Address>(callback); + ApiFunction fun(api_function_address); + + const int kApiArgc = 1; // API function gets reference to the v8::Arguments. + + // Allocate the v8::Arguments structure in the arguments' space since + // it's not controlled by GC. + const int kApiStackSpace = 4; + + __ PrepareCallApiFunction(kApiArgc + kApiStackSpace, ebx); + + __ mov(ApiParameterOperand(1), eax); // v8::Arguments::implicit_args_. + __ add(Operand(eax), Immediate(argc * kPointerSize)); + __ mov(ApiParameterOperand(2), eax); // v8::Arguments::values_. + __ Set(ApiParameterOperand(3), Immediate(argc)); // v8::Arguments::length_. + // v8::Arguments::is_construct_call_. + __ Set(ApiParameterOperand(4), Immediate(0)); + + // v8::InvocationCallback's argument. + __ lea(eax, ApiParameterOperand(1)); + __ mov(ApiParameterOperand(0), eax); + + // Emitting a stub call may try to allocate (if the code is not + // already generated). Do not allow the assembler to perform a + // garbage collection but instead return the allocation failure + // object. + MaybeObject* result = + masm->TryCallApiFunctionAndReturn(&fun, argc + kFastApiCallArguments + 1); + if (result->IsFailure()) { + *failure = Failure::cast(result); + return false; + } + return true; } @@ -515,7 +535,7 @@ class CallInterceptorCompiler BASE_EMBEDDED { arguments_(arguments), name_(name) {} - void Compile(MacroAssembler* masm, + bool Compile(MacroAssembler* masm, JSObject* object, JSObject* holder, String* name, @@ -524,7 +544,8 @@ class CallInterceptorCompiler BASE_EMBEDDED { Register scratch1, Register scratch2, Register scratch3, - Label* miss) { + Label* miss, + Failure** failure) { ASSERT(holder->HasNamedInterceptor()); ASSERT(!holder->GetNamedInterceptor()->getter()->IsUndefined()); @@ -535,17 +556,18 @@ class CallInterceptorCompiler BASE_EMBEDDED { CallOptimization optimization(lookup); if (optimization.is_constant_call()) { - CompileCacheable(masm, - object, - receiver, - scratch1, - scratch2, - scratch3, - holder, - lookup, - name, - optimization, - miss); + return CompileCacheable(masm, + object, + receiver, + scratch1, + scratch2, + scratch3, + holder, + lookup, + name, + optimization, + miss, + failure); } else { CompileRegular(masm, object, @@ -556,11 +578,12 @@ class CallInterceptorCompiler BASE_EMBEDDED { name, holder, miss); + return true; } } private: - void CompileCacheable(MacroAssembler* masm, + bool CompileCacheable(MacroAssembler* masm, JSObject* object, Register receiver, Register scratch1, @@ -570,7 +593,8 @@ class CallInterceptorCompiler BASE_EMBEDDED { LookupResult* lookup, String* name, const CallOptimization& optimization, - Label* miss_label) { + Label* miss_label, + Failure** failure) { ASSERT(optimization.is_constant_call()); ASSERT(!lookup->holder()->IsGlobalObject()); @@ -632,7 +656,11 @@ class CallInterceptorCompiler BASE_EMBEDDED { // Invoke function. if (can_do_fast_api_call) { - GenerateFastApiCall(masm, optimization, arguments_.immediate()); + bool success = GenerateFastApiCall(masm, optimization, + arguments_.immediate(), failure); + if (!success) { + return false; + } } else { __ InvokeFunction(optimization.constant_function(), arguments_, JUMP_FUNCTION); @@ -650,6 +678,8 @@ class CallInterceptorCompiler BASE_EMBEDDED { if (can_do_fast_api_call) { FreeSpaceForFastApiCall(masm, scratch1); } + + return true; } void CompileRegular(MacroAssembler* masm, @@ -905,7 +935,7 @@ Register StubCompiler::CheckPrototypes(JSObject* object, MaybeObject* maybe_lookup_result = Heap::LookupSymbol(name); Object* lookup_result = NULL; // Initialization to please compiler. if (!maybe_lookup_result->ToObject(&lookup_result)) { - set_failure(Failure::cast(lookup_result)); + set_failure(Failure::cast(maybe_lookup_result)); return reg; } name = String::cast(lookup_result); @@ -1044,46 +1074,55 @@ bool StubCompiler::GenerateLoadCallback(JSObject* object, Handle<AccessorInfo> callback_handle(callback); - __ EnterInternalFrame(); - // Push the stack address where the list of arguments ends. - __ mov(scratch2, esp); - __ sub(Operand(scratch2), Immediate(2 * kPointerSize)); - __ push(scratch2); + // Insert additional parameters into the stack frame above return address. + ASSERT(!scratch3.is(reg)); + __ pop(scratch3); // Get return address to place it below. + __ push(receiver); // receiver + __ mov(scratch2, Operand(esp)); + ASSERT(!scratch2.is(reg)); __ push(reg); // holder // Push data from AccessorInfo. if (Heap::InNewSpace(callback_handle->data())) { - __ mov(scratch2, Immediate(callback_handle)); - __ push(FieldOperand(scratch2, AccessorInfo::kDataOffset)); + __ mov(scratch1, Immediate(callback_handle)); + __ push(FieldOperand(scratch1, AccessorInfo::kDataOffset)); } else { __ push(Immediate(Handle<Object>(callback_handle->data()))); } - __ push(name_reg); // name + // Save a pointer to where we pushed the arguments pointer. // This will be passed as the const AccessorInfo& to the C++ callback. - __ mov(eax, esp); - __ add(Operand(eax), Immediate(4 * kPointerSize)); - __ mov(ebx, esp); + __ push(scratch2); + + __ push(name_reg); // name + __ mov(ebx, esp); // esp points to reference to name (handler). + + __ push(scratch3); // Restore return address. // Do call through the api. - ASSERT_EQ(5, ApiGetterEntryStub::kStackSpace); Address getter_address = v8::ToCData<Address>(callback->getter()); ApiFunction fun(getter_address); - ApiGetterEntryStub stub(callback_handle, &fun); + + // 3 elements array for v8::Agruments::values_, handler for name and pointer + // to the values (it considered as smi in GC). + const int kStackSpace = 5; + const int kApiArgc = 2; + + __ PrepareCallApiFunction(kApiArgc, eax); + __ mov(ApiParameterOperand(0), ebx); // name. + __ add(Operand(ebx), Immediate(kPointerSize)); + __ mov(ApiParameterOperand(1), ebx); // arguments pointer. + // Emitting a stub call may try to allocate (if the code is not // already generated). Do not allow the assembler to perform a // garbage collection but instead return the allocation failure // object. - Object* result = NULL; // Initialization to please compiler. - { MaybeObject* try_call_result = masm()->TryCallStub(&stub); - if (!try_call_result->ToObject(&result)) { - *failure = Failure::cast(result); - return false; - } + MaybeObject* result = masm()->TryCallApiFunctionAndReturn(&fun, kStackSpace); + if (result->IsFailure()) { + *failure = Failure::cast(result); + return false; } - __ LeaveInternalFrame(); - __ ret(0); return true; } @@ -2129,7 +2168,10 @@ MaybeObject* CallStubCompiler::CompileCallConstant(Object* object, if (depth != kInvalidProtoDepth) { __ IncrementCounter(&Counters::call_const_fast_api, 1); - ReserveSpaceForFastApiCall(masm(), eax); + + // Allocate space for v8::Arguments implicit values. Must be initialized + // before to call any runtime function. + __ sub(Operand(esp), Immediate(kFastApiCallArguments * kPointerSize)); } // Check that the maps haven't changed. @@ -2208,7 +2250,17 @@ MaybeObject* CallStubCompiler::CompileCallConstant(Object* object, } if (depth != kInvalidProtoDepth) { - GenerateFastApiCall(masm(), optimization, argc); + Failure* failure; + // Move the return address on top of the stack. + __ mov(eax, Operand(esp, 3 * kPointerSize)); + __ mov(Operand(esp, 0 * kPointerSize), eax); + + // esp[2 * kPointerSize] is uninitialized, esp[3 * kPointerSize] contains + // duplicate of return address and will be overwritten. + bool success = GenerateFastApiCall(masm(), optimization, argc, &failure); + if (!success) { + return failure; + } } else { __ InvokeFunction(function, arguments(), JUMP_FUNCTION); } @@ -2216,7 +2268,7 @@ MaybeObject* CallStubCompiler::CompileCallConstant(Object* object, // Handle call cache miss. __ bind(&miss); if (depth != kInvalidProtoDepth) { - FreeSpaceForFastApiCall(masm(), eax); + __ add(Operand(esp), Immediate(kFastApiCallArguments * kPointerSize)); } __ bind(&miss_in_smi_check); Object* obj; @@ -2253,16 +2305,21 @@ MaybeObject* CallStubCompiler::CompileCallInterceptor(JSObject* object, __ mov(edx, Operand(esp, (argc + 1) * kPointerSize)); CallInterceptorCompiler compiler(this, arguments(), ecx); - compiler.Compile(masm(), - object, - holder, - name, - &lookup, - edx, - ebx, - edi, - eax, - &miss); + Failure* failure; + bool success = compiler.Compile(masm(), + object, + holder, + name, + &lookup, + edx, + ebx, + edi, + eax, + &miss, + &failure); + if (!success) { + return false; + } // Restore receiver. __ mov(edx, Operand(esp, (argc + 1) * kPointerSize)); diff --git a/deps/v8/src/ia32/virtual-frame-ia32.cc b/deps/v8/src/ia32/virtual-frame-ia32.cc index a31f6e8360..11e1aaf2b3 100644 --- a/deps/v8/src/ia32/virtual-frame-ia32.cc +++ b/deps/v8/src/ia32/virtual-frame-ia32.cc @@ -33,6 +33,7 @@ #include "register-allocator-inl.h" #include "scopes.h" #include "virtual-frame-inl.h" +#include "stub-cache.h" namespace v8 { namespace internal { @@ -1108,7 +1109,7 @@ Result VirtualFrame::CallCallIC(RelocInfo::Mode mode, // The IC expects the name in ecx and the rest on the stack and // drops them all. InLoopFlag in_loop = loop_nesting > 0 ? IN_LOOP : NOT_IN_LOOP; - Handle<Code> ic = cgen()->ComputeCallInitialize(arg_count, in_loop); + Handle<Code> ic = StubCache::ComputeCallInitialize(arg_count, in_loop); // Spill args, receiver, and function. The call will drop args and // receiver. Result name = Pop(); @@ -1126,7 +1127,7 @@ Result VirtualFrame::CallKeyedCallIC(RelocInfo::Mode mode, // The IC expects the name in ecx and the rest on the stack and // drops them all. InLoopFlag in_loop = loop_nesting > 0 ? IN_LOOP : NOT_IN_LOOP; - Handle<Code> ic = cgen()->ComputeKeyedCallInitialize(arg_count, in_loop); + Handle<Code> ic = StubCache::ComputeKeyedCallInitialize(arg_count, in_loop); // Spill args, receiver, and function. The call will drop args and // receiver. Result name = Pop(); diff --git a/deps/v8/src/jump-target-heavy.cc b/deps/v8/src/jump-target-heavy.cc index e0585e7942..c3c22f1ac6 100644 --- a/deps/v8/src/jump-target-heavy.cc +++ b/deps/v8/src/jump-target-heavy.cc @@ -414,8 +414,9 @@ void BreakTarget::Branch(Condition cc, Hint hint) { DeferredCode::DeferredCode() : masm_(CodeGeneratorScope::Current()->masm()), - statement_position_(masm_->current_statement_position()), - position_(masm_->current_position()), + statement_position_(masm_->positions_recorder()-> + current_statement_position()), + position_(masm_->positions_recorder()->current_position()), frame_state_(CodeGeneratorScope::Current()->frame()) { ASSERT(statement_position_ != RelocInfo::kNoPosition); ASSERT(position_ != RelocInfo::kNoPosition); diff --git a/deps/v8/src/jump-target-light.cc b/deps/v8/src/jump-target-light.cc index 19f7bfec0a..36dc176bce 100644 --- a/deps/v8/src/jump-target-light.cc +++ b/deps/v8/src/jump-target-light.cc @@ -36,8 +36,9 @@ namespace internal { DeferredCode::DeferredCode() : masm_(CodeGeneratorScope::Current()->masm()), - statement_position_(masm_->current_statement_position()), - position_(masm_->current_position()), + statement_position_(masm_->positions_recorder()-> + current_statement_position()), + position_(masm_->positions_recorder()->current_position()), frame_state_(*CodeGeneratorScope::Current()->frame()) { ASSERT(statement_position_ != RelocInfo::kNoPosition); ASSERT(position_ != RelocInfo::kNoPosition); diff --git a/deps/v8/src/list.h b/deps/v8/src/list.h index 5a08212b1e..24f34945b5 100644 --- a/deps/v8/src/list.h +++ b/deps/v8/src/list.h @@ -148,14 +148,6 @@ class List { DISALLOW_COPY_AND_ASSIGN(List); }; -class FrameElement; - -// Add() is inlined, ResizeAdd() called by Add() is inlined except for -// Lists of FrameElements, and ResizeAddInternal() is inlined in ResizeAdd(). -template <> -void List<FrameElement, - FreeStoreAllocationPolicy>::ResizeAdd(const FrameElement& element); - } } // namespace v8::internal #endif // V8_LIST_H_ diff --git a/deps/v8/src/mark-compact.cc b/deps/v8/src/mark-compact.cc index 484497f087..40194e3612 100644 --- a/deps/v8/src/mark-compact.cc +++ b/deps/v8/src/mark-compact.cc @@ -1099,13 +1099,6 @@ void MarkCompactCollector::MarkLiveObjects() { } -static int CountMarkedCallback(HeapObject* obj) { - MapWord map_word = obj->map_word(); - map_word.ClearMark(); - return obj->SizeFromMap(map_word.ToMap()); -} - - #ifdef DEBUG void MarkCompactCollector::UpdateLiveObjectCount(HeapObject* obj) { live_bytes_ += obj->Size(); @@ -1152,7 +1145,7 @@ bool MarkCompactCollector::SafeIsMap(HeapObject* object) { void MarkCompactCollector::ClearNonLiveTransitions() { - HeapObjectIterator map_iterator(Heap::map_space(), &CountMarkedCallback); + HeapObjectIterator map_iterator(Heap::map_space(), &SizeOfMarkedObject); // Iterate over the map space, setting map transitions that go from // a marked map to an unmarked map to null transitions. At the same time, // set all the prototype fields of maps back to their original value, @@ -2673,6 +2666,13 @@ void MarkCompactCollector::ReportDeleteIfNeeded(HeapObject* obj) { } +int MarkCompactCollector::SizeOfMarkedObject(HeapObject* obj) { + MapWord map_word = obj->map_word(); + map_word.ClearMark(); + return obj->SizeFromMap(map_word.ToMap()); +} + + void MarkCompactCollector::Initialize() { StaticPointersToNewGenUpdatingVisitor::Initialize(); StaticMarkingVisitor::Initialize(); diff --git a/deps/v8/src/mark-compact.h b/deps/v8/src/mark-compact.h index 7444c5bf3c..1b7e60022e 100644 --- a/deps/v8/src/mark-compact.h +++ b/deps/v8/src/mark-compact.h @@ -119,6 +119,9 @@ class MarkCompactCollector: public AllStatic { // Determine type of object and emit deletion log event. static void ReportDeleteIfNeeded(HeapObject* obj); + // Returns size of a possibly marked object. + static int SizeOfMarkedObject(HeapObject* obj); + // Distinguishable invalid map encodings (for single word and multiple words) // that indicate free regions. static const uint32_t kSingleFreeEncoding = 0; diff --git a/deps/v8/src/objects-debug.cc b/deps/v8/src/objects-debug.cc index c0e5610aba..69219ee364 100644 --- a/deps/v8/src/objects-debug.cc +++ b/deps/v8/src/objects-debug.cc @@ -982,7 +982,6 @@ void AccessorInfo::AccessorInfoVerify() { VerifyPointer(name()); VerifyPointer(data()); VerifyPointer(flag()); - VerifyPointer(load_stub_cache()); } void AccessorInfo::AccessorInfoPrint() { @@ -1054,6 +1053,7 @@ void CallHandlerInfo::CallHandlerInfoPrint() { callback()->ShortPrint(); PrintF("\n - data: "); data()->ShortPrint(); + PrintF("\n - call_stub_cache: "); } void TemplateInfo::TemplateInfoVerify() { diff --git a/deps/v8/src/objects-inl.h b/deps/v8/src/objects-inl.h index 1852b549bf..499cb91dde 100644 --- a/deps/v8/src/objects-inl.h +++ b/deps/v8/src/objects-inl.h @@ -2542,7 +2542,6 @@ ACCESSORS(AccessorInfo, setter, Object, kSetterOffset) ACCESSORS(AccessorInfo, data, Object, kDataOffset) ACCESSORS(AccessorInfo, name, Object, kNameOffset) ACCESSORS(AccessorInfo, flag, Smi, kFlagOffset) -ACCESSORS(AccessorInfo, load_stub_cache, Object, kLoadStubCacheOffset) ACCESSORS(AccessCheckInfo, named_callback, Object, kNamedCallbackOffset) ACCESSORS(AccessCheckInfo, indexed_callback, Object, kIndexedCallbackOffset) @@ -2671,6 +2670,7 @@ SMI_ACCESSORS(SharedFunctionInfo, this_property_assignments_count, #else #define PSEUDO_SMI_ACCESSORS_LO(holder, name, offset) \ + STATIC_ASSERT(holder::offset % kPointerSize == 0); \ int holder::name() { \ int value = READ_INT_FIELD(this, offset); \ ASSERT(kHeapObjectTag == 1); \ @@ -2686,30 +2686,36 @@ SMI_ACCESSORS(SharedFunctionInfo, this_property_assignments_count, (value << 1) & ~kHeapObjectTag); \ } -#define PSEUDO_SMI_ACCESSORS_HI(holder, name, offset) \ +#define PSEUDO_SMI_ACCESSORS_HI(holder, name, offset) \ + STATIC_ASSERT(holder::offset % kPointerSize == kIntSize); \ INT_ACCESSORS(holder, name, offset) - PSEUDO_SMI_ACCESSORS_LO(SharedFunctionInfo, length, kLengthOffset) -PSEUDO_SMI_ACCESSORS_HI(SharedFunctionInfo, formal_parameter_count, - kFormalParameterCountOffset) +PSEUDO_SMI_ACCESSORS_HI(SharedFunctionInfo, + formal_parameter_count, + kFormalParameterCountOffset) -PSEUDO_SMI_ACCESSORS_LO(SharedFunctionInfo, expected_nof_properties, - kExpectedNofPropertiesOffset) +PSEUDO_SMI_ACCESSORS_LO(SharedFunctionInfo, + expected_nof_properties, + kExpectedNofPropertiesOffset) PSEUDO_SMI_ACCESSORS_HI(SharedFunctionInfo, num_literals, kNumLiteralsOffset) -PSEUDO_SMI_ACCESSORS_LO(SharedFunctionInfo, start_position_and_type, - kStartPositionAndTypeOffset) -PSEUDO_SMI_ACCESSORS_HI(SharedFunctionInfo, end_position, kEndPositionOffset) - -PSEUDO_SMI_ACCESSORS_LO(SharedFunctionInfo, function_token_position, - kFunctionTokenPositionOffset) -PSEUDO_SMI_ACCESSORS_HI(SharedFunctionInfo, compiler_hints, - kCompilerHintsOffset) - -PSEUDO_SMI_ACCESSORS_LO(SharedFunctionInfo, this_property_assignments_count, - kThisPropertyAssignmentsCountOffset) +PSEUDO_SMI_ACCESSORS_LO(SharedFunctionInfo, end_position, kEndPositionOffset) +PSEUDO_SMI_ACCESSORS_HI(SharedFunctionInfo, + start_position_and_type, + kStartPositionAndTypeOffset) + +PSEUDO_SMI_ACCESSORS_LO(SharedFunctionInfo, + function_token_position, + kFunctionTokenPositionOffset) +PSEUDO_SMI_ACCESSORS_HI(SharedFunctionInfo, + compiler_hints, + kCompilerHintsOffset) + +PSEUDO_SMI_ACCESSORS_LO(SharedFunctionInfo, + this_property_assignments_count, + kThisPropertyAssignmentsCountOffset) #endif diff --git a/deps/v8/src/objects.cc b/deps/v8/src/objects.cc index c1cb922de5..8efb0daae6 100644 --- a/deps/v8/src/objects.cc +++ b/deps/v8/src/objects.cc @@ -35,7 +35,7 @@ #include "objects-inl.h" #include "objects-visiting.h" #include "macro-assembler.h" -#include "scanner.h" +#include "scanner-base.h" #include "scopeinfo.h" #include "string-stream.h" #include "utils.h" @@ -1208,7 +1208,8 @@ MaybeObject* JSObject::AddFastProperty(String* name, // Normalize the object if the name is an actual string (not the // hidden symbols) and is not a real identifier. StringInputBuffer buffer(name); - if (!Scanner::IsIdentifier(&buffer) && name != Heap::hidden_symbol()) { + if (!ScannerConstants::IsIdentifier(&buffer) + && name != Heap::hidden_symbol()) { Object* obj; { MaybeObject* maybe_obj = NormalizeProperties(CLEAR_INOBJECT_PROPERTIES, 0); @@ -5088,7 +5089,8 @@ bool String::MarkAsUndetectable() { bool String::IsEqualTo(Vector<const char> str) { int slen = length(); - Access<Scanner::Utf8Decoder> decoder(Scanner::utf8_decoder()); + Access<ScannerConstants::Utf8Decoder> + decoder(ScannerConstants::utf8_decoder()); decoder->Reset(str.start(), str.length()); int i; for (i = 0; i < slen && decoder->has_more(); i++) { diff --git a/deps/v8/src/objects.h b/deps/v8/src/objects.h index 6029ad545b..b52bac27be 100644 --- a/deps/v8/src/objects.h +++ b/deps/v8/src/objects.h @@ -5327,7 +5327,6 @@ class AccessorInfo: public Struct { DECL_ACCESSORS(data, Object) DECL_ACCESSORS(name, Object) DECL_ACCESSORS(flag, Smi) - DECL_ACCESSORS(load_stub_cache, Object) inline bool all_can_read(); inline void set_all_can_read(bool value); @@ -5353,8 +5352,7 @@ class AccessorInfo: public Struct { static const int kDataOffset = kSetterOffset + kPointerSize; static const int kNameOffset = kDataOffset + kPointerSize; static const int kFlagOffset = kNameOffset + kPointerSize; - static const int kLoadStubCacheOffset = kFlagOffset + kPointerSize; - static const int kSize = kLoadStubCacheOffset + kPointerSize; + static const int kSize = kFlagOffset + kPointerSize; private: // Bit positions in flag. diff --git a/deps/v8/src/parser.cc b/deps/v8/src/parser.cc index aad7a615a5..a9ba0e7169 100644 --- a/deps/v8/src/parser.cc +++ b/deps/v8/src/parser.cc @@ -36,9 +36,10 @@ #include "messages.h" #include "parser.h" #include "platform.h" +#include "preparser.h" +#include "prescanner.h" #include "runtime.h" #include "scopeinfo.h" -#include "scopes.h" #include "string-stream.h" #include "ast-inl.h" @@ -323,302 +324,96 @@ TemporaryScope::~TemporaryScope() { } -// A zone list wrapper lets code either access a access a zone list -// or appear to do so while actually ignoring all operations. -template <typename T> -class ZoneListWrapper { - public: - ZoneListWrapper() : list_(NULL) { } - explicit ZoneListWrapper(int size) : list_(new ZoneList<T*>(size)) { } - void Add(T* that) { if (list_) list_->Add(that); } - int length() { return list_->length(); } - ZoneList<T*>* elements() { return list_; } - T* at(int index) { return list_->at(index); } - private: - ZoneList<T*>* list_; -}; - - -// Allocation macro that should be used to allocate objects that must -// only be allocated in real parsing mode. Note that in preparse mode -// not only is the syntax tree not created but the constructor -// arguments are not evaluated. -#define NEW(expr) (is_pre_parsing_ ? NULL : new expr) - - -class ParserFactory BASE_EMBEDDED { - public: - explicit ParserFactory(bool is_pre_parsing) : - is_pre_parsing_(is_pre_parsing) { } - - virtual ~ParserFactory() { } - - virtual Scope* NewScope(Scope* parent, Scope::Type type, bool inside_with); - - virtual Handle<String> LookupSymbol(int index, Vector<const char> string) { - return Handle<String>(); - } - - virtual Handle<String> EmptySymbol() { - return Handle<String>(); - } - - virtual Expression* NewProperty(Expression* obj, Expression* key, int pos) { - if (obj == VariableProxySentinel::this_proxy()) { - return Property::this_property(); - } else { - return ValidLeftHandSideSentinel::instance(); - } - } - - virtual Expression* NewCall(Expression* expression, - ZoneList<Expression*>* arguments, - int pos) { - return Call::sentinel(); - } - - virtual Statement* EmptyStatement() { - return NULL; - } - - template <typename T> ZoneListWrapper<T> NewList(int size) { - return is_pre_parsing_ ? ZoneListWrapper<T>() : ZoneListWrapper<T>(size); - } - - private: - bool is_pre_parsing_; -}; - - -class ParserLog BASE_EMBEDDED { - public: - virtual ~ParserLog() { } - - // Records the occurrence of a function. - virtual FunctionEntry LogFunction(int start) { return FunctionEntry(); } - virtual void LogSymbol(int start, Vector<const char> symbol) {} - virtual void LogError() { } - // Return the current position in the function entry log. - virtual int function_position() { return 0; } - virtual int symbol_position() { return 0; } - virtual int symbol_ids() { return 0; } - virtual void PauseRecording() {} - virtual void ResumeRecording() {} - virtual Vector<unsigned> ExtractData() { - return Vector<unsigned>(); - }; -}; - - - -class ConditionalLogPauseScope { - public: - ConditionalLogPauseScope(bool pause, ParserLog* log) - : log_(log), pause_(pause) { - if (pause) log->PauseRecording(); - } - ~ConditionalLogPauseScope() { - if (pause_) log_->ResumeRecording(); - } - private: - ParserLog* log_; - bool pause_; -}; - - -class AstBuildingParserFactory : public ParserFactory { - public: - explicit AstBuildingParserFactory(int expected_symbols) - : ParserFactory(false), symbol_cache_(expected_symbols) { } - - virtual Scope* NewScope(Scope* parent, Scope::Type type, bool inside_with); - - virtual Handle<String> LookupSymbol(int symbol_id, - Vector<const char> string) { - // Length of symbol cache is the number of identified symbols. - // If we are larger than that, or negative, it's not a cached symbol. - // This might also happen if there is no preparser symbol data, even - // if there is some preparser data. - if (static_cast<unsigned>(symbol_id) - >= static_cast<unsigned>(symbol_cache_.length())) { - return Factory::LookupSymbol(string); - } - return LookupCachedSymbol(symbol_id, string); - } - - Handle<String> LookupCachedSymbol(int symbol_id, +Handle<String> Parser::LookupSymbol(int symbol_id, Vector<const char> string) { - // Make sure the cache is large enough to hold the symbol identifier. - if (symbol_cache_.length() <= symbol_id) { - // Increase length to index + 1. - symbol_cache_.AddBlock(Handle<String>::null(), - symbol_id + 1 - symbol_cache_.length()); - } - Handle<String> result = symbol_cache_.at(symbol_id); - if (result.is_null()) { - result = Factory::LookupSymbol(string); - symbol_cache_.at(symbol_id) = result; - return result; - } - Counters::total_preparse_symbols_skipped.Increment(); + // Length of symbol cache is the number of identified symbols. + // If we are larger than that, or negative, it's not a cached symbol. + // This might also happen if there is no preparser symbol data, even + // if there is some preparser data. + if (static_cast<unsigned>(symbol_id) + >= static_cast<unsigned>(symbol_cache_.length())) { + return Factory::LookupSymbol(string); + } + return LookupCachedSymbol(symbol_id, string); +} + + +Handle<String> Parser::LookupCachedSymbol(int symbol_id, + Vector<const char> string) { + // Make sure the cache is large enough to hold the symbol identifier. + if (symbol_cache_.length() <= symbol_id) { + // Increase length to index + 1. + symbol_cache_.AddBlock(Handle<String>::null(), + symbol_id + 1 - symbol_cache_.length()); + } + Handle<String> result = symbol_cache_.at(symbol_id); + if (result.is_null()) { + result = Factory::LookupSymbol(string); + symbol_cache_.at(symbol_id) = result; return result; } - - virtual Handle<String> EmptySymbol() { - return Factory::empty_symbol(); - } - - virtual Expression* NewProperty(Expression* obj, Expression* key, int pos) { - return new Property(obj, key, pos); - } - - virtual Expression* NewCall(Expression* expression, - ZoneList<Expression*>* arguments, - int pos) { - return new Call(expression, arguments, pos); - } - - virtual Statement* EmptyStatement(); - private: - List<Handle<String> > symbol_cache_; -}; + Counters::total_preparse_symbols_skipped.Increment(); + return result; +} -// Record only functions. -class PartialParserRecorder: public ParserLog { - public: - PartialParserRecorder(); - virtual FunctionEntry LogFunction(int start); - - virtual int function_position() { return function_store_.size(); } - - virtual void LogError() { } - - virtual void LogMessage(Scanner::Location loc, - const char* message, - Vector<const char*> args); - - virtual Vector<unsigned> ExtractData() { - int function_size = function_store_.size(); - int total_size = ScriptDataImpl::kHeaderSize + function_size; - Vector<unsigned> data = Vector<unsigned>::New(total_size); - preamble_[ScriptDataImpl::kFunctionsSizeOffset] = function_size; - preamble_[ScriptDataImpl::kSymbolCountOffset] = 0; - memcpy(data.start(), preamble_, sizeof(preamble_)); - int symbol_start = ScriptDataImpl::kHeaderSize + function_size; - if (function_size > 0) { - function_store_.WriteTo(data.SubVector(ScriptDataImpl::kHeaderSize, - symbol_start)); - } - return data; +Vector<unsigned> PartialParserRecorder::ExtractData() { + int function_size = function_store_.size(); + int total_size = ScriptDataImpl::kHeaderSize + function_size; + Vector<unsigned> data = Vector<unsigned>::New(total_size); + preamble_[ScriptDataImpl::kFunctionsSizeOffset] = function_size; + preamble_[ScriptDataImpl::kSymbolCountOffset] = 0; + memcpy(data.start(), preamble_, sizeof(preamble_)); + int symbol_start = ScriptDataImpl::kHeaderSize + function_size; + if (function_size > 0) { + function_store_.WriteTo(data.SubVector(ScriptDataImpl::kHeaderSize, + symbol_start)); } + return data; +} - virtual void PauseRecording() { - pause_count_++; - is_recording_ = false; - } - virtual void ResumeRecording() { - ASSERT(pause_count_ > 0); - if (--pause_count_ == 0) is_recording_ = !has_error(); - } +void CompleteParserRecorder::LogSymbol(int start, Vector<const char> literal) { + if (!is_recording_) return; - protected: - bool has_error() { - return static_cast<bool>(preamble_[ScriptDataImpl::kHasErrorOffset]); + int hash = vector_hash(literal); + HashMap::Entry* entry = symbol_table_.Lookup(&literal, hash, true); + int id = static_cast<int>(reinterpret_cast<intptr_t>(entry->value)); + if (id == 0) { + // Put (symbol_id_ + 1) into entry and increment it. + id = ++symbol_id_; + entry->value = reinterpret_cast<void*>(id); + Vector<Vector<const char> > symbol = symbol_entries_.AddBlock(1, literal); + entry->key = &symbol[0]; } - bool is_recording() { - return is_recording_; - } - - void WriteString(Vector<const char> str); - - Collector<unsigned> function_store_; - unsigned preamble_[ScriptDataImpl::kHeaderSize]; - bool is_recording_; - int pause_count_; - -#ifdef DEBUG - int prev_start; -#endif -}; - + WriteNumber(id - 1); +} -// Record both functions and symbols. -class CompleteParserRecorder: public PartialParserRecorder { - public: - CompleteParserRecorder(); - - virtual void LogSymbol(int start, Vector<const char> literal) { - if (!is_recording_) return; - int hash = vector_hash(literal); - HashMap::Entry* entry = symbol_table_.Lookup(&literal, hash, true); - int id = static_cast<int>(reinterpret_cast<intptr_t>(entry->value)); - if (id == 0) { - // Put (symbol_id_ + 1) into entry and increment it. - id = ++symbol_id_; - entry->value = reinterpret_cast<void*>(id); - Vector<Vector<const char> > symbol = symbol_entries_.AddBlock(1, literal); - entry->key = &symbol[0]; - } - WriteNumber(id - 1); - } - - virtual Vector<unsigned> ExtractData() { - int function_size = function_store_.size(); - // Add terminator to symbols, then pad to unsigned size. - int symbol_size = symbol_store_.size(); - int padding = sizeof(unsigned) - (symbol_size % sizeof(unsigned)); - symbol_store_.AddBlock(padding, ScriptDataImpl::kNumberTerminator); - symbol_size += padding; - int total_size = ScriptDataImpl::kHeaderSize + function_size - + (symbol_size / sizeof(unsigned)); - Vector<unsigned> data = Vector<unsigned>::New(total_size); - preamble_[ScriptDataImpl::kFunctionsSizeOffset] = function_size; - preamble_[ScriptDataImpl::kSymbolCountOffset] = symbol_id_; - memcpy(data.start(), preamble_, sizeof(preamble_)); - int symbol_start = ScriptDataImpl::kHeaderSize + function_size; - if (function_size > 0) { - function_store_.WriteTo(data.SubVector(ScriptDataImpl::kHeaderSize, - symbol_start)); - } - if (!has_error()) { - symbol_store_.WriteTo( - Vector<byte>::cast(data.SubVector(symbol_start, total_size))); - } - return data; - } - virtual int symbol_position() { return symbol_store_.size(); } - virtual int symbol_ids() { return symbol_id_; } - private: - static int vector_hash(Vector<const char> string) { - int hash = 0; - for (int i = 0; i < string.length(); i++) { - int c = string[i]; - hash += c; - hash += (hash << 10); - hash ^= (hash >> 6); - } - return hash; +Vector<unsigned> CompleteParserRecorder::ExtractData() { + int function_size = function_store_.size(); + // Add terminator to symbols, then pad to unsigned size. + int symbol_size = symbol_store_.size(); + int padding = sizeof(unsigned) - (symbol_size % sizeof(unsigned)); + symbol_store_.AddBlock(padding, ScriptDataImpl::kNumberTerminator); + symbol_size += padding; + int total_size = ScriptDataImpl::kHeaderSize + function_size + + (symbol_size / sizeof(unsigned)); + Vector<unsigned> data = Vector<unsigned>::New(total_size); + preamble_[ScriptDataImpl::kFunctionsSizeOffset] = function_size; + preamble_[ScriptDataImpl::kSymbolCountOffset] = symbol_id_; + memcpy(data.start(), preamble_, sizeof(preamble_)); + int symbol_start = ScriptDataImpl::kHeaderSize + function_size; + if (function_size > 0) { + function_store_.WriteTo(data.SubVector(ScriptDataImpl::kHeaderSize, + symbol_start)); } - - static bool vector_compare(void* a, void* b) { - Vector<const char>* string1 = reinterpret_cast<Vector<const char>* >(a); - Vector<const char>* string2 = reinterpret_cast<Vector<const char>* >(b); - int length = string1->length(); - if (string2->length() != length) return false; - return memcmp(string1->start(), string2->start(), length) == 0; + if (!has_error()) { + symbol_store_.WriteTo( + Vector<byte>::cast(data.SubVector(symbol_start, total_size))); } - - // Write a non-negative number to the symbol store. - void WriteNumber(int number); - - Collector<byte> symbol_store_; - Collector<Vector<const char> > symbol_entries_; - HashMap symbol_table_; - int symbol_id_; -}; + return data; +} FunctionEntry ScriptDataImpl::GetFunctionEntry(int start) { @@ -691,7 +486,7 @@ PartialParserRecorder::PartialParserRecorder() preamble_[ScriptDataImpl::kSizeOffset] = 0; ASSERT_EQ(6, ScriptDataImpl::kHeaderSize); #ifdef DEBUG - prev_start = -1; + prev_start_ = -1; #endif } @@ -742,8 +537,8 @@ const char* ScriptDataImpl::ReadString(unsigned* start, int* chars) { void PartialParserRecorder::LogMessage(Scanner::Location loc, - const char* message, - Vector<const char*> args) { + const char* message, + Vector<const char*> args) { if (has_error()) return; preamble_[ScriptDataImpl::kHasErrorOffset] = true; function_store_.Reset(); @@ -800,120 +595,12 @@ unsigned* ScriptDataImpl::ReadAddress(int position) { } -FunctionEntry PartialParserRecorder::LogFunction(int start) { -#ifdef DEBUG - ASSERT(start > prev_start); - prev_start = start; -#endif - if (!is_recording_) return FunctionEntry(); - FunctionEntry result(function_store_.AddBlock(FunctionEntry::kSize, 0)); - result.set_start_pos(start); - return result; -} - - -class AstBuildingParser : public Parser { - public: - AstBuildingParser(Handle<Script> script, bool allow_natives_syntax, - v8::Extension* extension, ScriptDataImpl* pre_data) - : Parser(script, - allow_natives_syntax, - extension, - PARSE, - factory(), - log(), - pre_data), - factory_(pre_data ? pre_data->symbol_count() : 0) { } - virtual void ReportMessageAt(Scanner::Location loc, const char* message, - Vector<const char*> args); - virtual VariableProxy* Declare(Handle<String> name, Variable::Mode mode, - FunctionLiteral* fun, bool resolve, bool* ok); - AstBuildingParserFactory* factory() { return &factory_; } - ParserLog* log() { return &log_; } - - private: - ParserLog log_; - AstBuildingParserFactory factory_; -}; - - -class PreParser : public Parser { - public: - PreParser(Handle<Script> script, bool allow_natives_syntax, - v8::Extension* extension, ParserLog* recorder) - : Parser(script, allow_natives_syntax, extension, PREPARSE, - factory(), recorder, NULL), - factory_(true) { } - virtual void ReportMessageAt(Scanner::Location loc, const char* message, - Vector<const char*> args); - virtual VariableProxy* Declare(Handle<String> name, Variable::Mode mode, - FunctionLiteral* fun, bool resolve, bool* ok); - ParserFactory* factory() { return &factory_; } - virtual PartialParserRecorder* recorder() = 0; - - private: - ParserFactory factory_; -}; - - -class CompletePreParser : public PreParser { - public: - CompletePreParser(Handle<Script> script, bool allow_natives_syntax, - v8::Extension* extension) - : PreParser(script, allow_natives_syntax, extension, &recorder_), - recorder_() { } - virtual PartialParserRecorder* recorder() { return &recorder_; } - private: - CompleteParserRecorder recorder_; -}; - - -class PartialPreParser : public PreParser { - public: - PartialPreParser(Handle<Script> script, bool allow_natives_syntax, - v8::Extension* extension) - : PreParser(script, allow_natives_syntax, extension, &recorder_), - recorder_() { } - virtual PartialParserRecorder* recorder() { return &recorder_; } - private: - PartialParserRecorder recorder_; -}; - - -Scope* AstBuildingParserFactory::NewScope(Scope* parent, Scope::Type type, - bool inside_with) { +Scope* Parser::NewScope(Scope* parent, Scope::Type type, bool inside_with) { Scope* result = new Scope(parent, type); result->Initialize(inside_with); return result; } - -Statement* AstBuildingParserFactory::EmptyStatement() { - // Use a statically allocated empty statement singleton to avoid - // allocating lots and lots of empty statements. - static v8::internal::EmptyStatement empty; - return ∅ -} - - -Scope* ParserFactory::NewScope(Scope* parent, Scope::Type type, - bool inside_with) { - ASSERT(parent != NULL); - parent->type_ = type; - // Initialize function is hijacked by DummyScope to increment scope depth. - parent->Initialize(inside_with); - return parent; -} - - -VariableProxy* PreParser::Declare(Handle<String> name, Variable::Mode mode, - FunctionLiteral* fun, bool resolve, - bool* ok) { - return NULL; -} - - - // ---------------------------------------------------------------------------- // Target is a support class to facilitate manipulation of the // Parser's target_stack_ (the stack of potential 'break' and @@ -1016,11 +703,9 @@ class LexicalScope BASE_EMBEDDED { Parser::Parser(Handle<Script> script, bool allow_natives_syntax, v8::Extension* extension, - ParserMode is_pre_parsing, - ParserFactory* factory, - ParserLog* log, ScriptDataImpl* pre_data) - : script_(script), + : symbol_cache_(pre_data ? pre_data->symbol_count() : 0), + script_(script), scanner_(), top_scope_(NULL), with_nesting_level_(0), @@ -1028,34 +713,11 @@ Parser::Parser(Handle<Script> script, target_stack_(NULL), allow_natives_syntax_(allow_natives_syntax), extension_(extension), - factory_(factory), - log_(log), - is_pre_parsing_(is_pre_parsing == PREPARSE), pre_data_(pre_data), fni_(NULL) { } -bool Parser::PreParseProgram(Handle<String> source, - unibrow::CharacterStream* stream) { - HistogramTimerScope timer(&Counters::pre_parse); - AssertNoZoneAllocation assert_no_zone_allocation; - AssertNoAllocation assert_no_allocation; - NoHandleAllocation no_handle_allocation; - scanner_.Initialize(source, stream, JAVASCRIPT); - ASSERT(target_stack_ == NULL); - mode_ = FLAG_lazy ? PARSE_LAZILY : PARSE_EAGERLY; - if (allow_natives_syntax_ || extension_ != NULL) mode_ = PARSE_EAGERLY; - DummyScope top_scope; - LexicalScope scope(&this->top_scope_, &this->with_nesting_level_, &top_scope); - TemporaryScope temp_scope(&this->temp_scope_); - ZoneListWrapper<Statement> processor; - bool ok = true; - ParseSourceElements(&processor, Token::EOS, &ok); - return !scanner().stack_overflow(); -} - - FunctionLiteral* Parser::ParseProgram(Handle<String> source, bool in_global_context) { CompilationZoneScope zone_scope(DONT_DELETE_ON_EXIT); @@ -1078,21 +740,21 @@ FunctionLiteral* Parser::ParseProgram(Handle<String> source, in_global_context ? Scope::GLOBAL_SCOPE : Scope::EVAL_SCOPE; - Handle<String> no_name = factory()->EmptySymbol(); + Handle<String> no_name = Factory::empty_symbol(); FunctionLiteral* result = NULL; - { Scope* scope = factory()->NewScope(top_scope_, type, inside_with()); + { Scope* scope = NewScope(top_scope_, type, inside_with()); LexicalScope lexical_scope(&this->top_scope_, &this->with_nesting_level_, scope); TemporaryScope temp_scope(&this->temp_scope_); - ZoneListWrapper<Statement> body(16); + ZoneList<Statement*>* body = new ZoneList<Statement*>(16); bool ok = true; - ParseSourceElements(&body, Token::EOS, &ok); + ParseSourceElements(body, Token::EOS, &ok); if (ok) { - result = NEW(FunctionLiteral( + result = new FunctionLiteral( no_name, top_scope_, - body.elements(), + body, temp_scope.materialized_literal_count(), temp_scope.expected_property_count(), temp_scope.only_simple_this_property_assignments(), @@ -1101,7 +763,7 @@ FunctionLiteral* Parser::ParseProgram(Handle<String> source, 0, source->length(), false, - temp_scope.ContainsLoops())); + temp_scope.ContainsLoops()); } else if (scanner().stack_overflow()) { Top::StackOverflow(); } @@ -1139,9 +801,9 @@ FunctionLiteral* Parser::ParseLazy(Handle<SharedFunctionInfo> info) { { // Parse the function literal. - Handle<String> no_name = factory()->EmptySymbol(); + Handle<String> no_name = Factory::empty_symbol(); Scope* scope = - factory()->NewScope(top_scope_, Scope::GLOBAL_SCOPE, inside_with()); + NewScope(top_scope_, Scope::GLOBAL_SCOPE, inside_with()); LexicalScope lexical_scope(&this->top_scope_, &this->with_nesting_level_, scope); TemporaryScope temp_scope(&this->temp_scope_); @@ -1169,28 +831,24 @@ FunctionLiteral* Parser::ParseLazy(Handle<SharedFunctionInfo> info) { } -void Parser::ReportMessage(const char* type, Vector<const char*> args) { - Scanner::Location source_location = scanner_.location(); - ReportMessageAt(source_location, type, args); -} - - Handle<String> Parser::GetSymbol(bool* ok) { - if (is_pre_parsing_) { - log()->LogSymbol(scanner_.location().beg_pos, scanner_.literal()); - return Handle<String>::null(); - } int symbol_id = -1; if (pre_data() != NULL) { symbol_id = pre_data()->GetSymbolIdentifier(); } - return factory()->LookupSymbol(symbol_id, scanner_.literal()); + return LookupSymbol(symbol_id, scanner_.literal()); +} + + +void Parser::ReportMessage(const char* type, Vector<const char*> args) { + Scanner::Location source_location = scanner_.location(); + ReportMessageAt(source_location, type, args); } -void AstBuildingParser::ReportMessageAt(Scanner::Location source_location, - const char* type, - Vector<const char*> args) { +void Parser::ReportMessageAt(Scanner::Location source_location, + const char* type, + Vector<const char*> args) { MessageLocation location(script_, source_location.beg_pos, source_location.end_pos); Handle<JSArray> array = Factory::NewJSArray(args.length()); @@ -1202,13 +860,6 @@ void AstBuildingParser::ReportMessageAt(Scanner::Location source_location, } -void PreParser::ReportMessageAt(Scanner::Location source_location, - const char* type, - Vector<const char*> args) { - recorder()->LogMessage(source_location, type, args); -} - - // Base class containing common code for the different finder classes used by // the parser. class ParserFinder { @@ -1250,6 +901,11 @@ class InitializationBlockFinder : public ParserFinder { } private: + // The minimum number of contiguous assignment that will + // be treated as an initialization block. Benchmarks show that + // the overhead exceeds the savings below this limit. + static const int kMinInitializationBlock = 3; + // Returns true if the expressions appear to denote the same object. // In the context of initialization blocks, we only consider expressions // of the form 'expr.x' or expr["x"]. @@ -1302,7 +958,7 @@ class InitializationBlockFinder : public ParserFinder { } void EndBlock() { - if (block_size_ >= Parser::kMinInitializationBlock) { + if (block_size_ >= kMinInitializationBlock) { first_in_block_->mark_block_start(); last_in_block_->mark_block_end(); } @@ -1460,7 +1116,7 @@ class ThisNamedPropertyAssigmentFinder : public ParserFinder { }; -void* Parser::ParseSourceElements(ZoneListWrapper<Statement>* processor, +void* Parser::ParseSourceElements(ZoneList<Statement*>* processor, int end_token, bool* ok) { // SourceElements :: @@ -1492,7 +1148,7 @@ void* Parser::ParseSourceElements(ZoneListWrapper<Statement>* processor, } // Propagate the collected information on this property assignments. - if (!is_pre_parsing_ && top_scope_->is_function_scope()) { + if (top_scope_->is_function_scope()) { bool only_simple_this_property_assignments = this_property_assignment_finder.only_simple_this_property_assignments() && top_scope_->declarations()->length() == 0; @@ -1545,7 +1201,7 @@ Statement* Parser::ParseStatement(ZoneStringList* labels, bool* ok) { case Token::SEMICOLON: Next(); - return factory()->EmptyStatement(); + return EmptyStatement(); case Token::IF: stmt = ParseIfStatement(labels, ok); @@ -1593,7 +1249,7 @@ Statement* Parser::ParseStatement(ZoneStringList* labels, bool* ok) { // one must take great care not to treat it as a // fall-through. It is much easier just to wrap the entire // try-statement in a statement block and put the labels there - Block* result = NEW(Block(labels, 1, false)); + Block* result = new Block(labels, 1, false); Target target(&this->target_stack_, result); TryStatement* statement = ParseTryStatement(CHECK_OK); if (statement) { @@ -1623,11 +1279,11 @@ Statement* Parser::ParseStatement(ZoneStringList* labels, bool* ok) { } -VariableProxy* AstBuildingParser::Declare(Handle<String> name, - Variable::Mode mode, - FunctionLiteral* fun, - bool resolve, - bool* ok) { +VariableProxy* Parser::Declare(Handle<String> name, + Variable::Mode mode, + FunctionLiteral* fun, + bool resolve, + bool* ok) { Variable* var = NULL; // If we are inside a function, a declaration of a variable // is a truly local variable, and the scope of the variable @@ -1682,13 +1338,13 @@ VariableProxy* AstBuildingParser::Declare(Handle<String> name, // a performance issue since it may lead to repeated // Runtime::DeclareContextSlot() calls. VariableProxy* proxy = top_scope_->NewUnresolved(name, inside_with()); - top_scope_->AddDeclaration(NEW(Declaration(proxy, mode, fun))); + top_scope_->AddDeclaration(new Declaration(proxy, mode, fun)); // For global const variables we bind the proxy to a variable. if (mode == Variable::CONST && top_scope_->is_global_scope()) { ASSERT(resolve); // should be set by all callers Variable::Kind kind = Variable::NORMAL; - var = NEW(Variable(top_scope_, name, Variable::CONST, true, kind)); + var = new Variable(top_scope_, name, Variable::CONST, true, kind); } // If requested and we have a local variable, bind the proxy to the variable @@ -1740,13 +1396,13 @@ Statement* Parser::ParseNativeDeclaration(bool* ok) { while (!done) { ParseIdentifier(CHECK_OK); done = (peek() == Token::RPAREN); - if (!done) Expect(Token::COMMA, CHECK_OK); + if (!done) { + Expect(Token::COMMA, CHECK_OK); + } } Expect(Token::RPAREN, CHECK_OK); Expect(Token::SEMICOLON, CHECK_OK); - if (is_pre_parsing_) return NULL; - // Make sure that the function containing the native declaration // isn't lazily compiled. The extension structures are only // accessible while parsing the first time not when reparsing @@ -1776,10 +1432,10 @@ Statement* Parser::ParseNativeDeclaration(bool* ok) { // TODO(1240846): It's weird that native function declarations are // introduced dynamically when we meet their declarations, whereas // other functions are setup when entering the surrounding scope. - SharedFunctionInfoLiteral* lit = NEW(SharedFunctionInfoLiteral(shared)); + SharedFunctionInfoLiteral* lit = new SharedFunctionInfoLiteral(shared); VariableProxy* var = Declare(name, Variable::VAR, NULL, true, CHECK_OK); - return NEW(ExpressionStatement( - new Assignment(Token::INIT_VAR, var, lit, RelocInfo::kNoPosition))); + return new ExpressionStatement( + new Assignment(Token::INIT_VAR, var, lit, RelocInfo::kNoPosition)); } @@ -1797,7 +1453,7 @@ Statement* Parser::ParseFunctionDeclaration(bool* ok) { // scope, we treat is as such and introduce the function with it's // initial value upon entering the corresponding scope. Declare(name, Variable::VAR, fun, true, CHECK_OK); - return factory()->EmptyStatement(); + return EmptyStatement(); } @@ -1809,7 +1465,7 @@ Block* Parser::ParseBlock(ZoneStringList* labels, bool* ok) { // (ECMA-262, 3rd, 12.2) // // Construct block expecting 16 statements. - Block* result = NEW(Block(labels, 16, false)); + Block* result = new Block(labels, 16, false); Target target(&this->target_stack_, result); Expect(Token::LBRACE, CHECK_OK); while (peek() != Token::RBRACE) { @@ -1868,7 +1524,7 @@ Block* Parser::ParseVariableDeclarations(bool accept_IN, // is inside an initializer block, it is ignored. // // Create new block with one expected declaration. - Block* block = NEW(Block(NULL, 1, true)); + Block* block = new Block(NULL, 1, true); VariableProxy* last_var = NULL; // the last variable declared int nvars = 0; // the number of variables declared do { @@ -1959,14 +1615,14 @@ Block* Parser::ParseVariableDeclarations(bool accept_IN, // browsers where the global object (window) has lots of // properties defined in prototype objects. - if (!is_pre_parsing_ && top_scope_->is_global_scope()) { + if (top_scope_->is_global_scope()) { // Compute the arguments for the runtime call. ZoneList<Expression*>* arguments = new ZoneList<Expression*>(2); // Be careful not to assign a value to the global variable if // we're in a with. The initialization value should not // necessarily be stored in the global object in that case, // which is why we need to generate a separate assignment node. - arguments->Add(NEW(Literal(name))); // we have at least 1 parameter + arguments->Add(new Literal(name)); // we have at least 1 parameter if (is_const || (value != NULL && !inside_with())) { arguments->Add(value); value = NULL; // zap the value to avoid the unnecessary assignment @@ -1978,18 +1634,18 @@ Block* Parser::ParseVariableDeclarations(bool accept_IN, CallRuntime* initialize; if (is_const) { initialize = - NEW(CallRuntime( + new CallRuntime( Factory::InitializeConstGlobal_symbol(), Runtime::FunctionForId(Runtime::kInitializeConstGlobal), - arguments)); + arguments); } else { initialize = - NEW(CallRuntime( + new CallRuntime( Factory::InitializeVarGlobal_symbol(), Runtime::FunctionForId(Runtime::kInitializeVarGlobal), - arguments)); + arguments); } - block->AddStatement(NEW(ExpressionStatement(initialize))); + block->AddStatement(new ExpressionStatement(initialize)); } // Add an assignment node to the initialization statement block if @@ -2004,8 +1660,8 @@ Block* Parser::ParseVariableDeclarations(bool accept_IN, // the top context for variables). Sigh... if (value != NULL) { Token::Value op = (is_const ? Token::INIT_CONST : Token::INIT_VAR); - Assignment* assignment = NEW(Assignment(op, last_var, value, position)); - if (block) block->AddStatement(NEW(ExpressionStatement(assignment))); + Assignment* assignment = new Assignment(op, last_var, value, position); + if (block) block->AddStatement(new ExpressionStatement(assignment)); } if (fni_ != NULL) fni_->Leave(); @@ -2013,14 +1669,8 @@ Block* Parser::ParseVariableDeclarations(bool accept_IN, if (!is_const && nvars == 1) { // We have a single, non-const variable. - if (is_pre_parsing_) { - // If we're preparsing then we need to set the var to something - // in order for for-in loops to parse correctly. - *var = ValidLeftHandSideSentinel::instance(); - } else { - ASSERT(last_var != NULL); - *var = last_var; - } + ASSERT(last_var != NULL); + *var = last_var; } return block; @@ -2043,11 +1693,13 @@ Statement* Parser::ParseExpressionOrLabelledStatement(ZoneStringList* labels, // ExpressionStatement | LabelledStatement :: // Expression ';' // Identifier ':' Statement - + bool starts_with_idenfifier = (peek() == Token::IDENTIFIER); Expression* expr = ParseExpression(true, CHECK_OK); - if (peek() == Token::COLON && expr && + if (peek() == Token::COLON && starts_with_idenfifier && expr && expr->AsVariableProxy() != NULL && !expr->AsVariableProxy()->is_this()) { + // Expression is a single identifier, and not, e.g., a parenthesized + // identifier. VariableProxy* var = expr->AsVariableProxy(); Handle<String> label = var->name(); // TODO(1240780): We don't check for redeclaration of labels @@ -2055,29 +1707,27 @@ Statement* Parser::ParseExpressionOrLabelledStatement(ZoneStringList* labels, // labels requires nontrivial changes to the way scopes are // structured. However, these are probably changes we want to // make later anyway so we should go back and fix this then. - if (!is_pre_parsing_) { - if (ContainsLabel(labels, label) || TargetStackContainsLabel(label)) { - SmartPointer<char> c_string = label->ToCString(DISALLOW_NULLS); - const char* elms[2] = { "Label", *c_string }; - Vector<const char*> args(elms, 2); - ReportMessage("redeclaration", args); - *ok = false; - return NULL; - } - if (labels == NULL) labels = new ZoneStringList(4); - labels->Add(label); - // Remove the "ghost" variable that turned out to be a label - // from the top scope. This way, we don't try to resolve it - // during the scope processing. - top_scope_->RemoveUnresolved(var); + if (ContainsLabel(labels, label) || TargetStackContainsLabel(label)) { + SmartPointer<char> c_string = label->ToCString(DISALLOW_NULLS); + const char* elms[2] = { "Label", *c_string }; + Vector<const char*> args(elms, 2); + ReportMessage("redeclaration", args); + *ok = false; + return NULL; } + if (labels == NULL) labels = new ZoneStringList(4); + labels->Add(label); + // Remove the "ghost" variable that turned out to be a label + // from the top scope. This way, we don't try to resolve it + // during the scope processing. + top_scope_->RemoveUnresolved(var); Expect(Token::COLON, CHECK_OK); return ParseStatement(labels, ok); } // Parsed expression statement. ExpectSemicolon(CHECK_OK); - return NEW(ExpressionStatement(expr)); + return new ExpressionStatement(expr); } @@ -2094,10 +1744,10 @@ IfStatement* Parser::ParseIfStatement(ZoneStringList* labels, bool* ok) { if (peek() == Token::ELSE) { Next(); else_statement = ParseStatement(labels, CHECK_OK); - } else if (!is_pre_parsing_) { - else_statement = factory()->EmptyStatement(); + } else { + else_statement = EmptyStatement(); } - return NEW(IfStatement(condition, then_statement, else_statement)); + return new IfStatement(condition, then_statement, else_statement); } @@ -2113,19 +1763,17 @@ Statement* Parser::ParseContinueStatement(bool* ok) { label = ParseIdentifier(CHECK_OK); } IterationStatement* target = NULL; - if (!is_pre_parsing_) { - target = LookupContinueTarget(label, CHECK_OK); - if (target == NULL) { - // Illegal continue statement. To be consistent with KJS we delay - // reporting of the syntax error until runtime. - Handle<String> error_type = Factory::illegal_continue_symbol(); - if (!label.is_null()) error_type = Factory::unknown_label_symbol(); - Expression* throw_error = NewThrowSyntaxError(error_type, label); - return NEW(ExpressionStatement(throw_error)); - } + target = LookupContinueTarget(label, CHECK_OK); + if (target == NULL) { + // Illegal continue statement. To be consistent with KJS we delay + // reporting of the syntax error until runtime. + Handle<String> error_type = Factory::illegal_continue_symbol(); + if (!label.is_null()) error_type = Factory::unknown_label_symbol(); + Expression* throw_error = NewThrowSyntaxError(error_type, label); + return new ExpressionStatement(throw_error); } ExpectSemicolon(CHECK_OK); - return NEW(ContinueStatement(target)); + return new ContinueStatement(target); } @@ -2143,22 +1791,20 @@ Statement* Parser::ParseBreakStatement(ZoneStringList* labels, bool* ok) { // Parse labeled break statements that target themselves into // empty statements, e.g. 'l1: l2: l3: break l2;' if (!label.is_null() && ContainsLabel(labels, label)) { - return factory()->EmptyStatement(); + return EmptyStatement(); } BreakableStatement* target = NULL; - if (!is_pre_parsing_) { - target = LookupBreakTarget(label, CHECK_OK); - if (target == NULL) { - // Illegal break statement. To be consistent with KJS we delay - // reporting of the syntax error until runtime. - Handle<String> error_type = Factory::illegal_break_symbol(); - if (!label.is_null()) error_type = Factory::unknown_label_symbol(); - Expression* throw_error = NewThrowSyntaxError(error_type, label); - return NEW(ExpressionStatement(throw_error)); - } + target = LookupBreakTarget(label, CHECK_OK); + if (target == NULL) { + // Illegal break statement. To be consistent with KJS we delay + // reporting of the syntax error until runtime. + Handle<String> error_type = Factory::illegal_break_symbol(); + if (!label.is_null()) error_type = Factory::unknown_label_symbol(); + Expression* throw_error = NewThrowSyntaxError(error_type, label); + return new ExpressionStatement(throw_error); } ExpectSemicolon(CHECK_OK); - return NEW(BreakStatement(target)); + return new BreakStatement(target); } @@ -2176,10 +1822,10 @@ Statement* Parser::ParseReturnStatement(bool* ok) { // function. See ECMA-262, section 12.9, page 67. // // To be consistent with KJS we report the syntax error at runtime. - if (!is_pre_parsing_ && !top_scope_->is_function_scope()) { + if (!top_scope_->is_function_scope()) { Handle<String> type = Factory::illegal_return_symbol(); Expression* throw_error = NewThrowSyntaxError(type, Handle<Object>::null()); - return NEW(ExpressionStatement(throw_error)); + return new ExpressionStatement(throw_error); } Token::Value tok = peek(); @@ -2188,12 +1834,12 @@ Statement* Parser::ParseReturnStatement(bool* ok) { tok == Token::RBRACE || tok == Token::EOS) { ExpectSemicolon(CHECK_OK); - return NEW(ReturnStatement(GetLiteralUndefined())); + return new ReturnStatement(GetLiteralUndefined()); } Expression* expr = ParseExpression(true, CHECK_OK); ExpectSemicolon(CHECK_OK); - return NEW(ReturnStatement(expr)); + return new ReturnStatement(expr); } @@ -2202,7 +1848,7 @@ Block* Parser::WithHelper(Expression* obj, bool is_catch_block, bool* ok) { // Parse the statement and collect escaping labels. - ZoneList<BreakTarget*>* target_list = NEW(ZoneList<BreakTarget*>(0)); + ZoneList<BreakTarget*>* target_list = new ZoneList<BreakTarget*>(0); TargetCollector collector(target_list); Statement* stat; { Target target(&this->target_stack_, &collector); @@ -2214,21 +1860,21 @@ Block* Parser::WithHelper(Expression* obj, // Create resulting block with two statements. // 1: Evaluate the with expression. // 2: The try-finally block evaluating the body. - Block* result = NEW(Block(NULL, 2, false)); + Block* result = new Block(NULL, 2, false); if (result != NULL) { - result->AddStatement(NEW(WithEnterStatement(obj, is_catch_block))); + result->AddStatement(new WithEnterStatement(obj, is_catch_block)); // Create body block. - Block* body = NEW(Block(NULL, 1, false)); + Block* body = new Block(NULL, 1, false); body->AddStatement(stat); // Create exit block. - Block* exit = NEW(Block(NULL, 1, false)); - exit->AddStatement(NEW(WithExitStatement())); + Block* exit = new Block(NULL, 1, false); + exit->AddStatement(new WithExitStatement()); // Return a try-finally statement. - TryFinallyStatement* wrapper = NEW(TryFinallyStatement(body, exit)); + TryFinallyStatement* wrapper = new TryFinallyStatement(body, exit); wrapper->set_escaping_targets(collector.targets()); result->AddStatement(wrapper); } @@ -2270,15 +1916,15 @@ CaseClause* Parser::ParseCaseClause(bool* default_seen_ptr, bool* ok) { } Expect(Token::COLON, CHECK_OK); - ZoneListWrapper<Statement> statements = factory()->NewList<Statement>(5); + ZoneList<Statement*>* statements = new ZoneList<Statement*>(5); while (peek() != Token::CASE && peek() != Token::DEFAULT && peek() != Token::RBRACE) { Statement* stat = ParseStatement(NULL, CHECK_OK); - statements.Add(stat); + statements->Add(stat); } - return NEW(CaseClause(label, statements.elements())); + return new CaseClause(label, statements); } @@ -2287,7 +1933,7 @@ SwitchStatement* Parser::ParseSwitchStatement(ZoneStringList* labels, // SwitchStatement :: // 'switch' '(' Expression ')' '{' CaseClause* '}' - SwitchStatement* statement = NEW(SwitchStatement(labels)); + SwitchStatement* statement = new SwitchStatement(labels); Target target(&this->target_stack_, statement); Expect(Token::SWITCH, CHECK_OK); @@ -2296,15 +1942,15 @@ SwitchStatement* Parser::ParseSwitchStatement(ZoneStringList* labels, Expect(Token::RPAREN, CHECK_OK); bool default_seen = false; - ZoneListWrapper<CaseClause> cases = factory()->NewList<CaseClause>(4); + ZoneList<CaseClause*>* cases = new ZoneList<CaseClause*>(4); Expect(Token::LBRACE, CHECK_OK); while (peek() != Token::RBRACE) { CaseClause* clause = ParseCaseClause(&default_seen, CHECK_OK); - cases.Add(clause); + cases->Add(clause); } Expect(Token::RBRACE, CHECK_OK); - if (statement) statement->Initialize(tag, cases.elements()); + if (statement) statement->Initialize(tag, cases); return statement; } @@ -2323,7 +1969,7 @@ Statement* Parser::ParseThrowStatement(bool* ok) { Expression* exception = ParseExpression(true, CHECK_OK); ExpectSemicolon(CHECK_OK); - return NEW(ExpressionStatement(new Throw(exception, pos))); + return new ExpressionStatement(new Throw(exception, pos)); } @@ -2341,7 +1987,7 @@ TryStatement* Parser::ParseTryStatement(bool* ok) { Expect(Token::TRY, CHECK_OK); - ZoneList<BreakTarget*>* target_list = NEW(ZoneList<BreakTarget*>(0)); + ZoneList<BreakTarget*>* target_list = new ZoneList<BreakTarget*>(0); TargetCollector collector(target_list); Block* try_block; @@ -2364,7 +2010,7 @@ TryStatement* Parser::ParseTryStatement(bool* ok) { // then we will need to collect jump targets from the catch block. Since // we don't know yet if there will be a finally block, we always collect // the jump targets. - ZoneList<BreakTarget*>* catch_target_list = NEW(ZoneList<BreakTarget*>(0)); + ZoneList<BreakTarget*>* catch_target_list = new ZoneList<BreakTarget*>(0); TargetCollector catch_collector(catch_target_list); bool has_catch = false; if (tok == Token::CATCH) { @@ -2379,8 +2025,8 @@ TryStatement* Parser::ParseTryStatement(bool* ok) { // Allocate a temporary for holding the finally state while // executing the finally block. catch_var = top_scope_->NewTemporary(Factory::catch_var_symbol()); - Literal* name_literal = NEW(Literal(name)); - Expression* obj = NEW(CatchExtensionObject(name_literal, catch_var)); + Literal* name_literal = new Literal(name); + Expression* obj = new CatchExtensionObject(name_literal, catch_var); { Target target(&this->target_stack_, &catch_collector); catch_block = WithHelper(obj, NULL, true, CHECK_OK); } @@ -2403,30 +2049,28 @@ TryStatement* Parser::ParseTryStatement(bool* ok) { // to: // 'try { try { } catch { } } finally { }' - if (!is_pre_parsing_ && catch_block != NULL && finally_block != NULL) { + if (catch_block != NULL && finally_block != NULL) { TryCatchStatement* statement = - NEW(TryCatchStatement(try_block, catch_var, catch_block)); + new TryCatchStatement(try_block, catch_var, catch_block); statement->set_escaping_targets(collector.targets()); - try_block = NEW(Block(NULL, 1, false)); + try_block = new Block(NULL, 1, false); try_block->AddStatement(statement); catch_block = NULL; } TryStatement* result = NULL; - if (!is_pre_parsing_) { - if (catch_block != NULL) { - ASSERT(finally_block == NULL); - result = NEW(TryCatchStatement(try_block, catch_var, catch_block)); - result->set_escaping_targets(collector.targets()); - } else { - ASSERT(finally_block != NULL); - result = NEW(TryFinallyStatement(try_block, finally_block)); - // Add the jump targets of the try block and the catch block. - for (int i = 0; i < collector.targets()->length(); i++) { - catch_collector.AddTarget(collector.targets()->at(i)); - } - result->set_escaping_targets(catch_collector.targets()); + if (catch_block != NULL) { + ASSERT(finally_block == NULL); + result = new TryCatchStatement(try_block, catch_var, catch_block); + result->set_escaping_targets(collector.targets()); + } else { + ASSERT(finally_block != NULL); + result = new TryFinallyStatement(try_block, finally_block); + // Add the jump targets of the try block and the catch block. + for (int i = 0; i < collector.targets()->length(); i++) { + catch_collector.AddTarget(collector.targets()->at(i)); } + result->set_escaping_targets(catch_collector.targets()); } return result; @@ -2439,7 +2083,7 @@ DoWhileStatement* Parser::ParseDoWhileStatement(ZoneStringList* labels, // 'do' Statement 'while' '(' Expression ')' ';' temp_scope_->AddLoop(); - DoWhileStatement* loop = NEW(DoWhileStatement(labels)); + DoWhileStatement* loop = new DoWhileStatement(labels); Target target(&this->target_stack_, loop); Expect(Token::DO, CHECK_OK); @@ -2472,7 +2116,7 @@ WhileStatement* Parser::ParseWhileStatement(ZoneStringList* labels, bool* ok) { // 'while' '(' Expression ')' Statement temp_scope_->AddLoop(); - WhileStatement* loop = NEW(WhileStatement(labels)); + WhileStatement* loop = new WhileStatement(labels); Target target(&this->target_stack_, loop); Expect(Token::WHILE, CHECK_OK); @@ -2502,7 +2146,7 @@ Statement* Parser::ParseForStatement(ZoneStringList* labels, bool* ok) { Block* variable_statement = ParseVariableDeclarations(false, &each, CHECK_OK); if (peek() == Token::IN && each != NULL) { - ForInStatement* loop = NEW(ForInStatement(labels)); + ForInStatement* loop = new ForInStatement(labels); Target target(&this->target_stack_, loop); Expect(Token::IN, CHECK_OK); @@ -2510,17 +2154,12 @@ Statement* Parser::ParseForStatement(ZoneStringList* labels, bool* ok) { Expect(Token::RPAREN, CHECK_OK); Statement* body = ParseStatement(NULL, CHECK_OK); - if (is_pre_parsing_) { - return NULL; - } else { - loop->Initialize(each, enumerable, body); - Block* result = NEW(Block(NULL, 2, false)); - result->AddStatement(variable_statement); - result->AddStatement(loop); - // Parsed for-in loop w/ variable/const declaration. - return result; - } - + loop->Initialize(each, enumerable, body); + Block* result = new Block(NULL, 2, false); + result->AddStatement(variable_statement); + result->AddStatement(loop); + // Parsed for-in loop w/ variable/const declaration. + return result; } else { init = variable_statement; } @@ -2536,7 +2175,7 @@ Statement* Parser::ParseForStatement(ZoneStringList* labels, bool* ok) { Handle<String> type = Factory::invalid_lhs_in_for_in_symbol(); expression = NewThrowReferenceError(type); } - ForInStatement* loop = NEW(ForInStatement(labels)); + ForInStatement* loop = new ForInStatement(labels); Target target(&this->target_stack_, loop); Expect(Token::IN, CHECK_OK); @@ -2549,13 +2188,13 @@ Statement* Parser::ParseForStatement(ZoneStringList* labels, bool* ok) { return loop; } else { - init = NEW(ExpressionStatement(expression)); + init = new ExpressionStatement(expression); } } } // Standard 'for' loop - ForStatement* loop = NEW(ForStatement(labels)); + ForStatement* loop = new ForStatement(labels); Target target(&this->target_stack_, loop); // Parsed initializer at this point. @@ -2571,7 +2210,7 @@ Statement* Parser::ParseForStatement(ZoneStringList* labels, bool* ok) { Statement* next = NULL; if (peek() != Token::RPAREN) { Expression* exp = ParseExpression(true, CHECK_OK); - next = NEW(ExpressionStatement(exp)); + next = new ExpressionStatement(exp); } Expect(Token::RPAREN, CHECK_OK); @@ -2592,7 +2231,7 @@ Expression* Parser::ParseExpression(bool accept_IN, bool* ok) { Expect(Token::COMMA, CHECK_OK); int position = scanner().location().beg_pos; Expression* right = ParseAssignmentExpression(accept_IN, CHECK_OK); - result = NEW(BinaryOperation(Token::COMMA, result, right, position)); + result = new BinaryOperation(Token::COMMA, result, right, position); } return result; } @@ -2652,7 +2291,7 @@ Expression* Parser::ParseAssignmentExpression(bool accept_IN, bool* ok) { fni_->Leave(); } - return NEW(Assignment(op, expression, right, pos)); + return new Assignment(op, expression, right, pos); } @@ -2674,8 +2313,8 @@ Expression* Parser::ParseConditionalExpression(bool accept_IN, bool* ok) { Expect(Token::COLON, CHECK_OK); int right_position = scanner().peek_location().beg_pos; Expression* right = ParseAssignmentExpression(accept_IN, CHECK_OK); - return NEW(Conditional(expression, left, right, - left_position, right_position)); + return new Conditional(expression, left, right, + left_position, right_position); } @@ -2782,12 +2421,12 @@ Expression* Parser::ParseBinaryExpression(int prec, bool accept_IN, bool* ok) { x = NewCompareNode(cmp, x, y, position); if (cmp != op) { // The comparison was negated - add a NOT. - x = NEW(UnaryOperation(Token::NOT, x)); + x = new UnaryOperation(Token::NOT, x); } } else { // We have a "normal" binary operation. - x = NEW(BinaryOperation(op, x, y, position)); + x = new BinaryOperation(op, x, y, position); } } } @@ -2800,19 +2439,19 @@ Expression* Parser::NewCompareNode(Token::Value op, Expression* y, int position) { ASSERT(op != Token::NE && op != Token::NE_STRICT); - if (!is_pre_parsing_ && (op == Token::EQ || op == Token::EQ_STRICT)) { + if (op == Token::EQ || op == Token::EQ_STRICT) { bool is_strict = (op == Token::EQ_STRICT); Literal* x_literal = x->AsLiteral(); if (x_literal != NULL && x_literal->IsNull()) { - return NEW(CompareToNull(is_strict, y)); + return new CompareToNull(is_strict, y); } Literal* y_literal = y->AsLiteral(); if (y_literal != NULL && y_literal->IsNull()) { - return NEW(CompareToNull(is_strict, x)); + return new CompareToNull(is_strict, x); } } - return NEW(CompareOperation(op, x, y, position)); + return new CompareOperation(op, x, y, position); } @@ -2849,7 +2488,7 @@ Expression* Parser::ParseUnaryExpression(bool* ok) { } } - return NEW(UnaryOperation(op, expression)); + return new UnaryOperation(op, expression); } else if (Token::IsCountOp(op)) { op = Next(); @@ -2863,8 +2502,8 @@ Expression* Parser::ParseUnaryExpression(bool* ok) { expression = NewThrowReferenceError(type); } int position = scanner().location().beg_pos; - IncrementOperation* increment = NEW(IncrementOperation(op, expression)); - return NEW(CountOperation(true /* prefix */, increment, position)); + IncrementOperation* increment = new IncrementOperation(op, expression); + return new CountOperation(true /* prefix */, increment, position); } else { return ParsePostfixExpression(ok); @@ -2888,8 +2527,8 @@ Expression* Parser::ParsePostfixExpression(bool* ok) { } Token::Value next = Next(); int position = scanner().location().beg_pos; - IncrementOperation* increment = NEW(IncrementOperation(next, expression)); - expression = NEW(CountOperation(false /* postfix */, increment, position)); + IncrementOperation* increment = new IncrementOperation(next, expression); + expression = new CountOperation(false /* postfix */, increment, position); } return expression; } @@ -2912,7 +2551,7 @@ Expression* Parser::ParseLeftHandSideExpression(bool* ok) { Consume(Token::LBRACK); int pos = scanner().location().beg_pos; Expression* index = ParseExpression(true, CHECK_OK); - result = factory()->NewProperty(result, index, pos); + result = new Property(result, index, pos); Expect(Token::RBRACK, CHECK_OK); break; } @@ -2929,17 +2568,15 @@ Expression* Parser::ParseLeftHandSideExpression(bool* ok) { // declared in the current scope chain. These calls are marked as // potentially direct eval calls. Whether they are actually direct calls // to eval is determined at run time. - if (!is_pre_parsing_) { - VariableProxy* callee = result->AsVariableProxy(); - if (callee != NULL && callee->IsVariable(Factory::eval_symbol())) { - Handle<String> name = callee->name(); - Variable* var = top_scope_->Lookup(name); - if (var == NULL) { - top_scope_->RecordEvalCall(); - } + VariableProxy* callee = result->AsVariableProxy(); + if (callee != NULL && callee->IsVariable(Factory::eval_symbol())) { + Handle<String> name = callee->name(); + Variable* var = top_scope_->Lookup(name); + if (var == NULL) { + top_scope_->RecordEvalCall(); } } - result = factory()->NewCall(result, args, pos); + result = NewCall(result, args, pos); break; } @@ -2947,7 +2584,7 @@ Expression* Parser::ParseLeftHandSideExpression(bool* ok) { Consume(Token::PERIOD); int pos = scanner().location().beg_pos; Handle<String> name = ParseIdentifierName(CHECK_OK); - result = factory()->NewProperty(result, NEW(Literal(name)), pos); + result = new Property(result, new Literal(name), pos); if (fni_ != NULL) fni_->PushLiteralName(name); break; } @@ -2959,7 +2596,6 @@ Expression* Parser::ParseLeftHandSideExpression(bool* ok) { } - Expression* Parser::ParseNewPrefix(PositionStack* stack, bool* ok) { // NewExpression :: // ('new')+ MemberExpression @@ -2984,7 +2620,7 @@ Expression* Parser::ParseNewPrefix(PositionStack* stack, bool* ok) { if (!stack->is_empty()) { int last = stack->pop(); - result = NEW(CallNew(result, new ZoneList<Expression*>(0), last)); + result = new CallNew(result, new ZoneList<Expression*>(0), last); } return result; } @@ -3026,7 +2662,7 @@ Expression* Parser::ParseMemberWithNewPrefixesExpression(PositionStack* stack, Consume(Token::LBRACK); int pos = scanner().location().beg_pos; Expression* index = ParseExpression(true, CHECK_OK); - result = factory()->NewProperty(result, index, pos); + result = new Property(result, index, pos); Expect(Token::RBRACK, CHECK_OK); break; } @@ -3034,7 +2670,7 @@ Expression* Parser::ParseMemberWithNewPrefixesExpression(PositionStack* stack, Consume(Token::PERIOD); int pos = scanner().location().beg_pos; Handle<String> name = ParseIdentifierName(CHECK_OK); - result = factory()->NewProperty(result, NEW(Literal(name)), pos); + result = new Property(result, new Literal(name), pos); if (fni_ != NULL) fni_->PushLiteralName(name); break; } @@ -3043,7 +2679,7 @@ Expression* Parser::ParseMemberWithNewPrefixesExpression(PositionStack* stack, // Consume one of the new prefixes (already parsed). ZoneList<Expression*>* args = ParseArguments(CHECK_OK); int last = stack->pop(); - result = NEW(CallNew(result, args, last)); + result = new CallNew(result, args, last); break; } default: @@ -3062,7 +2698,7 @@ DebuggerStatement* Parser::ParseDebuggerStatement(bool* ok) { Expect(Token::DEBUGGER, CHECK_OK); ExpectSemicolon(CHECK_OK); - return NEW(DebuggerStatement()); + return new DebuggerStatement(); } @@ -3120,38 +2756,30 @@ Expression* Parser::ParsePrimaryExpression(bool* ok) { switch (peek()) { case Token::THIS: { Consume(Token::THIS); - if (is_pre_parsing_) { - result = VariableProxySentinel::this_proxy(); - } else { - VariableProxy* recv = top_scope_->receiver(); - result = recv; - } + VariableProxy* recv = top_scope_->receiver(); + result = recv; break; } case Token::NULL_LITERAL: Consume(Token::NULL_LITERAL); - result = NEW(Literal(Factory::null_value())); + result = new Literal(Factory::null_value()); break; case Token::TRUE_LITERAL: Consume(Token::TRUE_LITERAL); - result = NEW(Literal(Factory::true_value())); + result = new Literal(Factory::true_value()); break; case Token::FALSE_LITERAL: Consume(Token::FALSE_LITERAL); - result = NEW(Literal(Factory::false_value())); + result = new Literal(Factory::false_value()); break; case Token::IDENTIFIER: { Handle<String> name = ParseIdentifier(CHECK_OK); if (fni_ != NULL) fni_->PushVariableName(name); - if (is_pre_parsing_) { - result = VariableProxySentinel::identifier_proxy(); - } else { - result = top_scope_->NewUnresolved(name, inside_with()); - } + result = top_scope_->NewUnresolved(name, inside_with()); break; } @@ -3166,7 +2794,7 @@ Expression* Parser::ParsePrimaryExpression(bool* ok) { case Token::STRING: { Consume(Token::STRING); Handle<String> symbol = GetSymbol(CHECK_OK); - result = NEW(Literal(symbol)); + result = new Literal(symbol); if (fni_ != NULL) fni_->PushLiteralName(symbol); break; } @@ -3244,7 +2872,7 @@ Expression* Parser::ParseArrayLiteral(bool* ok) { // ArrayLiteral :: // '[' Expression? (',' Expression?)* ']' - ZoneListWrapper<Expression> values = factory()->NewList<Expression>(4); + ZoneList<Expression*>* values = new ZoneList<Expression*>(4); Expect(Token::LBRACK, CHECK_OK); while (peek() != Token::RBRACK) { Expression* elem; @@ -3253,7 +2881,7 @@ Expression* Parser::ParseArrayLiteral(bool* ok) { } else { elem = ParseAssignmentExpression(true, CHECK_OK); } - values.Add(elem); + values->Add(elem); if (peek() != Token::RBRACK) { Expect(Token::COMMA, CHECK_OK); } @@ -3263,21 +2891,19 @@ Expression* Parser::ParseArrayLiteral(bool* ok) { // Update the scope information before the pre-parsing bailout. int literal_index = temp_scope_->NextMaterializedLiteralIndex(); - if (is_pre_parsing_) return NULL; - // Allocate a fixed array with all the literals. Handle<FixedArray> literals = - Factory::NewFixedArray(values.length(), TENURED); + Factory::NewFixedArray(values->length(), TENURED); // Fill in the literals. bool is_simple = true; int depth = 1; - for (int i = 0; i < values.length(); i++) { - MaterializedLiteral* m_literal = values.at(i)->AsMaterializedLiteral(); + for (int i = 0, n = values->length(); i < n; i++) { + MaterializedLiteral* m_literal = values->at(i)->AsMaterializedLiteral(); if (m_literal != NULL && m_literal->depth() + 1 > depth) { depth = m_literal->depth() + 1; } - Handle<Object> boilerplate_value = GetBoilerplateValue(values.at(i)); + Handle<Object> boilerplate_value = GetBoilerplateValue(values->at(i)); if (boilerplate_value->IsUndefined()) { literals->set_the_hole(i); is_simple = false; @@ -3288,12 +2914,12 @@ Expression* Parser::ParseArrayLiteral(bool* ok) { // Simple and shallow arrays can be lazily copied, we transform the // elements array to a copy-on-write array. - if (is_simple && depth == 1 && values.length() > 0) { + if (is_simple && depth == 1 && values->length() > 0) { literals->set_map(Heap::fixed_cow_array_map()); } - return NEW(ArrayLiteral(literals, values.elements(), - literal_index, is_simple, depth)); + return new ArrayLiteral(literals, values, + literal_index, is_simple, depth); } @@ -3440,7 +3066,7 @@ ObjectLiteral::Property* Parser::ParseObjectLiteralGetSet(bool is_getter, DECLARATION, CHECK_OK); ObjectLiteral::Property* property = - NEW(ObjectLiteral::Property(is_getter, value)); + new ObjectLiteral::Property(is_getter, value); return property; } else { ReportUnexpectedToken(next); @@ -3457,8 +3083,8 @@ Expression* Parser::ParseObjectLiteral(bool* ok) { // | (('get' | 'set') (IdentifierName | String | Number) FunctionLiteral) // )*[','] '}' - ZoneListWrapper<ObjectLiteral::Property> properties = - factory()->NewList<ObjectLiteral::Property>(4); + ZoneList<ObjectLiteral::Property*>* properties = + new ZoneList<ObjectLiteral::Property*>(4); int number_of_boilerplate_properties = 0; Expect(Token::LBRACE, CHECK_OK); @@ -3481,7 +3107,7 @@ Expression* Parser::ParseObjectLiteral(bool* ok) { if (IsBoilerplateProperty(property)) { number_of_boilerplate_properties++; } - properties.Add(property); + properties->Add(property); if (peek() != Token::RBRACE) Expect(Token::COMMA, CHECK_OK); if (fni_ != NULL) { @@ -3492,7 +3118,7 @@ Expression* Parser::ParseObjectLiteral(bool* ok) { } // Failed to parse as get/set property, so it's just a property // called "get" or "set". - key = NEW(Literal(id)); + key = new Literal(id); break; } case Token::STRING: { @@ -3504,7 +3130,7 @@ Expression* Parser::ParseObjectLiteral(bool* ok) { key = NewNumberLiteral(index); break; } - key = NEW(Literal(string)); + key = new Literal(string); break; } case Token::NUMBER: { @@ -3518,7 +3144,7 @@ Expression* Parser::ParseObjectLiteral(bool* ok) { if (Token::IsKeyword(next)) { Consume(next); Handle<String> string = GetSymbol(CHECK_OK); - key = NEW(Literal(string)); + key = new Literal(string); } else { // Unexpected token. Token::Value next = Next(); @@ -3532,11 +3158,11 @@ Expression* Parser::ParseObjectLiteral(bool* ok) { Expression* value = ParseAssignmentExpression(true, CHECK_OK); ObjectLiteral::Property* property = - NEW(ObjectLiteral::Property(key, value)); + new ObjectLiteral::Property(key, value); // Count CONSTANT or COMPUTED properties to maintain the enumeration order. if (IsBoilerplateProperty(property)) number_of_boilerplate_properties++; - properties.Add(property); + properties->Add(property); // TODO(1240767): Consider allowing trailing comma. if (peek() != Token::RBRACE) Expect(Token::COMMA, CHECK_OK); @@ -3549,7 +3175,6 @@ Expression* Parser::ParseObjectLiteral(bool* ok) { Expect(Token::RBRACE, CHECK_OK); // Computation of literal_index must happen before pre parse bailout. int literal_index = temp_scope_->NextMaterializedLiteralIndex(); - if (is_pre_parsing_) return NULL; Handle<FixedArray> constant_properties = Factory::NewFixedArray(number_of_boilerplate_properties * 2, TENURED); @@ -3557,13 +3182,13 @@ Expression* Parser::ParseObjectLiteral(bool* ok) { bool is_simple = true; bool fast_elements = true; int depth = 1; - BuildObjectLiteralConstantProperties(properties.elements(), + BuildObjectLiteralConstantProperties(properties, constant_properties, &is_simple, &fast_elements, &depth); return new ObjectLiteral(constant_properties, - properties.elements(), + properties, literal_index, is_simple, fast_elements, @@ -3581,19 +3206,6 @@ Expression* Parser::ParseRegExpLiteral(bool seen_equal, bool* ok) { int literal_index = temp_scope_->NextMaterializedLiteralIndex(); - if (is_pre_parsing_) { - // If we're preparsing we just do all the parsing stuff without - // building anything. - if (!scanner_.ScanRegExpFlags()) { - Next(); - ReportMessage("invalid_regexp_flags", Vector<const char*>::empty()); - *ok = false; - return NULL; - } - Next(); - return NULL; - } - Handle<String> js_pattern = Factory::NewStringFromUtf8(scanner_.next_literal(), TENURED); scanner_.ScanRegExpFlags(); @@ -3609,17 +3221,17 @@ ZoneList<Expression*>* Parser::ParseArguments(bool* ok) { // Arguments :: // '(' (AssignmentExpression)*[','] ')' - ZoneListWrapper<Expression> result = factory()->NewList<Expression>(4); + ZoneList<Expression*>* result = new ZoneList<Expression*>(4); Expect(Token::LPAREN, CHECK_OK); bool done = (peek() == Token::RPAREN); while (!done) { Expression* argument = ParseAssignmentExpression(true, CHECK_OK); - result.Add(argument); + result->Add(argument); done = (peek() == Token::RPAREN); if (!done) Expect(Token::COMMA, CHECK_OK); } Expect(Token::RPAREN, CHECK_OK); - return result.elements(); + return result; } @@ -3635,9 +3247,9 @@ FunctionLiteral* Parser::ParseFunctionLiteral(Handle<String> var_name, // this is the actual function name, otherwise this is the name of the // variable declared and initialized with the function (expression). In // that case, we don't have a function name (it's empty). - Handle<String> name = is_named ? var_name : factory()->EmptySymbol(); + Handle<String> name = is_named ? var_name : Factory::empty_symbol(); // The function name, if any. - Handle<String> function_name = factory()->EmptySymbol(); + Handle<String> function_name = Factory::empty_symbol(); if (is_named && (type == EXPRESSION || type == NESTED)) { function_name = name; } @@ -3645,7 +3257,7 @@ FunctionLiteral* Parser::ParseFunctionLiteral(Handle<String> var_name, int num_parameters = 0; // Parse function body. { Scope* scope = - factory()->NewScope(top_scope_, Scope::FUNCTION_SCOPE, inside_with()); + NewScope(top_scope_, Scope::FUNCTION_SCOPE, inside_with()); LexicalScope lexical_scope(&this->top_scope_, &this->with_nesting_level_, scope); TemporaryScope temp_scope(&this->temp_scope_); @@ -3658,18 +3270,16 @@ FunctionLiteral* Parser::ParseFunctionLiteral(Handle<String> var_name, bool done = (peek() == Token::RPAREN); while (!done) { Handle<String> param_name = ParseIdentifier(CHECK_OK); - if (!is_pre_parsing_) { - top_scope_->AddParameter(top_scope_->DeclareLocal(param_name, - Variable::VAR)); - num_parameters++; - } + top_scope_->AddParameter(top_scope_->DeclareLocal(param_name, + Variable::VAR)); + num_parameters++; done = (peek() == Token::RPAREN); if (!done) Expect(Token::COMMA, CHECK_OK); } Expect(Token::RPAREN, CHECK_OK); Expect(Token::LBRACE, CHECK_OK); - ZoneListWrapper<Statement> body = factory()->NewList<Statement>(8); + ZoneList<Statement*>* body = new ZoneList<Statement*>(8); // If we have a named function expression, we add a local variable // declaration to the body of the function with the name of the @@ -3677,17 +3287,15 @@ FunctionLiteral* Parser::ParseFunctionLiteral(Handle<String> var_name, // NOTE: We create a proxy and resolve it here so that in the // future we can change the AST to only refer to VariableProxies // instead of Variables and Proxis as is the case now. - if (!is_pre_parsing_ - && !function_name.is_null() - && function_name->length() > 0) { + if (!function_name.is_null() && function_name->length() > 0) { Variable* fvar = top_scope_->DeclareFunctionVar(function_name); VariableProxy* fproxy = top_scope_->NewUnresolved(function_name, inside_with()); fproxy->BindTo(fvar); - body.Add(new ExpressionStatement( - new Assignment(Token::INIT_CONST, fproxy, - NEW(ThisFunction()), - RelocInfo::kNoPosition))); + body->Add(new ExpressionStatement( + new Assignment(Token::INIT_CONST, fproxy, + new ThisFunction(), + RelocInfo::kNoPosition))); } // Determine if the function will be lazily compiled. The mode can @@ -3719,12 +3327,8 @@ FunctionLiteral* Parser::ParseFunctionLiteral(Handle<String> var_name, this_property_assignments = Factory::empty_fixed_array(); Expect(Token::RBRACE, CHECK_OK); } else { - FunctionEntry entry; - if (is_lazily_compiled) entry = log()->LogFunction(function_block_pos); - { - ConditionalLogPauseScope pause_if(is_lazily_compiled, log()); - ParseSourceElements(&body, Token::RBRACE, CHECK_OK); - } + ParseSourceElements(body, Token::RBRACE, CHECK_OK); + materialized_literal_count = temp_scope.materialized_literal_count(); expected_property_count = temp_scope.expected_property_count(); only_simple_this_property_assignments = @@ -3733,19 +3337,12 @@ FunctionLiteral* Parser::ParseFunctionLiteral(Handle<String> var_name, Expect(Token::RBRACE, CHECK_OK); end_pos = scanner_.location().end_pos; - if (entry.is_valid()) { - ASSERT(is_lazily_compiled); - ASSERT(is_pre_parsing_); - entry.set_end_pos(end_pos); - entry.set_literal_count(materialized_literal_count); - entry.set_property_count(expected_property_count); - } } FunctionLiteral* function_literal = - NEW(FunctionLiteral(name, + new FunctionLiteral(name, top_scope_, - body.elements(), + body, materialized_literal_count, expected_property_count, only_simple_this_property_assignments, @@ -3754,10 +3351,8 @@ FunctionLiteral* Parser::ParseFunctionLiteral(Handle<String> var_name, start_pos, end_pos, function_name->length() > 0, - temp_scope.ContainsLoops())); - if (!is_pre_parsing_) { - function_literal->set_function_token_position(function_token_position); - } + temp_scope.ContainsLoops()); + function_literal->set_function_token_position(function_token_position); if (fni_ != NULL && !is_named) fni_->AddFunction(function_literal); return function_literal; @@ -3772,7 +3367,6 @@ Expression* Parser::ParseV8Intrinsic(bool* ok) { Expect(Token::MOD, CHECK_OK); Handle<String> name = ParseIdentifier(CHECK_OK); ZoneList<Expression*>* args = ParseArguments(CHECK_OK); - if (is_pre_parsing_) return NULL; if (extension_ != NULL) { // The extension structures are only accessible while parsing the @@ -3808,7 +3402,7 @@ Expression* Parser::ParseV8Intrinsic(bool* ok) { } // We have a valid intrinsics call or a call to a builtin. - return NEW(CallRuntime(name, function, args)); + return new CallRuntime(name, function, args); } @@ -3856,12 +3450,12 @@ void Parser::ExpectSemicolon(bool* ok) { Literal* Parser::GetLiteralUndefined() { - return NEW(Literal(Factory::undefined_value())); + return new Literal(Factory::undefined_value()); } Literal* Parser::GetLiteralTheHole() { - return NEW(Literal(Factory::the_hole_value())); + return new Literal(Factory::the_hole_value()); } @@ -3964,7 +3558,7 @@ void Parser::RegisterTargetUse(BreakTarget* target, Target* stop) { Literal* Parser::NewNumberLiteral(double number) { - return NEW(Literal(Factory::NewNumber(number, TENURED))); + return new Literal(Factory::NewNumber(number, TENURED)); } @@ -3996,8 +3590,6 @@ Expression* Parser::NewThrowTypeError(Handle<String> type, Expression* Parser::NewThrowError(Handle<String> constructor, Handle<String> type, Vector< Handle<Object> > arguments) { - if (is_pre_parsing_) return NULL; - int argc = arguments.length(); Handle<JSArray> array = Factory::NewJSArray(argc, TENURED); ASSERT(array->IsJSArray() && array->HasFastElements()); @@ -4185,17 +3777,17 @@ Handle<Object> JsonParser::ParseJsonArray() { RegExpParser::RegExpParser(FlatStringReader* in, Handle<String>* error, bool multiline) - : current_(kEndMarker), + : error_(error), + captures_(NULL), + in_(in), + current_(kEndMarker), + next_pos_(0), + capture_count_(0), has_more_(true), multiline_(multiline), - next_pos_(0), - in_(in), - error_(error), simple_(false), contains_anchor_(false), - captures_(NULL), is_scanned_for_captures_(false), - capture_count_(0), failed_(false) { Advance(1); } @@ -5003,23 +4595,6 @@ bool ScriptDataImpl::HasError() { } -// Preparse, but only collect data that is immediately useful, -// even if the preparser data is only used once. -ScriptDataImpl* ParserApi::PartialPreParse(Handle<String> source, - unibrow::CharacterStream* stream, - v8::Extension* extension) { - Handle<Script> no_script; - bool allow_natives_syntax = - FLAG_allow_natives_syntax || Bootstrapper::IsActive(); - PartialPreParser parser(no_script, allow_natives_syntax, extension); - if (!parser.PreParseProgram(source, stream)) return NULL; - // Extract the accumulated data from the recorder as a single - // contiguous vector that we are responsible for disposing. - Vector<unsigned> store = parser.recorder()->ExtractData(); - return new ScriptDataImpl(store); -} - - void ScriptDataImpl::Initialize() { // Prepares state for use. if (store_.length() >= kHeaderSize) { @@ -5063,18 +4638,82 @@ int ScriptDataImpl::ReadNumber(byte** source) { } +static ScriptDataImpl* DoPreParse(UTF16Buffer* stream, + bool allow_lazy, + PartialParserRecorder* recorder) { + typedef preparser::Scanner<UTF16Buffer, UTF8Buffer> PreScanner; + PreScanner scanner; + scanner.Initialize(stream); + preparser::PreParser<PreScanner, PartialParserRecorder> preparser; + if (!preparser.PreParseProgram(&scanner, recorder, allow_lazy)) { + Top::StackOverflow(); + return NULL; + } + + // Extract the accumulated data from the recorder as a single + // contiguous vector that we are responsible for disposing. + Vector<unsigned> store = recorder->ExtractData(); + return new ScriptDataImpl(store); +} + + +// Create an UTF16Buffer for the preparser to use as input, +// and preparse the source. +static ScriptDataImpl* DoPreParse(Handle<String> source, + unibrow::CharacterStream* stream, + bool allow_lazy, + PartialParserRecorder* recorder) { + if (source.is_null()) { + CharacterStreamUTF16Buffer buffer; + int length = stream->Length(); + buffer.Initialize(source, stream, 0, length); + return DoPreParse(&buffer, allow_lazy, recorder); + } else if (source->IsExternalAsciiString()) { + ExternalStringUTF16Buffer<ExternalAsciiString, char> buffer; + int length = source->length(); + buffer.Initialize(Handle<ExternalAsciiString>::cast(source), 0, length); + return DoPreParse(&buffer, allow_lazy, recorder); + } else if (source->IsExternalTwoByteString()) { + ExternalStringUTF16Buffer<ExternalTwoByteString, uint16_t> buffer; + int length = source->length(); + buffer.Initialize(Handle<ExternalTwoByteString>::cast(source), 0, length); + return DoPreParse(&buffer, allow_lazy, recorder); + } else { + CharacterStreamUTF16Buffer buffer; + SafeStringInputBuffer input; + input.Reset(0, source.location()); + int length = source->length(); + buffer.Initialize(source, &input, 0, length); + return DoPreParse(&buffer, allow_lazy, recorder); + } +} + + +// Preparse, but only collect data that is immediately useful, +// even if the preparser data is only used once. +ScriptDataImpl* ParserApi::PartialPreParse(Handle<String> source, + unibrow::CharacterStream* stream, + v8::Extension* extension) { + Handle<Script> no_script; + bool allow_lazy = FLAG_lazy && (extension == NULL); + if (!allow_lazy) { + // Partial preparsing is only about lazily compiled functions. + // If we don't allow lazy compilation, the log data will be empty. + return NULL; + } + PartialParserRecorder recorder; + + return DoPreParse(source, stream, allow_lazy, &recorder); +} + + ScriptDataImpl* ParserApi::PreParse(Handle<String> source, unibrow::CharacterStream* stream, v8::Extension* extension) { Handle<Script> no_script; - bool allow_natives_syntax = - FLAG_allow_natives_syntax || Bootstrapper::IsActive(); - CompletePreParser parser(no_script, allow_natives_syntax, extension); - if (!parser.PreParseProgram(source, stream)) return NULL; - // Extract the accumulated data from the recorder as a single - // contiguous vector that we are responsible for disposing. - Vector<unsigned> store = parser.recorder()->ExtractData(); - return new ScriptDataImpl(store); + bool allow_lazy = FLAG_lazy && (extension == NULL); + CompleteParserRecorder recorder; + return DoPreParse(source, stream, allow_lazy, &recorder); } @@ -5105,14 +4744,13 @@ bool ParserApi::Parse(CompilationInfo* info) { FunctionLiteral* result = NULL; Handle<Script> script = info->script(); if (info->is_lazy()) { - AstBuildingParser parser(script, true, NULL, NULL); + Parser parser(script, true, NULL, NULL); result = parser.ParseLazy(info->shared_info()); } else { bool allow_natives_syntax = FLAG_allow_natives_syntax || Bootstrapper::IsActive(); ScriptDataImpl* pre_data = info->pre_parse_data(); - AstBuildingParser parser(script, allow_natives_syntax, info->extension(), - pre_data); + Parser parser(script, allow_natives_syntax, info->extension(), pre_data); if (pre_data != NULL && pre_data->has_error()) { Scanner::Location loc = pre_data->MessageLocation(); const char* message = pre_data->BuildMessage(); @@ -5134,6 +4772,4 @@ bool ParserApi::Parse(CompilationInfo* info) { return (result != NULL); } -#undef NEW - } } // namespace v8::internal diff --git a/deps/v8/src/parser.h b/deps/v8/src/parser.h index 19b382e8de..64f621c9e2 100644 --- a/deps/v8/src/parser.h +++ b/deps/v8/src/parser.h @@ -31,13 +31,13 @@ #include "allocation.h" #include "ast.h" #include "scanner.h" +#include "scopes.h" namespace v8 { namespace internal { class CompilationInfo; class FuncNameInferrer; -class ParserFactory; class ParserLog; class PositionStack; class Target; @@ -132,7 +132,7 @@ class ScriptDataImpl : public ScriptData { unsigned version() { return store_[kVersionOffset]; } static const unsigned kMagicNumber = 0xBadDead; - static const unsigned kCurrentVersion = 4; + static const unsigned kCurrentVersion = 5; static const int kMagicOffset = 0; static const int kVersionOffset = 1; @@ -177,6 +177,125 @@ class ScriptDataImpl : public ScriptData { }; +// Record only functions. +class PartialParserRecorder { + public: + PartialParserRecorder(); + + void LogFunction(int start, int end, int literals, int properties) { + function_store_.Add(start); + function_store_.Add(end); + function_store_.Add(literals); + function_store_.Add(properties); + } + + void LogSymbol(int start, const char* symbol, int length) { } + + // Logs an error message and marks the log as containing an error. + // Further logging will be ignored, and ExtractData will return a vector + // representing the error only. + void LogMessage(int start, + int end, + const char* message, + const char* argument_opt) { + Scanner::Location location(start, end); + Vector<const char*> arguments; + if (argument_opt != NULL) { + arguments = Vector<const char*>(&argument_opt, 1); + } + this->LogMessage(location, message, arguments); + } + + int function_position() { return function_store_.size(); } + + void LogMessage(Scanner::Location loc, + const char* message, + Vector<const char*> args); + + Vector<unsigned> ExtractData(); + + void PauseRecording() { + pause_count_++; + is_recording_ = false; + } + + void ResumeRecording() { + ASSERT(pause_count_ > 0); + if (--pause_count_ == 0) is_recording_ = !has_error(); + } + + int symbol_position() { return 0; } + int symbol_ids() { return 0; } + + protected: + bool has_error() { + return static_cast<bool>(preamble_[ScriptDataImpl::kHasErrorOffset]); + } + + bool is_recording() { + return is_recording_; + } + + void WriteString(Vector<const char> str); + + Collector<unsigned> function_store_; + unsigned preamble_[ScriptDataImpl::kHeaderSize]; + bool is_recording_; + int pause_count_; + +#ifdef DEBUG + int prev_start_; +#endif +}; + + +// Record both functions and symbols. +class CompleteParserRecorder: public PartialParserRecorder { + public: + CompleteParserRecorder(); + + void LogSymbol(int start, Vector<const char> literal); + + void LogSymbol(int start, const char* symbol, int length) { + LogSymbol(start, Vector<const char>(symbol, length)); + } + + Vector<unsigned> ExtractData(); + + int symbol_position() { return symbol_store_.size(); } + int symbol_ids() { return symbol_id_; } + + private: + static int vector_hash(Vector<const char> string) { + int hash = 0; + for (int i = 0; i < string.length(); i++) { + int c = string[i]; + hash += c; + hash += (hash << 10); + hash ^= (hash >> 6); + } + return hash; + } + + static bool vector_compare(void* a, void* b) { + Vector<const char>* string1 = reinterpret_cast<Vector<const char>* >(a); + Vector<const char>* string2 = reinterpret_cast<Vector<const char>* >(b); + int length = string1->length(); + if (string2->length() != length) return false; + return memcmp(string1->start(), string2->start(), length) == 0; + } + + // Write a non-negative number to the symbol store. + void WriteNumber(int number); + + Collector<byte> symbol_store_; + Collector<Vector<const char> > symbol_entries_; + HashMap symbol_table_; + int symbol_id_; +}; + + + class ParserApi { public: // Parses the source code represented by the compilation info and sets its @@ -196,6 +315,8 @@ class ParserApi { v8::Extension* extension); }; +// ---------------------------------------------------------------------------- +// REGEXP PARSING // A BuffferedZoneList is an automatically growing list, just like (and backed // by) a ZoneList, that is optimized for the case of adding and removing @@ -411,51 +532,44 @@ class RegExpParser { uc32 Next(); FlatStringReader* in() { return in_; } void ScanForCaptures(); + + Handle<String>* error_; + ZoneList<RegExpCapture*>* captures_; + FlatStringReader* in_; uc32 current_; + int next_pos_; + // The capture count is only valid after we have scanned for captures. + int capture_count_; bool has_more_; bool multiline_; - int next_pos_; - FlatStringReader* in_; - Handle<String>* error_; bool simple_; bool contains_anchor_; - ZoneList<RegExpCapture*>* captures_; bool is_scanned_for_captures_; - // The capture count is only valid after we have scanned for captures. - int capture_count_; bool failed_; }; +// ---------------------------------------------------------------------------- +// JAVASCRIPT PARSING class Parser { public: - Parser(Handle<Script> script, bool allow_natives_syntax, - v8::Extension* extension, ParserMode is_pre_parsing, - ParserFactory* factory, ParserLog* log, ScriptDataImpl* pre_data); + Parser(Handle<Script> script, + bool allow_natives_syntax, + v8::Extension* extension, + ScriptDataImpl* pre_data); virtual ~Parser() { } - // Pre-parse the program from the character stream; returns true on - // success, false if a stack-overflow happened during parsing. - bool PreParseProgram(Handle<String> source, unibrow::CharacterStream* stream); - - void ReportMessage(const char* message, Vector<const char*> args); - virtual void ReportMessageAt(Scanner::Location loc, - const char* message, - Vector<const char*> args) = 0; - - // Returns NULL if parsing failed. FunctionLiteral* ParseProgram(Handle<String> source, bool in_global_context); + FunctionLiteral* ParseLazy(Handle<SharedFunctionInfo> info); - // The minimum number of contiguous assignment that will - // be treated as an initialization block. Benchmarks show that - // the overhead exceeds the savings below this limit. - static const int kMinInitializationBlock = 3; + void ReportMessageAt(Scanner::Location loc, + const char* message, + Vector<const char*> args); protected: - enum Mode { PARSE_LAZILY, PARSE_EAGERLY @@ -464,28 +578,9 @@ class Parser { // Report syntax error void ReportUnexpectedToken(Token::Value token); void ReportInvalidPreparseData(Handle<String> name, bool* ok); - - Handle<Script> script_; - Scanner scanner_; - - Scope* top_scope_; - int with_nesting_level_; - - TemporaryScope* temp_scope_; - Mode mode_; - - Target* target_stack_; // for break, continue statements - bool allow_natives_syntax_; - v8::Extension* extension_; - ParserFactory* factory_; - ParserLog* log_; - bool is_pre_parsing_; - ScriptDataImpl* pre_data_; - FuncNameInferrer* fni_; + void ReportMessage(const char* message, Vector<const char*> args); bool inside_with() const { return with_nesting_level_ > 0; } - ParserFactory* factory() const { return factory_; } - ParserLog* log() const { return log_; } Scanner& scanner() { return scanner_; } Mode mode() const { return mode_; } ScriptDataImpl* pre_data() const { return pre_data_; } @@ -494,7 +589,7 @@ class Parser { // which is set to false if parsing failed; it is unchanged otherwise. // By making the 'exception handling' explicit, we are forced to check // for failure at the call sites. - void* ParseSourceElements(ZoneListWrapper<Statement>* processor, + void* ParseSourceElements(ZoneList<Statement*>* processor, int end_token, bool* ok); Statement* ParseStatement(ZoneStringList* labels, bool* ok); Statement* ParseFunctionDeclaration(bool* ok); @@ -607,10 +702,10 @@ class Parser { bool* ok); // Parser support - virtual VariableProxy* Declare(Handle<String> name, Variable::Mode mode, - FunctionLiteral* fun, - bool resolve, - bool* ok) = 0; + VariableProxy* Declare(Handle<String> name, Variable::Mode mode, + FunctionLiteral* fun, + bool resolve, + bool* ok); bool TargetStackContainsLabel(Handle<String> label); BreakableStatement* LookupBreakTarget(Handle<String> label, bool* ok); @@ -618,6 +713,28 @@ class Parser { void RegisterTargetUse(BreakTarget* target, Target* stop); + // Factory methods. + + Statement* EmptyStatement() { + static v8::internal::EmptyStatement empty; + return ∅ + } + + Scope* NewScope(Scope* parent, Scope::Type type, bool inside_with); + + Handle<String> LookupSymbol(int symbol_id, + Vector<const char> string); + + Handle<String> LookupCachedSymbol(int symbol_id, + Vector<const char> string); + + Expression* NewCall(Expression* expression, + ZoneList<Expression*>* arguments, + int pos) { + return new Call(expression, arguments, pos); + } + + // Create a number literal. Literal* NewNumberLiteral(double value); @@ -639,6 +756,24 @@ class Parser { Expression* NewThrowError(Handle<String> constructor, Handle<String> type, Vector< Handle<Object> > arguments); + + ZoneList<Handle<String> > symbol_cache_; + + Handle<Script> script_; + Scanner scanner_; + + Scope* top_scope_; + int with_nesting_level_; + + TemporaryScope* temp_scope_; + Mode mode_; + + Target* target_stack_; // for break, continue statements + bool allow_natives_syntax_; + v8::Extension* extension_; + bool is_pre_parsing_; + ScriptDataImpl* pre_data_; + FuncNameInferrer* fni_; }; @@ -673,6 +808,9 @@ class CompileTimeValue: public AllStatic { }; +// ---------------------------------------------------------------------------- +// JSON PARSING + // JSON is a subset of JavaScript, as specified in, e.g., the ECMAScript 5 // specification section 15.12.1 (and appendix A.8). // The grammar is given section 15.12.1.2 (and appendix A.8.2). diff --git a/deps/v8/src/platform-linux.cc b/deps/v8/src/platform-linux.cc index c0eb21395f..89003ba83f 100644 --- a/deps/v8/src/platform-linux.cc +++ b/deps/v8/src/platform-linux.cc @@ -99,30 +99,12 @@ uint64_t OS::CpuFeaturesImpliedByPlatform() { #ifdef __arm__ -bool OS::ArmCpuHasFeature(CpuFeature feature) { - const char* search_string = NULL; +static bool CPUInfoContainsString(const char * search_string) { const char* file_name = "/proc/cpuinfo"; - // Simple detection of VFP at runtime for Linux. - // It is based on /proc/cpuinfo, which reveals hardware configuration - // to user-space applications. According to ARM (mid 2009), no similar - // facility is universally available on the ARM architectures, - // so it's up to individual OSes to provide such. - // // This is written as a straight shot one pass parser // and not using STL string and ifstream because, // on Linux, it's reading from a (non-mmap-able) // character special device. - switch (feature) { - case VFP3: - search_string = "vfp"; - break; - case ARMv7: - search_string = "ARMv7"; - break; - default: - UNREACHABLE(); - } - FILE* f = NULL; const char* what = search_string; @@ -149,6 +131,43 @@ bool OS::ArmCpuHasFeature(CpuFeature feature) { // Did not find string in the proc file. return false; } + +bool OS::ArmCpuHasFeature(CpuFeature feature) { + const int max_items = 2; + const char* search_strings[max_items] = { NULL, NULL }; + int search_items = 0; + // Simple detection of VFP at runtime for Linux. + // It is based on /proc/cpuinfo, which reveals hardware configuration + // to user-space applications. According to ARM (mid 2009), no similar + // facility is universally available on the ARM architectures, + // so it's up to individual OSes to provide such. + switch (feature) { + case VFP3: + search_strings[0] = "vfpv3"; + // Some old kernels will report vfp for A8, not vfpv3, so we check for + // A8 explicitely. The cpuinfo file report the CPU Part which for Cortex + // A8 is 0xc08. + search_strings[1] = "0xc08"; + search_items = 2; + ASSERT(search_items <= max_items); + break; + case ARMv7: + search_strings[0] = "ARMv7" ; + search_items = 1; + ASSERT(search_items <= max_items); + break; + default: + UNREACHABLE(); + } + + for (int i = 0; i < search_items; ++i) { + if (CPUInfoContainsString(search_strings[i])) { + return true; + } + } + + return false; +} #endif // def __arm__ @@ -809,8 +828,17 @@ class Sampler::PlatformData : public Malloced { syscall(SYS_tgkill, vm_tgid_, vm_tid_, SIGPROF); // Convert ms to us and subtract 100 us to compensate delays // occuring during signal delivery. - int result = usleep(sampler_->interval_ * 1000 - 100); - ASSERT(result == 0 || errno == EINTR); + const useconds_t interval = sampler_->interval_ * 1000 - 100; + int result = usleep(interval); +#ifdef DEBUG + if (result != 0 && errno != EINTR) { + fprintf(stderr, + "SignalSender usleep error; interval = %u, errno = %d\n", + interval, + errno); + ASSERT(result == 0 || errno == EINTR); + } +#endif USE(result); } } @@ -843,6 +871,7 @@ Sampler::Sampler(int interval, bool profiling) Sampler::~Sampler() { + ASSERT(!data_->signal_sender_launched_); delete data_; } diff --git a/deps/v8/src/platform-win32.cc b/deps/v8/src/platform-win32.cc index caea16cb9b..a0ba5e865c 100644 --- a/deps/v8/src/platform-win32.cc +++ b/deps/v8/src/platform-win32.cc @@ -865,8 +865,9 @@ void* OS::Allocate(const size_t requested, // For exectutable pages try and randomize the allocation address if (prot == PAGE_EXECUTE_READWRITE && msize >= Page::kPageSize) { - address = (V8::Random() << kPageSizeBits) | kAllocationRandomAddressMin; - address &= kAllocationRandomAddressMax; + address = (V8::RandomPrivate() << kPageSizeBits) + | kAllocationRandomAddressMin; + address &= kAllocationRandomAddressMax; } LPVOID mbase = VirtualAlloc(reinterpret_cast<void *>(address), diff --git a/deps/v8/src/preparser.h b/deps/v8/src/preparser.h new file mode 100644 index 0000000000..547e766752 --- /dev/null +++ b/deps/v8/src/preparser.h @@ -0,0 +1,1419 @@ +// Copyright 2010 the V8 project authors. All rights reserved. +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following +// disclaimer in the documentation and/or other materials provided +// with the distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +#ifndef V8_PREPARSER_H +#define V8_PREPARSER_H + +#include "unicode.h" +#include "utils.h" + +namespace v8 { +namespace preparser { + +// Preparsing checks a JavaScript program and emits preparse-data that helps +// a later parsing to be faster. +// See preparser-data.h for the data. + +// The PreParser checks that the syntax follows the grammar for JavaScript, +// and collects some information about the program along the way. +// The grammar check is only performed in order to understand the program +// sufficiently to deduce some information about it, that can be used +// to speed up later parsing. Finding errors is not the goal of pre-parsing, +// rather it is to speed up properly written and correct programs. +// That means that contextual checks (like a label being declared where +// it is used) are generally omitted. + +namespace i = v8::internal; + +enum StatementType { + kUnknownStatement +}; + +enum ExpressionType { + kUnknownExpression, + kIdentifierExpression, // Used to detect labels. + kThisExpression, + kThisPropertyExpression +}; + +enum IdentifierType { + kUnknownIdentifier +}; + +enum SourceElementTypes { + kUnknownSourceElements +}; + + +typedef int SourceElements; +typedef int Expression; +typedef int Statement; +typedef int Identifier; +typedef int Arguments; + + +template <typename Scanner, typename PreParserLog> +class PreParser { + public: + PreParser() : scope_(NULL), allow_lazy_(true) { } + ~PreParser() { } + + // Pre-parse the program from the character stream; returns true on + // success (even if parsing failed, the pre-parse data successfully + // captured the syntax error), and false if a stack-overflow happened + // during parsing. + bool PreParseProgram(Scanner* scanner, + PreParserLog* log, + bool allow_lazy) { + allow_lazy_ = allow_lazy; + scanner_ = scanner; + log_ = log; + Scope top_scope(&scope_, kTopLevelScope); + bool ok = true; + ParseSourceElements(i::Token::EOS, &ok); + bool stack_overflow = scanner_->stack_overflow(); + if (!ok && !stack_overflow) { + ReportUnexpectedToken(scanner_->current_token()); + } + return !stack_overflow; + } + + private: + enum ScopeType { + kTopLevelScope, + kFunctionScope + }; + + class Scope { + public: + Scope(Scope** variable, ScopeType type) + : variable_(variable), + prev_(*variable), + type_(type), + materialized_literal_count_(0), + expected_properties_(0), + with_nesting_count_(0) { + *variable = this; + } + ~Scope() { *variable_ = prev_; } + void NextMaterializedLiteralIndex() { materialized_literal_count_++; } + void AddProperty() { expected_properties_++; } + ScopeType type() { return type_; } + int expected_properties() { return expected_properties_; } + int materialized_literal_count() { return materialized_literal_count_; } + bool IsInsideWith() { return with_nesting_count_ != 0; } + void EnterWith() { with_nesting_count_++; } + void LeaveWith() { with_nesting_count_--; } + + private: + Scope** const variable_; + Scope* const prev_; + const ScopeType type_; + int materialized_literal_count_; + int expected_properties_; + int with_nesting_count_; + }; + + // Types that allow us to recognize simple this-property assignments. + // A simple this-property assignment is a statement on the form + // "this.propertyName = {primitive constant or function parameter name);" + // where propertyName isn't "__proto__". + // The result is only relevant if the function body contains only + // simple this-property assignments. + + // Report syntax error + void ReportUnexpectedToken(i::Token::Value token); + void ReportMessageAt(int start_pos, + int end_pos, + const char* type, + const char* name_opt) { + log_->LogMessage(start_pos, end_pos, type, name_opt); + } + + // All ParseXXX functions take as the last argument an *ok parameter + // which is set to false if parsing failed; it is unchanged otherwise. + // By making the 'exception handling' explicit, we are forced to check + // for failure at the call sites. + SourceElements ParseSourceElements(int end_token, bool* ok); + Statement ParseStatement(bool* ok); + Statement ParseFunctionDeclaration(bool* ok); + Statement ParseNativeDeclaration(bool* ok); + Statement ParseBlock(bool* ok); + Statement ParseVariableStatement(bool* ok); + Statement ParseVariableDeclarations(bool accept_IN, int* num_decl, bool* ok); + Statement ParseExpressionOrLabelledStatement(bool* ok); + Statement ParseIfStatement(bool* ok); + Statement ParseContinueStatement(bool* ok); + Statement ParseBreakStatement(bool* ok); + Statement ParseReturnStatement(bool* ok); + Statement ParseWithStatement(bool* ok); + Statement ParseSwitchStatement(bool* ok); + Statement ParseDoWhileStatement(bool* ok); + Statement ParseWhileStatement(bool* ok); + Statement ParseForStatement(bool* ok); + Statement ParseThrowStatement(bool* ok); + Statement ParseTryStatement(bool* ok); + Statement ParseDebuggerStatement(bool* ok); + + Expression ParseExpression(bool accept_IN, bool* ok); + Expression ParseAssignmentExpression(bool accept_IN, bool* ok); + Expression ParseConditionalExpression(bool accept_IN, bool* ok); + Expression ParseBinaryExpression(int prec, bool accept_IN, bool* ok); + Expression ParseUnaryExpression(bool* ok); + Expression ParsePostfixExpression(bool* ok); + Expression ParseLeftHandSideExpression(bool* ok); + Expression ParseNewExpression(bool* ok); + Expression ParseMemberExpression(bool* ok); + Expression ParseMemberWithNewPrefixesExpression(unsigned new_count, bool* ok); + Expression ParsePrimaryExpression(bool* ok); + Expression ParseArrayLiteral(bool* ok); + Expression ParseObjectLiteral(bool* ok); + Expression ParseRegExpLiteral(bool seen_equal, bool* ok); + Expression ParseV8Intrinsic(bool* ok); + + Arguments ParseArguments(bool* ok); + Expression ParseFunctionLiteral(bool* ok); + + Identifier ParseIdentifier(bool* ok); + Identifier ParseIdentifierName(bool* ok); + Identifier ParseIdentifierOrGetOrSet(bool* is_get, bool* is_set, bool* ok); + + Identifier GetIdentifierSymbol(); + unsigned int HexDigitValue(char digit); + Expression GetStringSymbol(); + + + i::Token::Value peek() { return scanner_->peek(); } + i::Token::Value Next() { + i::Token::Value next = scanner_->Next(); + return next; + } + + void Consume(i::Token::Value token) { + Next(); + } + + void Expect(i::Token::Value token, bool* ok) { + if (Next() != token) { + *ok = false; + } + } + + bool Check(i::Token::Value token) { + i::Token::Value next = peek(); + if (next == token) { + Consume(next); + return true; + } + return false; + } + void ExpectSemicolon(bool* ok); + + static int Precedence(i::Token::Value tok, bool accept_IN); + + Scanner* scanner_; + PreParserLog* log_; + Scope* scope_; + bool allow_lazy_; +}; + + +#define CHECK_OK ok); \ + if (!*ok) return -1; \ + ((void)0 +#define DUMMY ) // to make indentation work +#undef DUMMY + + +template <typename Scanner, typename Log> +void PreParser<Scanner, Log>::ReportUnexpectedToken(i::Token::Value token) { + // We don't report stack overflows here, to avoid increasing the + // stack depth even further. Instead we report it after parsing is + // over, in ParseProgram. + if (token == i::Token::ILLEGAL && scanner_->stack_overflow()) { + return; + } + typename Scanner::Location source_location = scanner_->location(); + + // Four of the tokens are treated specially + switch (token) { + case i::Token::EOS: + return ReportMessageAt(source_location.beg_pos, source_location.end_pos, + "unexpected_eos", NULL); + case i::Token::NUMBER: + return ReportMessageAt(source_location.beg_pos, source_location.end_pos, + "unexpected_token_number", NULL); + case i::Token::STRING: + return ReportMessageAt(source_location.beg_pos, source_location.end_pos, + "unexpected_token_string", NULL); + case i::Token::IDENTIFIER: + return ReportMessageAt(source_location.beg_pos, source_location.end_pos, + "unexpected_token_identifier", NULL); + default: + const char* name = i::Token::String(token); + ReportMessageAt(source_location.beg_pos, source_location.end_pos, + "unexpected_token", name); + } +} + + +template <typename Scanner, typename Log> +SourceElements PreParser<Scanner, Log>::ParseSourceElements(int end_token, + bool* ok) { + // SourceElements :: + // (Statement)* <end_token> + + while (peek() != end_token) { + ParseStatement(CHECK_OK); + } + return kUnknownSourceElements; +} + + +template <typename Scanner, typename Log> +Statement PreParser<Scanner, Log>::ParseStatement(bool* ok) { + // Statement :: + // Block + // VariableStatement + // EmptyStatement + // ExpressionStatement + // IfStatement + // IterationStatement + // ContinueStatement + // BreakStatement + // ReturnStatement + // WithStatement + // LabelledStatement + // SwitchStatement + // ThrowStatement + // TryStatement + // DebuggerStatement + + // Note: Since labels can only be used by 'break' and 'continue' + // statements, which themselves are only valid within blocks, + // iterations or 'switch' statements (i.e., BreakableStatements), + // labels can be simply ignored in all other cases; except for + // trivial labeled break statements 'label: break label' which is + // parsed into an empty statement. + + // Keep the source position of the statement + switch (peek()) { + case i::Token::LBRACE: + return ParseBlock(ok); + + case i::Token::CONST: + case i::Token::VAR: + return ParseVariableStatement(ok); + + case i::Token::SEMICOLON: + Next(); + return kUnknownStatement; + + case i::Token::IF: + return ParseIfStatement(ok); + + case i::Token::DO: + return ParseDoWhileStatement(ok); + + case i::Token::WHILE: + return ParseWhileStatement(ok); + + case i::Token::FOR: + return ParseForStatement(ok); + + case i::Token::CONTINUE: + return ParseContinueStatement(ok); + + case i::Token::BREAK: + return ParseBreakStatement(ok); + + case i::Token::RETURN: + return ParseReturnStatement(ok); + + case i::Token::WITH: + return ParseWithStatement(ok); + + case i::Token::SWITCH: + return ParseSwitchStatement(ok); + + case i::Token::THROW: + return ParseThrowStatement(ok); + + case i::Token::TRY: + return ParseTryStatement(ok); + + case i::Token::FUNCTION: + return ParseFunctionDeclaration(ok); + + case i::Token::NATIVE: + return ParseNativeDeclaration(ok); + + case i::Token::DEBUGGER: + return ParseDebuggerStatement(ok); + + default: + return ParseExpressionOrLabelledStatement(ok); + } +} + + +template <typename Scanner, typename Log> +Statement PreParser<Scanner, Log>::ParseFunctionDeclaration(bool* ok) { + // FunctionDeclaration :: + // 'function' Identifier '(' FormalParameterListopt ')' '{' FunctionBody '}' + Expect(i::Token::FUNCTION, CHECK_OK); + ParseIdentifier(CHECK_OK); + ParseFunctionLiteral(CHECK_OK); + return kUnknownStatement; +} + + +// Language extension which is only enabled for source files loaded +// through the API's extension mechanism. A native function +// declaration is resolved by looking up the function through a +// callback provided by the extension. +template <typename Scanner, typename Log> +Statement PreParser<Scanner, Log>::ParseNativeDeclaration(bool* ok) { + Expect(i::Token::NATIVE, CHECK_OK); + Expect(i::Token::FUNCTION, CHECK_OK); + ParseIdentifier(CHECK_OK); + Expect(i::Token::LPAREN, CHECK_OK); + bool done = (peek() == i::Token::RPAREN); + while (!done) { + ParseIdentifier(CHECK_OK); + done = (peek() == i::Token::RPAREN); + if (!done) { + Expect(i::Token::COMMA, CHECK_OK); + } + } + Expect(i::Token::RPAREN, CHECK_OK); + Expect(i::Token::SEMICOLON, CHECK_OK); + return kUnknownStatement; +} + + +template <typename Scanner, typename Log> +Statement PreParser<Scanner, Log>::ParseBlock(bool* ok) { + // Block :: + // '{' Statement* '}' + + // Note that a Block does not introduce a new execution scope! + // (ECMA-262, 3rd, 12.2) + // + Expect(i::Token::LBRACE, CHECK_OK); + while (peek() != i::Token::RBRACE) { + ParseStatement(CHECK_OK); + } + Expect(i::Token::RBRACE, CHECK_OK); + return kUnknownStatement; +} + + +template <typename Scanner, typename Log> +Statement PreParser<Scanner, Log>::ParseVariableStatement(bool* ok) { + // VariableStatement :: + // VariableDeclarations ';' + + Statement result = ParseVariableDeclarations(true, NULL, CHECK_OK); + ExpectSemicolon(CHECK_OK); + return result; +} + + +// If the variable declaration declares exactly one non-const +// variable, then *var is set to that variable. In all other cases, +// *var is untouched; in particular, it is the caller's responsibility +// to initialize it properly. This mechanism is also used for the parsing +// of 'for-in' loops. +template <typename Scanner, typename Log> +Statement PreParser<Scanner, Log>::ParseVariableDeclarations(bool accept_IN, + int* num_decl, + bool* ok) { + // VariableDeclarations :: + // ('var' | 'const') (Identifier ('=' AssignmentExpression)?)+[','] + + if (peek() == i::Token::VAR) { + Consume(i::Token::VAR); + } else if (peek() == i::Token::CONST) { + Consume(i::Token::CONST); + } else { + *ok = false; + return 0; + } + + // The scope of a variable/const declared anywhere inside a function + // is the entire function (ECMA-262, 3rd, 10.1.3, and 12.2). . + int nvars = 0; // the number of variables declared + do { + // Parse variable name. + if (nvars > 0) Consume(i::Token::COMMA); + ParseIdentifier(CHECK_OK); + nvars++; + if (peek() == i::Token::ASSIGN) { + Expect(i::Token::ASSIGN, CHECK_OK); + ParseAssignmentExpression(accept_IN, CHECK_OK); + } + } while (peek() == i::Token::COMMA); + + if (num_decl != NULL) *num_decl = nvars; + return kUnknownStatement; +} + + +template <typename Scanner, typename Log> +Statement PreParser<Scanner, Log>::ParseExpressionOrLabelledStatement( + bool* ok) { + // ExpressionStatement | LabelledStatement :: + // Expression ';' + // Identifier ':' Statement + + Expression expr = ParseExpression(true, CHECK_OK); + if (peek() == i::Token::COLON && expr == kIdentifierExpression) { + Consume(i::Token::COLON); + return ParseStatement(ok); + } + // Parsed expression statement. + ExpectSemicolon(CHECK_OK); + return kUnknownStatement; +} + + +template <typename Scanner, typename Log> +Statement PreParser<Scanner, Log>::ParseIfStatement(bool* ok) { + // IfStatement :: + // 'if' '(' Expression ')' Statement ('else' Statement)? + + Expect(i::Token::IF, CHECK_OK); + Expect(i::Token::LPAREN, CHECK_OK); + ParseExpression(true, CHECK_OK); + Expect(i::Token::RPAREN, CHECK_OK); + ParseStatement(CHECK_OK); + if (peek() == i::Token::ELSE) { + Next(); + ParseStatement(CHECK_OK); + } + return kUnknownStatement; +} + + +template <typename Scanner, typename Log> +Statement PreParser<Scanner, Log>::ParseContinueStatement(bool* ok) { + // ContinueStatement :: + // 'continue' [no line terminator] Identifier? ';' + + Expect(i::Token::CONTINUE, CHECK_OK); + i::Token::Value tok = peek(); + if (!scanner_->has_line_terminator_before_next() && + tok != i::Token::SEMICOLON && + tok != i::Token::RBRACE && + tok != i::Token::EOS) { + ParseIdentifier(CHECK_OK); + } + ExpectSemicolon(CHECK_OK); + return kUnknownStatement; +} + + +template <typename Scanner, typename Log> +Statement PreParser<Scanner, Log>::ParseBreakStatement(bool* ok) { + // BreakStatement :: + // 'break' [no line terminator] Identifier? ';' + + Expect(i::Token::BREAK, CHECK_OK); + i::Token::Value tok = peek(); + if (!scanner_->has_line_terminator_before_next() && + tok != i::Token::SEMICOLON && + tok != i::Token::RBRACE && + tok != i::Token::EOS) { + ParseIdentifier(CHECK_OK); + } + ExpectSemicolon(CHECK_OK); + return kUnknownStatement; +} + + +template <typename Scanner, typename Log> +Statement PreParser<Scanner, Log>::ParseReturnStatement(bool* ok) { + // ReturnStatement :: + // 'return' [no line terminator] Expression? ';' + + // Consume the return token. It is necessary to do the before + // reporting any errors on it, because of the way errors are + // reported (underlining). + Expect(i::Token::RETURN, CHECK_OK); + + // An ECMAScript program is considered syntactically incorrect if it + // contains a return statement that is not within the body of a + // function. See ECMA-262, section 12.9, page 67. + // This is not handled during preparsing. + + i::Token::Value tok = peek(); + if (!scanner_->has_line_terminator_before_next() && + tok != i::Token::SEMICOLON && + tok != i::Token::RBRACE && + tok != i::Token::EOS) { + ParseExpression(true, CHECK_OK); + } + ExpectSemicolon(CHECK_OK); + return kUnknownStatement; +} + + +template <typename Scanner, typename Log> +Statement PreParser<Scanner, Log>::ParseWithStatement(bool* ok) { + // WithStatement :: + // 'with' '(' Expression ')' Statement + Expect(i::Token::WITH, CHECK_OK); + Expect(i::Token::LPAREN, CHECK_OK); + ParseExpression(true, CHECK_OK); + Expect(i::Token::RPAREN, CHECK_OK); + + scope_->EnterWith(); + ParseStatement(CHECK_OK); + scope_->LeaveWith(); + return kUnknownStatement; +} + + +template <typename Scanner, typename Log> +Statement PreParser<Scanner, Log>::ParseSwitchStatement(bool* ok) { + // SwitchStatement :: + // 'switch' '(' Expression ')' '{' CaseClause* '}' + + Expect(i::Token::SWITCH, CHECK_OK); + Expect(i::Token::LPAREN, CHECK_OK); + ParseExpression(true, CHECK_OK); + Expect(i::Token::RPAREN, CHECK_OK); + + Expect(i::Token::LBRACE, CHECK_OK); + i::Token::Value token = peek(); + while (token != i::Token::RBRACE) { + if (token == i::Token::CASE) { + Expect(i::Token::CASE, CHECK_OK); + ParseExpression(true, CHECK_OK); + Expect(i::Token::COLON, CHECK_OK); + } else if (token == i::Token::DEFAULT) { + Expect(i::Token::DEFAULT, CHECK_OK); + Expect(i::Token::COLON, CHECK_OK); + } else { + ParseStatement(CHECK_OK); + } + token = peek(); + } + Expect(i::Token::RBRACE, CHECK_OK); + + return kUnknownStatement; +} + + +template <typename Scanner, typename Log> +Statement PreParser<Scanner, Log>::ParseDoWhileStatement(bool* ok) { + // DoStatement :: + // 'do' Statement 'while' '(' Expression ')' ';' + + Expect(i::Token::DO, CHECK_OK); + ParseStatement(CHECK_OK); + Expect(i::Token::WHILE, CHECK_OK); + Expect(i::Token::LPAREN, CHECK_OK); + ParseExpression(true, CHECK_OK); + Expect(i::Token::RPAREN, CHECK_OK); + return kUnknownStatement; +} + + +template <typename Scanner, typename Log> +Statement PreParser<Scanner, Log>::ParseWhileStatement(bool* ok) { + // WhileStatement :: + // 'while' '(' Expression ')' Statement + + Expect(i::Token::WHILE, CHECK_OK); + Expect(i::Token::LPAREN, CHECK_OK); + ParseExpression(true, CHECK_OK); + Expect(i::Token::RPAREN, CHECK_OK); + ParseStatement(CHECK_OK); + return kUnknownStatement; +} + + +template <typename Scanner, typename Log> +Statement PreParser<Scanner, Log>::ParseForStatement(bool* ok) { + // ForStatement :: + // 'for' '(' Expression? ';' Expression? ';' Expression? ')' Statement + + Expect(i::Token::FOR, CHECK_OK); + Expect(i::Token::LPAREN, CHECK_OK); + if (peek() != i::Token::SEMICOLON) { + if (peek() == i::Token::VAR || peek() == i::Token::CONST) { + int decl_count; + ParseVariableDeclarations(false, &decl_count, CHECK_OK); + if (peek() == i::Token::IN && decl_count == 1) { + Expect(i::Token::IN, CHECK_OK); + ParseExpression(true, CHECK_OK); + Expect(i::Token::RPAREN, CHECK_OK); + + ParseStatement(CHECK_OK); + return kUnknownStatement; + } + } else { + ParseExpression(false, CHECK_OK); + if (peek() == i::Token::IN) { + Expect(i::Token::IN, CHECK_OK); + ParseExpression(true, CHECK_OK); + Expect(i::Token::RPAREN, CHECK_OK); + + ParseStatement(CHECK_OK); + return kUnknownStatement; + } + } + } + + // Parsed initializer at this point. + Expect(i::Token::SEMICOLON, CHECK_OK); + + if (peek() != i::Token::SEMICOLON) { + ParseExpression(true, CHECK_OK); + } + Expect(i::Token::SEMICOLON, CHECK_OK); + + if (peek() != i::Token::RPAREN) { + ParseExpression(true, CHECK_OK); + } + Expect(i::Token::RPAREN, CHECK_OK); + + ParseStatement(CHECK_OK); + return kUnknownStatement; +} + + +template <typename Scanner, typename Log> +Statement PreParser<Scanner, Log>::ParseThrowStatement(bool* ok) { + // ThrowStatement :: + // 'throw' [no line terminator] Expression ';' + + Expect(i::Token::THROW, CHECK_OK); + if (scanner_->has_line_terminator_before_next()) { + typename Scanner::Location pos = scanner_->location(); + ReportMessageAt(pos.beg_pos, pos.end_pos, + "newline_after_throw", NULL); + *ok = false; + return NULL; + } + ParseExpression(true, CHECK_OK); + ExpectSemicolon(CHECK_OK); + + return kUnknownStatement; +} + + +template <typename Scanner, typename Log> +Statement PreParser<Scanner, Log>::ParseTryStatement(bool* ok) { + // TryStatement :: + // 'try' Block Catch + // 'try' Block Finally + // 'try' Block Catch Finally + // + // Catch :: + // 'catch' '(' Identifier ')' Block + // + // Finally :: + // 'finally' Block + + // In preparsing, allow any number of catch/finally blocks, including zero + // of both. + + Expect(i::Token::TRY, CHECK_OK); + + ParseBlock(CHECK_OK); + + bool catch_or_finally_seen = false; + if (peek() == i::Token::CATCH) { + Consume(i::Token::CATCH); + Expect(i::Token::LPAREN, CHECK_OK); + ParseIdentifier(CHECK_OK); + Expect(i::Token::RPAREN, CHECK_OK); + scope_->EnterWith(); + ParseBlock(ok); + scope_->LeaveWith(); + if (!*ok) return kUnknownStatement; + catch_or_finally_seen = true; + } + if (peek() == i::Token::FINALLY) { + Consume(i::Token::FINALLY); + ParseBlock(CHECK_OK); + catch_or_finally_seen = true; + } + if (!catch_or_finally_seen) { + *ok = false; + } + return kUnknownStatement; +} + + +template <typename Scanner, typename Log> +Statement PreParser<Scanner, Log>::ParseDebuggerStatement(bool* ok) { + // In ECMA-262 'debugger' is defined as a reserved keyword. In some browser + // contexts this is used as a statement which invokes the debugger as if a + // break point is present. + // DebuggerStatement :: + // 'debugger' ';' + + Expect(i::Token::DEBUGGER, CHECK_OK); + ExpectSemicolon(CHECK_OK); + return kUnknownStatement; +} + + +// Precedence = 1 +template <typename Scanner, typename Log> +Expression PreParser<Scanner, Log>::ParseExpression(bool accept_IN, bool* ok) { + // Expression :: + // AssignmentExpression + // Expression ',' AssignmentExpression + + Expression result = ParseAssignmentExpression(accept_IN, CHECK_OK); + while (peek() == i::Token::COMMA) { + Expect(i::Token::COMMA, CHECK_OK); + ParseAssignmentExpression(accept_IN, CHECK_OK); + result = kUnknownExpression; + } + return result; +} + + +// Precedence = 2 +template <typename Scanner, typename Log> +Expression PreParser<Scanner, Log>::ParseAssignmentExpression(bool accept_IN, + bool* ok) { + // AssignmentExpression :: + // ConditionalExpression + // LeftHandSideExpression AssignmentOperator AssignmentExpression + + Expression expression = ParseConditionalExpression(accept_IN, CHECK_OK); + + if (!i::Token::IsAssignmentOp(peek())) { + // Parsed conditional expression only (no assignment). + return expression; + } + + i::Token::Value op = Next(); // Get assignment operator. + ParseAssignmentExpression(accept_IN, CHECK_OK); + + if ((op == i::Token::ASSIGN) && (expression == kThisPropertyExpression)) { + scope_->AddProperty(); + } + + return kUnknownExpression; +} + + +// Precedence = 3 +template <typename Scanner, typename Log> +Expression PreParser<Scanner, Log>::ParseConditionalExpression(bool accept_IN, + bool* ok) { + // ConditionalExpression :: + // LogicalOrExpression + // LogicalOrExpression '?' AssignmentExpression ':' AssignmentExpression + + // We start using the binary expression parser for prec >= 4 only! + Expression expression = ParseBinaryExpression(4, accept_IN, CHECK_OK); + if (peek() != i::Token::CONDITIONAL) return expression; + Consume(i::Token::CONDITIONAL); + // In parsing the first assignment expression in conditional + // expressions we always accept the 'in' keyword; see ECMA-262, + // section 11.12, page 58. + ParseAssignmentExpression(true, CHECK_OK); + Expect(i::Token::COLON, CHECK_OK); + ParseAssignmentExpression(accept_IN, CHECK_OK); + return kUnknownExpression; +} + + +template <typename Scanner, typename Log> +int PreParser<Scanner, Log>::Precedence(i::Token::Value tok, bool accept_IN) { + if (tok == i::Token::IN && !accept_IN) + return 0; // 0 precedence will terminate binary expression parsing + + return i::Token::Precedence(tok); +} + + +// Precedence >= 4 +template <typename Scanner, typename Log> +Expression PreParser<Scanner, Log>::ParseBinaryExpression(int prec, + bool accept_IN, + bool* ok) { + Expression result = ParseUnaryExpression(CHECK_OK); + for (int prec1 = Precedence(peek(), accept_IN); prec1 >= prec; prec1--) { + // prec1 >= 4 + while (Precedence(peek(), accept_IN) == prec1) { + Next(); + ParseBinaryExpression(prec1 + 1, accept_IN, CHECK_OK); + result = kUnknownExpression; + } + } + return result; +} + + +template <typename Scanner, typename Log> +Expression PreParser<Scanner, Log>::ParseUnaryExpression(bool* ok) { + // UnaryExpression :: + // PostfixExpression + // 'delete' UnaryExpression + // 'void' UnaryExpression + // 'typeof' UnaryExpression + // '++' UnaryExpression + // '--' UnaryExpression + // '+' UnaryExpression + // '-' UnaryExpression + // '~' UnaryExpression + // '!' UnaryExpression + + i::Token::Value op = peek(); + if (i::Token::IsUnaryOp(op) || i::Token::IsCountOp(op)) { + op = Next(); + ParseUnaryExpression(ok); + return kUnknownExpression; + } else { + return ParsePostfixExpression(ok); + } +} + + +template <typename Scanner, typename Log> +Expression PreParser<Scanner, Log>::ParsePostfixExpression(bool* ok) { + // PostfixExpression :: + // LeftHandSideExpression ('++' | '--')? + + Expression expression = ParseLeftHandSideExpression(CHECK_OK); + if (!scanner_->has_line_terminator_before_next() && + i::Token::IsCountOp(peek())) { + Next(); + return kUnknownExpression; + } + return expression; +} + + +template <typename Scanner, typename Log> +Expression PreParser<Scanner, Log>::ParseLeftHandSideExpression(bool* ok) { + // LeftHandSideExpression :: + // (NewExpression | MemberExpression) ... + + Expression result; + if (peek() == i::Token::NEW) { + result = ParseNewExpression(CHECK_OK); + } else { + result = ParseMemberExpression(CHECK_OK); + } + + while (true) { + switch (peek()) { + case i::Token::LBRACK: { + Consume(i::Token::LBRACK); + ParseExpression(true, CHECK_OK); + Expect(i::Token::RBRACK, CHECK_OK); + if (result == kThisExpression) { + result = kThisPropertyExpression; + } else { + result = kUnknownExpression; + } + break; + } + + case i::Token::LPAREN: { + ParseArguments(CHECK_OK); + result = kUnknownExpression; + break; + } + + case i::Token::PERIOD: { + Consume(i::Token::PERIOD); + ParseIdentifierName(CHECK_OK); + if (result == kThisExpression) { + result = kThisPropertyExpression; + } else { + result = kUnknownExpression; + } + break; + } + + default: + return result; + } + } +} + + +template <typename Scanner, typename Log> +Expression PreParser<Scanner, Log>::ParseNewExpression(bool* ok) { + // NewExpression :: + // ('new')+ MemberExpression + + // The grammar for new expressions is pretty warped. The keyword + // 'new' can either be a part of the new expression (where it isn't + // followed by an argument list) or a part of the member expression, + // where it must be followed by an argument list. To accommodate + // this, we parse the 'new' keywords greedily and keep track of how + // many we have parsed. This information is then passed on to the + // member expression parser, which is only allowed to match argument + // lists as long as it has 'new' prefixes left + unsigned new_count = 0; + do { + Consume(i::Token::NEW); + new_count++; + } while (peek() == i::Token::NEW); + + return ParseMemberWithNewPrefixesExpression(new_count, ok); +} + + +template <typename Scanner, typename Log> +Expression PreParser<Scanner, Log>::ParseMemberExpression(bool* ok) { + return ParseMemberWithNewPrefixesExpression(0, ok); +} + + +template <typename Scanner, typename Log> +Expression PreParser<Scanner, Log>::ParseMemberWithNewPrefixesExpression( + unsigned new_count, bool* ok) { + // MemberExpression :: + // (PrimaryExpression | FunctionLiteral) + // ('[' Expression ']' | '.' Identifier | Arguments)* + + // Parse the initial primary or function expression. + Expression result = NULL; + if (peek() == i::Token::FUNCTION) { + Consume(i::Token::FUNCTION); + if (peek() == i::Token::IDENTIFIER) { + ParseIdentifier(CHECK_OK); + } + result = ParseFunctionLiteral(CHECK_OK); + } else { + result = ParsePrimaryExpression(CHECK_OK); + } + + while (true) { + switch (peek()) { + case i::Token::LBRACK: { + Consume(i::Token::LBRACK); + ParseExpression(true, CHECK_OK); + Expect(i::Token::RBRACK, CHECK_OK); + if (result == kThisExpression) { + result = kThisPropertyExpression; + } else { + result = kUnknownExpression; + } + break; + } + case i::Token::PERIOD: { + Consume(i::Token::PERIOD); + ParseIdentifierName(CHECK_OK); + if (result == kThisExpression) { + result = kThisPropertyExpression; + } else { + result = kUnknownExpression; + } + break; + } + case i::Token::LPAREN: { + if (new_count == 0) return result; + // Consume one of the new prefixes (already parsed). + ParseArguments(CHECK_OK); + new_count--; + result = kUnknownExpression; + break; + } + default: + return result; + } + } +} + + +template <typename Scanner, typename Log> +Expression PreParser<Scanner, Log>::ParsePrimaryExpression(bool* ok) { + // PrimaryExpression :: + // 'this' + // 'null' + // 'true' + // 'false' + // Identifier + // Number + // String + // ArrayLiteral + // ObjectLiteral + // RegExpLiteral + // '(' Expression ')' + + Expression result = kUnknownExpression; + switch (peek()) { + case i::Token::THIS: { + Next(); + result = kThisExpression; + break; + } + + case i::Token::IDENTIFIER: { + ParseIdentifier(CHECK_OK); + result = kIdentifierExpression; + break; + } + + case i::Token::NULL_LITERAL: + case i::Token::TRUE_LITERAL: + case i::Token::FALSE_LITERAL: + case i::Token::NUMBER: { + Next(); + break; + } + case i::Token::STRING: { + Next(); + result = GetStringSymbol(); + break; + } + + case i::Token::ASSIGN_DIV: + result = ParseRegExpLiteral(true, CHECK_OK); + break; + + case i::Token::DIV: + result = ParseRegExpLiteral(false, CHECK_OK); + break; + + case i::Token::LBRACK: + result = ParseArrayLiteral(CHECK_OK); + break; + + case i::Token::LBRACE: + result = ParseObjectLiteral(CHECK_OK); + break; + + case i::Token::LPAREN: + Consume(i::Token::LPAREN); + result = ParseExpression(true, CHECK_OK); + Expect(i::Token::RPAREN, CHECK_OK); + if (result == kIdentifierExpression) result = kUnknownExpression; + break; + + case i::Token::MOD: + result = ParseV8Intrinsic(CHECK_OK); + break; + + default: { + Next(); + *ok = false; + return kUnknownExpression; + } + } + + return result; +} + + +template <typename Scanner, typename Log> +Expression PreParser<Scanner, Log>::ParseArrayLiteral(bool* ok) { + // ArrayLiteral :: + // '[' Expression? (',' Expression?)* ']' + Expect(i::Token::LBRACK, CHECK_OK); + while (peek() != i::Token::RBRACK) { + if (peek() != i::Token::COMMA) { + ParseAssignmentExpression(true, CHECK_OK); + } + if (peek() != i::Token::RBRACK) { + Expect(i::Token::COMMA, CHECK_OK); + } + } + Expect(i::Token::RBRACK, CHECK_OK); + + scope_->NextMaterializedLiteralIndex(); + return kUnknownExpression; +} + + +template <typename Scanner, typename Log> +Expression PreParser<Scanner, Log>::ParseObjectLiteral(bool* ok) { + // ObjectLiteral :: + // '{' ( + // ((IdentifierName | String | Number) ':' AssignmentExpression) + // | (('get' | 'set') (IdentifierName | String | Number) FunctionLiteral) + // )*[','] '}' + + Expect(i::Token::LBRACE, CHECK_OK); + while (peek() != i::Token::RBRACE) { + i::Token::Value next = peek(); + switch (next) { + case i::Token::IDENTIFIER: { + bool is_getter = false; + bool is_setter = false; + ParseIdentifierOrGetOrSet(&is_getter, &is_setter, CHECK_OK); + if ((is_getter || is_setter) && peek() != i::Token::COLON) { + i::Token::Value name = Next(); + if (name != i::Token::IDENTIFIER && + name != i::Token::NUMBER && + name != i::Token::STRING && + !i::Token::IsKeyword(name)) { + *ok = false; + return kUnknownExpression; + } + ParseFunctionLiteral(CHECK_OK); + if (peek() != i::Token::RBRACE) { + Expect(i::Token::COMMA, CHECK_OK); + } + continue; // restart the while + } + break; + } + case i::Token::STRING: + Consume(next); + GetStringSymbol(); + break; + case i::Token::NUMBER: + Consume(next); + break; + default: + if (i::Token::IsKeyword(next)) { + Consume(next); + } else { + // Unexpected token. + *ok = false; + return kUnknownExpression; + } + } + + Expect(i::Token::COLON, CHECK_OK); + ParseAssignmentExpression(true, CHECK_OK); + + // TODO(1240767): Consider allowing trailing comma. + if (peek() != i::Token::RBRACE) Expect(i::Token::COMMA, CHECK_OK); + } + Expect(i::Token::RBRACE, CHECK_OK); + + scope_->NextMaterializedLiteralIndex(); + return kUnknownExpression; +} + + +template <typename Scanner, typename Log> +Expression PreParser<Scanner, Log>::ParseRegExpLiteral(bool seen_equal, + bool* ok) { + if (!scanner_->ScanRegExpPattern(seen_equal)) { + Next(); + typename Scanner::Location location = scanner_->location(); + ReportMessageAt(location.beg_pos, location.end_pos, + "unterminated_regexp", NULL); + *ok = false; + return kUnknownExpression; + } + + scope_->NextMaterializedLiteralIndex(); + + if (!scanner_->ScanRegExpFlags()) { + Next(); + typename Scanner::Location location = scanner_->location(); + ReportMessageAt(location.beg_pos, location.end_pos, + "invalid_regexp_flags", NULL); + *ok = false; + return kUnknownExpression; + } + Next(); + return kUnknownExpression; +} + + +template <typename Scanner, typename Log> +Arguments PreParser<Scanner, Log>::ParseArguments(bool* ok) { + // Arguments :: + // '(' (AssignmentExpression)*[','] ')' + + Expect(i::Token::LPAREN, CHECK_OK); + bool done = (peek() == i::Token::RPAREN); + int argc = 0; + while (!done) { + ParseAssignmentExpression(true, CHECK_OK); + argc++; + done = (peek() == i::Token::RPAREN); + if (!done) Expect(i::Token::COMMA, CHECK_OK); + } + Expect(i::Token::RPAREN, CHECK_OK); + return argc; +} + + +template <typename Scanner, typename Log> +Expression PreParser<Scanner, Log>::ParseFunctionLiteral(bool* ok) { + // Function :: + // '(' FormalParameterList? ')' '{' FunctionBody '}' + + // Parse function body. + ScopeType outer_scope_type = scope_->type(); + bool inside_with = scope_->IsInsideWith(); + Scope function_scope(&scope_, kFunctionScope); + + // FormalParameterList :: + // '(' (Identifier)*[','] ')' + Expect(i::Token::LPAREN, CHECK_OK); + bool done = (peek() == i::Token::RPAREN); + while (!done) { + ParseIdentifier(CHECK_OK); + done = (peek() == i::Token::RPAREN); + if (!done) { + Expect(i::Token::COMMA, CHECK_OK); + } + } + Expect(i::Token::RPAREN, CHECK_OK); + + Expect(i::Token::LBRACE, CHECK_OK); + int function_block_pos = scanner_->location().beg_pos; + + // Determine if the function will be lazily compiled. + // Currently only happens to top-level functions. + // Optimistically assume that all top-level functions are lazily compiled. + bool is_lazily_compiled = + (outer_scope_type == kTopLevelScope && !inside_with && allow_lazy_); + + if (is_lazily_compiled) { + log_->PauseRecording(); + ParseSourceElements(i::Token::RBRACE, ok); + log_->ResumeRecording(); + if (!*ok) return kUnknownExpression; + + Expect(i::Token::RBRACE, CHECK_OK); + + int end_pos = scanner_->location().end_pos; + log_->LogFunction(function_block_pos, end_pos, + function_scope.materialized_literal_count(), + function_scope.expected_properties()); + } else { + ParseSourceElements(i::Token::RBRACE, CHECK_OK); + Expect(i::Token::RBRACE, CHECK_OK); + } + return kUnknownExpression; +} + + +template <typename Scanner, typename Log> +Expression PreParser<Scanner, Log>::ParseV8Intrinsic(bool* ok) { + // CallRuntime :: + // '%' Identifier Arguments + + Expect(i::Token::MOD, CHECK_OK); + ParseIdentifier(CHECK_OK); + ParseArguments(CHECK_OK); + + return kUnknownExpression; +} + + +template <typename Scanner, typename Log> +void PreParser<Scanner, Log>::ExpectSemicolon(bool* ok) { + // Check for automatic semicolon insertion according to + // the rules given in ECMA-262, section 7.9, page 21. + i::Token::Value tok = peek(); + if (tok == i::Token::SEMICOLON) { + Next(); + return; + } + if (scanner_->has_line_terminator_before_next() || + tok == i::Token::RBRACE || + tok == i::Token::EOS) { + return; + } + Expect(i::Token::SEMICOLON, ok); +} + + +template <typename Scanner, typename Log> +Identifier PreParser<Scanner, Log>::GetIdentifierSymbol() { + const char* literal_chars = scanner_->literal_string(); + int literal_length = scanner_->literal_length(); + int identifier_pos = scanner_->location().beg_pos; + + log_->LogSymbol(identifier_pos, literal_chars, literal_length); + + return kUnknownExpression; +} + + +template <typename Scanner, typename Log> +Expression PreParser<Scanner, Log>::GetStringSymbol() { + const char* literal_chars = scanner_->literal_string(); + int literal_length = scanner_->literal_length(); + + int literal_position = scanner_->location().beg_pos; + log_->LogSymbol(literal_position, literal_chars, literal_length); + + return kUnknownExpression; +} + + +template <typename Scanner, typename Log> +Identifier PreParser<Scanner, Log>::ParseIdentifier(bool* ok) { + Expect(i::Token::IDENTIFIER, ok); + if (!*ok) return kUnknownIdentifier; + return GetIdentifierSymbol(); +} + + +template <typename Scanner, typename Log> +Identifier PreParser<Scanner, Log>::ParseIdentifierName(bool* ok) { + i::Token::Value next = Next(); + if (i::Token::IsKeyword(next)) { + int pos = scanner_->location().beg_pos; + const char* keyword = i::Token::String(next); + log_->LogSymbol(pos, keyword, i::StrLength(keyword)); + return kUnknownExpression; + } + if (next == i::Token::IDENTIFIER) { + return GetIdentifierSymbol(); + } + *ok = false; + return kUnknownIdentifier; +} + + +// This function reads an identifier and determines whether or not it +// is 'get' or 'set'. The reason for not using ParseIdentifier and +// checking on the output is that this involves heap allocation which +// we can't do during preparsing. +template <typename Scanner, typename Log> +Identifier PreParser<Scanner, Log>::ParseIdentifierOrGetOrSet(bool* is_get, + bool* is_set, + bool* ok) { + Expect(i::Token::IDENTIFIER, CHECK_OK); + if (scanner_->literal_length() == 3) { + const char* token = scanner_->literal_string(); + *is_get = strncmp(token, "get", 3) == 0; + *is_set = !*is_get && strncmp(token, "set", 3) == 0; + } + return GetIdentifierSymbol(); +} + +#undef CHECK_OK +} } // v8::preparser + +#endif // V8_PREPARSER_H diff --git a/deps/v8/src/prescanner.h b/deps/v8/src/prescanner.h new file mode 100644 index 0000000000..7fb8aa5b1d --- /dev/null +++ b/deps/v8/src/prescanner.h @@ -0,0 +1,1098 @@ +// Copyright 2010 the V8 project authors. All rights reserved. +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following +// disclaimer in the documentation and/or other materials provided +// with the distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +#ifndef V8_PRESCANNER_H_ +#define V8_PRESCANNER_H_ + +#include "token.h" +#include "char-predicates-inl.h" +#include "utils.h" +#include "scanner-base.h" + +namespace v8 { +namespace preparser { + +namespace i = v8::internal; + +typedef int uc32; + +int HexValue(uc32 c) { + int res = c | 0x20; // Uppercase letters. + int is_digit = (c & 0x10) >> 4; // 0 if non-digit, 1 if digit. + // What to add to digits to make them consecutive with 'a'-'f' letters. + int kDelta = 'a' - '9' - 1; + // What to subtract to digits and letters to get them back to the range 0..15. + int kStart = '0' + kDelta; + res -= kStart; + res += kDelta * is_digit; + return res; +} + + +class PreScannerStackGuard { + public: + explicit PreScannerStackGuard(int max_size) + : limit_(StackPoint().at() - max_size) { } + bool has_overflowed() { + return StackPoint().at() < limit_; + } + private: + class StackPoint { + public: + char* at() { return reinterpret_cast<char*>(this); } + }; + char* limit_; +}; + + +// Scanner for preparsing. +// InputStream is a source of UC16 characters with limited push-back. +// LiteralsBuffer is a collector of (UTF-8) characters used to capture literals. +template <typename InputStream, typename LiteralsBuffer> +class Scanner { + public: + enum LiteralType { + kLiteralNumber, + kLiteralIdentifier, + kLiteralString, + kLiteralRegExp, + kLiteralRegExpFlags + }; + + class LiteralScope { + public: + explicit LiteralScope(Scanner* self, LiteralType type); + ~LiteralScope(); + void Complete(); + + private: + Scanner* scanner_; + bool complete_; + }; + + Scanner(); + + void Initialize(InputStream* stream); + + // Returns the next token. + i::Token::Value Next(); + + // Returns the current token again. + i::Token::Value current_token() { return current_.token; } + + // One token look-ahead (past the token returned by Next()). + i::Token::Value peek() const { return next_.token; } + + // Returns true if there was a line terminator before the peek'ed token. + bool has_line_terminator_before_next() const { + return has_line_terminator_before_next_; + } + + struct Location { + Location(int b, int e) : beg_pos(b), end_pos(e) { } + Location() : beg_pos(0), end_pos(0) { } + int beg_pos; + int end_pos; + }; + + // Returns the location information for the current token + // (the token returned by Next()). + Location location() const { return current_.location; } + // Returns the location information for the look-ahead token + // (the token returned by peek()). + Location peek_location() const { return next_.location; } + + // Returns the literal string, if any, for the current token (the + // token returned by Next()). The string is 0-terminated and in + // UTF-8 format; they may contain 0-characters. Literal strings are + // collected for identifiers, strings, and numbers. + // These functions only give the correct result if the literal + // was scanned between calls to StartLiteral() and TerminateLiteral(). + const char* literal_string() const { + return current_.literal_chars; + } + + int literal_length() const { + // Excluding terminal '\x00' added by TerminateLiteral(). + return current_.literal_length - 1; + } + + i::Vector<const char> literal() const { + return i::Vector<const char>(literal_string(), literal_length()); + } + + // Returns the literal string for the next token (the token that + // would be returned if Next() were called). + const char* next_literal_string() const { + return next_.literal_chars; + } + + + // Returns the length of the next token (that would be returned if + // Next() were called). + int next_literal_length() const { + // Excluding terminal '\x00' added by TerminateLiteral(). + return next_.literal_length - 1; + } + + i::Vector<const char> next_literal() const { + return i::Vector<const char>(next_literal_string(), next_literal_length()); + } + + // Scans the input as a regular expression pattern, previous + // character(s) must be /(=). Returns true if a pattern is scanned. + bool ScanRegExpPattern(bool seen_equal); + // Returns true if regexp flags are scanned (always since flags can + // be empty). + bool ScanRegExpFlags(); + + // Seek forward to the given position. This operation does not + // work in general, for instance when there are pushed back + // characters, but works for seeking forward until simple delimiter + // tokens, which is what it is used for. + void SeekForward(int pos); + + bool stack_overflow() { return stack_overflow_; } + + static const int kCharacterLookaheadBufferSize = 1; + static const int kNoEndPosition = 1; + + private: + // The current and look-ahead token. + struct TokenDesc { + i::Token::Value token; + Location location; + const char* literal_chars; + int literal_length; + }; + + // Default stack limit is 128K pointers. + static const int kMaxStackSize = 128 * 1024 * sizeof(void*); // NOLINT. + + void Init(unibrow::CharacterStream* stream); + + // Literal buffer support + inline void StartLiteral(LiteralType type); + inline void AddLiteralChar(uc32 ch); + inline void AddLiteralCharAdvance(); + inline void TerminateLiteral(); + // Stops scanning of a literal, e.g., due to an encountered error. + inline void DropLiteral(); + + // Low-level scanning support. + void Advance() { c0_ = source_->Advance(); } + void PushBack(uc32 ch) { + source_->PushBack(ch); + c0_ = ch; + } + + bool SkipWhiteSpace(); + + i::Token::Value SkipSingleLineComment(); + i::Token::Value SkipMultiLineComment(); + + inline i::Token::Value Select(i::Token::Value tok); + inline i::Token::Value Select(uc32 next, + i::Token::Value then, + i::Token::Value else_); + + // Scans a single JavaScript token. + void Scan(); + + void ScanDecimalDigits(); + i::Token::Value ScanNumber(bool seen_period); + i::Token::Value ScanIdentifier(); + uc32 ScanHexEscape(uc32 c, int length); + uc32 ScanOctalEscape(uc32 c, int length); + void ScanEscape(); + i::Token::Value ScanString(); + + // Scans a possible HTML comment -- begins with '<!'. + i::Token::Value ScanHtmlComment(); + + // Return the current source position. + int source_pos() { + return source_->pos() - kCharacterLookaheadBufferSize; + } + + // Decodes a unicode escape-sequence which is part of an identifier. + // If the escape sequence cannot be decoded the result is kBadRune. + uc32 ScanIdentifierUnicodeEscape(); + + PreScannerStackGuard stack_guard_; + + TokenDesc current_; // desc for current token (as returned by Next()) + TokenDesc next_; // desc for next token (one token look-ahead) + bool has_line_terminator_before_next_; + + // Source. + InputStream* source_; + + // Buffer to hold literal values (identifiers, strings, numerals, regexps and + // regexp flags) using '\x00'-terminated UTF-8 encoding. + // Handles allocation internally. + // Notice that the '\x00' termination is meaningless for strings and regexps + // which may contain the zero-character, but can be used as terminator for + // identifiers, numerals and regexp flags. + LiteralsBuffer literal_buffer_; + + bool stack_overflow_; + + // One Unicode character look-ahead; c0_ < 0 at the end of the input. + uc32 c0_; +}; + + +// ---------------------------------------------------------------------------- +// Scanner::LiteralScope + +template <typename InputStream, typename LiteralsBuffer> +Scanner<InputStream, LiteralsBuffer>::LiteralScope::LiteralScope( + Scanner* self, LiteralType type) + : scanner_(self), complete_(false) { + self->StartLiteral(type); +} + + +template <typename InputStream, typename LiteralsBuffer> +Scanner<InputStream, LiteralsBuffer>::LiteralScope::~LiteralScope() { + if (!complete_) scanner_->DropLiteral(); +} + +template <typename InputStream, typename LiteralsBuffer> +void Scanner<InputStream, LiteralsBuffer>::LiteralScope::Complete() { + scanner_->TerminateLiteral(); + complete_ = true; +} + + +// ---------------------------------------------------------------------------- +// Scanner. +template <typename InputStream, typename LiteralsBuffer> +Scanner<InputStream, LiteralsBuffer>::Scanner() + : stack_guard_(kMaxStackSize), + has_line_terminator_before_next_(false), + source_(NULL), + stack_overflow_(false) {} + + +template <typename InputStream, typename LiteralsBuffer> +void Scanner<InputStream, LiteralsBuffer>::Initialize(InputStream* stream) { + source_ = stream; + + // Initialize current_ to not refer to a literal. + current_.literal_length = 0; + // Reset literal buffer. + literal_buffer_.Reset(); + + // Set c0_ (one character ahead) + ASSERT(kCharacterLookaheadBufferSize == 1); + Advance(); + + // Skip initial whitespace allowing HTML comment ends just like + // after a newline and scan first token. + has_line_terminator_before_next_ = true; + SkipWhiteSpace(); + Scan(); +} + + +template <typename InputStream, typename LiteralsBuffer> +i::Token::Value Scanner<InputStream, LiteralsBuffer>::Next() { + // BUG 1215673: Find a thread safe way to set a stack limit in + // pre-parse mode. Otherwise, we cannot safely pre-parse from other + // threads. + current_ = next_; + // Check for stack-overflow before returning any tokens. + if (stack_guard_.has_overflowed()) { + stack_overflow_ = true; + next_.token = i::Token::ILLEGAL; + } else { + has_line_terminator_before_next_ = false; + Scan(); + } + return current_.token; +} + + +template <typename InputStream, typename LiteralsBuffer> +void Scanner<InputStream, LiteralsBuffer>::StartLiteral(LiteralType type) { + // Only record string and literal identifiers when preparsing. + // Those are the ones that are recorded as symbols. Numbers and + // regexps are not recorded. + if (type == kLiteralString || type == kLiteralIdentifier) { + literal_buffer_.StartLiteral(); + } +} + + +template <typename InputStream, typename LiteralsBuffer> +void Scanner<InputStream, LiteralsBuffer>::AddLiteralChar(uc32 c) { + literal_buffer_.AddChar(c); +} + + +template <typename InputStream, typename LiteralsBuffer> +void Scanner<InputStream, LiteralsBuffer>::TerminateLiteral() { + i::Vector<const char> chars = literal_buffer_.EndLiteral(); + next_.literal_chars = chars.start(); + next_.literal_length = chars.length(); +} + + +template <typename InputStream, typename LiteralsBuffer> +void Scanner<InputStream, LiteralsBuffer>::DropLiteral() { + literal_buffer_.DropLiteral(); +} + + +template <typename InputStream, typename LiteralsBuffer> +void Scanner<InputStream, LiteralsBuffer>::AddLiteralCharAdvance() { + AddLiteralChar(c0_); + Advance(); +} + + +static inline bool IsByteOrderMark(uc32 c) { + // The Unicode value U+FFFE is guaranteed never to be assigned as a + // Unicode character; this implies that in a Unicode context the + // 0xFF, 0xFE byte pattern can only be interpreted as the U+FEFF + // character expressed in little-endian byte order (since it could + // not be a U+FFFE character expressed in big-endian byte + // order). Nevertheless, we check for it to be compatible with + // Spidermonkey. + return c == 0xFEFF || c == 0xFFFE; +} + + +template <typename InputStream, typename LiteralsBuffer> +bool Scanner<InputStream, LiteralsBuffer>::SkipWhiteSpace() { + int start_position = source_pos(); + + while (true) { + // We treat byte-order marks (BOMs) as whitespace for better + // compatibility with Spidermonkey and other JavaScript engines. + while (i::ScannerConstants::kIsWhiteSpace.get(c0_) + || IsByteOrderMark(c0_)) { + // IsWhiteSpace() includes line terminators! + if (i::ScannerConstants::kIsLineTerminator.get(c0_)) { + // Ignore line terminators, but remember them. This is necessary + // for automatic semicolon insertion. + has_line_terminator_before_next_ = true; + } + Advance(); + } + + // If there is an HTML comment end '-->' at the beginning of a + // line (with only whitespace in front of it), we treat the rest + // of the line as a comment. This is in line with the way + // SpiderMonkey handles it. + if (c0_ == '-' && has_line_terminator_before_next_) { + Advance(); + if (c0_ == '-') { + Advance(); + if (c0_ == '>') { + // Treat the rest of the line as a comment. + SkipSingleLineComment(); + // Continue skipping white space after the comment. + continue; + } + PushBack('-'); // undo Advance() + } + PushBack('-'); // undo Advance() + } + // Return whether or not we skipped any characters. + return source_pos() != start_position; + } +} + + +template <typename InputStream, typename LiteralsBuffer> +i::Token::Value Scanner<InputStream, LiteralsBuffer>::SkipSingleLineComment() { + Advance(); + + // The line terminator at the end of the line is not considered + // to be part of the single-line comment; it is recognized + // separately by the lexical grammar and becomes part of the + // stream of input elements for the syntactic grammar (see + // ECMA-262, section 7.4, page 12). + while (c0_ >= 0 && !i::ScannerConstants::kIsLineTerminator.get(c0_)) { + Advance(); + } + + return i::Token::WHITESPACE; +} + + +template <typename InputStream, typename LiteralsBuffer> +i::Token::Value Scanner<InputStream, LiteralsBuffer>::SkipMultiLineComment() { + ASSERT(c0_ == '*'); + Advance(); + + while (c0_ >= 0) { + char ch = c0_; + Advance(); + // If we have reached the end of the multi-line comment, we + // consume the '/' and insert a whitespace. This way all + // multi-line comments are treated as whitespace - even the ones + // containing line terminators. This contradicts ECMA-262, section + // 7.4, page 12, that says that multi-line comments containing + // line terminators should be treated as a line terminator, but it + // matches the behaviour of SpiderMonkey and KJS. + if (ch == '*' && c0_ == '/') { + c0_ = ' '; + return i::Token::WHITESPACE; + } + } + + // Unterminated multi-line comment. + return i::Token::ILLEGAL; +} + + +template <typename InputStream, typename LiteralsBuffer> +i::Token::Value Scanner<InputStream, LiteralsBuffer>::ScanHtmlComment() { + // Check for <!-- comments. + ASSERT(c0_ == '!'); + Advance(); + if (c0_ == '-') { + Advance(); + if (c0_ == '-') return SkipSingleLineComment(); + PushBack('-'); // undo Advance() + } + PushBack('!'); // undo Advance() + ASSERT(c0_ == '!'); + return i::Token::LT; +} + + +template <typename InputStream, typename LiteralsBuffer> +void Scanner<InputStream, LiteralsBuffer>::Scan() { + next_.literal_length = 0; + i::Token::Value token; + do { + // Remember the position of the next token + next_.location.beg_pos = source_pos(); + + switch (c0_) { + case ' ': + case '\t': + Advance(); + token = i::Token::WHITESPACE; + break; + + case '\n': + Advance(); + has_line_terminator_before_next_ = true; + token = i::Token::WHITESPACE; + break; + + case '"': case '\'': + token = ScanString(); + break; + + case '<': + // < <= << <<= <!-- + Advance(); + if (c0_ == '=') { + token = Select(i::Token::LTE); + } else if (c0_ == '<') { + token = Select('=', i::Token::ASSIGN_SHL, i::Token::SHL); + } else if (c0_ == '!') { + token = ScanHtmlComment(); + } else { + token = i::Token::LT; + } + break; + + case '>': + // > >= >> >>= >>> >>>= + Advance(); + if (c0_ == '=') { + token = Select(i::Token::GTE); + } else if (c0_ == '>') { + // >> >>= >>> >>>= + Advance(); + if (c0_ == '=') { + token = Select(i::Token::ASSIGN_SAR); + } else if (c0_ == '>') { + token = Select('=', i::Token::ASSIGN_SHR, i::Token::SHR); + } else { + token = i::Token::SAR; + } + } else { + token = i::Token::GT; + } + break; + + case '=': + // = == === + Advance(); + if (c0_ == '=') { + token = Select('=', i::Token::EQ_STRICT, i::Token::EQ); + } else { + token = i::Token::ASSIGN; + } + break; + + case '!': + // ! != !== + Advance(); + if (c0_ == '=') { + token = Select('=', i::Token::NE_STRICT, i::Token::NE); + } else { + token = i::Token::NOT; + } + break; + + case '+': + // + ++ += + Advance(); + if (c0_ == '+') { + token = Select(i::Token::INC); + } else if (c0_ == '=') { + token = Select(i::Token::ASSIGN_ADD); + } else { + token = i::Token::ADD; + } + break; + + case '-': + // - -- --> -= + Advance(); + if (c0_ == '-') { + Advance(); + if (c0_ == '>' && has_line_terminator_before_next_) { + // For compatibility with SpiderMonkey, we skip lines that + // start with an HTML comment end '-->'. + token = SkipSingleLineComment(); + } else { + token = i::Token::DEC; + } + } else if (c0_ == '=') { + token = Select(i::Token::ASSIGN_SUB); + } else { + token = i::Token::SUB; + } + break; + + case '*': + // * *= + token = Select('=', i::Token::ASSIGN_MUL, i::Token::MUL); + break; + + case '%': + // % %= + token = Select('=', i::Token::ASSIGN_MOD, i::Token::MOD); + break; + + case '/': + // / // /* /= + Advance(); + if (c0_ == '/') { + token = SkipSingleLineComment(); + } else if (c0_ == '*') { + token = SkipMultiLineComment(); + } else if (c0_ == '=') { + token = Select(i::Token::ASSIGN_DIV); + } else { + token = i::Token::DIV; + } + break; + + case '&': + // & && &= + Advance(); + if (c0_ == '&') { + token = Select(i::Token::AND); + } else if (c0_ == '=') { + token = Select(i::Token::ASSIGN_BIT_AND); + } else { + token = i::Token::BIT_AND; + } + break; + + case '|': + // | || |= + Advance(); + if (c0_ == '|') { + token = Select(i::Token::OR); + } else if (c0_ == '=') { + token = Select(i::Token::ASSIGN_BIT_OR); + } else { + token = i::Token::BIT_OR; + } + break; + + case '^': + // ^ ^= + token = Select('=', i::Token::ASSIGN_BIT_XOR, i::Token::BIT_XOR); + break; + + case '.': + // . Number + Advance(); + if (i::IsDecimalDigit(c0_)) { + token = ScanNumber(true); + } else { + token = i::Token::PERIOD; + } + break; + + case ':': + token = Select(i::Token::COLON); + break; + + case ';': + token = Select(i::Token::SEMICOLON); + break; + + case ',': + token = Select(i::Token::COMMA); + break; + + case '(': + token = Select(i::Token::LPAREN); + break; + + case ')': + token = Select(i::Token::RPAREN); + break; + + case '[': + token = Select(i::Token::LBRACK); + break; + + case ']': + token = Select(i::Token::RBRACK); + break; + + case '{': + token = Select(i::Token::LBRACE); + break; + + case '}': + token = Select(i::Token::RBRACE); + break; + + case '?': + token = Select(i::Token::CONDITIONAL); + break; + + case '~': + token = Select(i::Token::BIT_NOT); + break; + + default: + if (i::ScannerConstants::kIsIdentifierStart.get(c0_)) { + token = ScanIdentifier(); + } else if (i::IsDecimalDigit(c0_)) { + token = ScanNumber(false); + } else if (SkipWhiteSpace()) { + token = i::Token::WHITESPACE; + } else if (c0_ < 0) { + token = i::Token::EOS; + } else { + token = Select(i::Token::ILLEGAL); + } + break; + } + + // Continue scanning for tokens as long as we're just skipping + // whitespace. + } while (token == i::Token::WHITESPACE); + + next_.location.end_pos = source_pos(); + next_.token = token; +} + + +template <typename InputStream, typename LiteralsBuffer> +void Scanner<InputStream, LiteralsBuffer>::SeekForward(int pos) { + source_->SeekForward(pos - 1); + Advance(); + // This function is only called to seek to the location + // of the end of a function (at the "}" token). It doesn't matter + // whether there was a line terminator in the part we skip. + has_line_terminator_before_next_ = false; + Scan(); +} + + +template <typename InputStream, typename LiteralsBuffer> +uc32 Scanner<InputStream, LiteralsBuffer>::ScanHexEscape(uc32 c, int length) { + ASSERT(length <= 4); // prevent overflow + + uc32 digits[4]; + uc32 x = 0; + for (int i = 0; i < length; i++) { + digits[i] = c0_; + int d = HexValue(c0_); + if (d < 0) { + // According to ECMA-262, 3rd, 7.8.4, page 18, these hex escapes + // should be illegal, but other JS VMs just return the + // non-escaped version of the original character. + + // Push back digits read, except the last one (in c0_). + for (int j = i-1; j >= 0; j--) { + PushBack(digits[j]); + } + // Notice: No handling of error - treat it as "\u"->"u". + return c; + } + x = x * 16 + d; + Advance(); + } + + return x; +} + + +// Octal escapes of the forms '\0xx' and '\xxx' are not a part of +// ECMA-262. Other JS VMs support them. +template <typename InputStream, typename LiteralsBuffer> +uc32 Scanner<InputStream, LiteralsBuffer>::ScanOctalEscape( + uc32 c, int length) { + uc32 x = c - '0'; + for (int i = 0; i < length; i++) { + int d = c0_ - '0'; + if (d < 0 || d > 7) break; + int nx = x * 8 + d; + if (nx >= 256) break; + x = nx; + Advance(); + } + return x; +} + + +template <typename InputStream, typename LiteralsBuffer> +void Scanner<InputStream, LiteralsBuffer>::ScanEscape() { + uc32 c = c0_; + Advance(); + + // Skip escaped newlines. + if (i::ScannerConstants::kIsLineTerminator.get(c)) { + // Allow CR+LF newlines in multiline string literals. + if (i::IsCarriageReturn(c) && i::IsLineFeed(c0_)) Advance(); + // Allow LF+CR newlines in multiline string literals. + if (i::IsLineFeed(c) && i::IsCarriageReturn(c0_)) Advance(); + return; + } + + switch (c) { + case '\'': // fall through + case '"' : // fall through + case '\\': break; + case 'b' : c = '\b'; break; + case 'f' : c = '\f'; break; + case 'n' : c = '\n'; break; + case 'r' : c = '\r'; break; + case 't' : c = '\t'; break; + case 'u' : c = ScanHexEscape(c, 4); break; + case 'v' : c = '\v'; break; + case 'x' : c = ScanHexEscape(c, 2); break; + case '0' : // fall through + case '1' : // fall through + case '2' : // fall through + case '3' : // fall through + case '4' : // fall through + case '5' : // fall through + case '6' : // fall through + case '7' : c = ScanOctalEscape(c, 2); break; + } + + // According to ECMA-262, 3rd, 7.8.4 (p 18ff) these + // should be illegal, but they are commonly handled + // as non-escaped characters by JS VMs. + AddLiteralChar(c); +} + + +template <typename InputStream, typename LiteralsBuffer> +i::Token::Value Scanner<InputStream, LiteralsBuffer>::ScanString() { + uc32 quote = c0_; + Advance(); // consume quote + + LiteralScope literal(this, kLiteralString); + while (c0_ != quote && c0_ >= 0 + && !i::ScannerConstants::kIsLineTerminator.get(c0_)) { + uc32 c = c0_; + Advance(); + if (c == '\\') { + if (c0_ < 0) return i::Token::ILLEGAL; + ScanEscape(); + } else { + AddLiteralChar(c); + } + } + if (c0_ != quote) return i::Token::ILLEGAL; + literal.Complete(); + + Advance(); // consume quote + return i::Token::STRING; +} + + +template <typename InputStream, typename LiteralsBuffer> +i::Token::Value Scanner<InputStream, LiteralsBuffer>::Select( + i::Token::Value tok) { + Advance(); + return tok; +} + + +template <typename InputStream, typename LiteralsBuffer> +i::Token::Value Scanner<InputStream, LiteralsBuffer>::Select( + uc32 next, + i::Token::Value then, + i::Token::Value else_) { + Advance(); + if (c0_ == next) { + Advance(); + return then; + } else { + return else_; + } +} + + +// Returns true if any decimal digits were scanned, returns false otherwise. +template <typename InputStream, typename LiteralsBuffer> +void Scanner<InputStream, LiteralsBuffer>::ScanDecimalDigits() { + while (i::IsDecimalDigit(c0_)) + AddLiteralCharAdvance(); +} + + +template <typename InputStream, typename LiteralsBuffer> +i::Token::Value Scanner<InputStream, LiteralsBuffer>::ScanNumber( + bool seen_period) { + // c0_ is the first digit of the number or the fraction. + ASSERT(i::IsDecimalDigit(c0_)); + + enum { DECIMAL, HEX, OCTAL } kind = DECIMAL; + + LiteralScope literal(this, kLiteralNumber); + if (seen_period) { + // we have already seen a decimal point of the float + AddLiteralChar('.'); + ScanDecimalDigits(); // we know we have at least one digit + + } else { + // if the first character is '0' we must check for octals and hex + if (c0_ == '0') { + AddLiteralCharAdvance(); + + // either 0, 0exxx, 0Exxx, 0.xxx, an octal number, or a hex number + if (c0_ == 'x' || c0_ == 'X') { + // hex number + kind = HEX; + AddLiteralCharAdvance(); + if (!i::IsHexDigit(c0_)) { + // we must have at least one hex digit after 'x'/'X' + return i::Token::ILLEGAL; + } + while (i::IsHexDigit(c0_)) { + AddLiteralCharAdvance(); + } + } else if ('0' <= c0_ && c0_ <= '7') { + // (possible) octal number + kind = OCTAL; + while (true) { + if (c0_ == '8' || c0_ == '9') { + kind = DECIMAL; + break; + } + if (c0_ < '0' || '7' < c0_) break; + AddLiteralCharAdvance(); + } + } + } + + // Parse decimal digits and allow trailing fractional part. + if (kind == DECIMAL) { + ScanDecimalDigits(); // optional + if (c0_ == '.') { + AddLiteralCharAdvance(); + ScanDecimalDigits(); // optional + } + } + } + + // scan exponent, if any + if (c0_ == 'e' || c0_ == 'E') { + ASSERT(kind != HEX); // 'e'/'E' must be scanned as part of the hex number + if (kind == OCTAL) return i::Token::ILLEGAL; + // scan exponent + AddLiteralCharAdvance(); + if (c0_ == '+' || c0_ == '-') + AddLiteralCharAdvance(); + if (!i::IsDecimalDigit(c0_)) { + // we must have at least one decimal digit after 'e'/'E' + return i::Token::ILLEGAL; + } + ScanDecimalDigits(); + } + + // The source character immediately following a numeric literal must + // not be an identifier start or a decimal digit; see ECMA-262 + // section 7.8.3, page 17 (note that we read only one decimal digit + // if the value is 0). + if (i::IsDecimalDigit(c0_) + || i::ScannerConstants::kIsIdentifierStart.get(c0_)) + return i::Token::ILLEGAL; + + literal.Complete(); + + return i::Token::NUMBER; +} + + +template <typename InputStream, typename LiteralsBuffer> +uc32 Scanner<InputStream, LiteralsBuffer>::ScanIdentifierUnicodeEscape() { + Advance(); + if (c0_ != 'u') return unibrow::Utf8::kBadChar; + Advance(); + uc32 c = ScanHexEscape('u', 4); + // We do not allow a unicode escape sequence to start another + // unicode escape sequence. + if (c == '\\') return unibrow::Utf8::kBadChar; + return c; +} + + +template <typename InputStream, typename LiteralsBuffer> +i::Token::Value Scanner<InputStream, LiteralsBuffer>::ScanIdentifier() { + ASSERT(i::ScannerConstants::kIsIdentifierStart.get(c0_)); + + LiteralScope literal(this, kLiteralIdentifier); + i::KeywordMatcher keyword_match; + + // Scan identifier start character. + if (c0_ == '\\') { + uc32 c = ScanIdentifierUnicodeEscape(); + // Only allow legal identifier start characters. + if (!i::ScannerConstants::kIsIdentifierStart.get(c)) { + return i::Token::ILLEGAL; + } + AddLiteralChar(c); + keyword_match.Fail(); + } else { + AddLiteralChar(c0_); + keyword_match.AddChar(c0_); + Advance(); + } + + // Scan the rest of the identifier characters. + while (i::ScannerConstants::kIsIdentifierPart.get(c0_)) { + if (c0_ == '\\') { + uc32 c = ScanIdentifierUnicodeEscape(); + // Only allow legal identifier part characters. + if (!i::ScannerConstants::kIsIdentifierPart.get(c)) { + return i::Token::ILLEGAL; + } + AddLiteralChar(c); + keyword_match.Fail(); + } else { + AddLiteralChar(c0_); + keyword_match.AddChar(c0_); + Advance(); + } + } + literal.Complete(); + + return keyword_match.token(); +} + + +template <typename InputStream, typename LiteralsBuffer> +bool Scanner<InputStream, LiteralsBuffer>::ScanRegExpPattern(bool seen_equal) { + // Scan: ('/' | '/=') RegularExpressionBody '/' RegularExpressionFlags + bool in_character_class = false; + + // Previous token is either '/' or '/=', in the second case, the + // pattern starts at =. + next_.location.beg_pos = source_pos() - (seen_equal ? 2 : 1); + next_.location.end_pos = source_pos() - (seen_equal ? 1 : 0); + + // Scan regular expression body: According to ECMA-262, 3rd, 7.8.5, + // the scanner should pass uninterpreted bodies to the RegExp + // constructor. + LiteralScope literal(this, kLiteralRegExp); + if (seen_equal) + AddLiteralChar('='); + + while (c0_ != '/' || in_character_class) { + if (i::ScannerConstants::kIsLineTerminator.get(c0_) || c0_ < 0) { + return false; + } + if (c0_ == '\\') { // escaped character + AddLiteralCharAdvance(); + if (i::ScannerConstants::kIsLineTerminator.get(c0_) || c0_ < 0) { + return false; + } + AddLiteralCharAdvance(); + } else { // unescaped character + if (c0_ == '[') in_character_class = true; + if (c0_ == ']') in_character_class = false; + AddLiteralCharAdvance(); + } + } + Advance(); // consume '/' + + literal.Complete(); + + return true; +} + +template <typename InputStream, typename LiteralsBuffer> +bool Scanner<InputStream, LiteralsBuffer>::ScanRegExpFlags() { + // Scan regular expression flags. + LiteralScope literal(this, kLiteralRegExpFlags); + while (i::ScannerConstants::kIsIdentifierPart.get(c0_)) { + if (c0_ == '\\') { + uc32 c = ScanIdentifierUnicodeEscape(); + if (c != static_cast<uc32>(unibrow::Utf8::kBadChar)) { + // We allow any escaped character, unlike the restriction on + // IdentifierPart when it is used to build an IdentifierName. + AddLiteralChar(c); + continue; + } + } + AddLiteralCharAdvance(); + } + literal.Complete(); + + next_.location.end_pos = source_pos() - 1; + return true; +} + + +} } // namespace v8::preparser + +#endif // V8_PRESCANNER_H_ diff --git a/deps/v8/src/profile-generator.cc b/deps/v8/src/profile-generator.cc index a4d9a828dd..29f9ab4d35 100644 --- a/deps/v8/src/profile-generator.cc +++ b/deps/v8/src/profile-generator.cc @@ -1848,22 +1848,6 @@ HeapEntry* HeapSnapshotGenerator::GetEntry(Object* obj) { } -int HeapSnapshotGenerator::GetGlobalSecurityToken() { - return collection_->token_enumerator()->GetTokenId( - Top::context()->global()->global_context()->security_token()); -} - - -int HeapSnapshotGenerator::GetObjectSecurityToken(HeapObject* obj) { - if (obj->IsGlobalContext()) { - return collection_->token_enumerator()->GetTokenId( - Context::cast(obj)->security_token()); - } else { - return TokenEnumerator::kNoSecurityToken; - } -} - - class IndexedReferencesExtractor : public ObjectVisitor { public: IndexedReferencesExtractor(HeapSnapshotGenerator* generator, @@ -1893,19 +1877,11 @@ class IndexedReferencesExtractor : public ObjectVisitor { void HeapSnapshotGenerator::ExtractReferences(HeapObject* obj) { // We need to reference JS global objects from snapshot's root. - // We also need to only include global objects from the current - // security context. And we don't want to add the global proxy, - // as we don't have a special type for it. + // We use JSGlobalProxy because this is what embedder (e.g. browser) + // uses for the global object. if (obj->IsJSGlobalProxy()) { - int global_security_token = GetGlobalSecurityToken(); JSGlobalProxy* proxy = JSGlobalProxy::cast(obj); - int object_security_token = - collection_->token_enumerator()->GetTokenId( - Context::cast(proxy->context())->security_token()); - if (object_security_token == TokenEnumerator::kNoSecurityToken - || object_security_token == global_security_token) { - SetRootReference(proxy->map()->prototype()); - } + SetRootReference(proxy->map()->prototype()); return; } diff --git a/deps/v8/src/profile-generator.h b/deps/v8/src/profile-generator.h index 6f63f6a122..b691a056e8 100644 --- a/deps/v8/src/profile-generator.h +++ b/deps/v8/src/profile-generator.h @@ -948,8 +948,6 @@ class HeapSnapshotGenerator { private: HeapEntry* GetEntry(Object* obj); - int GetGlobalSecurityToken(); - int GetObjectSecurityToken(HeapObject* obj); void ExtractReferences(HeapObject* obj); void ExtractClosureReferences(JSObject* js_obj, HeapEntry* entry); void ExtractPropertyReferences(JSObject* js_obj, HeapEntry* entry); diff --git a/deps/v8/src/regexp.js b/deps/v8/src/regexp.js index 51f4b094d3..d01d04f2e3 100644 --- a/deps/v8/src/regexp.js +++ b/deps/v8/src/regexp.js @@ -71,9 +71,6 @@ function DoConstructRegExp(object, pattern, flags, isConstructorCall) { } } - if (!isConstructorCall) { - regExpCache.type = 'none'; - } %RegExpInitializeObject(object, pattern, global, ignoreCase, multiline); // Call internal function to compile the pattern. @@ -121,22 +118,6 @@ function DoRegExpExec(regexp, string, index) { } -function RegExpCache() { - this.type = 'none'; - this.regExp = 0; - this.subject = 0; - this.replaceString = 0; - this.answer = 0; - // answerSaved marks whether the contents of answer is valid for a cache - // hit in RegExpExec, StringMatch and StringSplit. - this.answerSaved = false; - this.splitLimit = 0; // Used only when type is "split". -} - - -var regExpCache = new RegExpCache(); - - function BuildResultFromMatchInfo(lastMatchInfo, s) { var numResults = NUMBER_OF_CAPTURES(lastMatchInfo) >> 1; var result = %_RegExpConstructResult(numResults, lastMatchInfo[CAPTURE0], s); @@ -178,32 +159,6 @@ function RegExpExec(string) { ['RegExp.prototype.exec', this]); } - var cache = regExpCache; - var saveAnswer = false; - - var lastIndex = this.lastIndex; - - // Since cache.subject is always a string, a matching input can not - // cause visible side-effects when converted to a string, so we can omit - // the conversion required by the specification. - // Likewise, the regexp.lastIndex and regexp.global properties are value - // properties that are not configurable, so reading them can also not cause - // any side effects (converting lastIndex to a number can, though). - if (%_ObjectEquals(cache.type, 'exec') && - %_ObjectEquals(0, lastIndex) && - %_IsRegExpEquivalent(cache.regExp, this) && - %_ObjectEquals(cache.subject, string)) { - if (cache.answerSaved) { - // The regexp.lastIndex value must be 0 for non-global RegExps, and for - // global RegExps we only cache negative results, which gives a lastIndex - // of zero as well. - this.lastIndex = 0; - return %_RegExpCloneResult(cache.answer); - } else { - saveAnswer = true; - } - } - if (%_ArgumentsLength() === 0) { var regExpInput = LAST_INPUT(lastMatchInfo); if (IS_UNDEFINED(regExpInput)) { @@ -217,11 +172,13 @@ function RegExpExec(string) { } else { s = ToString(string); } - var global = this.global; + var lastIndex = this.lastIndex; // Conversion is required by the ES5 specification (RegExp.prototype.exec // algorithm, step 5) even if the value is discarded for non-global RegExps. var i = TO_INTEGER(lastIndex); + + var global = this.global; if (global) { if (i < 0 || i > s.length) { this.lastIndex = 0; @@ -236,35 +193,16 @@ function RegExpExec(string) { var matchIndices = %_RegExpExec(this, s, i, lastMatchInfo); if (matchIndices === null) { - if (global) { - // Cache negative result only if initial lastIndex was zero. - this.lastIndex = 0; - if (lastIndex !== 0) return matchIndices; - } - cache.regExp = this; - cache.subject = s; // Always a string. - cache.answer = null; - cache.answerSaved = true; // Safe since no cloning is needed. - cache.type = 'exec'; - return matchIndices; // No match. + if (global) this.lastIndex = 0; + return null; } // Successful match. lastMatchInfoOverride = null; - var result = BuildResultFromMatchInfo(matchIndices, s); - if (global) { - // Don't cache positive results for global regexps. this.lastIndex = lastMatchInfo[CAPTURE1]; - } else { - cache.regExp = this; - cache.subject = s; - if (saveAnswer) cache.answer = %_RegExpCloneResult(result); - cache.answerSaved = saveAnswer; - cache.type = 'exec'; } - return result; - + return BuildResultFromMatchInfo(matchIndices, s); } @@ -289,80 +227,57 @@ function RegExpTest(string) { string = regExpInput; } - var lastIndex = this.lastIndex; - - var cache = regExpCache; - if (%_ObjectEquals(cache.type, 'test') && - %_IsRegExpEquivalent(cache.regExp, this) && - %_ObjectEquals(cache.subject, string) && - %_ObjectEquals(0, lastIndex)) { - // The regexp.lastIndex value must be 0 for non-global RegExps, and for - // global RegExps we only cache negative results, which gives a resulting - // lastIndex of zero as well. - if (global) this.lastIndex = 0; - return cache.answer; - } - var s; if (IS_STRING(string)) { s = string; } else { s = ToString(string); } - var length = s.length; + + var lastIndex = this.lastIndex; // Conversion is required by the ES5 specification (RegExp.prototype.exec // algorithm, step 5) even if the value is discarded for non-global RegExps. var i = TO_INTEGER(lastIndex); - if (global) { - if (i < 0 || i > length) { + + if (this.global) { + if (i < 0 || i > s.length) { this.lastIndex = 0; return false; } - } else { - i = 0; - } - - var global = this.global; - - // Remove irrelevant preceeding '.*' in a test regexp. The expression - // checks whether this.source starts with '.*' and that the third - // char is not a '?' - if (%_StringCharCodeAt(this.source, 0) == 46 && // '.' - %_StringCharCodeAt(this.source, 1) == 42 && // '*' - %_StringCharCodeAt(this.source, 2) != 63) { // '?' - if (!%_ObjectEquals(regexp_key, this)) { - regexp_key = this; - regexp_val = new $RegExp(this.source.substring(2, this.source.length), - (this.global ? 'g' : '') - + (this.ignoreCase ? 'i' : '') - + (this.multiline ? 'm' : '')); - } - if (!regexp_val.test(s)) return false; - } - - %_Log('regexp', 'regexp-exec,%0r,%1S,%2i', [this, s, lastIndex]); - // matchIndices is either null or the lastMatchInfo array. - var matchIndices = %_RegExpExec(this, s, i, lastMatchInfo); - - var result = (matchIndices !== null); - if (result) { - lastMatchInfoOverride = null; - } - if (global) { - if (result) { - this.lastIndex = lastMatchInfo[CAPTURE1]; - return true; - } else { + %_Log('regexp', 'regexp-exec,%0r,%1S,%2i', [this, s, lastIndex]); + // matchIndices is either null or the lastMatchInfo array. + var matchIndices = %_RegExpExec(this, s, i, lastMatchInfo); + if (matchIndices === null) { this.lastIndex = 0; - if (lastIndex !== 0) return false; + return false; } + lastMatchInfoOverride = null; + this.lastIndex = lastMatchInfo[CAPTURE1]; + return true; + } else { + // Non-global regexp. + // Remove irrelevant preceeding '.*' in a non-global test regexp. + // The expression checks whether this.source starts with '.*' and + // that the third char is not a '?'. + if (%_StringCharCodeAt(this.source, 0) == 46 && // '.' + %_StringCharCodeAt(this.source, 1) == 42 && // '*' + %_StringCharCodeAt(this.source, 2) != 63) { // '?' + if (!%_ObjectEquals(regexp_key, this)) { + regexp_key = this; + regexp_val = new $RegExp(this.source.substring(2, this.source.length), + (this.ignoreCase ? 'i' : '') + + (this.multiline ? 'm' : '')); + } + if (!regexp_val.test(s)) return false; + } + %_Log('regexp', 'regexp-exec,%0r,%1S,%2i', [this, s, lastIndex]); + // matchIndices is either null or the lastMatchInfo array. + var matchIndices = %_RegExpExec(this, s, 0, lastMatchInfo); + if (matchIndices === null) return false; + lastMatchInfoOverride = null; + return true; } - cache.type = 'test'; - cache.regExp = this; - cache.subject = s; - cache.answer = result; - return result; } @@ -510,7 +425,6 @@ function SetupRegExp() { return IS_UNDEFINED(regExpInput) ? "" : regExpInput; } function RegExpSetInput(string) { - regExpCache.type = 'none'; LAST_INPUT(lastMatchInfo) = ToString(string); }; diff --git a/deps/v8/src/runtime.cc b/deps/v8/src/runtime.cc index fc1a023229..5534db557c 100644 --- a/deps/v8/src/runtime.cc +++ b/deps/v8/src/runtime.cc @@ -1424,66 +1424,6 @@ static MaybeObject* Runtime_RegExpConstructResult(Arguments args) { } -static MaybeObject* Runtime_RegExpCloneResult(Arguments args) { - ASSERT(args.length() == 1); - Map* regexp_result_map; - { - AssertNoAllocation no_gc; - HandleScope handles; - regexp_result_map = Top::global_context()->regexp_result_map(); - } - if (!args[0]->IsJSArray()) return args[0]; - - JSArray* result = JSArray::cast(args[0]); - // Arguments to RegExpCloneResult should always be fresh RegExp exec call - // results (either a fresh JSRegExpResult or null). - // If the argument is not a JSRegExpResult, or isn't unmodified, just return - // the argument uncloned. - if (result->map() != regexp_result_map) return result; - - // Having the original JSRegExpResult map guarantees that we have - // fast elements and no properties except the two in-object properties. - ASSERT(result->HasFastElements()); - ASSERT(result->properties() == Heap::empty_fixed_array()); - ASSERT_EQ(2, regexp_result_map->inobject_properties()); - - Object* new_array_alloc; - { MaybeObject* maybe_new_array_alloc = - Heap::AllocateRaw(JSRegExpResult::kSize, NEW_SPACE, OLD_POINTER_SPACE); - if (!maybe_new_array_alloc->ToObject(&new_array_alloc)) { - return maybe_new_array_alloc; - } - } - - // Set HeapObject map to JSRegExpResult map. - reinterpret_cast<HeapObject*>(new_array_alloc)->set_map(regexp_result_map); - - JSArray* new_array = JSArray::cast(new_array_alloc); - - // Copy JSObject properties. - new_array->set_properties(result->properties()); // Empty FixedArray. - - // Copy JSObject elements as copy-on-write. - FixedArray* elements = FixedArray::cast(result->elements()); - if (elements != Heap::empty_fixed_array()) { - elements->set_map(Heap::fixed_cow_array_map()); - } - new_array->set_elements(elements); - - // Copy JSArray length. - new_array->set_length(result->length()); - - // Copy JSRegExpResult in-object property fields input and index. - new_array->FastPropertyAtPut(JSRegExpResult::kIndexIndex, - result->FastPropertyAt( - JSRegExpResult::kIndexIndex)); - new_array->FastPropertyAtPut(JSRegExpResult::kInputIndex, - result->FastPropertyAt( - JSRegExpResult::kInputIndex)); - return new_array; -} - - static MaybeObject* Runtime_RegExpInitializeObject(Arguments args) { AssertNoAllocation no_alloc; ASSERT(args.length() == 5); @@ -8891,12 +8831,6 @@ static MaybeObject* Runtime_DebugPrintScopes(Arguments args) { } -static MaybeObject* Runtime_GetCFrames(Arguments args) { - // See bug 906. - return Heap::undefined_value(); -} - - static MaybeObject* Runtime_GetThreadCount(Arguments args) { HandleScope scope; ASSERT(args.length() == 1); diff --git a/deps/v8/src/runtime.h b/deps/v8/src/runtime.h index 72a8037970..756099b413 100644 --- a/deps/v8/src/runtime.h +++ b/deps/v8/src/runtime.h @@ -162,7 +162,6 @@ namespace internal { F(RegExpExecMultiple, 4, 1) \ F(RegExpInitializeObject, 5, 1) \ F(RegExpConstructResult, 3, 1) \ - F(RegExpCloneResult, 1, 1) \ \ /* JSON */ \ F(ParseJson, 1, 1) \ @@ -325,7 +324,6 @@ namespace internal { F(GetScopeCount, 2, 1) \ F(GetScopeDetails, 3, 1) \ F(DebugPrintScopes, 0, 1) \ - F(GetCFrames, 1, 1) \ F(GetThreadCount, 1, 1) \ F(GetThreadDetails, 2, 1) \ F(SetDisableBreak, 1, 1) \ @@ -426,7 +424,7 @@ namespace internal { // ---------------------------------------------------------------------------- // INLINE_AND_RUNTIME_FUNCTION_LIST defines all inlined functions accessed // with a native call of the form %_name from within JS code that also have - // a corresponding runtime function, that is called for slow cases. +// a corresponding runtime function, that is called for slow cases. // Entries have the form F(name, number of arguments, number of return values). #define INLINE_RUNTIME_FUNCTION_LIST(F) \ F(IsConstructCall, 0, 1) \ @@ -438,7 +436,6 @@ namespace internal { F(StringCompare, 2, 1) \ F(RegExpExec, 4, 1) \ F(RegExpConstructResult, 3, 1) \ - F(RegExpCloneResult, 1, 1) \ F(GetFromCache, 2, 1) \ F(NumberToString, 1, 1) \ F(SwapElements, 3, 1) diff --git a/deps/v8/src/scanner-base.cc b/deps/v8/src/scanner-base.cc new file mode 100644 index 0000000000..6cde51787f --- /dev/null +++ b/deps/v8/src/scanner-base.cc @@ -0,0 +1,195 @@ +// Copyright 2010 the V8 project authors. All rights reserved. +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following +// disclaimer in the documentation and/or other materials provided +// with the distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +// Features shared by parsing and pre-parsing scanners. + +#include "../include/v8stdint.h" +#include "scanner-base.h" + +namespace v8 { +namespace internal { + +// ---------------------------------------------------------------------------- +// Character predicates + +unibrow::Predicate<IdentifierStart, 128> ScannerConstants::kIsIdentifierStart; +unibrow::Predicate<IdentifierPart, 128> ScannerConstants::kIsIdentifierPart; +unibrow::Predicate<unibrow::WhiteSpace, 128> ScannerConstants::kIsWhiteSpace; +unibrow::Predicate<unibrow::LineTerminator, 128> + ScannerConstants::kIsLineTerminator; + +StaticResource<ScannerConstants::Utf8Decoder> ScannerConstants::utf8_decoder_; + +// Compound predicates. + +bool ScannerConstants::IsIdentifier(unibrow::CharacterStream* buffer) { + // Checks whether the buffer contains an identifier (no escape). + if (!buffer->has_more()) return false; + if (!kIsIdentifierStart.get(buffer->GetNext())) { + return false; + } + while (buffer->has_more()) { + if (!kIsIdentifierPart.get(buffer->GetNext())) { + return false; + } + } + return true; +} + +// ---------------------------------------------------------------------------- +// Keyword Matcher + +KeywordMatcher::FirstState KeywordMatcher::first_states_[] = { + { "break", KEYWORD_PREFIX, Token::BREAK }, + { NULL, C, Token::ILLEGAL }, + { NULL, D, Token::ILLEGAL }, + { "else", KEYWORD_PREFIX, Token::ELSE }, + { NULL, F, Token::ILLEGAL }, + { NULL, UNMATCHABLE, Token::ILLEGAL }, + { NULL, UNMATCHABLE, Token::ILLEGAL }, + { NULL, I, Token::ILLEGAL }, + { NULL, UNMATCHABLE, Token::ILLEGAL }, + { NULL, UNMATCHABLE, Token::ILLEGAL }, + { NULL, UNMATCHABLE, Token::ILLEGAL }, + { NULL, UNMATCHABLE, Token::ILLEGAL }, + { NULL, N, Token::ILLEGAL }, + { NULL, UNMATCHABLE, Token::ILLEGAL }, + { NULL, UNMATCHABLE, Token::ILLEGAL }, + { NULL, UNMATCHABLE, Token::ILLEGAL }, + { "return", KEYWORD_PREFIX, Token::RETURN }, + { "switch", KEYWORD_PREFIX, Token::SWITCH }, + { NULL, T, Token::ILLEGAL }, + { NULL, UNMATCHABLE, Token::ILLEGAL }, + { NULL, V, Token::ILLEGAL }, + { NULL, W, Token::ILLEGAL } +}; + + +void KeywordMatcher::Step(unibrow::uchar input) { + switch (state_) { + case INITIAL: { + // matching the first character is the only state with significant fanout. + // Match only lower-case letters in range 'b'..'w'. + unsigned int offset = input - kFirstCharRangeMin; + if (offset < kFirstCharRangeLength) { + state_ = first_states_[offset].state; + if (state_ == KEYWORD_PREFIX) { + keyword_ = first_states_[offset].keyword; + counter_ = 1; + keyword_token_ = first_states_[offset].token; + } + return; + } + break; + } + case KEYWORD_PREFIX: + if (static_cast<unibrow::uchar>(keyword_[counter_]) == input) { + counter_++; + if (keyword_[counter_] == '\0') { + state_ = KEYWORD_MATCHED; + token_ = keyword_token_; + } + return; + } + break; + case KEYWORD_MATCHED: + token_ = Token::IDENTIFIER; + break; + case C: + if (MatchState(input, 'a', CA)) return; + if (MatchState(input, 'o', CO)) return; + break; + case CA: + if (MatchKeywordStart(input, "case", 2, Token::CASE)) return; + if (MatchKeywordStart(input, "catch", 2, Token::CATCH)) return; + break; + case CO: + if (MatchState(input, 'n', CON)) return; + break; + case CON: + if (MatchKeywordStart(input, "const", 3, Token::CONST)) return; + if (MatchKeywordStart(input, "continue", 3, Token::CONTINUE)) return; + break; + case D: + if (MatchState(input, 'e', DE)) return; + if (MatchKeyword(input, 'o', KEYWORD_MATCHED, Token::DO)) return; + break; + case DE: + if (MatchKeywordStart(input, "debugger", 2, Token::DEBUGGER)) return; + if (MatchKeywordStart(input, "default", 2, Token::DEFAULT)) return; + if (MatchKeywordStart(input, "delete", 2, Token::DELETE)) return; + break; + case F: + if (MatchKeywordStart(input, "false", 1, Token::FALSE_LITERAL)) return; + if (MatchKeywordStart(input, "finally", 1, Token::FINALLY)) return; + if (MatchKeywordStart(input, "for", 1, Token::FOR)) return; + if (MatchKeywordStart(input, "function", 1, Token::FUNCTION)) return; + break; + case I: + if (MatchKeyword(input, 'f', KEYWORD_MATCHED, Token::IF)) return; + if (MatchKeyword(input, 'n', IN, Token::IN)) return; + break; + case IN: + token_ = Token::IDENTIFIER; + if (MatchKeywordStart(input, "instanceof", 2, Token::INSTANCEOF)) { + return; + } + break; + case N: + if (MatchKeywordStart(input, "native", 1, Token::NATIVE)) return; + if (MatchKeywordStart(input, "new", 1, Token::NEW)) return; + if (MatchKeywordStart(input, "null", 1, Token::NULL_LITERAL)) return; + break; + case T: + if (MatchState(input, 'h', TH)) return; + if (MatchState(input, 'r', TR)) return; + if (MatchKeywordStart(input, "typeof", 1, Token::TYPEOF)) return; + break; + case TH: + if (MatchKeywordStart(input, "this", 2, Token::THIS)) return; + if (MatchKeywordStart(input, "throw", 2, Token::THROW)) return; + break; + case TR: + if (MatchKeywordStart(input, "true", 2, Token::TRUE_LITERAL)) return; + if (MatchKeyword(input, 'y', KEYWORD_MATCHED, Token::TRY)) return; + break; + case V: + if (MatchKeywordStart(input, "var", 1, Token::VAR)) return; + if (MatchKeywordStart(input, "void", 1, Token::VOID)) return; + break; + case W: + if (MatchKeywordStart(input, "while", 1, Token::WHILE)) return; + if (MatchKeywordStart(input, "with", 1, Token::WITH)) return; + break; + case UNMATCHABLE: + break; + } + // On fallthrough, it's a failure. + state_ = UNMATCHABLE; +} + +} } // namespace v8::internal diff --git a/deps/v8/src/scanner-base.h b/deps/v8/src/scanner-base.h new file mode 100644 index 0000000000..50f30305c4 --- /dev/null +++ b/deps/v8/src/scanner-base.h @@ -0,0 +1,206 @@ +// Copyright 2010 the V8 project authors. All rights reserved. +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following +// disclaimer in the documentation and/or other materials provided +// with the distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +// Features shared by parsing and pre-parsing scanners. + +#ifndef V8_SCANNER_BASE_H_ +#define V8_SCANNER_BASE_H_ + +#include "globals.h" +#include "checks.h" +#include "allocation.h" +#include "token.h" +#include "unicode-inl.h" +#include "char-predicates.h" +#include "utils.h" + +namespace v8 { +namespace internal { + +// Interface through which the scanner reads characters from the input source. +class UTF16Buffer { + public: + UTF16Buffer(); + virtual ~UTF16Buffer() {} + + virtual void PushBack(uc32 ch) = 0; + // Returns a value < 0 when the buffer end is reached. + virtual uc32 Advance() = 0; + virtual void SeekForward(int pos) = 0; + + int pos() const { return pos_; } + + protected: + int pos_; // Current position in the buffer. + int end_; // Position where scanning should stop (EOF). +}; + + +class ScannerConstants : AllStatic { + public: + typedef unibrow::Utf8InputBuffer<1024> Utf8Decoder; + + static StaticResource<Utf8Decoder>* utf8_decoder() { + return &utf8_decoder_; + } + + static unibrow::Predicate<IdentifierStart, 128> kIsIdentifierStart; + static unibrow::Predicate<IdentifierPart, 128> kIsIdentifierPart; + static unibrow::Predicate<unibrow::LineTerminator, 128> kIsLineTerminator; + static unibrow::Predicate<unibrow::WhiteSpace, 128> kIsWhiteSpace; + + static bool IsIdentifier(unibrow::CharacterStream* buffer); + + private: + static StaticResource<Utf8Decoder> utf8_decoder_; +}; + + +class KeywordMatcher { +// Incrementally recognize keywords. +// +// Recognized keywords: +// break case catch const* continue debugger* default delete do else +// finally false for function if in instanceof native* new null +// return switch this throw true try typeof var void while with +// +// *: Actually "future reserved keywords". These are the only ones we +// recognize, the remaining are allowed as identifiers. +// In ES5 strict mode, we should disallow all reserved keywords. + public: + KeywordMatcher() + : state_(INITIAL), + token_(Token::IDENTIFIER), + keyword_(NULL), + counter_(0), + keyword_token_(Token::ILLEGAL) {} + + Token::Value token() { return token_; } + + inline void AddChar(unibrow::uchar input) { + if (state_ != UNMATCHABLE) { + Step(input); + } + } + + void Fail() { + token_ = Token::IDENTIFIER; + state_ = UNMATCHABLE; + } + + private: + enum State { + UNMATCHABLE, + INITIAL, + KEYWORD_PREFIX, + KEYWORD_MATCHED, + C, + CA, + CO, + CON, + D, + DE, + F, + I, + IN, + N, + T, + TH, + TR, + V, + W + }; + + struct FirstState { + const char* keyword; + State state; + Token::Value token; + }; + + // Range of possible first characters of a keyword. + static const unsigned int kFirstCharRangeMin = 'b'; + static const unsigned int kFirstCharRangeMax = 'w'; + static const unsigned int kFirstCharRangeLength = + kFirstCharRangeMax - kFirstCharRangeMin + 1; + // State map for first keyword character range. + static FirstState first_states_[kFirstCharRangeLength]; + + // If input equals keyword's character at position, continue matching keyword + // from that position. + inline bool MatchKeywordStart(unibrow::uchar input, + const char* keyword, + int position, + Token::Value token_if_match) { + if (input == static_cast<unibrow::uchar>(keyword[position])) { + state_ = KEYWORD_PREFIX; + this->keyword_ = keyword; + this->counter_ = position + 1; + this->keyword_token_ = token_if_match; + return true; + } + return false; + } + + // If input equals match character, transition to new state and return true. + inline bool MatchState(unibrow::uchar input, char match, State new_state) { + if (input == static_cast<unibrow::uchar>(match)) { + state_ = new_state; + return true; + } + return false; + } + + inline bool MatchKeyword(unibrow::uchar input, + char match, + State new_state, + Token::Value keyword_token) { + if (input != static_cast<unibrow::uchar>(match)) { + return false; + } + state_ = new_state; + token_ = keyword_token; + return true; + } + + void Step(unibrow::uchar input); + + // Current state. + State state_; + // Token for currently added characters. + Token::Value token_; + + // Matching a specific keyword string (there is only one possible valid + // keyword with the current prefix). + const char* keyword_; + int counter_; + Token::Value keyword_token_; +}; + + +} } // namespace v8::internal + +#endif // V8_SCANNER_BASE_H_ diff --git a/deps/v8/src/scanner.cc b/deps/v8/src/scanner.cc index 79d63f1774..6b2fcb4c5e 100755 --- a/deps/v8/src/scanner.cc +++ b/deps/v8/src/scanner.cc @@ -30,27 +30,15 @@ #include "ast.h" #include "handles.h" #include "scanner.h" +#include "unicode-inl.h" namespace v8 { namespace internal { // ---------------------------------------------------------------------------- -// Character predicates - - -unibrow::Predicate<IdentifierStart, 128> Scanner::kIsIdentifierStart; -unibrow::Predicate<IdentifierPart, 128> Scanner::kIsIdentifierPart; -unibrow::Predicate<unibrow::LineTerminator, 128> Scanner::kIsLineTerminator; -unibrow::Predicate<unibrow::WhiteSpace, 128> Scanner::kIsWhiteSpace; - - -StaticResource<Scanner::Utf8Decoder> Scanner::utf8_decoder_; - - -// ---------------------------------------------------------------------------- // UTF8Buffer -UTF8Buffer::UTF8Buffer() : buffer_(kInitialCapacity) { } +UTF8Buffer::UTF8Buffer() : buffer_(kInitialCapacity), recording_(false) { } UTF8Buffer::~UTF8Buffer() {} @@ -135,191 +123,6 @@ void CharacterStreamUTF16Buffer::SeekForward(int pos) { } -// ExternalStringUTF16Buffer -template <typename StringType, typename CharType> -ExternalStringUTF16Buffer<StringType, CharType>::ExternalStringUTF16Buffer() - : raw_data_(NULL) { } - - -template <typename StringType, typename CharType> -void ExternalStringUTF16Buffer<StringType, CharType>::Initialize( - Handle<StringType> data, - int start_position, - int end_position) { - ASSERT(!data.is_null()); - raw_data_ = data->resource()->data(); - - ASSERT(end_position <= data->length()); - if (start_position > 0) { - SeekForward(start_position); - } - end_ = - end_position != Scanner::kNoEndPosition ? end_position : data->length(); -} - - -template <typename StringType, typename CharType> -uc32 ExternalStringUTF16Buffer<StringType, CharType>::Advance() { - if (pos_ < end_) { - return raw_data_[pos_++]; - } else { - // note: currently the following increment is necessary to avoid a - // test-parser problem! - pos_++; - return static_cast<uc32>(-1); - } -} - - -template <typename StringType, typename CharType> -void ExternalStringUTF16Buffer<StringType, CharType>::PushBack(uc32 ch) { - pos_--; - ASSERT(pos_ >= Scanner::kCharacterLookaheadBufferSize); - ASSERT(raw_data_[pos_ - Scanner::kCharacterLookaheadBufferSize] == ch); -} - - -template <typename StringType, typename CharType> -void ExternalStringUTF16Buffer<StringType, CharType>::SeekForward(int pos) { - pos_ = pos; -} - - -// ---------------------------------------------------------------------------- -// Keyword Matcher - -KeywordMatcher::FirstState KeywordMatcher::first_states_[] = { - { "break", KEYWORD_PREFIX, Token::BREAK }, - { NULL, C, Token::ILLEGAL }, - { NULL, D, Token::ILLEGAL }, - { "else", KEYWORD_PREFIX, Token::ELSE }, - { NULL, F, Token::ILLEGAL }, - { NULL, UNMATCHABLE, Token::ILLEGAL }, - { NULL, UNMATCHABLE, Token::ILLEGAL }, - { NULL, I, Token::ILLEGAL }, - { NULL, UNMATCHABLE, Token::ILLEGAL }, - { NULL, UNMATCHABLE, Token::ILLEGAL }, - { NULL, UNMATCHABLE, Token::ILLEGAL }, - { NULL, UNMATCHABLE, Token::ILLEGAL }, - { NULL, N, Token::ILLEGAL }, - { NULL, UNMATCHABLE, Token::ILLEGAL }, - { NULL, UNMATCHABLE, Token::ILLEGAL }, - { NULL, UNMATCHABLE, Token::ILLEGAL }, - { "return", KEYWORD_PREFIX, Token::RETURN }, - { "switch", KEYWORD_PREFIX, Token::SWITCH }, - { NULL, T, Token::ILLEGAL }, - { NULL, UNMATCHABLE, Token::ILLEGAL }, - { NULL, V, Token::ILLEGAL }, - { NULL, W, Token::ILLEGAL } -}; - - -void KeywordMatcher::Step(uc32 input) { - switch (state_) { - case INITIAL: { - // matching the first character is the only state with significant fanout. - // Match only lower-case letters in range 'b'..'w'. - unsigned int offset = input - kFirstCharRangeMin; - if (offset < kFirstCharRangeLength) { - state_ = first_states_[offset].state; - if (state_ == KEYWORD_PREFIX) { - keyword_ = first_states_[offset].keyword; - counter_ = 1; - keyword_token_ = first_states_[offset].token; - } - return; - } - break; - } - case KEYWORD_PREFIX: - if (keyword_[counter_] == input) { - ASSERT_NE(input, '\0'); - counter_++; - if (keyword_[counter_] == '\0') { - state_ = KEYWORD_MATCHED; - token_ = keyword_token_; - } - return; - } - break; - case KEYWORD_MATCHED: - token_ = Token::IDENTIFIER; - break; - case C: - if (MatchState(input, 'a', CA)) return; - if (MatchState(input, 'o', CO)) return; - break; - case CA: - if (MatchKeywordStart(input, "case", 2, Token::CASE)) return; - if (MatchKeywordStart(input, "catch", 2, Token::CATCH)) return; - break; - case CO: - if (MatchState(input, 'n', CON)) return; - break; - case CON: - if (MatchKeywordStart(input, "const", 3, Token::CONST)) return; - if (MatchKeywordStart(input, "continue", 3, Token::CONTINUE)) return; - break; - case D: - if (MatchState(input, 'e', DE)) return; - if (MatchKeyword(input, 'o', KEYWORD_MATCHED, Token::DO)) return; - break; - case DE: - if (MatchKeywordStart(input, "debugger", 2, Token::DEBUGGER)) return; - if (MatchKeywordStart(input, "default", 2, Token::DEFAULT)) return; - if (MatchKeywordStart(input, "delete", 2, Token::DELETE)) return; - break; - case F: - if (MatchKeywordStart(input, "false", 1, Token::FALSE_LITERAL)) return; - if (MatchKeywordStart(input, "finally", 1, Token::FINALLY)) return; - if (MatchKeywordStart(input, "for", 1, Token::FOR)) return; - if (MatchKeywordStart(input, "function", 1, Token::FUNCTION)) return; - break; - case I: - if (MatchKeyword(input, 'f', KEYWORD_MATCHED, Token::IF)) return; - if (MatchKeyword(input, 'n', IN, Token::IN)) return; - break; - case IN: - token_ = Token::IDENTIFIER; - if (MatchKeywordStart(input, "instanceof", 2, Token::INSTANCEOF)) { - return; - } - break; - case N: - if (MatchKeywordStart(input, "native", 1, Token::NATIVE)) return; - if (MatchKeywordStart(input, "new", 1, Token::NEW)) return; - if (MatchKeywordStart(input, "null", 1, Token::NULL_LITERAL)) return; - break; - case T: - if (MatchState(input, 'h', TH)) return; - if (MatchState(input, 'r', TR)) return; - if (MatchKeywordStart(input, "typeof", 1, Token::TYPEOF)) return; - break; - case TH: - if (MatchKeywordStart(input, "this", 2, Token::THIS)) return; - if (MatchKeywordStart(input, "throw", 2, Token::THROW)) return; - break; - case TR: - if (MatchKeywordStart(input, "true", 2, Token::TRUE_LITERAL)) return; - if (MatchKeyword(input, 'y', KEYWORD_MATCHED, Token::TRY)) return; - break; - case V: - if (MatchKeywordStart(input, "var", 1, Token::VAR)) return; - if (MatchKeywordStart(input, "void", 1, Token::VOID)) return; - break; - case W: - if (MatchKeywordStart(input, "while", 1, Token::WHILE)) return; - if (MatchKeywordStart(input, "with", 1, Token::WITH)) return; - break; - default: - UNREACHABLE(); - } - // On fallthrough, it's a failure. - state_ = UNMATCHABLE; -} - - - // ---------------------------------------------------------------------------- // Scanner::LiteralScope @@ -445,7 +248,7 @@ void Scanner::StartLiteral() { } -void Scanner::AddChar(uc32 c) { +void Scanner::AddLiteralChar(uc32 c) { literal_buffer_.AddChar(c); } @@ -460,8 +263,8 @@ void Scanner::DropLiteral() { } -void Scanner::AddCharAdvance() { - AddChar(c0_); +void Scanner::AddLiteralCharAdvance() { + AddLiteralChar(c0_); Advance(); } @@ -494,9 +297,9 @@ bool Scanner::SkipJavaScriptWhiteSpace() { while (true) { // We treat byte-order marks (BOMs) as whitespace for better // compatibility with Spidermonkey and other JavaScript engines. - while (kIsWhiteSpace.get(c0_) || IsByteOrderMark(c0_)) { + while (ScannerConstants::kIsWhiteSpace.get(c0_) || IsByteOrderMark(c0_)) { // IsWhiteSpace() includes line terminators! - if (kIsLineTerminator.get(c0_)) { + if (ScannerConstants::kIsLineTerminator.get(c0_)) { // Ignore line terminators, but remember them. This is necessary // for automatic semicolon insertion. has_line_terminator_before_next_ = true; @@ -536,7 +339,7 @@ Token::Value Scanner::SkipSingleLineComment() { // separately by the lexical grammar and becomes part of the // stream of input elements for the syntactic grammar (see // ECMA-262, section 7.4, page 12). - while (c0_ >= 0 && !kIsLineTerminator.get(c0_)) { + while (c0_ >= 0 && !ScannerConstants::kIsLineTerminator.get(c0_)) { Advance(); } @@ -673,29 +476,29 @@ Token::Value Scanner::ScanJsonString() { // Check for control character (0x00-0x1f) or unterminated string (<0). if (c0_ < 0x20) return Token::ILLEGAL; if (c0_ != '\\') { - AddCharAdvance(); + AddLiteralCharAdvance(); } else { Advance(); switch (c0_) { case '"': case '\\': case '/': - AddChar(c0_); + AddLiteralChar(c0_); break; case 'b': - AddChar('\x08'); + AddLiteralChar('\x08'); break; case 'f': - AddChar('\x0c'); + AddLiteralChar('\x0c'); break; case 'n': - AddChar('\x0a'); + AddLiteralChar('\x0a'); break; case 'r': - AddChar('\x0d'); + AddLiteralChar('\x0d'); break; case 't': - AddChar('\x09'); + AddLiteralChar('\x09'); break; case 'u': { uc32 value = 0; @@ -707,7 +510,7 @@ Token::Value Scanner::ScanJsonString() { } value = value * 16 + digit; } - AddChar(value); + AddLiteralChar(value); break; } default: @@ -727,31 +530,31 @@ Token::Value Scanner::ScanJsonString() { Token::Value Scanner::ScanJsonNumber() { LiteralScope literal(this); - if (c0_ == '-') AddCharAdvance(); + if (c0_ == '-') AddLiteralCharAdvance(); if (c0_ == '0') { - AddCharAdvance(); + AddLiteralCharAdvance(); // Prefix zero is only allowed if it's the only digit before // a decimal point or exponent. if ('0' <= c0_ && c0_ <= '9') return Token::ILLEGAL; } else { if (c0_ < '1' || c0_ > '9') return Token::ILLEGAL; do { - AddCharAdvance(); + AddLiteralCharAdvance(); } while (c0_ >= '0' && c0_ <= '9'); } if (c0_ == '.') { - AddCharAdvance(); + AddLiteralCharAdvance(); if (c0_ < '0' || c0_ > '9') return Token::ILLEGAL; do { - AddCharAdvance(); + AddLiteralCharAdvance(); } while (c0_ >= '0' && c0_ <= '9'); } if (AsciiAlphaToLower(c0_) == 'e') { - AddCharAdvance(); - if (c0_ == '-' || c0_ == '+') AddCharAdvance(); + AddLiteralCharAdvance(); + if (c0_ == '-' || c0_ == '+') AddLiteralCharAdvance(); if (c0_ < '0' || c0_ > '9') return Token::ILLEGAL; do { - AddCharAdvance(); + AddLiteralCharAdvance(); } while (c0_ >= '0' && c0_ <= '9'); } literal.Complete(); @@ -767,7 +570,7 @@ Token::Value Scanner::ScanJsonIdentifier(const char* text, Advance(); text++; } - if (kIsIdentifierPart.get(c0_)) return Token::ILLEGAL; + if (ScannerConstants::kIsIdentifierPart.get(c0_)) return Token::ILLEGAL; literal.Complete(); return token; } @@ -990,7 +793,7 @@ void Scanner::ScanJavaScript() { break; default: - if (kIsIdentifierStart.get(c0_)) { + if (ScannerConstants::kIsIdentifierStart.get(c0_)) { token = ScanIdentifier(); } else if (IsDecimalDigit(c0_)) { token = ScanNumber(false); @@ -1073,7 +876,7 @@ void Scanner::ScanEscape() { Advance(); // Skip escaped newlines. - if (kIsLineTerminator.get(c)) { + if (ScannerConstants::kIsLineTerminator.get(c)) { // Allow CR+LF newlines in multiline string literals. if (IsCarriageReturn(c) && IsLineFeed(c0_)) Advance(); // Allow LF+CR newlines in multiline string literals. @@ -1106,7 +909,7 @@ void Scanner::ScanEscape() { // According to ECMA-262, 3rd, 7.8.4 (p 18ff) these // should be illegal, but they are commonly handled // as non-escaped characters by JS VMs. - AddChar(c); + AddLiteralChar(c); } @@ -1115,14 +918,15 @@ Token::Value Scanner::ScanString() { Advance(); // consume quote LiteralScope literal(this); - while (c0_ != quote && c0_ >= 0 && !kIsLineTerminator.get(c0_)) { + while (c0_ != quote && c0_ >= 0 + && !ScannerConstants::kIsLineTerminator.get(c0_)) { uc32 c = c0_; Advance(); if (c == '\\') { if (c0_ < 0) return Token::ILLEGAL; ScanEscape(); } else { - AddChar(c); + AddLiteralChar(c); } } if (c0_ != quote) return Token::ILLEGAL; @@ -1153,7 +957,7 @@ Token::Value Scanner::Select(uc32 next, Token::Value then, Token::Value else_) { // Returns true if any decimal digits were scanned, returns false otherwise. void Scanner::ScanDecimalDigits() { while (IsDecimalDigit(c0_)) - AddCharAdvance(); + AddLiteralCharAdvance(); } @@ -1165,25 +969,25 @@ Token::Value Scanner::ScanNumber(bool seen_period) { LiteralScope literal(this); if (seen_period) { // we have already seen a decimal point of the float - AddChar('.'); + AddLiteralChar('.'); ScanDecimalDigits(); // we know we have at least one digit } else { // if the first character is '0' we must check for octals and hex if (c0_ == '0') { - AddCharAdvance(); + AddLiteralCharAdvance(); // either 0, 0exxx, 0Exxx, 0.xxx, an octal number, or a hex number if (c0_ == 'x' || c0_ == 'X') { // hex number kind = HEX; - AddCharAdvance(); + AddLiteralCharAdvance(); if (!IsHexDigit(c0_)) { // we must have at least one hex digit after 'x'/'X' return Token::ILLEGAL; } while (IsHexDigit(c0_)) { - AddCharAdvance(); + AddLiteralCharAdvance(); } } else if ('0' <= c0_ && c0_ <= '7') { // (possible) octal number @@ -1194,7 +998,7 @@ Token::Value Scanner::ScanNumber(bool seen_period) { break; } if (c0_ < '0' || '7' < c0_) break; - AddCharAdvance(); + AddLiteralCharAdvance(); } } } @@ -1203,7 +1007,7 @@ Token::Value Scanner::ScanNumber(bool seen_period) { if (kind == DECIMAL) { ScanDecimalDigits(); // optional if (c0_ == '.') { - AddCharAdvance(); + AddLiteralCharAdvance(); ScanDecimalDigits(); // optional } } @@ -1214,9 +1018,9 @@ Token::Value Scanner::ScanNumber(bool seen_period) { ASSERT(kind != HEX); // 'e'/'E' must be scanned as part of the hex number if (kind == OCTAL) return Token::ILLEGAL; // no exponent for octals allowed // scan exponent - AddCharAdvance(); + AddLiteralCharAdvance(); if (c0_ == '+' || c0_ == '-') - AddCharAdvance(); + AddLiteralCharAdvance(); if (!IsDecimalDigit(c0_)) { // we must have at least one decimal digit after 'e'/'E' return Token::ILLEGAL; @@ -1228,7 +1032,7 @@ Token::Value Scanner::ScanNumber(bool seen_period) { // not be an identifier start or a decimal digit; see ECMA-262 // section 7.8.3, page 17 (note that we read only one decimal digit // if the value is 0). - if (IsDecimalDigit(c0_) || kIsIdentifierStart.get(c0_)) + if (IsDecimalDigit(c0_) || ScannerConstants::kIsIdentifierStart.get(c0_)) return Token::ILLEGAL; literal.Complete(); @@ -1250,7 +1054,7 @@ uc32 Scanner::ScanIdentifierUnicodeEscape() { Token::Value Scanner::ScanIdentifier() { - ASSERT(kIsIdentifierStart.get(c0_)); + ASSERT(ScannerConstants::kIsIdentifierStart.get(c0_)); LiteralScope literal(this); KeywordMatcher keyword_match; @@ -1259,25 +1063,25 @@ Token::Value Scanner::ScanIdentifier() { if (c0_ == '\\') { uc32 c = ScanIdentifierUnicodeEscape(); // Only allow legal identifier start characters. - if (!kIsIdentifierStart.get(c)) return Token::ILLEGAL; - AddChar(c); + if (!ScannerConstants::kIsIdentifierStart.get(c)) return Token::ILLEGAL; + AddLiteralChar(c); keyword_match.Fail(); } else { - AddChar(c0_); + AddLiteralChar(c0_); keyword_match.AddChar(c0_); Advance(); } // Scan the rest of the identifier characters. - while (kIsIdentifierPart.get(c0_)) { + while (ScannerConstants::kIsIdentifierPart.get(c0_)) { if (c0_ == '\\') { uc32 c = ScanIdentifierUnicodeEscape(); // Only allow legal identifier part characters. - if (!kIsIdentifierPart.get(c)) return Token::ILLEGAL; - AddChar(c); + if (!ScannerConstants::kIsIdentifierPart.get(c)) return Token::ILLEGAL; + AddLiteralChar(c); keyword_match.Fail(); } else { - AddChar(c0_); + AddLiteralChar(c0_); keyword_match.AddChar(c0_); Advance(); } @@ -1289,17 +1093,6 @@ Token::Value Scanner::ScanIdentifier() { -bool Scanner::IsIdentifier(unibrow::CharacterStream* buffer) { - // Checks whether the buffer contains an identifier (no escape). - if (!buffer->has_more()) return false; - if (!kIsIdentifierStart.get(buffer->GetNext())) return false; - while (buffer->has_more()) { - if (!kIsIdentifierPart.get(buffer->GetNext())) return false; - } - return true; -} - - bool Scanner::ScanRegExpPattern(bool seen_equal) { // Scan: ('/' | '/=') RegularExpressionBody '/' RegularExpressionFlags bool in_character_class = false; @@ -1314,18 +1107,18 @@ bool Scanner::ScanRegExpPattern(bool seen_equal) { // constructor. LiteralScope literal(this); if (seen_equal) - AddChar('='); + AddLiteralChar('='); while (c0_ != '/' || in_character_class) { - if (kIsLineTerminator.get(c0_) || c0_ < 0) return false; + if (ScannerConstants::kIsLineTerminator.get(c0_) || c0_ < 0) return false; if (c0_ == '\\') { // escaped character - AddCharAdvance(); - if (kIsLineTerminator.get(c0_) || c0_ < 0) return false; - AddCharAdvance(); + AddLiteralCharAdvance(); + if (ScannerConstants::kIsLineTerminator.get(c0_) || c0_ < 0) return false; + AddLiteralCharAdvance(); } else { // unescaped character if (c0_ == '[') in_character_class = true; if (c0_ == ']') in_character_class = false; - AddCharAdvance(); + AddLiteralCharAdvance(); } } Advance(); // consume '/' @@ -1338,17 +1131,17 @@ bool Scanner::ScanRegExpPattern(bool seen_equal) { bool Scanner::ScanRegExpFlags() { // Scan regular expression flags. LiteralScope literal(this); - while (kIsIdentifierPart.get(c0_)) { + while (ScannerConstants::kIsIdentifierPart.get(c0_)) { if (c0_ == '\\') { uc32 c = ScanIdentifierUnicodeEscape(); if (c != static_cast<uc32>(unibrow::Utf8::kBadChar)) { // We allow any escaped character, unlike the restriction on // IdentifierPart when it is used to build an IdentifierName. - AddChar(c); + AddLiteralChar(c); continue; } } - AddCharAdvance(); + AddLiteralCharAdvance(); } literal.Complete(); diff --git a/deps/v8/src/scanner.h b/deps/v8/src/scanner.h index dab3d67281..df5cd72949 100644 --- a/deps/v8/src/scanner.h +++ b/deps/v8/src/scanner.h @@ -30,6 +30,7 @@ #include "token.h" #include "char-predicates-inl.h" +#include "scanner-base.h" namespace v8 { namespace internal { @@ -41,25 +42,35 @@ class UTF8Buffer { ~UTF8Buffer(); inline void AddChar(uc32 c) { - if (static_cast<unsigned>(c) <= unibrow::Utf8::kMaxOneByteChar) { - buffer_.Add(static_cast<char>(c)); - } else { - AddCharSlow(c); + if (recording_) { + if (static_cast<unsigned>(c) <= unibrow::Utf8::kMaxOneByteChar) { + buffer_.Add(static_cast<char>(c)); + } else { + AddCharSlow(c); + } } } void StartLiteral() { buffer_.StartSequence(); + recording_ = true; } Vector<const char> EndLiteral() { - buffer_.Add(kEndMarker); - Vector<char> sequence = buffer_.EndSequence(); - return Vector<const char>(sequence.start(), sequence.length()); + if (recording_) { + recording_ = false; + buffer_.Add(kEndMarker); + Vector<char> sequence = buffer_.EndSequence(); + return Vector<const char>(sequence.start(), sequence.length()); + } + return Vector<const char>(); } void DropLiteral() { - buffer_.DropSequence(); + if (recording_) { + recording_ = false; + buffer_.DropSequence(); + } } void Reset() { @@ -78,30 +89,11 @@ class UTF8Buffer { private: static const int kInitialCapacity = 256; SequenceCollector<char, 4> buffer_; - + bool recording_; void AddCharSlow(uc32 c); }; -// Interface through which the scanner reads characters from the input source. -class UTF16Buffer { - public: - UTF16Buffer(); - virtual ~UTF16Buffer() {} - - virtual void PushBack(uc32 ch) = 0; - // Returns a value < 0 when the buffer end is reached. - virtual uc32 Advance() = 0; - virtual void SeekForward(int pos) = 0; - - int pos() const { return pos_; } - - protected: - int pos_; // Current position in the buffer. - int end_; // Position where scanning should stop (EOF). -}; - - // UTF16 buffer to read characters from a character stream. class CharacterStreamUTF16Buffer: public UTF16Buffer { public: @@ -142,127 +134,6 @@ class ExternalStringUTF16Buffer: public UTF16Buffer { }; -class KeywordMatcher { -// Incrementally recognize keywords. -// -// Recognized keywords: -// break case catch const* continue debugger* default delete do else -// finally false for function if in instanceof native* new null -// return switch this throw true try typeof var void while with -// -// *: Actually "future reserved keywords". These are the only ones we -// recognized, the remaining are allowed as identifiers. - public: - KeywordMatcher() - : state_(INITIAL), - token_(Token::IDENTIFIER), - keyword_(NULL), - counter_(0), - keyword_token_(Token::ILLEGAL) {} - - Token::Value token() { return token_; } - - inline void AddChar(uc32 input) { - if (state_ != UNMATCHABLE) { - Step(input); - } - } - - void Fail() { - token_ = Token::IDENTIFIER; - state_ = UNMATCHABLE; - } - - private: - enum State { - UNMATCHABLE, - INITIAL, - KEYWORD_PREFIX, - KEYWORD_MATCHED, - C, - CA, - CO, - CON, - D, - DE, - F, - I, - IN, - N, - T, - TH, - TR, - V, - W - }; - - struct FirstState { - const char* keyword; - State state; - Token::Value token; - }; - - // Range of possible first characters of a keyword. - static const unsigned int kFirstCharRangeMin = 'b'; - static const unsigned int kFirstCharRangeMax = 'w'; - static const unsigned int kFirstCharRangeLength = - kFirstCharRangeMax - kFirstCharRangeMin + 1; - // State map for first keyword character range. - static FirstState first_states_[kFirstCharRangeLength]; - - // If input equals keyword's character at position, continue matching keyword - // from that position. - inline bool MatchKeywordStart(uc32 input, - const char* keyword, - int position, - Token::Value token_if_match) { - if (input == keyword[position]) { - state_ = KEYWORD_PREFIX; - this->keyword_ = keyword; - this->counter_ = position + 1; - this->keyword_token_ = token_if_match; - return true; - } - return false; - } - - // If input equals match character, transition to new state and return true. - inline bool MatchState(uc32 input, char match, State new_state) { - if (input == match) { - state_ = new_state; - return true; - } - return false; - } - - inline bool MatchKeyword(uc32 input, - char match, - State new_state, - Token::Value keyword_token) { - if (input != match) { - return false; - } - state_ = new_state; - token_ = keyword_token; - return true; - } - - void Step(uc32 input); - - // Current state. - State state_; - // Token for currently added characters. - Token::Value token_; - - // Matching a specific keyword string (there is only one possible valid - // keyword with the current prefix). - const char* keyword_; - int counter_; - Token::Value keyword_token_; -}; - - -enum ParserMode { PARSE, PREPARSE }; enum ParserLanguage { JAVASCRIPT, JSON }; @@ -371,17 +242,10 @@ class Scanner { bool stack_overflow() { return stack_overflow_; } - static StaticResource<Utf8Decoder>* utf8_decoder() { return &utf8_decoder_; } - // Tells whether the buffer contains an identifier (no escapes). // Used for checking if a property name is an identifier. static bool IsIdentifier(unibrow::CharacterStream* buffer); - static unibrow::Predicate<IdentifierStart, 128> kIsIdentifierStart; - static unibrow::Predicate<IdentifierPart, 128> kIsIdentifierPart; - static unibrow::Predicate<unibrow::LineTerminator, 128> kIsLineTerminator; - static unibrow::Predicate<unibrow::WhiteSpace, 128> kIsWhiteSpace; - static const int kCharacterLookaheadBufferSize = 1; static const int kNoEndPosition = 1; @@ -400,8 +264,8 @@ class Scanner { // Literal buffer support inline void StartLiteral(); - inline void AddChar(uc32 ch); - inline void AddCharAdvance(); + inline void AddLiteralChar(uc32 ch); + inline void AddLiteralCharAdvance(); inline void TerminateLiteral(); // Stops scanning of a literal, e.g., due to an encountered error. inline void DropLiteral(); @@ -511,12 +375,61 @@ class Scanner { UTF8Buffer literal_buffer_; bool stack_overflow_; - static StaticResource<Utf8Decoder> utf8_decoder_; // One Unicode character look-ahead; c0_ < 0 at the end of the input. uc32 c0_; }; + +// ExternalStringUTF16Buffer +template <typename StringType, typename CharType> +ExternalStringUTF16Buffer<StringType, CharType>::ExternalStringUTF16Buffer() + : raw_data_(NULL) { } + + +template <typename StringType, typename CharType> +void ExternalStringUTF16Buffer<StringType, CharType>::Initialize( + Handle<StringType> data, + int start_position, + int end_position) { + ASSERT(!data.is_null()); + raw_data_ = data->resource()->data(); + + ASSERT(end_position <= data->length()); + if (start_position > 0) { + SeekForward(start_position); + } + end_ = + end_position != Scanner::kNoEndPosition ? end_position : data->length(); +} + + +template <typename StringType, typename CharType> +uc32 ExternalStringUTF16Buffer<StringType, CharType>::Advance() { + if (pos_ < end_) { + return raw_data_[pos_++]; + } else { + // note: currently the following increment is necessary to avoid a + // test-parser problem! + pos_++; + return static_cast<uc32>(-1); + } +} + + +template <typename StringType, typename CharType> +void ExternalStringUTF16Buffer<StringType, CharType>::PushBack(uc32 ch) { + pos_--; + ASSERT(pos_ >= Scanner::kCharacterLookaheadBufferSize); + ASSERT(raw_data_[pos_ - Scanner::kCharacterLookaheadBufferSize] == ch); +} + + +template <typename StringType, typename CharType> +void ExternalStringUTF16Buffer<StringType, CharType>::SeekForward(int pos) { + pos_ = pos; +} + } } // namespace v8::internal #endif // V8_SCANNER_H_ diff --git a/deps/v8/src/serialize.cc b/deps/v8/src/serialize.cc index 763c12f4f5..15fed442b9 100644 --- a/deps/v8/src/serialize.cc +++ b/deps/v8/src/serialize.cc @@ -290,10 +290,6 @@ void ExternalReferenceTable::PopulateTable() { Add(Top::get_address_from_id((Top::AddressId)i), TOP_ADDRESS, i, chars); } - // Extensions - Add(FUNCTION_ADDR(GCExtension::GC), EXTENSION, 1, - "GCExtension::GC"); - // Accessors #define ACCESSOR_DESCRIPTOR_DECLARATION(name) \ Add((Address)&Accessors::name, \ diff --git a/deps/v8/src/spaces.cc b/deps/v8/src/spaces.cc index e3fb923312..239c9cd6c9 100644 --- a/deps/v8/src/spaces.cc +++ b/deps/v8/src/spaces.cc @@ -270,8 +270,9 @@ void CodeRange::TearDown() { // ----------------------------------------------------------------------------- // MemoryAllocator // -intptr_t MemoryAllocator::capacity_ = 0; -intptr_t MemoryAllocator::size_ = 0; +intptr_t MemoryAllocator::capacity_ = 0; +intptr_t MemoryAllocator::capacity_executable_ = 0; +intptr_t MemoryAllocator::size_ = 0; intptr_t MemoryAllocator::size_executable_ = 0; List<MemoryAllocator::MemoryAllocationCallbackRegistration> @@ -302,8 +303,10 @@ int MemoryAllocator::Pop() { } -bool MemoryAllocator::Setup(intptr_t capacity) { +bool MemoryAllocator::Setup(intptr_t capacity, intptr_t capacity_executable) { capacity_ = RoundUp(capacity, Page::kPageSize); + capacity_executable_ = RoundUp(capacity_executable, Page::kPageSize); + ASSERT_GE(capacity_, capacity_executable_); // Over-estimate the size of chunks_ array. It assumes the expansion of old // space is always in the unit of a chunk (kChunkSize) except the last @@ -346,6 +349,7 @@ void MemoryAllocator::TearDown() { ASSERT(top_ == max_nof_chunks_); // all chunks are free top_ = 0; capacity_ = 0; + capacity_executable_ = 0; size_ = 0; max_nof_chunks_ = 0; } @@ -357,16 +361,31 @@ void* MemoryAllocator::AllocateRawMemory(const size_t requested, if (size_ + static_cast<size_t>(requested) > static_cast<size_t>(capacity_)) { return NULL; } + void* mem; - if (executable == EXECUTABLE && CodeRange::exists()) { - mem = CodeRange::AllocateRawMemory(requested, allocated); + if (executable == EXECUTABLE) { + // Check executable memory limit. + if (size_executable_ + requested > + static_cast<size_t>(capacity_executable_)) { + LOG(StringEvent("MemoryAllocator::AllocateRawMemory", + "V8 Executable Allocation capacity exceeded")); + return NULL; + } + // Allocate executable memory either from code range or from the + // OS. + if (CodeRange::exists()) { + mem = CodeRange::AllocateRawMemory(requested, allocated); + } else { + mem = OS::Allocate(requested, allocated, true); + } + // Update executable memory size. + size_executable_ += static_cast<int>(*allocated); } else { - mem = OS::Allocate(requested, allocated, (executable == EXECUTABLE)); + mem = OS::Allocate(requested, allocated, false); } int alloced = static_cast<int>(*allocated); size_ += alloced; - if (executable == EXECUTABLE) size_executable_ += alloced; #ifdef DEBUG ZapBlock(reinterpret_cast<Address>(mem), alloced); #endif @@ -391,6 +410,7 @@ void MemoryAllocator::FreeRawMemory(void* mem, if (executable == EXECUTABLE) size_executable_ -= static_cast<int>(length); ASSERT(size_ >= 0); + ASSERT(size_executable_ >= 0); } @@ -1878,6 +1898,18 @@ MaybeObject* OldSpaceFreeList::Allocate(int size_in_bytes, int* wasted_bytes) { } +void OldSpaceFreeList::MarkNodes() { + for (int i = 0; i < kFreeListsLength; i++) { + Address cur_addr = free_[i].head_node_; + while (cur_addr != NULL) { + FreeListNode* cur_node = FreeListNode::FromAddress(cur_addr); + cur_addr = cur_node->next(); + cur_node->SetMark(); + } + } +} + + #ifdef DEBUG bool OldSpaceFreeList::Contains(FreeListNode* node) { for (int i = 0; i < kFreeListsLength; i++) { @@ -1937,6 +1969,16 @@ MaybeObject* FixedSizeFreeList::Allocate() { } +void FixedSizeFreeList::MarkNodes() { + Address cur_addr = head_; + while (cur_addr != NULL && cur_addr != tail_) { + FreeListNode* cur_node = FreeListNode::FromAddress(cur_addr); + cur_addr = cur_node->next(); + cur_node->SetMark(); + } +} + + // ----------------------------------------------------------------------------- // OldSpace implementation @@ -2691,13 +2733,15 @@ LargeObjectSpace::LargeObjectSpace(AllocationSpace id) : Space(id, NOT_EXECUTABLE), // Managed on a per-allocation basis first_chunk_(NULL), size_(0), - page_count_(0) {} + page_count_(0), + objects_size_(0) {} bool LargeObjectSpace::Setup() { first_chunk_ = NULL; size_ = 0; page_count_ = 0; + objects_size_ = 0; return true; } @@ -2720,6 +2764,7 @@ void LargeObjectSpace::TearDown() { size_ = 0; page_count_ = 0; + objects_size_ = 0; } @@ -2766,6 +2811,7 @@ MaybeObject* LargeObjectSpace::AllocateRawInternal(int requested_size, } size_ += static_cast<int>(chunk_size); + objects_size_ += requested_size; page_count_++; chunk->set_next(first_chunk_); chunk->set_size(chunk_size); @@ -2928,6 +2974,7 @@ void LargeObjectSpace::FreeUnmarkedObjects() { // Free the chunk. MarkCompactCollector::ReportDeleteIfNeeded(object); size_ -= static_cast<int>(chunk_size); + objects_size_ -= object->Size(); page_count_--; ObjectSpace space = kObjectSpaceLoSpace; if (executable == EXECUTABLE) space = kObjectSpaceCodeSpace; @@ -3032,7 +3079,8 @@ void LargeObjectSpace::ReportStatistics() { CollectHistogramInfo(obj); } - PrintF(" number of objects %d\n", num_objects); + PrintF(" number of objects %d, " + "size of objects %" V8_PTR_PREFIX "d\n", num_objects, objects_size_); if (num_objects > 0) ReportHistogram(false); } diff --git a/deps/v8/src/spaces.h b/deps/v8/src/spaces.h index 3ed2fe8b98..60068c3d91 100644 --- a/deps/v8/src/spaces.h +++ b/deps/v8/src/spaces.h @@ -371,8 +371,13 @@ class Space : public Malloced { // Identity used in error reporting. AllocationSpace identity() { return id_; } + // Returns allocated size. virtual intptr_t Size() = 0; + // Returns size of objects. Can differ from the allocated size + // (e.g. see LargeObjectSpace). + virtual intptr_t SizeOfObjects() { return Size(); } + #ifdef ENABLE_HEAP_PROTECTION // Protect/unprotect the space by marking it read-only/writable. virtual void Protect() = 0; @@ -491,8 +496,8 @@ class CodeRange : public AllStatic { class MemoryAllocator : public AllStatic { public: // Initializes its internal bookkeeping structures. - // Max capacity of the total space. - static bool Setup(intptr_t max_capacity); + // Max capacity of the total space and executable memory limit. + static bool Setup(intptr_t max_capacity, intptr_t capacity_executable); // Deletes valid chunks. static void TearDown(); @@ -590,6 +595,12 @@ class MemoryAllocator : public AllStatic { // Returns allocated spaces in bytes. static intptr_t Size() { return size_; } + // Returns the maximum available executable bytes of heaps. + static intptr_t AvailableExecutable() { + if (capacity_executable_ < size_executable_) return 0; + return capacity_executable_ - size_executable_; + } + // Returns allocated executable spaces in bytes. static intptr_t SizeExecutable() { return size_executable_; } @@ -653,6 +664,8 @@ class MemoryAllocator : public AllStatic { private: // Maximum space size in bytes. static intptr_t capacity_; + // Maximum subset of capacity_ that can be executable + static intptr_t capacity_executable_; // Allocated space size in bytes. static intptr_t size_; @@ -1707,6 +1720,8 @@ class OldSpaceFreeList BASE_EMBEDDED { // 'wasted_bytes'. The size should be a non-zero multiple of the word size. MUST_USE_RESULT MaybeObject* Allocate(int size_in_bytes, int* wasted_bytes); + void MarkNodes(); + private: // The size range of blocks, in bytes. (Smaller allocations are allowed, but // will always result in waste.) @@ -1805,6 +1820,8 @@ class FixedSizeFreeList BASE_EMBEDDED { // A failure is returned if no block is available. MUST_USE_RESULT MaybeObject* Allocate(); + void MarkNodes(); + private: // Available bytes on the free list. intptr_t available_; @@ -1876,6 +1893,8 @@ class OldSpace : public PagedSpace { virtual void PutRestOfCurrentPageOnFreeList(Page* current_page); + void MarkFreeListNodes() { free_list_.MarkNodes(); } + #ifdef DEBUG // Reports statistics for the space void ReportStatistics(); @@ -1943,6 +1962,9 @@ class FixedSpace : public PagedSpace { virtual void DeallocateBlock(Address start, int size_in_bytes, bool add_to_freelist); + + void MarkFreeListNodes() { free_list_.MarkNodes(); } + #ifdef DEBUG // Reports statistic info of the space void ReportStatistics(); @@ -2183,6 +2205,10 @@ class LargeObjectSpace : public Space { return size_; } + virtual intptr_t SizeOfObjects() { + return objects_size_; + } + int PageCount() { return page_count_; } @@ -2234,7 +2260,7 @@ class LargeObjectSpace : public Space { LargeObjectChunk* first_chunk_; intptr_t size_; // allocated bytes int page_count_; // number of chunks - + intptr_t objects_size_; // size of objects // Shared implementation of AllocateRaw, AllocateRawCode and // AllocateRawFixedArray. diff --git a/deps/v8/src/string.js b/deps/v8/src/string.js index d97f632b88..d82ce05237 100644 --- a/deps/v8/src/string.js +++ b/deps/v8/src/string.js @@ -144,16 +144,6 @@ function StringLastIndexOf(searchString /* position */) { // length == 1 } -function CloneDenseArray(array) { - if (array === null) return null; - var clone = new $Array(array.length); - for (var i = 0; i < array.length; i++) { - clone[i] = array[i]; - } - return clone; -} - - // ECMA-262 section 15.5.4.9 // // This function is implementation specific. For now, we do not @@ -172,33 +162,12 @@ function StringMatch(regexp) { var subject = TO_STRING_INLINE(this); if (IS_REGEXP(regexp)) { if (!regexp.global) return regexp.exec(subject); - - var cache = regExpCache; - var saveAnswer = false; - - if (%_ObjectEquals(cache.type, 'match') && - %_IsRegExpEquivalent(cache.regExp, regexp) && - %_ObjectEquals(cache.subject, subject)) { - if (cache.answerSaved) { - return CloneDenseArray(cache.answer); - } else { - saveAnswer = true; - } - } %_Log('regexp', 'regexp-match,%0S,%1r', [subject, regexp]); // lastMatchInfo is defined in regexp.js. - var result = %StringMatch(subject, regexp, lastMatchInfo); - cache.type = 'match'; - cache.regExp = regexp; - cache.subject = subject; - if (saveAnswer) cache.answer = CloneDenseArray(result); - cache.answerSaved = saveAnswer; - return result; + return %StringMatch(subject, regexp, lastMatchInfo); } // Non-regexp argument. regexp = new $RegExp(regexp); - // Don't check regexp exec cache, since the regexp is new. - // TODO(lrn): Change this if we start caching regexps here. return RegExpExecNoTests(regexp, subject, 0); } @@ -231,7 +200,6 @@ function StringReplace(search, replace) { if (IS_REGEXP(search)) { %_Log('regexp', 'regexp-replace,%0r,%1S', [search, subject]); if (IS_FUNCTION(replace)) { - regExpCache.type = 'none'; if (search.global) { return StringReplaceGlobalRegExpWithFunction(subject, search, replace); } else { @@ -273,24 +241,10 @@ function StringReplace(search, replace) { // Helper function for regular expressions in String.prototype.replace. function StringReplaceRegExp(subject, regexp, replace) { - var cache = regExpCache; - if (%_ObjectEquals(cache.type, 'replace') && - %_IsRegExpEquivalent(cache.regExp, regexp) && - %_ObjectEquals(cache.replaceString, replace) && - %_ObjectEquals(cache.subject, subject)) { - return cache.answer; - } - replace = TO_STRING_INLINE(replace); - var answer = %StringReplaceRegExpWithString(subject, - regexp, - replace, - lastMatchInfo); - cache.subject = subject; - cache.regExp = regexp; - cache.replaceString = replace; - cache.answer = answer; - cache.type = 'replace'; - return answer; + return %StringReplaceRegExpWithString(subject, + regexp, + TO_STRING_INLINE(replace), + lastMatchInfo); } @@ -605,34 +559,12 @@ function StringSplit(separator, limit) { return result; } - var cache = regExpCache; - var saveAnswer = false; - - if (%_ObjectEquals(cache.type, 'split') && - %_IsRegExpEquivalent(cache.regExp, separator) && - %_ObjectEquals(cache.subject, subject) && - %_ObjectEquals(cache.splitLimit, limit)) { - if (cache.answerSaved) { - return CloneDenseArray(cache.answer); - } else { - saveAnswer = true; - } - } - - cache.type = 'split'; - cache.regExp = separator; - cache.subject = subject; - cache.splitLimit = limit; - %_Log('regexp', 'regexp-split,%0S,%1r', [subject, separator]); if (length === 0) { - cache.answerSaved = true; - if (splitMatch(separator, subject, 0, 0) != null) { - cache.answer = []; + if (DoRegExpExec(separator, subject, 0, 0) != null) { return []; } - cache.answer = [subject]; return [subject]; } @@ -680,8 +612,6 @@ function StringSplit(separator, limit) { startIndex = currentIndex = endIndex; } - if (saveAnswer) cache.answer = CloneDenseArray(result); - cache.answerSaved = saveAnswer; return result; } diff --git a/deps/v8/src/strtod.cc b/deps/v8/src/strtod.cc index 0ed1b0d914..cedbff91ea 100644 --- a/deps/v8/src/strtod.cc +++ b/deps/v8/src/strtod.cc @@ -31,6 +31,7 @@ #include "v8.h" #include "strtod.h" +#include "bignum.h" #include "cached-powers.h" #include "double.h" @@ -83,44 +84,12 @@ static const double exact_powers_of_ten[] = { // 10^22 = 0x21e19e0c9bab2400000 = 0x878678326eac9 * 2^22 10000000000000000000000.0 }; - static const int kExactPowersOfTenSize = ARRAY_SIZE(exact_powers_of_ten); - -extern "C" double gay_strtod(const char* s00, const char** se); - -static double old_strtod(Vector<const char> buffer, int exponent) { - // gay_strtod is broken on Linux,x86. For numbers with few decimal digits - // the computation is done using floating-point operations which (on Linux) - // are prone to double-rounding errors. - // By adding several zeroes to the buffer gay_strtod falls back to a slower - // (but correct) algorithm. - const int kInsertedZeroesCount = 20; - char gay_buffer[1024]; - Vector<char> gay_buffer_vector(gay_buffer, sizeof(gay_buffer)); - int pos = 0; - for (int i = 0; i < buffer.length(); ++i) { - gay_buffer_vector[pos++] = buffer[i]; - } - for (int i = 0; i < kInsertedZeroesCount; ++i) { - gay_buffer_vector[pos++] = '0'; - } - exponent -= kInsertedZeroesCount; - gay_buffer_vector[pos++] = 'e'; - if (exponent < 0) { - gay_buffer_vector[pos++] = '-'; - exponent = -exponent; - } - const int kNumberOfExponentDigits = 5; - for (int i = kNumberOfExponentDigits - 1; i >= 0; i--) { - gay_buffer_vector[pos + i] = exponent % 10 + '0'; - exponent /= 10; - } - pos += kNumberOfExponentDigits; - gay_buffer_vector[pos] = '\0'; - return gay_strtod(gay_buffer, NULL); -} - +// Maximum number of significant digits in the decimal representation. +// In fact the value is 772 (see conversions.cc), but to give us some margin +// we round up to 780. +static const int kMaxSignificantDecimalDigits = 780; static Vector<const char> TrimLeadingZeros(Vector<const char> buffer) { for (int i = 0; i < buffer.length(); i++) { @@ -142,6 +111,23 @@ static Vector<const char> TrimTrailingZeros(Vector<const char> buffer) { } +static void TrimToMaxSignificantDigits(Vector<const char> buffer, + int exponent, + char* significant_buffer, + int* significant_exponent) { + for (int i = 0; i < kMaxSignificantDecimalDigits - 1; ++i) { + significant_buffer[i] = buffer[i]; + } + // The input buffer has been trimmed. Therefore the last digit must be + // different from '0'. + ASSERT(buffer[buffer.length() - 1] != '0'); + // Set the last digit to be non-zero. This is sufficient to guarantee + // correct rounding. + significant_buffer[kMaxSignificantDecimalDigits - 1] = '1'; + *significant_exponent = + exponent + (buffer.length() - kMaxSignificantDecimalDigits); +} + // Reads digits from the buffer and converts them to a uint64. // Reads in as many digits as fit into a uint64. // When the string starts with "1844674407370955161" no further digit is read. @@ -374,20 +360,81 @@ static bool DiyFpStrtod(Vector<const char> buffer, } +// Returns the correct double for the buffer*10^exponent. +// The variable guess should be a close guess that is either the correct double +// or its lower neighbor (the nearest double less than the correct one). +// Preconditions: +// buffer.length() + exponent <= kMaxDecimalPower + 1 +// buffer.length() + exponent > kMinDecimalPower +// buffer.length() <= kMaxDecimalSignificantDigits +static double BignumStrtod(Vector<const char> buffer, + int exponent, + double guess) { + if (guess == V8_INFINITY) { + return guess; + } + + DiyFp upper_boundary = Double(guess).UpperBoundary(); + + ASSERT(buffer.length() + exponent <= kMaxDecimalPower + 1); + ASSERT(buffer.length() + exponent > kMinDecimalPower); + ASSERT(buffer.length() <= kMaxSignificantDecimalDigits); + // Make sure that the Bignum will be able to hold all our numbers. + // Our Bignum implementation has a separate field for exponents. Shifts will + // consume at most one bigit (< 64 bits). + // ln(10) == 3.3219... + ASSERT(((kMaxDecimalPower + 1) * 333 / 100) < Bignum::kMaxSignificantBits); + Bignum input; + Bignum boundary; + input.AssignDecimalString(buffer); + boundary.AssignUInt64(upper_boundary.f()); + if (exponent >= 0) { + input.MultiplyByPowerOfTen(exponent); + } else { + boundary.MultiplyByPowerOfTen(-exponent); + } + if (upper_boundary.e() > 0) { + boundary.ShiftLeft(upper_boundary.e()); + } else { + input.ShiftLeft(-upper_boundary.e()); + } + int comparison = Bignum::Compare(input, boundary); + if (comparison < 0) { + return guess; + } else if (comparison > 0) { + return Double(guess).NextDouble(); + } else if ((Double(guess).Significand() & 1) == 0) { + // Round towards even. + return guess; + } else { + return Double(guess).NextDouble(); + } +} + + double Strtod(Vector<const char> buffer, int exponent) { Vector<const char> left_trimmed = TrimLeadingZeros(buffer); Vector<const char> trimmed = TrimTrailingZeros(left_trimmed); exponent += left_trimmed.length() - trimmed.length(); if (trimmed.length() == 0) return 0.0; + if (trimmed.length() > kMaxSignificantDecimalDigits) { + char significant_buffer[kMaxSignificantDecimalDigits]; + int significant_exponent; + TrimToMaxSignificantDigits(trimmed, exponent, + significant_buffer, &significant_exponent); + return Strtod(Vector<const char>(significant_buffer, + kMaxSignificantDecimalDigits), + significant_exponent); + } if (exponent + trimmed.length() - 1 >= kMaxDecimalPower) return V8_INFINITY; if (exponent + trimmed.length() <= kMinDecimalPower) return 0.0; - double result; - if (DoubleStrtod(trimmed, exponent, &result) || - DiyFpStrtod(trimmed, exponent, &result)) { - return result; + double guess; + if (DoubleStrtod(trimmed, exponent, &guess) || + DiyFpStrtod(trimmed, exponent, &guess)) { + return guess; } - return old_strtod(trimmed, exponent); + return BignumStrtod(trimmed, exponent, guess); } } } // namespace v8::internal diff --git a/deps/v8/src/stub-cache.cc b/deps/v8/src/stub-cache.cc index e794f0932d..b7c769c7a2 100644 --- a/deps/v8/src/stub-cache.cc +++ b/deps/v8/src/stub-cache.cc @@ -820,6 +820,34 @@ MaybeObject* StubCache::ComputeCallInitialize(int argc, } +Handle<Code> StubCache::ComputeCallInitialize(int argc, InLoopFlag in_loop) { + if (in_loop == IN_LOOP) { + // Force the creation of the corresponding stub outside loops, + // because it may be used when clearing the ICs later - it is + // possible for a series of IC transitions to lose the in-loop + // information, and the IC clearing code can't generate a stub + // that it needs so we need to ensure it is generated already. + ComputeCallInitialize(argc, NOT_IN_LOOP); + } + CALL_HEAP_FUNCTION(ComputeCallInitialize(argc, in_loop, Code::CALL_IC), Code); +} + + +Handle<Code> StubCache::ComputeKeyedCallInitialize(int argc, + InLoopFlag in_loop) { + if (in_loop == IN_LOOP) { + // Force the creation of the corresponding stub outside loops, + // because it may be used when clearing the ICs later - it is + // possible for a series of IC transitions to lose the in-loop + // information, and the IC clearing code can't generate a stub + // that it needs so we need to ensure it is generated already. + ComputeKeyedCallInitialize(argc, NOT_IN_LOOP); + } + CALL_HEAP_FUNCTION( + ComputeCallInitialize(argc, in_loop, Code::KEYED_CALL_IC), Code); +} + + MaybeObject* StubCache::ComputeCallPreMonomorphic(int argc, InLoopFlag in_loop, Code::Kind kind) { diff --git a/deps/v8/src/stub-cache.h b/deps/v8/src/stub-cache.h index 9d947e404b..4886c7eb29 100644 --- a/deps/v8/src/stub-cache.h +++ b/deps/v8/src/stub-cache.h @@ -204,6 +204,10 @@ class StubCache : public AllStatic { InLoopFlag in_loop, Code::Kind kind); + static Handle<Code> ComputeCallInitialize(int argc, InLoopFlag in_loop); + + static Handle<Code> ComputeKeyedCallInitialize(int argc, InLoopFlag in_loop); + MUST_USE_RESULT static MaybeObject* ComputeCallPreMonomorphic( int argc, InLoopFlag in_loop, diff --git a/deps/v8/src/token.h b/deps/v8/src/token.h index ebc7fea1cd..74d9539f49 100644 --- a/deps/v8/src/token.h +++ b/deps/v8/src/token.h @@ -28,6 +28,8 @@ #ifndef V8_TOKEN_H_ #define V8_TOKEN_H_ +#include "checks.h" + namespace v8 { namespace internal { diff --git a/deps/v8/src/utils.cc b/deps/v8/src/utils.cc index 45a4cd60f8..7096ba35ab 100644 --- a/deps/v8/src/utils.cc +++ b/deps/v8/src/utils.cc @@ -37,34 +37,6 @@ namespace v8 { namespace internal { -// Implementation is from "Hacker's Delight" by Henry S. Warren, Jr., -// figure 3-3, page 48, where the function is called clp2. -uint32_t RoundUpToPowerOf2(uint32_t x) { - ASSERT(x <= 0x80000000u); - x = x - 1; - x = x | (x >> 1); - x = x | (x >> 2); - x = x | (x >> 4); - x = x | (x >> 8); - x = x | (x >> 16); - return x + 1; -} - - -// Thomas Wang, Integer Hash Functions. -// http://www.concentric.net/~Ttwang/tech/inthash.htm -uint32_t ComputeIntegerHash(uint32_t key) { - uint32_t hash = key; - hash = ~hash + (hash << 15); // hash = (hash << 15) - hash - 1; - hash = hash ^ (hash >> 12); - hash = hash + (hash << 2); - hash = hash ^ (hash >> 4); - hash = hash * 2057; // hash = (hash + (hash << 3)) + (hash << 11); - hash = hash ^ (hash >> 16); - return hash; -} - - void PrintF(const char* format, ...) { va_list arguments; va_start(arguments, format); @@ -274,12 +246,4 @@ char* StringBuilder::Finalize() { } -int TenToThe(int exponent) { - ASSERT(exponent <= 9); - ASSERT(exponent >= 1); - int answer = 10; - for (int i = 1; i < exponent; i++) answer *= 10; - return answer; -} - } } // namespace v8::internal diff --git a/deps/v8/src/utils.h b/deps/v8/src/utils.h index 0521767788..6270f8efaf 100644 --- a/deps/v8/src/utils.h +++ b/deps/v8/src/utils.h @@ -31,6 +31,10 @@ #include <stdlib.h> #include <string.h> +#include "globals.h" +#include "checks.h" +#include "allocation.h" + namespace v8 { namespace internal { @@ -142,7 +146,19 @@ static int PointerValueCompare(const T* a, const T* b) { // Returns the smallest power of two which is >= x. If you pass in a // number that is already a power of two, it is returned as is. -uint32_t RoundUpToPowerOf2(uint32_t x); +// Implementation is from "Hacker's Delight" by Henry S. Warren, Jr., +// figure 3-3, page 48, where the function is called clp2. +static inline uint32_t RoundUpToPowerOf2(uint32_t x) { + ASSERT(x <= 0x80000000u); + x = x - 1; + x = x | (x >> 1); + x = x | (x >> 2); + x = x | (x >> 4); + x = x | (x >> 8); + x = x | (x >> 16); + return x + 1; +} + template <typename T> @@ -216,65 +232,18 @@ class BitField { // ---------------------------------------------------------------------------- // Hash function. -uint32_t ComputeIntegerHash(uint32_t key); - - -// ---------------------------------------------------------------------------- -// I/O support. - -#if __GNUC__ >= 4 -// On gcc we can ask the compiler to check the types of %d-style format -// specifiers and their associated arguments. TODO(erikcorry) fix this -// so it works on MacOSX. -#if defined(__MACH__) && defined(__APPLE__) -#define PRINTF_CHECKING -#else // MacOsX. -#define PRINTF_CHECKING __attribute__ ((format (printf, 1, 2))) -#endif -#else -#define PRINTF_CHECKING -#endif - -// Our version of printf(). -void PRINTF_CHECKING PrintF(const char* format, ...); - -// Our version of fflush. -void Flush(); - - -// Read a line of characters after printing the prompt to stdout. The resulting -// char* needs to be disposed off with DeleteArray by the caller. -char* ReadLine(const char* prompt); - - -// Read and return the raw bytes in a file. the size of the buffer is returned -// in size. -// The returned buffer must be freed by the caller. -byte* ReadBytes(const char* filename, int* size, bool verbose = true); - - -// Write size chars from str to the file given by filename. -// The file is overwritten. Returns the number of chars written. -int WriteChars(const char* filename, - const char* str, - int size, - bool verbose = true); - - -// Write size bytes to the file given by filename. -// The file is overwritten. Returns the number of bytes written. -int WriteBytes(const char* filename, - const byte* bytes, - int size, - bool verbose = true); - - -// Write the C code -// const char* <varname> = "<str>"; -// const int <varname>_len = <len>; -// to the file given by filename. Only the first len chars are written. -int WriteAsCFile(const char* filename, const char* varname, - const char* str, int size, bool verbose = true); +// Thomas Wang, Integer Hash Functions. +// http://www.concentric.net/~Ttwang/tech/inthash.htm +static inline uint32_t ComputeIntegerHash(uint32_t key) { + uint32_t hash = key; + hash = ~hash + (hash << 15); // hash = (hash << 15) - hash - 1; + hash = hash ^ (hash >> 12); + hash = hash + (hash << 2); + hash = hash ^ (hash >> 4); + hash = hash * 2057; // hash = (hash + (hash << 3)) + (hash << 11); + hash = hash ^ (hash >> 16); + return hash; +} // ---------------------------------------------------------------------------- @@ -416,23 +385,6 @@ class Vector { }; -// A temporary assignment sets a (non-local) variable to a value on -// construction and resets it the value on destruction. -template <typename T> -class TempAssign { - public: - TempAssign(T* var, T value): var_(var), old_value_(*var) { - *var = value; - } - - ~TempAssign() { *var_ = old_value_; } - - private: - T* var_; - T old_value_; -}; - - template <typename T, int kSize> class EmbeddedVector : public Vector<T> { public: @@ -484,13 +436,6 @@ inline Vector<char> MutableCStrVector(char* data, int max) { return Vector<char>(data, (length < max) ? length : max); } -template <typename T> -inline Vector< Handle<Object> > HandleVector(v8::internal::Handle<T>* elms, - int length) { - return Vector< Handle<Object> >( - reinterpret_cast<v8::internal::Handle<Object>*>(elms), length); -} - /* * A class that collects values into a backing store. @@ -699,156 +644,6 @@ class SequenceCollector : public Collector<T, growth_factor, max_growth> { }; -// Simple support to read a file into a 0-terminated C-string. -// The returned buffer must be freed by the caller. -// On return, *exits tells whether the file existed. -Vector<const char> ReadFile(const char* filename, - bool* exists, - bool verbose = true); - - -// Simple wrapper that allows an ExternalString to refer to a -// Vector<const char>. Doesn't assume ownership of the data. -class AsciiStringAdapter: public v8::String::ExternalAsciiStringResource { - public: - explicit AsciiStringAdapter(Vector<const char> data) : data_(data) {} - - virtual const char* data() const { return data_.start(); } - - virtual size_t length() const { return data_.length(); } - - private: - Vector<const char> data_; -}; - - -// Helper class for building result strings in a character buffer. The -// purpose of the class is to use safe operations that checks the -// buffer bounds on all operations in debug mode. -class StringBuilder { - public: - // Create a string builder with a buffer of the given size. The - // buffer is allocated through NewArray<char> and must be - // deallocated by the caller of Finalize(). - explicit StringBuilder(int size); - - StringBuilder(char* buffer, int size) - : buffer_(buffer, size), position_(0) { } - - ~StringBuilder() { if (!is_finalized()) Finalize(); } - - int size() const { return buffer_.length(); } - - // Get the current position in the builder. - int position() const { - ASSERT(!is_finalized()); - return position_; - } - - // Reset the position. - void Reset() { position_ = 0; } - - // Add a single character to the builder. It is not allowed to add - // 0-characters; use the Finalize() method to terminate the string - // instead. - void AddCharacter(char c) { - ASSERT(c != '\0'); - ASSERT(!is_finalized() && position_ < buffer_.length()); - buffer_[position_++] = c; - } - - // Add an entire string to the builder. Uses strlen() internally to - // compute the length of the input string. - void AddString(const char* s); - - // Add the first 'n' characters of the given string 's' to the - // builder. The input string must have enough characters. - void AddSubstring(const char* s, int n); - - // Add formatted contents to the builder just like printf(). - void AddFormatted(const char* format, ...); - - // Add character padding to the builder. If count is non-positive, - // nothing is added to the builder. - void AddPadding(char c, int count); - - // Finalize the string by 0-terminating it and returning the buffer. - char* Finalize(); - - private: - Vector<char> buffer_; - int position_; - - bool is_finalized() const { return position_ < 0; } - - DISALLOW_IMPLICIT_CONSTRUCTORS(StringBuilder); -}; - - -// Custom memcpy implementation for platforms where the standard version -// may not be good enough. -// TODO(lrn): Check whether some IA32 platforms should be excluded. -#if defined(V8_TARGET_ARCH_IA32) - -// TODO(lrn): Extend to other platforms as needed. - -typedef void (*MemCopyFunction)(void* dest, const void* src, size_t size); - -// Implemented in codegen-<arch>.cc. -MemCopyFunction CreateMemCopyFunction(); - -// Copy memory area to disjoint memory area. -static inline void MemCopy(void* dest, const void* src, size_t size) { - static MemCopyFunction memcopy = CreateMemCopyFunction(); - (*memcopy)(dest, src, size); -#ifdef DEBUG - CHECK_EQ(0, memcmp(dest, src, size)); -#endif -} - - -// Limit below which the extra overhead of the MemCopy function is likely -// to outweigh the benefits of faster copying. -// TODO(lrn): Try to find a more precise value. -static const int kMinComplexMemCopy = 64; - -#else // V8_TARGET_ARCH_IA32 - -static inline void MemCopy(void* dest, const void* src, size_t size) { - memcpy(dest, src, size); -} - -static const int kMinComplexMemCopy = 256; - -#endif // V8_TARGET_ARCH_IA32 - - -// Copy from ASCII/16bit chars to ASCII/16bit chars. -template <typename sourcechar, typename sinkchar> -static inline void CopyChars(sinkchar* dest, const sourcechar* src, int chars) { - sinkchar* limit = dest + chars; -#ifdef V8_HOST_CAN_READ_UNALIGNED - if (sizeof(*dest) == sizeof(*src)) { - if (chars >= static_cast<int>(kMinComplexMemCopy / sizeof(*dest))) { - MemCopy(dest, src, chars * sizeof(*dest)); - return; - } - // Number of characters in a uintptr_t. - static const int kStepSize = sizeof(uintptr_t) / sizeof(*dest); // NOLINT - while (dest <= limit - kStepSize) { - *reinterpret_cast<uintptr_t*>(dest) = - *reinterpret_cast<const uintptr_t*>(src); - dest += kStepSize; - src += kStepSize; - } - } -#endif - while (dest < limit) { - *dest++ = static_cast<sinkchar>(*src++); - } -} - - // Compare ASCII/16bit chars to ASCII/16bit chars. template <typename lchar, typename rchar> static inline int CompareChars(const lchar* lhs, const rchar* rhs, int chars) { @@ -877,54 +672,14 @@ static inline int CompareChars(const lchar* lhs, const rchar* rhs, int chars) { } -template <typename T> -static inline void MemsetPointer(T** dest, T* value, int counter) { -#if defined(V8_HOST_ARCH_IA32) -#define STOS "stosl" -#elif defined(V8_HOST_ARCH_X64) -#define STOS "stosq" -#endif - -#if defined(__GNUC__) && defined(STOS) - asm volatile( - "cld;" - "rep ; " STOS - : "+&c" (counter), "+&D" (dest) - : "a" (value) - : "memory", "cc"); -#else - for (int i = 0; i < counter; i++) { - dest[i] = value; - } -#endif - -#undef STOS -} - - -// Copies data from |src| to |dst|. The data spans MUST not overlap. -inline void CopyWords(Object** dst, Object** src, int num_words) { - ASSERT(Min(dst, src) + num_words <= Max(dst, src)); - ASSERT(num_words > 0); - - // Use block copying memcpy if the segment we're copying is - // enough to justify the extra call/setup overhead. - static const int kBlockCopyLimit = 16; - - if (num_words >= kBlockCopyLimit) { - memcpy(dst, src, num_words * kPointerSize); - } else { - int remaining = num_words; - do { - remaining--; - *dst++ = *src++; - } while (remaining > 0); - } -} - - // Calculate 10^exponent. -int TenToThe(int exponent); +static inline int TenToThe(int exponent) { + ASSERT(exponent <= 9); + ASSERT(exponent >= 1); + int answer = 10; + for (int i = 1; i < exponent; i++) answer *= 10; + return answer; +} // The type-based aliasing rule allows the compiler to assume that pointers of diff --git a/deps/v8/src/v8.cc b/deps/v8/src/v8.cc index 0623400abb..c8d719b144 100644 --- a/deps/v8/src/v8.cc +++ b/deps/v8/src/v8.cc @@ -44,6 +44,7 @@ bool V8::has_been_setup_ = false; bool V8::has_been_disposed_ = false; bool V8::has_fatal_error_ = false; + bool V8::Initialize(Deserializer* des) { bool create_heap_objects = des == NULL; if (has_been_disposed_ || has_fatal_error_) return false; @@ -176,22 +177,41 @@ static uint32_t random_seed() { } -uint32_t V8::Random() { - // Random number generator using George Marsaglia's MWC algorithm. - static uint32_t hi = 0; - static uint32_t lo = 0; +typedef struct { + uint32_t hi; + uint32_t lo; +} random_state; + +// Random number generator using George Marsaglia's MWC algorithm. +static uint32_t random_base(random_state *state) { // Initialize seed using the system random(). If one of the seeds // should ever become zero again, or if random() returns zero, we // avoid getting stuck with zero bits in hi or lo by re-initializing // them on demand. - if (hi == 0) hi = random_seed(); - if (lo == 0) lo = random_seed(); + if (state->hi == 0) state->hi = random_seed(); + if (state->lo == 0) state->lo = random_seed(); // Mix the bits. - hi = 36969 * (hi & 0xFFFF) + (hi >> 16); - lo = 18273 * (lo & 0xFFFF) + (lo >> 16); - return (hi << 16) + (lo & 0xFFFF); + state->hi = 36969 * (state->hi & 0xFFFF) + (state->hi >> 16); + state->lo = 18273 * (state->lo & 0xFFFF) + (state->lo >> 16); + return (state->hi << 16) + (state->lo & 0xFFFF); +} + + +// Used by JavaScript APIs +uint32_t V8::Random() { + static random_state state = {0, 0}; + return random_base(&state); +} + + +// Used internally by the JIT and memory allocator for security +// purposes. So, we keep a different state to prevent informations +// leaks that could be used in an exploit. +uint32_t V8::RandomPrivate() { + static random_state state = {0, 0}; + return random_base(&state); } diff --git a/deps/v8/src/v8.h b/deps/v8/src/v8.h index 9dbdf4c28a..74e98f151d 100644 --- a/deps/v8/src/v8.h +++ b/deps/v8/src/v8.h @@ -53,10 +53,10 @@ // Basic includes #include "../include/v8.h" -#include "globals.h" +#include "v8globals.h" #include "checks.h" #include "allocation.h" -#include "utils.h" +#include "v8utils.h" #include "flags.h" // Objects & heap @@ -94,6 +94,11 @@ class V8 : public AllStatic { // Random number generation support. Not cryptographically safe. static uint32_t Random(); + // We use random numbers internally in memory allocation and in the + // compilers for security. In order to prevent information leaks we + // use a separate random state for internal random number + // generation. + static uint32_t RandomPrivate(); static Object* FillHeapNumberWithRandom(Object* heap_number); // Idle notification directly from the API. diff --git a/deps/v8/src/v8globals.h b/deps/v8/src/v8globals.h new file mode 100644 index 0000000000..2815771a29 --- /dev/null +++ b/deps/v8/src/v8globals.h @@ -0,0 +1,464 @@ +// Copyright 2010 the V8 project authors. All rights reserved. +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following +// disclaimer in the documentation and/or other materials provided +// with the distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +#ifndef V8_V8GLOBALS_H_ +#define V8_V8GLOBALS_H_ + +#include "globals.h" + +namespace v8 { +namespace internal { + +// This file contains constants and global declarations related to the +// V8 system. + +// Mask for the sign bit in a smi. +const intptr_t kSmiSignMask = kIntptrSignBit; + +const int kObjectAlignmentBits = kPointerSizeLog2; +const intptr_t kObjectAlignment = 1 << kObjectAlignmentBits; +const intptr_t kObjectAlignmentMask = kObjectAlignment - 1; + +// Desired alignment for pointers. +const intptr_t kPointerAlignment = (1 << kPointerSizeLog2); +const intptr_t kPointerAlignmentMask = kPointerAlignment - 1; + +// Desired alignment for maps. +#if V8_HOST_ARCH_64_BIT +const intptr_t kMapAlignmentBits = kObjectAlignmentBits; +#else +const intptr_t kMapAlignmentBits = kObjectAlignmentBits + 3; +#endif +const intptr_t kMapAlignment = (1 << kMapAlignmentBits); +const intptr_t kMapAlignmentMask = kMapAlignment - 1; + +// Desired alignment for generated code is 32 bytes (to improve cache line +// utilization). +const int kCodeAlignmentBits = 5; +const intptr_t kCodeAlignment = 1 << kCodeAlignmentBits; +const intptr_t kCodeAlignmentMask = kCodeAlignment - 1; + +// Tag information for Failure. +const int kFailureTag = 3; +const int kFailureTagSize = 2; +const intptr_t kFailureTagMask = (1 << kFailureTagSize) - 1; + + +// Zap-value: The value used for zapping dead objects. +// Should be a recognizable hex value tagged as a heap object pointer. +#ifdef V8_HOST_ARCH_64_BIT +const Address kZapValue = + reinterpret_cast<Address>(V8_UINT64_C(0xdeadbeedbeadbeed)); +const Address kHandleZapValue = + reinterpret_cast<Address>(V8_UINT64_C(0x1baddead0baddead)); +const Address kFromSpaceZapValue = + reinterpret_cast<Address>(V8_UINT64_C(0x1beefdad0beefdad)); +const uint64_t kDebugZapValue = 0xbadbaddbbadbaddb; +#else +const Address kZapValue = reinterpret_cast<Address>(0xdeadbeed); +const Address kHandleZapValue = reinterpret_cast<Address>(0xbaddead); +const Address kFromSpaceZapValue = reinterpret_cast<Address>(0xbeefdad); +const uint32_t kDebugZapValue = 0xbadbaddb; +#endif + + +// Number of bits to represent the page size for paged spaces. The value of 13 +// gives 8K bytes per page. +const int kPageSizeBits = 13; + +// On Intel architecture, cache line size is 64 bytes. +// On ARM it may be less (32 bytes), but as far this constant is +// used for aligning data, it doesn't hurt to align on a greater value. +const int kProcessorCacheLineSize = 64; + +// Constants relevant to double precision floating point numbers. + +// Quiet NaNs have bits 51 to 62 set, possibly the sign bit, and no +// other bits set. +const uint64_t kQuietNaNMask = static_cast<uint64_t>(0xfff) << 51; +// If looking only at the top 32 bits, the QNaN mask is bits 19 to 30. +const uint32_t kQuietNaNHighBitsMask = 0xfff << (51 - 32); + + +// ----------------------------------------------------------------------------- +// Forward declarations for frequently used classes +// (sorted alphabetically) + +class AccessorInfo; +class Allocation; +class Arguments; +class Assembler; +class AssertNoAllocation; +class BreakableStatement; +class Code; +class CodeGenerator; +class CodeStub; +class Context; +class Debug; +class Debugger; +class DebugInfo; +class Descriptor; +class DescriptorArray; +class Expression; +class ExternalReference; +class FixedArray; +class FunctionEntry; +class FunctionLiteral; +class FunctionTemplateInfo; +class NumberDictionary; +class StringDictionary; +template <typename T> class Handle; +class Heap; +class HeapObject; +class IC; +class InterceptorInfo; +class IterationStatement; +class JSArray; +class JSFunction; +class JSObject; +class LargeObjectSpace; +class LookupResult; +class MacroAssembler; +class Map; +class MapSpace; +class MarkCompactCollector; +class NewSpace; +class NodeVisitor; +class Object; +class MaybeObject; +class OldSpace; +class Property; +class Proxy; +class RegExpNode; +struct RegExpCompileData; +class RegExpTree; +class RegExpCompiler; +class RegExpVisitor; +class Scope; +template<class Allocator = FreeStoreAllocationPolicy> class ScopeInfo; +class SerializedScopeInfo; +class Script; +class Slot; +class Smi; +template <typename Config, class Allocator = FreeStoreAllocationPolicy> + class SplayTree; +class Statement; +class String; +class Struct; +class SwitchStatement; +class AstVisitor; +class Variable; +class VariableProxy; +class RelocInfo; +class Deserializer; +class MessageLocation; +class ObjectGroup; +class TickSample; +class VirtualMemory; +class Mutex; + +typedef bool (*WeakSlotCallback)(Object** pointer); + +// ----------------------------------------------------------------------------- +// Miscellaneous + +// NOTE: SpaceIterator depends on AllocationSpace enumeration values being +// consecutive. +enum AllocationSpace { + NEW_SPACE, // Semispaces collected with copying collector. + OLD_POINTER_SPACE, // May contain pointers to new space. + OLD_DATA_SPACE, // Must not have pointers to new space. + CODE_SPACE, // No pointers to new space, marked executable. + MAP_SPACE, // Only and all map objects. + CELL_SPACE, // Only and all cell objects. + LO_SPACE, // Promoted large objects. + + FIRST_SPACE = NEW_SPACE, + LAST_SPACE = LO_SPACE, + FIRST_PAGED_SPACE = OLD_POINTER_SPACE, + LAST_PAGED_SPACE = CELL_SPACE +}; +const int kSpaceTagSize = 3; +const int kSpaceTagMask = (1 << kSpaceTagSize) - 1; + + +// A flag that indicates whether objects should be pretenured when +// allocated (allocated directly into the old generation) or not +// (allocated in the young generation if the object size and type +// allows). +enum PretenureFlag { NOT_TENURED, TENURED }; + +enum GarbageCollector { SCAVENGER, MARK_COMPACTOR }; + +enum Executability { NOT_EXECUTABLE, EXECUTABLE }; + +enum VisitMode { VISIT_ALL, VISIT_ALL_IN_SCAVENGE, VISIT_ONLY_STRONG }; + +// Flag indicating whether code is built into the VM (one of the natives files). +enum NativesFlag { NOT_NATIVES_CODE, NATIVES_CODE }; + + +// A CodeDesc describes a buffer holding instructions and relocation +// information. The instructions start at the beginning of the buffer +// and grow forward, the relocation information starts at the end of +// the buffer and grows backward. +// +// |<--------------- buffer_size ---------------->| +// |<-- instr_size -->| |<-- reloc_size -->| +// +==================+========+==================+ +// | instructions | free | reloc info | +// +==================+========+==================+ +// ^ +// | +// buffer + +struct CodeDesc { + byte* buffer; + int buffer_size; + int instr_size; + int reloc_size; + Assembler* origin; +}; + + +// Callback function on object slots, used for iterating heap object slots in +// HeapObjects, global pointers to heap objects, etc. The callback allows the +// callback function to change the value of the slot. +typedef void (*ObjectSlotCallback)(HeapObject** pointer); + + +// Callback function used for iterating objects in heap spaces, +// for example, scanning heap objects. +typedef int (*HeapObjectCallback)(HeapObject* obj); + + +// Callback function used for checking constraints when copying/relocating +// objects. Returns true if an object can be copied/relocated from its +// old_addr to a new_addr. +typedef bool (*ConstraintCallback)(Address new_addr, Address old_addr); + + +// Callback function on inline caches, used for iterating over inline caches +// in compiled code. +typedef void (*InlineCacheCallback)(Code* code, Address ic); + + +// State for inline cache call sites. Aliased as IC::State. +enum InlineCacheState { + // Has never been executed. + UNINITIALIZED, + // Has been executed but monomorhic state has been delayed. + PREMONOMORPHIC, + // Has been executed and only one receiver type has been seen. + MONOMORPHIC, + // Like MONOMORPHIC but check failed due to prototype. + MONOMORPHIC_PROTOTYPE_FAILURE, + // Multiple receiver types have been seen. + MEGAMORPHIC, + // Special states for debug break or step in prepare stubs. + DEBUG_BREAK, + DEBUG_PREPARE_STEP_IN +}; + + +enum InLoopFlag { + NOT_IN_LOOP, + IN_LOOP +}; + + +enum CallFunctionFlags { + NO_CALL_FUNCTION_FLAGS = 0, + RECEIVER_MIGHT_BE_VALUE = 1 << 0 // Receiver might not be a JSObject. +}; + + +enum InlineCacheHolderFlag { + OWN_MAP, // For fast properties objects. + PROTOTYPE_MAP // For slow properties objects (except GlobalObjects). +}; + + +// Type of properties. +// Order of properties is significant. +// Must fit in the BitField PropertyDetails::TypeField. +// A copy of this is in mirror-debugger.js. +enum PropertyType { + NORMAL = 0, // only in slow mode + FIELD = 1, // only in fast mode + CONSTANT_FUNCTION = 2, // only in fast mode + CALLBACKS = 3, + INTERCEPTOR = 4, // only in lookup results, not in descriptors. + MAP_TRANSITION = 5, // only in fast mode + CONSTANT_TRANSITION = 6, // only in fast mode + NULL_DESCRIPTOR = 7, // only in fast mode + // All properties before MAP_TRANSITION are real. + FIRST_PHANTOM_PROPERTY_TYPE = MAP_TRANSITION, + // There are no IC stubs for NULL_DESCRIPTORS. Therefore, + // NULL_DESCRIPTOR can be used as the type flag for IC stubs for + // nonexistent properties. + NONEXISTENT = NULL_DESCRIPTOR +}; + + +// Whether to remove map transitions and constant transitions from a +// DescriptorArray. +enum TransitionFlag { + REMOVE_TRANSITIONS, + KEEP_TRANSITIONS +}; + + +// Union used for fast testing of specific double values. +union DoubleRepresentation { + double value; + int64_t bits; + DoubleRepresentation(double x) { value = x; } +}; + + +// Union used for customized checking of the IEEE double types +// inlined within v8 runtime, rather than going to the underlying +// platform headers and libraries +union IeeeDoubleLittleEndianArchType { + double d; + struct { + unsigned int man_low :32; + unsigned int man_high :20; + unsigned int exp :11; + unsigned int sign :1; + } bits; +}; + + +union IeeeDoubleBigEndianArchType { + double d; + struct { + unsigned int sign :1; + unsigned int exp :11; + unsigned int man_high :20; + unsigned int man_low :32; + } bits; +}; + + +// AccessorCallback +struct AccessorDescriptor { + MaybeObject* (*getter)(Object* object, void* data); + MaybeObject* (*setter)(JSObject* object, Object* value, void* data); + void* data; +}; + + +// Logging and profiling. +// A StateTag represents a possible state of the VM. When compiled with +// ENABLE_VMSTATE_TRACKING, the logger maintains a stack of these. +// Creating a VMState object enters a state by pushing on the stack, and +// destroying a VMState object leaves a state by popping the current state +// from the stack. + +#define STATE_TAG_LIST(V) \ + V(JS) \ + V(GC) \ + V(COMPILER) \ + V(OTHER) \ + V(EXTERNAL) + +enum StateTag { +#define DEF_STATE_TAG(name) name, + STATE_TAG_LIST(DEF_STATE_TAG) +#undef DEF_STATE_TAG + // Pseudo-types. + state_tag_count +}; + + +// ----------------------------------------------------------------------------- +// Macros + +// Testers for test. + +#define HAS_SMI_TAG(value) \ + ((reinterpret_cast<intptr_t>(value) & kSmiTagMask) == kSmiTag) + +#define HAS_FAILURE_TAG(value) \ + ((reinterpret_cast<intptr_t>(value) & kFailureTagMask) == kFailureTag) + +// OBJECT_POINTER_ALIGN returns the value aligned as a HeapObject pointer +#define OBJECT_POINTER_ALIGN(value) \ + (((value) + kObjectAlignmentMask) & ~kObjectAlignmentMask) + +// POINTER_SIZE_ALIGN returns the value aligned as a pointer. +#define POINTER_SIZE_ALIGN(value) \ + (((value) + kPointerAlignmentMask) & ~kPointerAlignmentMask) + +// MAP_POINTER_ALIGN returns the value aligned as a map pointer. +#define MAP_POINTER_ALIGN(value) \ + (((value) + kMapAlignmentMask) & ~kMapAlignmentMask) + +// CODE_POINTER_ALIGN returns the value aligned as a generated code segment. +#define CODE_POINTER_ALIGN(value) \ + (((value) + kCodeAlignmentMask) & ~kCodeAlignmentMask) + +// Support for tracking C++ memory allocation. Insert TRACK_MEMORY("Fisk") +// inside a C++ class and new and delete will be overloaded so logging is +// performed. +// This file (globals.h) is included before log.h, so we use direct calls to +// the Logger rather than the LOG macro. +#ifdef DEBUG +#define TRACK_MEMORY(name) \ + void* operator new(size_t size) { \ + void* result = ::operator new(size); \ + Logger::NewEvent(name, result, size); \ + return result; \ + } \ + void operator delete(void* object) { \ + Logger::DeleteEvent(name, object); \ + ::operator delete(object); \ + } +#else +#define TRACK_MEMORY(name) +#endif + + +// Feature flags bit positions. They are mostly based on the CPUID spec. +// (We assign CPUID itself to one of the currently reserved bits -- +// feel free to change this if needed.) +// On X86/X64, values below 32 are bits in EDX, values above 32 are bits in ECX. +enum CpuFeature { SSE4_1 = 32 + 19, // x86 + SSE3 = 32 + 0, // x86 + SSE2 = 26, // x86 + CMOV = 15, // x86 + RDTSC = 4, // x86 + CPUID = 10, // x86 + VFP3 = 1, // ARM + ARMv7 = 2, // ARM + SAHF = 0}; // x86 + +} } // namespace v8::internal + +#endif // V8_V8GLOBALS_H_ diff --git a/deps/v8/src/v8utils.h b/deps/v8/src/v8utils.h new file mode 100644 index 0000000000..a907c9f556 --- /dev/null +++ b/deps/v8/src/v8utils.h @@ -0,0 +1,301 @@ +// Copyright 2010 the V8 project authors. All rights reserved. +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following +// disclaimer in the documentation and/or other materials provided +// with the distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +#ifndef V8_V8UTILS_H_ +#define V8_V8UTILS_H_ + +#include "utils.h" + +namespace v8 { +namespace internal { + +// ---------------------------------------------------------------------------- +// I/O support. + +#if __GNUC__ >= 4 +// On gcc we can ask the compiler to check the types of %d-style format +// specifiers and their associated arguments. TODO(erikcorry) fix this +// so it works on MacOSX. +#if defined(__MACH__) && defined(__APPLE__) +#define PRINTF_CHECKING +#else // MacOsX. +#define PRINTF_CHECKING __attribute__ ((format (printf, 1, 2))) +#endif +#else +#define PRINTF_CHECKING +#endif + +// Our version of printf(). +void PRINTF_CHECKING PrintF(const char* format, ...); + +// Our version of fflush. +void Flush(); + + +// Read a line of characters after printing the prompt to stdout. The resulting +// char* needs to be disposed off with DeleteArray by the caller. +char* ReadLine(const char* prompt); + + +// Read and return the raw bytes in a file. the size of the buffer is returned +// in size. +// The returned buffer must be freed by the caller. +byte* ReadBytes(const char* filename, int* size, bool verbose = true); + + +// Write size chars from str to the file given by filename. +// The file is overwritten. Returns the number of chars written. +int WriteChars(const char* filename, + const char* str, + int size, + bool verbose = true); + + +// Write size bytes to the file given by filename. +// The file is overwritten. Returns the number of bytes written. +int WriteBytes(const char* filename, + const byte* bytes, + int size, + bool verbose = true); + + +// Write the C code +// const char* <varname> = "<str>"; +// const int <varname>_len = <len>; +// to the file given by filename. Only the first len chars are written. +int WriteAsCFile(const char* filename, const char* varname, + const char* str, int size, bool verbose = true); + + +// Data structures + +template <typename T> +inline Vector< Handle<Object> > HandleVector(v8::internal::Handle<T>* elms, + int length) { + return Vector< Handle<Object> >( + reinterpret_cast<v8::internal::Handle<Object>*>(elms), length); +} + +// Memory + +// Copies data from |src| to |dst|. The data spans MUST not overlap. +inline void CopyWords(Object** dst, Object** src, int num_words) { + ASSERT(Min(dst, src) + num_words <= Max(dst, src)); + ASSERT(num_words > 0); + + // Use block copying memcpy if the segment we're copying is + // enough to justify the extra call/setup overhead. + static const int kBlockCopyLimit = 16; + + if (num_words >= kBlockCopyLimit) { + memcpy(dst, src, num_words * kPointerSize); + } else { + int remaining = num_words; + do { + remaining--; + *dst++ = *src++; + } while (remaining > 0); + } +} + + +template <typename T> +static inline void MemsetPointer(T** dest, T* value, int counter) { +#if defined(V8_HOST_ARCH_IA32) +#define STOS "stosl" +#elif defined(V8_HOST_ARCH_X64) +#define STOS "stosq" +#endif + +#if defined(__GNUC__) && defined(STOS) + asm volatile( + "cld;" + "rep ; " STOS + : "+&c" (counter), "+&D" (dest) + : "a" (value) + : "memory", "cc"); +#else + for (int i = 0; i < counter; i++) { + dest[i] = value; + } +#endif + +#undef STOS +} + + +// Simple wrapper that allows an ExternalString to refer to a +// Vector<const char>. Doesn't assume ownership of the data. +class AsciiStringAdapter: public v8::String::ExternalAsciiStringResource { + public: + explicit AsciiStringAdapter(Vector<const char> data) : data_(data) {} + + virtual const char* data() const { return data_.start(); } + + virtual size_t length() const { return data_.length(); } + + private: + Vector<const char> data_; +}; + + +// Simple support to read a file into a 0-terminated C-string. +// The returned buffer must be freed by the caller. +// On return, *exits tells whether the file existed. +Vector<const char> ReadFile(const char* filename, + bool* exists, + bool verbose = true); + + +// Helper class for building result strings in a character buffer. The +// purpose of the class is to use safe operations that checks the +// buffer bounds on all operations in debug mode. +class StringBuilder { + public: + // Create a string builder with a buffer of the given size. The + // buffer is allocated through NewArray<char> and must be + // deallocated by the caller of Finalize(). + explicit StringBuilder(int size); + + StringBuilder(char* buffer, int size) + : buffer_(buffer, size), position_(0) { } + + ~StringBuilder() { if (!is_finalized()) Finalize(); } + + int size() const { return buffer_.length(); } + + // Get the current position in the builder. + int position() const { + ASSERT(!is_finalized()); + return position_; + } + + // Reset the position. + void Reset() { position_ = 0; } + + // Add a single character to the builder. It is not allowed to add + // 0-characters; use the Finalize() method to terminate the string + // instead. + void AddCharacter(char c) { + ASSERT(c != '\0'); + ASSERT(!is_finalized() && position_ < buffer_.length()); + buffer_[position_++] = c; + } + + // Add an entire string to the builder. Uses strlen() internally to + // compute the length of the input string. + void AddString(const char* s); + + // Add the first 'n' characters of the given string 's' to the + // builder. The input string must have enough characters. + void AddSubstring(const char* s, int n); + + // Add formatted contents to the builder just like printf(). + void AddFormatted(const char* format, ...); + + // Add character padding to the builder. If count is non-positive, + // nothing is added to the builder. + void AddPadding(char c, int count); + + // Finalize the string by 0-terminating it and returning the buffer. + char* Finalize(); + + private: + Vector<char> buffer_; + int position_; + + bool is_finalized() const { return position_ < 0; } + + DISALLOW_IMPLICIT_CONSTRUCTORS(StringBuilder); +}; + + +// Custom memcpy implementation for platforms where the standard version +// may not be good enough. +#if defined(V8_TARGET_ARCH_IA32) + +// The default memcpy on ia32 architectures is generally not as efficient +// as possible. (If any further ia32 platforms are introduced where the +// memcpy function is efficient, exclude them from this branch). + +typedef void (*MemCopyFunction)(void* dest, const void* src, size_t size); + +// Implemented in codegen-<arch>.cc. +MemCopyFunction CreateMemCopyFunction(); + +// Copy memory area to disjoint memory area. +static inline void MemCopy(void* dest, const void* src, size_t size) { + static MemCopyFunction memcopy = CreateMemCopyFunction(); + (*memcopy)(dest, src, size); +#ifdef DEBUG + CHECK_EQ(0, memcmp(dest, src, size)); +#endif +} + +// Limit below which the extra overhead of the MemCopy function is likely +// to outweigh the benefits of faster copying. +static const int kMinComplexMemCopy = 64; + +#else // V8_TARGET_ARCH_IA32 + +static inline void MemCopy(void* dest, const void* src, size_t size) { + memcpy(dest, src, size); +} + +static const int kMinComplexMemCopy = 256; + +#endif // V8_TARGET_ARCH_IA32 + + +// Copy from ASCII/16bit chars to ASCII/16bit chars. +template <typename sourcechar, typename sinkchar> +static inline void CopyChars(sinkchar* dest, const sourcechar* src, int chars) { + sinkchar* limit = dest + chars; +#ifdef V8_HOST_CAN_READ_UNALIGNED + if (sizeof(*dest) == sizeof(*src)) { + if (chars >= static_cast<int>(kMinComplexMemCopy / sizeof(*dest))) { + MemCopy(dest, src, chars * sizeof(*dest)); + return; + } + // Number of characters in a uintptr_t. + static const int kStepSize = sizeof(uintptr_t) / sizeof(*dest); // NOLINT + while (dest <= limit - kStepSize) { + *reinterpret_cast<uintptr_t*>(dest) = + *reinterpret_cast<const uintptr_t*>(src); + dest += kStepSize; + src += kStepSize; + } + } +#endif + while (dest < limit) { + *dest++ = static_cast<sinkchar>(*src++); + } +} + +} } // namespace v8::internal + +#endif // V8_V8UTILS_H_ diff --git a/deps/v8/src/version.cc b/deps/v8/src/version.cc index e27b9153fc..ae3250fb87 100644 --- a/deps/v8/src/version.cc +++ b/deps/v8/src/version.cc @@ -34,7 +34,7 @@ // cannot be changed without changing the SCons build script. #define MAJOR_VERSION 2 #define MINOR_VERSION 5 -#define BUILD_NUMBER 3 +#define BUILD_NUMBER 7 #define PATCH_LEVEL 0 #define CANDIDATE_VERSION false diff --git a/deps/v8/src/virtual-frame.h b/deps/v8/src/virtual-frame.h index 220823ed4b..65d1009899 100644 --- a/deps/v8/src/virtual-frame.h +++ b/deps/v8/src/virtual-frame.h @@ -31,6 +31,9 @@ #include "frame-element.h" #include "macro-assembler.h" +#include "list-inl.h" +#include "utils.h" + #if V8_TARGET_ARCH_IA32 #include "ia32/virtual-frame-ia32.h" #elif V8_TARGET_ARCH_X64 @@ -43,4 +46,14 @@ #error Unsupported target architecture. #endif +namespace v8 { +namespace internal { + +// Add() on List is inlined, ResizeAdd() called by Add() is inlined except for +// Lists of FrameElements, and ResizeAddInternal() is inlined in ResizeAdd(). +template <> +void List<FrameElement, + FreeStoreAllocationPolicy>::ResizeAdd(const FrameElement& element); +} } // namespace v8::internal + #endif // V8_VIRTUAL_FRAME_H_ diff --git a/deps/v8/src/x64/assembler-x64.cc b/deps/v8/src/x64/assembler-x64.cc index bf5ee5bbb7..caed7c8aa9 100644 --- a/deps/v8/src/x64/assembler-x64.cc +++ b/deps/v8/src/x64/assembler-x64.cc @@ -296,7 +296,7 @@ static void InitCoverageLog(); byte* Assembler::spare_buffer_ = NULL; Assembler::Assembler(void* buffer, int buffer_size) - : code_targets_(100) { + : code_targets_(100), positions_recorder_(this) { if (buffer == NULL) { // Do our own buffer management. if (buffer_size <= kMinimalBufferSize) { @@ -337,10 +337,7 @@ Assembler::Assembler(void* buffer, int buffer_size) reloc_info_writer.Reposition(buffer_ + buffer_size, pc_); last_pc_ = NULL; - current_statement_position_ = RelocInfo::kNoPosition; - current_position_ = RelocInfo::kNoPosition; - written_statement_position_ = current_statement_position_; - written_position_ = current_position_; + #ifdef GENERATED_CODE_COVERAGE InitCoverageLog(); #endif @@ -845,7 +842,7 @@ void Assembler::call(Label* L) { void Assembler::call(Handle<Code> target, RelocInfo::Mode rmode) { - WriteRecordedPositions(); + positions_recorder()->WriteRecordedPositions(); EnsureSpace ensure_space(this); last_pc_ = pc_; // 1110 1000 #32-bit disp. @@ -2935,14 +2932,14 @@ void Assembler::RecordRelocInfo(RelocInfo::Mode rmode, intptr_t data) { } void Assembler::RecordJSReturn() { - WriteRecordedPositions(); + positions_recorder()->WriteRecordedPositions(); EnsureSpace ensure_space(this); RecordRelocInfo(RelocInfo::JS_RETURN); } void Assembler::RecordDebugBreakSlot() { - WriteRecordedPositions(); + positions_recorder()->WriteRecordedPositions(); EnsureSpace ensure_space(this); RecordRelocInfo(RelocInfo::DEBUG_BREAK_SLOT); } @@ -2956,47 +2953,6 @@ void Assembler::RecordComment(const char* msg) { } -void Assembler::RecordPosition(int pos) { - ASSERT(pos != RelocInfo::kNoPosition); - ASSERT(pos >= 0); - current_position_ = pos; -} - - -void Assembler::RecordStatementPosition(int pos) { - ASSERT(pos != RelocInfo::kNoPosition); - ASSERT(pos >= 0); - current_statement_position_ = pos; -} - - -bool Assembler::WriteRecordedPositions() { - bool written = false; - - // Write the statement position if it is different from what was written last - // time. - if (current_statement_position_ != written_statement_position_) { - EnsureSpace ensure_space(this); - RecordRelocInfo(RelocInfo::STATEMENT_POSITION, current_statement_position_); - written_statement_position_ = current_statement_position_; - written = true; - } - - // Write the position if it is different from what was written last time and - // also different from the written statement position. - if (current_position_ != written_position_ && - current_position_ != written_statement_position_) { - EnsureSpace ensure_space(this); - RecordRelocInfo(RelocInfo::POSITION, current_position_); - written_position_ = current_position_; - written = true; - } - - // Return whether something was written. - return written; -} - - const int RelocInfo::kApplyMask = RelocInfo::kCodeTargetMask | 1 << RelocInfo::INTERNAL_REFERENCE; diff --git a/deps/v8/src/x64/assembler-x64.h b/deps/v8/src/x64/assembler-x64.h index bbc1010627..c7f763222e 100644 --- a/deps/v8/src/x64/assembler-x64.h +++ b/deps/v8/src/x64/assembler-x64.h @@ -1174,13 +1174,9 @@ class Assembler : public Malloced { // Use --debug_code to enable. void RecordComment(const char* msg); - void RecordPosition(int pos); - void RecordStatementPosition(int pos); - bool WriteRecordedPositions(); - int pc_offset() const { return static_cast<int>(pc_ - buffer_); } - int current_statement_position() const { return current_statement_position_; } - int current_position() const { return current_position_; } + + PositionsRecorder* positions_recorder() { return &positions_recorder_; } // Check if there is less than kGap bytes available in the buffer. // If this is the case, we need to grow the buffer before emitting @@ -1404,11 +1400,8 @@ class Assembler : public Malloced { // push-pop elimination byte* last_pc_; - // source position information - int current_statement_position_; - int current_position_; - int written_statement_position_; - int written_position_; + PositionsRecorder positions_recorder_; + friend class PositionsRecorder; }; diff --git a/deps/v8/src/x64/code-stubs-x64.cc b/deps/v8/src/x64/code-stubs-x64.cc index c179769b8a..04173e1afb 100644 --- a/deps/v8/src/x64/code-stubs-x64.cc +++ b/deps/v8/src/x64/code-stubs-x64.cc @@ -2483,20 +2483,6 @@ void CEntryStub::GenerateThrowTOS(MacroAssembler* masm) { } -void ApiGetterEntryStub::Generate(MacroAssembler* masm) { - __ PrepareCallApiFunction(kStackSpace); -#ifdef _WIN64 - // All the parameters should be set up by a caller. -#else - // Set 1st parameter register with property name. - __ movq(rsi, rdx); - // Second parameter register rdi should be set with pointer to AccessorInfo - // by a caller. -#endif - __ CallApiFunctionAndReturn(fun()); -} - - void CEntryStub::GenerateCore(MacroAssembler* masm, Label* throw_normal_exception, Label* throw_termination_exception, @@ -2549,18 +2535,18 @@ void CEntryStub::GenerateCore(MacroAssembler* masm, #ifdef _WIN64 // Windows 64-bit ABI passes arguments in rcx, rdx, r8, r9 // Store Arguments object on stack, below the 4 WIN64 ABI parameter slots. - __ movq(Operand(rsp, 4 * kPointerSize), r14); // argc. - __ movq(Operand(rsp, 5 * kPointerSize), r12); // argv. + __ movq(StackSpaceOperand(0), r14); // argc. + __ movq(StackSpaceOperand(1), r12); // argv. if (result_size_ < 2) { // Pass a pointer to the Arguments object as the first argument. // Return result in single register (rax). - __ lea(rcx, Operand(rsp, 4 * kPointerSize)); + __ lea(rcx, StackSpaceOperand(0)); } else { ASSERT_EQ(2, result_size_); // Pass a pointer to the result location as the first argument. - __ lea(rcx, Operand(rsp, 6 * kPointerSize)); + __ lea(rcx, StackSpaceOperand(2)); // Pass a pointer to the Arguments object as the second argument. - __ lea(rdx, Operand(rsp, 4 * kPointerSize)); + __ lea(rdx, StackSpaceOperand(0)); } #else // _WIN64 @@ -2596,7 +2582,7 @@ void CEntryStub::GenerateCore(MacroAssembler* masm, __ j(zero, &failure_returned); // Exit the JavaScript to C++ exit frame. - __ LeaveExitFrame(result_size_); + __ LeaveExitFrame(); __ ret(0); // Handling of failure. @@ -2700,7 +2686,12 @@ void CEntryStub::Generate(MacroAssembler* masm) { // builtin once. // Enter the exit frame that transitions from JavaScript to C++. - __ EnterExitFrame(result_size_); +#ifdef _WIN64 + int arg_stack_space = (result_size_ < 2 ? 2 : 4); +#else + int arg_stack_space = 0; +#endif + __ EnterExitFrame(arg_stack_space); // rax: Holds the context at this point, but should not be used. // On entry to code generated by GenerateCore, it must hold diff --git a/deps/v8/src/x64/codegen-x64.cc b/deps/v8/src/x64/codegen-x64.cc index 0faf775d51..6e98a00900 100644 --- a/deps/v8/src/x64/codegen-x64.cc +++ b/deps/v8/src/x64/codegen-x64.cc @@ -568,10 +568,10 @@ void CodeGenerator::Load(Expression* expr) { void CodeGenerator::LoadGlobal() { if (in_spilled_code()) { - frame_->EmitPush(GlobalObject()); + frame_->EmitPush(GlobalObjectOperand()); } else { Result temp = allocator_->Allocate(); - __ movq(temp.reg(), GlobalObject()); + __ movq(temp.reg(), GlobalObjectOperand()); frame_->Push(&temp); } } @@ -580,7 +580,7 @@ void CodeGenerator::LoadGlobal() { void CodeGenerator::LoadGlobalReceiver() { Result temp = allocator_->Allocate(); Register reg = temp.reg(); - __ movq(reg, GlobalObject()); + __ movq(reg, GlobalObjectOperand()); __ movq(reg, FieldOperand(reg, GlobalObject::kGlobalReceiverOffset)); frame_->Push(&temp); } @@ -2956,7 +2956,7 @@ void CodeGenerator::VisitReturnStatement(ReturnStatement* node) { CodeForStatementPosition(node); Load(node->expression()); Result return_value = frame_->Pop(); - masm()->WriteRecordedPositions(); + masm()->positions_recorder()->WriteRecordedPositions(); if (function_return_is_shadowed_) { function_return_.Jump(&return_value); } else { @@ -5592,6 +5592,18 @@ void CodeGenerator::VisitCall(Call* node) { // Push the receiver onto the frame. Load(property->obj()); + // Load the name of the function. + Load(property->key()); + + // Swap the name of the function and the receiver on the stack to follow + // the calling convention for call ICs. + Result key = frame_->Pop(); + Result receiver = frame_->Pop(); + frame_->Push(&key); + frame_->Push(&receiver); + key.Unuse(); + receiver.Unuse(); + // Load the arguments. int arg_count = args->length(); for (int i = 0; i < arg_count; i++) { @@ -5599,14 +5611,13 @@ void CodeGenerator::VisitCall(Call* node) { frame_->SpillTop(); } - // Load the name of the function. - Load(property->key()); - - // Call the IC initialization code. + // Place the key on top of stack and call the IC initialization code. + frame_->PushElementAt(arg_count + 1); CodeForSourcePosition(node->position()); Result result = frame_->CallKeyedCallIC(RelocInfo::CODE_TARGET, arg_count, loop_nesting()); + frame_->Drop(); // Drop the key still on the stack. frame_->RestoreContextRegister(); frame_->Push(&result); } @@ -6062,7 +6073,7 @@ class DeferredIsStringWrapperSafeForDefaultValueOf : public DeferredCode { __ movq(scratch2_, FieldOperand(scratch2_, GlobalObject::kGlobalContextOffset)); __ cmpq(scratch1_, - CodeGenerator::ContextOperand( + ContextOperand( scratch2_, Context::STRING_FUNCTION_PROTOTYPE_MAP_INDEX)); __ j(not_equal, &false_result); // Set the bit in the map to indicate that it has been checked safe for @@ -6564,86 +6575,6 @@ void CodeGenerator::GenerateRegExpConstructResult(ZoneList<Expression*>* args) { } -void CodeGenerator::GenerateRegExpCloneResult(ZoneList<Expression*>* args) { - ASSERT_EQ(1, args->length()); - - Load(args->at(0)); - Result object_result = frame_->Pop(); - object_result.ToRegister(rax); - object_result.Unuse(); - { - VirtualFrame::SpilledScope spilled_scope; - - Label done; - __ JumpIfSmi(rax, &done); - - // Load JSRegExpResult map into rdx. - // Arguments to this function should be results of calling RegExp exec, - // which is either an unmodified JSRegExpResult or null. Anything not having - // the unmodified JSRegExpResult map is returned unmodified. - // This also ensures that elements are fast. - - __ movq(rdx, ContextOperand(rsi, Context::GLOBAL_INDEX)); - __ movq(rdx, FieldOperand(rdx, GlobalObject::kGlobalContextOffset)); - __ movq(rdx, ContextOperand(rdx, Context::REGEXP_RESULT_MAP_INDEX)); - __ cmpq(rdx, FieldOperand(rax, HeapObject::kMapOffset)); - __ j(not_equal, &done); - - if (FLAG_debug_code) { - // Check that object really has empty properties array, as the map - // should guarantee. - __ CompareRoot(FieldOperand(rax, JSObject::kPropertiesOffset), - Heap::kEmptyFixedArrayRootIndex); - __ Check(equal, "JSRegExpResult: default map but non-empty properties."); - } - - DeferredAllocateInNewSpace* allocate_fallback = - new DeferredAllocateInNewSpace(JSRegExpResult::kSize, - rbx, - rdx.bit() | rax.bit()); - - // All set, copy the contents to a new object. - __ AllocateInNewSpace(JSRegExpResult::kSize, - rbx, - no_reg, - no_reg, - allocate_fallback->entry_label(), - TAG_OBJECT); - __ bind(allocate_fallback->exit_label()); - - STATIC_ASSERT(JSRegExpResult::kSize % (2 * kPointerSize) == 0); - // There is an even number of fields, so unroll the loop once - // for efficiency. - for (int i = 0; i < JSRegExpResult::kSize; i += 2 * kPointerSize) { - STATIC_ASSERT(JSObject::kMapOffset % (2 * kPointerSize) == 0); - if (i != JSObject::kMapOffset) { - // The map was already loaded into edx. - __ movq(rdx, FieldOperand(rax, i)); - } - __ movq(rcx, FieldOperand(rax, i + kPointerSize)); - - STATIC_ASSERT(JSObject::kElementsOffset % (2 * kPointerSize) == 0); - if (i == JSObject::kElementsOffset) { - // If the elements array isn't empty, make it copy-on-write - // before copying it. - Label empty; - __ CompareRoot(rdx, Heap::kEmptyFixedArrayRootIndex); - __ j(equal, &empty); - __ LoadRoot(kScratchRegister, Heap::kFixedCOWArrayMapRootIndex); - __ movq(FieldOperand(rdx, HeapObject::kMapOffset), kScratchRegister); - __ bind(&empty); - } - __ movq(FieldOperand(rbx, i), rdx); - __ movq(FieldOperand(rbx, i + kPointerSize), rcx); - } - __ movq(rax, rbx); - - __ bind(&done); - } - frame_->Push(rax); -} - - class DeferredSearchCache: public DeferredCode { public: DeferredSearchCache(Register dst, @@ -7299,7 +7230,7 @@ void CodeGenerator::VisitCallRuntime(CallRuntime* node) { // Push the builtins object found in the current global object. Result temp = allocator()->Allocate(); ASSERT(temp.is_valid()); - __ movq(temp.reg(), GlobalObject()); + __ movq(temp.reg(), GlobalObjectOperand()); __ movq(temp.reg(), FieldOperand(temp.reg(), GlobalObject::kBuiltinsOffset)); frame_->Push(&temp); diff --git a/deps/v8/src/x64/codegen-x64.h b/deps/v8/src/x64/codegen-x64.h index 795732451c..c3270add26 100644 --- a/deps/v8/src/x64/codegen-x64.h +++ b/deps/v8/src/x64/codegen-x64.h @@ -341,10 +341,6 @@ class CodeGenerator: public AstVisitor { bool in_spilled_code() const { return in_spilled_code_; } void set_in_spilled_code(bool flag) { in_spilled_code_ = flag; } - static Operand ContextOperand(Register context, int index) { - return Operand(context, Context::SlotOffset(index)); - } - private: // Type of a member function that generates inline code for a native function. typedef void (CodeGenerator::*InlineFunctionGenerator) @@ -417,10 +413,6 @@ class CodeGenerator: public AstVisitor { JumpTarget* slow); // Expressions - static Operand GlobalObject() { - return ContextOperand(rsi, Context::GLOBAL_INDEX); - } - void LoadCondition(Expression* x, ControlDestination* destination, bool force_control); @@ -588,10 +580,6 @@ class CodeGenerator: public AstVisitor { void ProcessDeclarations(ZoneList<Declaration*>* declarations); - static Handle<Code> ComputeCallInitialize(int argc, InLoopFlag in_loop); - - static Handle<Code> ComputeKeyedCallInitialize(int argc, InLoopFlag in_loop); - // Declare global variables and functions in the given array of // name/value pairs. void DeclareGlobals(Handle<FixedArray> pairs); @@ -656,8 +644,6 @@ class CodeGenerator: public AstVisitor { void GenerateRegExpConstructResult(ZoneList<Expression*>* args); - void GenerateRegExpCloneResult(ZoneList<Expression*>* args); - // Support for fast native caches. void GenerateGetFromCache(ZoneList<Expression*>* args); diff --git a/deps/v8/src/x64/full-codegen-x64.cc b/deps/v8/src/x64/full-codegen-x64.cc index 4e0f6d4b62..e4b24ff4cd 100644 --- a/deps/v8/src/x64/full-codegen-x64.cc +++ b/deps/v8/src/x64/full-codegen-x64.cc @@ -36,6 +36,7 @@ #include "full-codegen.h" #include "parser.h" #include "scopes.h" +#include "stub-cache.h" namespace v8 { namespace internal { @@ -912,7 +913,7 @@ void FullCodeGenerator::EmitLoadGlobalSlotCheckExtensions( // All extension objects were empty and it is safe to use a global // load IC call. - __ movq(rax, CodeGenerator::GlobalObject()); + __ movq(rax, GlobalObjectOperand()); __ Move(rcx, slot->var()->name()); Handle<Code> ic(Builtins::builtin(Builtins::LoadIC_Initialize)); RelocInfo::Mode mode = (typeof_state == INSIDE_TYPEOF) @@ -1016,7 +1017,7 @@ void FullCodeGenerator::EmitVariableLoad(Variable* var) { // Use inline caching. Variable name is passed in rcx and the global // object on the stack. __ Move(rcx, var->name()); - __ movq(rax, CodeGenerator::GlobalObject()); + __ movq(rax, GlobalObjectOperand()); Handle<Code> ic(Builtins::builtin(Builtins::LoadIC_Initialize)); EmitCallIC(ic, RelocInfo::CODE_TARGET_CONTEXT); context()->Plug(rax); @@ -1555,7 +1556,7 @@ void FullCodeGenerator::EmitVariableAssignment(Variable* var, // assignment. Right-hand-side value is passed in rax, variable name in // rcx, and the global object on the stack. __ Move(rcx, var->name()); - __ movq(rdx, CodeGenerator::GlobalObject()); + __ movq(rdx, GlobalObjectOperand()); Handle<Code> ic(Builtins::builtin(Builtins::StoreIC_Initialize)); EmitCallIC(ic, RelocInfo::CODE_TARGET); @@ -1717,16 +1718,17 @@ void FullCodeGenerator::EmitCallWithIC(Call* expr, // Code common for calls using the IC. ZoneList<Expression*>* args = expr->arguments(); int arg_count = args->length(); - for (int i = 0; i < arg_count; i++) { - VisitForStackValue(args->at(i)); + { PreserveStatementPositionScope scope(masm()->positions_recorder()); + for (int i = 0; i < arg_count; i++) { + VisitForStackValue(args->at(i)); + } + __ Move(rcx, name); } - __ Move(rcx, name); // Record source position for debugger. - SetSourcePosition(expr->position()); + SetSourcePosition(expr->position(), FORCED_POSITION); // Call the IC initialization code. InLoopFlag in_loop = (loop_depth() > 0) ? IN_LOOP : NOT_IN_LOOP; - Handle<Code> ic = CodeGenerator::ComputeCallInitialize(arg_count, - in_loop); + Handle<Code> ic = StubCache::ComputeCallInitialize(arg_count, in_loop); EmitCallIC(ic, mode); // Restore context register. __ movq(rsi, Operand(rbp, StandardFrameConstants::kContextOffset)); @@ -1737,24 +1739,33 @@ void FullCodeGenerator::EmitCallWithIC(Call* expr, void FullCodeGenerator::EmitKeyedCallWithIC(Call* expr, Expression* key, RelocInfo::Mode mode) { - // Code common for calls using the IC. + // Load the key. + VisitForAccumulatorValue(key); + + // Swap the name of the function and the receiver on the stack to follow + // the calling convention for call ICs. + __ pop(rcx); + __ push(rax); + __ push(rcx); + + // Load the arguments. ZoneList<Expression*>* args = expr->arguments(); int arg_count = args->length(); - for (int i = 0; i < arg_count; i++) { - VisitForStackValue(args->at(i)); + { PreserveStatementPositionScope scope(masm()->positions_recorder()); + for (int i = 0; i < arg_count; i++) { + VisitForStackValue(args->at(i)); + } } - VisitForAccumulatorValue(key); - __ movq(rcx, rax); // Record source position for debugger. - SetSourcePosition(expr->position()); + SetSourcePosition(expr->position(), FORCED_POSITION); // Call the IC initialization code. InLoopFlag in_loop = (loop_depth() > 0) ? IN_LOOP : NOT_IN_LOOP; - Handle<Code> ic = CodeGenerator::ComputeKeyedCallInitialize(arg_count, - in_loop); + Handle<Code> ic = StubCache::ComputeKeyedCallInitialize(arg_count, in_loop); + __ movq(rcx, Operand(rsp, (arg_count + 1) * kPointerSize)); // Key. EmitCallIC(ic, mode); // Restore context register. __ movq(rsi, Operand(rbp, StandardFrameConstants::kContextOffset)); - context()->Plug(rax); + context()->DropAndPlug(1, rax); // Drop the key still on the stack. } @@ -1762,11 +1773,13 @@ void FullCodeGenerator::EmitCallWithStub(Call* expr) { // Code common for calls using the call stub. ZoneList<Expression*>* args = expr->arguments(); int arg_count = args->length(); - for (int i = 0; i < arg_count; i++) { - VisitForStackValue(args->at(i)); + { PreserveStatementPositionScope scope(masm()->positions_recorder()); + for (int i = 0; i < arg_count; i++) { + VisitForStackValue(args->at(i)); + } } // Record source position for debugger. - SetSourcePosition(expr->position()); + SetSourcePosition(expr->position(), FORCED_POSITION); InLoopFlag in_loop = (loop_depth() > 0) ? IN_LOOP : NOT_IN_LOOP; CallFunctionStub stub(arg_count, in_loop, RECEIVER_MIGHT_BE_VALUE); __ CallStub(&stub); @@ -1787,37 +1800,38 @@ void FullCodeGenerator::VisitCall(Call* expr) { // resolve the function we need to call and the receiver of the // call. The we call the resolved function using the given // arguments. - VisitForStackValue(fun); - __ PushRoot(Heap::kUndefinedValueRootIndex); // Reserved receiver slot. - - // Push the arguments. ZoneList<Expression*>* args = expr->arguments(); int arg_count = args->length(); - for (int i = 0; i < arg_count; i++) { - VisitForStackValue(args->at(i)); - } + { PreserveStatementPositionScope pos_scope(masm()->positions_recorder()); + VisitForStackValue(fun); + __ PushRoot(Heap::kUndefinedValueRootIndex); // Reserved receiver slot. - // Push copy of the function - found below the arguments. - __ push(Operand(rsp, (arg_count + 1) * kPointerSize)); + // Push the arguments. + for (int i = 0; i < arg_count; i++) { + VisitForStackValue(args->at(i)); + } - // Push copy of the first argument or undefined if it doesn't exist. - if (arg_count > 0) { - __ push(Operand(rsp, arg_count * kPointerSize)); - } else { - __ PushRoot(Heap::kUndefinedValueRootIndex); - } + // Push copy of the function - found below the arguments. + __ push(Operand(rsp, (arg_count + 1) * kPointerSize)); - // Push the receiver of the enclosing function and do runtime call. - __ push(Operand(rbp, (2 + scope()->num_parameters()) * kPointerSize)); - __ CallRuntime(Runtime::kResolvePossiblyDirectEval, 3); + // Push copy of the first argument or undefined if it doesn't exist. + if (arg_count > 0) { + __ push(Operand(rsp, arg_count * kPointerSize)); + } else { + __ PushRoot(Heap::kUndefinedValueRootIndex); + } - // The runtime call returns a pair of values in rax (function) and - // rdx (receiver). Touch up the stack with the right values. - __ movq(Operand(rsp, (arg_count + 0) * kPointerSize), rdx); - __ movq(Operand(rsp, (arg_count + 1) * kPointerSize), rax); + // Push the receiver of the enclosing function and do runtime call. + __ push(Operand(rbp, (2 + scope()->num_parameters()) * kPointerSize)); + __ CallRuntime(Runtime::kResolvePossiblyDirectEval, 3); + // The runtime call returns a pair of values in rax (function) and + // rdx (receiver). Touch up the stack with the right values. + __ movq(Operand(rsp, (arg_count + 0) * kPointerSize), rdx); + __ movq(Operand(rsp, (arg_count + 1) * kPointerSize), rax); + } // Record source position for debugger. - SetSourcePosition(expr->position()); + SetSourcePosition(expr->position(), FORCED_POSITION); InLoopFlag in_loop = (loop_depth() > 0) ? IN_LOOP : NOT_IN_LOOP; CallFunctionStub stub(arg_count, in_loop, RECEIVER_MIGHT_BE_VALUE); __ CallStub(&stub); @@ -1827,42 +1841,44 @@ void FullCodeGenerator::VisitCall(Call* expr) { } else if (var != NULL && !var->is_this() && var->is_global()) { // Call to a global variable. // Push global object as receiver for the call IC lookup. - __ push(CodeGenerator::GlobalObject()); + __ push(GlobalObjectOperand()); EmitCallWithIC(expr, var->name(), RelocInfo::CODE_TARGET_CONTEXT); } else if (var != NULL && var->AsSlot() != NULL && var->AsSlot()->type() == Slot::LOOKUP) { // Call to a lookup slot (dynamically introduced variable). Label slow, done; - // Generate code for loading from variables potentially shadowed - // by eval-introduced variables. - EmitDynamicLoadFromSlotFastCase(var->AsSlot(), - NOT_INSIDE_TYPEOF, - &slow, - &done); - - __ bind(&slow); - // Call the runtime to find the function to call (returned in rax) - // and the object holding it (returned in rdx). - __ push(context_register()); - __ Push(var->name()); - __ CallRuntime(Runtime::kLoadContextSlot, 2); - __ push(rax); // Function. - __ push(rdx); // Receiver. - - // If fast case code has been generated, emit code to push the - // function and receiver and have the slow path jump around this - // code. - if (done.is_linked()) { - NearLabel call; - __ jmp(&call); - __ bind(&done); - // Push function. - __ push(rax); - // Push global receiver. - __ movq(rbx, CodeGenerator::GlobalObject()); - __ push(FieldOperand(rbx, GlobalObject::kGlobalReceiverOffset)); - __ bind(&call); + { PreserveStatementPositionScope scope(masm()->positions_recorder()); + // Generate code for loading from variables potentially shadowed + // by eval-introduced variables. + EmitDynamicLoadFromSlotFastCase(var->AsSlot(), + NOT_INSIDE_TYPEOF, + &slow, + &done); + + __ bind(&slow); + // Call the runtime to find the function to call (returned in rax) + // and the object holding it (returned in rdx). + __ push(context_register()); + __ Push(var->name()); + __ CallRuntime(Runtime::kLoadContextSlot, 2); + __ push(rax); // Function. + __ push(rdx); // Receiver. + + // If fast case code has been generated, emit code to push the + // function and receiver and have the slow path jump around this + // code. + if (done.is_linked()) { + NearLabel call; + __ jmp(&call); + __ bind(&done); + // Push function. + __ push(rax); + // Push global receiver. + __ movq(rbx, GlobalObjectOperand()); + __ push(FieldOperand(rbx, GlobalObject::kGlobalReceiverOffset)); + __ bind(&call); + } } EmitCallWithStub(expr); @@ -1873,18 +1889,24 @@ void FullCodeGenerator::VisitCall(Call* expr) { Literal* key = prop->key()->AsLiteral(); if (key != NULL && key->handle()->IsSymbol()) { // Call to a named property, use call IC. - VisitForStackValue(prop->obj()); + { PreserveStatementPositionScope scope(masm()->positions_recorder()); + VisitForStackValue(prop->obj()); + } EmitCallWithIC(expr, key->handle(), RelocInfo::CODE_TARGET); } else { // Call to a keyed property. // For a synthetic property use keyed load IC followed by function call, // for a regular property use KeyedCallIC. - VisitForStackValue(prop->obj()); + { PreserveStatementPositionScope scope(masm()->positions_recorder()); + VisitForStackValue(prop->obj()); + } if (prop->is_synthetic()) { - VisitForAccumulatorValue(prop->key()); - __ movq(rdx, Operand(rsp, 0)); + { PreserveStatementPositionScope scope(masm()->positions_recorder()); + VisitForAccumulatorValue(prop->key()); + __ movq(rdx, Operand(rsp, 0)); + } // Record source code position for IC call. - SetSourcePosition(prop->position()); + SetSourcePosition(prop->position(), FORCED_POSITION); Handle<Code> ic(Builtins::builtin(Builtins::KeyedLoadIC_Initialize)); EmitCallIC(ic, RelocInfo::CODE_TARGET); // Pop receiver. @@ -1892,7 +1914,7 @@ void FullCodeGenerator::VisitCall(Call* expr) { // Push result (function). __ push(rax); // Push receiver object on stack. - __ movq(rcx, CodeGenerator::GlobalObject()); + __ movq(rcx, GlobalObjectOperand()); __ push(FieldOperand(rcx, GlobalObject::kGlobalReceiverOffset)); EmitCallWithStub(expr); } else { @@ -1909,9 +1931,11 @@ void FullCodeGenerator::VisitCall(Call* expr) { loop_depth() == 0) { lit->set_try_full_codegen(true); } - VisitForStackValue(fun); + { PreserveStatementPositionScope scope(masm()->positions_recorder()); + VisitForStackValue(fun); + } // Load global receiver object. - __ movq(rbx, CodeGenerator::GlobalObject()); + __ movq(rbx, GlobalObjectOperand()); __ push(FieldOperand(rbx, GlobalObject::kGlobalReceiverOffset)); // Emit function call. EmitCallWithStub(expr); @@ -2784,7 +2808,7 @@ void FullCodeGenerator::VisitCallRuntime(CallRuntime* expr) { if (expr->is_jsruntime()) { // Prepare for calling JS runtime function. - __ movq(rax, CodeGenerator::GlobalObject()); + __ movq(rax, GlobalObjectOperand()); __ push(FieldOperand(rax, GlobalObject::kBuiltinsOffset)); } @@ -2798,7 +2822,7 @@ void FullCodeGenerator::VisitCallRuntime(CallRuntime* expr) { // Call the JS runtime function using a call IC. __ Move(rcx, expr->name()); InLoopFlag in_loop = (loop_depth() > 0) ? IN_LOOP : NOT_IN_LOOP; - Handle<Code> ic = CodeGenerator::ComputeCallInitialize(arg_count, in_loop); + Handle<Code> ic = StubCache::ComputeCallInitialize(arg_count, in_loop); EmitCallIC(ic, RelocInfo::CODE_TARGET); // Restore context register. __ movq(rsi, Operand(rbp, StandardFrameConstants::kContextOffset)); @@ -2834,7 +2858,7 @@ void FullCodeGenerator::VisitUnaryOperation(UnaryOperation* expr) { VisitForStackValue(prop->obj()); VisitForStackValue(prop->key()); } else if (var->is_global()) { - __ push(CodeGenerator::GlobalObject()); + __ push(GlobalObjectOperand()); __ Push(var->name()); } else { // Non-global variable. Call the runtime to look up the context @@ -3105,7 +3129,7 @@ void FullCodeGenerator::VisitForTypeofValue(Expression* expr) { if (proxy != NULL && !proxy->var()->is_this() && proxy->var()->is_global()) { Comment cmnt(masm_, "Global variable"); __ Move(rcx, proxy->name()); - __ movq(rax, CodeGenerator::GlobalObject()); + __ movq(rax, GlobalObjectOperand()); Handle<Code> ic(Builtins::builtin(Builtins::LoadIC_Initialize)); // Use a regular load, not a contextual load, to avoid a reference // error. diff --git a/deps/v8/src/x64/ic-x64.cc b/deps/v8/src/x64/ic-x64.cc index 1d95b7f663..9ec781487b 100644 --- a/deps/v8/src/x64/ic-x64.cc +++ b/deps/v8/src/x64/ic-x64.cc @@ -33,7 +33,6 @@ #include "ic-inl.h" #include "runtime.h" #include "stub-cache.h" -#include "utils.h" namespace v8 { namespace internal { diff --git a/deps/v8/src/x64/macro-assembler-x64.cc b/deps/v8/src/x64/macro-assembler-x64.cc index 293d8a5633..834a6f6ef5 100644 --- a/deps/v8/src/x64/macro-assembler-x64.cc +++ b/deps/v8/src/x64/macro-assembler-x64.cc @@ -327,7 +327,7 @@ MaybeObject* MacroAssembler::TryCallStub(CodeStub* stub) { void MacroAssembler::TailCallStub(CodeStub* stub) { - ASSERT(allow_stub_calls()); // calls are not allowed in some stubs + ASSERT(allow_stub_calls()); // Calls are not allowed in some stubs. Jump(stub->GetCode(), RelocInfo::CODE_TARGET); } @@ -456,6 +456,24 @@ void MacroAssembler::TailCallExternalReference(const ExternalReference& ext, } +MaybeObject* MacroAssembler::TryTailCallExternalReference( + const ExternalReference& ext, int num_arguments, int result_size) { + // ----------- S t a t e ------------- + // -- rsp[0] : return address + // -- rsp[8] : argument num_arguments - 1 + // ... + // -- rsp[8 * num_arguments] : argument 0 (receiver) + // ----------------------------------- + + // TODO(1236192): Most runtime routines don't need the number of + // arguments passed in because it is constant. At some point we + // should remove this need and make the runtime routine entry code + // smarter. + Set(rax, num_arguments); + return TryJumpToExternalReference(ext, result_size); +} + + void MacroAssembler::TailCallRuntime(Runtime::FunctionId fid, int num_arguments, int result_size) { @@ -463,6 +481,15 @@ void MacroAssembler::TailCallRuntime(Runtime::FunctionId fid, } +MaybeObject* MacroAssembler::TryTailCallRuntime(Runtime::FunctionId fid, + int num_arguments, + int result_size) { + return TryTailCallExternalReference(ExternalReference(fid), + num_arguments, + result_size); +} + + static int Offset(ExternalReference ref0, ExternalReference ref1) { int64_t offset = (ref0.address() - ref1.address()); // Check that fits into int. @@ -471,12 +498,22 @@ static int Offset(ExternalReference ref0, ExternalReference ref1) { } -void MacroAssembler::PrepareCallApiFunction(int stack_space) { - EnterApiExitFrame(stack_space, 0); +void MacroAssembler::PrepareCallApiFunction(int arg_stack_space) { +#ifdef _WIN64 + // We need to prepare a slot for result handle on stack and put + // a pointer to it into 1st arg register. + EnterApiExitFrame(arg_stack_space + 1); + + // rcx must be used to pass the pointer to the return value slot. + lea(rcx, StackSpaceOperand(arg_stack_space)); +#else + EnterApiExitFrame(arg_stack_space); +#endif } -void MacroAssembler::CallApiFunctionAndReturn(ApiFunction* function) { +MaybeObject* MacroAssembler::TryCallApiFunctionAndReturn( + ApiFunction* function, int stack_space) { Label empty_result; Label prologue; Label promote_scheduled_exception; @@ -499,7 +536,7 @@ void MacroAssembler::CallApiFunctionAndReturn(ApiFunction* function) { // Allocate HandleScope in callee-save registers. Register prev_next_address_reg = r14; Register prev_limit_reg = rbx; - Register base_reg = kSmiConstantRegister; + Register base_reg = r12; movq(base_reg, next_address); movq(prev_next_address_reg, Operand(base_reg, kNextOffset)); movq(prev_limit_reg, Operand(base_reg, kLimitOffset)); @@ -528,18 +565,21 @@ void MacroAssembler::CallApiFunctionAndReturn(ApiFunction* function) { cmpq(prev_limit_reg, Operand(base_reg, kLimitOffset)); j(not_equal, &delete_allocated_handles); bind(&leave_exit_frame); - InitializeSmiConstantRegister(); // Check if the function scheduled an exception. movq(rsi, scheduled_exception_address); Cmp(Operand(rsi, 0), Factory::the_hole_value()); j(not_equal, &promote_scheduled_exception); - LeaveExitFrame(); - ret(0); + LeaveApiExitFrame(); + ret(stack_space * kPointerSize); bind(&promote_scheduled_exception); - TailCallRuntime(Runtime::kPromoteScheduledException, 0, 1); + MaybeObject* result = TryTailCallRuntime(Runtime::kPromoteScheduledException, + 0, 1); + if (result->IsFailure()) { + return result; + } bind(&empty_result); // It was zero; the result is undefined. @@ -554,6 +594,8 @@ void MacroAssembler::CallApiFunctionAndReturn(ApiFunction* function) { call(rax); movq(rax, prev_limit_reg); jmp(&leave_exit_frame); + + return result; } @@ -566,6 +608,15 @@ void MacroAssembler::JumpToExternalReference(const ExternalReference& ext, } +MaybeObject* MacroAssembler::TryJumpToExternalReference( + const ExternalReference& ext, int result_size) { + // Set the entry point and jump to the C entry runtime stub. + movq(rbx, ext); + CEntryStub ces(result_size); + return TryTailCallStub(&ces); +} + + void MacroAssembler::InvokeBuiltin(Builtins::JavaScript id, InvokeFlag flag) { // Calls are not allowed in some stubs. ASSERT(flag == JUMP_FUNCTION || allow_stub_calls()); @@ -1690,22 +1741,15 @@ void MacroAssembler::EnterExitFramePrologue(bool save_rax) { store_rax(context_address); } -void MacroAssembler::EnterExitFrameEpilogue(int result_size, - int argc) { + +void MacroAssembler::EnterExitFrameEpilogue(int arg_stack_space) { #ifdef _WIN64 - // Reserve space on stack for result and argument structures, if necessary. - int result_stack_space = (result_size < 2) ? 0 : result_size * kPointerSize; - // Reserve space for the Arguments object. The Windows 64-bit ABI - // requires us to pass this structure as a pointer to its location on - // the stack. The structure contains 2 values. - int argument_stack_space = argc * kPointerSize; - // We also need backing space for 4 parameters, even though - // we only pass one or two parameter, and it is in a register. - int argument_mirror_space = 4 * kPointerSize; - int total_stack_space = - argument_mirror_space + argument_stack_space + result_stack_space; - subq(rsp, Immediate(total_stack_space)); + const int kShaddowSpace = 4; + arg_stack_space += kShaddowSpace; #endif + if (arg_stack_space > 0) { + subq(rsp, Immediate(arg_stack_space * kPointerSize)); + } // Get the required frame alignment for the OS. static const int kFrameAlignment = OS::ActivationFrameAlignment(); @@ -1720,7 +1764,7 @@ void MacroAssembler::EnterExitFrameEpilogue(int result_size, } -void MacroAssembler::EnterExitFrame(int result_size) { +void MacroAssembler::EnterExitFrame(int arg_stack_space) { EnterExitFramePrologue(true); // Setup argv in callee-saved register r12. It is reused in LeaveExitFrame, @@ -1728,25 +1772,17 @@ void MacroAssembler::EnterExitFrame(int result_size) { int offset = StandardFrameConstants::kCallerSPOffset - kPointerSize; lea(r12, Operand(rbp, r14, times_pointer_size, offset)); - EnterExitFrameEpilogue(result_size, 2); + EnterExitFrameEpilogue(arg_stack_space); } -void MacroAssembler::EnterApiExitFrame(int stack_space, - int argc, - int result_size) { +void MacroAssembler::EnterApiExitFrame(int arg_stack_space) { EnterExitFramePrologue(false); - - // Setup argv in callee-saved register r12. It is reused in LeaveExitFrame, - // so it must be retained across the C-call. - int offset = StandardFrameConstants::kCallerSPOffset - kPointerSize; - lea(r12, Operand(rbp, (stack_space * kPointerSize) + offset)); - - EnterExitFrameEpilogue(result_size, argc); + EnterExitFrameEpilogue(arg_stack_space); } -void MacroAssembler::LeaveExitFrame(int result_size) { +void MacroAssembler::LeaveExitFrame() { // Registers: // r12 : argv @@ -1758,6 +1794,22 @@ void MacroAssembler::LeaveExitFrame(int result_size) { // from the caller stack. lea(rsp, Operand(r12, 1 * kPointerSize)); + // Push the return address to get ready to return. + push(rcx); + + LeaveExitFrameEpilogue(); +} + + +void MacroAssembler::LeaveApiExitFrame() { + movq(rsp, rbp); + pop(rbp); + + LeaveExitFrameEpilogue(); +} + + +void MacroAssembler::LeaveExitFrameEpilogue() { // Restore current context from top and clear it in debug mode. ExternalReference context_address(Top::k_context_address); movq(kScratchRegister, context_address); @@ -1766,9 +1818,6 @@ void MacroAssembler::LeaveExitFrame(int result_size) { movq(Operand(kScratchRegister, 0), Immediate(0)); #endif - // Push the return address to get ready to return. - push(rcx); - // Clear the top frame. ExternalReference c_entry_fp_address(Top::k_c_entry_fp_address); movq(kScratchRegister, c_entry_fp_address); diff --git a/deps/v8/src/x64/macro-assembler-x64.h b/deps/v8/src/x64/macro-assembler-x64.h index 2f6e956165..5b082fd627 100644 --- a/deps/v8/src/x64/macro-assembler-x64.h +++ b/deps/v8/src/x64/macro-assembler-x64.h @@ -155,17 +155,23 @@ class MacroAssembler: public Assembler { // debug mode. Expects the number of arguments in register rax and // sets up the number of arguments in register rdi and the pointer // to the first argument in register rsi. - void EnterExitFrame(int result_size = 1); + // + // Allocates arg_stack_space * kPointerSize memory (not GCed) on the stack + // accessible via StackSpaceOperand. + void EnterExitFrame(int arg_stack_space = 0); - void EnterApiExitFrame(int stack_space, - int argc, - int result_size = 1); + // Enter specific kind of exit frame. Allocates arg_stack_space * kPointerSize + // memory (not GCed) on the stack accessible via StackSpaceOperand. + void EnterApiExitFrame(int arg_stack_space); // Leave the current exit frame. Expects/provides the return value in // register rax:rdx (untouched) and the pointer to the first // argument in register rsi. - void LeaveExitFrame(int result_size = 1); + void LeaveExitFrame(); + // Leave the current exit frame. Expects/provides the return value in + // register rax (untouched). + void LeaveApiExitFrame(); // --------------------------------------------------------------------------- // JavaScript invokes @@ -813,22 +819,38 @@ class MacroAssembler: public Assembler { int num_arguments, int result_size); + MUST_USE_RESULT MaybeObject* TryTailCallExternalReference( + const ExternalReference& ext, int num_arguments, int result_size); + // Convenience function: tail call a runtime routine (jump). void TailCallRuntime(Runtime::FunctionId fid, int num_arguments, int result_size); + MUST_USE_RESULT MaybeObject* TryTailCallRuntime(Runtime::FunctionId fid, + int num_arguments, + int result_size); + // Jump to a runtime routine. void JumpToExternalReference(const ExternalReference& ext, int result_size); - // Prepares stack to put arguments (aligns and so on). - // Uses calle-saved esi to restore stack state after call. - void PrepareCallApiFunction(int stack_space); + // Jump to a runtime routine. + MaybeObject* TryJumpToExternalReference(const ExternalReference& ext, + int result_size); - // Tail call an API function (jump). Allocates HandleScope, extracts - // returned value from handle and propogates exceptions. - // Clobbers ebx, edi and caller-save registers. - void CallApiFunctionAndReturn(ApiFunction* function); + // Prepares stack to put arguments (aligns and so on). + // WIN64 calling convention requires to put the pointer to the return value + // slot into rcx (rcx must be preserverd until TryCallApiFunctionAndReturn). + // Saves context (rsi). Clobbers rax. Allocates arg_stack_space * kPointerSize + // inside the exit frame (not GCed) accessible via StackSpaceOperand. + void PrepareCallApiFunction(int arg_stack_space); + + // Calls an API function. Allocates HandleScope, extracts + // returned value from handle and propagates exceptions. + // Clobbers r12, r14, rbx and caller-save registers. Restores context. + // On return removes stack_space * kPointerSize (GCed). + MUST_USE_RESULT MaybeObject* TryCallApiFunctionAndReturn( + ApiFunction* function, int stack_space); // Before calling a C-function from generated code, align arguments on stack. // After aligning the frame, arguments must be stored in esp[0], esp[4], @@ -919,7 +941,12 @@ class MacroAssembler: public Assembler { void LeaveFrame(StackFrame::Type type); void EnterExitFramePrologue(bool save_rax); - void EnterExitFrameEpilogue(int result_size, int argc); + + // Allocates arg_stack_space * kPointerSize memory (not GCed) on the stack + // accessible via StackSpaceOperand. + void EnterExitFrameEpilogue(int arg_stack_space); + + void LeaveExitFrameEpilogue(); // Allocation support helpers. // Loads the top of new-space into the result register. @@ -982,6 +1009,28 @@ static inline Operand FieldOperand(Register object, } +static inline Operand ContextOperand(Register context, int index) { + return Operand(context, Context::SlotOffset(index)); +} + + +static inline Operand GlobalObjectOperand() { + return ContextOperand(rsi, Context::GLOBAL_INDEX); +} + + +// Provides access to exit frame stack space (not GCed). +static inline Operand StackSpaceOperand(int index) { +#ifdef _WIN64 + const int kShaddowSpace = 4; + return Operand(rsp, (index + kShaddowSpace) * kPointerSize); +#else + return Operand(rsp, index * kPointerSize); +#endif +} + + + #ifdef GENERATED_CODE_COVERAGE extern void LogGeneratedCodeCoverage(const char* file_line); #define CODE_COVERAGE_STRINGIFY(x) #x diff --git a/deps/v8/src/x64/stub-cache-x64.cc b/deps/v8/src/x64/stub-cache-x64.cc index 24609bf642..2e60dd53b4 100644 --- a/deps/v8/src/x64/stub-cache-x64.cc +++ b/deps/v8/src/x64/stub-cache-x64.cc @@ -497,8 +497,10 @@ void StubCompiler::GenerateLoadFunctionPrototype(MacroAssembler* masm, __ ret(0); } +// Number of pointers to be reserved on stack for fast API call. +static const int kFastApiCallArguments = 3; -// Reserves space for the extra arguments to FastHandleApiCall in the +// Reserves space for the extra arguments to API function in the // caller's frame. // // These arguments are set by CheckPrototypes and GenerateFastApiCall. @@ -508,48 +510,48 @@ static void ReserveSpaceForFastApiCall(MacroAssembler* masm, Register scratch) { // -- rsp[8] : last argument in the internal frame of the caller // ----------------------------------- __ movq(scratch, Operand(rsp, 0)); - __ subq(rsp, Immediate(4 * kPointerSize)); + __ subq(rsp, Immediate(kFastApiCallArguments * kPointerSize)); __ movq(Operand(rsp, 0), scratch); __ Move(scratch, Smi::FromInt(0)); - __ movq(Operand(rsp, 1 * kPointerSize), scratch); - __ movq(Operand(rsp, 2 * kPointerSize), scratch); - __ movq(Operand(rsp, 3 * kPointerSize), scratch); - __ movq(Operand(rsp, 4 * kPointerSize), scratch); + for (int i = 1; i <= kFastApiCallArguments; i++) { + __ movq(Operand(rsp, i * kPointerSize), scratch); + } } // Undoes the effects of ReserveSpaceForFastApiCall. static void FreeSpaceForFastApiCall(MacroAssembler* masm, Register scratch) { // ----------- S t a t e ------------- - // -- rsp[0] : return address - // -- rsp[8] : last fast api call extra argument + // -- rsp[0] : return address. + // -- rsp[8] : last fast api call extra argument. // -- ... - // -- rsp[32] : first fast api call extra argument - // -- rsp[40] : last argument in the internal frame + // -- rsp[kFastApiCallArguments * 8] : first fast api call extra argument. + // -- rsp[kFastApiCallArguments * 8 + 8] : last argument in the internal + // frame. // ----------------------------------- __ movq(scratch, Operand(rsp, 0)); - __ movq(Operand(rsp, 4 * kPointerSize), scratch); - __ addq(rsp, Immediate(kPointerSize * 4)); + __ movq(Operand(rsp, kFastApiCallArguments * kPointerSize), scratch); + __ addq(rsp, Immediate(kPointerSize * kFastApiCallArguments)); } -// Generates call to FastHandleApiCall builtin. -static void GenerateFastApiCall(MacroAssembler* masm, +// Generates call to API function. +static bool GenerateFastApiCall(MacroAssembler* masm, const CallOptimization& optimization, - int argc) { + int argc, + Failure** failure) { // ----------- S t a t e ------------- // -- rsp[0] : return address // -- rsp[8] : object passing the type check // (last fast api call extra argument, // set by CheckPrototypes) - // -- rsp[16] : api call data - // -- rsp[24] : api callback - // -- rsp[32] : api function + // -- rsp[16] : api function // (first fast api call extra argument) - // -- rsp[40] : last argument + // -- rsp[24] : api call data + // -- rsp[32] : last argument // -- ... - // -- rsp[(argc + 5) * 8] : first argument - // -- rsp[(argc + 6) * 8] : receiver + // -- rsp[(argc + 3) * 8] : first argument + // -- rsp[(argc + 4) * 8] : receiver // ----------------------------------- // Get the function and setup the context. @@ -557,38 +559,58 @@ static void GenerateFastApiCall(MacroAssembler* masm, __ Move(rdi, Handle<JSFunction>(function)); __ movq(rsi, FieldOperand(rdi, JSFunction::kContextOffset)); - // Pass the additional arguments FastHandleApiCall expects. - __ movq(Operand(rsp, 4 * kPointerSize), rdi); - bool info_loaded = false; - Object* callback = optimization.api_call_info()->callback(); - if (Heap::InNewSpace(callback)) { - info_loaded = true; - __ Move(rcx, Handle<CallHandlerInfo>(optimization.api_call_info())); - __ movq(rbx, FieldOperand(rcx, CallHandlerInfo::kCallbackOffset)); - __ movq(Operand(rsp, 3 * kPointerSize), rbx); - } else { - __ Move(Operand(rsp, 3 * kPointerSize), Handle<Object>(callback)); - } + // Pass the additional arguments. + __ movq(Operand(rsp, 2 * kPointerSize), rdi); Object* call_data = optimization.api_call_info()->data(); + Handle<CallHandlerInfo> api_call_info_handle(optimization.api_call_info()); if (Heap::InNewSpace(call_data)) { - if (!info_loaded) { - __ Move(rcx, Handle<CallHandlerInfo>(optimization.api_call_info())); - } + __ Move(rcx, api_call_info_handle); __ movq(rbx, FieldOperand(rcx, CallHandlerInfo::kDataOffset)); - __ movq(Operand(rsp, 2 * kPointerSize), rbx); + __ movq(Operand(rsp, 3 * kPointerSize), rbx); } else { - __ Move(Operand(rsp, 2 * kPointerSize), Handle<Object>(call_data)); + __ Move(Operand(rsp, 3 * kPointerSize), Handle<Object>(call_data)); } - // Set the number of arguments. - __ movq(rax, Immediate(argc + 4)); + // Prepare arguments. + __ lea(rbx, Operand(rsp, 3 * kPointerSize)); - // Jump to the fast api call builtin (tail call). - Handle<Code> code = Handle<Code>( - Builtins::builtin(Builtins::FastHandleApiCall)); - ParameterCount expected(0); - __ InvokeCode(code, expected, expected, - RelocInfo::CODE_TARGET, JUMP_FUNCTION); + Object* callback = optimization.api_call_info()->callback(); + Address api_function_address = v8::ToCData<Address>(callback); + ApiFunction fun(api_function_address); + +#ifdef _WIN64 + // Win64 uses first register--rcx--for returned value. + Register arguments_arg = rdx; +#else + Register arguments_arg = rdi; +#endif + + // Allocate the v8::Arguments structure in the arguments' space since + // it's not controlled by GC. + const int kApiStackSpace = 4; + + __ PrepareCallApiFunction(kApiStackSpace); + + __ movq(StackSpaceOperand(0), rbx); // v8::Arguments::implicit_args_. + __ addq(rbx, Immediate(argc * kPointerSize)); + __ movq(StackSpaceOperand(1), rbx); // v8::Arguments::values_. + __ Set(StackSpaceOperand(2), argc); // v8::Arguments::length_. + // v8::Arguments::is_construct_call_. + __ Set(StackSpaceOperand(3), 0); + + // v8::InvocationCallback's argument. + __ lea(arguments_arg, StackSpaceOperand(0)); + // Emitting a stub call may try to allocate (if the code is not + // already generated). Do not allow the assembler to perform a + // garbage collection but instead return the allocation failure + // object. + MaybeObject* result = + masm->TryCallApiFunctionAndReturn(&fun, argc + kFastApiCallArguments + 1); + if (result->IsFailure()) { + *failure = Failure::cast(result); + return false; + } + return true; } @@ -601,7 +623,7 @@ class CallInterceptorCompiler BASE_EMBEDDED { arguments_(arguments), name_(name) {} - void Compile(MacroAssembler* masm, + bool Compile(MacroAssembler* masm, JSObject* object, JSObject* holder, String* name, @@ -610,7 +632,8 @@ class CallInterceptorCompiler BASE_EMBEDDED { Register scratch1, Register scratch2, Register scratch3, - Label* miss) { + Label* miss, + Failure** failure) { ASSERT(holder->HasNamedInterceptor()); ASSERT(!holder->GetNamedInterceptor()->getter()->IsUndefined()); @@ -620,17 +643,18 @@ class CallInterceptorCompiler BASE_EMBEDDED { CallOptimization optimization(lookup); if (optimization.is_constant_call()) { - CompileCacheable(masm, - object, - receiver, - scratch1, - scratch2, - scratch3, - holder, - lookup, - name, - optimization, - miss); + return CompileCacheable(masm, + object, + receiver, + scratch1, + scratch2, + scratch3, + holder, + lookup, + name, + optimization, + miss, + failure); } else { CompileRegular(masm, object, @@ -641,11 +665,12 @@ class CallInterceptorCompiler BASE_EMBEDDED { name, holder, miss); + return true; } } private: - void CompileCacheable(MacroAssembler* masm, + bool CompileCacheable(MacroAssembler* masm, JSObject* object, Register receiver, Register scratch1, @@ -655,7 +680,8 @@ class CallInterceptorCompiler BASE_EMBEDDED { LookupResult* lookup, String* name, const CallOptimization& optimization, - Label* miss_label) { + Label* miss_label, + Failure** failure) { ASSERT(optimization.is_constant_call()); ASSERT(!lookup->holder()->IsGlobalObject()); @@ -717,7 +743,13 @@ class CallInterceptorCompiler BASE_EMBEDDED { // Invoke function. if (can_do_fast_api_call) { - GenerateFastApiCall(masm, optimization, arguments_.immediate()); + bool success = GenerateFastApiCall(masm, + optimization, + arguments_.immediate(), + failure); + if (!success) { + return false; + } } else { __ InvokeFunction(optimization.constant_function(), arguments_, JUMP_FUNCTION); @@ -735,6 +767,8 @@ class CallInterceptorCompiler BASE_EMBEDDED { if (can_do_fast_api_call) { FreeSpaceForFastApiCall(masm, scratch1); } + + return true; } void CompileRegular(MacroAssembler* masm, @@ -958,7 +992,9 @@ MaybeObject* CallStubCompiler::CompileCallConstant( if (depth != kInvalidProtoDepth) { __ IncrementCounter(&Counters::call_const_fast_api, 1); - ReserveSpaceForFastApiCall(masm(), rax); + // Allocate space for v8::Arguments implicit values. Must be initialized + // before to call any runtime function. + __ subq(rsp, Immediate(kFastApiCallArguments * kPointerSize)); } // Check that the maps haven't changed. @@ -1036,7 +1072,17 @@ MaybeObject* CallStubCompiler::CompileCallConstant( } if (depth != kInvalidProtoDepth) { - GenerateFastApiCall(masm(), optimization, argc); + Failure* failure; + // Move the return address on top of the stack. + __ movq(rax, Operand(rsp, 3 * kPointerSize)); + __ movq(Operand(rsp, 0 * kPointerSize), rax); + + // rsp[2 * kPointerSize] is uninitialized, rsp[3 * kPointerSize] contains + // duplicate of return address and will be overwritten. + bool success = GenerateFastApiCall(masm(), optimization, argc, &failure); + if (!success) { + return failure; + } } else { __ InvokeFunction(function, arguments(), JUMP_FUNCTION); } @@ -1044,7 +1090,7 @@ MaybeObject* CallStubCompiler::CompileCallConstant( // Handle call cache miss. __ bind(&miss); if (depth != kInvalidProtoDepth) { - FreeSpaceForFastApiCall(masm(), rax); + __ addq(rsp, Immediate(kFastApiCallArguments * kPointerSize)); } // Handle call cache miss. @@ -1723,16 +1769,21 @@ MaybeObject* CallStubCompiler::CompileCallInterceptor(JSObject* object, __ movq(rdx, Operand(rsp, (argc + 1) * kPointerSize)); CallInterceptorCompiler compiler(this, arguments(), rcx); - compiler.Compile(masm(), - object, - holder, - name, - &lookup, - rdx, - rbx, - rdi, - rax, - &miss); + Failure* failure; + bool success = compiler.Compile(masm(), + object, + holder, + name, + &lookup, + rdx, + rbx, + rdi, + rax, + &miss, + &failure); + if (!success) { + return failure; + } // Restore receiver. __ movq(rdx, Operand(rsp, (argc + 1) * kPointerSize)); @@ -1844,7 +1895,7 @@ MaybeObject* LoadStubCompiler::CompileLoadCallback(String* name, Label miss; Failure* failure = Failure::InternalError(); - bool success = GenerateLoadCallback(object, holder, rax, rcx, rbx, rdx, rdi, + bool success = GenerateLoadCallback(object, holder, rax, rcx, rdx, rbx, rdi, callback, name, &miss, &failure); if (!success) { miss.Unuse(); @@ -2585,16 +2636,15 @@ bool StubCompiler::GenerateLoadCallback(JSObject* object, Handle<AccessorInfo> callback_handle(callback); - __ EnterInternalFrame(); - // Push the stack address where the list of arguments ends. - __ movq(scratch2, rsp); - __ subq(scratch2, Immediate(2 * kPointerSize)); - __ push(scratch2); + // Insert additional parameters into the stack frame above return address. + ASSERT(!scratch2.is(reg)); + __ pop(scratch2); // Get return address to place it below. + __ push(receiver); // receiver __ push(reg); // holder if (Heap::InNewSpace(callback_handle->data())) { - __ Move(scratch2, callback_handle); - __ push(FieldOperand(scratch2, AccessorInfo::kDataOffset)); // data + __ Move(scratch1, callback_handle); + __ push(FieldOperand(scratch1, AccessorInfo::kDataOffset)); // data } else { __ Push(Handle<Object>(callback_handle->data())); } @@ -2607,42 +2657,43 @@ bool StubCompiler::GenerateLoadCallback(JSObject* object, Register accessor_info_arg = r8; Register name_arg = rdx; #else - Register accessor_info_arg = rdx; // temporary, copied to rsi by the stub. + Register accessor_info_arg = rsi; Register name_arg = rdi; #endif - __ movq(accessor_info_arg, rsp); - __ addq(accessor_info_arg, Immediate(4 * kPointerSize)); + ASSERT(!name_arg.is(scratch2)); __ movq(name_arg, rsp); + __ push(scratch2); // Restore return address. // Do call through the api. - ASSERT_EQ(5, ApiGetterEntryStub::kStackSpace); Address getter_address = v8::ToCData<Address>(callback->getter()); ApiFunction fun(getter_address); - ApiGetterEntryStub stub(callback_handle, &fun); -#ifdef _WIN64 - // We need to prepare a slot for result handle on stack and put - // a pointer to it into 1st arg register. - __ push(Immediate(0)); - __ movq(rcx, rsp); -#endif + + // 3 elements array for v8::Agruments::values_ and handler for name. + const int kStackSpace = 4; + + // Allocate v8::AccessorInfo in non-GCed stack space. + const int kArgStackSpace = 1; + + __ PrepareCallApiFunction(kArgStackSpace); + __ lea(rax, Operand(name_arg, 3 * kPointerSize)); + + // v8::AccessorInfo::args_. + __ movq(StackSpaceOperand(0), rax); + + // The context register (rsi) has been saved in PrepareCallApiFunction and + // could be used to pass arguments. + __ lea(accessor_info_arg, StackSpaceOperand(0)); + // Emitting a stub call may try to allocate (if the code is not // already generated). Do not allow the assembler to perform a // garbage collection but instead return the allocation failure // object. - MaybeObject* result = masm()->TryCallStub(&stub); + MaybeObject* result = masm()->TryCallApiFunctionAndReturn(&fun, kStackSpace); if (result->IsFailure()) { *failure = Failure::cast(result); return false; } -#ifdef _WIN64 - // Discard allocated slot. - __ addq(rsp, Immediate(kPointerSize)); -#endif - __ LeaveInternalFrame(); - - __ ret(0); - return true; } diff --git a/deps/v8/src/x64/virtual-frame-x64.cc b/deps/v8/src/x64/virtual-frame-x64.cc index e88a993b25..3f7b1db7e5 100644 --- a/deps/v8/src/x64/virtual-frame-x64.cc +++ b/deps/v8/src/x64/virtual-frame-x64.cc @@ -32,6 +32,7 @@ #include "codegen-inl.h" #include "register-allocator-inl.h" #include "scopes.h" +#include "stub-cache.h" #include "virtual-frame-inl.h" namespace v8 { @@ -1194,7 +1195,7 @@ Result VirtualFrame::CallCallIC(RelocInfo::Mode mode, // and dropped by the call. The IC expects the name in rcx and the rest // on the stack, and drops them all. InLoopFlag in_loop = loop_nesting > 0 ? IN_LOOP : NOT_IN_LOOP; - Handle<Code> ic = cgen()->ComputeCallInitialize(arg_count, in_loop); + Handle<Code> ic = StubCache::ComputeCallInitialize(arg_count, in_loop); Result name = Pop(); // Spill args, receiver, and function. The call will drop args and // receiver. @@ -1213,7 +1214,7 @@ Result VirtualFrame::CallKeyedCallIC(RelocInfo::Mode mode, // on the stack, and drops them all. InLoopFlag in_loop = loop_nesting > 0 ? IN_LOOP : NOT_IN_LOOP; Handle<Code> ic = - cgen()->ComputeKeyedCallInitialize(arg_count, in_loop); + StubCache::ComputeKeyedCallInitialize(arg_count, in_loop); Result name = Pop(); // Spill args, receiver, and function. The call will drop args and // receiver. |