diff options
author | Ryan Dahl <ry@tinyclouds.org> | 2010-11-11 22:42:06 -0800 |
---|---|---|
committer | Ryan Dahl <ry@tinyclouds.org> | 2010-11-11 22:42:06 -0800 |
commit | 564a48643bd3edc6da845e458277a54c8068d0e2 (patch) | |
tree | ea483922ae95ef2f1c206aaab2238bf58fd860df /deps/v8/src | |
parent | d4af8a6b6ac0045620ad6da94e97a71e6e6fad52 (diff) | |
download | node-564a48643bd3edc6da845e458277a54c8068d0e2.tar.gz |
Upgrade V8 to 2.5.6
Diffstat (limited to 'deps/v8/src')
77 files changed, 4664 insertions, 2548 deletions
diff --git a/deps/v8/src/SConscript b/deps/v8/src/SConscript index ef5485d85..ad647dfa0 100755 --- a/deps/v8/src/SConscript +++ b/deps/v8/src/SConscript @@ -40,6 +40,7 @@ SOURCES = { api.cc assembler.cc ast.cc + bignum.cc bootstrapper.cc builtins.cc cached-powers.cc @@ -95,6 +96,7 @@ SOURCES = { register-allocator.cc rewriter.cc runtime.cc + scanner-base.cc scanner.cc scopeinfo.cc scopes.cc diff --git a/deps/v8/src/api.cc b/deps/v8/src/api.cc index 617922dd5..9da3346d7 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" @@ -394,14 +393,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 +3263,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 ebbd9b113..4cb421c57 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 5b647a753..de3931c2c 100644 --- a/deps/v8/src/arm/assembler-arm.h +++ b/deps/v8/src/arm/assembler-arm.h @@ -448,6 +448,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 +1008,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 +1067,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 +1131,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 +1269,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 +1294,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 70ff24464..d7fd9a490 100644 --- a/deps/v8/src/arm/codegen-arm.cc +++ b/deps/v8/src/arm/codegen-arm.cc @@ -5496,73 +5496,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) diff --git a/deps/v8/src/arm/codegen-arm.h b/deps/v8/src/arm/codegen-arm.h index e6fd6071e..97e50b426 100644 --- a/deps/v8/src/arm/codegen-arm.h +++ b/deps/v8/src/arm/codegen-arm.h @@ -518,8 +518,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 123c5e797..36f6283c9 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/full-codegen-arm.cc b/deps/v8/src/arm/full-codegen-arm.cc index 9935e038f..bf27d0c7f 100644 --- a/deps/v8/src/arm/full-codegen-arm.cc +++ b/deps/v8/src/arm/full-codegen-arm.cc @@ -1688,12 +1688,14 @@ 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); @@ -1710,13 +1712,15 @@ void FullCodeGenerator::EmitKeyedCallWithIC(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)); + } + VisitForAccumulatorValue(key); + __ mov(r2, r0); } - 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, @@ -1732,11 +1736,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 +1762,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); @@ -1807,12 +1817,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) @@ -1846,17 +1858,23 @@ 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)); @@ -1879,7 +1897,10 @@ 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, FieldMemOperand(r1, GlobalObject::kGlobalReceiverOffset)); diff --git a/deps/v8/src/arm/ic-arm.cc b/deps/v8/src/arm/ic-arm.cc index a09afdf75..4c1f9835f 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 7f6090bc5..d2c22af53 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/simulator-arm.cc b/deps/v8/src/arm/simulator-arm.cc index cb91520f3..3ec5f449d 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 3e023489e..c37b3f715 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/assembler.cc b/deps/v8/src/assembler.cc index ce90dceac..7493673e8 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 66811777f..09159fed0 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.cc b/deps/v8/src/bignum.cc new file mode 100644 index 000000000..dd1537a25 --- /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 000000000..1d2bff61a --- /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/checks.cc b/deps/v8/src/checks.cc index b5df316d0..1ab8802ec 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 5ea59920a..e0704774b 100644 --- a/deps/v8/src/checks.h +++ b/deps/v8/src/checks.h @@ -30,7 +30,7 @@ #include <string.h> -#include "flags.h" +#include "../include/v8stdint.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 +279,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 +293,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 +309,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.h b/deps/v8/src/code-stubs.h index c0a8d3063..ec6435341 100644 --- a/deps/v8/src/code-stubs.h +++ b/deps/v8/src/code-stubs.h @@ -542,7 +542,7 @@ class ApiGetterEntryStub : public CodeStub { ApiFunction* fun() { return fun_; } Major MajorKey() { return NoCache; } int MinorKey() { return 0; } - const char* GetName() { return "ApiEntryStub"; } + const char* GetName() { return "ApiGetterEntryStub"; } // The accessor info associated with the function. Handle<AccessorInfo> info_; // The function to be called. @@ -550,6 +550,32 @@ class ApiGetterEntryStub : public CodeStub { }; +class ApiCallEntryStub : public CodeStub { + public: + ApiCallEntryStub(Handle<CallHandlerInfo> 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 = 0; + static const int kArgc = 5; + private: + Handle<CallHandlerInfo> info() { return info_; } + ApiFunction* fun() { return fun_; } + Major MajorKey() { return NoCache; } + int MinorKey() { return 0; } + const char* GetName() { return "ApiCallEntryStub"; } + // The call handler info associated with the function. + Handle<CallHandlerInfo> 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 bda697aba..e954dd66c 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()); @@ -402,10 +403,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 +436,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,8 +482,8 @@ int CEntryStub::MinorKey() { } -bool ApiGetterEntryStub::GetCustomCache(Code** code_out) { - Object* cache = info()->load_stub_cache(); +// Implementation of CodeStub::GetCustomCache. +static bool GetCustomCacheHelper(Object* cache, Code** code_out) { if (cache->IsUndefined()) { return false; } else { @@ -492,9 +493,24 @@ bool ApiGetterEntryStub::GetCustomCache(Code** code_out) { } +bool ApiGetterEntryStub::GetCustomCache(Code** code_out) { + return GetCustomCacheHelper(info()->load_stub_cache(), code_out); +} + + void ApiGetterEntryStub::SetCustomCache(Code* value) { info()->set_load_stub_cache(value); } +bool ApiCallEntryStub::GetCustomCache(Code** code_out) { + return GetCustomCacheHelper(info()->call_stub_cache(), code_out); +} + + +void ApiCallEntryStub::SetCustomCache(Code* value) { + info()->set_call_stub_cache(value); +} + + } } // namespace v8::internal diff --git a/deps/v8/src/compiler.cc b/deps/v8/src/compiler.cc index 6f02960dd..29bbbc703 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 790e807ae..4cc674485 100644 --- a/deps/v8/src/conversions.cc +++ b/deps/v8/src/conversions.cc @@ -816,7 +816,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,9 +840,9 @@ 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, diff --git a/deps/v8/src/debug-debugger.js b/deps/v8/src/debug-debugger.js index 0eab8d1b8..d091991a1 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 24f040986..f3bf954da 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 e805173e0..65eded998 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/flag-definitions.h b/deps/v8/src/flag-definitions.h index 54501ec95..46feea77a 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 97987c27a..c770e189b 100644 --- a/deps/v8/src/full-codegen.cc +++ b/deps/v8/src/full-codegen.cc @@ -563,9 +563,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 +1226,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 201507b2a..a3270aa77 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(); diff --git a/deps/v8/src/global-handles.cc b/deps/v8/src/global-handles.cc index 9ede90852..533984098 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 659f86eca..37b2b4452 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 c218f80dc..a74b6c793 100644 --- a/deps/v8/src/globals.h +++ b/deps/v8/src/globals.h @@ -193,10 +193,9 @@ const uint32_t kMaxUInt32 = 0xFFFFFFFFu; const int kCharSize = sizeof(char); // NOLINT const int kShortSize = sizeof(short); // NOLINT -const int kIntSize = sizeof(int); // NOLINT const int kDoubleSize = sizeof(double); // NOLINT -const int kPointerSize = sizeof(void*); // NOLINT const int kIntptrSize = sizeof(intptr_t); // NOLINT +// kIntSize and kPointerSize are defined in include/v8.h. #if V8_HOST_ARCH_64_BIT const int kPointerSizeLog2 = 3; diff --git a/deps/v8/src/heap-inl.h b/deps/v8/src/heap-inl.h index 15feb9d5f..ba50c0f73 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.cc b/deps/v8/src/heap.cc index b037efd80..134f40e50 100644 --- a/deps/v8/src/heap.cc +++ b/deps/v8/src/heap.cc @@ -83,16 +83,19 @@ int Heap::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; 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; 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 // The snapshot semispace size will be the default semispace size if @@ -172,6 +175,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; @@ -429,7 +438,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 +475,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 +490,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 +502,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 +690,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 +759,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 +787,8 @@ void Heap::PerformGarbageCollection(GarbageCollector collector, global_gc_epilogue_callback_(); } VerifySymbolTable(); + + return next_gc_likely_to_collect_more; } @@ -4280,7 +4322,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 +4345,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 +4371,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); } @@ -4402,7 +4456,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; diff --git a/deps/v8/src/heap.h b/deps/v8/src/heap.h index 8ff2f5f34..c37ced393 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 diff --git a/deps/v8/src/ia32/assembler-ia32.cc b/deps/v8/src/ia32/assembler-ia32.cc index 019f478ad..125f503be 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 5286788fa..79637a190 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 b2b73926b..a7d658bdc 100644 --- a/deps/v8/src/ia32/code-stubs-ia32.cc +++ b/deps/v8/src/ia32/code-stubs-ia32.cc @@ -3067,6 +3067,26 @@ void ApiGetterEntryStub::Generate(MacroAssembler* masm) { } +void ApiCallEntryStub::Generate(MacroAssembler* masm) { + __ PrepareCallApiFunction(kStackSpace, kArgc); + STATIC_ASSERT(kArgc == 5); + + // Allocate the v8::Arguments structure in the arguments' space since + // it's not controlled by GC. + __ mov(ApiParameterOperand(1), eax); // v8::Arguments::implicit_args_. + __ mov(ApiParameterOperand(2), ebx); // v8::Arguments::values_. + __ mov(ApiParameterOperand(3), edx); // v8::Arguments::length_. + // v8::Arguments::is_construct_call_. + __ mov(ApiParameterOperand(4), Immediate(0)); + + // v8::InvocationCallback's argument. + __ lea(eax, ApiParameterOperand(1)); + __ mov(ApiParameterOperand(0), eax); + + __ 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 6d23dd7df..6f4ef87e8 100644 --- a/deps/v8/src/ia32/codegen-ia32.cc +++ b/deps/v8/src/ia32/codegen-ia32.cc @@ -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 { @@ -7292,88 +7292,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) @@ -8660,9 +8578,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 4594b19dd..5a12e10ea 100644 --- a/deps/v8/src/ia32/codegen-ia32.h +++ b/deps/v8/src/ia32/codegen-ia32.h @@ -697,11 +697,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 ee4e6458a..1ea719d71 100644 --- a/deps/v8/src/ia32/full-codegen-ia32.cc +++ b/deps/v8/src/ia32/full-codegen-ia32.cc @@ -1996,12 +1996,14 @@ 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); EmitCallIC(ic, mode); @@ -2017,13 +2019,15 @@ void FullCodeGenerator::EmitKeyedCallWithIC(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)); + } + VisitForAccumulatorValue(key); + __ mov(ecx, eax); } - 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); @@ -2038,11 +2042,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 +2068,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); @@ -2108,12 +2116,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 eax) @@ -2152,11 +2162,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)); @@ -2181,7 +2195,9 @@ 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()); __ push(FieldOperand(ebx, GlobalObject::kGlobalReceiverOffset)); diff --git a/deps/v8/src/ia32/ic-ia32.cc b/deps/v8/src/ia32/ic-ia32.cc index b5f4deefe..a0bc086d8 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.h b/deps/v8/src/ia32/macro-assembler-ia32.h index 7b9b84393..d65eebbc2 100644 --- a/deps/v8/src/ia32/macro-assembler-ia32.h +++ b/deps/v8/src/ia32/macro-assembler-ia32.h @@ -488,7 +488,7 @@ class MacroAssembler: public Assembler { // stored in ApiParameterOperand(0), ApiParameterOperand(1) etc. void PrepareCallApiFunction(int stack_space, int argc); - // 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); diff --git a/deps/v8/src/ia32/stub-cache-ia32.cc b/deps/v8/src/ia32/stub-cache-ia32.cc index e38708835..f59928fe2 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,81 @@ 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 for ApiCallEntryStub. + __ lea(eax, Operand(esp, 3 * kPointerSize)); + __ lea(ebx, Operand(esp, (argc + 3) * kPointerSize)); + __ Set(edx, Immediate(argc)); - // 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); + + ApiCallEntryStub stub(api_call_info_handle, &fun); + + __ EnterInternalFrame(); + + // 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); + if (result->IsFailure()) { + *failure = Failure::cast(result); + return false; + } + + __ LeaveInternalFrame(); + __ ret((argc + 4) * kPointerSize); + return true; } @@ -515,7 +524,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 +533,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 +545,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 +567,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 +582,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 +645,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 +667,8 @@ class CallInterceptorCompiler BASE_EMBEDDED { if (can_do_fast_api_call) { FreeSpaceForFastApiCall(masm, scratch1); } + + return true; } void CompileRegular(MacroAssembler* masm, @@ -905,7 +924,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); @@ -1046,8 +1065,7 @@ bool StubCompiler::GenerateLoadCallback(JSObject* object, __ EnterInternalFrame(); // Push the stack address where the list of arguments ends. - __ mov(scratch2, esp); - __ sub(Operand(scratch2), Immediate(2 * kPointerSize)); + __ lea(scratch2, Operand(esp, -2 * kPointerSize)); __ push(scratch2); __ push(receiver); // receiver __ push(reg); // holder @@ -1061,12 +1079,11 @@ bool StubCompiler::GenerateLoadCallback(JSObject* object, __ 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)); + STATIC_ASSERT(ApiGetterEntryStub::kStackSpace == 5); + __ lea(eax, Operand(esp, 4 * kPointerSize)); __ mov(ebx, esp); // 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); @@ -1077,7 +1094,7 @@ bool StubCompiler::GenerateLoadCallback(JSObject* 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); + *failure = Failure::cast(try_call_result); return false; } } @@ -2208,7 +2225,11 @@ MaybeObject* CallStubCompiler::CompileCallConstant(Object* object, } if (depth != kInvalidProtoDepth) { - GenerateFastApiCall(masm(), optimization, argc); + Failure* failure; + bool success = GenerateFastApiCall(masm(), optimization, argc, &failure); + if (!success) { + return failure; + } } else { __ InvokeFunction(function, arguments(), JUMP_FUNCTION); } @@ -2253,16 +2274,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/jump-target-heavy.cc b/deps/v8/src/jump-target-heavy.cc index e0585e794..c3c22f1ac 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 19f7bfec0..36dc176bc 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/objects-debug.cc b/deps/v8/src/objects-debug.cc index c0e5610ab..2b79016a0 100644 --- a/deps/v8/src/objects-debug.cc +++ b/deps/v8/src/objects-debug.cc @@ -997,6 +997,8 @@ void AccessorInfo::AccessorInfoPrint() { data()->ShortPrint(); PrintF("\n - flag: "); flag()->ShortPrint(); + PrintF("\n - load_stub_cache: "); + load_stub_cache()->ShortPrint(); } void AccessCheckInfo::AccessCheckInfoVerify() { @@ -1046,6 +1048,7 @@ void CallHandlerInfo::CallHandlerInfoVerify() { CHECK(IsCallHandlerInfo()); VerifyPointer(callback()); VerifyPointer(data()); + VerifyPointer(call_stub_cache()); } void CallHandlerInfo::CallHandlerInfoPrint() { @@ -1054,6 +1057,8 @@ void CallHandlerInfo::CallHandlerInfoPrint() { callback()->ShortPrint(); PrintF("\n - data: "); data()->ShortPrint(); + PrintF("\n - call_stub_cache: "); + call_stub_cache()->ShortPrint(); } void TemplateInfo::TemplateInfoVerify() { diff --git a/deps/v8/src/objects-inl.h b/deps/v8/src/objects-inl.h index 1852b549b..79d70e114 100644 --- a/deps/v8/src/objects-inl.h +++ b/deps/v8/src/objects-inl.h @@ -2557,6 +2557,7 @@ ACCESSORS(InterceptorInfo, data, Object, kDataOffset) ACCESSORS(CallHandlerInfo, callback, Object, kCallbackOffset) ACCESSORS(CallHandlerInfo, data, Object, kDataOffset) +ACCESSORS(CallHandlerInfo, call_stub_cache, Object, kCallStubCacheOffset) ACCESSORS(TemplateInfo, tag, Object, kTagOffset) ACCESSORS(TemplateInfo, property_list, Object, kPropertyListOffset) @@ -2671,6 +2672,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 +2688,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.h b/deps/v8/src/objects.h index 6029ad545..9d975ec51 100644 --- a/deps/v8/src/objects.h +++ b/deps/v8/src/objects.h @@ -5423,6 +5423,7 @@ class CallHandlerInfo: public Struct { public: DECL_ACCESSORS(callback, Object) DECL_ACCESSORS(data, Object) + DECL_ACCESSORS(call_stub_cache, Object) static inline CallHandlerInfo* cast(Object* obj); @@ -5433,7 +5434,8 @@ class CallHandlerInfo: public Struct { static const int kCallbackOffset = HeapObject::kHeaderSize; static const int kDataOffset = kCallbackOffset + kPointerSize; - static const int kSize = kDataOffset + kPointerSize; + static const int kCallStubCacheOffset = kDataOffset + kPointerSize; + static const int kSize = kCallStubCacheOffset + kPointerSize; private: DISALLOW_IMPLICIT_CONSTRUCTORS(CallHandlerInfo); diff --git a/deps/v8/src/parser.cc b/deps/v8/src/parser.cc index aad7a615a..a0f3b7147 100644 --- a/deps/v8/src/parser.cc +++ b/deps/v8/src/parser.cc @@ -36,9 +36,9 @@ #include "messages.h" #include "parser.h" #include "platform.h" +#include "preparser.h" #include "runtime.h" #include "scopeinfo.h" -#include "scopes.h" #include "string-stream.h" #include "ast-inl.h" @@ -323,302 +323,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]); - } - bool is_recording() { - return is_recording_; + 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]; } - - 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; +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)); } - - 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; + if (!has_error()) { + symbol_store_.WriteTo( + Vector<byte>::cast(data.SubVector(symbol_start, total_size))); } - - 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_; -}; + return data; +} FunctionEntry ScriptDataImpl::GetFunctionEntry(int start) { @@ -691,7 +485,7 @@ PartialParserRecorder::PartialParserRecorder() preamble_[ScriptDataImpl::kSizeOffset] = 0; ASSERT_EQ(6, ScriptDataImpl::kHeaderSize); #ifdef DEBUG - prev_start = -1; + prev_start_ = -1; #endif } @@ -742,8 +536,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 +594,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 +702,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 +712,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 +739,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 +762,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 +800,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 +830,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 AstBuildingParser::ReportMessageAt(Scanner::Location source_location, - const char* type, - Vector<const char*> args) { +void Parser::ReportMessage(const char* type, Vector<const char*> args) { + Scanner::Location source_location = scanner_.location(); + ReportMessageAt(source_location, type, 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 +859,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 +900,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 +957,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 +1115,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 +1147,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 +1200,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 +1248,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 +1278,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 +1337,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 +1395,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 +1431,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 +1452,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 +1464,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 +1523,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 +1614,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 +1633,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 +1659,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 +1668,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; @@ -2055,29 +1704,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 +1741,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 +1760,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 +1788,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 +1819,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 +1831,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 +1845,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 +1857,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 +1913,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 +1930,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 +1939,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 +1966,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 +1984,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 +2007,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 +2022,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 +2046,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 +2080,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 +2113,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 +2143,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 +2151,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 +2172,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 +2185,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 +2207,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 +2228,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 +2288,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 +2310,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 +2418,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 +2436,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 +2485,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 +2499,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 +2524,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 +2548,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 +2565,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 +2581,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 +2593,6 @@ Expression* Parser::ParseLeftHandSideExpression(bool* ok) { } - Expression* Parser::ParseNewPrefix(PositionStack* stack, bool* ok) { // NewExpression :: // ('new')+ MemberExpression @@ -2984,7 +2617,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 +2659,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 +2667,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 +2676,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 +2695,7 @@ DebuggerStatement* Parser::ParseDebuggerStatement(bool* ok) { Expect(Token::DEBUGGER, CHECK_OK); ExpectSemicolon(CHECK_OK); - return NEW(DebuggerStatement()); + return new DebuggerStatement(); } @@ -3120,38 +2753,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 +2791,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 +2869,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 +2878,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 +2888,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 +2911,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 +3063,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 +3080,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 +3104,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 +3115,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 +3127,7 @@ Expression* Parser::ParseObjectLiteral(bool* ok) { key = NewNumberLiteral(index); break; } - key = NEW(Literal(string)); + key = new Literal(string); break; } case Token::NUMBER: { @@ -3518,7 +3141,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 +3155,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 +3172,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 +3179,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 +3203,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 +3218,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 +3244,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 +3254,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 +3267,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 +3284,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 +3324,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 +3334,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 +3348,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 +3364,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 +3399,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 +3447,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 +3555,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 +3587,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 +3774,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 +4592,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,17 +4635,50 @@ int ScriptDataImpl::ReadNumber(byte** source) { } +// 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; + } + preparser::PreParser<Scanner, PartialParserRecorder> parser; + Scanner scanner; + scanner.Initialize(source, stream, JAVASCRIPT); + PartialParserRecorder recorder; + if (!parser.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); +} + + 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; + preparser::PreParser<Scanner, CompleteParserRecorder> parser; + Scanner scanner; + scanner.Initialize(source, stream, JAVASCRIPT); + bool allow_lazy = FLAG_lazy && (extension == NULL); + CompleteParserRecorder recorder; + if (!parser.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 = parser.recorder()->ExtractData(); + Vector<unsigned> store = recorder.ExtractData(); return new ScriptDataImpl(store); } @@ -5105,14 +4710,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 +4738,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 19b382e8d..667410b3c 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; @@ -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 c0eb21395..89003ba83 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/preparser.h b/deps/v8/src/preparser.h new file mode 100644 index 000000000..44c55cf7d --- /dev/null +++ b/deps/v8/src/preparser.h @@ -0,0 +1,1414 @@ +// 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" + +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) { + Expect(i::Token::CATCH, CHECK_OK); + Expect(i::Token::LPAREN, CHECK_OK); + ParseIdentifier(CHECK_OK); + Expect(i::Token::RPAREN, CHECK_OK); + ParseBlock(CHECK_OK); + catch_or_finally_seen = true; + } + if (peek() == i::Token::FINALLY) { + Expect(i::Token::FINALLY, CHECK_OK); + 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); + 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, strlen(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/profile-generator.cc b/deps/v8/src/profile-generator.cc index a4d9a828d..29f9ab4d3 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 6f63f6a12..b691a056e 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 51f4b094d..9e708fd07 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; @@ -237,16 +194,9 @@ function RegExpExec(string) { 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. + return null; } // Successful match. @@ -254,17 +204,9 @@ function RegExpExec(string) { 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; - } @@ -289,33 +231,22 @@ 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); + + var global = this.global; if (global) { - if (i < 0 || i > length) { + if (i < 0 || i > s.length) { this.lastIndex = 0; return false; } @@ -323,8 +254,6 @@ function RegExpTest(string) { 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 '?' @@ -334,7 +263,7 @@ function RegExpTest(string) { if (!%_ObjectEquals(regexp_key, this)) { regexp_key = this; regexp_val = new $RegExp(this.source.substring(2, this.source.length), - (this.global ? 'g' : '') + (global ? 'g' : '') + (this.ignoreCase ? 'i' : '') + (this.multiline ? 'm' : '')); } @@ -345,24 +274,13 @@ function RegExpTest(string) { // 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 { - this.lastIndex = 0; - if (lastIndex !== 0) return false; - } + if (matchIndices === null) { + if (global) this.lastIndex = 0; + return false; } - cache.type = 'test'; - cache.regExp = this; - cache.subject = s; - cache.answer = result; - return result; + lastMatchInfoOverride = null; + if (global) this.lastIndex = lastMatchInfo[CAPTURE1]; + return true; } @@ -510,7 +428,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 fc1a02322..5534db557 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 72a803797..756099b41 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 000000000..6e9d40e0d --- /dev/null +++ b/deps/v8/src/scanner-base.cc @@ -0,0 +1,167 @@ +// 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 "scanner-base.h" + +namespace v8 { +namespace internal { + +// ---------------------------------------------------------------------------- +// 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 000000000..500870b57 --- /dev/null +++ b/deps/v8/src/scanner-base.h @@ -0,0 +1,165 @@ +// 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 "token.h" +#include "unicode.h" + +namespace v8 { +namespace internal { + +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(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 79d63f177..a24952ac2 100755 --- a/deps/v8/src/scanner.cc +++ b/deps/v8/src/scanner.cc @@ -184,142 +184,6 @@ 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 diff --git a/deps/v8/src/scanner.h b/deps/v8/src/scanner.h index dab3d6728..1f49fd0e6 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 { @@ -142,127 +143,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 }; diff --git a/deps/v8/src/spaces.cc b/deps/v8/src/spaces.cc index e3fb92331..2f3e41aea 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); } diff --git a/deps/v8/src/spaces.h b/deps/v8/src/spaces.h index 3ed2fe8b9..0c10d2c77 100644 --- a/deps/v8/src/spaces.h +++ b/deps/v8/src/spaces.h @@ -491,8 +491,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 +590,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 int 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 +659,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_; diff --git a/deps/v8/src/string.js b/deps/v8/src/string.js index d97f632b8..d82ce0523 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 0ed1b0d91..0523d885f 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); + trimmed = + Vector<const char>(significant_buffer, kMaxSignificantDecimalDigits); + exponent = 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/token.h b/deps/v8/src/token.h index ebc7fea1c..74d9539f4 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 45a4cd60f..7096ba35a 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 052176778..86f4259ac 100644 --- a/deps/v8/src/utils.h +++ b/deps/v8/src/utils.h @@ -31,6 +31,8 @@ #include <stdlib.h> #include <string.h> +#include "checks.h" + namespace v8 { namespace internal { @@ -142,7 +144,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 +230,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 +383,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 +434,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 +642,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 +670,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.h b/deps/v8/src/v8.h index 9dbdf4c28..1cb8d2f1a 100644 --- a/deps/v8/src/v8.h +++ b/deps/v8/src/v8.h @@ -56,7 +56,7 @@ #include "globals.h" #include "checks.h" #include "allocation.h" -#include "utils.h" +#include "v8utils.h" #include "flags.h" // Objects & heap diff --git a/deps/v8/src/v8utils.h b/deps/v8/src/v8utils.h new file mode 100644 index 000000000..a907c9f55 --- /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 e27b9153f..b45510ca1 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 6 #define PATCH_LEVEL 0 #define CANDIDATE_VERSION false diff --git a/deps/v8/src/x64/assembler-x64.cc b/deps/v8/src/x64/assembler-x64.cc index bf5ee5bbb..caed7c8aa 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 bbc101062..c7f763222 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/codegen-x64.cc b/deps/v8/src/x64/codegen-x64.cc index 0faf775d5..e0e40950e 100644 --- a/deps/v8/src/x64/codegen-x64.cc +++ b/deps/v8/src/x64/codegen-x64.cc @@ -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 { @@ -6564,86 +6564,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, diff --git a/deps/v8/src/x64/codegen-x64.h b/deps/v8/src/x64/codegen-x64.h index 795732451..1853c8327 100644 --- a/deps/v8/src/x64/codegen-x64.h +++ b/deps/v8/src/x64/codegen-x64.h @@ -656,8 +656,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 4e0f6d4b6..00ea68458 100644 --- a/deps/v8/src/x64/full-codegen-x64.cc +++ b/deps/v8/src/x64/full-codegen-x64.cc @@ -1717,12 +1717,14 @@ 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, @@ -1740,13 +1742,15 @@ void FullCodeGenerator::EmitKeyedCallWithIC(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)); + } + VisitForAccumulatorValue(key); + __ movq(rcx, rax); } - 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, @@ -1762,11 +1766,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 +1793,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); @@ -1834,35 +1841,37 @@ 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); - - __ 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, CodeGenerator::GlobalObject()); + __ push(FieldOperand(rbx, GlobalObject::kGlobalReceiverOffset)); + __ bind(&call); + } } EmitCallWithStub(expr); @@ -1873,18 +1882,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. @@ -1909,7 +1924,9 @@ 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()); __ push(FieldOperand(rbx, GlobalObject::kGlobalReceiverOffset)); diff --git a/deps/v8/src/x64/ic-x64.cc b/deps/v8/src/x64/ic-x64.cc index 1d95b7f66..9ec781487 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 { |