// Copyright 2010 the V8 project authors. All rights reserved. // Redistribution and use in source and binary forms, with or without // modification, are permitted provided that the following conditions are // met: // // * Redistributions of source code must retain the above copyright // notice, this list of conditions and the following disclaimer. // * Redistributions in binary form must reproduce the above // copyright notice, this list of conditions and the following // disclaimer in the documentation and/or other materials provided // with the distribution. // * Neither the name of Google Inc. nor the names of its // contributors may be used to endorse or promote products derived // from this software without specific prior written permission. // // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR // A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT // OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT // LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. #include "v8.h" #include "api.h" #include "ast.h" #include "bootstrapper.h" #include "codegen.h" #include "compiler.h" #include "func-name-inferrer.h" #include "messages.h" #include "parser.h" #include "platform.h" #include "runtime.h" #include "scopeinfo.h" #include "scopes.h" #include "string-stream.h" #include "ast-inl.h" #include "jump-target-inl.h" namespace v8 { namespace internal { // PositionStack is used for on-stack allocation of token positions for // new expressions. Please look at ParseNewExpression. class PositionStack { public: explicit PositionStack(bool* ok) : top_(NULL), ok_(ok) {} ~PositionStack() { ASSERT(!*ok_ || is_empty()); } class Element { public: Element(PositionStack* stack, int value) { previous_ = stack->top(); value_ = value; stack->set_top(this); } private: Element* previous() { return previous_; } int value() { return value_; } friend class PositionStack; Element* previous_; int value_; }; bool is_empty() { return top_ == NULL; } int pop() { ASSERT(!is_empty()); int result = top_->value(); top_ = top_->previous(); return result; } private: Element* top() { return top_; } void set_top(Element* value) { top_ = value; } Element* top_; bool* ok_; }; template class BufferedZoneList { public: BufferedZoneList() : list_(NULL), last_(NULL) {} // Adds element at end of list. This element is buffered and can // be read using last() or removed using RemoveLast until a new Add or until // RemoveLast or GetList has been called. void Add(T* value) { if (last_ != NULL) { if (list_ == NULL) { list_ = new ZoneList(initial_size); } list_->Add(last_); } last_ = value; } T* last() { ASSERT(last_ != NULL); return last_; } T* RemoveLast() { ASSERT(last_ != NULL); T* result = last_; if (list_ != NULL && list_->length() > 0) last_ = list_->RemoveLast(); else last_ = NULL; return result; } T* Get(int i) { ASSERT(0 <= i && i < length()); if (list_ == NULL) { ASSERT_EQ(0, i); return last_; } else { if (i == list_->length()) { ASSERT(last_ != NULL); return last_; } else { return list_->at(i); } } } void Clear() { list_ = NULL; last_ = NULL; } int length() { int length = (list_ == NULL) ? 0 : list_->length(); return length + ((last_ == NULL) ? 0 : 1); } ZoneList* GetList() { if (list_ == NULL) { list_ = new ZoneList(initial_size); } if (last_ != NULL) { list_->Add(last_); last_ = NULL; } return list_; } private: ZoneList* list_; T* last_; }; // Accumulates RegExp atoms and assertions into lists of terms and alternatives. class RegExpBuilder: public ZoneObject { public: RegExpBuilder(); void AddCharacter(uc16 character); // "Adds" an empty expression. Does nothing except consume a // following quantifier void AddEmpty(); void AddAtom(RegExpTree* tree); void AddAssertion(RegExpTree* tree); void NewAlternative(); // '|' void AddQuantifierToAtom(int min, int max, RegExpQuantifier::Type type); RegExpTree* ToRegExp(); private: void FlushCharacters(); void FlushText(); void FlushTerms(); bool pending_empty_; ZoneList* characters_; BufferedZoneList terms_; BufferedZoneList text_; BufferedZoneList alternatives_; #ifdef DEBUG enum {ADD_NONE, ADD_CHAR, ADD_TERM, ADD_ASSERT, ADD_ATOM} last_added_; #define LAST(x) last_added_ = x; #else #define LAST(x) #endif }; RegExpBuilder::RegExpBuilder() : pending_empty_(false), characters_(NULL), terms_(), alternatives_() #ifdef DEBUG , last_added_(ADD_NONE) #endif {} void RegExpBuilder::FlushCharacters() { pending_empty_ = false; if (characters_ != NULL) { RegExpTree* atom = new RegExpAtom(characters_->ToConstVector()); characters_ = NULL; text_.Add(atom); LAST(ADD_ATOM); } } void RegExpBuilder::FlushText() { FlushCharacters(); int num_text = text_.length(); if (num_text == 0) { return; } else if (num_text == 1) { terms_.Add(text_.last()); } else { RegExpText* text = new RegExpText(); for (int i = 0; i < num_text; i++) text_.Get(i)->AppendToText(text); terms_.Add(text); } text_.Clear(); } void RegExpBuilder::AddCharacter(uc16 c) { pending_empty_ = false; if (characters_ == NULL) { characters_ = new ZoneList(4); } characters_->Add(c); LAST(ADD_CHAR); } void RegExpBuilder::AddEmpty() { pending_empty_ = true; } void RegExpBuilder::AddAtom(RegExpTree* term) { if (term->IsEmpty()) { AddEmpty(); return; } if (term->IsTextElement()) { FlushCharacters(); text_.Add(term); } else { FlushText(); terms_.Add(term); } LAST(ADD_ATOM); } void RegExpBuilder::AddAssertion(RegExpTree* assert) { FlushText(); terms_.Add(assert); LAST(ADD_ASSERT); } void RegExpBuilder::NewAlternative() { FlushTerms(); } void RegExpBuilder::FlushTerms() { FlushText(); int num_terms = terms_.length(); RegExpTree* alternative; if (num_terms == 0) { alternative = RegExpEmpty::GetInstance(); } else if (num_terms == 1) { alternative = terms_.last(); } else { alternative = new RegExpAlternative(terms_.GetList()); } alternatives_.Add(alternative); terms_.Clear(); LAST(ADD_NONE); } RegExpTree* RegExpBuilder::ToRegExp() { FlushTerms(); int num_alternatives = alternatives_.length(); if (num_alternatives == 0) { return RegExpEmpty::GetInstance(); } if (num_alternatives == 1) { return alternatives_.last(); } return new RegExpDisjunction(alternatives_.GetList()); } void RegExpBuilder::AddQuantifierToAtom(int min, int max, RegExpQuantifier::Type type) { if (pending_empty_) { pending_empty_ = false; return; } RegExpTree* atom; if (characters_ != NULL) { ASSERT(last_added_ == ADD_CHAR); // Last atom was character. Vector char_vector = characters_->ToConstVector(); int num_chars = char_vector.length(); if (num_chars > 1) { Vector prefix = char_vector.SubVector(0, num_chars - 1); text_.Add(new RegExpAtom(prefix)); char_vector = char_vector.SubVector(num_chars - 1, num_chars); } characters_ = NULL; atom = new RegExpAtom(char_vector); FlushText(); } else if (text_.length() > 0) { ASSERT(last_added_ == ADD_ATOM); atom = text_.RemoveLast(); FlushText(); } else if (terms_.length() > 0) { ASSERT(last_added_ == ADD_ATOM); atom = terms_.RemoveLast(); if (atom->max_match() == 0) { // Guaranteed to only match an empty string. LAST(ADD_TERM); if (min == 0) { return; } terms_.Add(atom); return; } } else { // Only call immediately after adding an atom or character! UNREACHABLE(); return; } terms_.Add(new RegExpQuantifier(min, max, type, atom)); LAST(ADD_TERM); } class RegExpParser { public: RegExpParser(FlatStringReader* in, Handle* error, bool multiline_mode); RegExpTree* ParsePattern(); RegExpTree* ParseDisjunction(); RegExpTree* ParseGroup(); RegExpTree* ParseCharacterClass(); // Parses a {...,...} quantifier and stores the range in the given // out parameters. bool ParseIntervalQuantifier(int* min_out, int* max_out); // Parses and returns a single escaped character. The character // must not be 'b' or 'B' since they are usually handle specially. uc32 ParseClassCharacterEscape(); // Checks whether the following is a length-digit hexadecimal number, // and sets the value if it is. bool ParseHexEscape(int length, uc32* value); uc32 ParseControlLetterEscape(); uc32 ParseOctalLiteral(); // Tries to parse the input as a back reference. If successful it // stores the result in the output parameter and returns true. If // it fails it will push back the characters read so the same characters // can be reparsed. bool ParseBackReferenceIndex(int* index_out); CharacterRange ParseClassAtom(uc16* char_class); RegExpTree* ReportError(Vector message); void Advance(); void Advance(int dist); void Reset(int pos); // Reports whether the pattern might be used as a literal search string. // Only use if the result of the parse is a single atom node. bool simple(); bool contains_anchor() { return contains_anchor_; } void set_contains_anchor() { contains_anchor_ = true; } int captures_started() { return captures_ == NULL ? 0 : captures_->length(); } int position() { return next_pos_ - 1; } bool failed() { return failed_; } static const int kMaxCaptures = 1 << 16; static const uc32 kEndMarker = (1 << 21); private: enum SubexpressionType { INITIAL, CAPTURE, // All positive values represent captures. POSITIVE_LOOKAHEAD, NEGATIVE_LOOKAHEAD, GROUPING }; class RegExpParserState : public ZoneObject { public: RegExpParserState(RegExpParserState* previous_state, SubexpressionType group_type, int disjunction_capture_index) : previous_state_(previous_state), builder_(new RegExpBuilder()), group_type_(group_type), disjunction_capture_index_(disjunction_capture_index) {} // Parser state of containing expression, if any. RegExpParserState* previous_state() { return previous_state_; } bool IsSubexpression() { return previous_state_ != NULL; } // RegExpBuilder building this regexp's AST. RegExpBuilder* builder() { return builder_; } // Type of regexp being parsed (parenthesized group or entire regexp). SubexpressionType group_type() { return group_type_; } // Index in captures array of first capture in this sub-expression, if any. // Also the capture index of this sub-expression itself, if group_type // is CAPTURE. int capture_index() { return disjunction_capture_index_; } private: // Linked list implementation of stack of states. RegExpParserState* previous_state_; // Builder for the stored disjunction. RegExpBuilder* builder_; // Stored disjunction type (capture, look-ahead or grouping), if any. SubexpressionType group_type_; // Stored disjunction's capture index (if any). int disjunction_capture_index_; }; uc32 current() { return current_; } bool has_more() { return has_more_; } bool has_next() { return next_pos_ < in()->length(); } uc32 Next(); FlatStringReader* in() { return in_; } void ScanForCaptures(); uc32 current_; bool has_more_; bool multiline_; int next_pos_; FlatStringReader* in_; Handle* error_; bool simple_; bool contains_anchor_; ZoneList* captures_; bool is_scanned_for_captures_; // The capture count is only valid after we have scanned for captures. int capture_count_; bool failed_; }; // A temporary scope stores information during parsing, just like // a plain scope. However, temporary scopes are not kept around // after parsing or referenced by syntax trees so they can be stack- // allocated and hence used by the pre-parser. class TemporaryScope BASE_EMBEDDED { public: explicit TemporaryScope(Parser* parser); ~TemporaryScope(); int NextMaterializedLiteralIndex() { int next_index = materialized_literal_count_ + JSFunction::kLiteralsPrefixSize; materialized_literal_count_++; return next_index; } int materialized_literal_count() { return materialized_literal_count_; } void SetThisPropertyAssignmentInfo( bool only_simple_this_property_assignments, Handle this_property_assignments) { only_simple_this_property_assignments_ = only_simple_this_property_assignments; this_property_assignments_ = this_property_assignments; } bool only_simple_this_property_assignments() { return only_simple_this_property_assignments_; } Handle this_property_assignments() { return this_property_assignments_; } void AddProperty() { expected_property_count_++; } int expected_property_count() { return expected_property_count_; } void AddLoop() { loop_count_++; } bool ContainsLoops() const { return loop_count_ > 0; } private: // Captures the number of literals that need materialization in the // function. Includes regexp literals, and boilerplate for object // and array literals. int materialized_literal_count_; // Properties count estimation. int expected_property_count_; // Keeps track of assignments to properties of this. Used for // optimizing constructors. bool only_simple_this_property_assignments_; Handle this_property_assignments_; // Captures the number of loops inside the scope. int loop_count_; // Bookkeeping Parser* parser_; TemporaryScope* parent_; friend class Parser; }; TemporaryScope::TemporaryScope(Parser* parser) : materialized_literal_count_(0), expected_property_count_(0), only_simple_this_property_assignments_(false), this_property_assignments_(Factory::empty_fixed_array()), loop_count_(0), parser_(parser), parent_(parser->temp_scope_) { parser->temp_scope_ = this; } TemporaryScope::~TemporaryScope() { parser_->temp_scope_ = parent_; } // A zone list wrapper lets code either access a access a zone list // or appear to do so while actually ignoring all operations. template class ZoneListWrapper { public: ZoneListWrapper() : list_(NULL) { } explicit ZoneListWrapper(int size) : list_(new ZoneList(size)) { } void Add(T* that) { if (list_) list_->Add(that); } int length() { return list_->length(); } ZoneList* elements() { return list_; } T* at(int index) { return list_->at(index); } private: ZoneList* list_; }; // Allocation macro that should be used to allocate objects that must // only be allocated in real parsing mode. Note that in preparse mode // not only is the syntax tree not created but the constructor // arguments are not evaluated. #define NEW(expr) (is_pre_parsing_ ? NULL : new expr) class ParserFactory BASE_EMBEDDED { public: explicit ParserFactory(bool is_pre_parsing) : is_pre_parsing_(is_pre_parsing) { } virtual ~ParserFactory() { } virtual Scope* NewScope(Scope* parent, Scope::Type type, bool inside_with); virtual Handle LookupSymbol(int index, Vector string) { return Handle(); } virtual Handle EmptySymbol() { return Handle(); } virtual Expression* NewProperty(Expression* obj, Expression* key, int pos) { if (obj == VariableProxySentinel::this_proxy()) { return Property::this_property(); } else { return ValidLeftHandSideSentinel::instance(); } } virtual Expression* NewCall(Expression* expression, ZoneList* arguments, int pos) { return Call::sentinel(); } virtual Statement* EmptyStatement() { return NULL; } template ZoneListWrapper NewList(int size) { return is_pre_parsing_ ? ZoneListWrapper() : ZoneListWrapper(size); } private: bool is_pre_parsing_; }; class ParserLog BASE_EMBEDDED { public: virtual ~ParserLog() { } // Records the occurrence of a function. virtual FunctionEntry LogFunction(int start) { return FunctionEntry(); } virtual void LogSymbol(int start, Vector symbol) {} virtual void LogError() { } // Return the current position in the function entry log. virtual int function_position() { return 0; } virtual int symbol_position() { return 0; } virtual int symbol_ids() { return 0; } virtual void PauseRecording() {} virtual void ResumeRecording() {} virtual Vector ExtractData() { return Vector(); }; }; class ConditionalLogPauseScope { public: ConditionalLogPauseScope(bool pause, ParserLog* log) : log_(log), pause_(pause) { if (pause) log->PauseRecording(); } ~ConditionalLogPauseScope() { if (pause_) log_->ResumeRecording(); } private: ParserLog* log_; bool pause_; }; class AstBuildingParserFactory : public ParserFactory { public: explicit AstBuildingParserFactory(int expected_symbols) : ParserFactory(false), symbol_cache_(expected_symbols) { } virtual Scope* NewScope(Scope* parent, Scope::Type type, bool inside_with); virtual Handle LookupSymbol(int symbol_id, Vector string) { // Length of symbol cache is the number of identified symbols. // If we are larger than that, or negative, it's not a cached symbol. // This might also happen if there is no preparser symbol data, even // if there is some preparser data. if (static_cast(symbol_id) >= static_cast(symbol_cache_.length())) { return Factory::LookupSymbol(string); } return LookupCachedSymbol(symbol_id, string); } Handle LookupCachedSymbol(int symbol_id, Vector string) { // Make sure the cache is large enough to hold the symbol identifier. if (symbol_cache_.length() <= symbol_id) { // Increase length to index + 1. symbol_cache_.AddBlock(Handle::null(), symbol_id + 1 - symbol_cache_.length()); } Handle result = symbol_cache_.at(symbol_id); if (result.is_null()) { result = Factory::LookupSymbol(string); symbol_cache_.at(symbol_id) = result; return result; } Counters::total_preparse_symbols_skipped.Increment(); return result; } virtual Handle EmptySymbol() { return Factory::empty_symbol(); } virtual Expression* NewProperty(Expression* obj, Expression* key, int pos) { return new Property(obj, key, pos); } virtual Expression* NewCall(Expression* expression, ZoneList* arguments, int pos) { return new Call(expression, arguments, pos); } virtual Statement* EmptyStatement(); private: List > symbol_cache_; }; // Record only functions. class PartialParserRecorder: public ParserLog { public: PartialParserRecorder(); virtual FunctionEntry LogFunction(int start); virtual int function_position() { return function_store_.size(); } virtual void LogError() { } virtual void LogMessage(Scanner::Location loc, const char* message, Vector args); virtual Vector 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; } 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_[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 void 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); } virtual Vector 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; } 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_; }; FunctionEntry ScriptDataImpl::GetFunctionEntry(int start) { // The current pre-data entry must be a FunctionEntry with the given // start position. if ((function_index_ + FunctionEntry::kSize <= store_.length()) && (static_cast(store_[function_index_]) == start)) { int index = function_index_; function_index_ += FunctionEntry::kSize; return FunctionEntry(store_.SubVector(index, index + FunctionEntry::kSize)); } return FunctionEntry(); } int ScriptDataImpl::GetSymbolIdentifier() { return ReadNumber(&symbol_data_); } 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 (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; for (unsigned int i = 0; i <= arg_count; i++) { if (store_.length() <= 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; return true; } // Check that the space allocated for function entries is sane. int functions_size = static_cast(store_[ScriptDataImpl::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]); 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; 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); for (int i = 0; i < length; i++) { result[i] = start[i + 1]; } result[length] = '\0'; if (chars != NULL) *chars = length; 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); return Scanner::Location(beg_pos, end_pos); } const char* ScriptDataImpl::BuildMessage() { unsigned* start = ReadAddress(kMessageTextPos); return ReadString(start, NULL); } Vector ScriptDataImpl::BuildArgs() { int arg_count = Read(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); for (int i = 0; i < arg_count; i++) { int count = 0; array[i] = ReadString(ReadAddress(pos), &count); pos += count + 1; } return Vector(array, arg_count); } unsigned ScriptDataImpl::Read(int position) { return store_[ScriptDataImpl::kHeaderSize + position]; } unsigned* ScriptDataImpl::ReadAddress(int position) { return &store_[ScriptDataImpl::kHeaderSize + position]; } FunctionEntry PartialParserRecorder::LogFunction(int start) { #ifdef DEBUG ASSERT(start > prev_start); prev_start = start; #endif if (!is_recording_) return FunctionEntry(); FunctionEntry result(function_store_.AddBlock(FunctionEntry::kSize, 0)); result.set_start_pos(start); return result; } class AstBuildingParser : public Parser { public: AstBuildingParser(Handle