diff options
author | Teng-Hui Zhu <ztenghui@google.com> | 2010-11-09 16:16:48 -0800 |
---|---|---|
committer | Teng-Hui Zhu <ztenghui@google.com> | 2010-11-15 17:07:50 -0800 |
commit | 3e5fa29ddb82551500b118e9bf37af3966277b70 (patch) | |
tree | a74a16cc186a742dd182289692dfbe9ce1c3c5d4 | |
parent | 5913587db4c6bab03d97bfe44b06289fd6d7270d (diff) | |
download | v8-3e5fa29ddb82551500b118e9bf37af3966277b70.tar.gz |
Update V8 to r5780 as required by WebKit r71558
Change-Id: Ie3936550b99967a13755930d0dac0a59c3562625
109 files changed, 4959 insertions, 3364 deletions
diff --git a/Android.v8common.mk b/Android.v8common.mk index 59ea39de..67b0c3cf 100644 --- a/Android.v8common.mk +++ b/Android.v8common.mk @@ -61,6 +61,7 @@ V8_LOCAL_SRC_FILES := \ src/rewriter.cc \ src/runtime.cc \ src/scanner.cc \ + src/scanner-base.cc \ src/scopeinfo.cc \ src/scopes.cc \ src/serialize.cc \ @@ -1,3 +1,26 @@ +2010-11-08: Version 2.5.5 + + Added more aggressive GC of external objects in near out-of-memory + situations. + + Fixed a bug that gave the incorrect result for String.split called + on the empty string (issue 924). + + +2010-11-03: Version 2.5.4 + + Improved V8 VFPv3 runtime detection to address issue 914. + + +2010-11-01: Version 2.5.3 + + Fixed a bug that prevents constants from overwriting function values + in object literals (issue 907). + + Fixed a bug with reporting of impossible nested calls of DOM functions + (issue http://crbug.com/60753). + + 2010-10-27: Version 2.5.2 Improved sampler resolution on Linux. diff --git a/V8_MERGE_REVISION b/V8_MERGE_REVISION index bf2944f6..f12be983 100644 --- a/V8_MERGE_REVISION +++ b/V8_MERGE_REVISION @@ -1,4 +1,4 @@ We use a V8 revision that has been used for a Chromium release. -http://src.chromium.org/svn/releases/9.0.569.0/DEPS -http://v8.googlecode.com/svn/trunk@5716 +http://src.chromium.org/svn/releases/9.0.577.0/DEPS +http://v8.googlecode.com/svn/trunk@5780 diff --git a/include/v8-debug.h b/include/v8-debug.h index 4314727a..f17b8485 100755 --- a/include/v8-debug.h +++ b/include/v8-debug.h @@ -142,7 +142,7 @@ class EXPORT Debug { virtual ~Message() {} }; - + /** * An event details object passed to the debug event listener. @@ -300,7 +300,7 @@ class EXPORT Debug { * get access to information otherwise not available during normal JavaScript * execution e.g. details on stack frames. Receiver of the function call will * be the debugger context global object, however this is a subject to change. - * The following example show a JavaScript function which when passed to + * The following example show a JavaScript function which when passed to * v8::Debug::Call will return the current line of JavaScript execution. * * \code diff --git a/include/v8.h b/include/v8.h index 89502cb9..8c730df8 100644 --- a/include/v8.h +++ b/include/v8.h @@ -38,23 +38,9 @@ #ifndef V8_H_ #define V8_H_ -#include <stdio.h> +#include "v8stdint.h" #ifdef _WIN32 -// When compiling on MinGW stdint.h is available. -#ifdef __MINGW32__ -#include <stdint.h> -#else // __MINGW32__ -typedef signed char int8_t; -typedef unsigned char uint8_t; -typedef short int16_t; // NOLINT -typedef unsigned short uint16_t; // NOLINT -typedef int int32_t; -typedef unsigned int uint32_t; -typedef __int64 int64_t; -typedef unsigned __int64 uint64_t; -// intptr_t and friends are defined in crtdefs.h through stdio.h. -#endif // __MINGW32__ // Setup for Windows DLL export/import. When building the V8 DLL the // BUILDING_V8_SHARED needs to be defined. When building a program which uses @@ -76,8 +62,6 @@ typedef unsigned __int64 uint64_t; #else // _WIN32 -#include <stdint.h> - // Setup for Linux shared library export. There is no need to distinguish // between building or using the V8 shared library, but we should not // export symbols when we are building a static library. @@ -127,7 +111,6 @@ class Arguments; class Object; class Heap; class Top; - } @@ -476,10 +459,10 @@ class V8EXPORT HandleScope { level = 0; } }; - + void Leave(); - + internal::Object** prev_next_; internal::Object** prev_limit_; @@ -1055,7 +1038,7 @@ class String : public Primitive { */ V8EXPORT bool IsExternalAscii() const; - class V8EXPORT ExternalStringResourceBase { + class V8EXPORT ExternalStringResourceBase { // NOLINT public: virtual ~ExternalStringResourceBase() {} @@ -1790,18 +1773,19 @@ class Arguments { inline bool IsConstructCall() const; inline Local<Value> Data() const; private: + static const int kDataIndex = 0; + static const int kCalleeIndex = -1; + static const int kHolderIndex = -2; + friend class ImplementationUtilities; - inline Arguments(Local<Value> data, - Local<Object> holder, - Local<Function> callee, - bool is_construct_call, - void** values, int length); - Local<Value> data_; - Local<Object> holder_; - Local<Function> callee_; - bool is_construct_call_; - void** values_; + inline Arguments(internal::Object** implicit_args, + internal::Object** values, + int length, + bool is_construct_call); + internal::Object** implicit_args_; + internal::Object** values_; int length_; + bool is_construct_call_; }; @@ -3259,8 +3243,8 @@ class V8EXPORT Locker { /** * An interface for exporting data from V8, using "push" model. */ -class V8EXPORT OutputStream { -public: +class V8EXPORT OutputStream { // NOLINT + public: enum OutputEncoding { kAscii = 0 // 7-bit ASCII. }; @@ -3290,6 +3274,8 @@ public: namespace internal { +const int kPointerSize = sizeof(void*); // NOLINT +const int kIntSize = sizeof(int); // NOLINT // Tag information for HeapObject. const int kHeapObjectTag = 1; @@ -3325,19 +3311,19 @@ template <> struct SmiConstants<8> { } }; -const int kSmiShiftSize = SmiConstants<sizeof(void*)>::kSmiShiftSize; -const int kSmiValueSize = SmiConstants<sizeof(void*)>::kSmiValueSize; +const int kSmiShiftSize = SmiConstants<kPointerSize>::kSmiShiftSize; +const int kSmiValueSize = SmiConstants<kPointerSize>::kSmiValueSize; template <size_t ptr_size> struct InternalConstants; // Internal constants for 32-bit systems. template <> struct InternalConstants<4> { - static const int kStringResourceOffset = 3 * sizeof(void*); + static const int kStringResourceOffset = 3 * kPointerSize; }; // Internal constants for 64-bit systems. template <> struct InternalConstants<8> { - static const int kStringResourceOffset = 3 * sizeof(void*); + static const int kStringResourceOffset = 3 * kPointerSize; }; /** @@ -3351,12 +3337,12 @@ class Internals { // These values match non-compiler-dependent values defined within // the implementation of v8. static const int kHeapObjectMapOffset = 0; - static const int kMapInstanceTypeOffset = sizeof(void*) + sizeof(int); + static const int kMapInstanceTypeOffset = kPointerSize + kIntSize; static const int kStringResourceOffset = - InternalConstants<sizeof(void*)>::kStringResourceOffset; + InternalConstants<kPointerSize>::kStringResourceOffset; - static const int kProxyProxyOffset = sizeof(void*); - static const int kJSObjectHeaderSize = 3 * sizeof(void*); + static const int kProxyProxyOffset = kPointerSize; + static const int kJSObjectHeaderSize = 3 * kPointerSize; static const int kFullStringRepresentationMask = 0x07; static const int kExternalTwoByteRepresentationTag = 0x02; @@ -3374,7 +3360,7 @@ class Internals { } static inline int SmiValue(internal::Object* value) { - return SmiConstants<sizeof(void*)>::SmiToInt(value); + return SmiConstants<kPointerSize>::SmiToInt(value); } static inline int GetInstanceType(internal::Object* obj) { @@ -3403,10 +3389,9 @@ class Internals { uint8_t* addr = reinterpret_cast<uint8_t*>(ptr) + offset - kHeapObjectTag; return *reinterpret_cast<T*>(addr); } - }; -} +} // namespace internal template <class T> @@ -3470,14 +3455,13 @@ void Persistent<T>::ClearWeak() { } -Arguments::Arguments(v8::Local<v8::Value> data, - v8::Local<v8::Object> holder, - v8::Local<v8::Function> callee, - bool is_construct_call, - void** values, int length) - : data_(data), holder_(holder), callee_(callee), - is_construct_call_(is_construct_call), - values_(values), length_(length) { } +Arguments::Arguments(internal::Object** implicit_args, + internal::Object** values, int length, + bool is_construct_call) + : implicit_args_(implicit_args), + values_(values), + length_(length), + is_construct_call_(is_construct_call) { } Local<Value> Arguments::operator[](int i) const { @@ -3487,7 +3471,8 @@ Local<Value> Arguments::operator[](int i) const { Local<Function> Arguments::Callee() const { - return callee_; + return Local<Function>(reinterpret_cast<Function*>( + &implicit_args_[kCalleeIndex])); } @@ -3497,12 +3482,13 @@ Local<Object> Arguments::This() const { Local<Object> Arguments::Holder() const { - return holder_; + return Local<Object>(reinterpret_cast<Object*>( + &implicit_args_[kHolderIndex])); } Local<Value> Arguments::Data() const { - return data_; + return Local<Value>(reinterpret_cast<Value*>(&implicit_args_[kDataIndex])); } @@ -3565,7 +3551,7 @@ Local<Value> Object::UncheckedGetInternalField(int index) { // If the object is a plain JSObject, which is the common case, // we know where to find the internal fields and can return the // value directly. - int offset = I::kJSObjectHeaderSize + (sizeof(void*) * index); + int offset = I::kJSObjectHeaderSize + (internal::kPointerSize * index); O* value = I::ReadField<O*>(obj, offset); O** result = HandleScope::CreateHandle(value); return Local<Value>(reinterpret_cast<Value*>(result)); @@ -3601,7 +3587,7 @@ void* Object::GetPointerFromInternalField(int index) { // If the object is a plain JSObject, which is the common case, // we know where to find the internal fields and can return the // value directly. - int offset = I::kJSObjectHeaderSize + (sizeof(void*) * index); + int offset = I::kJSObjectHeaderSize + (internal::kPointerSize * index); O* value = I::ReadField<O*>(obj, offset); return I::GetExternalPointer(value); } diff --git a/include/v8stdint.h b/include/v8stdint.h new file mode 100644 index 00000000..50b4f29a --- /dev/null +++ b/include/v8stdint.h @@ -0,0 +1,53 @@ +// 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. + +// Load definitions of standard types. + +#ifndef V8STDINT_H_ +#define V8STDINT_H_ + +#include <stdio.h> + +#if defined(_WIN32) && !defined(__MINGW32__) + +typedef signed char int8_t; +typedef unsigned char uint8_t; +typedef short int16_t; // NOLINT +typedef unsigned short uint16_t; // NOLINT +typedef int int32_t; +typedef unsigned int uint32_t; +typedef __int64 int64_t; +typedef unsigned __int64 uint64_t; +// intptr_t and friends are defined in crtdefs.h through stdio.h. + +#else + +#include <stdint.h> + +#endif + +#endif // V8STDINT_H_ diff --git a/src/SConscript b/src/SConscript index 8995d482..596caf7f 100755 --- a/src/SConscript +++ b/src/SConscript @@ -95,6 +95,7 @@ SOURCES = { register-allocator.cc rewriter.cc runtime.cc + scanner-base.cc scanner.cc scopeinfo.cc scopes.cc @@ -43,7 +43,6 @@ #include "serialize.h" #include "snapshot.h" #include "top.h" -#include "utils.h" #include "v8threads.h" #include "version.h" @@ -1155,13 +1154,13 @@ void ObjectTemplate::SetInternalFieldCount(int value) { ScriptData* ScriptData::PreCompile(const char* input, int length) { unibrow::Utf8InputBuffer<> buf(input, length); - return i::Parser::PreParse(i::Handle<i::String>(), &buf, NULL); + return i::ParserApi::PreParse(i::Handle<i::String>(), &buf, NULL); } ScriptData* ScriptData::PreCompile(v8::Handle<String> source) { i::Handle<i::String> str = Utils::OpenHandle(*source); - return i::Parser::PreParse(str, NULL, NULL); + return i::ParserApi::PreParse(str, NULL, NULL); } diff --git a/src/apiutils.h b/src/apiutils.h index 8c791ebd..1313ddaa 100644 --- a/src/apiutils.h +++ b/src/apiutils.h @@ -29,7 +29,6 @@ #define V8_APIUTILS_H_ namespace v8 { - class ImplementationUtilities { public: static v8::Handle<v8::Primitive> Undefined(); @@ -45,12 +44,21 @@ class ImplementationUtilities { return that->names_; } - static v8::Arguments NewArguments(Local<Value> data, - Local<Object> holder, - Local<Function> callee, - bool is_construct_call, - void** argv, int argc) { - return v8::Arguments(data, holder, callee, is_construct_call, argv, argc); + // Packs additional parameters for the NewArguments function. |implicit_args| + // is a pointer to the last element of 3-elements array controlled by GC. + static void PrepareArgumentsData(internal::Object** implicit_args, + internal::Object* data, + internal::JSFunction* callee, + internal::Object* holder) { + implicit_args[v8::Arguments::kDataIndex] = data; + implicit_args[v8::Arguments::kCalleeIndex] = callee; + implicit_args[v8::Arguments::kHolderIndex] = holder; + } + + static v8::Arguments NewArguments(internal::Object** implicit_args, + internal::Object** argv, int argc, + bool is_construct_call) { + return v8::Arguments(implicit_args, argv, argc, is_construct_call); } // Introduce an alias for the handle scope data to allow non-friends diff --git a/src/arguments.h b/src/arguments.h index c17f4cf8..d51c9e4c 100644 --- a/src/arguments.h +++ b/src/arguments.h @@ -84,6 +84,15 @@ class CustomArguments : public Relocatable { values_[1] = holder; values_[0] = data; } + + inline CustomArguments() { +#ifdef DEBUG + for (size_t i = 0; i < ARRAY_SIZE(values_); i++) { + values_[i] = reinterpret_cast<Object*>(kZapValue); + } +#endif + } + void IterateInstance(ObjectVisitor* v); Object** end() { return values_ + ARRAY_SIZE(values_) - 1; } private: diff --git a/src/arm/assembler-arm.cc b/src/arm/assembler-arm.cc index 7d368bf4..72835ba3 100644 --- a/src/arm/assembler-arm.cc +++ b/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,24 +995,24 @@ 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; ASSERT(is_int24(imm24)); - emit(15 << 28 | B27 | B25 | h | (imm24 & Imm24Mask)); + emit(nv | B27 | B25 | h | (imm24 & Imm24Mask)); } 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); @@ -1634,15 +1630,29 @@ void Assembler::stm(BlockAddrMode am, // Exception-generating instructions and debugging support. -void Assembler::stop(const char* msg) { +// Stops with a non-negative code less than kNumOfWatchedStops support +// enabling/disabling and a counter feature. See simulator-arm.h . +void Assembler::stop(const char* msg, Condition cond, int32_t code) { #ifndef __arm__ - // The simulator handles these special instructions and stops execution. - emit(15 << 28 | ((intptr_t) msg)); + // See constants-arm.h SoftwareInterruptCodes. Unluckily the Assembler and + // Simulator do not share constants declaration. + ASSERT(code >= kDefaultStopCode); + static const uint32_t kStopInterruptCode = 1 << 23; + static const uint32_t kMaxStopCode = kStopInterruptCode - 1; + // The Simulator will handle the stop instruction and get the message address. + // It expects to find the address just after the svc instruction. + BlockConstPoolFor(2); + if (code >= 0) { + svc(kStopInterruptCode + code, cond); + } else { + svc(kStopInterruptCode + kMaxStopCode, cond); + } + emit(reinterpret_cast<Instr>(msg)); #else // def __arm__ #ifdef CAN_USE_ARMV5_INSTRUCTIONS bkpt(0); #else // ndef CAN_USE_ARMV5_INSTRUCTIONS - swi(0x9f0001); + svc(0x9f0001); #endif // ndef CAN_USE_ARMV5_INSTRUCTIONS #endif // def __arm__ } @@ -1654,7 +1664,7 @@ void Assembler::bkpt(uint32_t imm16) { // v5 and above } -void Assembler::swi(uint32_t imm24, Condition cond) { +void Assembler::svc(uint32_t imm24, Condition cond) { ASSERT(is_uint24(imm24)); emit(cond | 15*B24 | imm24); } @@ -2363,14 +2373,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); } @@ -2384,47 +2394,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/src/arm/assembler-arm.h b/src/arm/assembler-arm.h index 1c4fd609..05792354 100644 --- a/src/arm/assembler-arm.h +++ b/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()); @@ -904,10 +905,13 @@ class Assembler : public Malloced { void stm(BlockAddrMode am, Register base, RegList src, Condition cond = al); // Exception-generating instructions and debugging support - void stop(const char* msg); + static const int kDefaultStopCode = -1; + void stop(const char* msg, + Condition cond = al, + int32_t code = kDefaultStopCode); void bkpt(uint32_t imm16); // v5 and above - void swi(uint32_t imm24, Condition cond = al); + void svc(uint32_t imm24, Condition cond = al); // Coprocessor instructions @@ -1114,13 +1118,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; @@ -1256,12 +1256,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(); @@ -1287,8 +1281,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/src/arm/codegen-arm.cc b/src/arm/codegen-arm.cc index 0c060f0f..d7fd9a49 100644 --- a/src/arm/codegen-arm.cc +++ b/src/arm/codegen-arm.cc @@ -3596,6 +3596,12 @@ void CodeGenerator::VisitObjectLiteral(ObjectLiteral* node) { frame_->CallRuntime(Runtime::kCreateObjectLiteralShallow, 4); } frame_->EmitPush(r0); // save the result + + // Mark all computed expressions that are bound to a key that + // is shadowed by a later occurrence of the same key. For the + // marked expressions, no store code is emitted. + node->CalculateEmitStore(); + for (int i = 0; i < node->properties()->length(); i++) { // At the start of each iteration, the top of stack contains // the newly created object literal. @@ -3612,11 +3618,15 @@ void CodeGenerator::VisitObjectLiteral(ObjectLiteral* node) { if (key->handle()->IsSymbol()) { Handle<Code> ic(Builtins::builtin(Builtins::StoreIC_Initialize)); Load(value); - frame_->PopToR0(); - // Fetch the object literal. - frame_->SpillAllButCopyTOSToR1(); - __ mov(r2, Operand(key->handle())); - frame_->CallCodeObject(ic, RelocInfo::CODE_TARGET, 0); + if (property->emit_store()) { + frame_->PopToR0(); + // Fetch the object literal. + frame_->SpillAllButCopyTOSToR1(); + __ mov(r2, Operand(key->handle())); + frame_->CallCodeObject(ic, RelocInfo::CODE_TARGET, 0); + } else { + frame_->Drop(); + } break; } // else fall through @@ -3624,7 +3634,11 @@ void CodeGenerator::VisitObjectLiteral(ObjectLiteral* node) { frame_->Dup(); Load(key); Load(value); - frame_->CallRuntime(Runtime::kSetProperty, 3); + if (property->emit_store()) { + frame_->CallRuntime(Runtime::kSetProperty, 3); + } else { + frame_->Drop(3); + } break; } case ObjectLiteral::Property::SETTER: { @@ -5482,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/src/arm/codegen-arm.h b/src/arm/codegen-arm.h index e6fd6071..97e50b42 100644 --- a/src/arm/codegen-arm.h +++ b/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/src/arm/constants-arm.h b/src/arm/constants-arm.h index b2b5cb56..123c5e79 100644 --- a/src/arm/constants-arm.h +++ b/src/arm/constants-arm.h @@ -186,12 +186,18 @@ enum Shift { // Special Software Interrupt codes when used in the presence of the ARM // simulator. +// svc (formerly swi) provides a 24bit immediate value. Use bits 22:0 for +// standard SoftwareInterrupCode. Bit 23 is reserved for the stop feature. enum SoftwareInterruptCodes { // transition to C code call_rt_redirected = 0x10, // break point - break_point = 0x20 + break_point = 0x20, + // stop + stop = 1 << 23 }; +static const int32_t kStopCodeMask = stop - 1; +static const uint32_t kMaxStopCode = stop - 1; // Type of VFP register. Determines register encoding. @@ -325,7 +331,7 @@ class Instr { inline int SImmed24Field() const { return ((InstructionBits() << 8) >> 8); } // Fields used in Software interrupt instructions - inline SoftwareInterruptCodes SwiField() const { + inline SoftwareInterruptCodes SvcField() const { return static_cast<SoftwareInterruptCodes>(Bits(23, 0)); } diff --git a/src/arm/cpu-arm.cc b/src/arm/cpu-arm.cc index a3bf4832..e998b6f5 100644 --- a/src/arm/cpu-arm.cc +++ b/src/arm/cpu-arm.cc @@ -70,7 +70,7 @@ void CPU::FlushICache(void* start, size_t size) { // __arm__ may be defined in thumb mode. register uint32_t scno asm("r7") = __ARM_NR_cacheflush; asm volatile( - "swi 0x0" + "svc 0x0" : "=r" (beg) : "0" (beg), "r" (end), "r" (flg), "r" (scno)); #else @@ -83,7 +83,7 @@ void CPU::FlushICache(void* start, size_t size) { ".ARM \n" "1: push {r7} \n\t" "mov r7, %4 \n\t" - "swi 0x0 \n\t" + "svc 0x0 \n\t" "pop {r7} \n\t" "@ Enter THUMB Mode\n\t" "adr r3, 2f+1 \n\t" @@ -98,20 +98,20 @@ void CPU::FlushICache(void* start, size_t size) { #if defined (__arm__) && !defined(__thumb__) // __arm__ may be defined in thumb mode. asm volatile( - "swi %1" + "svc %1" : "=r" (beg) : "i" (__ARM_NR_cacheflush), "0" (beg), "r" (end), "r" (flg)); #else // Do not use the value of __ARM_NR_cacheflush in the inline assembly // below, because the thumb mode value would be used, which would be - // wrong, since we switch to ARM mode before executing the swi instruction + // wrong, since we switch to ARM mode before executing the svc instruction asm volatile( "@ Enter ARM Mode \n\t" "adr r3, 1f \n\t" "bx r3 \n\t" ".ALIGN 4 \n\t" ".ARM \n" - "1: swi 0x9f0002 \n" + "1: svc 0x9f0002 \n" "@ Enter THUMB Mode\n\t" "adr r3, 2f+1 \n\t" "bx r3 \n\t" diff --git a/src/arm/disasm-arm.cc b/src/arm/disasm-arm.cc index 5122f437..4e7580f8 100644 --- a/src/arm/disasm-arm.cc +++ b/src/arm/disasm-arm.cc @@ -108,7 +108,7 @@ class Decoder { void PrintShiftImm(Instr* instr); void PrintShiftSat(Instr* instr); void PrintPU(Instr* instr); - void PrintSoftwareInterrupt(SoftwareInterruptCodes swi); + void PrintSoftwareInterrupt(SoftwareInterruptCodes svc); // Handle formatting of instructions and their options. int FormatRegister(Instr* instr, const char* option); @@ -126,8 +126,8 @@ class Decoder { void DecodeType4(Instr* instr); void DecodeType5(Instr* instr); void DecodeType6(Instr* instr); - void DecodeType7(Instr* instr); - void DecodeUnconditional(Instr* instr); + // Type 7 includes special Debugger instructions. + int DecodeType7(Instr* instr); // For VFP support. void DecodeTypeVFP(Instr* instr); void DecodeType6CoprocessorIns(Instr* instr); @@ -290,8 +290,8 @@ void Decoder::PrintPU(Instr* instr) { // Print SoftwareInterrupt codes. Factoring this out reduces the complexity of // the FormatOption method. -void Decoder::PrintSoftwareInterrupt(SoftwareInterruptCodes swi) { - switch (swi) { +void Decoder::PrintSoftwareInterrupt(SoftwareInterruptCodes svc) { + switch (svc) { case call_rt_redirected: Print("call_rt_redirected"); return; @@ -299,9 +299,16 @@ void Decoder::PrintSoftwareInterrupt(SoftwareInterruptCodes swi) { Print("break_point"); return; default: - out_buffer_pos_ += v8i::OS::SNPrintF(out_buffer_ + out_buffer_pos_, - "%d", - swi); + if (svc >= stop) { + out_buffer_pos_ += v8i::OS::SNPrintF(out_buffer_ + out_buffer_pos_, + "%d - 0x%x", + svc & kStopCodeMask, + svc & kStopCodeMask); + } else { + out_buffer_pos_ += v8i::OS::SNPrintF(out_buffer_ + out_buffer_pos_, + "%d", + svc); + } return; } } @@ -553,9 +560,9 @@ int Decoder::FormatOption(Instr* instr, const char* format) { PrintShiftRm(instr); return 8; } - } else if (format[1] == 'w') { // 'swi - ASSERT(STRING_STARTS_WITH(format, "swi")); - PrintSoftwareInterrupt(instr->SwiField()); + } else if (format[1] == 'v') { // 'svc + ASSERT(STRING_STARTS_WITH(format, "svc")); + PrintSoftwareInterrupt(instr->SvcField()); return 3; } else if (format[1] == 'i') { // 'sign: signed extra loads and stores ASSERT(STRING_STARTS_WITH(format, "sign")); @@ -1004,72 +1011,27 @@ void Decoder::DecodeType6(Instr* instr) { } -void Decoder::DecodeType7(Instr* instr) { +int Decoder::DecodeType7(Instr* instr) { if (instr->Bit(24) == 1) { - Format(instr, "swi'cond 'swi"); + if (instr->SvcField() >= stop) { + Format(instr, "stop'cond 'svc"); + // Also print the stop message. Its address is encoded + // in the following 4 bytes. + out_buffer_pos_ += + v8i::OS::SNPrintF(out_buffer_ + out_buffer_pos_, + "\n %p %08x stop message: %s", + reinterpret_cast<int32_t*>(instr + Instr::kInstrSize), + *reinterpret_cast<char**>(instr + Instr::kInstrSize), + *reinterpret_cast<char**>(instr + Instr::kInstrSize)); + // We have decoded 2 * Instr::kInstrSize bytes. + return 2 * Instr::kInstrSize; + } else { + Format(instr, "svc'cond 'svc"); + } } else { DecodeTypeVFP(instr); } -} - -void Decoder::DecodeUnconditional(Instr* instr) { - if (instr->Bits(7, 4) == 0xB && instr->Bits(27, 25) == 0 && instr->HasL()) { - Format(instr, "'memop'h'pu 'rd, "); - bool immediate = instr->HasB(); - switch (instr->PUField()) { - case 0: { - // Post index, negative. - if (instr->HasW()) { - Unknown(instr); - break; - } - if (immediate) { - Format(instr, "['rn], #-'imm12"); - } else { - Format(instr, "['rn], -'rm"); - } - break; - } - case 1: { - // Post index, positive. - if (instr->HasW()) { - Unknown(instr); - break; - } - if (immediate) { - Format(instr, "['rn], #+'imm12"); - } else { - Format(instr, "['rn], +'rm"); - } - break; - } - case 2: { - // Pre index or offset, negative. - if (immediate) { - Format(instr, "['rn, #-'imm12]'w"); - } else { - Format(instr, "['rn, -'rm]'w"); - } - break; - } - case 3: { - // Pre index or offset, positive. - if (immediate) { - Format(instr, "['rn, #+'imm12]'w"); - } else { - Format(instr, "['rn, +'rm]'w"); - } - break; - } - default: { - // The PU field is a 2-bit field. - UNREACHABLE(); - break; - } - } - return; - } - Format(instr, "break 'msg"); + return Instr::kInstrSize; } @@ -1332,7 +1294,7 @@ int Decoder::InstructionDecode(byte* instr_ptr) { "%08x ", instr->InstructionBits()); if (instr->ConditionField() == special_condition) { - DecodeUnconditional(instr); + UNIMPLEMENTED(); return Instr::kInstrSize; } switch (instr->TypeField()) { @@ -1362,8 +1324,7 @@ int Decoder::InstructionDecode(byte* instr_ptr) { break; } case 7: { - DecodeType7(instr); - break; + return DecodeType7(instr); } default: { // The type field is 3-bits in the ARM encoding. diff --git a/src/arm/full-codegen-arm.cc b/src/arm/full-codegen-arm.cc index 2855ca4f..bf27d0c7 100644 --- a/src/arm/full-codegen-arm.cc +++ b/src/arm/full-codegen-arm.cc @@ -1169,6 +1169,11 @@ void FullCodeGenerator::VisitObjectLiteral(ObjectLiteral* expr) { // result_saved is false the result is in r0. bool result_saved = false; + // Mark all computed expressions that are bound to a key that + // is shadowed by a later occurrence of the same key. For the + // marked expressions, no store code is emitted. + expr->CalculateEmitStore(); + for (int i = 0; i < expr->properties()->length(); i++) { ObjectLiteral::Property* property = expr->properties()->at(i); if (property->IsCompileTimeValue()) continue; @@ -1190,8 +1195,10 @@ void FullCodeGenerator::VisitObjectLiteral(ObjectLiteral* expr) { VisitForAccumulatorValue(value); __ mov(r2, Operand(key->handle())); __ ldr(r1, MemOperand(sp)); - Handle<Code> ic(Builtins::builtin(Builtins::StoreIC_Initialize)); - EmitCallIC(ic, RelocInfo::CODE_TARGET); + if (property->emit_store()) { + Handle<Code> ic(Builtins::builtin(Builtins::StoreIC_Initialize)); + EmitCallIC(ic, RelocInfo::CODE_TARGET); + } break; } // Fall through. @@ -1201,7 +1208,11 @@ void FullCodeGenerator::VisitObjectLiteral(ObjectLiteral* expr) { __ push(r0); VisitForStackValue(key); VisitForStackValue(value); - __ CallRuntime(Runtime::kSetProperty, 3); + if (property->emit_store()) { + __ CallRuntime(Runtime::kSetProperty, 3); + } else { + __ Drop(3); + } break; case ObjectLiteral::Property::GETTER: case ObjectLiteral::Property::SETTER: @@ -1677,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); @@ -1699,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, @@ -1721,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); @@ -1745,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 copy of the first argument or undefined if it doesn't exist. - if (arg_count > 0) { - __ ldr(r1, MemOperand(sp, arg_count * kPointerSize)); + // Push the arguments. + 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); - } 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); @@ -1796,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) @@ -1835,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)); @@ -1868,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/src/arm/ic-arm.cc b/src/arm/ic-arm.cc index c460f9ca..a09afdf7 100644 --- a/src/arm/ic-arm.cc +++ b/src/arm/ic-arm.cc @@ -544,7 +544,7 @@ static void GenerateMonomorphicCacheProbe(MacroAssembler* masm, // Probe the stub cache. Code::Flags flags = Code::ComputeFlags(kind, NOT_IN_LOOP, MONOMORPHIC, NORMAL, argc); - StubCache::GenerateProbe(masm, flags, r1, r2, r3, no_reg); + StubCache::GenerateProbe(masm, flags, r1, r2, r3, r4, r5); // If the stub cache probing failed, the receiver might be a value. // For value objects, we use the map of the prototype objects for @@ -583,7 +583,7 @@ static void GenerateMonomorphicCacheProbe(MacroAssembler* masm, // Probe the stub cache for the value object. __ bind(&probe); - StubCache::GenerateProbe(masm, flags, r1, r2, r3, no_reg); + StubCache::GenerateProbe(masm, flags, r1, r2, r3, r4, r5); __ bind(&miss); } @@ -858,7 +858,7 @@ void LoadIC::GenerateMegamorphic(MacroAssembler* masm) { Code::Flags flags = Code::ComputeFlags(Code::LOAD_IC, NOT_IN_LOOP, MONOMORPHIC); - StubCache::GenerateProbe(masm, flags, r0, r2, r3, no_reg); + StubCache::GenerateProbe(masm, flags, r0, r2, r3, r4, r5); // Cache miss: Jump to runtime. GenerateMiss(masm); @@ -2163,7 +2163,7 @@ void StoreIC::GenerateMegamorphic(MacroAssembler* masm) { Code::Flags flags = Code::ComputeFlags(Code::STORE_IC, NOT_IN_LOOP, MONOMORPHIC); - StubCache::GenerateProbe(masm, flags, r1, r2, r3, no_reg); + StubCache::GenerateProbe(masm, flags, r1, r2, r3, r4, r5); // Cache miss: Jump to runtime. GenerateMiss(masm); diff --git a/src/arm/macro-assembler-arm.cc b/src/arm/macro-assembler-arm.cc index 7f6090bc..d2c22af5 100644 --- a/src/arm/macro-assembler-arm.cc +++ b/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/src/arm/simulator-arm.cc b/src/arm/simulator-arm.cc index 534e394a..cb91520f 100644 --- a/src/arm/simulator-arm.cc +++ b/src/arm/simulator-arm.cc @@ -112,15 +112,29 @@ static void InitializeCoverage() { void Debugger::Stop(Instr* instr) { - char* str = reinterpret_cast<char*>(instr->InstructionBits() & 0x0fffffff); - if (strlen(str) > 0) { + // Get the stop code. + uint32_t code = instr->SvcField() & kStopCodeMask; + // Retrieve the encoded address, which comes just after this stop. + char** msg_address = + reinterpret_cast<char**>(sim_->get_pc() + Instr::kInstrSize); + char* msg = *msg_address; + ASSERT(msg != NULL); + + // Update this stop description. + if (isWatchedStop(code) && !watched_stops[code].desc) { + watched_stops[code].desc = msg; + } + + if (strlen(msg) > 0) { if (coverage_log != NULL) { - fprintf(coverage_log, "%s\n", str); + fprintf(coverage_log, "%s\n", msg); fflush(coverage_log); } - instr->SetInstructionBits(0xe1a00000); // Overwrite with nop. + // Overwrite the instruction and address with nops. + instr->SetInstructionBits(kNopInstr); + reinterpret_cast<Instr*>(msg_address)->SetInstructionBits(kNopInstr); } - sim_->set_pc(sim_->get_pc() + Instr::kInstrSize); + sim_->set_pc(sim_->get_pc() + 2 * Instr::kInstrSize); } #else // ndef GENERATED_CODE_COVERAGE @@ -130,9 +144,16 @@ static void InitializeCoverage() { void Debugger::Stop(Instr* instr) { - const char* str = (const char*)(instr->InstructionBits() & 0x0fffffff); - PrintF("Simulator hit %s\n", str); - sim_->set_pc(sim_->get_pc() + Instr::kInstrSize); + // Get the stop code. + uint32_t code = instr->SvcField() & kStopCodeMask; + // Retrieve the encoded address, which comes just after this stop. + char* msg = *reinterpret_cast<char**>(sim_->get_pc() + Instr::kInstrSize); + // Update this stop description. + if (sim_->isWatchedStop(code) && !sim_->watched_stops[code].desc) { + sim_->watched_stops[code].desc = msg; + } + PrintF("Simulator hit %s\n", msg); + sim_->set_pc(sim_->get_pc() + 2 * Instr::kInstrSize); Debug(); } #endif @@ -359,6 +380,7 @@ void Debugger::Debug() { // use a reasonably large buffer v8::internal::EmbeddedVector<char, 256> buffer; + byte* prev = NULL; byte* cur = NULL; byte* end = NULL; @@ -368,9 +390,9 @@ void Debugger::Debug() { } else if (argc == 2) { int32_t value; if (GetValue(arg1, &value)) { - cur = reinterpret_cast<byte*>(value); - // no length parameter passed, assume 10 instructions - end = cur + (10 * Instr::kInstrSize); + cur = reinterpret_cast<byte*>(sim_->get_pc()); + // Disassemble <arg1> instructions. + end = cur + (value * Instr::kInstrSize); } } else { int32_t value1; @@ -382,10 +404,10 @@ void Debugger::Debug() { } while (cur < end) { - dasm.InstructionDecode(buffer, cur); + prev = cur; + cur += dasm.InstructionDecode(buffer, cur); PrintF(" 0x%08x %s\n", - reinterpret_cast<intptr_t>(cur), buffer.start()); - cur += Instr::kInstrSize; + reinterpret_cast<intptr_t>(prev), buffer.start()); } } else if (strcmp(cmd, "gdb") == 0) { PrintF("relinquishing control to gdb\n"); @@ -418,13 +440,58 @@ void Debugger::Debug() { PrintF("OVERFLOW flag: %d; ", sim_->overflow_vfp_flag_); PrintF("UNDERFLOW flag: %d; ", sim_->underflow_vfp_flag_); PrintF("INEXACT flag: %d; ", sim_->inexact_vfp_flag_); - } else if (strcmp(cmd, "unstop") == 0) { - intptr_t stop_pc = sim_->get_pc() - Instr::kInstrSize; + } else if (strcmp(cmd, "stop") == 0) { + int32_t value; + intptr_t stop_pc = sim_->get_pc() - 2 * Instr::kInstrSize; Instr* stop_instr = reinterpret_cast<Instr*>(stop_pc); - if (stop_instr->ConditionField() == special_condition) { - stop_instr->SetInstructionBits(kNopInstr); + Instr* msg_address = + reinterpret_cast<Instr*>(stop_pc + Instr::kInstrSize); + if ((argc == 2) && (strcmp(arg1, "unstop") == 0)) { + // Remove the current stop. + if (sim_->isStopInstruction(stop_instr)) { + stop_instr->SetInstructionBits(kNopInstr); + msg_address->SetInstructionBits(kNopInstr); + } else { + PrintF("Not at debugger stop.\n"); + } + } else if (argc == 3) { + // Print information about all/the specified breakpoint(s). + if (strcmp(arg1, "info") == 0) { + if (strcmp(arg2, "all") == 0) { + PrintF("Stop information:\n"); + for (uint32_t i = 0; i < sim_->kNumOfWatchedStops; i++) { + sim_->PrintStopInfo(i); + } + } else if (GetValue(arg2, &value)) { + sim_->PrintStopInfo(value); + } else { + PrintF("Unrecognized argument.\n"); + } + } else if (strcmp(arg1, "enable") == 0) { + // Enable all/the specified breakpoint(s). + if (strcmp(arg2, "all") == 0) { + for (uint32_t i = 0; i < sim_->kNumOfWatchedStops; i++) { + sim_->EnableStop(i); + } + } else if (GetValue(arg2, &value)) { + sim_->EnableStop(value); + } else { + PrintF("Unrecognized argument.\n"); + } + } else if (strcmp(arg1, "disable") == 0) { + // Disable all/the specified breakpoint(s). + if (strcmp(arg2, "all") == 0) { + for (uint32_t i = 0; i < sim_->kNumOfWatchedStops; i++) { + sim_->DisableStop(i); + } + } else if (GetValue(arg2, &value)) { + sim_->DisableStop(value); + } else { + PrintF("Unrecognized argument.\n"); + } + } } else { - PrintF("Not at debugger stop."); + PrintF("Wrong usage. Use help command for more information.\n"); } } else if ((strcmp(cmd, "t") == 0) || strcmp(cmd, "trace") == 0) { ::v8::internal::FLAG_trace_sim = !::v8::internal::FLAG_trace_sim; @@ -455,11 +522,29 @@ void Debugger::Debug() { PrintF(" set a break point on the address\n"); PrintF("del\n"); PrintF(" delete the breakpoint\n"); - PrintF("unstop\n"); - PrintF(" ignore the stop instruction at the current location"); - PrintF(" from now on\n"); PrintF("trace (alias 't')\n"); PrintF(" toogle the tracing of all executed statements\n"); + PrintF("stop feature:\n"); + PrintF(" Description:\n"); + PrintF(" Stops are debug instructions inserted by\n"); + PrintF(" the Assembler::stop() function.\n"); + PrintF(" When hitting a stop, the Simulator will\n"); + PrintF(" stop and and give control to the Debugger.\n"); + PrintF(" The first %d stop codes are watched:\n", + Simulator::kNumOfWatchedStops); + PrintF(" - They can be enabled / disabled: the Simulator\n"); + PrintF(" will / won't stop when hitting them.\n"); + PrintF(" - The Simulator keeps track of how many times they \n"); + PrintF(" are met. (See the info command.) Going over a\n"); + PrintF(" disabled stop still increases its counter. \n"); + PrintF(" Commands:\n"); + PrintF(" stop info all/<code> : print infos about number <code>\n"); + PrintF(" or all stop(s).\n"); + PrintF(" stop enable/disable all/<code> : enables / disables\n"); + PrintF(" all or number <code> stop(s)\n"); + PrintF(" stop unstop\n"); + PrintF(" ignore the stop instruction at the current location\n"); + PrintF(" from now on\n"); } else { PrintF("Unknown command: %s\n", cmd); } @@ -643,9 +728,9 @@ Simulator::Simulator() { // the simulator. The external reference will be a function compiled for the // host architecture. We need to call that function instead of trying to // execute it with the simulator. We do that by redirecting the external -// reference to a swi (software-interrupt) instruction that is handled by +// reference to a svc (Supervisor Call) instruction that is handled by // the simulator. We write the original destination of the jump just at a known -// offset from the swi instruction so the simulator knows what to call. +// offset from the svc instruction so the simulator knows what to call. class Redirection { public: Redirection(void* external_function, bool fp_return) @@ -1434,8 +1519,8 @@ typedef double (*SimulatorRuntimeFPCall)(int32_t arg0, // Software interrupt instructions are used by the simulator to call into the // C-based V8 runtime. void Simulator::SoftwareInterrupt(Instr* instr) { - int swi = instr->SwiField(); - switch (swi) { + int svc = instr->SvcField(); + switch (svc) { case call_rt_redirected: { // Check if stack is aligned. Error if not aligned is reported below to // include information on the function called. @@ -1505,9 +1590,98 @@ void Simulator::SoftwareInterrupt(Instr* instr) { dbg.Debug(); break; } + // stop uses all codes greater than 1 << 23. default: { - UNREACHABLE(); - break; + if (svc >= (1 << 23)) { + uint32_t code = svc & kStopCodeMask; + if (isWatchedStop(code)) { + IncreaseStopCounter(code); + } + // Stop if it is enabled, otherwise go on jumping over the stop + // and the message address. + if (isEnabledStop(code)) { + Debugger dbg(this); + dbg.Stop(instr); + } else { + set_pc(get_pc() + 2 * Instr::kInstrSize); + } + } else { + // This is not a valid svc code. + UNREACHABLE(); + break; + } + } + } +} + + +// Stop helper functions. +bool Simulator::isStopInstruction(Instr* instr) { + return (instr->Bits(27, 24) == 0xF) && (instr->SvcField() >= stop); +} + + +bool Simulator::isWatchedStop(uint32_t code) { + ASSERT(code <= kMaxStopCode); + return code < kNumOfWatchedStops; +} + + +bool Simulator::isEnabledStop(uint32_t code) { + ASSERT(code <= kMaxStopCode); + // Unwatched stops are always enabled. + return !isWatchedStop(code) || + !(watched_stops[code].count & kStopDisabledBit); +} + + +void Simulator::EnableStop(uint32_t code) { + ASSERT(isWatchedStop(code)); + if (!isEnabledStop(code)) { + watched_stops[code].count &= ~kStopDisabledBit; + } +} + + +void Simulator::DisableStop(uint32_t code) { + ASSERT(isWatchedStop(code)); + if (isEnabledStop(code)) { + watched_stops[code].count |= kStopDisabledBit; + } +} + + +void Simulator::IncreaseStopCounter(uint32_t code) { + ASSERT(code <= kMaxStopCode); + ASSERT(isWatchedStop(code)); + if ((watched_stops[code].count & ~(1 << 31)) == 0x7fffffff) { + PrintF("Stop counter for code %i has overflowed.\n" + "Enabling this code and reseting the counter to 0.\n", code); + watched_stops[code].count = 0; + EnableStop(code); + } else { + watched_stops[code].count++; + } +} + + +// Print a stop status. +void Simulator::PrintStopInfo(uint32_t code) { + ASSERT(code <= kMaxStopCode); + if (!isWatchedStop(code)) { + PrintF("Stop not watched."); + } else { + const char* state = isEnabledStop(code) ? "Enabled" : "Disabled"; + int32_t count = watched_stops[code].count & ~kStopDisabledBit; + // Don't print the state of unused breakpoints. + if (count != 0) { + if (watched_stops[code].desc) { + PrintF("stop %i - 0x%x: \t%s, \tcounter = %i, \t%s\n", + code, code, state, count, watched_stops[code].desc); + } else { + PrintF("stop %i - 0x%x: \t%s, \tcounter = %i\n", + code, code, state, count); + } } } } @@ -2216,73 +2390,6 @@ void Simulator::DecodeType7(Instr* instr) { } -void Simulator::DecodeUnconditional(Instr* instr) { - if (instr->Bits(7, 4) == 0x0B && instr->Bits(27, 25) == 0 && instr->HasL()) { - // Load halfword instruction, either register or immediate offset. - int rd = instr->RdField(); - int rn = instr->RnField(); - int32_t rn_val = get_register(rn); - int32_t addr = 0; - int32_t offset; - if (instr->Bit(22) == 0) { - // Register offset. - int rm = instr->RmField(); - offset = get_register(rm); - } else { - // Immediate offset - offset = instr->Bits(3, 0) + (instr->Bits(11, 8) << 4); - } - switch (instr->PUField()) { - case 0: { - // Post index, negative. - ASSERT(!instr->HasW()); - addr = rn_val; - rn_val -= offset; - set_register(rn, rn_val); - break; - } - case 1: { - // Post index, positive. - ASSERT(!instr->HasW()); - addr = rn_val; - rn_val += offset; - set_register(rn, rn_val); - break; - } - case 2: { - // Pre index or offset, negative. - rn_val -= offset; - addr = rn_val; - if (instr->HasW()) { - set_register(rn, rn_val); - } - break; - } - case 3: { - // Pre index or offset, positive. - rn_val += offset; - addr = rn_val; - if (instr->HasW()) { - set_register(rn, rn_val); - } - break; - } - default: { - // The PU field is a 2-bit field. - UNREACHABLE(); - break; - } - } - // Not sign extending, so load as unsigned. - uint16_t halfword = ReadH(addr, instr); - set_register(rd, halfword); - } else { - Debugger dbg(this); - dbg.Stop(instr); - } -} - - // void Simulator::DecodeTypeVFP(Instr* instr) // The Following ARMv7 VFPv instructions are currently supported. // vmov :Sn = Rt @@ -2655,7 +2762,7 @@ void Simulator::InstructionDecode(Instr* instr) { PrintF(" 0x%08x %s\n", reinterpret_cast<intptr_t>(instr), buffer.start()); } if (instr->ConditionField() == special_condition) { - DecodeUnconditional(instr); + UNIMPLEMENTED(); } else if (ConditionallyExecute(instr)) { switch (instr->TypeField()) { case 0: diff --git a/src/arm/simulator-arm.h b/src/arm/simulator-arm.h index e0658fc9..3e023489 100644 --- a/src/arm/simulator-arm.h +++ b/src/arm/simulator-arm.h @@ -226,6 +226,15 @@ class Simulator { void HandleRList(Instr* instr, bool load); void SoftwareInterrupt(Instr* instr); + // Stop helper functions. + inline bool isStopInstruction(Instr* instr); + inline bool isWatchedStop(uint32_t bkpt_code); + inline bool isEnabledStop(uint32_t bkpt_code); + inline void EnableStop(uint32_t bkpt_code); + inline void DisableStop(uint32_t bkpt_code); + inline void IncreaseStopCounter(uint32_t bkpt_code); + void PrintStopInfo(uint32_t code); + // Read and write memory. inline uint8_t ReadBU(int32_t addr); inline int8_t ReadB(int32_t addr); @@ -252,7 +261,6 @@ class Simulator { void DecodeType5(Instr* instr); void DecodeType6(Instr* instr); void DecodeType7(Instr* instr); - void DecodeUnconditional(Instr* instr); // Support for VFP. void DecodeTypeVFP(Instr* instr); @@ -317,6 +325,23 @@ class Simulator { // Registered breakpoints. Instr* break_pc_; instr_t break_instr_; + + // A stop is watched if its code is less than kNumOfWatchedStops. + // Only watched stops support enabling/disabling and the counter feature. + static const uint32_t kNumOfWatchedStops = 256; + + // Breakpoint is disabled if bit 31 is set. + static const uint32_t kStopDisabledBit = 1 << 31; + + // A stop is enabled, meaning the simulator will stop when meeting the + // instruction, if bit 31 of watched_stops[code].count is unset. + // The value watched_stops[code].count & ~(1 << 31) indicates how many times + // the breakpoint was hit or gone through. + struct StopCoundAndDesc { + uint32_t count; + char* desc; + }; + StopCoundAndDesc watched_stops[kNumOfWatchedStops]; }; } } // namespace assembler::arm diff --git a/src/arm/stub-cache-arm.cc b/src/arm/stub-cache-arm.cc index fbad6698..5e29c2e4 100644 --- a/src/arm/stub-cache-arm.cc +++ b/src/arm/stub-cache-arm.cc @@ -43,43 +43,49 @@ static void ProbeTable(MacroAssembler* masm, Code::Flags flags, StubCache::Table table, Register name, - Register offset) { + Register offset, + Register scratch, + Register scratch2) { ExternalReference key_offset(SCTableReference::keyReference(table)); ExternalReference value_offset(SCTableReference::valueReference(table)); - Label miss; + uint32_t key_off_addr = reinterpret_cast<uint32_t>(key_offset.address()); + uint32_t value_off_addr = reinterpret_cast<uint32_t>(value_offset.address()); + + // Check the relative positions of the address fields. + ASSERT(value_off_addr > key_off_addr); + ASSERT((value_off_addr - key_off_addr) % 4 == 0); + ASSERT((value_off_addr - key_off_addr) < (256 * 4)); - // Save the offset on the stack. - __ push(offset); + Label miss; + Register offsets_base_addr = scratch; // Check that the key in the entry matches the name. - __ mov(ip, Operand(key_offset)); - __ ldr(ip, MemOperand(ip, offset, LSL, 1)); + __ mov(offsets_base_addr, Operand(key_offset)); + __ ldr(ip, MemOperand(offsets_base_addr, offset, LSL, 1)); __ cmp(name, ip); __ b(ne, &miss); // Get the code entry from the cache. - __ mov(ip, Operand(value_offset)); - __ ldr(offset, MemOperand(ip, offset, LSL, 1)); + __ add(offsets_base_addr, offsets_base_addr, + Operand(value_off_addr - key_off_addr)); + __ ldr(scratch2, MemOperand(offsets_base_addr, offset, LSL, 1)); // Check that the flags match what we're looking for. - __ ldr(offset, FieldMemOperand(offset, Code::kFlagsOffset)); - __ and_(offset, offset, Operand(~Code::kFlagsNotUsedInLookup)); - __ cmp(offset, Operand(flags)); + __ ldr(scratch2, FieldMemOperand(scratch2, Code::kFlagsOffset)); + __ bic(scratch2, scratch2, Operand(Code::kFlagsNotUsedInLookup)); + __ cmp(scratch2, Operand(flags)); __ b(ne, &miss); - // Restore offset and re-load code entry from cache. - __ pop(offset); - __ mov(ip, Operand(value_offset)); - __ ldr(offset, MemOperand(ip, offset, LSL, 1)); + // Re-load code entry from cache. + __ ldr(offset, MemOperand(offsets_base_addr, offset, LSL, 1)); // Jump to the first instruction in the code stub. __ add(offset, offset, Operand(Code::kHeaderSize - kHeapObjectTag)); __ Jump(offset); - // Miss: Restore offset and fall through. + // Miss: fall through. __ bind(&miss); - __ pop(offset); } @@ -201,7 +207,8 @@ void StubCache::GenerateProbe(MacroAssembler* masm, Register receiver, Register name, Register scratch, - Register extra) { + Register extra, + Register extra2) { Label miss; // Make sure that code is valid. The shifting code relies on the @@ -214,6 +221,18 @@ void StubCache::GenerateProbe(MacroAssembler* masm, // Make sure that there are no register conflicts. ASSERT(!scratch.is(receiver)); ASSERT(!scratch.is(name)); + ASSERT(!extra.is(receiver)); + ASSERT(!extra.is(name)); + ASSERT(!extra.is(scratch)); + ASSERT(!extra2.is(receiver)); + ASSERT(!extra2.is(name)); + ASSERT(!extra2.is(scratch)); + ASSERT(!extra2.is(extra)); + + // Check scratch, extra and extra2 registers are valid. + ASSERT(!scratch.is(no_reg)); + ASSERT(!extra.is(no_reg)); + ASSERT(!extra2.is(no_reg)); // Check that the receiver isn't a smi. __ tst(receiver, Operand(kSmiTagMask)); @@ -229,7 +248,7 @@ void StubCache::GenerateProbe(MacroAssembler* masm, Operand((kPrimaryTableSize - 1) << kHeapObjectTagSize)); // Probe the primary table. - ProbeTable(masm, flags, kPrimary, name, scratch); + ProbeTable(masm, flags, kPrimary, name, scratch, extra, extra2); // Primary miss: Compute hash for secondary probe. __ sub(scratch, scratch, Operand(name)); @@ -239,7 +258,7 @@ void StubCache::GenerateProbe(MacroAssembler* masm, Operand((kSecondaryTableSize - 1) << kHeapObjectTagSize)); // Probe the secondary table. - ProbeTable(masm, flags, kSecondary, name, scratch); + ProbeTable(masm, flags, kSecondary, name, scratch, extra, extra2); // Cache miss: Fall-through and let caller handle the miss by // entering the runtime system. diff --git a/src/assembler.cc b/src/assembler.cc index ce90dcea..7493673e 100644 --- a/src/assembler.cc +++ b/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/src/assembler.h b/src/assembler.h index 66811777..09159fed 100644 --- a/src/assembler.h +++ b/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) { @@ -140,6 +140,7 @@ bool FunctionLiteral::AllowsLazyCompilation() { ObjectLiteral::Property::Property(Literal* key, Expression* value) { + emit_store_ = true; key_ = key; value_ = value; Object* k = *key->handle(); @@ -156,6 +157,7 @@ ObjectLiteral::Property::Property(Literal* key, Expression* value) { ObjectLiteral::Property::Property(bool is_getter, FunctionLiteral* value) { + emit_store_ = true; key_ = new Literal(value->name()); value_ = value; kind_ = is_getter ? GETTER : SETTER; @@ -169,6 +171,78 @@ bool ObjectLiteral::Property::IsCompileTimeValue() { } +void ObjectLiteral::Property::set_emit_store(bool emit_store) { + emit_store_ = emit_store; +} + + +bool ObjectLiteral::Property::emit_store() { + return emit_store_; +} + + +bool IsEqualString(void* first, void* second) { + Handle<String> h1(reinterpret_cast<String**>(first)); + Handle<String> h2(reinterpret_cast<String**>(second)); + return (*h1)->Equals(*h2); +} + +bool IsEqualSmi(void* first, void* second) { + Handle<Smi> h1(reinterpret_cast<Smi**>(first)); + Handle<Smi> h2(reinterpret_cast<Smi**>(second)); + return (*h1)->value() == (*h2)->value(); +} + +void ObjectLiteral::CalculateEmitStore() { + HashMap properties(&IsEqualString); + HashMap elements(&IsEqualSmi); + for (int i = this->properties()->length() - 1; i >= 0; i--) { + ObjectLiteral::Property* property = this->properties()->at(i); + Literal* literal = property->key(); + Handle<Object> handle = literal->handle(); + + if (handle->IsNull()) { + continue; + } + + uint32_t hash; + HashMap* table; + void* key; + uint32_t index; + if (handle->IsSymbol()) { + Handle<String> name(String::cast(*handle)); + ASSERT(!name->AsArrayIndex(&index)); + key = name.location(); + hash = name->Hash(); + table = &properties; + } else if (handle->ToArrayIndex(&index)) { + key = handle.location(); + hash = index; + table = &elements; + } else { + ASSERT(handle->IsNumber()); + double num = handle->Number(); + char arr[100]; + Vector<char> buffer(arr, ARRAY_SIZE(arr)); + const char* str = DoubleToCString(num, buffer); + Handle<String> name = Factory::NewStringFromAscii(CStrVector(str)); + key = name.location(); + hash = name->Hash(); + table = &properties; + } + // If the key of a computed property is in the table, do not emit + // a store for the property later. + if (property->kind() == ObjectLiteral::Property::COMPUTED) { + if (table->Lookup(literal, hash, false) != NULL) { + property->set_emit_store(false); + } + } + // Add key to the table. + table->Lookup(literal, hash, true); + } +} + + void TargetCollector::AddTarget(BreakTarget* target) { // Add the label to the collector, but discard duplicates. int length = targets_->length(); @@ -832,10 +832,14 @@ class ObjectLiteral: public MaterializedLiteral { bool IsCompileTimeValue(); + void set_emit_store(bool emit_store); + bool emit_store(); + private: Literal* key_; Expression* value_; Kind kind_; + bool emit_store_; }; ObjectLiteral(Handle<FixedArray> constant_properties, @@ -858,6 +862,12 @@ class ObjectLiteral: public MaterializedLiteral { bool fast_elements() const { return fast_elements_; } + + // Mark all computed expressions that are bound to a key that + // is shadowed by a later occurrence of the same key. For the + // marked expressions, no store code is emitted. + void CalculateEmitStore(); + private: Handle<FixedArray> constant_properties_; ZoneList<Property*>* properties_; diff --git a/src/builtins.cc b/src/builtins.cc index 52d5530c..aede3020 100644 --- a/src/builtins.cc +++ b/src/builtins.cc @@ -1014,20 +1014,18 @@ MUST_USE_RESULT static MaybeObject* HandleApiCallHelper( Object* data_obj = call_data->data(); Object* result; - Handle<Object> data_handle(data_obj); - v8::Local<v8::Value> data = v8::Utils::ToLocal(data_handle); - ASSERT(raw_holder->IsJSObject()); - v8::Local<v8::Function> callee = v8::Utils::ToLocal(function); - Handle<JSObject> holder_handle(JSObject::cast(raw_holder)); - v8::Local<v8::Object> holder = v8::Utils::ToLocal(holder_handle); LOG(ApiObjectAccess("call", JSObject::cast(*args.receiver()))); + ASSERT(raw_holder->IsJSObject()); + + CustomArguments custom; + v8::ImplementationUtilities::PrepareArgumentsData(custom.end(), + data_obj, *function, raw_holder); + v8::Arguments new_args = v8::ImplementationUtilities::NewArguments( - data, - holder, - callee, - is_construct, - reinterpret_cast<void**>(&args[0] - 1), - args.length() - 1); + custom.end(), + &args[0] - 1, + args.length() - 1, + is_construct); v8::Handle<v8::Value> value; { @@ -1089,26 +1087,22 @@ BUILTIN(FastHandleApiCall) { Handle<JSFunction> function = args.at<JSFunction>(args_length); Object* callback_obj = args[args_length + 1]; - Handle<Object> data_handle = args.at<Object>(args_length + 2); + Handle<Object> data = args.at<Object>(args_length + 2); Handle<JSObject> checked_holder = args.at<JSObject>(args_length + 3); #ifdef DEBUG VerifyTypeCheck(checked_holder, function); #endif - v8::Local<v8::Object> holder = v8::Utils::ToLocal(checked_holder); - v8::Local<v8::Function> callee = v8::Utils::ToLocal(function); - v8::InvocationCallback callback = - v8::ToCData<v8::InvocationCallback>(callback_obj); - v8::Local<v8::Value> data = v8::Utils::ToLocal(data_handle); + CustomArguments custom; + v8::ImplementationUtilities::PrepareArgumentsData(custom.end(), + *data, *function, *checked_holder); v8::Arguments new_args = v8::ImplementationUtilities::NewArguments( - data, - holder, - callee, - is_construct, - reinterpret_cast<void**>(&args[0] - 1), - args_length - 1); + custom.end(), + &args[0] - 1, + args_length - 1, + is_construct); HandleScope scope; Object* result; @@ -1119,6 +1113,9 @@ BUILTIN(FastHandleApiCall) { #ifdef ENABLE_LOGGING_AND_PROFILING state.set_external_callback(v8::ToCData<Address>(callback_obj)); #endif + v8::InvocationCallback callback = + v8::ToCData<v8::InvocationCallback>(callback_obj); + value = callback(new_args); } if (value.IsEmpty()) { @@ -1161,23 +1158,20 @@ MUST_USE_RESULT static MaybeObject* HandleApiCallAsFunctionOrConstructor( v8::ToCData<v8::InvocationCallback>(callback_obj); // Get the data for the call and perform the callback. - Object* data_obj = call_data->data(); Object* result; - { HandleScope scope; - v8::Local<v8::Object> self = - v8::Utils::ToLocal(Handle<JSObject>::cast(args.receiver())); - Handle<Object> data_handle(data_obj); - v8::Local<v8::Value> data = v8::Utils::ToLocal(data_handle); - Handle<JSFunction> callee_handle(constructor); - v8::Local<v8::Function> callee = v8::Utils::ToLocal(callee_handle); - LOG(ApiObjectAccess("call non-function", JSObject::cast(*args.receiver()))); + { + HandleScope scope; + + LOG(ApiObjectAccess("call non-function", obj)); + + CustomArguments custom; + v8::ImplementationUtilities::PrepareArgumentsData(custom.end(), + call_data->data(), constructor, obj); v8::Arguments new_args = v8::ImplementationUtilities::NewArguments( - data, - self, - callee, - is_construct_call, - reinterpret_cast<void**>(&args[0] - 1), - args.length() - 1); + custom.end(), + &args[0] - 1, + args.length() - 1, + is_construct_call); v8::Handle<v8::Value> value; { // Leaving JavaScript. diff --git a/src/checks.cc b/src/checks.cc index b5df316d..1ab8802e 100644 --- a/src/checks.cc +++ b/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/src/checks.h b/src/checks.h index 5ea59920..e0704774 100644 --- a/src/checks.h +++ b/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/src/codegen.cc b/src/codegen.cc index bda697ab..2e324180 100644 --- a/src/codegen.cc +++ b/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); } } diff --git a/src/compiler.cc b/src/compiler.cc index 6cc09713..29bbbc70 100755 --- a/src/compiler.cc +++ b/src/compiler.cc @@ -152,10 +152,8 @@ static Handle<SharedFunctionInfo> MakeFunctionInfo(CompilationInfo* info) { script->set_context_data((*i::Top::global_context())->data()); #ifdef ENABLE_DEBUGGER_SUPPORT - if (info->is_eval() || info->is_json()) { - Script::CompilationType compilation_type = info->is_json() - ? Script::COMPILATION_TYPE_JSON - : Script::COMPILATION_TYPE_EVAL; + if (info->is_eval()) { + Script::CompilationType compilation_type = Script::COMPILATION_TYPE_EVAL; script->set_compilation_type(Smi::FromInt(compilation_type)); // For eval scripts add information on the function from which eval was // called. @@ -178,7 +176,7 @@ static Handle<SharedFunctionInfo> MakeFunctionInfo(CompilationInfo* info) { // Only allow non-global compiles for eval. ASSERT(info->is_eval() || info->is_global()); - if (!Parser::Parse(info)) return Handle<SharedFunctionInfo>::null(); + if (!ParserApi::Parse(info)) return Handle<SharedFunctionInfo>::null(); // Measure how long it takes to do the compilation; only take the // rest of the function into account to avoid overlap with the @@ -281,9 +279,8 @@ 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 = Parser::PartialPreParse(source, NULL, extension); + pre_data = ParserApi::PartialPreParse(source, NULL, extension); } // Create a script object describing the script to be compiled. @@ -323,13 +320,7 @@ Handle<SharedFunctionInfo> Compiler::Compile(Handle<String> source, Handle<SharedFunctionInfo> Compiler::CompileEval(Handle<String> source, Handle<Context> context, - bool is_global, - ValidationState validate) { - // Note that if validation is required then no path through this function - // is allowed to return a value without validating that the input is legal - // json. - bool is_json = (validate == VALIDATE_JSON); - + bool is_global) { int source_length = source->length(); Counters::total_eval_size.Increment(source_length); Counters::total_compile_size.Increment(source_length); @@ -338,13 +329,9 @@ Handle<SharedFunctionInfo> Compiler::CompileEval(Handle<String> source, VMState state(COMPILER); // Do a lookup in the compilation cache; if the entry is not there, invoke - // the compiler and add the result to the cache. If we're evaluating json - // we bypass the cache since we can't be sure a potential value in the - // cache has been validated. + // the compiler and add the result to the cache. Handle<SharedFunctionInfo> result; - if (!is_json) { - result = CompilationCache::LookupEval(source, context, is_global); - } + result = CompilationCache::LookupEval(source, context, is_global); if (result.is_null()) { // Create a script object describing the script to be compiled. @@ -352,12 +339,9 @@ Handle<SharedFunctionInfo> Compiler::CompileEval(Handle<String> source, CompilationInfo info(script); info.MarkAsEval(); if (is_global) info.MarkAsGlobal(); - if (is_json) info.MarkAsJson(); info.SetCallingContext(context); result = MakeFunctionInfo(&info); - if (!result.is_null() && !is_json) { - // For json it's unlikely that we'll ever see exactly the same string - // again so we don't use the compilation cache. + if (!result.is_null()) { CompilationCache::PutEval(source, context, is_global, result); } } @@ -379,7 +363,7 @@ bool Compiler::CompileLazy(CompilationInfo* info) { Counters::total_compile_size.Increment(compiled_size); // Generate the AST for the lazily compiled function. - if (Parser::Parse(info)) { + if (ParserApi::Parse(info)) { // Measure how long it takes to do the lazy compilation; only take the // rest of the function into account to avoid overlap with the lazy // parsing statistics. diff --git a/src/compiler.h b/src/compiler.h index d6f4e69d..20868e54 100644 --- a/src/compiler.h +++ b/src/compiler.h @@ -49,7 +49,6 @@ class CompilationInfo BASE_EMBEDDED { bool is_lazy() const { return (flags_ & IsLazy::mask()) != 0; } bool is_eval() const { return (flags_ & IsEval::mask()) != 0; } bool is_global() const { return (flags_ & IsGlobal::mask()) != 0; } - bool is_json() const { return (flags_ & IsJson::mask()) != 0; } bool is_in_loop() const { return (flags_ & IsInLoop::mask()) != 0; } FunctionLiteral* function() const { return function_; } Scope* scope() const { return scope_; } @@ -69,10 +68,6 @@ class CompilationInfo BASE_EMBEDDED { ASSERT(!is_lazy()); flags_ |= IsGlobal::encode(true); } - void MarkAsJson() { - ASSERT(!is_lazy()); - flags_ |= IsJson::encode(true); - } void MarkAsInLoop() { ASSERT(is_lazy()); flags_ |= IsInLoop::encode(true); @@ -108,16 +103,15 @@ class CompilationInfo BASE_EMBEDDED { // Flags that can be set for eager compilation. class IsEval: public BitField<bool, 1, 1> {}; class IsGlobal: public BitField<bool, 2, 1> {}; - class IsJson: public BitField<bool, 3, 1> {}; // Flags that can be set for lazy compilation. - class IsInLoop: public BitField<bool, 4, 1> {}; + class IsInLoop: public BitField<bool, 3, 1> {}; unsigned flags_; // Fields filled in by the compilation pipeline. // AST filled in by the parser. FunctionLiteral* function_; - // The scope of the function literal as a convenience. Set to indidicate + // The scope of the function literal as a convenience. Set to indicate // that scopes have been analyzed. Scope* scope_; // The compiled code. @@ -153,8 +147,6 @@ class CompilationInfo BASE_EMBEDDED { class Compiler : public AllStatic { public: - enum ValidationState { DONT_VALIDATE_JSON, VALIDATE_JSON }; - // All routines return a JSFunction. // If an error occurs an exception is raised and // the return handle contains NULL. @@ -172,8 +164,7 @@ class Compiler : public AllStatic { // Compile a String source within a context for Eval. static Handle<SharedFunctionInfo> CompileEval(Handle<String> source, Handle<Context> context, - bool is_global, - ValidationState validation); + bool is_global); // Compile from function info (used for lazy compilation). Returns true on // success and false if the compilation resulted in a stack overflow. diff --git a/src/conversions.cc b/src/conversions.cc index 790e807a..4cc67448 100644 --- a/src/conversions.cc +++ b/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/src/debug-debugger.js b/src/debug-debugger.js index a0c68081..d091991a 100644 --- a/src/debug-debugger.js +++ b/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.'); @@ -1301,7 +1297,7 @@ DebugCommandProcessor.prototype.processDebugJSONRequest = function(json_request) try { try { // Convert the JSON string to an object. - request = %CompileString('(' + json_request + ')', false)(); + request = %CompileString('(' + json_request + ')')(); // Create an initial response. response = this.createResponse(request); @@ -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/src/debug.cc b/src/debug.cc index 5c6ddbe3..f3bf954d 100644 --- a/src/debug.cc +++ b/src/debug.cc @@ -1464,8 +1464,7 @@ bool Debug::IsSourceBreakStub(Code* code) { // location. bool Debug::IsBreakStub(Code* code) { CodeStub::Major major_key = CodeStub::GetMajorKey(code); - return major_key == CodeStub::CallFunction || - major_key == CodeStub::StackCheck; + return major_key == CodeStub::CallFunction; } @@ -1503,8 +1502,7 @@ Handle<Code> Debug::FindDebugBreak(Handle<Code> code, RelocInfo::Mode mode) { return result; } if (code->kind() == Code::STUB) { - ASSERT(code->major_key() == CodeStub::CallFunction || - code->major_key() == CodeStub::StackCheck); + ASSERT(code->major_key() == CodeStub::CallFunction); Handle<Code> result = Handle<Code>(Builtins::builtin(Builtins::StubNoRegisters_DebugBreak)); return result; @@ -1841,6 +1839,7 @@ bool Debug::IsDebugGlobal(GlobalObject* global) { void Debug::ClearMirrorCache() { + PostponeInterruptsScope postpone; HandleScope scope; ASSERT(Top::context() == *Debug::debug_context()); diff --git a/src/execution.cc b/src/execution.cc index 3bbac0fa..885bf63c 100644 --- a/src/execution.cc +++ b/src/execution.cc @@ -797,6 +797,7 @@ v8::Handle<v8::Value> ExternalizeStringExtension::Externalize( if (result && !string->IsSymbol()) { i::ExternalStringTable::AddString(*string); } + if (!result) delete resource; } else { uc16* data = new uc16[string->length()]; String::WriteToFlat(*string, data, 0, string->length()); @@ -806,6 +807,7 @@ v8::Handle<v8::Value> ExternalizeStringExtension::Externalize( if (result && !string->IsSymbol()) { i::ExternalStringTable::AddString(*string); } + if (!result) delete resource; } if (!result) { return v8::ThrowException(v8::String::New("externalizeString() failed.")); diff --git a/src/flag-definitions.h b/src/flag-definitions.h index 2474c62b..54501ec9 100644 --- a/src/flag-definitions.h +++ b/src/flag-definitions.h @@ -140,6 +140,9 @@ DEFINE_bool(stack_trace_on_abort, true, // codegen-ia32.cc / codegen-arm.cc DEFINE_bool(trace, false, "trace function calls") DEFINE_bool(defer_negation, true, "defer negation operation") +DEFINE_bool(mask_constants_with_cookie, + true, + "use random jit cookie to mask large constants") // codegen.cc DEFINE_bool(lazy, true, "use lazy compilation") diff --git a/src/full-codegen.cc b/src/full-codegen.cc index 97987c27..c770e189 100644 --- a/src/full-codegen.cc +++ b/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/src/full-codegen.h b/src/full-codegen.h index 201507b2..a3270aa7 100644 --- a/src/full-codegen.h +++ b/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/src/global-handles.cc b/src/global-handles.cc index 9ede9085..53398409 100644 --- a/src/global-handles.cc +++ b/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/src/global-handles.h b/src/global-handles.h index 659f86ec..37b2b445 100644 --- a/src/global-handles.h +++ b/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/src/globals.h b/src/globals.h index c218f80d..a74b6c79 100644 --- a/src/globals.h +++ b/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/src/heap-inl.h b/src/heap-inl.h index 15feb9d5..ba50c0f7 100644 --- a/src/heap-inl.h +++ b/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/src/heap.cc b/src/heap.cc index fc908665..226a2022 100644 --- a/src/heap.cc +++ b/src/heap.cc @@ -429,7 +429,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 +466,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 +481,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 +493,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; } @@ -581,25 +609,22 @@ void Heap::EnsureFromSpaceIsCommitted() { } -class ClearThreadJSFunctionResultCachesVisitor: public ThreadVisitor { - virtual void VisitThread(ThreadLocalTop* top) { - Context* context = top->context_; - if (context == NULL) return; +void Heap::ClearJSFunctionResultCaches() { + if (Bootstrapper::IsActive()) return; + Object* context = global_contexts_list_; + while (!context->IsUndefined()) { + // Get the caches for this context: FixedArray* caches = - context->global()->global_context()->jsfunction_result_caches(); + Context::cast(context)->jsfunction_result_caches(); + // Clear the caches: int length = caches->length(); for (int i = 0; i < length; i++) { JSFunctionResultCache::cast(caches->get(i))->Clear(); } + // Get the next context: + context = Context::cast(context)->get(Context::NEXT_CONTEXT_LINK); } -}; - - -void Heap::ClearJSFunctionResultCaches() { - if (Bootstrapper::IsActive()) return; - ClearThreadJSFunctionResultCachesVisitor visitor; - ThreadManager::IterateArchivedThreads(&visitor); } @@ -656,8 +681,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()); } @@ -723,7 +750,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. @@ -750,6 +778,8 @@ void Heap::PerformGarbageCollection(GarbageCollector collector, global_gc_epilogue_callback_(); } VerifySymbolTable(); + + return next_gc_likely_to_collect_more; } @@ -705,13 +705,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_; } @@ -1246,7 +1255,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/src/ia32/assembler-ia32.cc b/src/ia32/assembler-ia32.cc index 019f478a..125f503b 100644 --- a/src/ia32/assembler-ia32.cc +++ b/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/src/ia32/assembler-ia32.h b/src/ia32/assembler-ia32.h index 5286788f..624be0c3 100644 --- a/src/ia32/assembler-ia32.h +++ b/src/ia32/assembler-ia32.h @@ -847,17 +847,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 +863,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 +943,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/src/ia32/codegen-ia32.cc b/src/ia32/codegen-ia32.cc index f2ac7f70..72953407 100644 --- a/src/ia32/codegen-ia32.cc +++ b/src/ia32/codegen-ia32.cc @@ -153,7 +153,8 @@ CodeGenerator::CodeGenerator(MacroAssembler* masm) in_safe_int32_mode_(false), safe_int32_mode_enabled_(true), function_return_is_shadowed_(false), - in_spilled_code_(false) { + in_spilled_code_(false), + jit_cookie_((FLAG_mask_constants_with_cookie) ? V8::Random() : 0) { } @@ -3733,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 { @@ -5363,16 +5364,16 @@ void CodeGenerator::VisitLiteral(Literal* node) { void CodeGenerator::PushUnsafeSmi(Handle<Object> value) { ASSERT(value->IsSmi()); int bits = reinterpret_cast<int>(*value); - __ push(Immediate(bits & 0x0000FFFF)); - __ or_(Operand(esp, 0), Immediate(bits & 0xFFFF0000)); + __ push(Immediate(bits ^ jit_cookie_)); + __ xor_(Operand(esp, 0), Immediate(jit_cookie_)); } void CodeGenerator::StoreUnsafeSmiToLocal(int offset, Handle<Object> value) { ASSERT(value->IsSmi()); int bits = reinterpret_cast<int>(*value); - __ mov(Operand(ebp, offset), Immediate(bits & 0x0000FFFF)); - __ or_(Operand(ebp, offset), Immediate(bits & 0xFFFF0000)); + __ mov(Operand(ebp, offset), Immediate(bits ^ jit_cookie_)); + __ xor_(Operand(ebp, offset), Immediate(jit_cookie_)); } @@ -5380,8 +5381,8 @@ void CodeGenerator::MoveUnsafeSmi(Register target, Handle<Object> value) { ASSERT(target.is_valid()); ASSERT(value->IsSmi()); int bits = reinterpret_cast<int>(*value); - __ Set(target, Immediate(bits & 0x0000FFFF)); - __ or_(target, bits & 0xFFFF0000); + __ Set(target, Immediate(bits ^ jit_cookie_)); + __ xor_(target, jit_cookie_); } @@ -5559,6 +5560,11 @@ void CodeGenerator::VisitObjectLiteral(ObjectLiteral* node) { } frame_->Push(&clone); + // Mark all computed expressions that are bound to a key that + // is shadowed by a later occurrence of the same key. For the + // marked expressions, no store code is emitted. + node->CalculateEmitStore(); + for (int i = 0; i < node->properties()->length(); i++) { ObjectLiteral::Property* property = node->properties()->at(i); switch (property->kind()) { @@ -5573,24 +5579,32 @@ void CodeGenerator::VisitObjectLiteral(ObjectLiteral* node) { // Duplicate the object as the IC receiver. frame_->Dup(); Load(property->value()); - Result ignored = - frame_->CallStoreIC(Handle<String>::cast(key), false); - // A test eax instruction following the store IC call would - // indicate the presence of an inlined version of the - // store. Add a nop to indicate that there is no such - // inlined version. - __ nop(); + if (property->emit_store()) { + Result ignored = + frame_->CallStoreIC(Handle<String>::cast(key), false); + // A test eax instruction following the store IC call would + // indicate the presence of an inlined version of the + // store. Add a nop to indicate that there is no such + // inlined version. + __ nop(); + } else { + frame_->Drop(2); + } break; } // Fall through } case ObjectLiteral::Property::PROTOTYPE: { - // Duplicate the object as an argument to the runtime call. - frame_->Dup(); - Load(property->key()); - Load(property->value()); - Result ignored = frame_->CallRuntime(Runtime::kSetProperty, 3); - // Ignore the result. + // Duplicate the object as an argument to the runtime call. + frame_->Dup(); + Load(property->key()); + Load(property->value()); + if (property->emit_store()) { + // Ignore the result. + Result ignored = frame_->CallRuntime(Runtime::kSetProperty, 3); + } else { + frame_->Drop(3); + } break; } case ObjectLiteral::Property::SETTER: { @@ -7278,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) diff --git a/src/ia32/codegen-ia32.h b/src/ia32/codegen-ia32.h index b0724092..5a12e10e 100644 --- a/src/ia32/codegen-ia32.h +++ b/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); @@ -785,6 +780,11 @@ class CodeGenerator: public AstVisitor { // in a spilled state. bool in_spilled_code_; + // A cookie that is used for JIT IMM32 Encoding. Initialized to a + // random number when the command-line + // FLAG_mask_constants_with_cookie is true, zero otherwise. + int jit_cookie_; + friend class VirtualFrame; friend class JumpTarget; friend class Reference; diff --git a/src/ia32/full-codegen-ia32.cc b/src/ia32/full-codegen-ia32.cc index 150df995..1ea719d7 100644 --- a/src/ia32/full-codegen-ia32.cc +++ b/src/ia32/full-codegen-ia32.cc @@ -1202,6 +1202,11 @@ void FullCodeGenerator::VisitObjectLiteral(ObjectLiteral* expr) { // result_saved is false the result is in eax. bool result_saved = false; + // Mark all computed expressions that are bound to a key that + // is shadowed by a later occurrence of the same key. For the + // marked expressions, no store code is emitted. + expr->CalculateEmitStore(); + for (int i = 0; i < expr->properties()->length(); i++) { ObjectLiteral::Property* property = expr->properties()->at(i); if (property->IsCompileTimeValue()) continue; @@ -1221,8 +1226,10 @@ void FullCodeGenerator::VisitObjectLiteral(ObjectLiteral* expr) { VisitForAccumulatorValue(value); __ mov(ecx, Immediate(key->handle())); __ mov(edx, Operand(esp, 0)); - Handle<Code> ic(Builtins::builtin(Builtins::StoreIC_Initialize)); - EmitCallIC(ic, RelocInfo::CODE_TARGET); + if (property->emit_store()) { + Handle<Code> ic(Builtins::builtin(Builtins::StoreIC_Initialize)); + EmitCallIC(ic, RelocInfo::CODE_TARGET); + } break; } // Fall through. @@ -1230,7 +1237,11 @@ void FullCodeGenerator::VisitObjectLiteral(ObjectLiteral* expr) { __ push(Operand(esp, 0)); // Duplicate receiver. VisitForStackValue(key); VisitForStackValue(value); - __ CallRuntime(Runtime::kSetProperty, 3); + if (property->emit_store()) { + __ CallRuntime(Runtime::kSetProperty, 3); + } else { + __ Drop(3); + } break; case ObjectLiteral::Property::SETTER: case ObjectLiteral::Property::GETTER: @@ -1985,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); @@ -2006,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); @@ -2027,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); @@ -2051,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); @@ -2097,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) @@ -2141,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)); @@ -2170,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/src/ia32/ic-ia32.cc b/src/ia32/ic-ia32.cc index b5f4deef..a0bc086d 100644 --- a/src/ia32/ic-ia32.cc +++ b/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/src/ia32/stub-cache-ia32.cc b/src/ia32/stub-cache-ia32.cc index 90dabed0..042335ae 100644 --- a/src/ia32/stub-cache-ia32.cc +++ b/src/ia32/stub-cache-ia32.cc @@ -206,8 +206,10 @@ void StubCache::GenerateProbe(MacroAssembler* masm, Register receiver, Register name, Register scratch, - Register extra) { + Register extra, + Register extra2) { Label miss; + USE(extra2); // The register extra2 is not used on the ia32 platform. // Make sure that code is valid. The shifting code relies on the // entry size being 8. @@ -223,6 +225,10 @@ void StubCache::GenerateProbe(MacroAssembler* masm, ASSERT(!extra.is(name)); ASSERT(!extra.is(scratch)); + // Check scratch and extra registers are valid, and extra2 is unused. + ASSERT(!scratch.is(no_reg)); + ASSERT(extra2.is(no_reg)); + // Check that the receiver isn't a smi. __ test(receiver, Immediate(kSmiTagMask)); __ j(zero, &miss, not_taken); @@ -899,7 +905,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); @@ -1071,7 +1077,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; } } diff --git a/src/json.js b/src/json.js index a39d7c4a..5993100f 100644 --- a/src/json.js +++ b/src/json.js @@ -29,8 +29,7 @@ var $JSON = global.JSON; function ParseJSONUnfiltered(text) { var s = $String(text); - var f = %CompileString(s, true); - return f(); + return %ParseJson(s); } function Revive(holder, name, reviver) { diff --git a/src/jsregexp.cc b/src/jsregexp.cc index 3c5ddfbe..8cd13bc4 100644 --- a/src/jsregexp.cc +++ b/src/jsregexp.cc @@ -125,7 +125,8 @@ Handle<Object> RegExpImpl::Compile(Handle<JSRegExp> re, PostponeInterruptsScope postpone; RegExpCompileData parse_result; FlatStringReader reader(pattern); - if (!Parser::ParseRegExp(&reader, flags.is_multiline(), &parse_result)) { + if (!RegExpParser::ParseRegExp(&reader, flags.is_multiline(), + &parse_result)) { // Throw an exception if we fail to parse the pattern. ThrowRegExpException(re, pattern, @@ -267,7 +268,8 @@ bool RegExpImpl::CompileIrregexp(Handle<JSRegExp> re, bool is_ascii) { RegExpCompileData compile_data; FlatStringReader reader(pattern); - if (!Parser::ParseRegExp(&reader, flags.is_multiline(), &compile_data)) { + if (!RegExpParser::ParseRegExp(&reader, flags.is_multiline(), + &compile_data)) { // Throw an exception if we fail to parse the pattern. // THIS SHOULD NOT HAPPEN. We already pre-parsed it successfully once. ThrowRegExpException(re, diff --git a/src/jump-target-heavy.cc b/src/jump-target-heavy.cc index e0585e79..c3c22f1a 100644 --- a/src/jump-target-heavy.cc +++ b/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/src/jump-target-light.cc b/src/jump-target-light.cc index 19f7bfec..36dc176b 100644 --- a/src/jump-target-light.cc +++ b/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/src/liveedit.cc b/src/liveedit.cc index 49f221a4..642b3e6a 100644 --- a/src/liveedit.cc +++ b/src/liveedit.cc @@ -404,7 +404,7 @@ static void CompileScriptForTracker(Handle<Script> script) { // Build AST. CompilationInfo info(script); info.MarkAsGlobal(); - if (Parser::Parse(&info)) { + if (ParserApi::Parse(&info)) { // Compile the code. LiveEditFunctionTracker tracker(info.function()); if (Compiler::MakeCodeForLiveEdit(&info)) { @@ -164,7 +164,10 @@ void StackTracer::Trace(TickSample* sample) { int i = 0; const Address callback = VMState::external_callback(); - if (callback != NULL) { + // Surprisingly, PC can point _exactly_ to callback start, with good + // probability, and this will result in reporting fake nested + // callback call. + if (callback != NULL && callback != sample->pc) { sample->stack[i++] = callback; } diff --git a/src/mips/stub-cache-mips.cc b/src/mips/stub-cache-mips.cc index faaacbc4..91dec175 100644 --- a/src/mips/stub-cache-mips.cc +++ b/src/mips/stub-cache-mips.cc @@ -44,7 +44,8 @@ void StubCache::GenerateProbe(MacroAssembler* masm, Register receiver, Register name, Register scratch, - Register extra) { + Register extra, + Register extra2) { UNIMPLEMENTED_MIPS(); } diff --git a/src/objects-inl.h b/src/objects-inl.h index 4d210172..399ef35c 100644 --- a/src/objects-inl.h +++ b/src/objects-inl.h @@ -1952,7 +1952,9 @@ void JSFunctionResultCache::MakeZeroSize() { void JSFunctionResultCache::Clear() { int cache_size = Smi::cast(get(kCacheSizeIndex))->value(); Object** entries_start = RawField(this, OffsetOfElementAt(kEntriesIndex)); - MemsetPointer(entries_start, Heap::the_hole_value(), cache_size); + MemsetPointer(entries_start, + Heap::the_hole_value(), + cache_size - kEntriesIndex); MakeZeroSize(); } @@ -2669,6 +2671,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); \ @@ -2684,30 +2687,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/src/objects.h b/src/objects.h index 87234ea2..6029ad54 100644 --- a/src/objects.h +++ b/src/objects.h @@ -3409,8 +3409,7 @@ class Script: public Struct { // Script compilation types. enum CompilationType { COMPILATION_TYPE_HOST = 0, - COMPILATION_TYPE_EVAL = 1, - COMPILATION_TYPE_JSON = 2 + COMPILATION_TYPE_EVAL = 1 }; // [source]: the script source. diff --git a/src/parser.cc b/src/parser.cc index 180d0d24..a0f3b714 100644 --- a/src/parser.cc +++ b/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" @@ -87,112 +87,6 @@ class PositionStack { }; -template <typename T, int initial_size> -class BufferedZoneList { - public: - BufferedZoneList() : list_(NULL), last_(NULL) {} - - // Adds element at end of list. This element is buffered and can - // be read using last() or removed using RemoveLast until a new Add or until - // RemoveLast or GetList has been called. - void Add(T* value) { - if (last_ != NULL) { - if (list_ == NULL) { - list_ = new ZoneList<T*>(initial_size); - } - list_->Add(last_); - } - last_ = value; - } - - T* last() { - ASSERT(last_ != NULL); - return last_; - } - - T* RemoveLast() { - ASSERT(last_ != NULL); - T* result = last_; - if (list_ != NULL && list_->length() > 0) - last_ = list_->RemoveLast(); - else - last_ = NULL; - return result; - } - - T* Get(int i) { - ASSERT(0 <= i && i < length()); - if (list_ == NULL) { - ASSERT_EQ(0, i); - return last_; - } else { - if (i == list_->length()) { - ASSERT(last_ != NULL); - return last_; - } else { - return list_->at(i); - } - } - } - - void Clear() { - list_ = NULL; - last_ = NULL; - } - - int length() { - int length = (list_ == NULL) ? 0 : list_->length(); - return length + ((last_ == NULL) ? 0 : 1); - } - - ZoneList<T*>* GetList() { - if (list_ == NULL) { - list_ = new ZoneList<T*>(initial_size); - } - if (last_ != NULL) { - list_->Add(last_); - last_ = NULL; - } - return list_; - } - - private: - ZoneList<T*>* list_; - T* last_; -}; - - -// Accumulates RegExp atoms and assertions into lists of terms and alternatives. -class RegExpBuilder: public ZoneObject { - public: - RegExpBuilder(); - void AddCharacter(uc16 character); - // "Adds" an empty expression. Does nothing except consume a - // following quantifier - void AddEmpty(); - void AddAtom(RegExpTree* tree); - void AddAssertion(RegExpTree* tree); - void NewAlternative(); // '|' - void AddQuantifierToAtom(int min, int max, RegExpQuantifier::Type type); - RegExpTree* ToRegExp(); - private: - void FlushCharacters(); - void FlushText(); - void FlushTerms(); - bool pending_empty_; - ZoneList<uc16>* characters_; - BufferedZoneList<RegExpTree, 2> terms_; - BufferedZoneList<RegExpTree, 2> text_; - BufferedZoneList<RegExpTree, 2> alternatives_; -#ifdef DEBUG - enum {ADD_NONE, ADD_CHAR, ADD_TERM, ADD_ASSERT, ADD_ATOM} last_added_; -#define LAST(x) last_added_ = x; -#else -#define LAST(x) -#endif -}; - - RegExpBuilder::RegExpBuilder() : pending_empty_(false), characters_(NULL), @@ -352,124 +246,13 @@ void RegExpBuilder::AddQuantifierToAtom(int min, } -class RegExpParser { - public: - RegExpParser(FlatStringReader* in, - Handle<String>* error, - bool multiline_mode); - RegExpTree* ParsePattern(); - RegExpTree* ParseDisjunction(); - RegExpTree* ParseGroup(); - RegExpTree* ParseCharacterClass(); - - // Parses a {...,...} quantifier and stores the range in the given - // out parameters. - bool ParseIntervalQuantifier(int* min_out, int* max_out); - - // Parses and returns a single escaped character. The character - // must not be 'b' or 'B' since they are usually handle specially. - uc32 ParseClassCharacterEscape(); - - // Checks whether the following is a length-digit hexadecimal number, - // and sets the value if it is. - bool ParseHexEscape(int length, uc32* value); - - uc32 ParseControlLetterEscape(); - uc32 ParseOctalLiteral(); - - // Tries to parse the input as a back reference. If successful it - // stores the result in the output parameter and returns true. If - // it fails it will push back the characters read so the same characters - // can be reparsed. - bool ParseBackReferenceIndex(int* index_out); - - CharacterRange ParseClassAtom(uc16* char_class); - RegExpTree* ReportError(Vector<const char> message); - void Advance(); - void Advance(int dist); - void Reset(int pos); - - // Reports whether the pattern might be used as a literal search string. - // Only use if the result of the parse is a single atom node. - bool simple(); - bool contains_anchor() { return contains_anchor_; } - void set_contains_anchor() { contains_anchor_ = true; } - int captures_started() { return captures_ == NULL ? 0 : captures_->length(); } - int position() { return next_pos_ - 1; } - bool failed() { return failed_; } - - static const int kMaxCaptures = 1 << 16; - static const uc32 kEndMarker = (1 << 21); - - private: - enum SubexpressionType { - INITIAL, - CAPTURE, // All positive values represent captures. - POSITIVE_LOOKAHEAD, - NEGATIVE_LOOKAHEAD, - GROUPING - }; - - class RegExpParserState : public ZoneObject { - public: - RegExpParserState(RegExpParserState* previous_state, - SubexpressionType group_type, - int disjunction_capture_index) - : previous_state_(previous_state), - builder_(new RegExpBuilder()), - group_type_(group_type), - disjunction_capture_index_(disjunction_capture_index) {} - // Parser state of containing expression, if any. - RegExpParserState* previous_state() { return previous_state_; } - bool IsSubexpression() { return previous_state_ != NULL; } - // RegExpBuilder building this regexp's AST. - RegExpBuilder* builder() { return builder_; } - // Type of regexp being parsed (parenthesized group or entire regexp). - SubexpressionType group_type() { return group_type_; } - // Index in captures array of first capture in this sub-expression, if any. - // Also the capture index of this sub-expression itself, if group_type - // is CAPTURE. - int capture_index() { return disjunction_capture_index_; } - private: - // Linked list implementation of stack of states. - RegExpParserState* previous_state_; - // Builder for the stored disjunction. - RegExpBuilder* builder_; - // Stored disjunction type (capture, look-ahead or grouping), if any. - SubexpressionType group_type_; - // Stored disjunction's capture index (if any). - int disjunction_capture_index_; - }; - - uc32 current() { return current_; } - bool has_more() { return has_more_; } - bool has_next() { return next_pos_ < in()->length(); } - uc32 Next(); - FlatStringReader* in() { return in_; } - void ScanForCaptures(); - uc32 current_; - 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_; -}; - - // A temporary scope stores information during parsing, just like // a plain scope. However, temporary scopes are not kept around // after parsing or referenced by syntax trees so they can be stack- // allocated and hence used by the pre-parser. class TemporaryScope BASE_EMBEDDED { public: - explicit TemporaryScope(Parser* parser); + explicit TemporaryScope(TemporaryScope** variable); ~TemporaryScope(); int NextMaterializedLiteralIndex() { @@ -518,326 +301,118 @@ class TemporaryScope BASE_EMBEDDED { int loop_count_; // Bookkeeping - Parser* parser_; + TemporaryScope** variable_; TemporaryScope* parent_; - - friend class Parser; }; -TemporaryScope::TemporaryScope(Parser* parser) +TemporaryScope::TemporaryScope(TemporaryScope** variable) : materialized_literal_count_(0), expected_property_count_(0), only_simple_this_property_assignments_(false), this_property_assignments_(Factory::empty_fixed_array()), loop_count_(0), - parser_(parser), - parent_(parser->temp_scope_) { - parser->temp_scope_ = this; + variable_(variable), + parent_(*variable) { + *variable = this; } TemporaryScope::~TemporaryScope() { - parser_->temp_scope_ = parent_; + *variable_ = parent_; } -// 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]; } + WriteNumber(id - 1); +} - 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(); - - virtual void LogSymbol(int start, Vector<const char> literal) { - if (!is_recording_) return; - int hash = vector_hash(literal); - HashMap::Entry* entry = symbol_table_.Lookup(&literal, hash, true); - int id = static_cast<int>(reinterpret_cast<intptr_t>(entry->value)); - if (id == 0) { - // Put (symbol_id_ + 1) into entry and increment it. - id = ++symbol_id_; - entry->value = reinterpret_cast<void*>(id); - Vector<Vector<const char> > symbol = symbol_entries_.AddBlock(1, literal); - entry->key = &symbol[0]; - } - WriteNumber(id - 1); - } - - virtual Vector<unsigned> ExtractData() { - int function_size = function_store_.size(); - // Add terminator to symbols, then pad to unsigned size. - int symbol_size = symbol_store_.size(); - int padding = sizeof(unsigned) - (symbol_size % sizeof(unsigned)); - symbol_store_.AddBlock(padding, ScriptDataImpl::kNumberTerminator); - symbol_size += padding; - int total_size = ScriptDataImpl::kHeaderSize + function_size - + (symbol_size / sizeof(unsigned)); - Vector<unsigned> data = Vector<unsigned>::New(total_size); - preamble_[ScriptDataImpl::kFunctionsSizeOffset] = function_size; - preamble_[ScriptDataImpl::kSymbolCountOffset] = symbol_id_; - memcpy(data.start(), preamble_, sizeof(preamble_)); - int symbol_start = ScriptDataImpl::kHeaderSize + function_size; - if (function_size > 0) { - function_store_.WriteTo(data.SubVector(ScriptDataImpl::kHeaderSize, - symbol_start)); - } - if (!has_error()) { - symbol_store_.WriteTo( - Vector<byte>::cast(data.SubVector(symbol_start, total_size))); - } - return data; - } - virtual int symbol_position() { return symbol_store_.size(); } - virtual int symbol_ids() { return symbol_id_; } - private: - static int vector_hash(Vector<const char> string) { - int hash = 0; - for (int i = 0; i < string.length(); i++) { - int c = string[i]; - hash += c; - hash += (hash << 10); - hash ^= (hash >> 6); - } - return hash; +Vector<unsigned> CompleteParserRecorder::ExtractData() { + int function_size = function_store_.size(); + // Add terminator to symbols, then pad to unsigned size. + int symbol_size = symbol_store_.size(); + int padding = sizeof(unsigned) - (symbol_size % sizeof(unsigned)); + symbol_store_.AddBlock(padding, ScriptDataImpl::kNumberTerminator); + symbol_size += padding; + int total_size = ScriptDataImpl::kHeaderSize + function_size + + (symbol_size / sizeof(unsigned)); + Vector<unsigned> data = Vector<unsigned>::New(total_size); + preamble_[ScriptDataImpl::kFunctionsSizeOffset] = function_size; + preamble_[ScriptDataImpl::kSymbolCountOffset] = symbol_id_; + memcpy(data.start(), preamble_, sizeof(preamble_)); + int symbol_start = ScriptDataImpl::kHeaderSize + function_size; + if (function_size > 0) { + function_store_.WriteTo(data.SubVector(ScriptDataImpl::kHeaderSize, + symbol_start)); } - - static bool vector_compare(void* a, void* b) { - Vector<const char>* string1 = reinterpret_cast<Vector<const char>* >(a); - Vector<const char>* string2 = reinterpret_cast<Vector<const char>* >(b); - int length = string1->length(); - if (string2->length() != length) return false; - return memcmp(string1->start(), string2->start(), length) == 0; + if (!has_error()) { + symbol_store_.WriteTo( + Vector<byte>::cast(data.SubVector(symbol_start, total_size))); } - - // Write a non-negative number to the symbol store. - void WriteNumber(int number); - - Collector<byte> symbol_store_; - Collector<Vector<const char> > symbol_entries_; - HashMap symbol_table_; - int symbol_id_; -}; + return data; +} FunctionEntry ScriptDataImpl::GetFunctionEntry(int start) { @@ -910,7 +485,7 @@ PartialParserRecorder::PartialParserRecorder() preamble_[ScriptDataImpl::kSizeOffset] = 0; ASSERT_EQ(6, ScriptDataImpl::kHeaderSize); #ifdef DEBUG - prev_start = -1; + prev_start_ = -1; #endif } @@ -961,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(); @@ -1019,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 @@ -1141,20 +608,20 @@ VariableProxy* PreParser::Declare(Handle<String> name, Variable::Mode mode, class Target BASE_EMBEDDED { public: - Target(Parser* parser, AstNode* node) - : parser_(parser), node_(node), previous_(parser_->target_stack_) { - parser_->target_stack_ = this; + Target(Target** variable, AstNode* node) + : variable_(variable), node_(node), previous_(*variable) { + *variable = this; } ~Target() { - parser_->target_stack_ = previous_; + *variable_ = previous_; } Target* previous() { return previous_; } AstNode* node() { return node_; } private: - Parser* parser_; + Target** variable_; AstNode* node_; Target* previous_; }; @@ -1162,17 +629,17 @@ class Target BASE_EMBEDDED { class TargetScope BASE_EMBEDDED { public: - explicit TargetScope(Parser* parser) - : parser_(parser), previous_(parser->target_stack_) { - parser->target_stack_ = NULL; + explicit TargetScope(Target** variable) + : variable_(variable), previous_(*variable) { + *variable = NULL; } ~TargetScope() { - parser_->target_stack_ = previous_; + *variable_ = previous_; } private: - Parser* parser_; + Target** variable_; Target* previous_; }; @@ -1184,22 +651,26 @@ class TargetScope BASE_EMBEDDED { class LexicalScope BASE_EMBEDDED { public: - LexicalScope(Parser* parser, Scope* scope) - : parser_(parser), - prev_scope_(parser->top_scope_), - prev_level_(parser->with_nesting_level_) { - parser_->top_scope_ = scope; - parser_->with_nesting_level_ = 0; + LexicalScope(Scope** scope_variable, + int* with_nesting_level_variable, + Scope* scope) + : scope_variable_(scope_variable), + with_nesting_level_variable_(with_nesting_level_variable), + prev_scope_(*scope_variable), + prev_level_(*with_nesting_level_variable) { + *scope_variable = scope; + *with_nesting_level_variable = 0; } ~LexicalScope() { - parser_->top_scope_->Leave(); - parser_->top_scope_ = prev_scope_; - parser_->with_nesting_level_ = prev_level_; + (*scope_variable_)->Leave(); + *scope_variable_ = prev_scope_; + *with_nesting_level_variable_ = prev_level_; } private: - Parser* parser_; + Scope** scope_variable_; + int* with_nesting_level_variable_; Scope* prev_scope_; int prev_level_; }; @@ -1231,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), @@ -1243,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); - TemporaryScope temp_scope(this); - 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); @@ -1293,20 +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()); - LexicalScope lexical_scope(this, scope); - TemporaryScope temp_scope(this); - ZoneListWrapper<Statement> body(16); + { 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_); + 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(), @@ -1315,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(); } @@ -1353,11 +800,12 @@ 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()); - LexicalScope lexical_scope(this, scope); - TemporaryScope temp_scope(this); + 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_); FunctionLiteralType type = info->is_expression() ? EXPRESSION : DECLARATION; @@ -1382,78 +830,24 @@ FunctionLiteral* Parser::ParseLazy(Handle<SharedFunctionInfo> info) { } -FunctionLiteral* Parser::ParseJson(Handle<String> source) { - CompilationZoneScope zone_scope(DONT_DELETE_ON_EXIT); - - HistogramTimerScope timer(&Counters::parse); - Counters::total_parse_size.Increment(source->length()); - - // Initialize parser state. - source->TryFlatten(TENURED); - scanner_.Initialize(source, JSON); - ASSERT(target_stack_ == NULL); - - FunctionLiteral* result = NULL; - Handle<String> no_name = factory()->EmptySymbol(); - - { - Scope* scope = factory()->NewScope(top_scope_, Scope::GLOBAL_SCOPE, false); - LexicalScope lexical_scope(this, scope); - TemporaryScope temp_scope(this); - bool ok = true; - Expression* expression = ParseJson(&ok); - if (ok) { - ZoneListWrapper<Statement> statement = factory()->NewList<Statement>(1); - statement.Add(new ExpressionStatement(expression)); - result = NEW(FunctionLiteral( - no_name, - top_scope_, - statement.elements(), - temp_scope.materialized_literal_count(), - temp_scope.expected_property_count(), - temp_scope.only_simple_this_property_assignments(), - temp_scope.this_property_assignments(), - 0, - 0, - source->length(), - false, - temp_scope.ContainsLoops())); - } else if (scanner().stack_overflow()) { - Top::StackOverflow(); - } +Handle<String> Parser::GetSymbol(bool* ok) { + int symbol_id = -1; + if (pre_data() != NULL) { + symbol_id = pre_data()->GetSymbolIdentifier(); } - - // Make sure the target stack is empty. - ASSERT(target_stack_ == NULL); - - // If there was a syntax error we have to get rid of the AST - // and it is not safe to do so before the scope has been deleted. - if (result == NULL) zone_scope.DeleteOnExit(); - return result; + return LookupSymbol(symbol_id, scanner_.literal()); } + void Parser::ReportMessage(const char* type, Vector<const char*> args) { Scanner::Location source_location = scanner_.location(); ReportMessageAt(source_location, type, args); } -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()); -} - - -void AstBuildingParser::ReportMessageAt(Scanner::Location source_location, - const char* type, - Vector<const char*> args) { +void Parser::ReportMessageAt(Scanner::Location source_location, + const char* type, + Vector<const char*> args) { MessageLocation location(script_, source_location.beg_pos, source_location.end_pos); Handle<JSArray> array = Factory::NewJSArray(args.length()); @@ -1465,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 { @@ -1513,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"]. @@ -1565,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(); } @@ -1723,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 :: @@ -1733,7 +1125,7 @@ void* Parser::ParseSourceElements(ZoneListWrapper<Statement>* processor, // elements. This way, all scripts and functions get their own // target stack thus avoiding illegal breaks and continues across // functions. - TargetScope scope(this); + TargetScope scope(&this->target_stack_); ASSERT(processor != NULL); InitializationBlockFinder block_finder; @@ -1755,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; @@ -1808,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); @@ -1856,8 +1248,8 @@ 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)); - Target target(this, result); + Block* result = new Block(labels, 1, false); + Target target(&this->target_stack_, result); TryStatement* statement = ParseTryStatement(CHECK_OK); if (statement) { statement->set_statement_pos(statement_pos); @@ -1886,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 @@ -1945,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 @@ -2003,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 @@ -2039,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)); } @@ -2060,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(); } @@ -2072,8 +1464,8 @@ Block* Parser::ParseBlock(ZoneStringList* labels, bool* ok) { // (ECMA-262, 3rd, 12.2) // // Construct block expecting 16 statements. - Block* result = NEW(Block(labels, 16, false)); - Target target(this, result); + Block* result = new Block(labels, 16, false); + Target target(&this->target_stack_, result); Expect(Token::LBRACE, CHECK_OK); while (peek() != Token::RBRACE) { Statement* stat = ParseStatement(NULL, CHECK_OK); @@ -2131,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 { @@ -2222,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 @@ -2241,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 @@ -2267,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(); @@ -2276,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; @@ -2318,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); } @@ -2357,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); } @@ -2376,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); } @@ -2406,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); } @@ -2439,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(); @@ -2451,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); } @@ -2465,10 +1845,10 @@ 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, &collector); + { Target target(&this->target_stack_, &collector); with_nesting_level_++; top_scope_->RecordWithStatement(); stat = ParseStatement(labels, CHECK_OK); @@ -2477,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); } @@ -2533,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); } @@ -2550,8 +1930,8 @@ SwitchStatement* Parser::ParseSwitchStatement(ZoneStringList* labels, // SwitchStatement :: // 'switch' '(' Expression ')' '{' CaseClause* '}' - SwitchStatement* statement = NEW(SwitchStatement(labels)); - Target target(this, statement); + SwitchStatement* statement = new SwitchStatement(labels); + Target target(&this->target_stack_, statement); Expect(Token::SWITCH, CHECK_OK); Expect(Token::LPAREN, CHECK_OK); @@ -2559,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; } @@ -2586,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)); } @@ -2604,11 +1984,11 @@ 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; - { Target target(this, &collector); + { Target target(&this->target_stack_, &collector); try_block = ParseBlock(NULL, CHECK_OK); } @@ -2627,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) { @@ -2642,9 +2022,9 @@ 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)); - { Target target(this, &catch_collector); + 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); } } else { @@ -2666,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; @@ -2702,8 +2080,8 @@ DoWhileStatement* Parser::ParseDoWhileStatement(ZoneStringList* labels, // 'do' Statement 'while' '(' Expression ')' ';' temp_scope_->AddLoop(); - DoWhileStatement* loop = NEW(DoWhileStatement(labels)); - Target target(this, loop); + DoWhileStatement* loop = new DoWhileStatement(labels); + Target target(&this->target_stack_, loop); Expect(Token::DO, CHECK_OK); Statement* body = ParseStatement(NULL, CHECK_OK); @@ -2735,8 +2113,8 @@ WhileStatement* Parser::ParseWhileStatement(ZoneStringList* labels, bool* ok) { // 'while' '(' Expression ')' Statement temp_scope_->AddLoop(); - WhileStatement* loop = NEW(WhileStatement(labels)); - Target target(this, loop); + WhileStatement* loop = new WhileStatement(labels); + Target target(&this->target_stack_, loop); Expect(Token::WHILE, CHECK_OK); Expect(Token::LPAREN, CHECK_OK); @@ -2765,25 +2143,20 @@ 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)); - Target target(this, loop); + ForInStatement* loop = new ForInStatement(labels); + Target target(&this->target_stack_, loop); Expect(Token::IN, CHECK_OK); Expression* enumerable = ParseExpression(true, CHECK_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; } @@ -2799,8 +2172,8 @@ 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)); - Target target(this, loop); + ForInStatement* loop = new ForInStatement(labels); + Target target(&this->target_stack_, loop); Expect(Token::IN, CHECK_OK); Expression* enumerable = ParseExpression(true, CHECK_OK); @@ -2812,14 +2185,14 @@ 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)); - Target target(this, loop); + ForStatement* loop = new ForStatement(labels); + Target target(&this->target_stack_, loop); // Parsed initializer at this point. Expect(Token::SEMICOLON, CHECK_OK); @@ -2834,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); @@ -2855,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; } @@ -2915,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); } @@ -2937,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); } @@ -3045,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); } } } @@ -3063,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); } @@ -3112,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(); @@ -3126,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); @@ -3151,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; } @@ -3175,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; } @@ -3192,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; } @@ -3210,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; } @@ -3222,7 +2593,6 @@ Expression* Parser::ParseLeftHandSideExpression(bool* ok) { } - Expression* Parser::ParseNewPrefix(PositionStack* stack, bool* ok) { // NewExpression :: // ('new')+ MemberExpression @@ -3247,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; } @@ -3289,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; } @@ -3297,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; } @@ -3306,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: @@ -3325,7 +2695,7 @@ DebuggerStatement* Parser::ParseDebuggerStatement(bool* ok) { Expect(Token::DEBUGGER, CHECK_OK); ExpectSemicolon(CHECK_OK); - return NEW(DebuggerStatement()); + return new DebuggerStatement(); } @@ -3383,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; } @@ -3429,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; } @@ -3507,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; @@ -3516,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); } @@ -3526,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; @@ -3551,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); } @@ -3703,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); @@ -3720,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); @@ -3744,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) { @@ -3755,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: { @@ -3767,7 +3127,7 @@ Expression* Parser::ParseObjectLiteral(bool* ok) { key = NewNumberLiteral(index); break; } - key = NEW(Literal(string)); + key = new Literal(string); break; } case Token::NUMBER: { @@ -3781,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(); @@ -3795,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); @@ -3812,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); @@ -3820,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, @@ -3844,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(); @@ -3872,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; } @@ -3898,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; } @@ -3908,9 +3254,10 @@ 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()); - LexicalScope lexical_scope(this, scope); - TemporaryScope temp_scope(this); + 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_); top_scope_->SetScopeName(name); // FormalParameterList :: @@ -3920,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 @@ -3939,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 @@ -3981,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 = @@ -3995,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, @@ -4016,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; @@ -4034,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 @@ -4070,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); } @@ -4118,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()); } @@ -4226,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)); } @@ -4258,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()); @@ -4281,145 +3608,165 @@ Expression* Parser::NewThrowError(Handle<String> constructor, // ---------------------------------------------------------------------------- // JSON -Expression* Parser::ParseJson(bool* ok) { - Expression* result = ParseJsonValue(CHECK_OK); - Expect(Token::EOS, CHECK_OK); +Handle<Object> JsonParser::ParseJson(Handle<String> source) { + source->TryFlatten(); + scanner_.Initialize(source, JSON); + Handle<Object> result = ParseJsonValue(); + if (result.is_null() || scanner_.Next() != Token::EOS) { + if (scanner_.stack_overflow()) { + // Scanner failed. + Top::StackOverflow(); + } else { + // Parse failed. Scanner's current token is the unexpected token. + Token::Value token = scanner_.current_token(); + + const char* message; + const char* name_opt = NULL; + + switch (token) { + case Token::EOS: + message = "unexpected_eos"; + break; + case Token::NUMBER: + message = "unexpected_token_number"; + break; + case Token::STRING: + message = "unexpected_token_string"; + break; + case Token::IDENTIFIER: + message = "unexpected_token_identifier"; + break; + default: + message = "unexpected_token"; + name_opt = Token::String(token); + ASSERT(name_opt != NULL); + break; + } + + Scanner::Location source_location = scanner_.location(); + MessageLocation location(Factory::NewScript(source), + source_location.beg_pos, + source_location.end_pos); + int argc = (name_opt == NULL) ? 0 : 1; + Handle<JSArray> array = Factory::NewJSArray(argc); + if (name_opt != NULL) { + SetElement(array, + 0, + Factory::NewStringFromUtf8(CStrVector(name_opt))); + } + Handle<Object> result = Factory::NewSyntaxError(message, array); + Top::Throw(*result, &location); + return Handle<Object>::null(); + } + } return result; } +Handle<String> JsonParser::GetString() { + int literal_length = scanner_.literal_length(); + if (literal_length == 0) { + return Factory::empty_string(); + } + const char* literal_string = scanner_.literal_string(); + Vector<const char> literal(literal_string, literal_length); + return Factory::NewStringFromUtf8(literal); +} + + // Parse any JSON value. -Expression* Parser::ParseJsonValue(bool* ok) { - Token::Value token = peek(); +Handle<Object> JsonParser::ParseJsonValue() { + Token::Value token = scanner_.Next(); switch (token) { case Token::STRING: { - Consume(Token::STRING); - int literal_length = scanner_.literal_length(); - const char* literal_string = scanner_.literal_string(); - if (literal_length == 0) { - return NEW(Literal(Factory::empty_string())); - } - Vector<const char> literal(literal_string, literal_length); - return NEW(Literal(Factory::NewStringFromUtf8(literal, TENURED))); + return GetString(); } case Token::NUMBER: { - Consume(Token::NUMBER); - ASSERT(scanner_.literal_length() > 0); double value = StringToDouble(scanner_.literal(), NO_FLAGS, // Hex, octal or trailing junk. OS::nan_value()); - return NewNumberLiteral(value); + return Factory::NewNumber(value); } case Token::FALSE_LITERAL: - Consume(Token::FALSE_LITERAL); - return NEW(Literal(Factory::false_value())); + return Factory::false_value(); case Token::TRUE_LITERAL: - Consume(Token::TRUE_LITERAL); - return NEW(Literal(Factory::true_value())); + return Factory::true_value(); case Token::NULL_LITERAL: - Consume(Token::NULL_LITERAL); - return NEW(Literal(Factory::null_value())); - case Token::LBRACE: { - Expression* result = ParseJsonObject(CHECK_OK); - return result; - } - case Token::LBRACK: { - Expression* result = ParseJsonArray(CHECK_OK); - return result; - } + return Factory::null_value(); + case Token::LBRACE: + return ParseJsonObject(); + case Token::LBRACK: + return ParseJsonArray(); default: - *ok = false; - ReportUnexpectedToken(token); - return NULL; + return ReportUnexpectedToken(); } } // Parse a JSON object. Scanner must be right after '{' token. -Expression* Parser::ParseJsonObject(bool* ok) { - Consume(Token::LBRACE); - ZoneListWrapper<ObjectLiteral::Property> properties = - factory()->NewList<ObjectLiteral::Property>(4); - int boilerplate_properties = 0; - if (peek() != Token::RBRACE) { +Handle<Object> JsonParser::ParseJsonObject() { + Handle<JSFunction> object_constructor( + Top::global_context()->object_function()); + Handle<JSObject> json_object = Factory::NewJSObject(object_constructor); + if (scanner_.peek() == Token::RBRACE) { + scanner_.Next(); + } else { do { - Expect(Token::STRING, CHECK_OK); - Handle<String> key = GetSymbol(CHECK_OK); - Expect(Token::COLON, CHECK_OK); - Expression* value = ParseJsonValue(CHECK_OK); - Literal* key_literal; + if (scanner_.Next() != Token::STRING) { + return ReportUnexpectedToken(); + } + Handle<String> key = GetString(); + if (scanner_.Next() != Token::COLON) { + return ReportUnexpectedToken(); + } + Handle<Object> value = ParseJsonValue(); + if (value.is_null()) return Handle<Object>::null(); uint32_t index; if (key->AsArrayIndex(&index)) { - key_literal = NewNumberLiteral(index); + SetElement(json_object, index, value); } else { - key_literal = NEW(Literal(key)); - } - ObjectLiteral::Property* property = - NEW(ObjectLiteral::Property(key_literal, value)); - properties.Add(property); - - if (IsBoilerplateProperty(property)) { - boilerplate_properties++; + SetProperty(json_object, key, value, NONE); } - } while (Check(Token::COMMA)); + } while (scanner_.Next() == Token::COMMA); + if (scanner_.current_token() != Token::RBRACE) { + return ReportUnexpectedToken(); + } } - Expect(Token::RBRACE, CHECK_OK); - - int literal_index = temp_scope_->NextMaterializedLiteralIndex(); - if (is_pre_parsing_) return NULL; - - Handle<FixedArray> constant_properties = - Factory::NewFixedArray(boilerplate_properties * 2, TENURED); - bool is_simple = true; - bool fast_elements = true; - int depth = 1; - BuildObjectLiteralConstantProperties(properties.elements(), - constant_properties, - &is_simple, - &fast_elements, - &depth); - return new ObjectLiteral(constant_properties, - properties.elements(), - literal_index, - is_simple, - fast_elements, - depth); + return json_object; } // Parse a JSON array. Scanner must be right after '[' token. -Expression* Parser::ParseJsonArray(bool* ok) { - Consume(Token::LBRACK); +Handle<Object> JsonParser::ParseJsonArray() { + ZoneScope zone_scope(DELETE_ON_EXIT); + ZoneList<Handle<Object> > elements(4); - ZoneListWrapper<Expression> values = factory()->NewList<Expression>(4); - if (peek() != Token::RBRACK) { + Token::Value token = scanner_.peek(); + if (token == Token::RBRACK) { + scanner_.Next(); + } else { do { - Expression* exp = ParseJsonValue(CHECK_OK); - values.Add(exp); - } while (Check(Token::COMMA)); + Handle<Object> element = ParseJsonValue(); + if (element.is_null()) return Handle<Object>::null(); + elements.Add(element); + token = scanner_.Next(); + } while (token == Token::COMMA); + if (token != Token::RBRACK) { + return ReportUnexpectedToken(); + } } - Expect(Token::RBRACK, CHECK_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 elements. + Handle<FixedArray> fast_elements = + Factory::NewFixedArray(elements.length()); - // Allocate a fixed array with all the literals. - Handle<FixedArray> literals = - Factory::NewFixedArray(values.length(), TENURED); + for (int i = 0, n = elements.length(); i < n; i++) { + fast_elements->set(i, *elements[i]); + } - bool is_simple; - int depth; - BuildArrayLiteralBoilerplateLiterals(values.elements(), - literals, - &is_simple, - &depth); - return NEW(ArrayLiteral(literals, values.elements(), - literal_index, is_simple, depth)); + return Factory::NewJSArrayWithElements(fast_elements); } - // ---------------------------------------------------------------------------- // Regular expressions @@ -4427,17 +3774,17 @@ Expression* Parser::ParseJsonArray(bool* ok) { 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); } @@ -5245,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* Parser::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) { @@ -5305,24 +4635,57 @@ int ScriptDataImpl::ReadNumber(byte** source) { } -ScriptDataImpl* Parser::PreParse(Handle<String> source, - unibrow::CharacterStream* stream, - v8::Extension* extension) { +// 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); } -bool Parser::ParseRegExp(FlatStringReader* input, - bool multiline, - RegExpCompileData* result) { +bool RegExpParser::ParseRegExp(FlatStringReader* input, + bool multiline, + RegExpCompileData* result) { ASSERT(result != NULL); RegExpParser parser(input, &result->error, multiline); RegExpTree* tree = parser.ParsePattern(); @@ -5342,19 +4705,18 @@ bool Parser::ParseRegExp(FlatStringReader* input, } -bool Parser::Parse(CompilationInfo* info) { +bool ParserApi::Parse(CompilationInfo* info) { ASSERT(info->function() == NULL); 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(); @@ -5368,11 +4730,7 @@ bool Parser::Parse(CompilationInfo* info) { ASSERT(Top::has_pending_exception()); } else { Handle<String> source = Handle<String>(String::cast(script->source())); - // JSON is always global. - ASSERT(!info->is_json() || info->is_global()); - result = info->is_json() - ? parser.ParseJson(source) - : parser.ParseProgram(source, info->is_global()); + result = parser.ParseProgram(source, info->is_global()); } } @@ -5380,6 +4738,4 @@ bool Parser::Parse(CompilationInfo* info) { return (result != NULL); } -#undef NEW - } } // namespace v8::internal diff --git a/src/parser.h b/src/parser.h index 7142551c..667410b3 100644 --- a/src/parser.h +++ b/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,13 +177,127 @@ class ScriptDataImpl : public ScriptData { }; -class Parser { +// Record only functions. +class PartialParserRecorder { public: - Parser(Handle<Script> script, bool allow_natives_syntax, - v8::Extension* extension, ParserMode is_pre_parsing, - ParserFactory* factory, ParserLog* log, ScriptDataImpl* pre_data); - virtual ~Parser() { } + 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 // function literal. Returns false (and deallocates any allocated AST // nodes) if parsing failed. @@ -199,34 +313,263 @@ class Parser { static ScriptDataImpl* PartialPreParse(Handle<String> source, unibrow::CharacterStream* stream, 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 +// a single element. The last element added is stored outside the backing list, +// and if no more than one element is ever added, the ZoneList isn't even +// allocated. +// Elements must not be NULL pointers. +template <typename T, int initial_size> +class BufferedZoneList { + public: + BufferedZoneList() : list_(NULL), last_(NULL) {} + + // Adds element at end of list. This element is buffered and can + // be read using last() or removed using RemoveLast until a new Add or until + // RemoveLast or GetList has been called. + void Add(T* value) { + if (last_ != NULL) { + if (list_ == NULL) { + list_ = new ZoneList<T*>(initial_size); + } + list_->Add(last_); + } + last_ = value; + } + + T* last() { + ASSERT(last_ != NULL); + return last_; + } + + T* RemoveLast() { + ASSERT(last_ != NULL); + T* result = last_; + if ((list_ != NULL) && (list_->length() > 0)) + last_ = list_->RemoveLast(); + else + last_ = NULL; + return result; + } + + T* Get(int i) { + ASSERT((0 <= i) && (i < length())); + if (list_ == NULL) { + ASSERT_EQ(0, i); + return last_; + } else { + if (i == list_->length()) { + ASSERT(last_ != NULL); + return last_; + } else { + return list_->at(i); + } + } + } + + void Clear() { + list_ = NULL; + last_ = NULL; + } + + int length() { + int length = (list_ == NULL) ? 0 : list_->length(); + return length + ((last_ == NULL) ? 0 : 1); + } + + ZoneList<T*>* GetList() { + if (list_ == NULL) { + list_ = new ZoneList<T*>(initial_size); + } + if (last_ != NULL) { + list_->Add(last_); + last_ = NULL; + } + return list_; + } + + private: + ZoneList<T*>* list_; + T* last_; +}; + + +// Accumulates RegExp atoms and assertions into lists of terms and alternatives. +class RegExpBuilder: public ZoneObject { + public: + RegExpBuilder(); + void AddCharacter(uc16 character); + // "Adds" an empty expression. Does nothing except consume a + // following quantifier + void AddEmpty(); + void AddAtom(RegExpTree* tree); + void AddAssertion(RegExpTree* tree); + void NewAlternative(); // '|' + void AddQuantifierToAtom(int min, int max, RegExpQuantifier::Type type); + RegExpTree* ToRegExp(); + + private: + void FlushCharacters(); + void FlushText(); + void FlushTerms(); + bool pending_empty_; + ZoneList<uc16>* characters_; + BufferedZoneList<RegExpTree, 2> terms_; + BufferedZoneList<RegExpTree, 2> text_; + BufferedZoneList<RegExpTree, 2> alternatives_; +#ifdef DEBUG + enum {ADD_NONE, ADD_CHAR, ADD_TERM, ADD_ASSERT, ADD_ATOM} last_added_; +#define LAST(x) last_added_ = x; +#else +#define LAST(x) +#endif +}; + + +class RegExpParser { + public: + RegExpParser(FlatStringReader* in, + Handle<String>* error, + bool multiline_mode); static bool ParseRegExp(FlatStringReader* input, bool multiline, RegExpCompileData* result); - // 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); + RegExpTree* ParsePattern(); + RegExpTree* ParseDisjunction(); + RegExpTree* ParseGroup(); + RegExpTree* ParseCharacterClass(); + + // Parses a {...,...} quantifier and stores the range in the given + // out parameters. + bool ParseIntervalQuantifier(int* min_out, int* max_out); + + // Parses and returns a single escaped character. The character + // must not be 'b' or 'B' since they are usually handle specially. + uc32 ParseClassCharacterEscape(); + + // Checks whether the following is a length-digit hexadecimal number, + // and sets the value if it is. + bool ParseHexEscape(int length, uc32* value); + + uc32 ParseControlLetterEscape(); + uc32 ParseOctalLiteral(); + + // Tries to parse the input as a back reference. If successful it + // stores the result in the output parameter and returns true. If + // it fails it will push back the characters read so the same characters + // can be reparsed. + bool ParseBackReferenceIndex(int* index_out); + + CharacterRange ParseClassAtom(uc16* char_class); + RegExpTree* ReportError(Vector<const char> message); + void Advance(); + void Advance(int dist); + void Reset(int pos); + + // Reports whether the pattern might be used as a literal search string. + // Only use if the result of the parse is a single atom node. + bool simple(); + bool contains_anchor() { return contains_anchor_; } + void set_contains_anchor() { contains_anchor_ = true; } + int captures_started() { return captures_ == NULL ? 0 : captures_->length(); } + int position() { return next_pos_ - 1; } + bool failed() { return failed_; } + + static const int kMaxCaptures = 1 << 16; + static const uc32 kEndMarker = (1 << 21); - void ReportMessage(const char* message, Vector<const char*> args); - virtual void ReportMessageAt(Scanner::Location loc, - const char* message, - Vector<const char*> args) = 0; + private: + enum SubexpressionType { + INITIAL, + CAPTURE, // All positive values represent captures. + POSITIVE_LOOKAHEAD, + NEGATIVE_LOOKAHEAD, + GROUPING + }; + + class RegExpParserState : public ZoneObject { + public: + RegExpParserState(RegExpParserState* previous_state, + SubexpressionType group_type, + int disjunction_capture_index) + : previous_state_(previous_state), + builder_(new RegExpBuilder()), + group_type_(group_type), + disjunction_capture_index_(disjunction_capture_index) {} + // Parser state of containing expression, if any. + RegExpParserState* previous_state() { return previous_state_; } + bool IsSubexpression() { return previous_state_ != NULL; } + // RegExpBuilder building this regexp's AST. + RegExpBuilder* builder() { return builder_; } + // Type of regexp being parsed (parenthesized group or entire regexp). + SubexpressionType group_type() { return group_type_; } + // Index in captures array of first capture in this sub-expression, if any. + // Also the capture index of this sub-expression itself, if group_type + // is CAPTURE. + int capture_index() { return disjunction_capture_index_; } + + private: + // Linked list implementation of stack of states. + RegExpParserState* previous_state_; + // Builder for the stored disjunction. + RegExpBuilder* builder_; + // Stored disjunction type (capture, look-ahead or grouping), if any. + SubexpressionType group_type_; + // Stored disjunction's capture index (if any). + int disjunction_capture_index_; + }; + + uc32 current() { return current_; } + bool has_more() { return has_more_; } + bool has_next() { return next_pos_ < in()->length(); } + uc32 Next(); + FlatStringReader* in() { return in_; } + void ScanForCaptures(); + + Handle<String>* error_; + ZoneList<RegExpCapture*>* captures_; + FlatStringReader* in_; + uc32 current_; + int next_pos_; + // The capture count is only valid after we have scanned for captures. + int capture_count_; + bool has_more_; + bool multiline_; + bool simple_; + bool contains_anchor_; + bool is_scanned_for_captures_; + bool failed_; +}; + +// ---------------------------------------------------------------------------- +// JAVASCRIPT PARSING +class Parser { + public: + Parser(Handle<Script> script, + bool allow_natives_syntax, + v8::Extension* extension, + ScriptDataImpl* pre_data); + virtual ~Parser() { } // Returns NULL if parsing failed. FunctionLiteral* ParseProgram(Handle<String> source, bool in_global_context); + FunctionLiteral* ParseLazy(Handle<SharedFunctionInfo> info); - FunctionLiteral* ParseJson(Handle<String> source); - // 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 @@ -235,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_; } @@ -265,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); @@ -378,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); @@ -389,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); @@ -411,33 +757,23 @@ class Parser { Handle<String> type, Vector< Handle<Object> > arguments); - // 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). + ZoneList<Handle<String> > symbol_cache_; - // Parse JSON input as a single JSON value. - Expression* ParseJson(bool* ok); + Handle<Script> script_; + Scanner scanner_; - // Parse a single JSON value from input (grammar production JSONValue). - // A JSON value is either a (double-quoted) string literal, a number literal, - // one of "true", "false", or "null", or an object or array literal. - Expression* ParseJsonValue(bool* ok); - // Parse a JSON object literal (grammar production JSONObject). - // An object literal is a squiggly-braced and comma separated sequence - // (possibly empty) of key/value pairs, where the key is a JSON string - // literal, the value is a JSON value, and the two are spearated by a colon. - // A JavaScript object also allows numbers and identifiers as keys. - Expression* ParseJsonObject(bool* ok); - // Parses a JSON array literal (grammar production JSONArray). An array - // literal is a square-bracketed and comma separated sequence (possibly empty) - // of JSON values. - // A JavaScript array allows leaving out values from the sequence. - Expression* ParseJsonArray(bool* ok); + Scope* top_scope_; + int with_nesting_level_; - friend class Target; - friend class TargetScope; - friend class LexicalScope; - friend class TemporaryScope; + 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_; }; @@ -472,6 +808,52 @@ 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). +class JsonParser BASE_EMBEDDED { + public: + // Parse JSON input as a single JSON value. + // Returns null handle and sets exception if parsing failed. + static Handle<Object> Parse(Handle<String> source) { + return JsonParser().ParseJson(source); + } + + private: + JsonParser() { } + ~JsonParser() { } + + // Parse a string containing a single JSON value. + Handle<Object> ParseJson(Handle<String>); + // Parse a single JSON value from input (grammar production JSONValue). + // A JSON value is either a (double-quoted) string literal, a number literal, + // one of "true", "false", or "null", or an object or array literal. + Handle<Object> ParseJsonValue(); + // Parse a JSON object literal (grammar production JSONObject). + // An object literal is a squiggly-braced and comma separated sequence + // (possibly empty) of key/value pairs, where the key is a JSON string + // literal, the value is a JSON value, and the two are separated by a colon. + // A JSON array dosn't allow numbers and identifiers as keys, like a + // JavaScript array. + Handle<Object> ParseJsonObject(); + // Parses a JSON array literal (grammar production JSONArray). An array + // literal is a square-bracketed and comma separated sequence (possibly empty) + // of JSON values. + // A JSON array doesn't allow leaving out values from the sequence, nor does + // it allow a terminal comma, like a JavaScript array does. + Handle<Object> ParseJsonArray(); + + // Mark that a parsing error has happened at the current token, and + // return a null handle. Primarily for readability. + Handle<Object> ReportUnexpectedToken() { return Handle<Object>::null(); } + // Converts the currently parsed literal to a JavaScript String. + Handle<String> GetString(); + + Scanner scanner_; +}; } } // namespace v8::internal #endif // V8_PARSER_H_ diff --git a/src/platform-linux.cc b/src/platform-linux.cc index eefaec90..89003ba8 100644 --- a/src/platform-linux.cc +++ b/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_; } @@ -856,7 +885,7 @@ void Sampler::Start() { struct sigaction sa; sa.sa_sigaction = ProfilerSignalHandler; sigemptyset(&sa.sa_mask); - sa.sa_flags = SA_SIGINFO; + sa.sa_flags = SA_RESTART | SA_SIGINFO; if (sigaction(SIGPROF, &sa, &data_->old_signal_handler_) != 0) return; data_->signal_handler_installed_ = true; diff --git a/src/preparser.h b/src/preparser.h new file mode 100644 index 00000000..44c55cf7 --- /dev/null +++ b/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/src/regexp.js b/src/regexp.js index 51f4b094..9e708fd0 100644 --- a/src/regexp.js +++ b/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/src/runtime.cc b/src/runtime.cc index f701c031..5534db55 100644 --- a/src/runtime.cc +++ b/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); @@ -7129,20 +7069,31 @@ static MaybeObject* Runtime_GlobalReceiver(Arguments args) { } +static MaybeObject* Runtime_ParseJson(Arguments args) { + HandleScope scope; + ASSERT_EQ(1, args.length()); + CONVERT_ARG_CHECKED(String, source, 0); + + Handle<Object> result = JsonParser::Parse(source); + if (result.is_null()) { + // Syntax error or stack overflow in scanner. + ASSERT(Top::has_pending_exception()); + return Failure::Exception(); + } + return *result; +} + + static MaybeObject* Runtime_CompileString(Arguments args) { HandleScope scope; - ASSERT_EQ(2, args.length()); + ASSERT_EQ(1, args.length()); CONVERT_ARG_CHECKED(String, source, 0); - CONVERT_ARG_CHECKED(Oddball, is_json, 1) // Compile source string in the global context. Handle<Context> context(Top::context()->global_context()); - Compiler::ValidationState validate = (is_json->IsTrue()) - ? Compiler::VALIDATE_JSON : Compiler::DONT_VALIDATE_JSON; Handle<SharedFunctionInfo> shared = Compiler::CompileEval(source, context, - true, - validate); + true); if (shared.is_null()) return Failure::Exception(); Handle<JSFunction> fun = Factory::NewFunctionFromSharedFunctionInfo(shared, context, NOT_TENURED); @@ -7157,8 +7108,7 @@ static ObjectPair CompileGlobalEval(Handle<String> source, Handle<SharedFunctionInfo> shared = Compiler::CompileEval( source, Handle<Context>(Top::context()), - Top::context()->IsGlobalContext(), - Compiler::DONT_VALIDATE_JSON); + Top::context()->IsGlobalContext()); if (shared.is_null()) return MakePair(Failure::Exception(), NULL); Handle<JSFunction> compiled = Factory::NewFunctionFromSharedFunctionInfo( shared, @@ -8881,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); @@ -9370,8 +9314,7 @@ static MaybeObject* Runtime_DebugEvaluate(Arguments args) { Handle<SharedFunctionInfo> shared = Compiler::CompileEval(function_source, context, - context->IsGlobalContext(), - Compiler::DONT_VALIDATE_JSON); + context->IsGlobalContext()); if (shared.is_null()) return Failure::Exception(); Handle<JSFunction> compiled_function = Factory::NewFunctionFromSharedFunctionInfo(shared, context); @@ -9442,8 +9385,7 @@ static MaybeObject* Runtime_DebugEvaluateGlobal(Arguments args) { Handle<SharedFunctionInfo> shared = Compiler::CompileEval(source, context, - true, - Compiler::DONT_VALIDATE_JSON); + true); if (shared.is_null()) return Failure::Exception(); Handle<JSFunction> compiled_function = Handle<JSFunction>(Factory::NewFunctionFromSharedFunctionInfo(shared, diff --git a/src/runtime.h b/src/runtime.h index 8057d8bd..756099b4 100644 --- a/src/runtime.h +++ b/src/runtime.h @@ -162,7 +162,9 @@ namespace internal { F(RegExpExecMultiple, 4, 1) \ F(RegExpInitializeObject, 5, 1) \ F(RegExpConstructResult, 3, 1) \ - F(RegExpCloneResult, 1, 1) \ + \ + /* JSON */ \ + F(ParseJson, 1, 1) \ \ /* Strings */ \ F(StringCharCodeAt, 2, 1) \ @@ -222,7 +224,7 @@ namespace internal { /* Numbers */ \ \ /* Globals */ \ - F(CompileString, 2, 1) \ + F(CompileString, 1, 1) \ F(GlobalPrint, 1, 1) \ \ /* Eval */ \ @@ -322,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) \ @@ -423,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) \ @@ -435,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/src/scanner-base.cc b/src/scanner-base.cc new file mode 100644 index 00000000..6e9d40e0 --- /dev/null +++ b/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/src/scanner-base.h b/src/scanner-base.h new file mode 100644 index 00000000..500870b5 --- /dev/null +++ b/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/src/scanner.cc b/src/scanner.cc index 79d63f17..a24952ac 100755 --- a/src/scanner.cc +++ b/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/src/scanner.h b/src/scanner.h index 6e5333bc..1f49fd0e 100644 --- a/src/scanner.h +++ b/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 }; @@ -296,6 +176,9 @@ class Scanner { // Returns the next token. Token::Value Next(); + // Returns the current token again. + Token::Value current_token() { return current_.token; } + // One token look-ahead (past the token returned by Next()). Token::Value peek() const { return next_.token; } diff --git a/src/string.js b/src/string.js index d97f632b..d82ce052 100644 --- a/src/string.js +++ b/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/src/stub-cache.h b/src/stub-cache.h index 07d21979..9d947e40 100644 --- a/src/stub-cache.h +++ b/src/stub-cache.h @@ -241,13 +241,15 @@ class StubCache : public AllStatic { static void Clear(); // Generate code for probing the stub cache table. - // If extra != no_reg it might be used as am extra scratch register. + // Arguments extra and extra2 may be used to pass additional scratch + // registers. Set to no_reg if not needed. static void GenerateProbe(MacroAssembler* masm, Code::Flags flags, Register receiver, Register name, Register scratch, - Register extra); + Register extra, + Register extra2 = no_reg); enum Table { kPrimary, diff --git a/src/token.h b/src/token.h index ebc7fea1..74d9539f 100644 --- a/src/token.h +++ b/src/token.h @@ -28,6 +28,8 @@ #ifndef V8_TOKEN_H_ #define V8_TOKEN_H_ +#include "checks.h" + namespace v8 { namespace internal { @@ -105,7 +105,11 @@ class ThreadLocalTop BASE_EMBEDDED { Address handler_; // try-blocks are chained through the stack #ifdef USE_SIMULATOR +#ifdef V8_TARGET_ARCH_ARM assembler::arm::Simulator* simulator_; +#elif V8_TARGET_ARCH_MIPS + assembler::mips::Simulator* simulator_; +#endif #endif // USE_SIMULATOR #ifdef ENABLE_LOGGING_AND_PROFILING diff --git a/src/utils.cc b/src/utils.cc index 45a4cd60..7096ba35 100644 --- a/src/utils.cc +++ b/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/src/utils.h b/src/utils.h index ffdb639e..069be4f8 100644 --- a/src/utils.h +++ b/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 @@ -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/src/v8natives.js b/src/v8natives.js index 88aea9c1..50a2774d 100644 --- a/src/v8natives.js +++ b/src/v8natives.js @@ -140,7 +140,7 @@ function GlobalEval(x) { 'be the global object from which eval originated'); } - var f = %CompileString(x, false); + var f = %CompileString(x); if (!IS_FUNCTION(f)) return f; return f.call(this); @@ -151,7 +151,7 @@ function GlobalEval(x) { function GlobalExecScript(expr, lang) { // NOTE: We don't care about the character casing. if (!lang || /javascript/i.test(lang)) { - var f = %CompileString(ToString(expr), false); + var f = %CompileString(ToString(expr)); f.call(%GlobalReceiver(global)); } return null; @@ -1177,7 +1177,7 @@ function NewFunction(arg1) { // length == 1 // The call to SetNewFunctionAttributes will ensure the prototype // property of the resulting function is enumerable (ECMA262, 15.3.5.2). - var f = %CompileString(source, false)(); + var f = %CompileString(source)(); %FunctionSetName(f, "anonymous"); return %SetNewFunctionAttributes(f); } diff --git a/src/v8utils.h b/src/v8utils.h new file mode 100644 index 00000000..a907c9f5 --- /dev/null +++ b/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/src/version.cc b/src/version.cc index 4017ae5e..5ef2a651 100644 --- a/src/version.cc +++ b/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 2 +#define BUILD_NUMBER 5 #define PATCH_LEVEL 0 #define CANDIDATE_VERSION false diff --git a/src/x64/assembler-x64.cc b/src/x64/assembler-x64.cc index bf5ee5bb..caed7c8a 100644 --- a/src/x64/assembler-x64.cc +++ b/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/src/x64/assembler-x64.h b/src/x64/assembler-x64.h index bbc10106..c7f76322 100644 --- a/src/x64/assembler-x64.h +++ b/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/src/x64/codegen-x64.cc b/src/x64/codegen-x64.cc index 9e6ef3b5..e0e40950 100644 --- a/src/x64/codegen-x64.cc +++ b/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 { @@ -4866,6 +4866,11 @@ void CodeGenerator::VisitObjectLiteral(ObjectLiteral* node) { } frame_->Push(&clone); + // Mark all computed expressions that are bound to a key that + // is shadowed by a later occurrence of the same key. For the + // marked expressions, no store code is emitted. + node->CalculateEmitStore(); + for (int i = 0; i < node->properties()->length(); i++) { ObjectLiteral::Property* property = node->properties()->at(i); switch (property->kind()) { @@ -4880,13 +4885,17 @@ void CodeGenerator::VisitObjectLiteral(ObjectLiteral* node) { // Duplicate the object as the IC receiver. frame_->Dup(); Load(property->value()); - Result ignored = - frame_->CallStoreIC(Handle<String>::cast(key), false); - // A test rax instruction following the store IC call would - // indicate the presence of an inlined version of the - // store. Add a nop to indicate that there is no such - // inlined version. - __ nop(); + if (property->emit_store()) { + Result ignored = + frame_->CallStoreIC(Handle<String>::cast(key), false); + // A test rax instruction following the store IC call would + // indicate the presence of an inlined version of the + // store. Add a nop to indicate that there is no such + // inlined version. + __ nop(); + } else { + frame_->Drop(2); + } break; } // Fall through @@ -4896,8 +4905,12 @@ void CodeGenerator::VisitObjectLiteral(ObjectLiteral* node) { frame_->Dup(); Load(property->key()); Load(property->value()); - Result ignored = frame_->CallRuntime(Runtime::kSetProperty, 3); - // Ignore the result. + if (property->emit_store()) { + // Ignore the result. + Result ignored = frame_->CallRuntime(Runtime::kSetProperty, 3); + } else { + frame_->Drop(3); + } break; } case ObjectLiteral::Property::SETTER: { @@ -6551,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/src/x64/codegen-x64.h b/src/x64/codegen-x64.h index 79573245..1853c832 100644 --- a/src/x64/codegen-x64.h +++ b/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/src/x64/full-codegen-x64.cc b/src/x64/full-codegen-x64.cc index 32d62426..00ea6845 100644 --- a/src/x64/full-codegen-x64.cc +++ b/src/x64/full-codegen-x64.cc @@ -1158,6 +1158,11 @@ void FullCodeGenerator::VisitObjectLiteral(ObjectLiteral* expr) { // result_saved is false the result is in rax. bool result_saved = false; + // Mark all computed expressions that are bound to a key that + // is shadowed by a later occurrence of the same key. For the + // marked expressions, no store code is emitted. + expr->CalculateEmitStore(); + for (int i = 0; i < expr->properties()->length(); i++) { ObjectLiteral::Property* property = expr->properties()->at(i); if (property->IsCompileTimeValue()) continue; @@ -1179,8 +1184,10 @@ void FullCodeGenerator::VisitObjectLiteral(ObjectLiteral* expr) { VisitForAccumulatorValue(value); __ Move(rcx, key->handle()); __ movq(rdx, Operand(rsp, 0)); - Handle<Code> ic(Builtins::builtin(Builtins::StoreIC_Initialize)); - EmitCallIC(ic, RelocInfo::CODE_TARGET); + if (property->emit_store()) { + Handle<Code> ic(Builtins::builtin(Builtins::StoreIC_Initialize)); + EmitCallIC(ic, RelocInfo::CODE_TARGET); + } break; } // Fall through. @@ -1188,7 +1195,11 @@ void FullCodeGenerator::VisitObjectLiteral(ObjectLiteral* expr) { __ push(Operand(rsp, 0)); // Duplicate receiver. VisitForStackValue(key); VisitForStackValue(value); - __ CallRuntime(Runtime::kSetProperty, 3); + if (property->emit_store()) { + __ CallRuntime(Runtime::kSetProperty, 3); + } else { + __ Drop(3); + } break; case ObjectLiteral::Property::SETTER: case ObjectLiteral::Property::GETTER: @@ -1706,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, @@ -1729,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, @@ -1751,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); @@ -1776,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); @@ -1823,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); @@ -1862,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. @@ -1898,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/src/x64/ic-x64.cc b/src/x64/ic-x64.cc index 1d95b7f6..9ec78148 100644 --- a/src/x64/ic-x64.cc +++ b/src/x64/ic-x64.cc @@ -33,7 +33,6 @@ #include "ic-inl.h" #include "runtime.h" #include "stub-cache.h" -#include "utils.h" namespace v8 { namespace internal { diff --git a/src/x64/stub-cache-x64.cc b/src/x64/stub-cache-x64.cc index 3891e1d5..24609bf6 100644 --- a/src/x64/stub-cache-x64.cc +++ b/src/x64/stub-cache-x64.cc @@ -273,9 +273,11 @@ void StubCache::GenerateProbe(MacroAssembler* masm, Register receiver, Register name, Register scratch, - Register extra) { + Register extra, + Register extra2) { Label miss; - USE(extra); // The register extra is not used on the X64 platform. + USE(extra); // The register extra is not used on the X64 platform. + USE(extra2); // The register extra2 is not used on the X64 platform. // Make sure that code is valid. The shifting code relies on the // entry size being 16. ASSERT(sizeof(Entry) == 16); @@ -287,6 +289,10 @@ void StubCache::GenerateProbe(MacroAssembler* masm, ASSERT(!scratch.is(receiver)); ASSERT(!scratch.is(name)); + // Check scratch register is valid, extra and extra2 are unused. + ASSERT(!scratch.is(no_reg)); + ASSERT(extra2.is(no_reg)); + // Check that the receiver isn't a smi. __ JumpIfSmi(receiver, &miss); diff --git a/test/cctest/test-debug.cc b/test/cctest/test-debug.cc index d59e2f5a..748e3e8d 100644 --- a/test/cctest/test-debug.cc +++ b/test/cctest/test-debug.cc @@ -902,6 +902,7 @@ static void DebugEventBreak(v8::DebugEvent event, // Debug event handler which re-issues a debug break until a limit has been // reached. int max_break_point_hit_count = 0; +bool terminate_after_max_break_point_hit = false; static void DebugEventBreakMax(v8::DebugEvent event, v8::Handle<v8::Object> exec_state, v8::Handle<v8::Object> event_data, @@ -909,12 +910,17 @@ static void DebugEventBreakMax(v8::DebugEvent event, // When hitting a debug event listener there must be a break set. CHECK_NE(v8::internal::Debug::break_id(), 0); - if (event == v8::Break && break_point_hit_count < max_break_point_hit_count) { - // Count the number of breaks. - break_point_hit_count++; + if (event == v8::Break) { + if (break_point_hit_count < max_break_point_hit_count) { + // Count the number of breaks. + break_point_hit_count++; - // Set the break flag again to come back here as soon as possible. - v8::Debug::DebugBreak(); + // Set the break flag again to come back here as soon as possible. + v8::Debug::DebugBreak(); + } else if (terminate_after_max_break_point_hit) { + // Terminate execution after the last break if requested. + v8::V8::TerminateExecution(); + } } } @@ -6892,4 +6898,33 @@ TEST(DebugEventBreakData) { CheckDebuggerUnloaded(); } + +// Test that setting the terminate execution flag during debug break processing. +TEST(DebugBreakLoop) { + v8::HandleScope scope; + DebugLocalContext env; + + // Receive 100 breaks and terminate. + max_break_point_hit_count = 100; + terminate_after_max_break_point_hit = true; + + // Register a debug event listener which sets the break flag and counts. + v8::Debug::SetDebugEventListener(DebugEventBreakMax); + + // Function with infinite loop. + CompileRun("function f() { while (true) { } }"); + + // Set the debug break to enter the debugger as soon as possible. + v8::Debug::DebugBreak(); + + // Call function with infinite loop. + CompileRun("f();"); + CHECK_EQ(100, break_point_hit_count); + + // Get rid of the debug event listener. + v8::Debug::SetDebugEventListener(NULL); + CheckDebuggerUnloaded(); +} + + #endif // ENABLE_DEBUGGER_SUPPORT diff --git a/test/cctest/test-lock.cc b/test/cctest/test-lock.cc index 5eecfcee..9039e022 100644 --- a/test/cctest/test-lock.cc +++ b/test/cctest/test-lock.cc @@ -60,4 +60,5 @@ TEST(SemaphoreTimeout) { sem->Signal(); ok = sem->Wait(1000); CHECK(ok); + delete sem; } diff --git a/test/cctest/test-parsing.cc b/test/cctest/test-parsing.cc index 02503f22..7ae8dcfa 100755 --- a/test/cctest/test-parsing.cc +++ b/test/cctest/test-parsing.cc @@ -26,6 +26,7 @@ // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. #include <stdlib.h> +#include <stdio.h> #include "v8.h" @@ -34,7 +35,8 @@ #include "parser.h" #include "utils.h" #include "execution.h" - +#include "scanner.h" +#include "preparser.h" #include "cctest.h" namespace i = ::v8::internal; @@ -239,3 +241,32 @@ TEST(Preparsing) { i::Vector<const char*> args = pre_impl->BuildArgs(); CHECK_GT(strlen(message), 0); } + + +TEST(StandAlonePreParser) { + int marker; + i::StackGuard::SetStackLimit( + reinterpret_cast<uintptr_t>(&marker) - 128 * 1024); + + const char* programs[] = { + "{label: 42}", + "var x = 42;", + "function foo(x, y) { return x + y; }", + "native function foo(); return %ArgleBargle(glop);", + "var x = new new Function('this.x = 42');", + NULL + }; + + for (int i = 0; programs[i]; i++) { + const char* program = programs[i]; + unibrow::Utf8InputBuffer<256> stream(program, strlen(program)); + i::CompleteParserRecorder log; + i::Scanner scanner; + scanner.Initialize(i::Handle<i::String>::null(), &stream, i::JAVASCRIPT); + v8::preparser::PreParser<i::Scanner, i::CompleteParserRecorder> preparser; + bool result = preparser.PreParseProgram(&scanner, &log, true); + CHECK(result); + i::ScriptDataImpl data(log.ExtractData()); + CHECK(!data.has_error()); + } +} diff --git a/test/cctest/test-regexp.cc b/test/cctest/test-regexp.cc index 11a808e3..3e6709ae 100644 --- a/test/cctest/test-regexp.cc +++ b/test/cctest/test-regexp.cc @@ -64,7 +64,7 @@ static bool CheckParse(const char* input) { ZoneScope zone_scope(DELETE_ON_EXIT); FlatStringReader reader(CStrVector(input)); RegExpCompileData result; - return v8::internal::Parser::ParseRegExp(&reader, false, &result); + return v8::internal::RegExpParser::ParseRegExp(&reader, false, &result); } @@ -74,7 +74,7 @@ static SmartPointer<const char> Parse(const char* input) { ZoneScope zone_scope(DELETE_ON_EXIT); FlatStringReader reader(CStrVector(input)); RegExpCompileData result; - CHECK(v8::internal::Parser::ParseRegExp(&reader, false, &result)); + CHECK(v8::internal::RegExpParser::ParseRegExp(&reader, false, &result)); CHECK(result.tree != NULL); CHECK(result.error.is_null()); SmartPointer<const char> output = result.tree->ToString(); @@ -88,7 +88,7 @@ static bool CheckSimple(const char* input) { ZoneScope zone_scope(DELETE_ON_EXIT); FlatStringReader reader(CStrVector(input)); RegExpCompileData result; - CHECK(v8::internal::Parser::ParseRegExp(&reader, false, &result)); + CHECK(v8::internal::RegExpParser::ParseRegExp(&reader, false, &result)); CHECK(result.tree != NULL); CHECK(result.error.is_null()); return result.simple; @@ -106,7 +106,7 @@ static MinMaxPair CheckMinMaxMatch(const char* input) { ZoneScope zone_scope(DELETE_ON_EXIT); FlatStringReader reader(CStrVector(input)); RegExpCompileData result; - CHECK(v8::internal::Parser::ParseRegExp(&reader, false, &result)); + CHECK(v8::internal::RegExpParser::ParseRegExp(&reader, false, &result)); CHECK(result.tree != NULL); CHECK(result.error.is_null()); int min_match = result.tree->min_match(); @@ -365,7 +365,7 @@ static void ExpectError(const char* input, ZoneScope zone_scope(DELETE_ON_EXIT); FlatStringReader reader(CStrVector(input)); RegExpCompileData result; - CHECK_EQ(false, v8::internal::Parser::ParseRegExp(&reader, false, &result)); + CHECK(!v8::internal::RegExpParser::ParseRegExp(&reader, false, &result)); CHECK(result.tree == NULL); CHECK(!result.error.is_null()); SmartPointer<char> str = result.error->ToCString(ALLOW_NULLS); @@ -473,7 +473,8 @@ static RegExpNode* Compile(const char* input, bool multiline, bool is_ascii) { V8::Initialize(NULL); FlatStringReader reader(CStrVector(input)); RegExpCompileData compile_data; - if (!v8::internal::Parser::ParseRegExp(&reader, multiline, &compile_data)) + if (!v8::internal::RegExpParser::ParseRegExp(&reader, multiline, + &compile_data)) return NULL; Handle<String> pattern = Factory::NewStringFromUtf8(CStrVector(input)); RegExpEngine::Compile(&compile_data, false, multiline, pattern, is_ascii); diff --git a/test/cctest/test-serialize.cc b/test/cctest/test-serialize.cc index 6a513e5f..1cbaf2bf 100644 --- a/test/cctest/test-serialize.cc +++ b/test/cctest/test-serialize.cc @@ -216,6 +216,7 @@ void FileByteSink::WriteSpaceUsed( Vector<char> name = Vector<char>::New(file_name_length + 1); OS::SNPrintF(name, "%s.size", file_name_); FILE* fp = OS::FOpen(name.start(), "w"); + name.Dispose(); fprintf(fp, "new %d\n", new_space_used); fprintf(fp, "pointer %d\n", pointer_space_used); fprintf(fp, "data %d\n", data_space_used); @@ -381,6 +382,7 @@ TEST(PartialSerialization) { env.Dispose(); FileByteSink startup_sink(startup_name.start()); + startup_name.Dispose(); StartupSerializer startup_serializer(&startup_sink); startup_serializer.SerializeStrongReferences(); @@ -403,6 +405,7 @@ static void ReserveSpaceForPartialSnapshot(const char* file_name) { Vector<char> name = Vector<char>::New(file_name_length + 1); OS::SNPrintF(name, "%s.size", file_name); FILE* fp = OS::FOpen(name.start(), "r"); + name.Dispose(); int new_size, pointer_size, data_size, code_size, map_size, cell_size; int large_size; #ifdef _MSC_VER @@ -438,6 +441,7 @@ DEPENDENT_TEST(PartialDeserialization, PartialSerialization) { OS::SNPrintF(startup_name, "%s.startup", FLAG_testing_serialization_file); CHECK(Snapshot::Initialize(startup_name.start())); + startup_name.Dispose(); const char* file_name = FLAG_testing_serialization_file; ReserveSpaceForPartialSnapshot(file_name); @@ -495,6 +499,7 @@ TEST(ContextSerialization) { env.Dispose(); FileByteSink startup_sink(startup_name.start()); + startup_name.Dispose(); StartupSerializer startup_serializer(&startup_sink); startup_serializer.SerializeStrongReferences(); @@ -519,6 +524,7 @@ DEPENDENT_TEST(ContextDeserialization, ContextSerialization) { OS::SNPrintF(startup_name, "%s.startup", FLAG_testing_serialization_file); CHECK(Snapshot::Initialize(startup_name.start())); + startup_name.Dispose(); const char* file_name = FLAG_testing_serialization_file; ReserveSpaceForPartialSnapshot(file_name); diff --git a/test/mjsunit/debug-compile-event.js b/test/mjsunit/debug-compile-event.js index e7ecf47e..b00a907a 100644 --- a/test/mjsunit/debug-compile-event.js +++ b/test/mjsunit/debug-compile-event.js @@ -36,7 +36,6 @@ var current_source = ''; // Current source being compiled. var source_count = 0; // Total number of scources compiled. var host_compilations = 0; // Number of scources compiled through the API. var eval_compilations = 0; // Number of scources compiled through eval. -var json_compilations = 0; // Number of scources compiled through JSON.parse. function compileSource(source) { @@ -62,9 +61,6 @@ function listener(event, exec_state, event_data, data) { case Debug.ScriptCompilationType.Eval: eval_compilations++; break; - case Debug.ScriptCompilationType.JSON: - json_compilations++; - break; } } @@ -74,13 +70,6 @@ function listener(event, exec_state, event_data, data) { // For source with 'eval' there will be compile events with substrings // as well as with with the exact source. assertTrue(current_source.indexOf(event_data.script().source()) >= 0); - } else if (current_source.indexOf('JSON.parse') == 0) { - // For JSON the JSON source will be in parentheses. - var s = event_data.script().source(); - if (s[0] == '(') { - s = s.substring(1, s.length - 2); - } - assertTrue(current_source.indexOf(s) >= 0); } else { // For source without 'eval' there will be a compile events with the // exact source. @@ -113,7 +102,7 @@ source_count++; // Using eval causes additional compilation event. compileSource('eval("eval(\'(function(){return a;})\')")'); source_count += 2; // Using eval causes additional compilation event. compileSource('JSON.parse(\'{"a":1,"b":2}\')'); -source_count++; // Using JSON.parse causes additional compilation event. +// Using JSON.parse does not causes additional compilation events. compileSource('x=1; //@ sourceURL=myscript.js'); // Make sure that the debug event listener was invoked. @@ -123,10 +112,9 @@ assertFalse(exception, "exception in listener") assertEquals(before_compile_count, after_compile_count); // Check the actual number of events (no compilation through the API as all -// source compiled through eval except for one JSON.parse call). +// source compiled through eval). assertEquals(source_count, after_compile_count); assertEquals(0, host_compilations); -assertEquals(source_count - 1, eval_compilations); -assertEquals(1, json_compilations); +assertEquals(source_count, eval_compilations); Debug.setListener(null); diff --git a/test/mjsunit/mirror-script.js b/test/mjsunit/mirror-script.js index 8631028e..71561701 100644 --- a/test/mjsunit/mirror-script.js +++ b/test/mjsunit/mirror-script.js @@ -83,12 +83,10 @@ function testScriptMirror(f, file_name, file_lines, type, compilation_type, // Test the script mirror for different functions. -testScriptMirror(function(){}, 'mirror-script.js', 100, 2, 0); +testScriptMirror(function(){}, 'mirror-script.js', 98, 2, 0); testScriptMirror(Math.sin, 'native math.js', -1, 0, 0); testScriptMirror(eval('(function(){})'), null, 1, 2, 1, '(function(){})', 87); testScriptMirror(eval('(function(){\n })'), null, 2, 2, 1, '(function(){\n })', 88); -testScriptMirror(%CompileString('{"a":1,"b":2}', true), null, 1, 2, 2, '{"a":1,"b":2}'); -testScriptMirror(%CompileString('{"a":1,\n "b":2}', true), null, 2, 2, 2, '{"a":1,\n "b":2}'); // Test taking slices of source. var mirror = debug.MakeMirror(eval('(function(){\n 1;\n})')).script(); diff --git a/test/mjsunit/mjsunit.status b/test/mjsunit/mjsunit.status index 3c8cbdbf..820dca7c 100644 --- a/test/mjsunit/mjsunit.status +++ b/test/mjsunit/mjsunit.status @@ -45,6 +45,10 @@ unicode-case-overoptimization: PASS, TIMEOUT if ($arch == arm) # Skip long running test in debug and allow it to timeout in release mode. regress/regress-524: (PASS || TIMEOUT), SKIP if $mode == debug +# Stack manipulations in LiveEdit are buggy - see bug 915 +debug-liveedit-check-stack: SKIP +debug-liveedit-patch-positions-replace: SKIP + [ $arch == arm ] # Slow tests which times out in debug mode. @@ -61,14 +65,9 @@ array-splice: PASS || TIMEOUT # Skip long running test in debug mode on ARM. string-indexof-2: PASS, SKIP if $mode == debug -# Stack manipulations in LiveEdit is implemented for ia32 only. -debug-liveedit-check-stack: SKIP [ $arch == mips ] -# Stack manipulations in LiveEdit is implemented for ia32 only. -debug-liveedit-check-stack: SKIP - # Skip all tests on MIPS. *: SKIP diff --git a/test/mjsunit/object-literal-conversions.js b/test/mjsunit/object-literal-conversions.js new file mode 100644 index 00000000..8540d930 --- /dev/null +++ b/test/mjsunit/object-literal-conversions.js @@ -0,0 +1,46 @@ +// 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. + +// Test that the various conversions between property names are correctly +// used when overwriting initializers. + +var test1 = { 13: 6, "13": 7 }; +var test2 = { 13: 7, "13.0": 6 }; +var test3 = { "13": 6, 13.0000000000000000: 7 }; +var test4 = { 13.213000: 6, "13.213": 7 }; + +assertEquals(7, test1[13]); +assertEquals(7, test2[13]); +assertEquals(7, test3[13]); +assertEquals(7, test4[13.213]); + +var test5 = { 13: function() {}, "13": 7 }; +var test6 = { 17.31: function() {}, "17.31": 7 }; + +assertEquals(7, test5[13]); +assertEquals(7, test6[17.31]); +
\ No newline at end of file diff --git a/test/mjsunit/object-literal-overwrite.js b/test/mjsunit/object-literal-overwrite.js new file mode 100644 index 00000000..5c58a2dd --- /dev/null +++ b/test/mjsunit/object-literal-overwrite.js @@ -0,0 +1,118 @@ +// 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. + +// Check that constants and computed properties are overwriting each other +// correctly, i.e., the last initializer for any name is stored in the object. + + +// Tests for the full code generator (if active). + +var foo1 = { + bar: 6, + bar: 7 +}; + +var foo2 = { + bar: function(a){}, + bar: 7 +}; + +var foo3 = { + bar: function(a){}, + bar: function(b){}, + bar: 7 +}; + +var foo4 = { + bar: function(b){}, + bar: 7, + bar: function(){return 7}, +}; + +var foo5 = { + 13: function(a){}, + 13: 7 +} + +var foo6 = { + 14.31: function(a){}, + 14.31: 7 +} + +var foo7 = { + 15: 6, + 15: 7 +} + +assertEquals(7, foo1.bar); +assertEquals(7, foo2.bar); +assertEquals(7, foo3.bar); +assertEquals(7, foo4.bar()); +assertEquals(7, foo5[13]); +assertEquals(7, foo6[14.31]); +assertEquals(7, foo7[15]); + +// Test for the classic code generator. + +function fun(x) { + var inner = { j: function(x) { return x; }, j: 7 }; + return inner.j; +} + +assertEquals(7, fun(7) ); + +// Check that the initializers of computed properties are executed, even if +// no store instructions are generated for the literals. + +var glob1 = 0; + +var bar1 = { x: glob1++, x: glob1++, x: glob1++, x: 7}; + +assertEquals(3, glob1); + + +var glob2 = 0; + +function fun2() { + var r = { y: glob2++, y: glob2++, y: glob2++, y: 7}; + return r.y; +} + +var y = fun2(); +assertEquals(7, y); +assertEquals(3, glob2); + +var glob3 = 0; + +function fun3() { + var r = { 113: glob3++, 113: glob3++, 113: glob3++, 113: 7}; + return r[113]; +} + +var y = fun3(); +assertEquals(7, y); +assertEquals(3, glob3);
\ No newline at end of file diff --git a/test/mjsunit/regress/regress-conditional-position.js b/test/mjsunit/regress/regress-conditional-position.js new file mode 100644 index 00000000..cd8f7bd7 --- /dev/null +++ b/test/mjsunit/regress/regress-conditional-position.js @@ -0,0 +1,95 @@ +// 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. + +// Flags: --always-full-compiler + +var functionToCatch; +var lineNumber; + +function catchLineNumber () { + var x = {}; + + Error.prepareStackTrace = function (error, stackTrace) { + stackTrace.some(function (frame) { + if (frame.getFunction() == functionToCatch) { + lineNumber = frame.getLineNumber(); + return true; + } + return false; + }); + return lineNumber; + }; + + Error.captureStackTrace(x); + return x.stack; +} + +function log() { + catchLineNumber(); +} + +function foo() {} + +function test1() { + log(foo() == foo() + ? 'a' + : 'b'); +} + +function test2() { + var o = { foo: function () {}} + log(o.foo() == o.foo() + ? 'a' + : 'b'); +} + +function test3() { + var o = { log: log, foo: function() { } }; + o.log(o.foo() == o.foo() + ? 'a' + : 'b'); + +} + +function test(f, expectedLineNumber) { + functionToCatch = f; + f(); + + assertEquals(expectedLineNumber, lineNumber); +} + +test(test1, 58); +test(test2, 65); +test(test3, 72); + +eval(test1.toString() + "//@ sourceUrl=foo"); +eval(test2.toString() + "//@ sourceUrl=foo"); +eval(test3.toString() + "//@ sourceUrl=foo"); + +test(test1, 2); +test(test2, 3); +test(test3, 3); diff --git a/test/mjsunit/string-externalize.js b/test/mjsunit/string-externalize.js index 5b1f9170..da897869 100644 --- a/test/mjsunit/string-externalize.js +++ b/test/mjsunit/string-externalize.js @@ -25,7 +25,7 @@ // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -// Flags: --expose-externalize-string +// Flags: --expose-externalize-string --expose-gc var size = 1024; @@ -93,3 +93,7 @@ function test() { for (var i = 0; i < 10; i++) { test(); } + +// Clean up string to make Valgrind happy. +gc(); +gc(); diff --git a/test/mjsunit/string-replace-with-empty.js b/test/mjsunit/string-replace-with-empty.js index 0e1e70a1..aa97f27a 100644 --- a/test/mjsunit/string-replace-with-empty.js +++ b/test/mjsunit/string-replace-with-empty.js @@ -25,33 +25,45 @@ // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -// Flags: --expose-externalize-string +// Flags: --expose-externalize-string --expose-gc -assertEquals("0123", "aa0bb1cc2dd3".replace(/[a-z]/g, "")); -assertEquals("0123", "\u1234a0bb1cc2dd3".replace(/[\u1234a-z]/g, "")); +function test() { + assertEquals("0123", "aa0bb1cc2dd3".replace(/[a-z]/g, "")); + assertEquals("0123", "\u1234a0bb1cc2dd3".replace(/[\u1234a-z]/g, "")); -var expected = "0123"; -var cons = "a0b1c2d3"; -for (var i = 0; i < 5; i++) { - expected += expected; - cons += cons; -} -assertEquals(expected, cons.replace(/[a-z]/g, "")); -cons = "\u12340b1c2d3"; -for (var i = 0; i < 5; i++) { - cons += cons; -} -assertEquals(expected, cons.replace(/[\u1234a-z]/g, "")); + var expected = "0123"; + var cons = "a0b1c2d3"; + for (var i = 0; i < 5; i++) { + expected += expected; + cons += cons; + } + assertEquals(expected, cons.replace(/[a-z]/g, "")); + cons = "\u12340b1c2d3"; + for (var i = 0; i < 5; i++) { + cons += cons; + } + assertEquals(expected, cons.replace(/[\u1234a-z]/g, "")); -cons = "a0b1c2d3"; -for (var i = 0; i < 5; i++) { - cons += cons; -} -externalizeString(cons, true/* force two-byte */); -assertEquals(expected, cons.replace(/[a-z]/g, "")); -cons = "\u12340b1c2d3"; -for (var i = 0; i < 5; i++) { - cons += cons; + cons = "a0b1c2d3"; + for (var i = 0; i < 5; i++) { + cons += cons; + } + externalizeString(cons, true/* force two-byte */); + assertEquals(expected, cons.replace(/[a-z]/g, "")); + cons = "\u12340b1c2d3"; + for (var i = 0; i < 5; i++) { + cons += cons; + } + externalizeString(cons); + assertEquals(expected, cons.replace(/[\u1234a-z]/g, "")); } -externalizeString(cons); -assertEquals(expected, cons.replace(/[\u1234a-z]/g, "")); + +test(); + +// Clear the regexp cache to allow the GC to work. +"foo".replace(/foo/g, ""); + +// GC in order to free up things on the C side so we don't get +// a memory leak. This makes valgrind happy. +gc(); +gc(); diff --git a/test/mjsunit/string-split.js b/test/mjsunit/string-split.js index 59d3ad3e..c741f6a3 100644 --- a/test/mjsunit/string-split.js +++ b/test/mjsunit/string-split.js @@ -27,76 +27,45 @@ expected = ["A", undefined, "B", "bold", "/", "B", "and", undefined, "CODE", "coded", "/", "CODE", ""]; result = "A<B>bold</B>and<CODE>coded</CODE>".split(/<(\/)?([^<>]+)>/); -assertArrayEquals(expected, result, 1); +assertArrayEquals(expected, result); -expected = ["a", "b"]; -result = "ab".split(/a*?/); -assertArrayEquals(expected, result, 2); -expected = ["", "b"]; -result = "ab".split(/a*/); -assertArrayEquals(expected, result, 3); +assertArrayEquals(["a", "b"], "ab".split(/a*?/)); -expected = ["a"]; -result = "ab".split(/a*?/, 1); -assertArrayEquals(expected, result, 4); +assertArrayEquals(["", "b"], "ab".split(/a*/)); -expected = [""]; -result = "ab".split(/a*/, 1); -assertArrayEquals(expected, result, 5); +assertArrayEquals(["a"], "ab".split(/a*?/, 1)); -expected = ["as","fas","fas","f"]; -result = "asdfasdfasdf".split("d"); -assertArrayEquals(expected, result, 6); +assertArrayEquals([""], "ab".split(/a*/, 1)); -expected = ["as","fas","fas","f"]; -result = "asdfasdfasdf".split("d", -1); -assertArrayEquals(expected, result, 7); +assertArrayEquals(["as","fas","fas","f"], "asdfasdfasdf".split("d")); -expected = ["as", "fas"]; -result = "asdfasdfasdf".split("d", 2); -assertArrayEquals(expected, result, 8); +assertArrayEquals(["as","fas","fas","f"], "asdfasdfasdf".split("d", -1)); -expected = []; -result = "asdfasdfasdf".split("d", 0); -assertArrayEquals(expected, result, 9); +assertArrayEquals(["as", "fas"], "asdfasdfasdf".split("d", 2)); -expected = ["as","fas","fas",""]; -result = "asdfasdfasd".split("d"); -assertArrayEquals(expected, result, 10); +assertArrayEquals([], "asdfasdfasdf".split("d", 0)); -expected = []; -result = "".split(""); -assertArrayEquals(expected, result, 11); +assertArrayEquals(["as","fas","fas",""], "asdfasdfasd".split("d")); -expected = [""] -result = "".split("a"); -assertArrayEquals(expected, result, 12); +assertArrayEquals([], "".split("")); -expected = ["a","b"] -result = "axxb".split(/x*/); -assertArrayEquals(expected, result, 13); +assertArrayEquals([""], "".split("a")); -expected = ["a","b"] -result = "axxb".split(/x+/); -assertArrayEquals(expected, result, 14); +assertArrayEquals(["a","b"], "axxb".split(/x*/)); -expected = ["a","","b"] -result = "axxb".split(/x/); -assertArrayEquals(expected, result, 15); +assertArrayEquals(["a","b"], "axxb".split(/x+/)); + +assertArrayEquals(["a","","b"], "axxb".split(/x/)); // This was http://b/issue?id=1151354 -expected = ["div", "#id", ".class"] -result = "div#id.class".split(/(?=[#.])/); -assertArrayEquals(expected, result, 16); +assertArrayEquals(["div", "#id", ".class"], "div#id.class".split(/(?=[#.])/)); + -expected = ["div", "#i", "d", ".class"] -result = "div#id.class".split(/(?=[d#.])/); -assertArrayEquals(expected, result, 17); +assertArrayEquals(["div", "#i", "d", ".class"], "div#id.class".split(/(?=[d#.])/)); + +assertArrayEquals(["a", "b", "c"], "abc".split(/(?=.)/)); -expected = ["a", "b", "c"] -result = "abc".split(/(?=.)/); -assertArrayEquals(expected, result, 18); /* "ab".split(/((?=.))/) * @@ -108,19 +77,23 @@ assertArrayEquals(expected, result, 18); * * Opera seems to have this right. The others make no sense. */ -expected = ["a", "", "b"] -result = "ab".split(/((?=.))/); -assertArrayEquals(expected, result, 19); +assertArrayEquals(["a", "", "b"], "ab".split(/((?=.))/)); /* "ab".split(/(?=)/) * * KJS: a,b * SM: ab * IE: a,b - * Opera: a,b + * Opera: a,bb * V8: a,b */ -expected = ["a", "b"] -result = "ab".split(/(?=)/); -assertArrayEquals(expected, result, 20); +assertArrayEquals(["a", "b"], "ab".split(/(?=)/)); + +// For issue http://code.google.com/p/v8/issues/detail?id=924 +// Splitting the empty string is a special case. +assertEquals([""], ''.split()); +assertEquals([""], ''.split(/./)); +assertEquals([], ''.split(/.?/)); +assertEquals([], ''.split(/.??/)); +assertEquals([], ''.split(/()()/)); diff --git a/tools/gyp/v8.gyp b/tools/gyp/v8.gyp index 17d556f1..3e40fcc6 100644 --- a/tools/gyp/v8.gyp +++ b/tools/gyp/v8.gyp @@ -426,6 +426,8 @@ '../../src/rewriter.h', '../../src/runtime.cc', '../../src/runtime.h', + '../../src/scanner-base.cc', + '../../src/scanner-base.h', '../../src/scanner.cc', '../../src/scanner.h', '../../src/scopeinfo.cc', diff --git a/tools/ll_prof.py b/tools/ll_prof.py index 563084dd..8390d4af 100755 --- a/tools/ll_prof.py +++ b/tools/ll_prof.py @@ -353,7 +353,7 @@ class CodeLogReader(object): r"code-info,([^,]+),(\d+)") _CODE_CREATE_RE = re.compile( - r"code-creation,([^,]+),(0x[a-f0-9]+),(\d+),\"([^\"]*)\"(?:,(\d+))?") + r"code-creation,([^,]+),(0x[a-f0-9]+),(\d+),\"(.*)\"(?:,(\d+))?") _CODE_MOVE_RE = re.compile( r"code-move,(0x[a-f0-9]+),(0x[a-f0-9]+)") @@ -910,7 +910,7 @@ if __name__ == "__main__": start = time.time() mmap_info = trace_reader.ReadMmap(header, offset) if mmap_info.filename == V8_GC_FAKE_MMAP: - log_reader.ReadUpToGC() + log_reader.ReadUpToGC(code_info) else: library_repo.Load(mmap_info, code_map, options) mmap_time += time.time() - start diff --git a/tools/presubmit.py b/tools/presubmit.py index e69c9a85..ebf8bd89 100755 --- a/tools/presubmit.py +++ b/tools/presubmit.py @@ -195,7 +195,7 @@ class CppLintProcessor(SourceFileProcessor): or (name in CppLintProcessor.IGNORE_LINT)) def GetPathsToSearch(self): - return ['src', 'public', 'samples', join('test', 'cctest')] + return ['src', 'include', 'samples', join('test', 'cctest')] def ProcessFiles(self, files, path): good_files_cache = FileContentsCache('.cpplint-cache') diff --git a/tools/v8.xcodeproj/project.pbxproj b/tools/v8.xcodeproj/project.pbxproj index 08558cc5..5f93c78a 100644 --- a/tools/v8.xcodeproj/project.pbxproj +++ b/tools/v8.xcodeproj/project.pbxproj @@ -115,6 +115,7 @@ 89A88E180E71A6960043BA31 /* property.cc in Sources */ = {isa = PBXBuildFile; fileRef = 897FF16D0E719B8F00D62E90 /* property.cc */; }; 89A88E190E71A6970043BA31 /* rewriter.cc in Sources */ = {isa = PBXBuildFile; fileRef = 897FF16F0E719B8F00D62E90 /* rewriter.cc */; }; 89A88E1A0E71A69B0043BA31 /* runtime.cc in Sources */ = {isa = PBXBuildFile; fileRef = 897FF1710E719B8F00D62E90 /* runtime.cc */; }; + 89A88E1B0E71A69D0043BA31 /* scanner-base.cc in Sources */ = {isa = PBXBuildFile; fileRef = 897FF1730E719B8F00D62E90 /* scanner-base.cc */; }; 89A88E1B0E71A69D0043BA31 /* scanner.cc in Sources */ = {isa = PBXBuildFile; fileRef = 897FF1730E719B8F00D62E90 /* scanner.cc */; }; 89A88E1C0E71A69E0043BA31 /* scopeinfo.cc in Sources */ = {isa = PBXBuildFile; fileRef = 897FF1760E719B8F00D62E90 /* scopeinfo.cc */; }; 89A88E1D0E71A6A00043BA31 /* scopes.cc in Sources */ = {isa = PBXBuildFile; fileRef = 897FF1780E719B8F00D62E90 /* scopes.cc */; }; @@ -177,6 +178,7 @@ 89F23C6C0E78D5B2006B2466 /* property.cc in Sources */ = {isa = PBXBuildFile; fileRef = 897FF16D0E719B8F00D62E90 /* property.cc */; }; 89F23C6D0E78D5B2006B2466 /* rewriter.cc in Sources */ = {isa = PBXBuildFile; fileRef = 897FF16F0E719B8F00D62E90 /* rewriter.cc */; }; 89F23C6E0E78D5B2006B2466 /* runtime.cc in Sources */ = {isa = PBXBuildFile; fileRef = 897FF1710E719B8F00D62E90 /* runtime.cc */; }; + 89F23C6F0E78D5B2006B2466 /* scanner-base.cc in Sources */ = {isa = PBXBuildFile; fileRef = 897FF1730E719B8F00D62E90 /* scanner-base.cc */; }; 89F23C6F0E78D5B2006B2466 /* scanner.cc in Sources */ = {isa = PBXBuildFile; fileRef = 897FF1730E719B8F00D62E90 /* scanner.cc */; }; 89F23C700E78D5B2006B2466 /* scopeinfo.cc in Sources */ = {isa = PBXBuildFile; fileRef = 897FF1760E719B8F00D62E90 /* scopeinfo.cc */; }; 89F23C710E78D5B2006B2466 /* scopes.cc in Sources */ = {isa = PBXBuildFile; fileRef = 897FF1780E719B8F00D62E90 /* scopes.cc */; }; @@ -481,6 +483,8 @@ 897FF1700E719B8F00D62E90 /* rewriter.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = rewriter.h; sourceTree = "<group>"; }; 897FF1710E719B8F00D62E90 /* runtime.cc */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = runtime.cc; sourceTree = "<group>"; }; 897FF1720E719B8F00D62E90 /* runtime.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = runtime.h; sourceTree = "<group>"; }; + 897FF1730E719B8F00D62E90 /* scanner-base.cc */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = scanner-base.cc; sourceTree = "<group>"; }; + 897FF1740E719B8F00D62E90 /* scanner-base.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = scanner-base.h; sourceTree = "<group>"; }; 897FF1730E719B8F00D62E90 /* scanner.cc */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = scanner.cc; sourceTree = "<group>"; }; 897FF1740E719B8F00D62E90 /* scanner.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = scanner.h; sourceTree = "<group>"; }; 897FF1750E719B8F00D62E90 /* SConscript */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = SConscript; sourceTree = "<group>"; }; @@ -943,6 +947,8 @@ 897FF1700E719B8F00D62E90 /* rewriter.h */, 897FF1710E719B8F00D62E90 /* runtime.cc */, 897FF1720E719B8F00D62E90 /* runtime.h */, + 897FF1730E719B8F00D62E90 /* scanner-base.cc */, + 897FF1740E719B8F00D62E90 /* scanner-base.h */, 897FF1730E719B8F00D62E90 /* scanner.cc */, 897FF1740E719B8F00D62E90 /* scanner.h */, 897FF1760E719B8F00D62E90 /* scopeinfo.cc */, @@ -1348,6 +1354,7 @@ 58950D630F5551AF00F3E8BA /* register-allocator.cc in Sources */, 89A88E190E71A6970043BA31 /* rewriter.cc in Sources */, 89A88E1A0E71A69B0043BA31 /* runtime.cc in Sources */, + 89A88E1B0E71A69D0043BA31 /* scanner-base.cc in Sources */, 89A88E1B0E71A69D0043BA31 /* scanner.cc in Sources */, 89A88E1C0E71A69E0043BA31 /* scopeinfo.cc in Sources */, 89A88E1D0E71A6A00043BA31 /* scopes.cc in Sources */, @@ -1472,6 +1479,7 @@ 58950D640F5551B500F3E8BA /* register-allocator.cc in Sources */, 89F23C6D0E78D5B2006B2466 /* rewriter.cc in Sources */, 89F23C6E0E78D5B2006B2466 /* runtime.cc in Sources */, + 89F23C6F0E78D5B2006B2466 /* scanner-base.cc in Sources */, 89F23C6F0E78D5B2006B2466 /* scanner.cc in Sources */, 89F23C700E78D5B2006B2466 /* scopeinfo.cc in Sources */, 89F23C710E78D5B2006B2466 /* scopes.cc in Sources */, diff --git a/tools/visual_studio/v8_base.vcproj b/tools/visual_studio/v8_base.vcproj index 62d45015..bddf38e2 100644 --- a/tools/visual_studio/v8_base.vcproj +++ b/tools/visual_studio/v8_base.vcproj @@ -882,6 +882,14 @@ > </File> <File + RelativePath="..\..\src\scanner-base.cc" + > + </File> + <File + RelativePath="..\..\src\scanner-base.h" + > + </File> + <File RelativePath="..\..\src\scanner.cc" > </File> |