diff --git a/deps/v8/AUTHORS b/deps/v8/AUTHORS index 68f9b63bab..3749cebcd1 100644 --- a/deps/v8/AUTHORS +++ b/deps/v8/AUTHORS @@ -9,7 +9,6 @@ ARM Ltd. Hewlett-Packard Development Company, LP Alexander Botero-Lowry -Alexandre Rames Alexandre Vassalotti Andreas Anyuru Burcu Dogan diff --git a/deps/v8/ChangeLog b/deps/v8/ChangeLog index 9f2005f40f..86e41e175b 100644 --- a/deps/v8/ChangeLog +++ b/deps/v8/ChangeLog @@ -1,3 +1,28 @@ +2010-11-29: Version 2.5.9 + + Fixed crashes during GC caused by partially initialize heap + objects. + + Fixed bug in process sample that caused memory leaks. + + Improved performance on ARM by implementing missing stubs and + inlining. + + Improved heap profiler support. + + Added separate seeding on Windows of the random number generator + used internally by the compiler (issue 936). + + Exposed API for getting the name of the function used to construct + an object. + + Fixed date parser to handle one and two digit millisecond + values (issue 944). + + Fixed number parsing to disallow space between sign and + digits (issue 946). + + 2010-11-23: Version 2.5.8 Removed dependency on Gay's dtoa. diff --git a/deps/v8/include/v8.h b/deps/v8/include/v8.h index 9baa17db1d..8ecf63aebd 100644 --- a/deps/v8/include/v8.h +++ b/deps/v8/include/v8.h @@ -1539,6 +1539,11 @@ class Object : public Value { */ V8EXPORT Local ObjectProtoToString(); + /** + * Returns the name of the function invoked as a constructor for this object. + */ + V8EXPORT Local GetConstructorName(); + /** Gets the number of internal fields for this Object. */ V8EXPORT int InternalFieldCount(); /** Gets the value in an internal field. */ diff --git a/deps/v8/preparser/preparser-process.cc b/deps/v8/preparser/preparser-process.cc new file mode 100644 index 0000000000..706a225969 --- /dev/null +++ b/deps/v8/preparser/preparser-process.cc @@ -0,0 +1,227 @@ +// Copyright 2010 the V8 project authors. All rights reserved. +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following +// disclaimer in the documentation and/or other materials provided +// with the distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +#include +#include "../include/v8stdint.h" +#include "globals.h" +#include "checks.h" +#include "allocation.h" +#include "utils.h" +#include "list.h" +#include "smart-pointer.h" +#include "scanner-base.h" +#include "preparse-data.h" +#include "preparser.h" + +enum ResultCode { kSuccess = 0, kErrorReading = 1, kErrorWriting = 2 }; + +namespace v8 { +namespace internal { + +// THIS FILE IS PROOF-OF-CONCEPT ONLY. +// The final goal is a stand-alone preparser library. + +// UTF16Buffer based on an UTF-8 string in memory. +class UTF8UTF16Buffer : public UTF16Buffer { + public: + UTF8UTF16Buffer(uint8_t* buffer, size_t length) + : UTF16Buffer(), + buffer_(buffer), + offset_(0), + end_offset_(static_cast(length)) { } + + virtual void PushBack(uc32 ch) { + // Pushback assumes that the character pushed back is the + // one that was most recently read, and jumps back in the + // UTF-8 stream by the length of that character's encoding. + offset_ -= unibrow::Utf8::Length(ch); + pos_--; +#ifdef DEBUG + int tmp = 0; + ASSERT_EQ(ch, unibrow::Utf8::ValueOf(buffer_ + offset_, + end_offset_ - offset_, + &tmp); +#endif + } + + virtual uc32 Advance() { + if (offset_ == end_offset_) return -1; + uint8_t first_char = buffer_[offset_]; + if (first_char <= unibrow::Utf8::kMaxOneByteChar) { + pos_++; + offset_++; + return static_cast(first_char); + } + unibrow::uchar codepoint = + unibrow::Utf8::CalculateValue(buffer_ + offset_, + end_offset_ - offset_, + &offset_); + pos_++; + return static_cast(codepoint); + } + + virtual void SeekForward(int pos) { + while (pos_ < pos) { + uint8_t first_byte = buffer_[offset_++]; + while (first_byte & 0x80u && offset_ < end_offset_) { + offset_++; + first_byte <<= 1; + } + pos_++; + } + } + + private: + const uint8_t* buffer_; + unsigned offset_; + unsigned end_offset_; +}; + + +class StandAloneJavaScriptScanner : public JavaScriptScanner { + public: + void Initialize(UTF16Buffer* source) { + source_ = source; + literal_flags_ = kLiteralString | kLiteralIdentifier; + Init(); + // Skip initial whitespace allowing HTML comment ends just like + // after a newline and scan first token. + has_line_terminator_before_next_ = true; + SkipWhiteSpace(); + Scan(); + } +}; + + +// Write a number to dest in network byte order. +void WriteUInt32(FILE* dest, uint32_t value, bool* ok) { + for (int i = 3; i >= 0; i--) { + uint8_t byte = static_cast(value >> (i << 3)); + int result = fputc(byte, dest); + if (result == EOF) { + *ok = false; + return; + } + } +} + +// Read number from FILE* in network byte order. +uint32_t ReadUInt32(FILE* source, bool* ok) { + uint32_t n = 0; + for (int i = 0; i < 4; i++) { + int c = fgetc(source); + if (c == EOF) { + *ok = false; + return 0; + } + n = (n << 8) + static_cast(c); + } + return n; +} + + +bool ReadBuffer(FILE* source, void* buffer, size_t length) { + size_t actually_read = fread(buffer, 1, length, stdin); + return (actually_read == length); +} + + +bool WriteBuffer(FILE* dest, void* buffer, size_t length) { + size_t actually_written = fwrite(buffer, 1, length, dest); + return (actually_written == length); +} + +// Preparse stdin and output result on stdout. +int PreParseIO() { + fprintf(stderr, "LOG: Enter parsing loop\n"); + bool ok = true; + uint32_t length = ReadUInt32(stdin, &ok); + if (!ok) return kErrorReading; + SmartPointer buffer(NewArray(length)); + if (!ReadBuffer(stdin, *buffer, length)) { + return kErrorReading; + } + UTF8UTF16Buffer input_buffer(*buffer, static_cast(length)); + StandAloneJavaScriptScanner scanner; + scanner.Initialize(&input_buffer); + CompleteParserRecorder recorder; + preparser::PreParser preparser; + + if (!preparser.PreParseProgram(&scanner, &recorder, true)) { + if (scanner.stack_overflow()) { + // Report stack overflow error/no-preparser-data. + WriteUInt32(stdout, 0, &ok); + if (!ok) return kErrorWriting; + return 0; + } + } + Vector pre_data = recorder.ExtractData(); + + uint32_t size = static_cast(pre_data.length() * sizeof(uint32_t)); + WriteUInt32(stdout, size, &ok); + if (!ok) return kErrorWriting; + if (!WriteBuffer(stdout, + reinterpret_cast(pre_data.start()), + size)) { + return kErrorWriting; + } + return 0; +} + +// Functions declared by allocation.h + +void FatalProcessOutOfMemory(const char* location) { + V8_Fatal("", 0, location); +} + +bool EnableSlowAsserts() { return true; } + +} } // namespace v8::internal + + +int main(int argc, char* argv[]) { + int status = 0; + do { + status = v8::internal::PreParseIO(); + } while (status == 0); + fprintf(stderr, "EXIT: Failure %d\n", status); + return EXIT_FAILURE; +} + + +// Fatal error handling declared by checks.h. + +extern "C" void V8_Fatal(const char* file, int line, const char* format, ...) { + fflush(stdout); + fflush(stderr); + va_list arguments; + va_start(arguments, format); + vfprintf(stderr, format, arguments); + va_end(arguments); + fputs("\n#\n\n", stderr); + exit(EXIT_FAILURE); +} diff --git a/deps/v8/samples/process.cc b/deps/v8/samples/process.cc index 9233c0dfa2..6be4ea542a 100644 --- a/deps/v8/samples/process.cc +++ b/deps/v8/samples/process.cc @@ -152,18 +152,16 @@ bool JsHttpRequestProcessor::Initialize(map* opts, Handle global = ObjectTemplate::New(); global->Set(String::New("log"), FunctionTemplate::New(LogCallback)); - // Each processor gets its own context so different processors - // don't affect each other (ignore the first three lines). - Handle context = Context::New(NULL, global); - - // Store the context in the processor object in a persistent handle, - // since we want the reference to remain after we return from this - // method. - context_ = Persistent::New(context); + // Each processor gets its own context so different processors don't + // affect each other. Context::New returns a persistent handle which + // is what we need for the reference to remain after we return from + // this method. That persistent handle has to be disposed in the + // destructor. + context_ = Context::New(NULL, global); // Enter the new context so all the following operations take place // within it. - Context::Scope context_scope(context); + Context::Scope context_scope(context_); // Make the options mapping available within the context if (!InstallMaps(opts, output)) @@ -176,7 +174,7 @@ bool JsHttpRequestProcessor::Initialize(map* opts, // The script compiled and ran correctly. Now we fetch out the // Process function from the global object. Handle process_name = String::New("Process"); - Handle process_val = context->Global()->Get(process_name); + Handle process_val = context_->Global()->Get(process_name); // If there is no Process function, or if it is not a function, // bail out diff --git a/deps/v8/samples/shell.cc b/deps/v8/samples/shell.cc index 2bdc5a19da..1a13f5f80b 100644 --- a/deps/v8/samples/shell.cc +++ b/deps/v8/samples/shell.cc @@ -37,7 +37,6 @@ bool ExecuteString(v8::Handle source, v8::Handle name, bool print_result, bool report_exceptions); -v8::Handle PrintToInteger(const v8::Arguments& args); v8::Handle Print(const v8::Arguments& args); v8::Handle Read(const v8::Arguments& args); v8::Handle Load(const v8::Arguments& args); @@ -54,8 +53,7 @@ int RunMain(int argc, char* argv[]) { v8::Handle global = v8::ObjectTemplate::New(); // Bind the global 'print' function to the C++ Print callback. global->Set(v8::String::New("print"), v8::FunctionTemplate::New(Print)); -global->Set(v8::String::New("print2int"), v8::FunctionTemplate::New(PrintToInteger)); -// Bind the global 'read' function to the C++ Read callback. + // Bind the global 'read' function to the C++ Read callback. global->Set(v8::String::New("read"), v8::FunctionTemplate::New(Read)); // Bind the global 'load' function to the C++ Load callback. global->Set(v8::String::New("load"), v8::FunctionTemplate::New(Load)); @@ -140,16 +138,6 @@ v8::Handle Print(const v8::Arguments& args) { } -v8::Handle PrintToInteger(const v8::Arguments& args) { - v8::HandleScope handle_scope; - v8::String::Utf8Value str(args[0]); - const char* cstr = ToCString(str); - printf("%s -> %d\n", cstr, args[0]->ToInt32()->Value()); - fflush(stdout); - return v8::Undefined(); -} - - // The callback that is invoked by v8 whenever the JavaScript 'read' // function is called. This function loads the content of the file named in // the argument into a JavaScript string. diff --git a/deps/v8/src/SConscript b/deps/v8/src/SConscript index 98e0a0ffa4..0e0679f93c 100755 --- a/deps/v8/src/SConscript +++ b/deps/v8/src/SConscript @@ -89,6 +89,8 @@ SOURCES = { objects-visiting.cc oprofile-agent.cc parser.cc + preparser.cc + preparse-data.cc profile-generator.cc property.cc regexp-macro-assembler-irregexp.cc diff --git a/deps/v8/src/api.cc b/deps/v8/src/api.cc index b46cd02a3a..19af866c24 100644 --- a/deps/v8/src/api.cc +++ b/deps/v8/src/api.cc @@ -2451,6 +2451,15 @@ Local v8::Object::ObjectProtoToString() { } +Local v8::Object::GetConstructorName() { + ON_BAILOUT("v8::Object::GetConstructorName()", return Local()); + ENTER_V8; + i::Handle self = Utils::OpenHandle(this); + i::Handle name(self->constructor_name()); + return Utils::ToLocal(name); +} + + bool v8::Object::Delete(v8::Handle key) { ON_BAILOUT("v8::Object::Delete()", return false); ENTER_V8; diff --git a/deps/v8/src/arm/assembler-arm-inl.h b/deps/v8/src/arm/assembler-arm-inl.h index f72ad76abe..15720c956d 100644 --- a/deps/v8/src/arm/assembler-arm-inl.h +++ b/deps/v8/src/arm/assembler-arm-inl.h @@ -164,7 +164,7 @@ bool RelocInfo::IsPatchedReturnSequence() { bool RelocInfo::IsPatchedDebugBreakSlotSequence() { Instr current_instr = Assembler::instr_at(pc_); - return !Assembler::IsNop(current_instr, 2); + return !Assembler::IsNop(current_instr, Assembler::DEBUG_BREAK_NOP); } @@ -288,9 +288,7 @@ Address Assembler::target_address_address_at(Address pc) { } #endif - // Verify that the instruction to patch is a - // ldr , [pc +/- offset_12]. - ASSERT((instr & 0x0f7f0000) == 0x051f0000); + ASSERT(IsLdrPcImmediateOffset(instr)); int offset = instr & 0xfff; // offset_12 is unsigned if ((instr & (1 << 23)) == 0) offset = -offset; // U bit defines offset sign // Verify that the constant pool comes after the instruction referencing it. diff --git a/deps/v8/src/arm/assembler-arm.cc b/deps/v8/src/arm/assembler-arm.cc index 4cb421c577..cfdd164961 100644 --- a/deps/v8/src/arm/assembler-arm.cc +++ b/deps/v8/src/arm/assembler-arm.cc @@ -397,13 +397,6 @@ void Assembler::CodeTargetAlign() { } -bool Assembler::IsNop(Instr instr, int type) { - // Check for mov rx, rx. - ASSERT(0 <= type && type <= 14); // mov pc, pc is not a nop. - return instr == (al | 13*B21 | type*B12 | type); -} - - bool Assembler::IsBranch(Instr instr) { return (instr & (B27 | B25)) == (B27 | B25); } @@ -510,6 +503,13 @@ bool Assembler::IsLdrRegFpNegOffset(Instr instr) { } +bool Assembler::IsLdrPcImmediateOffset(Instr instr) { + // Check the instruction is indeed a + // ldr , [pc +/- offset_12]. + return (instr & 0x0f7f0000) == 0x051f0000; +} + + // Labels refer to positions in the (to be) generated code. // There are bound, linked, and unused labels. // @@ -1113,8 +1113,8 @@ void Assembler::mov(Register dst, const Operand& src, SBit s, Condition cond) { 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) - // pseudo instructions. + // the mov instruction. They must be generated using nop(int/NopMarkerTypes) + // or MarkCode(int/NopMarkerTypes) pseudo instructions. ASSERT(!(src.is_reg() && src.rm().is(dst) && s == LeaveCC && cond == al)); addrmod1(cond | 13*B21 | s, r0, dst, src); } @@ -2376,6 +2376,13 @@ void Assembler::nop(int type) { } +bool Assembler::IsNop(Instr instr, int type) { + // Check for mov rx, rx. + ASSERT(0 <= type && type <= 14); // mov pc, pc is not a nop. + return instr == (al | 13*B21 | type*B12 | type); +} + + bool Assembler::ImmediateFitsAddrMode1Instruction(int32_t imm32) { uint32_t dummy1; uint32_t dummy2; diff --git a/deps/v8/src/arm/assembler-arm.h b/deps/v8/src/arm/assembler-arm.h index 606ff86340..ee4c9aa52b 100644 --- a/deps/v8/src/arm/assembler-arm.h +++ b/deps/v8/src/arm/assembler-arm.h @@ -1079,7 +1079,22 @@ class Assembler : public Malloced { const Condition cond = al); // Pseudo instructions - void nop(int type = 0); + + // Different nop operations are used by the code generator to detect certain + // states of the generated code. + enum NopMarkerTypes { + NON_MARKING_NOP = 0, + DEBUG_BREAK_NOP, + // IC markers. + PROPERTY_ACCESS_INLINED, + PROPERTY_ACCESS_INLINED_CONTEXT, + PROPERTY_ACCESS_INLINED_CONTEXT_DONT_DELETE, + // Helper values. + LAST_CODE_MARKER, + FIRST_IC_MARKER = PROPERTY_ACCESS_INLINED + }; + + void nop(int type = 0); // 0 is the default non-marking type. void push(Register src, Condition cond = al) { str(src, MemOperand(sp, 4, NegPreIndex), cond); @@ -1151,7 +1166,6 @@ class Assembler : public Malloced { static void instr_at_put(byte* pc, Instr instr) { *reinterpret_cast(pc) = instr; } - static bool IsNop(Instr instr, int type = 0); static bool IsBranch(Instr instr); static int GetBranchOffset(Instr instr); static bool IsLdrRegisterImmediate(Instr instr); @@ -1168,6 +1182,8 @@ class Assembler : public Malloced { static bool IsLdrRegFpOffset(Instr instr); static bool IsStrRegFpNegOffset(Instr instr); static bool IsLdrRegFpNegOffset(Instr instr); + static bool IsLdrPcImmediateOffset(Instr instr); + static bool IsNop(Instr instr, int type = NON_MARKING_NOP); protected: diff --git a/deps/v8/src/arm/builtins-arm.cc b/deps/v8/src/arm/builtins-arm.cc index cf2f426243..862ef395f4 100644 --- a/deps/v8/src/arm/builtins-arm.cc +++ b/deps/v8/src/arm/builtins-arm.cc @@ -482,9 +482,128 @@ void Builtins::Generate_ArrayConstructCode(MacroAssembler* masm) { void Builtins::Generate_StringConstructCode(MacroAssembler* masm) { - // TODO(849): implement custom construct stub. - // Generate a copy of the generic stub for now. - Generate_JSConstructStubGeneric(masm); + // ----------- S t a t e ------------- + // -- r0 : number of arguments + // -- r1 : constructor function + // -- lr : return address + // -- sp[(argc - n - 1) * 4] : arg[n] (zero based) + // -- sp[argc * 4] : receiver + // ----------------------------------- + __ IncrementCounter(&Counters::string_ctor_calls, 1, r2, r3); + + Register function = r1; + if (FLAG_debug_code) { + __ LoadGlobalFunction(Context::STRING_FUNCTION_INDEX, r2); + __ cmp(function, Operand(r2)); + __ Assert(eq, "Unexpected String function"); + } + + // Load the first arguments in r0 and get rid of the rest. + Label no_arguments; + __ cmp(r0, Operand(0)); + __ b(eq, &no_arguments); + // First args = sp[(argc - 1) * 4]. + __ sub(r0, r0, Operand(1)); + __ ldr(r0, MemOperand(sp, r0, LSL, kPointerSizeLog2, PreIndex)); + // sp now point to args[0], drop args[0] + receiver. + __ Drop(2); + + Register argument = r2; + Label not_cached, argument_is_string; + NumberToStringStub::GenerateLookupNumberStringCache( + masm, + r0, // Input. + argument, // Result. + r3, // Scratch. + r4, // Scratch. + r5, // Scratch. + false, // Is it a Smi? + ¬_cached); + __ IncrementCounter(&Counters::string_ctor_cached_number, 1, r3, r4); + __ bind(&argument_is_string); + + // ----------- S t a t e ------------- + // -- r2 : argument converted to string + // -- r1 : constructor function + // -- lr : return address + // ----------------------------------- + + Label gc_required; + __ AllocateInNewSpace(JSValue::kSize, + r0, // Result. + r3, // Scratch. + r4, // Scratch. + &gc_required, + TAG_OBJECT); + + // Initialising the String Object. + Register map = r3; + __ LoadGlobalFunctionInitialMap(function, map, r4); + if (FLAG_debug_code) { + __ ldrb(r4, FieldMemOperand(map, Map::kInstanceSizeOffset)); + __ cmp(r4, Operand(JSValue::kSize >> kPointerSizeLog2)); + __ Assert(eq, "Unexpected string wrapper instance size"); + __ ldrb(r4, FieldMemOperand(map, Map::kUnusedPropertyFieldsOffset)); + __ cmp(r4, Operand(0)); + __ Assert(eq, "Unexpected unused properties of string wrapper"); + } + __ str(map, FieldMemOperand(r0, HeapObject::kMapOffset)); + + __ LoadRoot(r3, Heap::kEmptyFixedArrayRootIndex); + __ str(r3, FieldMemOperand(r0, JSObject::kPropertiesOffset)); + __ str(r3, FieldMemOperand(r0, JSObject::kElementsOffset)); + + __ str(argument, FieldMemOperand(r0, JSValue::kValueOffset)); + + // Ensure the object is fully initialized. + STATIC_ASSERT(JSValue::kSize == 4 * kPointerSize); + + __ Ret(); + + // The argument was not found in the number to string cache. Check + // if it's a string already before calling the conversion builtin. + Label convert_argument; + __ bind(¬_cached); + __ BranchOnSmi(r0, &convert_argument); + + // Is it a String? + __ ldr(r2, FieldMemOperand(r0, HeapObject::kMapOffset)); + __ ldrb(r3, FieldMemOperand(r2, Map::kInstanceTypeOffset)); + ASSERT(kNotStringTag != 0); + __ tst(r3, Operand(kIsNotStringMask)); + __ b(ne, &convert_argument); + __ mov(argument, r0); + __ IncrementCounter(&Counters::string_ctor_conversions, 1, r3, r4); + __ b(&argument_is_string); + + // Invoke the conversion builtin and put the result into r2. + __ bind(&convert_argument); + __ push(function); // Preserve the function. + __ IncrementCounter(&Counters::string_ctor_conversions, 1, r3, r4); + __ EnterInternalFrame(); + __ push(r0); + __ InvokeBuiltin(Builtins::TO_STRING, CALL_JS); + __ LeaveInternalFrame(); + __ pop(function); + __ mov(argument, r0); + __ b(&argument_is_string); + + // Load the empty string into r2, remove the receiver from the + // stack, and jump back to the case where the argument is a string. + __ bind(&no_arguments); + __ LoadRoot(argument, Heap::kEmptyStringRootIndex); + __ Drop(1); + __ b(&argument_is_string); + + // At this point the argument is already a string. Call runtime to + // create a string wrapper. + __ bind(&gc_required); + __ IncrementCounter(&Counters::string_ctor_gc_required, 1, r3, r4); + __ EnterInternalFrame(); + __ push(argument); + __ CallRuntime(Runtime::kNewStringWrapper, 1); + __ LeaveInternalFrame(); + __ Ret(); } diff --git a/deps/v8/src/arm/codegen-arm.cc b/deps/v8/src/arm/codegen-arm.cc index 3e6743afaf..27e14df481 100644 --- a/deps/v8/src/arm/codegen-arm.cc +++ b/deps/v8/src/arm/codegen-arm.cc @@ -6024,6 +6024,68 @@ void CodeGenerator::VisitUnaryOperation(UnaryOperation* node) { } +class DeferredCountOperation: public DeferredCode { + public: + DeferredCountOperation(Register value, + bool is_increment, + bool is_postfix, + int target_size) + : value_(value), + is_increment_(is_increment), + is_postfix_(is_postfix), + target_size_(target_size) {} + + virtual void Generate() { + VirtualFrame copied_frame(*frame_state()->frame()); + + Label slow; + // Check for smi operand. + __ tst(value_, Operand(kSmiTagMask)); + __ b(ne, &slow); + + // Revert optimistic increment/decrement. + if (is_increment_) { + __ sub(value_, value_, Operand(Smi::FromInt(1))); + } else { + __ add(value_, value_, Operand(Smi::FromInt(1))); + } + + // Slow case: Convert to number. At this point the + // value to be incremented is in the value register.. + __ bind(&slow); + + // Convert the operand to a number. + copied_frame.EmitPush(value_); + + copied_frame.InvokeBuiltin(Builtins::TO_NUMBER, CALL_JS, 1); + + if (is_postfix_) { + // Postfix: store to result (on the stack). + __ str(r0, MemOperand(sp, target_size_ * kPointerSize)); + } + + copied_frame.EmitPush(r0); + copied_frame.EmitPush(Operand(Smi::FromInt(1))); + + if (is_increment_) { + copied_frame.CallRuntime(Runtime::kNumberAdd, 2); + } else { + copied_frame.CallRuntime(Runtime::kNumberSub, 2); + } + + __ Move(value_, r0); + + copied_frame.MergeTo(frame_state()->frame()); + } + + private: + Register value_; + bool is_increment_; + bool is_postfix_; + int target_size_; +}; + + void CodeGenerator::VisitCountOperation(CountOperation* node) { #ifdef DEBUG int original_height = frame_->height(); @@ -6083,9 +6145,7 @@ void CodeGenerator::VisitCountOperation(CountOperation* node) { // the target. It also pushes the current value of the target. target.GetValue(); - JumpTarget slow; - JumpTarget exit; - + bool value_is_known_smi = frame_->KnownSmiAt(0); Register value = frame_->PopToRegister(); // Postfix: Store the old value as the result. @@ -6097,9 +6157,27 @@ void CodeGenerator::VisitCountOperation(CountOperation* node) { value = VirtualFrame::scratch0(); } - // Check for smi operand. - __ tst(value, Operand(kSmiTagMask)); - slow.Branch(ne); + // We can't use any type information here since the virtual frame from the + // deferred code may have lost information and we can't merge a virtual + // frame with less specific type knowledge to a virtual frame with more + // specific knowledge that has already used that specific knowledge to + // generate code. + frame_->ForgetTypeInfo(); + + // The constructor here will capture the current virtual frame and use it to + // merge to after the deferred code has run. No virtual frame changes are + // allowed from here until the 'BindExit' below. + DeferredCode* deferred = + new DeferredCountOperation(value, + is_increment, + is_postfix, + target.size()); + if (!value_is_known_smi) { + // Check for smi operand. + __ tst(value, Operand(kSmiTagMask)); + + deferred->Branch(ne); + } // Perform optimistic increment/decrement. if (is_increment) { @@ -6108,46 +6186,13 @@ void CodeGenerator::VisitCountOperation(CountOperation* node) { __ sub(value, value, Operand(Smi::FromInt(1)), SetCC); } - // If the increment/decrement didn't overflow, we're done. - exit.Branch(vc); - - // Revert optimistic increment/decrement. - if (is_increment) { - __ sub(value, value, Operand(Smi::FromInt(1))); - } else { - __ add(value, value, Operand(Smi::FromInt(1))); - } - - // Slow case: Convert to number. At this point the - // value to be incremented is in the value register.. - slow.Bind(); - - // Convert the operand to a number. - frame_->EmitPush(value); - - { - VirtualFrame::SpilledScope spilled(frame_); - frame_->InvokeBuiltin(Builtins::TO_NUMBER, CALL_JS, 1); - - if (is_postfix) { - // Postfix: store to result (on the stack). - __ str(r0, frame_->ElementAt(target.size())); - } + // If increment/decrement overflows, go to deferred code. + deferred->Branch(vs); - // Compute the new value. - frame_->EmitPush(r0); - frame_->EmitPush(Operand(Smi::FromInt(1))); - if (is_increment) { - frame_->CallRuntime(Runtime::kNumberAdd, 2); - } else { - frame_->CallRuntime(Runtime::kNumberSub, 2); - } - } + deferred->BindExit(); - __ Move(value, r0); // Store the new value in the target if not const. // At this point the answer is in the value register. - exit.Bind(); frame_->EmitPush(value); // Set the target with the result, leaving the result on // top of the stack. Removes the target from the stack if @@ -6537,16 +6582,29 @@ void CodeGenerator::VisitCompareToNull(CompareToNull* node) { class DeferredReferenceGetNamedValue: public DeferredCode { public: explicit DeferredReferenceGetNamedValue(Register receiver, - Handle name) - : receiver_(receiver), name_(name) { - set_comment("[ DeferredReferenceGetNamedValue"); + Handle name, + bool is_contextual) + : receiver_(receiver), + name_(name), + is_contextual_(is_contextual), + is_dont_delete_(false) { + set_comment(is_contextual + ? "[ DeferredReferenceGetNamedValue (contextual)" + : "[ DeferredReferenceGetNamedValue"); } virtual void Generate(); + void set_is_dont_delete(bool value) { + ASSERT(is_contextual_); + is_dont_delete_ = value; + } + private: Register receiver_; Handle name_; + bool is_contextual_; + bool is_dont_delete_; }; @@ -6573,10 +6631,20 @@ void DeferredReferenceGetNamedValue::Generate() { // The rest of the instructions in the deferred code must be together. { Assembler::BlockConstPoolScope block_const_pool(masm_); Handle ic(Builtins::builtin(Builtins::LoadIC_Initialize)); - __ Call(ic, RelocInfo::CODE_TARGET); - // The call must be followed by a nop(1) instruction to indicate that the - // in-object has been inlined. - __ nop(PROPERTY_ACCESS_INLINED); + RelocInfo::Mode mode = is_contextual_ + ? RelocInfo::CODE_TARGET_CONTEXT + : RelocInfo::CODE_TARGET; + __ Call(ic, mode); + // We must mark the code just after the call with the correct marker. + MacroAssembler::NopMarkerTypes code_marker; + if (is_contextual_) { + code_marker = is_dont_delete_ + ? MacroAssembler::PROPERTY_ACCESS_INLINED_CONTEXT_DONT_DELETE + : MacroAssembler::PROPERTY_ACCESS_INLINED_CONTEXT; + } else { + code_marker = MacroAssembler::PROPERTY_ACCESS_INLINED; + } + __ MarkCode(code_marker); // At this point the answer is in r0. We move it to the expected register // if necessary. @@ -6640,7 +6708,7 @@ void DeferredReferenceGetKeyedValue::Generate() { __ Call(ic, RelocInfo::CODE_TARGET); // The call must be followed by a nop instruction to indicate that the // keyed load has been inlined. - __ nop(PROPERTY_ACCESS_INLINED); + __ MarkCode(MacroAssembler::PROPERTY_ACCESS_INLINED); // Now go back to the frame that we entered with. This will not overwrite // the receiver or key registers since they were not in use when we came @@ -6697,7 +6765,7 @@ void DeferredReferenceSetKeyedValue::Generate() { __ Call(ic, RelocInfo::CODE_TARGET); // The call must be followed by a nop instruction to indicate that the // keyed store has been inlined. - __ nop(PROPERTY_ACCESS_INLINED); + __ MarkCode(MacroAssembler::PROPERTY_ACCESS_INLINED); // Block the constant pool for one more instruction after leaving this // constant pool block scope to include the branch instruction ending the @@ -6745,7 +6813,7 @@ void DeferredReferenceSetNamedValue::Generate() { __ Call(ic, RelocInfo::CODE_TARGET); // The call must be followed by a nop instruction to indicate that the // named store has been inlined. - __ nop(PROPERTY_ACCESS_INLINED); + __ MarkCode(MacroAssembler::PROPERTY_ACCESS_INLINED); // Go back to the frame we entered with. The instructions // generated by this merge are skipped over by the inline store @@ -6763,7 +6831,14 @@ void DeferredReferenceSetNamedValue::Generate() { // Consumes the top of stack (the receiver) and pushes the result instead. void CodeGenerator::EmitNamedLoad(Handle name, bool is_contextual) { - if (is_contextual || scope()->is_global_scope() || loop_nesting() == 0) { + bool contextual_load_in_builtin = + is_contextual && + (Bootstrapper::IsActive() || + (!info_->closure().is_null() && info_->closure()->IsBuiltin())); + + if (scope()->is_global_scope() || + loop_nesting() == 0 || + contextual_load_in_builtin) { Comment cmnt(masm(), "[ Load from named Property"); // Setup the name register and call load IC. frame_->CallLoadIC(name, @@ -6773,12 +6848,19 @@ void CodeGenerator::EmitNamedLoad(Handle name, bool is_contextual) { frame_->EmitPush(r0); // Push answer. } else { // Inline the in-object property case. - Comment cmnt(masm(), "[ Inlined named property load"); + Comment cmnt(masm(), is_contextual + ? "[ Inlined contextual property load" + : "[ Inlined named property load"); // Counter will be decremented in the deferred code. Placed here to avoid // having it in the instruction stream below where patching will occur. - __ IncrementCounter(&Counters::named_load_inline, 1, - frame_->scratch0(), frame_->scratch1()); + if (is_contextual) { + __ IncrementCounter(&Counters::named_load_global_inline, 1, + frame_->scratch0(), frame_->scratch1()); + } else { + __ IncrementCounter(&Counters::named_load_inline, 1, + frame_->scratch0(), frame_->scratch1()); + } // The following instructions are the inlined load of an in-object property. // Parts of this code is patched, so the exact instructions generated needs @@ -6789,19 +6871,57 @@ void CodeGenerator::EmitNamedLoad(Handle name, bool is_contextual) { Register receiver = frame_->PopToRegister(); DeferredReferenceGetNamedValue* deferred = - new DeferredReferenceGetNamedValue(receiver, name); + new DeferredReferenceGetNamedValue(receiver, name, is_contextual); + + bool is_dont_delete = false; + if (is_contextual) { + if (!info_->closure().is_null()) { + // When doing lazy compilation we can check if the global cell + // already exists and use its "don't delete" status as a hint. + AssertNoAllocation no_gc; + v8::internal::GlobalObject* global_object = + info_->closure()->context()->global(); + LookupResult lookup; + global_object->LocalLookupRealNamedProperty(*name, &lookup); + if (lookup.IsProperty() && lookup.type() == NORMAL) { + ASSERT(lookup.holder() == global_object); + ASSERT(global_object->property_dictionary()->ValueAt( + lookup.GetDictionaryEntry())->IsJSGlobalPropertyCell()); + is_dont_delete = lookup.IsDontDelete(); + } + } + if (is_dont_delete) { + __ IncrementCounter(&Counters::dont_delete_hint_hit, 1, + frame_->scratch0(), frame_->scratch1()); + } + } + + { Assembler::BlockConstPoolScope block_const_pool(masm_); + if (!is_contextual) { + // Check that the receiver is a heap object. + __ tst(receiver, Operand(kSmiTagMask)); + deferred->Branch(eq); + } + + // Check for the_hole_value if necessary. + // Below we rely on the number of instructions generated, and we can't + // cope with the Check macro which does not generate a fixed number of + // instructions. + Label skip, check_the_hole, cont; + if (FLAG_debug_code && is_contextual && is_dont_delete) { + __ b(&skip); + __ bind(&check_the_hole); + __ Check(ne, "DontDelete cells can't contain the hole"); + __ b(&cont); + __ bind(&skip); + } #ifdef DEBUG - int kInlinedNamedLoadInstructions = 7; - Label check_inlined_codesize; - masm_->bind(&check_inlined_codesize); + int InlinedNamedLoadInstructions = 5; + Label check_inlined_codesize; + masm_->bind(&check_inlined_codesize); #endif - { Assembler::BlockConstPoolScope block_const_pool(masm_); - // Check that the receiver is a heap object. - __ tst(receiver, Operand(kSmiTagMask)); - deferred->Branch(eq); - Register scratch = VirtualFrame::scratch0(); Register scratch2 = VirtualFrame::scratch1(); @@ -6812,12 +6932,42 @@ void CodeGenerator::EmitNamedLoad(Handle name, bool is_contextual) { __ cmp(scratch, scratch2); deferred->Branch(ne); - // Initially use an invalid index. The index will be patched by the - // inline cache code. - __ ldr(receiver, MemOperand(receiver, 0)); + if (is_contextual) { +#ifdef DEBUG + InlinedNamedLoadInstructions += 1; +#endif + // Load the (initially invalid) cell and get its value. + masm()->mov(receiver, Operand(Factory::null_value())); + __ ldr(receiver, + FieldMemOperand(receiver, JSGlobalPropertyCell::kValueOffset)); + + deferred->set_is_dont_delete(is_dont_delete); + + if (!is_dont_delete) { +#ifdef DEBUG + InlinedNamedLoadInstructions += 3; +#endif + __ cmp(receiver, Operand(Factory::the_hole_value())); + deferred->Branch(eq); + } else if (FLAG_debug_code) { +#ifdef DEBUG + InlinedNamedLoadInstructions += 3; +#endif + __ cmp(receiver, Operand(Factory::the_hole_value())); + __ b(&check_the_hole, eq); + __ bind(&cont); + } + } else { + // Initially use an invalid index. The index will be patched by the + // inline cache code. + __ ldr(receiver, MemOperand(receiver, 0)); + } // Make sure that the expected number of instructions are generated. - ASSERT_EQ(kInlinedNamedLoadInstructions, + // If the code before is updated, the offsets in ic-arm.cc + // LoadIC::PatchInlinedContextualLoad and PatchInlinedLoad need + // to be updated. + ASSERT_EQ(InlinedNamedLoadInstructions, masm_->InstructionsGeneratedSince(&check_inlined_codesize)); } diff --git a/deps/v8/src/arm/codegen-arm.h b/deps/v8/src/arm/codegen-arm.h index 6905d2331e..1930f5e1a4 100644 --- a/deps/v8/src/arm/codegen-arm.h +++ b/deps/v8/src/arm/codegen-arm.h @@ -194,14 +194,6 @@ enum ArgumentsAllocationMode { }; -// Different nop operations are used by the code generator to detect certain -// states of the generated code. -enum NopMarkerTypes { - NON_MARKING_NOP = 0, - PROPERTY_ACCESS_INLINED -}; - - // ------------------------------------------------------------------------- // CodeGenerator diff --git a/deps/v8/src/arm/debug-arm.cc b/deps/v8/src/arm/debug-arm.cc index 8128f7deaf..f19e69396e 100644 --- a/deps/v8/src/arm/debug-arm.cc +++ b/deps/v8/src/arm/debug-arm.cc @@ -279,7 +279,7 @@ void Debug::GenerateSlot(MacroAssembler* masm) { __ bind(&check_codesize); __ RecordDebugBreakSlot(); for (int i = 0; i < Assembler::kDebugBreakSlotInstructions; i++) { - __ nop(2); + __ nop(MacroAssembler::DEBUG_BREAK_NOP); } ASSERT_EQ(Assembler::kDebugBreakSlotInstructions, masm->InstructionsGeneratedSince(&check_codesize)); diff --git a/deps/v8/src/arm/ic-arm.cc b/deps/v8/src/arm/ic-arm.cc index 4c1f9835f4..ef7cf6af46 100644 --- a/deps/v8/src/arm/ic-arm.cc +++ b/deps/v8/src/arm/ic-arm.cc @@ -904,9 +904,9 @@ void LoadIC::GenerateMiss(MacroAssembler* masm) { __ TailCallExternalReference(ref, 2, 1); } - -static inline bool IsInlinedICSite(Address address, - Address* inline_end_address) { +// Returns the code marker, or the 0 if the code is not marked. +static inline int InlinedICSiteMarker(Address address, + Address* inline_end_address) { // If the instruction after the call site is not the pseudo instruction nop1 // then this is not related to an inlined in-object property load. The nop1 // instruction is located just after the call to the IC in the deferred code @@ -914,9 +914,11 @@ static inline bool IsInlinedICSite(Address address, // a branch instruction for jumping back from the deferred code. Address address_after_call = address + Assembler::kCallTargetAddressOffset; Instr instr_after_call = Assembler::instr_at(address_after_call); - if (!Assembler::IsNop(instr_after_call, PROPERTY_ACCESS_INLINED)) { - return false; - } + int code_marker = MacroAssembler::GetCodeMarker(instr_after_call); + + // A negative result means the code is not marked. + if (code_marker <= 0) return 0; + Address address_after_nop = address_after_call + Assembler::kInstrSize; Instr instr_after_nop = Assembler::instr_at(address_after_nop); // There may be some reg-reg move and frame merging code to skip over before @@ -933,7 +935,7 @@ static inline bool IsInlinedICSite(Address address, ASSERT(b_offset < 0); // Jumping back from deferred code. *inline_end_address = address_after_nop + b_offset; - return true; + return code_marker; } @@ -941,7 +943,10 @@ bool LoadIC::PatchInlinedLoad(Address address, Object* map, int offset) { // Find the end of the inlined code for handling the load if this is an // inlined IC call site. Address inline_end_address; - if (!IsInlinedICSite(address, &inline_end_address)) return false; + if (InlinedICSiteMarker(address, &inline_end_address) + != Assembler::PROPERTY_ACCESS_INLINED) { + return false; + } // Patch the offset of the property load instruction (ldr r0, [r1, #+XXX]). // The immediate must be representable in 12 bits. @@ -959,8 +964,12 @@ bool LoadIC::PatchInlinedLoad(Address address, Object* map, int offset) { CPU::FlushICache(ldr_property_instr_address, 1 * Assembler::kInstrSize); // Patch the map check. + // For PROPERTY_ACCESS_INLINED, the load map instruction is generated + // 4 instructions before the end of the inlined code. + // See codgen-arm.cc CodeGenerator::EmitNamedLoad. + int ldr_map_offset = -4; Address ldr_map_instr_address = - inline_end_address - 4 * Assembler::kInstrSize; + inline_end_address + ldr_map_offset * Assembler::kInstrSize; Assembler::set_target_address_at(ldr_map_instr_address, reinterpret_cast
(map)); return true; @@ -971,8 +980,41 @@ bool LoadIC::PatchInlinedContextualLoad(Address address, Object* map, Object* cell, bool is_dont_delete) { - // TODO(): implement this. - return false; + // Find the end of the inlined code for handling the contextual load if + // this is inlined IC call site. + Address inline_end_address; + int marker = InlinedICSiteMarker(address, &inline_end_address); + if (!((marker == Assembler::PROPERTY_ACCESS_INLINED_CONTEXT) || + (marker == Assembler::PROPERTY_ACCESS_INLINED_CONTEXT_DONT_DELETE))) { + return false; + } + // On ARM we don't rely on the is_dont_delete argument as the hint is already + // embedded in the code marker. + bool marker_is_dont_delete = + marker == Assembler::PROPERTY_ACCESS_INLINED_CONTEXT_DONT_DELETE; + + // These are the offsets from the end of the inlined code. + // See codgen-arm.cc CodeGenerator::EmitNamedLoad. + int ldr_map_offset = marker_is_dont_delete ? -5: -8; + int ldr_cell_offset = marker_is_dont_delete ? -2: -5; + if (FLAG_debug_code && marker_is_dont_delete) { + // Three extra instructions were generated to check for the_hole_value. + ldr_map_offset -= 3; + ldr_cell_offset -= 3; + } + Address ldr_map_instr_address = + inline_end_address + ldr_map_offset * Assembler::kInstrSize; + Address ldr_cell_instr_address = + inline_end_address + ldr_cell_offset * Assembler::kInstrSize; + + // Patch the map check. + Assembler::set_target_address_at(ldr_map_instr_address, + reinterpret_cast
(map)); + // Patch the cell address. + Assembler::set_target_address_at(ldr_cell_instr_address, + reinterpret_cast
(cell)); + + return true; } @@ -980,7 +1022,10 @@ bool StoreIC::PatchInlinedStore(Address address, Object* map, int offset) { // Find the end of the inlined code for the store if there is an // inlined version of the store. Address inline_end_address; - if (!IsInlinedICSite(address, &inline_end_address)) return false; + if (InlinedICSiteMarker(address, &inline_end_address) + != Assembler::PROPERTY_ACCESS_INLINED) { + return false; + } // Compute the address of the map load instruction. Address ldr_map_instr_address = @@ -1025,7 +1070,10 @@ bool StoreIC::PatchInlinedStore(Address address, Object* map, int offset) { bool KeyedLoadIC::PatchInlinedLoad(Address address, Object* map) { Address inline_end_address; - if (!IsInlinedICSite(address, &inline_end_address)) return false; + if (InlinedICSiteMarker(address, &inline_end_address) + != Assembler::PROPERTY_ACCESS_INLINED) { + return false; + } // Patch the map check. Address ldr_map_instr_address = @@ -1042,7 +1090,10 @@ bool KeyedStoreIC::PatchInlinedStore(Address address, Object* map) { // Find the end of the inlined code for handling the store if this is an // inlined IC call site. Address inline_end_address; - if (!IsInlinedICSite(address, &inline_end_address)) return false; + if (InlinedICSiteMarker(address, &inline_end_address) + != Assembler::PROPERTY_ACCESS_INLINED) { + return false; + } // Patch the map check. Address ldr_map_instr_address = diff --git a/deps/v8/src/arm/macro-assembler-arm.cc b/deps/v8/src/arm/macro-assembler-arm.cc index d2c22af53d..ea85c79454 100644 --- a/deps/v8/src/arm/macro-assembler-arm.cc +++ b/deps/v8/src/arm/macro-assembler-arm.cc @@ -1693,6 +1693,33 @@ void MacroAssembler::LoadContext(Register dst, int context_chain_length) { } +void MacroAssembler::LoadGlobalFunction(int index, Register function) { + // Load the global or builtins object from the current context. + ldr(function, MemOperand(cp, Context::SlotOffset(Context::GLOBAL_INDEX))); + // Load the global context from the global or builtins object. + ldr(function, FieldMemOperand(function, + GlobalObject::kGlobalContextOffset)); + // Load the function from the global context. + ldr(function, MemOperand(function, Context::SlotOffset(index))); +} + + +void MacroAssembler::LoadGlobalFunctionInitialMap(Register function, + Register map, + Register scratch) { + // Load the initial map. The global functions all have initial maps. + ldr(map, FieldMemOperand(function, JSFunction::kPrototypeOrInitialMapOffset)); + if (FLAG_debug_code) { + Label ok, fail; + CheckMap(map, scratch, Heap::kMetaMapRootIndex, &fail, false); + b(&ok); + bind(&fail); + Abort("Global functions must have initial map"); + bind(&ok); + } +} + + void MacroAssembler::JumpIfNotBothSmi(Register reg1, Register reg2, Label* on_not_both_smi) { diff --git a/deps/v8/src/arm/macro-assembler-arm.h b/deps/v8/src/arm/macro-assembler-arm.h index 8d89d6984c..8bd134c38e 100644 --- a/deps/v8/src/arm/macro-assembler-arm.h +++ b/deps/v8/src/arm/macro-assembler-arm.h @@ -264,6 +264,14 @@ class MacroAssembler: public Assembler { void LoadContext(Register dst, int context_chain_length); + void LoadGlobalFunction(int index, Register function); + + // Load the initial map from the global function. The registers + // function and map can be the same, function is then overwritten. + void LoadGlobalFunctionInitialMap(Register function, + Register map, + Register scratch); + // --------------------------------------------------------------------------- // JavaScript invokes @@ -319,6 +327,40 @@ class MacroAssembler: public Assembler { Register scratch, Label* miss); + inline void MarkCode(NopMarkerTypes type) { + nop(type); + } + + // Check if the given instruction is a 'type' marker. + // ie. check if is is a mov r, r (referenced as nop(type)) + // These instructions are generated to mark special location in the code, + // like some special IC code. + static inline bool IsMarkedCode(Instr instr, int type) { + ASSERT((FIRST_IC_MARKER <= type) && (type < LAST_CODE_MARKER)); + return IsNop(instr, type); + } + + + static inline int GetCodeMarker(Instr instr) { + int dst_reg_offset = 12; + int dst_mask = 0xf << dst_reg_offset; + int src_mask = 0xf; + int dst_reg = (instr & dst_mask) >> dst_reg_offset; + int src_reg = instr & src_mask; + uint32_t non_register_mask = ~(dst_mask | src_mask); + uint32_t mov_mask = al | 13 << 21; + + // Return if we have a mov rn rn, else return -1. + int type = ((instr & non_register_mask) == mov_mask) && + (dst_reg == src_reg) && + (FIRST_IC_MARKER <= dst_reg) && (dst_reg < LAST_CODE_MARKER) + ? src_reg + : -1; + ASSERT((type == -1) || + ((FIRST_IC_MARKER <= type) && (type < LAST_CODE_MARKER))); + return type; + } + // --------------------------------------------------------------------------- // Allocation support diff --git a/deps/v8/src/arm/stub-cache-arm.cc b/deps/v8/src/arm/stub-cache-arm.cc index f3f7a5d4b3..0a5eac27f6 100644 --- a/deps/v8/src/arm/stub-cache-arm.cc +++ b/deps/v8/src/arm/stub-cache-arm.cc @@ -2902,8 +2902,7 @@ MaybeObject* KeyedStoreStubCompiler::CompileStoreField(JSObject* object, } -MaybeObject* ConstructStubCompiler::CompileConstructStub( - SharedFunctionInfo* shared) { +MaybeObject* ConstructStubCompiler::CompileConstructStub(JSFunction* function) { // ----------- S t a t e ------------- // -- r0 : argc // -- r1 : constructor @@ -2987,6 +2986,7 @@ MaybeObject* ConstructStubCompiler::CompileConstructStub( // r7: undefined // Fill the initialized properties with a constant value or a passed argument // depending on the this.x = ...; assignment in the function. + SharedFunctionInfo* shared = function->shared(); for (int i = 0; i < shared->this_property_assignments_count(); i++) { if (shared->IsThisPropertyAssignmentArgument(i)) { Label not_passed, next; @@ -3011,8 +3011,9 @@ MaybeObject* ConstructStubCompiler::CompileConstructStub( } // Fill the unused in-object property fields with undefined. + ASSERT(function->has_initial_map()); for (int i = shared->this_property_assignments_count(); - i < shared->CalculateInObjectProperties(); + i < function->initial_map()->inobject_properties(); i++) { __ str(r7, MemOperand(r5, kPointerSize, PostIndex)); } diff --git a/deps/v8/src/checks.cc b/deps/v8/src/checks.cc index 1ab8802ec3..3c3d940be7 100644 --- a/deps/v8/src/checks.cc +++ b/deps/v8/src/checks.cc @@ -107,3 +107,4 @@ namespace v8 { namespace internal { intptr_t HeapObjectTagMask() { return kHeapObjectTagMask; } } } // namespace v8::internal + diff --git a/deps/v8/src/checks.h b/deps/v8/src/checks.h index 6b493225ad..d49f97f132 100644 --- a/deps/v8/src/checks.h +++ b/deps/v8/src/checks.h @@ -31,7 +31,6 @@ #include extern "C" void V8_Fatal(const char* file, int line, const char* format, ...); -void API_Fatal(const char* location, const char* format, ...); // The FATAL, UNREACHABLE and UNIMPLEMENTED macros are useful during // development, but they should not be relied on in the final product. @@ -222,28 +221,6 @@ static inline void CheckNonEqualsHelper(const char* file, } -namespace v8 { - class Value; - template class Handle; -} - - -void CheckNonEqualsHelper(const char* file, - int line, - const char* unexpected_source, - v8::Handle unexpected, - const char* value_source, - v8::Handle value); - - -void CheckEqualsHelper(const char* file, - int line, - const char* expected_source, - v8::Handle expected, - const char* value_source, - v8::Handle value); - - #define CHECK_EQ(expected, value) CheckEqualsHelper(__FILE__, __LINE__, \ #expected, expected, #value, value) @@ -307,17 +284,6 @@ bool EnableSlowAsserts(); // 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(address) & HeapObjectTagMask()) == 0) - -#define ASSERT_SIZE_TAG_ALIGNED(size) ASSERT((size & HeapObjectTagMask()) == 0) - #define ASSERT_NOT_NULL(p) ASSERT_NE(NULL, p) #endif // V8_CHECKS_H_ diff --git a/deps/v8/src/conversions.cc b/deps/v8/src/conversions.cc index 19fa7773ab..a954d6cc69 100644 --- a/deps/v8/src/conversions.cc +++ b/deps/v8/src/conversions.cc @@ -448,12 +448,12 @@ static double InternalStringToDouble(Iterator current, bool sign = false; if (*current == '+') { - // Ignore leading sign; skip following spaces. + // Ignore leading sign. ++current; - if (!AdvanceToNonspace(¤t, end)) return JUNK_STRING_VALUE; + if (current == end) return JUNK_STRING_VALUE; } else if (*current == '-') { ++current; - if (!AdvanceToNonspace(¤t, end)) return JUNK_STRING_VALUE; + if (current == end) return JUNK_STRING_VALUE; sign = true; } diff --git a/deps/v8/src/dateparser-inl.h b/deps/v8/src/dateparser-inl.h index e52cc94a48..ac28c62257 100644 --- a/deps/v8/src/dateparser-inl.h +++ b/deps/v8/src/dateparser-inl.h @@ -59,7 +59,7 @@ bool DateParser::Parse(Vector str, FixedArray* out) { } else if (in.Skip('.') && time.IsExpecting(n)) { time.Add(n); if (!in.IsAsciiDigit()) return false; - int n = in.ReadUnsignedNumber(); + int n = in.ReadMilliseconds(); time.AddFinal(n); } else if (tz.IsExpecting(n)) { tz.SetAbsoluteMinute(n); diff --git a/deps/v8/src/dateparser.h b/deps/v8/src/dateparser.h index 28053f46d1..40e56f3024 100644 --- a/deps/v8/src/dateparser.h +++ b/deps/v8/src/dateparser.h @@ -87,6 +87,18 @@ class DateParser : public AllStatic { return n; } + // Read a string of digits, take the first three or fewer as an unsigned + // number of milliseconds, and ignore any digits after the first three. + int ReadMilliseconds() { + has_read_number_ = true; + int n = 0; + int power; + for (power = 100; IsAsciiDigit(); Next(), power = power / 10) { + n = n + power * (ch_ - '0'); + } + return n; + } + // Read a word (sequence of chars. >= 'A'), fill the given buffer with a // lower-case prefix, and pad any remainder of the buffer with zeroes. // Return word length. diff --git a/deps/v8/src/handles.cc b/deps/v8/src/handles.cc index 3dfb886e99..37a5011ce7 100644 --- a/deps/v8/src/handles.cc +++ b/deps/v8/src/handles.cc @@ -143,7 +143,7 @@ Handle ReinitializeJSGlobalProxy( void SetExpectedNofProperties(Handle func, int nof) { // If objects constructed from this function exist then changing - // 'estimated_nof_properties' is dangerous since the previois value might + // 'estimated_nof_properties' is dangerous since the previous value might // have been compiled into the fast construct stub. More over, the inobject // slack tracking logic might have adjusted the previous value, so even // passing the same value is risky. diff --git a/deps/v8/src/hashmap.cc b/deps/v8/src/hashmap.cc index 3c4e5cdc60..1422afdc7e 100644 --- a/deps/v8/src/hashmap.cc +++ b/deps/v8/src/hashmap.cc @@ -25,7 +25,11 @@ // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -#include "v8.h" +#include "../include/v8stdint.h" +#include "globals.h" +#include "checks.h" +#include "utils.h" +#include "allocation.h" #include "hashmap.h" @@ -195,7 +199,7 @@ void HashMap::Initialize(uint32_t capacity) { ASSERT(IsPowerOf2(capacity)); map_ = reinterpret_cast(allocator_->New(capacity * sizeof(Entry))); if (map_ == NULL) { - V8::FatalProcessOutOfMemory("HashMap::Initialize"); + v8::internal::FatalProcessOutOfMemory("HashMap::Initialize"); return; } capacity_ = capacity; diff --git a/deps/v8/src/heap-profiler.cc b/deps/v8/src/heap-profiler.cc index 3fb1ec1122..91ac9867a2 100644 --- a/deps/v8/src/heap-profiler.cc +++ b/deps/v8/src/heap-profiler.cc @@ -69,7 +69,8 @@ class Clusterizer : public AllStatic { JSObjectsCluster Clusterizer::Clusterize(HeapObject* obj, bool fine_grain) { if (obj->IsJSObject()) { JSObject* js_obj = JSObject::cast(obj); - String* constructor = JSObject::cast(js_obj)->constructor_name(); + String* constructor = GetConstructorNameForHeapProfile( + JSObject::cast(js_obj)); // Differentiate Object and Array instances. if (fine_grain && (constructor == Heap::Object_symbol() || constructor == Heap::Array_symbol())) { @@ -714,7 +715,7 @@ static void StackWeakReferenceCallback(Persistent object, static void PrintProducerStackTrace(Object* obj, void* trace) { if (!obj->IsJSObject()) return; - String* constructor = JSObject::cast(obj)->constructor_name(); + String* constructor = GetConstructorNameForHeapProfile(JSObject::cast(obj)); SmartPointer s_name( constructor->ToCString(DISALLOW_NULLS, ROBUST_STRING_TRAVERSAL)); LOG(HeapSampleJSProducerEvent(GetConstructorName(*s_name), @@ -886,7 +887,8 @@ static JSObjectsCluster HeapObjectAsCluster(HeapObject* object) { return JSObjectsCluster(String::cast(object)); } else { JSObject* js_obj = JSObject::cast(object); - String* constructor = JSObject::cast(js_obj)->constructor_name(); + String* constructor = GetConstructorNameForHeapProfile( + JSObject::cast(js_obj)); return JSObjectsCluster(constructor, object); } } @@ -1064,6 +1066,8 @@ void AggregatedHeapSnapshotGenerator::FillHeapSnapshot(HeapSnapshot* snapshot) { // Fill up references. IterateRetainers(&entries_map); + + snapshot->SetDominatorsToSelf(); } diff --git a/deps/v8/src/ia32/macro-assembler-ia32.cc b/deps/v8/src/ia32/macro-assembler-ia32.cc index 61aadf7ebd..cbf93dd6a1 100644 --- a/deps/v8/src/ia32/macro-assembler-ia32.cc +++ b/deps/v8/src/ia32/macro-assembler-ia32.cc @@ -537,7 +537,6 @@ void MacroAssembler::CheckAccessGlobalProxy(Register holder_reg, void MacroAssembler::LoadAllocationTopHelper(Register result, - Register result_end, Register scratch, AllocationFlags flags) { ExternalReference new_space_allocation_top = @@ -559,7 +558,6 @@ void MacroAssembler::LoadAllocationTopHelper(Register result, if (scratch.is(no_reg)) { mov(result, Operand::StaticVariable(new_space_allocation_top)); } else { - ASSERT(!scratch.is(result_end)); mov(Operand(scratch), Immediate(new_space_allocation_top)); mov(result, Operand(scratch, 0)); } @@ -608,7 +606,7 @@ void MacroAssembler::AllocateInNewSpace(int object_size, ASSERT(!result.is(result_end)); // Load address of new object into result. - LoadAllocationTopHelper(result, result_end, scratch, flags); + LoadAllocationTopHelper(result, scratch, flags); Register top_reg = result_end.is_valid() ? result_end : result; @@ -664,7 +662,7 @@ void MacroAssembler::AllocateInNewSpace(int header_size, ASSERT(!result.is(result_end)); // Load address of new object into result. - LoadAllocationTopHelper(result, result_end, scratch, flags); + LoadAllocationTopHelper(result, scratch, flags); // Calculate new top and bail out if new space is exhausted. ExternalReference new_space_allocation_limit = @@ -705,7 +703,7 @@ void MacroAssembler::AllocateInNewSpace(Register object_size, ASSERT(!result.is(result_end)); // Load address of new object into result. - LoadAllocationTopHelper(result, result_end, scratch, flags); + LoadAllocationTopHelper(result, scratch, flags); // Calculate new top and bail out if new space is exhausted. ExternalReference new_space_allocation_limit = diff --git a/deps/v8/src/ia32/macro-assembler-ia32.h b/deps/v8/src/ia32/macro-assembler-ia32.h index cea7a70183..d208dbe3fa 100644 --- a/deps/v8/src/ia32/macro-assembler-ia32.h +++ b/deps/v8/src/ia32/macro-assembler-ia32.h @@ -631,7 +631,6 @@ class MacroAssembler: public Assembler { // Allocation support helpers. void LoadAllocationTopHelper(Register result, - Register result_end, Register scratch, AllocationFlags flags); void UpdateAllocationTopHelper(Register result_end, Register scratch); diff --git a/deps/v8/src/ia32/stub-cache-ia32.cc b/deps/v8/src/ia32/stub-cache-ia32.cc index 3120ff9da7..adcb5219ec 100644 --- a/deps/v8/src/ia32/stub-cache-ia32.cc +++ b/deps/v8/src/ia32/stub-cache-ia32.cc @@ -3021,8 +3021,7 @@ MaybeObject* KeyedLoadStubCompiler::CompileLoadFunctionPrototype(String* name) { // Specialized stub for constructing objects from functions which only have only // simple assignments of the form this.x = ...; in their body. -MaybeObject* ConstructStubCompiler::CompileConstructStub( - SharedFunctionInfo* shared) { +MaybeObject* ConstructStubCompiler::CompileConstructStub(JSFunction* function) { // ----------- S t a t e ------------- // -- eax : argc // -- edi : constructor @@ -3098,6 +3097,7 @@ MaybeObject* ConstructStubCompiler::CompileConstructStub( // edi: undefined // Fill the initialized properties with a constant value or a passed argument // depending on the this.x = ...; assignment in the function. + SharedFunctionInfo* shared = function->shared(); for (int i = 0; i < shared->this_property_assignments_count(); i++) { if (shared->IsThisPropertyAssignmentArgument(i)) { // Check if the argument assigned to the property is actually passed. @@ -3125,8 +3125,9 @@ MaybeObject* ConstructStubCompiler::CompileConstructStub( } // Fill the unused in-object property fields with undefined. + ASSERT(function->has_initial_map()); for (int i = shared->this_property_assignments_count(); - i < shared->CalculateInObjectProperties(); + i < function->initial_map()->inobject_properties(); i++) { __ mov(Operand(edx, i * kPointerSize), edi); } diff --git a/deps/v8/src/log.cc b/deps/v8/src/log.cc index d12aafb6df..55f15debd0 100644 --- a/deps/v8/src/log.cc +++ b/deps/v8/src/log.cc @@ -194,11 +194,6 @@ class Ticker: public Sampler { ~Ticker() { if (IsActive()) Stop(); } - virtual void SampleStack(TickSample* sample) { - ASSERT(IsSynchronous()); - StackTracer::Trace(sample); - } - virtual void Tick(TickSample* sample) { if (profiler_) profiler_->Insert(sample); if (window_) window_->AddState(sample->state); @@ -224,6 +219,12 @@ class Ticker: public Sampler { if (!window_ && IsActive()) Stop(); } + protected: + virtual void DoSampleStack(TickSample* sample) { + ASSERT(IsSynchronous()); + StackTracer::Trace(sample); + } + private: SlidingStateWindow* window_; Profiler* profiler_; diff --git a/deps/v8/src/objects.cc b/deps/v8/src/objects.cc index 8efb0daae6..f5d19e280d 100644 --- a/deps/v8/src/objects.cc +++ b/deps/v8/src/objects.cc @@ -1166,9 +1166,6 @@ String* JSObject::class_name() { String* JSObject::constructor_name() { - if (IsJSFunction()) { - return Heap::closure_symbol(); - } if (map()->constructor()->IsJSFunction()) { JSFunction* constructor = JSFunction::cast(map()->constructor()); String* name = String::cast(constructor->shared()->name()); diff --git a/deps/v8/src/parser.cc b/deps/v8/src/parser.cc index 7e4a51e2fa..186d1020d7 100644 --- a/deps/v8/src/parser.cc +++ b/deps/v8/src/parser.cc @@ -356,65 +356,6 @@ Handle Parser::LookupCachedSymbol(int symbol_id, } -Vector PartialParserRecorder::ExtractData() { - int function_size = function_store_.size(); - int total_size = ScriptDataImpl::kHeaderSize + function_size; - Vector data = Vector::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; -} - - -void CompleteParserRecorder::LogSymbol(int start, Vector literal) { - if (!is_recording_) return; - - int hash = vector_hash(literal); - HashMap::Entry* entry = symbol_table_.Lookup(&literal, hash, true); - int id = static_cast(reinterpret_cast(entry->value)); - if (id == 0) { - // Put (symbol_id_ + 1) into entry and increment it. - id = ++symbol_id_; - entry->value = reinterpret_cast(id); - Vector > symbol = symbol_entries_.AddBlock(1, literal); - entry->key = &symbol[0]; - } - WriteNumber(id - 1); -} - - -Vector 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 data = Vector::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::cast(data.SubVector(symbol_start, total_size))); - } - return data; -} - - FunctionEntry ScriptDataImpl::GetFunctionEntry(int start) { // The current pre-data entry must be a FunctionEntry with the given // start position. @@ -437,92 +378,52 @@ int ScriptDataImpl::GetSymbolIdentifier() { bool ScriptDataImpl::SanityCheck() { // Check that the header data is valid and doesn't specify // point to positions outside the store. - if (store_.length() < ScriptDataImpl::kHeaderSize) return false; - if (magic() != ScriptDataImpl::kMagicNumber) return false; - if (version() != ScriptDataImpl::kCurrentVersion) return false; + if (store_.length() < PreparseDataConstants::kHeaderSize) return false; + if (magic() != PreparseDataConstants::kMagicNumber) return false; + if (version() != PreparseDataConstants::kCurrentVersion) return false; if (has_error()) { // Extra sane sanity check for error message encoding. - if (store_.length() <= kHeaderSize + kMessageTextPos) return false; - if (Read(kMessageStartPos) > Read(kMessageEndPos)) return false; - unsigned arg_count = Read(kMessageArgCountPos); - int pos = kMessageTextPos; + if (store_.length() <= PreparseDataConstants::kHeaderSize + + PreparseDataConstants::kMessageTextPos) { + return false; + } + if (Read(PreparseDataConstants::kMessageStartPos) > + Read(PreparseDataConstants::kMessageEndPos)) { + return false; + } + unsigned arg_count = Read(PreparseDataConstants::kMessageArgCountPos); + int pos = PreparseDataConstants::kMessageTextPos; for (unsigned int i = 0; i <= arg_count; i++) { - if (store_.length() <= kHeaderSize + pos) return false; + if (store_.length() <= PreparseDataConstants::kHeaderSize + pos) { + return false; + } int length = static_cast(Read(pos)); if (length < 0) return false; pos += 1 + length; } - if (store_.length() < kHeaderSize + pos) return false; + if (store_.length() < PreparseDataConstants::kHeaderSize + pos) { + return false; + } return true; } // Check that the space allocated for function entries is sane. int functions_size = - static_cast(store_[ScriptDataImpl::kFunctionsSizeOffset]); + static_cast(store_[PreparseDataConstants::kFunctionsSizeOffset]); if (functions_size < 0) return false; if (functions_size % FunctionEntry::kSize != 0) return false; // Check that the count of symbols is non-negative. int symbol_count = - static_cast(store_[ScriptDataImpl::kSymbolCountOffset]); + static_cast(store_[PreparseDataConstants::kSymbolCountOffset]); if (symbol_count < 0) return false; // Check that the total size has room for header and function entries. int minimum_size = - ScriptDataImpl::kHeaderSize + functions_size; + PreparseDataConstants::kHeaderSize + functions_size; if (store_.length() < minimum_size) return false; return true; } -PartialParserRecorder::PartialParserRecorder() - : function_store_(0), - is_recording_(true), - pause_count_(0) { - preamble_[ScriptDataImpl::kMagicOffset] = ScriptDataImpl::kMagicNumber; - preamble_[ScriptDataImpl::kVersionOffset] = ScriptDataImpl::kCurrentVersion; - preamble_[ScriptDataImpl::kHasErrorOffset] = false; - preamble_[ScriptDataImpl::kFunctionsSizeOffset] = 0; - preamble_[ScriptDataImpl::kSymbolCountOffset] = 0; - preamble_[ScriptDataImpl::kSizeOffset] = 0; - ASSERT_EQ(6, ScriptDataImpl::kHeaderSize); -#ifdef DEBUG - prev_start_ = -1; -#endif -} - - -CompleteParserRecorder::CompleteParserRecorder() - : PartialParserRecorder(), - symbol_store_(0), - symbol_entries_(0), - symbol_table_(vector_compare), - symbol_id_(0) { -} - - -void PartialParserRecorder::WriteString(Vector str) { - function_store_.Add(str.length()); - for (int i = 0; i < str.length(); i++) { - function_store_.Add(str[i]); - } -} - - -void CompleteParserRecorder::WriteNumber(int number) { - ASSERT(number >= 0); - - int mask = (1 << 28) - 1; - for (int i = 28; i > 0; i -= 7) { - if (number > mask) { - symbol_store_.Add(static_cast(number >> i) | 0x80u); - number &= mask; - } - mask >>= 7; - } - symbol_store_.Add(static_cast(number)); -} - - - const char* ScriptDataImpl::ReadString(unsigned* start, int* chars) { int length = start[0]; char* result = NewArray(length + 1); @@ -534,47 +435,26 @@ const char* ScriptDataImpl::ReadString(unsigned* start, int* chars) { return result; } - -void PartialParserRecorder::LogMessage(Scanner::Location loc, - const char* message, - Vector args) { - if (has_error()) return; - preamble_[ScriptDataImpl::kHasErrorOffset] = true; - function_store_.Reset(); - STATIC_ASSERT(ScriptDataImpl::kMessageStartPos == 0); - function_store_.Add(loc.beg_pos); - STATIC_ASSERT(ScriptDataImpl::kMessageEndPos == 1); - function_store_.Add(loc.end_pos); - STATIC_ASSERT(ScriptDataImpl::kMessageArgCountPos == 2); - function_store_.Add(args.length()); - STATIC_ASSERT(ScriptDataImpl::kMessageTextPos == 3); - WriteString(CStrVector(message)); - for (int i = 0; i < args.length(); i++) { - WriteString(CStrVector(args[i])); - } - is_recording_ = false; -} - - Scanner::Location ScriptDataImpl::MessageLocation() { - int beg_pos = Read(kMessageStartPos); - int end_pos = Read(kMessageEndPos); + int beg_pos = Read(PreparseDataConstants::kMessageStartPos); + int end_pos = Read(PreparseDataConstants::kMessageEndPos); return Scanner::Location(beg_pos, end_pos); } const char* ScriptDataImpl::BuildMessage() { - unsigned* start = ReadAddress(kMessageTextPos); + unsigned* start = ReadAddress(PreparseDataConstants::kMessageTextPos); return ReadString(start, NULL); } Vector ScriptDataImpl::BuildArgs() { - int arg_count = Read(kMessageArgCountPos); + int arg_count = Read(PreparseDataConstants::kMessageArgCountPos); const char** array = NewArray(arg_count); // Position after text found by skipping past length field and // length field content words. - int pos = kMessageTextPos + 1 + Read(kMessageTextPos); + int pos = PreparseDataConstants::kMessageTextPos + 1 + + Read(PreparseDataConstants::kMessageTextPos); for (int i = 0; i < arg_count; i++) { int count = 0; array[i] = ReadString(ReadAddress(pos), &count); @@ -585,12 +465,12 @@ Vector ScriptDataImpl::BuildArgs() { unsigned ScriptDataImpl::Read(int position) { - return store_[ScriptDataImpl::kHeaderSize + position]; + return store_[PreparseDataConstants::kHeaderSize + position]; } unsigned* ScriptDataImpl::ReadAddress(int position) { - return &store_[ScriptDataImpl::kHeaderSize + position]; + return &store_[PreparseDataConstants::kHeaderSize + position]; } @@ -4601,9 +4481,10 @@ bool ScriptDataImpl::HasError() { void ScriptDataImpl::Initialize() { // Prepares state for use. - if (store_.length() >= kHeaderSize) { - function_index_ = kHeaderSize; - int symbol_data_offset = kHeaderSize + store_[kFunctionsSizeOffset]; + if (store_.length() >= PreparseDataConstants::kHeaderSize) { + function_index_ = PreparseDataConstants::kHeaderSize; + int symbol_data_offset = PreparseDataConstants::kHeaderSize + + store_[PreparseDataConstants::kFunctionsSizeOffset]; if (store_.length() > symbol_data_offset) { symbol_data_ = reinterpret_cast(&store_[symbol_data_offset]); } else { @@ -4625,7 +4506,7 @@ int ScriptDataImpl::ReadNumber(byte** source) { byte* data = *source; if (data >= symbol_data_end_) return -1; byte input = *data; - if (input == kNumberTerminator) { + if (input == PreparseDataConstants::kNumberTerminator) { // End of stream marker. return -1; } @@ -4646,11 +4527,11 @@ int ScriptDataImpl::ReadNumber(byte** source) { static ScriptDataImpl* DoPreParse(Handle source, unibrow::CharacterStream* stream, bool allow_lazy, - PartialParserRecorder* recorder, + ParserRecorder* recorder, int literal_flags) { V8JavaScriptScanner scanner; scanner.Initialize(source, stream, literal_flags); - preparser::PreParser preparser; + preparser::PreParser preparser; if (!preparser.PreParseProgram(&scanner, recorder, allow_lazy)) { Top::StackOverflow(); return NULL; diff --git a/deps/v8/src/parser.h b/deps/v8/src/parser.h index 9a84ab9071..a067bd7c7d 100644 --- a/deps/v8/src/parser.h +++ b/deps/v8/src/parser.h @@ -32,6 +32,7 @@ #include "ast.h" #include "scanner.h" #include "scopes.h" +#include "preparse-data.h" namespace v8 { namespace internal { @@ -123,32 +124,15 @@ class ScriptDataImpl : public ScriptData { Vector BuildArgs(); int symbol_count() { - return (store_.length() > kHeaderSize) ? store_[kSymbolCountOffset] : 0; + return (store_.length() > PreparseDataConstants::kHeaderSize) + ? store_[PreparseDataConstants::kSymbolCountOffset] + : 0; } // The following functions should only be called if SanityCheck has // returned true. - bool has_error() { return store_[kHasErrorOffset]; } - unsigned magic() { return store_[kMagicOffset]; } - unsigned version() { return store_[kVersionOffset]; } - - static const unsigned kMagicNumber = 0xBadDead; - static const unsigned kCurrentVersion = 5; - - static const int kMagicOffset = 0; - static const int kVersionOffset = 1; - static const int kHasErrorOffset = 2; - static const int kFunctionsSizeOffset = 3; - static const int kSymbolCountOffset = 4; - static const int kSizeOffset = 5; - static const int kHeaderSize = 6; - - // If encoding a message, the following positions are fixed. - static const int kMessageStartPos = 0; - static const int kMessageEndPos = 1; - static const int kMessageArgCountPos = 2; - static const int kMessageTextPos = 3; - - static const byte kNumberTerminator = 0x80u; + bool has_error() { return store_[PreparseDataConstants::kHasErrorOffset]; } + unsigned magic() { return store_[PreparseDataConstants::kMagicOffset]; } + unsigned version() { return store_[PreparseDataConstants::kVersionOffset]; } private: Vector store_; @@ -177,127 +161,6 @@ class ScriptDataImpl : public ScriptData { }; -// Record only functions. -class PartialParserRecorder { - public: - PartialParserRecorder(); - virtual ~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); - } - - virtual 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 arguments; - if (argument_opt != NULL) { - arguments = Vector(&argument_opt, 1); - } - this->LogMessage(location, message, arguments); - } - - int function_position() { return function_store_.size(); } - - void LogMessage(Scanner::Location loc, - const char* message, - Vector args); - - virtual Vector 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(preamble_[ScriptDataImpl::kHasErrorOffset]); - } - - bool is_recording() { - return is_recording_; - } - - void WriteString(Vector str); - - Collector 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 ~CompleteParserRecorder() { } - - void LogSymbol(int start, Vector literal); - - virtual void LogSymbol(int start, const char* symbol, int length) { - LogSymbol(start, Vector(symbol, length)); - } - - virtual Vector ExtractData(); - - int symbol_position() { return symbol_store_.size(); } - int symbol_ids() { return symbol_id_; } - - private: - static int vector_hash(Vector 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* string1 = reinterpret_cast* >(a); - Vector* string2 = reinterpret_cast* >(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 symbol_store_; - Collector > symbol_entries_; - HashMap symbol_table_; - int symbol_id_; -}; - - - class ParserApi { public: // Parses the source code represented by the compilation info and sets its diff --git a/deps/v8/src/platform-freebsd.cc b/deps/v8/src/platform-freebsd.cc index 6b8f2c048a..0d89a16f59 100644 --- a/deps/v8/src/platform-freebsd.cc +++ b/deps/v8/src/platform-freebsd.cc @@ -620,7 +620,8 @@ Sampler::Sampler(int interval, bool profiling) : interval_(interval), profiling_(profiling), synchronous_(profiling), - active_(false) { + active_(false), + samples_taken_(0) { data_ = new PlatformData(); } diff --git a/deps/v8/src/platform-linux.cc b/deps/v8/src/platform-linux.cc index 89003ba83f..cb8e919ea7 100644 --- a/deps/v8/src/platform-linux.cc +++ b/deps/v8/src/platform-linux.cc @@ -865,7 +865,8 @@ Sampler::Sampler(int interval, bool profiling) : interval_(interval), profiling_(profiling), synchronous_(profiling), - active_(false) { + active_(false), + samples_taken_(0) { data_ = new PlatformData(this); } diff --git a/deps/v8/src/platform-macos.cc b/deps/v8/src/platform-macos.cc index 5e0e78dbf8..c3f21dc514 100644 --- a/deps/v8/src/platform-macos.cc +++ b/deps/v8/src/platform-macos.cc @@ -634,7 +634,8 @@ Sampler::Sampler(int interval, bool profiling) : interval_(interval), profiling_(profiling), synchronous_(profiling), - active_(false) { + active_(false), + samples_taken_(0) { data_ = new PlatformData(this); } diff --git a/deps/v8/src/platform-openbsd.cc b/deps/v8/src/platform-openbsd.cc index e03059ad72..0751fc7eed 100644 --- a/deps/v8/src/platform-openbsd.cc +++ b/deps/v8/src/platform-openbsd.cc @@ -572,7 +572,11 @@ class Sampler::PlatformData : public Malloced { Sampler::Sampler(int interval, bool profiling) - : interval_(interval), profiling_(profiling), active_(false) { + : interval_(interval), + profiling_(profiling), + synchronous_(profiling), + active_(false), + samples_taken_(0) { data_ = new PlatformData(); } diff --git a/deps/v8/src/platform-solaris.cc b/deps/v8/src/platform-solaris.cc index fcd69deb42..ff5d83b660 100644 --- a/deps/v8/src/platform-solaris.cc +++ b/deps/v8/src/platform-solaris.cc @@ -605,7 +605,8 @@ Sampler::Sampler(int interval, bool profiling) : interval_(interval), profiling_(profiling), synchronous_(profiling), - active_(false) { + active_(false), + samples_taken_(0) { data_ = new PlatformData(); } diff --git a/deps/v8/src/platform-win32.cc b/deps/v8/src/platform-win32.cc index a0ba5e865c..c50424e57a 100644 --- a/deps/v8/src/platform-win32.cc +++ b/deps/v8/src/platform-win32.cc @@ -1903,7 +1903,8 @@ Sampler::Sampler(int interval, bool profiling) : interval_(interval), profiling_(profiling), synchronous_(profiling), - active_(false) { + active_(false), + samples_taken_(0) { data_ = new PlatformData(this); } diff --git a/deps/v8/src/platform.h b/deps/v8/src/platform.h index c4ef23013e..6c7294c242 100644 --- a/deps/v8/src/platform.h +++ b/deps/v8/src/platform.h @@ -559,11 +559,14 @@ class TickSample { class Sampler { public: // Initialize sampler. - explicit Sampler(int interval, bool profiling); + Sampler(int interval, bool profiling); virtual ~Sampler(); // Performs stack sampling. - virtual void SampleStack(TickSample* sample) = 0; + void SampleStack(TickSample* sample) { + DoSampleStack(sample); + IncSamplesTaken(); + } // This method is called for each sampling period with the current // program counter. @@ -585,14 +588,24 @@ class Sampler { // Whether the sampler is running (that is, consumes resources). bool IsActive() const { return active_; } + // Used in tests to make sure that stack sampling is performed. + int samples_taken() const { return samples_taken_; } + void ResetSamplesTaken() { samples_taken_ = 0; } + class PlatformData; + protected: + virtual void DoSampleStack(TickSample* sample) = 0; + private: + void IncSamplesTaken() { if (++samples_taken_ < 0) samples_taken_ = 0; } + const int interval_; const bool profiling_; const bool synchronous_; bool active_; PlatformData* data_; // Platform specific data. + int samples_taken_; // Counts stack samples taken. DISALLOW_IMPLICIT_CONSTRUCTORS(Sampler); }; diff --git a/deps/v8/src/preparse-data.cc b/deps/v8/src/preparse-data.cc new file mode 100644 index 0000000000..9a3677183e --- /dev/null +++ b/deps/v8/src/preparse-data.cc @@ -0,0 +1,180 @@ +// Copyright 2010 the V8 project authors. All rights reserved. +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following +// disclaimer in the documentation and/or other materials provided +// with the distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +#include "../include/v8stdint.h" +#include "globals.h" +#include "checks.h" +#include "allocation.h" +#include "utils.h" +#include "list-inl.h" +#include "hashmap.h" +#include "preparse-data.h" + +namespace v8 { +namespace internal { + +// ---------------------------------------------------------------------------- +// FunctionLoggingParserRecorder + +FunctionLoggingParserRecorder::FunctionLoggingParserRecorder() + : function_store_(0), + is_recording_(true), + pause_count_(0) { + preamble_[PreparseDataConstants::kMagicOffset] = + PreparseDataConstants::kMagicNumber; + preamble_[PreparseDataConstants::kVersionOffset] = + PreparseDataConstants::kCurrentVersion; + preamble_[PreparseDataConstants::kHasErrorOffset] = false; + preamble_[PreparseDataConstants::kFunctionsSizeOffset] = 0; + preamble_[PreparseDataConstants::kSymbolCountOffset] = 0; + preamble_[PreparseDataConstants::kSizeOffset] = 0; + ASSERT_EQ(6, PreparseDataConstants::kHeaderSize); +#ifdef DEBUG + prev_start_ = -1; +#endif +} + + +void FunctionLoggingParserRecorder::LogMessage(int start_pos, + int end_pos, + const char* message, + const char* arg_opt) { + if (has_error()) return; + preamble_[PreparseDataConstants::kHasErrorOffset] = true; + function_store_.Reset(); + STATIC_ASSERT(PreparseDataConstants::kMessageStartPos == 0); + function_store_.Add(start_pos); + STATIC_ASSERT(PreparseDataConstants::kMessageEndPos == 1); + function_store_.Add(end_pos); + STATIC_ASSERT(PreparseDataConstants::kMessageArgCountPos == 2); + function_store_.Add((arg_opt == NULL) ? 0 : 1); + STATIC_ASSERT(PreparseDataConstants::kMessageTextPos == 3); + WriteString(CStrVector(message)); + if (arg_opt) WriteString(CStrVector(arg_opt)); + is_recording_ = false; +} + + +void FunctionLoggingParserRecorder::WriteString(Vector str) { + function_store_.Add(str.length()); + for (int i = 0; i < str.length(); i++) { + function_store_.Add(str[i]); + } +} + +// ---------------------------------------------------------------------------- +// PartialParserRecorder - Record both function entries and symbols. + +Vector PartialParserRecorder::ExtractData() { + int function_size = function_store_.size(); + int total_size = PreparseDataConstants::kHeaderSize + function_size; + Vector data = Vector::New(total_size); + preamble_[PreparseDataConstants::kFunctionsSizeOffset] = function_size; + preamble_[PreparseDataConstants::kSymbolCountOffset] = 0; + memcpy(data.start(), preamble_, sizeof(preamble_)); + int symbol_start = PreparseDataConstants::kHeaderSize + function_size; + if (function_size > 0) { + function_store_.WriteTo(data.SubVector(PreparseDataConstants::kHeaderSize, + symbol_start)); + } + return data; +} + + +// ---------------------------------------------------------------------------- +// CompleteParserRecorder - Record both function entries and symbols. + +CompleteParserRecorder::CompleteParserRecorder() + : FunctionLoggingParserRecorder(), + symbol_store_(0), + symbol_entries_(0), + symbol_table_(vector_compare), + symbol_id_(0) { +} + + +void CompleteParserRecorder::LogSymbol( + int start, const char* literal_chars, int length) { + if (!is_recording_) return; + + Vector literal(literal_chars, length); + int hash = vector_hash(literal); + HashMap::Entry* entry = symbol_table_.Lookup(&literal, hash, true); + int id = static_cast(reinterpret_cast(entry->value)); + if (id == 0) { + // Put (symbol_id_ + 1) into entry and increment it. + id = ++symbol_id_; + entry->value = reinterpret_cast(id); + Vector > symbol = symbol_entries_.AddBlock(1, literal); + entry->key = &symbol[0]; + } + WriteNumber(id - 1); +} + + +Vector 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, PreparseDataConstants::kNumberTerminator); + symbol_size += padding; + int total_size = PreparseDataConstants::kHeaderSize + function_size + + (symbol_size / sizeof(unsigned)); + Vector data = Vector::New(total_size); + preamble_[PreparseDataConstants::kFunctionsSizeOffset] = function_size; + preamble_[PreparseDataConstants::kSymbolCountOffset] = symbol_id_; + memcpy(data.start(), preamble_, sizeof(preamble_)); + int symbol_start = PreparseDataConstants::kHeaderSize + function_size; + if (function_size > 0) { + function_store_.WriteTo(data.SubVector(PreparseDataConstants::kHeaderSize, + symbol_start)); + } + if (!has_error()) { + symbol_store_.WriteTo( + Vector::cast(data.SubVector(symbol_start, total_size))); + } + return data; +} + + +void CompleteParserRecorder::WriteNumber(int number) { + ASSERT(number >= 0); + + int mask = (1 << 28) - 1; + for (int i = 28; i > 0; i -= 7) { + if (number > mask) { + symbol_store_.Add(static_cast(number >> i) | 0x80u); + number &= mask; + } + mask >>= 7; + } + symbol_store_.Add(static_cast(number)); +} + + +} } // namespace v8::internal. diff --git a/deps/v8/src/preparse-data.h b/deps/v8/src/preparse-data.h new file mode 100644 index 0000000000..a96e50fa10 --- /dev/null +++ b/deps/v8/src/preparse-data.h @@ -0,0 +1,223 @@ +// 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_DATA_H_ +#define V8_PREPARSER_DATA_H_ + +#include "hashmap.h" + +namespace v8 { +namespace internal { + +// Generic and general data used by preparse data recorders and readers. + +class PreparseDataConstants : public AllStatic { + public: + // Layout and constants of the preparse data exchange format. + static const unsigned kMagicNumber = 0xBadDead; + static const unsigned kCurrentVersion = 5; + + static const int kMagicOffset = 0; + static const int kVersionOffset = 1; + static const int kHasErrorOffset = 2; + static const int kFunctionsSizeOffset = 3; + static const int kSymbolCountOffset = 4; + static const int kSizeOffset = 5; + static const int kHeaderSize = 6; + + // If encoding a message, the following positions are fixed. + static const int kMessageStartPos = 0; + static const int kMessageEndPos = 1; + static const int kMessageArgCountPos = 2; + static const int kMessageTextPos = 3; + + static const byte kNumberTerminator = 0x80u; +}; + + +// ---------------------------------------------------------------------------- +// ParserRecorder - Logging of preparser data. + +// Abstract interface for preparse data recorder. +class ParserRecorder { + public: + ParserRecorder() { } + virtual ~ParserRecorder() { } + + // Logs the scope and some details of a function literal in the source. + virtual void LogFunction(int start, + int end, + int literals, + int properties) = 0; + + // Logs a symbol creation of a literal or identifier. + virtual void LogSymbol(int start, const char* symbol, int length) = 0; + + // 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. + virtual void LogMessage(int start, + int end, + const char* message, + const char* argument_opt) = 0; + + virtual int function_position() = 0; + + virtual int symbol_position() = 0; + + virtual int symbol_ids() = 0; + + virtual Vector ExtractData() = 0; + + virtual void PauseRecording() = 0; + + virtual void ResumeRecording() = 0; +}; + + +// ---------------------------------------------------------------------------- +// FunctionLoggingParserRecorder - Record only function entries + +class FunctionLoggingParserRecorder : public ParserRecorder { + public: + FunctionLoggingParserRecorder(); + virtual ~FunctionLoggingParserRecorder() {} + + virtual 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); + } + + // 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. + virtual void LogMessage(int start, + int end, + const char* message, + const char* argument_opt); + + virtual int function_position() { return function_store_.size(); } + + + virtual Vector ExtractData() = 0; + + virtual void PauseRecording() { + pause_count_++; + is_recording_ = false; + } + + virtual void ResumeRecording() { + ASSERT(pause_count_ > 0); + if (--pause_count_ == 0) is_recording_ = !has_error(); + } + + protected: + bool has_error() { + return static_cast(preamble_[PreparseDataConstants::kHasErrorOffset]); + } + + bool is_recording() { + return is_recording_; + } + + void WriteString(Vector str); + + Collector function_store_; + unsigned preamble_[PreparseDataConstants::kHeaderSize]; + bool is_recording_; + int pause_count_; + +#ifdef DEBUG + int prev_start_; +#endif +}; + + +// ---------------------------------------------------------------------------- +// PartialParserRecorder - Record only function entries + +class PartialParserRecorder : public FunctionLoggingParserRecorder { + public: + PartialParserRecorder() : FunctionLoggingParserRecorder() { } + virtual void LogSymbol(int start, const char* symbol, int length) { } + virtual ~PartialParserRecorder() { } + virtual Vector ExtractData(); + virtual int symbol_position() { return 0; } + virtual int symbol_ids() { return 0; } +}; + + +// ---------------------------------------------------------------------------- +// CompleteParserRecorder - Record both function entries and symbols. + +class CompleteParserRecorder: public FunctionLoggingParserRecorder { + public: + CompleteParserRecorder(); + virtual ~CompleteParserRecorder() { } + + virtual void LogSymbol(int start, const char* symbol, int length); + + virtual Vector ExtractData(); + + virtual int symbol_position() { return symbol_store_.size(); } + virtual int symbol_ids() { return symbol_id_; } + + private: + static int vector_hash(Vector 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* string1 = reinterpret_cast* >(a); + Vector* string2 = reinterpret_cast* >(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 symbol_store_; + Collector > symbol_entries_; + HashMap symbol_table_; + int symbol_id_; +}; + + +} } // namespace v8::internal. + +#endif // V8_PREPARSER_DATA_H_ diff --git a/deps/v8/src/preparser.cc b/deps/v8/src/preparser.cc new file mode 100644 index 0000000000..9061731237 --- /dev/null +++ b/deps/v8/src/preparser.cc @@ -0,0 +1,1184 @@ +// Copyright 2010 the V8 project authors. All rights reserved. +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following +// disclaimer in the documentation and/or other materials provided +// with the distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +#include "../include/v8stdint.h" +#include "unicode.h" +#include "globals.h" +#include "checks.h" +#include "allocation.h" +#include "utils.h" +#include "list.h" +#include "scanner-base.h" +#include "preparse-data.h" +#include "preparser.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; + +#define CHECK_OK ok); \ + if (!*ok) return -1; \ + ((void)0 +#define DUMMY ) // to make indentation work +#undef DUMMY + + +void PreParser::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; + } + i::JavaScriptScanner::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); + } +} + + +SourceElements PreParser::ParseSourceElements(int end_token, + bool* ok) { + // SourceElements :: + // (Statement)* + + while (peek() != end_token) { + ParseStatement(CHECK_OK); + } + return kUnknownSourceElements; +} + + +Statement PreParser::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); + } +} + + +Statement PreParser::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. +Statement PreParser::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; +} + + +Statement PreParser::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; +} + + +Statement PreParser::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. +Statement PreParser::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; +} + + +Statement PreParser::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; +} + + +Statement PreParser::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; +} + + +Statement PreParser::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; +} + + +Statement PreParser::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; +} + + +Statement PreParser::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; +} + + +Statement PreParser::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; +} + + +Statement PreParser::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; +} + + +Statement PreParser::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; +} + + +Statement PreParser::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; +} + + +Statement PreParser::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; +} + + +Statement PreParser::ParseThrowStatement(bool* ok) { + // ThrowStatement :: + // 'throw' [no line terminator] Expression ';' + + Expect(i::Token::THROW, CHECK_OK); + if (scanner_->has_line_terminator_before_next()) { + i::JavaScriptScanner::Location pos = scanner_->location(); + ReportMessageAt(pos.beg_pos, pos.end_pos, + "newline_after_throw", NULL); + *ok = false; + return kUnknownStatement; + } + ParseExpression(true, CHECK_OK); + ExpectSemicolon(CHECK_OK); + + return kUnknownStatement; +} + + +Statement PreParser::ParseTryStatement(bool* ok) { + // TryStatement :: + // 'try' Block Catch + // 'try' Block Finally + // 'try' Block Catch Finally + // + // Catch :: + // 'catch' '(' Identifier ')' Block + // + // Finally :: + // 'finally' Block + + // In preparsing, allow any number of catch/finally blocks, including zero + // of both. + + Expect(i::Token::TRY, CHECK_OK); + + ParseBlock(CHECK_OK); + + bool catch_or_finally_seen = false; + if (peek() == i::Token::CATCH) { + Consume(i::Token::CATCH); + Expect(i::Token::LPAREN, CHECK_OK); + ParseIdentifier(CHECK_OK); + Expect(i::Token::RPAREN, CHECK_OK); + scope_->EnterWith(); + ParseBlock(ok); + scope_->LeaveWith(); + if (!*ok) return kUnknownStatement; + catch_or_finally_seen = true; + } + if (peek() == i::Token::FINALLY) { + Consume(i::Token::FINALLY); + ParseBlock(CHECK_OK); + catch_or_finally_seen = true; + } + if (!catch_or_finally_seen) { + *ok = false; + } + return kUnknownStatement; +} + + +Statement PreParser::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 +Expression PreParser::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 +Expression PreParser::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 +Expression PreParser::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; +} + + +int PreParser::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 +Expression PreParser::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; +} + + +Expression PreParser::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); + } +} + + +Expression PreParser::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; +} + + +Expression PreParser::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; + } + } +} + + +Expression PreParser::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); +} + + +Expression PreParser::ParseMemberExpression(bool* ok) { + return ParseMemberWithNewPrefixesExpression(0, ok); +} + + +Expression PreParser::ParseMemberWithNewPrefixesExpression( + unsigned new_count, bool* ok) { + // MemberExpression :: + // (PrimaryExpression | FunctionLiteral) + // ('[' Expression ']' | '.' Identifier | Arguments)* + + // Parse the initial primary or function expression. + Expression result = kUnknownExpression; + 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; + } + } +} + + +Expression PreParser::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; +} + + +Expression PreParser::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; +} + + +Expression PreParser::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; +} + + +Expression PreParser::ParseRegExpLiteral(bool seen_equal, + bool* ok) { + if (!scanner_->ScanRegExpPattern(seen_equal)) { + Next(); + i::JavaScriptScanner::Location location = scanner_->location(); + ReportMessageAt(location.beg_pos, location.end_pos, + "unterminated_regexp", NULL); + *ok = false; + return kUnknownExpression; + } + + scope_->NextMaterializedLiteralIndex(); + + if (!scanner_->ScanRegExpFlags()) { + Next(); + i::JavaScriptScanner::Location location = scanner_->location(); + ReportMessageAt(location.beg_pos, location.end_pos, + "invalid_regexp_flags", NULL); + *ok = false; + return kUnknownExpression; + } + Next(); + return kUnknownExpression; +} + + +Arguments PreParser::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; +} + + +Expression PreParser::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; +} + + +Expression PreParser::ParseV8Intrinsic(bool* ok) { + // CallRuntime :: + // '%' Identifier Arguments + + Expect(i::Token::MOD, CHECK_OK); + ParseIdentifier(CHECK_OK); + ParseArguments(CHECK_OK); + + return kUnknownExpression; +} + + +void PreParser::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); +} + + +Identifier PreParser::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; +} + + +Expression PreParser::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; +} + + +Identifier PreParser::ParseIdentifier(bool* ok) { + Expect(i::Token::IDENTIFIER, ok); + if (!*ok) return kUnknownIdentifier; + return GetIdentifierSymbol(); +} + + +Identifier PreParser::ParseIdentifierName(bool* ok) { + i::Token::Value next = Next(); + if (i::Token::IsKeyword(next)) { + int pos = scanner_->location().beg_pos; + const char* keyword = i::Token::String(next); + log_->LogSymbol(pos, keyword, i::StrLength(keyword)); + return kUnknownExpression; + } + if (next == i::Token::IDENTIFIER) { + return GetIdentifierSymbol(); + } + *ok = false; + return kUnknownIdentifier; +} + + +// This function reads an identifier and determines whether or not it +// is 'get' or 'set'. The reason for not using ParseIdentifier and +// checking on the output is that this involves heap allocation which +// we can't do during preparsing. +Identifier PreParser::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 diff --git a/deps/v8/src/preparser.h b/deps/v8/src/preparser.h index 8e8dfbe052..b783d65db3 100644 --- a/deps/v8/src/preparser.h +++ b/deps/v8/src/preparser.h @@ -28,15 +28,12 @@ #ifndef V8_PREPARSER_H #define V8_PREPARSER_H -#include "unicode.h" -#include "utils.h" - namespace v8 { namespace preparser { // Preparsing checks a JavaScript program and emits preparse-data that helps // a later parsing to be faster. -// See preparser-data.h for the data. +// See preparse-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. @@ -76,7 +73,6 @@ typedef int Identifier; typedef int Arguments; -template class PreParser { public: PreParser() : scope_(NULL), allow_lazy_(true) { } @@ -86,8 +82,8 @@ class PreParser { // 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 PreParseProgram(i::JavaScriptScanner* scanner, + i::ParserRecorder* log, bool allow_lazy) { allow_lazy_ = allow_lazy; scanner_ = scanner; @@ -235,1185 +231,11 @@ class PreParser { static int Precedence(i::Token::Value tok, bool accept_IN); - Scanner* scanner_; - PreParserLog* log_; + i::JavaScriptScanner* scanner_; + i::ParserRecorder* 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 -void PreParser::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 -SourceElements PreParser::ParseSourceElements(int end_token, - bool* ok) { - // SourceElements :: - // (Statement)* - - while (peek() != end_token) { - ParseStatement(CHECK_OK); - } - return kUnknownSourceElements; -} - - -template -Statement PreParser::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 -Statement PreParser::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 -Statement PreParser::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 -Statement PreParser::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 -Statement PreParser::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 -Statement PreParser::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 -Statement PreParser::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 -Statement PreParser::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 -Statement PreParser::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 -Statement PreParser::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 -Statement PreParser::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 -Statement PreParser::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 -Statement PreParser::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 -Statement PreParser::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 -Statement PreParser::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 -Statement PreParser::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 -Statement PreParser::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 kUnknownStatement; - } - ParseExpression(true, CHECK_OK); - ExpectSemicolon(CHECK_OK); - - return kUnknownStatement; -} - - -template -Statement PreParser::ParseTryStatement(bool* ok) { - // TryStatement :: - // 'try' Block Catch - // 'try' Block Finally - // 'try' Block Catch Finally - // - // Catch :: - // 'catch' '(' Identifier ')' Block - // - // Finally :: - // 'finally' Block - - // In preparsing, allow any number of catch/finally blocks, including zero - // of both. - - Expect(i::Token::TRY, CHECK_OK); - - ParseBlock(CHECK_OK); - - bool catch_or_finally_seen = false; - if (peek() == i::Token::CATCH) { - Consume(i::Token::CATCH); - Expect(i::Token::LPAREN, CHECK_OK); - ParseIdentifier(CHECK_OK); - Expect(i::Token::RPAREN, CHECK_OK); - scope_->EnterWith(); - ParseBlock(ok); - scope_->LeaveWith(); - if (!*ok) return kUnknownStatement; - catch_or_finally_seen = true; - } - if (peek() == i::Token::FINALLY) { - Consume(i::Token::FINALLY); - ParseBlock(CHECK_OK); - catch_or_finally_seen = true; - } - if (!catch_or_finally_seen) { - *ok = false; - } - return kUnknownStatement; -} - - -template -Statement PreParser::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 -Expression PreParser::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 -Expression PreParser::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 -Expression PreParser::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 -int PreParser::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 -Expression PreParser::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 -Expression PreParser::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 -Expression PreParser::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 -Expression PreParser::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 -Expression PreParser::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 -Expression PreParser::ParseMemberExpression(bool* ok) { - return ParseMemberWithNewPrefixesExpression(0, ok); -} - - -template -Expression PreParser::ParseMemberWithNewPrefixesExpression( - unsigned new_count, bool* ok) { - // MemberExpression :: - // (PrimaryExpression | FunctionLiteral) - // ('[' Expression ']' | '.' Identifier | Arguments)* - - // Parse the initial primary or function expression. - Expression result = kUnknownExpression; - 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 -Expression PreParser::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 -Expression PreParser::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 -Expression PreParser::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 -Expression PreParser::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 -Arguments PreParser::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 -Expression PreParser::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 -Expression PreParser::ParseV8Intrinsic(bool* ok) { - // CallRuntime :: - // '%' Identifier Arguments - - Expect(i::Token::MOD, CHECK_OK); - ParseIdentifier(CHECK_OK); - ParseArguments(CHECK_OK); - - return kUnknownExpression; -} - - -template -void PreParser::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 -Identifier PreParser::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 -Expression PreParser::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 -Identifier PreParser::ParseIdentifier(bool* ok) { - Expect(i::Token::IDENTIFIER, ok); - if (!*ok) return kUnknownIdentifier; - return GetIdentifierSymbol(); -} - - -template -Identifier PreParser::ParseIdentifierName(bool* ok) { - i::Token::Value next = Next(); - if (i::Token::IsKeyword(next)) { - int pos = scanner_->location().beg_pos; - const char* keyword = i::Token::String(next); - log_->LogSymbol(pos, keyword, i::StrLength(keyword)); - return kUnknownExpression; - } - if (next == i::Token::IDENTIFIER) { - return GetIdentifierSymbol(); - } - *ok = false; - return kUnknownIdentifier; -} - - -// This function reads an identifier and determines whether or not it -// is 'get' or 'set'. The reason for not using ParseIdentifier and -// checking on the output is that this involves heap allocation which -// we can't do during preparsing. -template -Identifier PreParser::ParseIdentifierOrGetOrSet(bool* is_get, - bool* is_set, - bool* ok) { - Expect(i::Token::IDENTIFIER, CHECK_OK); - if (scanner_->literal_length() == 3) { - const char* token = scanner_->literal_string(); - *is_get = strncmp(token, "get", 3) == 0; - *is_set = !*is_get && strncmp(token, "set", 3) == 0; - } - return GetIdentifierSymbol(); -} - -#undef CHECK_OK } } // v8::preparser #endif // V8_PREPARSER_H diff --git a/deps/v8/src/profile-generator.cc b/deps/v8/src/profile-generator.cc index e0b63f950c..640f13cd62 100644 --- a/deps/v8/src/profile-generator.cc +++ b/deps/v8/src/profile-generator.cc @@ -1295,8 +1295,8 @@ HeapEntry* HeapSnapshot::AddEntry(HeapObject* object, } else if (object->IsJSObject()) { return AddEntry(object, HeapEntry::kObject, - collection_->GetName( - JSObject::cast(object)->constructor_name()), + collection_->GetName(GetConstructorNameForHeapProfile( + JSObject::cast(object))), children_count, retainers_count); } else if (object->IsString()) { @@ -1462,6 +1462,14 @@ void HeapSnapshot::BuildDominatorTree(const Vector& entries, } +void HeapSnapshot::SetDominatorsToSelf() { + for (int i = 0; i < entries_.length(); ++i) { + HeapEntry* entry = entries_[i]; + if (entry->dominator() == NULL) entry->set_dominator(entry); + } +} + + void HeapSnapshot::SetEntriesDominators() { // This array is used for maintaining reverse postorder of nodes. ScopedVector ordered_entries(entries_.length()); @@ -1473,10 +1481,7 @@ void HeapSnapshot::SetEntriesDominators() { ordered_entries[i]->set_dominator(dominators[i]); } // For nodes unreachable from root, set dominator to itself. - for (int i = 0; i < entries_.length(); ++i) { - HeapEntry* entry = entries_[i]; - if (entry->dominator() == NULL) entry->set_dominator(entry); - } + SetDominatorsToSelf(); } @@ -2764,6 +2769,12 @@ void HeapSnapshotJSONSerializer::SortHashMap( sorted_entries->Sort(SortUsingEntryValue); } + +String* GetConstructorNameForHeapProfile(JSObject* object) { + if (object->IsJSFunction()) return Heap::closure_symbol(); + return object->constructor_name(); +} + } } // namespace v8::internal #endif // ENABLE_LOGGING_AND_PROFILING diff --git a/deps/v8/src/profile-generator.h b/deps/v8/src/profile-generator.h index 30d70a2c74..55c57fd528 100644 --- a/deps/v8/src/profile-generator.h +++ b/deps/v8/src/profile-generator.h @@ -700,6 +700,7 @@ class HeapSnapshot { List* GetSortedEntriesList(); template void IterateEntries(Visitor* visitor) { entries_.Iterate(visitor); } + void SetDominatorsToSelf(); void Print(int max_depth); void PrintEntriesSize(); @@ -1072,6 +1073,9 @@ class HeapSnapshotJSONSerializer { DISALLOW_COPY_AND_ASSIGN(HeapSnapshotJSONSerializer); }; + +String* GetConstructorNameForHeapProfile(JSObject* object); + } } // namespace v8::internal #endif // ENABLE_LOGGING_AND_PROFILING diff --git a/deps/v8/src/runtime.cc b/deps/v8/src/runtime.cc index e20c94fddc..c43a1ab327 100644 --- a/deps/v8/src/runtime.cc +++ b/deps/v8/src/runtime.cc @@ -5020,11 +5020,12 @@ static int CopyCachedAsciiCharsToArray(const char* chars, // For example, "foo" => ["f", "o", "o"]. static MaybeObject* Runtime_StringToArray(Arguments args) { HandleScope scope; - ASSERT(args.length() == 1); + ASSERT(args.length() == 2); CONVERT_ARG_CHECKED(String, s, 0); + CONVERT_NUMBER_CHECKED(uint32_t, limit, Uint32, args[1]); s->TryFlatten(); - const int length = s->length(); + const int length = static_cast(Min(s->length(), limit)); Handle elements; if (s->IsFlat() && s->IsAsciiRepresentation()) { @@ -6391,7 +6392,7 @@ static void TrySettingInlineConstructStub(Handle function) { } if (function->shared()->CanGenerateInlineConstructor(*prototype)) { ConstructStubCompiler compiler; - MaybeObject* code = compiler.CompileConstructStub(function->shared()); + MaybeObject* code = compiler.CompileConstructStub(*function); if (!code->IsFailure()) { function->shared()->set_construct_stub( Code::cast(code->ToObjectUnchecked())); @@ -6459,7 +6460,6 @@ static MaybeObject* Runtime_NewObject(Arguments args) { // track one initial_map at a time, so we force the completion before the // function is called as a constructor for the first time. shared->CompleteInobjectSlackTracking(); - TrySettingInlineConstructStub(function); } bool first_allocation = !shared->live_objects_may_exist(); diff --git a/deps/v8/src/runtime.h b/deps/v8/src/runtime.h index e36988da44..f9ebbc42ef 100644 --- a/deps/v8/src/runtime.h +++ b/deps/v8/src/runtime.h @@ -175,7 +175,7 @@ namespace internal { F(StringReplaceRegExpWithString, 4, 1) \ F(StringMatch, 3, 1) \ F(StringTrim, 3, 1) \ - F(StringToArray, 1, 1) \ + F(StringToArray, 2, 1) \ F(NewStringWrapper, 1, 1) \ \ /* Numbers */ \ diff --git a/deps/v8/src/string.js b/deps/v8/src/string.js index d82ce05237..3b3c82bb72 100644 --- a/deps/v8/src/string.js +++ b/deps/v8/src/string.js @@ -552,7 +552,7 @@ function StringSplit(separator, limit) { var separator_length = separator.length; // If the separator string is empty then return the elements in the subject. - if (separator_length === 0) return %StringToArray(subject); + if (separator_length === 0) return %StringToArray(subject, limit); var result = %StringSplit(subject, separator, limit); diff --git a/deps/v8/src/stub-cache.h b/deps/v8/src/stub-cache.h index 4886c7eb29..cef5481c3f 100644 --- a/deps/v8/src/stub-cache.h +++ b/deps/v8/src/stub-cache.h @@ -740,7 +740,7 @@ class ConstructStubCompiler: public StubCompiler { public: explicit ConstructStubCompiler() {} - MUST_USE_RESULT MaybeObject* CompileConstructStub(SharedFunctionInfo* shared); + MUST_USE_RESULT MaybeObject* CompileConstructStub(JSFunction* function); private: MaybeObject* GetCode(); diff --git a/deps/v8/src/token.cc b/deps/v8/src/token.cc index 21fa9ee4d7..488e90979f 100644 --- a/deps/v8/src/token.cc +++ b/deps/v8/src/token.cc @@ -25,8 +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. -#include "v8.h" - +#include "../include/v8stdint.h" #include "token.h" namespace v8 { diff --git a/deps/v8/src/v8.h b/deps/v8/src/v8.h index 74e98f151d..a2313b0e3c 100644 --- a/deps/v8/src/v8.h +++ b/deps/v8/src/v8.h @@ -54,7 +54,7 @@ // Basic includes #include "../include/v8.h" #include "v8globals.h" -#include "checks.h" +#include "v8checks.h" #include "allocation.h" #include "v8utils.h" #include "flags.h" diff --git a/deps/v8/src/v8checks.h b/deps/v8/src/v8checks.h new file mode 100644 index 0000000000..9857f73d17 --- /dev/null +++ b/deps/v8/src/v8checks.h @@ -0,0 +1,64 @@ +// Copyright 2006-2008 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_V8CHECKS_H_ +#define V8_V8CHECKS_H_ + +#include "checks.h" + +void API_Fatal(const char* location, const char* format, ...); + +namespace v8 { + class Value; + template class Handle; + +namespace internal { + intptr_t HeapObjectTagMask(); + +} } // namespace v8::internal + + +void CheckNonEqualsHelper(const char* file, + int line, + const char* unexpected_source, + v8::Handle unexpected, + const char* value_source, + v8::Handle value); + +void CheckEqualsHelper(const char* file, + int line, + const char* expected_source, + v8::Handle expected, + const char* value_source, + v8::Handle value); + +#define ASSERT_TAG_ALIGNED(address) \ + ASSERT((reinterpret_cast(address) & HeapObjectTagMask()) == 0) + +#define ASSERT_SIZE_TAG_ALIGNED(size) ASSERT((size & HeapObjectTagMask()) == 0) + +#endif // V8_V8CHECKS_H_ diff --git a/deps/v8/src/version.cc b/deps/v8/src/version.cc index 0e0a7cf42e..6e5b68f2fa 100644 --- a/deps/v8/src/version.cc +++ b/deps/v8/src/version.cc @@ -34,8 +34,8 @@ // cannot be changed without changing the SCons build script. #define MAJOR_VERSION 2 #define MINOR_VERSION 5 -#define BUILD_NUMBER 8 -#define PATCH_LEVEL 0 +#define BUILD_NUMBER 9 +#define PATCH_LEVEL 1 #define CANDIDATE_VERSION false // Define SONAME to have the SCons build the put a specific SONAME into the diff --git a/deps/v8/src/x64/macro-assembler-x64.cc b/deps/v8/src/x64/macro-assembler-x64.cc index 834a6f6ef5..d9198338b2 100644 --- a/deps/v8/src/x64/macro-assembler-x64.cc +++ b/deps/v8/src/x64/macro-assembler-x64.cc @@ -1889,7 +1889,6 @@ void MacroAssembler::CheckAccessGlobalProxy(Register holder_reg, void MacroAssembler::LoadAllocationTopHelper(Register result, - Register result_end, Register scratch, AllocationFlags flags) { ExternalReference new_space_allocation_top = @@ -1911,7 +1910,6 @@ void MacroAssembler::LoadAllocationTopHelper(Register result, // Move address of new object to result. Use scratch register if available, // and keep address in scratch until call to UpdateAllocationTopHelper. if (scratch.is_valid()) { - ASSERT(!scratch.is(result_end)); movq(scratch, new_space_allocation_top); movq(result, Operand(scratch, 0)); } else if (result.is(rax)) { @@ -1972,7 +1970,7 @@ void MacroAssembler::AllocateInNewSpace(int object_size, ASSERT(!result.is(result_end)); // Load address of new object into result. - LoadAllocationTopHelper(result, result_end, scratch, flags); + LoadAllocationTopHelper(result, scratch, flags); // Calculate new top and bail out if new space is exhausted. ExternalReference new_space_allocation_limit = @@ -2029,7 +2027,7 @@ void MacroAssembler::AllocateInNewSpace(int header_size, ASSERT(!result.is(result_end)); // Load address of new object into result. - LoadAllocationTopHelper(result, result_end, scratch, flags); + LoadAllocationTopHelper(result, scratch, flags); // Calculate new top and bail out if new space is exhausted. ExternalReference new_space_allocation_limit = @@ -2071,7 +2069,7 @@ void MacroAssembler::AllocateInNewSpace(Register object_size, ASSERT(!result.is(result_end)); // Load address of new object into result. - LoadAllocationTopHelper(result, result_end, scratch, flags); + LoadAllocationTopHelper(result, scratch, flags); // Calculate new top and bail out if new space is exhausted. ExternalReference new_space_allocation_limit = diff --git a/deps/v8/src/x64/macro-assembler-x64.h b/deps/v8/src/x64/macro-assembler-x64.h index 5b082fd627..0b7e6018f5 100644 --- a/deps/v8/src/x64/macro-assembler-x64.h +++ b/deps/v8/src/x64/macro-assembler-x64.h @@ -950,12 +950,9 @@ class MacroAssembler: public Assembler { // Allocation support helpers. // Loads the top of new-space into the result register. - // If flags contains RESULT_CONTAINS_TOP then result_end is valid and - // already contains the top of new-space, and scratch is invalid. // Otherwise the address of the new-space top is loaded into scratch (if // scratch is valid), and the new-space top is loaded into result. void LoadAllocationTopHelper(Register result, - Register result_end, Register scratch, AllocationFlags flags); // Update allocation top with value in result_end register. diff --git a/deps/v8/src/x64/stub-cache-x64.cc b/deps/v8/src/x64/stub-cache-x64.cc index dbf93f5ff2..7ba482c865 100644 --- a/deps/v8/src/x64/stub-cache-x64.cc +++ b/deps/v8/src/x64/stub-cache-x64.cc @@ -2890,8 +2890,7 @@ void StubCompiler::GenerateLoadConstant(JSObject* object, // Specialized stub for constructing objects from functions which only have only // simple assignments of the form this.x = ...; in their body. -MaybeObject* ConstructStubCompiler::CompileConstructStub( - SharedFunctionInfo* shared) { +MaybeObject* ConstructStubCompiler::CompileConstructStub(JSFunction* function) { // ----------- S t a t e ------------- // -- rax : argc // -- rdi : constructor @@ -2964,6 +2963,7 @@ MaybeObject* ConstructStubCompiler::CompileConstructStub( // r9: first in-object property of the JSObject // Fill the initialized properties with a constant value or a passed argument // depending on the this.x = ...; assignment in the function. + SharedFunctionInfo* shared = function->shared(); for (int i = 0; i < shared->this_property_assignments_count(); i++) { if (shared->IsThisPropertyAssignmentArgument(i)) { // Check if the argument assigned to the property is actually passed. @@ -2983,8 +2983,9 @@ MaybeObject* ConstructStubCompiler::CompileConstructStub( } // Fill the unused in-object property fields with undefined. + ASSERT(function->has_initial_map()); for (int i = shared->this_property_assignments_count(); - i < shared->CalculateInObjectProperties(); + i < function->initial_map()->inobject_properties(); i++) { __ movq(Operand(r9, i * kPointerSize), r8); } diff --git a/deps/v8/test/cctest/test-api.cc b/deps/v8/test/cctest/test-api.cc index 5322314417..8ce7a79a9b 100644 --- a/deps/v8/test/cctest/test-api.cc +++ b/deps/v8/test/cctest/test-api.cc @@ -7819,6 +7819,31 @@ THREADED_TEST(ObjectProtoToString) { } +THREADED_TEST(ObjectGetConstructorName) { + v8::HandleScope scope; + LocalContext context; + v8_compile("function Parent() {};" + "function Child() {};" + "Child.prototype = new Parent();" + "var outer = { inner: function() { } };" + "var p = new Parent();" + "var c = new Child();" + "var x = new outer.inner();")->Run(); + + Local p = context->Global()->Get(v8_str("p")); + CHECK(p->IsObject() && p->ToObject()->GetConstructorName()->Equals( + v8_str("Parent"))); + + Local c = context->Global()->Get(v8_str("c")); + CHECK(c->IsObject() && c->ToObject()->GetConstructorName()->Equals( + v8_str("Child"))); + + Local x = context->Global()->Get(v8_str("x")); + CHECK(x->IsObject() && x->ToObject()->GetConstructorName()->Equals( + v8_str("outer.inner"))); +} + + bool ApiTestFuzzer::fuzzing_ = false; i::Semaphore* ApiTestFuzzer::all_tests_done_= i::OS::CreateSemaphore(0); @@ -8734,7 +8759,7 @@ TEST(PreCompileInvalidPreparseDataError) { v8::ScriptData::PreCompile(script, i::StrLength(script)); CHECK(!sd->HasError()); // ScriptDataImpl private implementation details - const int kHeaderSize = i::ScriptDataImpl::kHeaderSize; + const int kHeaderSize = i::PreparseDataConstants::kHeaderSize; const int kFunctionEntrySize = i::FunctionEntry::kSize; const int kFunctionEntryStartOffset = 0; const int kFunctionEntryEndOffset = 1; diff --git a/deps/v8/test/cctest/test-conversions.cc b/deps/v8/test/cctest/test-conversions.cc index eef7184828..1b5cc2dee8 100644 --- a/deps/v8/test/cctest/test-conversions.cc +++ b/deps/v8/test/cctest/test-conversions.cc @@ -104,8 +104,10 @@ TEST(IntegerStrLiteral) { CHECK_EQ(0.0, StringToDouble("000", NO_FLAGS)); CHECK_EQ(1.0, StringToDouble("1", NO_FLAGS)); CHECK_EQ(-1.0, StringToDouble("-1", NO_FLAGS)); - CHECK_EQ(-1.0, StringToDouble(" - 1 ", NO_FLAGS)); - CHECK_EQ(1.0, StringToDouble(" + 1 ", NO_FLAGS)); + CHECK_EQ(-1.0, StringToDouble(" -1 ", NO_FLAGS)); + CHECK_EQ(1.0, StringToDouble(" +1 ", NO_FLAGS)); + CHECK(isnan(StringToDouble(" - 1 ", NO_FLAGS))); + CHECK(isnan(StringToDouble(" + 1 ", NO_FLAGS))); CHECK_EQ(0.0, StringToDouble("0e0", ALLOW_HEX | ALLOW_OCTALS)); CHECK_EQ(0.0, StringToDouble("0e1", ALLOW_HEX | ALLOW_OCTALS)); diff --git a/deps/v8/test/cctest/test-heap-profiler.cc b/deps/v8/test/cctest/test-heap-profiler.cc index 4dd7fe823f..95314d74a4 100644 --- a/deps/v8/test/cctest/test-heap-profiler.cc +++ b/deps/v8/test/cctest/test-heap-profiler.cc @@ -1178,4 +1178,19 @@ TEST(HeapSnapshotJSONSerializationAborting) { CHECK_EQ(0, stream.eos_signaled()); } + +// Must not crash in debug mode. +TEST(AggregatedHeapSnapshotJSONSerialization) { + v8::HandleScope scope; + LocalContext env; + + const v8::HeapSnapshot* snapshot = + v8::HeapProfiler::TakeSnapshot( + v8::String::New("agg"), v8::HeapSnapshot::kAggregated); + TestJSONStream stream; + snapshot->Serialize(&stream, v8::HeapSnapshot::kJSON); + CHECK_GT(stream.size(), 0); + CHECK_EQ(1, stream.eos_signaled()); +} + #endif // ENABLE_LOGGING_AND_PROFILING diff --git a/deps/v8/test/cctest/test-log.cc b/deps/v8/test/cctest/test-log.cc index 7168737416..710c10e996 100644 --- a/deps/v8/test/cctest/test-log.cc +++ b/deps/v8/test/cctest/test-log.cc @@ -139,6 +139,12 @@ namespace internal { class LoggerTestHelper : public AllStatic { public: static bool IsSamplerActive() { return Logger::IsProfilerSamplerActive(); } + static void ResetSamplesTaken() { + reinterpret_cast(Logger::ticker_)->ResetSamplesTaken(); + } + static bool has_samples_taken() { + return reinterpret_cast(Logger::ticker_)->samples_taken() > 0; + } }; } // namespace v8::internal @@ -147,24 +153,6 @@ class LoggerTestHelper : public AllStatic { using v8::internal::LoggerTestHelper; -// Under Linux, we need to check if signals were delivered to avoid false -// positives. Under other platforms profiling is done via a high-priority -// thread, so this case never happen. -static bool was_sigprof_received = true; -#ifdef __linux__ - -struct sigaction old_sigprof_handler; -pthread_t our_thread; - -static void SigProfSignalHandler(int signal, siginfo_t* info, void* context) { - if (signal != SIGPROF || !pthread_equal(pthread_self(), our_thread)) return; - was_sigprof_received = true; - old_sigprof_handler.sa_sigaction(signal, info, context); -} - -#endif // __linux__ - - namespace { class ScopedLoggerInitializer { @@ -258,6 +246,9 @@ class LogBufferMatcher { static void CheckThatProfilerWorks(LogBufferMatcher* matcher) { + CHECK(!LoggerTestHelper::IsSamplerActive()); + LoggerTestHelper::ResetSamplesTaken(); + Logger::ResumeProfiler(v8::PROFILER_MODULE_CPU, 0); CHECK(LoggerTestHelper::IsSamplerActive()); @@ -266,19 +257,6 @@ static void CheckThatProfilerWorks(LogBufferMatcher* matcher) { const char* code_creation = "\ncode-creation,"; // eq. to /^code-creation,/ CHECK_NE(NULL, matcher->Find(code_creation)); -#ifdef __linux__ - // Intercept SIGPROF handler to make sure that the test process - // had received it. Under load, system can defer it causing test failure. - // It is important to execute this after 'ResumeProfiler'. - our_thread = pthread_self(); - was_sigprof_received = false; - struct sigaction sa; - sa.sa_sigaction = SigProfSignalHandler; - sigemptyset(&sa.sa_mask); - sa.sa_flags = SA_SIGINFO; - CHECK_EQ(0, sigaction(SIGPROF, &sa, &old_sigprof_handler)); -#endif // __linux__ - // Force compiler to generate new code by parametrizing source. EmbeddedVector script_src; i::OS::SNPrintF(script_src, @@ -306,7 +284,7 @@ static void CheckThatProfilerWorks(LogBufferMatcher* matcher) { CHECK_NE(NULL, matcher->Find(code_creation)); const char* tick = "\ntick,"; const bool ticks_found = matcher->Find(tick) != NULL; - CHECK_EQ(was_sigprof_received, ticks_found); + CHECK_EQ(LoggerTestHelper::has_samples_taken(), ticks_found); } diff --git a/deps/v8/test/cctest/test-parsing.cc b/deps/v8/test/cctest/test-parsing.cc index 243d47884a..a93fc2712d 100755 --- a/deps/v8/test/cctest/test-parsing.cc +++ b/deps/v8/test/cctest/test-parsing.cc @@ -263,8 +263,7 @@ TEST(StandAlonePreParser) { i::CompleteParserRecorder log; i::V8JavaScriptScanner scanner; scanner.Initialize(i::Handle::null(), &stream); - v8::preparser::PreParser preparser; + v8::preparser::PreParser preparser; bool result = preparser.PreParseProgram(&scanner, &log, true); CHECK(result); i::ScriptDataImpl data(log.ExtractData()); diff --git a/deps/v8/test/mjsunit/regress/regress-944.js b/deps/v8/test/mjsunit/regress/regress-944.js new file mode 100644 index 0000000000..d165336f1f --- /dev/null +++ b/deps/v8/test/mjsunit/regress/regress-944.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. + +// Check for parsing of proper ES5 15.9.1.15 (ISO 8601 / RFC 3339) time +// strings that contain millisecond values with exactly 3 digits (as is +// required by the spec format if the string has milliseconds at all). +assertEquals(1290722550521, Date.parse("2010-11-25T22:02:30.521Z")); + +// Check for parsing of extension/generalization of the ES5 15.9.1.15 spec +// format where millisecond values have only 1 or 2 digits. +assertEquals(1290722550500, Date.parse("2010-11-25T22:02:30.5Z")); +assertEquals(1290722550520, Date.parse("2010-11-25T22:02:30.52Z")); +assertFalse(Date.parse("2010-11-25T22:02:30.5Z") === Date.parse("2010-11-25T22:02:30.005Z")); + +// Check that we truncate millisecond values having more than 3 digits. +assertEquals(Date.parse("2010-11-25T22:02:30.1005Z"), Date.parse("2010-11-25T22:02:30.100Z")); + +// Check that we accept lots of digits. +assertEquals(Date.parse("2010-11-25T22:02:30.999Z"), Date.parse("2010-11-25T22:02:30.99999999999999999999999999999999999999999999999999999999999999999999999999999999999999Z")); + +// Fail if there's a decimal point but zero digits for (expected) milliseconds. +assertTrue(isNaN(Date.parse("2010-11-25T22:02:30.Z"))); diff --git a/deps/v8/test/mjsunit/string-split.js b/deps/v8/test/mjsunit/string-split.js index c741f6a3e5..6fcf55799e 100644 --- a/deps/v8/test/mjsunit/string-split.js +++ b/deps/v8/test/mjsunit/string-split.js @@ -97,3 +97,22 @@ assertEquals([""], ''.split(/./)); assertEquals([], ''.split(/.?/)); assertEquals([], ''.split(/.??/)); assertEquals([], ''.split(/()()/)); + + +// Issue http://code.google.com/p/v8/issues/detail?id=929 +// (Splitting with empty separator and a limit.) + +function numberObj(num) { + return {valueOf: function() { return num; }}; +} + +assertEquals([], "abc".split("", 0)); +assertEquals([], "abc".split("", numberObj(0))); +assertEquals(["a"], "abc".split("", 1)); +assertEquals(["a"], "abc".split("", numberObj(1))); +assertEquals(["a", "b"], "abc".split("", 2)); +assertEquals(["a", "b"], "abc".split("", numberObj(2))); +assertEquals(["a", "b", "c"], "abc".split("", 3)); +assertEquals(["a", "b", "c"], "abc".split("", numberObj(3))); +assertEquals(["a", "b", "c"], "abc".split("", 4)); +assertEquals(["a", "b", "c"], "abc".split("", numberObj(4))); diff --git a/deps/v8/tools/gyp/v8.gyp b/deps/v8/tools/gyp/v8.gyp index 43e1bd4150..c1a5aab198 100644 --- a/deps/v8/tools/gyp/v8.gyp +++ b/deps/v8/tools/gyp/v8.gyp @@ -406,6 +406,10 @@ '../../src/parser.cc', '../../src/parser.h', '../../src/platform.h', + '../../src/preparse-data.cc', + '../../src/preparse-data.h', + '../../src/preparser.cc', + '../../src/preparser.h', '../../src/prettyprinter.cc', '../../src/prettyprinter.h', '../../src/property.cc', @@ -471,8 +475,11 @@ '../../src/v8-counters.h', '../../src/v8.cc', '../../src/v8.h', + '../../src/v8checks.h', + '../../src/v8globals.h', '../../src/v8threads.cc', '../../src/v8threads.h', + '../../src/v8utils.h', '../../src/variables.cc', '../../src/variables.h', '../../src/version.cc', diff --git a/deps/v8/tools/presubmit.py b/deps/v8/tools/presubmit.py index ebf8bd8939..1d80f92b8b 100755 --- a/deps/v8/tools/presubmit.py +++ b/deps/v8/tools/presubmit.py @@ -195,7 +195,7 @@ class CppLintProcessor(SourceFileProcessor): or (name in CppLintProcessor.IGNORE_LINT)) def GetPathsToSearch(self): - return ['src', 'include', 'samples', join('test', 'cctest')] + return ['src', 'preparser', 'include', 'samples', join('test', 'cctest')] def ProcessFiles(self, files, path): good_files_cache = FileContentsCache('.cpplint-cache') diff --git a/deps/v8/tools/visual_studio/v8_base.vcproj b/deps/v8/tools/visual_studio/v8_base.vcproj index 95eb196d31..902faff635 100644 --- a/deps/v8/tools/visual_studio/v8_base.vcproj +++ b/deps/v8/tools/visual_studio/v8_base.vcproj @@ -761,6 +761,22 @@ RelativePath="..\..\src\parser.h" > + + + + + + + + @@ -1033,6 +1049,14 @@ RelativePath="..\..\src\v8.h" > + + + + @@ -1041,6 +1065,10 @@ RelativePath="..\..\src\v8threads.h" > + + @@ -1157,6 +1185,10 @@ RelativePath="..\..\include\v8.h" > + +