diff options
152 files changed, 9737 insertions, 8510 deletions
diff --git a/.gitignore b/.gitignore new file mode 100644 index 00000000..974628d8 --- /dev/null +++ b/.gitignore @@ -0,0 +1,26 @@ +*.a +*.exe +*.lib +*.log +*.map +*.mk +*.ncb +*.pyc +*.scons* +*.suo +*.user +*.xcodeproj +*.idb +*.pdb +#*# +*~ +.cpplint-cache +d8 +d8_g +shell +shell_g +/obj/ +/tools/visual_studio/Debug +/tools/visual_studio/Release +/xcodebuild/ +TAGS @@ -9,7 +9,6 @@ ARM Ltd. Hewlett-Packard Development Company, LP Alexander Botero-Lowry <alexbl@FreeBSD.org> -Alexandre Rames <alexandre.rames@arm.com> Alexandre Vassalotti <avassalotti@gmail.com> Andreas Anyuru <andreas.anyuru@gmail.com> Burcu Dogan <burcujdogan@gmail.com> diff --git a/Android.v8common.mk b/Android.v8common.mk index 90d1037f..0c33d184 100644 --- a/Android.v8common.mk +++ b/Android.v8common.mk @@ -7,6 +7,7 @@ V8_LOCAL_SRC_FILES := \ src/assembler.cc \ src/ast.cc \ src/bignum.cc \ + src/bignum-dtoa.cc \ src/bootstrapper.cc \ src/builtins.cc \ src/cached-powers.cc \ @@ -27,6 +28,8 @@ V8_LOCAL_SRC_FILES := \ src/diy-fp.cc \ src/dtoa.cc \ src/execution.cc \ + src/extensions/externalize-string-extension.cc \ + src/extensions/gc-extension.cc \ src/factory.cc \ src/fast-dtoa.cc \ src/fixed-dtoa.cc \ @@ -54,6 +57,8 @@ V8_LOCAL_SRC_FILES := \ src/objects-visiting.cc \ src/oprofile-agent.cc \ src/parser.cc \ + src/preparse-data.cc \ + src/preparser.cc \ src/property.cc \ src/regexp-macro-assembler.cc \ src/regexp-macro-assembler-irregexp.cc \ @@ -132,9 +137,6 @@ ifeq ($(DEBUG_V8),true) src/regexp-macro-assembler-tracer.cc endif -V8_LOCAL_SRC_FILES += \ - src/dtoa-config.c - # The order of these JS library sources is important. The order here determines # the ordering of the JS code in libraries.cc, which must be in a specific order # to meet compiler dependency requirements. @@ -1,3 +1,61 @@ +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. + + Improved heap profiler precision and speed. + + Reduced overhead of callback invocations on ARM. + + +2010-11-18: Version 2.5.7 + + Fixed obscure evaluation order bug (issue 931). + + Split the random number state between JavaScript and the private API. + + Fixed performance bug causing GCs when generating stack traces on + code from very large scripts. + + Fixed bug in parser that allowed (foo):42 as a labelled statement + (issue 918). + + Provide more accurate results about used heap size via + GetHeapStatistics. + + Allow build-time customization of the max semispace size. + + Made String.prototype.split honor limit when separator is empty + (issue 929). + + Added missing failure check after expecting an identifier in + preparser (Chromium issue 62639). + + 2010-11-10: Version 2.5.6 Added support for VFP rounding modes to the ARM simulator. @@ -12,9 +12,6 @@ are: based on layout tests from webkit.org which are copyrighted by Apple Computer, Inc. and released under a 3-clause BSD license. - - Dtoa, located under third_party/dtoa. This code is copyrighted by - David M. Gay and released under an MIT license. - - Strongtalk assembler, the basis of the files assembler-arm-inl.h, assembler-arm.cc, assembler-arm.h, assembler-ia32-inl.h, assembler-ia32.cc, assembler-ia32.h, assembler.cc and assembler.h. diff --git a/V8_MERGE_REVISION b/V8_MERGE_REVISION index 59bb5306..49003045 100644 --- a/V8_MERGE_REVISION +++ b/V8_MERGE_REVISION @@ -1,4 +1,4 @@ We use a V8 revision that has been used for a Chromium release. -http://src.chromium.org/svn/releases/9.0.589.0/DEPS -http://v8.googlecode.com/svn/trunk@5804 +http://src.chromium.org/svn/releases/9.0.600.0/DEPS +http://v8.googlecode.com/svn/trunk@5901 diff --git a/include/v8-profiler.h b/include/v8-profiler.h index fb492d95..72195c44 100644 --- a/include/v8-profiler.h +++ b/include/v8-profiler.h @@ -197,8 +197,13 @@ class V8EXPORT HeapGraphEdge { kContextVariable = 0, // A variable from a function context. kElement = 1, // An element of an array. kProperty = 2, // A named object property. - kInternal = 3 // A link that can't be accessed from JS, - // thus, its name isn't a real property name. + kInternal = 3, // A link that can't be accessed from JS, + // thus, its name isn't a real property name + // (e.g. parts of a ConsString). + kHidden = 4, // A link that is needed for proper sizes + // calculation, but may be hidden from user. + kShortcut = 5 // A link that must not be followed during + // sizes calculation. }; /** Returns edge type (see HeapGraphEdge::Type). */ @@ -240,7 +245,8 @@ class V8EXPORT HeapGraphPath { class V8EXPORT HeapGraphNode { public: enum Type { - kInternal = 0, // Internal node, a virtual one, for housekeeping. + kInternal = 0, // For compatibility, will be removed. + kHidden = 0, // Hidden node, may be filtered when shown to user. kArray = 1, // An array of elements. kString = 2, // A string. kObject = 3, // A JS object (except for arrays and strings). @@ -276,16 +282,19 @@ class V8EXPORT HeapGraphNode { /** Returns node's own size, in bytes. */ int GetSelfSize() const; - /** Returns node's network (self + reachable nodes) size, in bytes. */ - int GetReachableSize() const; - /** * Returns node's retained size, in bytes. That is, self + sizes of * the objects that are reachable only from this object. In other * words, the size of memory that will be reclaimed having this node * collected. + * + * Exact retained size calculation has O(N) (number of nodes) + * computational complexity, while approximate has O(1). It is + * assumed that initially heap profiling tools provide approximate + * sizes for all nodes, and then exact sizes are calculated for the + * most 'interesting' nodes. */ - int GetRetainedSize() const; + int GetRetainedSize(bool exact) const; /** Returns child nodes count of the node. */ int GetChildrenCount() const; @@ -304,6 +313,12 @@ class V8EXPORT HeapGraphNode { /** Returns a retaining path by index. */ const HeapGraphPath* GetRetainingPath(int index) const; + + /** + * Returns a dominator node. This is the node that participates in every + * path from the snapshot root to the current node. + */ + const HeapGraphNode* GetDominatorNode() const; }; diff --git a/include/v8.h b/include/v8.h index f6c3c8b3..8ecf63ae 100644 --- a/include/v8.h +++ b/include/v8.h @@ -1539,6 +1539,11 @@ class Object : public Value { */ V8EXPORT Local<String> ObjectProtoToString(); + /** + * Returns the name of the function invoked as a constructor for this object. + */ + V8EXPORT Local<String> GetConstructorName(); + /** Gets the number of internal fields for this Object. */ V8EXPORT int InternalFieldCount(); /** Gets the value in an internal field. */ @@ -3282,8 +3287,8 @@ class V8EXPORT OutputStream { // NOLINT namespace internal { -const int kPointerSize = sizeof(void*); // NOLINT -const int kIntSize = sizeof(int); // NOLINT +static const int kApiPointerSize = sizeof(void*); // NOLINT +static const int kApiIntSize = sizeof(int); // NOLINT // Tag information for HeapObject. const int kHeapObjectTag = 1; @@ -3319,19 +3324,19 @@ template <> struct SmiConstants<8> { } }; -const int kSmiShiftSize = SmiConstants<kPointerSize>::kSmiShiftSize; -const int kSmiValueSize = SmiConstants<kPointerSize>::kSmiValueSize; +const int kSmiShiftSize = SmiConstants<kApiPointerSize>::kSmiShiftSize; +const int kSmiValueSize = SmiConstants<kApiPointerSize>::kSmiValueSize; template <size_t ptr_size> struct InternalConstants; // Internal constants for 32-bit systems. template <> struct InternalConstants<4> { - static const int kStringResourceOffset = 3 * kPointerSize; + static const int kStringResourceOffset = 3 * kApiPointerSize; }; // Internal constants for 64-bit systems. template <> struct InternalConstants<8> { - static const int kStringResourceOffset = 3 * kPointerSize; + static const int kStringResourceOffset = 3 * kApiPointerSize; }; /** @@ -3345,12 +3350,12 @@ class Internals { // These values match non-compiler-dependent values defined within // the implementation of v8. static const int kHeapObjectMapOffset = 0; - static const int kMapInstanceTypeOffset = kPointerSize + kIntSize; + static const int kMapInstanceTypeOffset = kApiPointerSize + kApiIntSize; static const int kStringResourceOffset = - InternalConstants<kPointerSize>::kStringResourceOffset; + InternalConstants<kApiPointerSize>::kStringResourceOffset; - static const int kProxyProxyOffset = kPointerSize; - static const int kJSObjectHeaderSize = 3 * kPointerSize; + static const int kProxyProxyOffset = kApiPointerSize; + static const int kJSObjectHeaderSize = 3 * kApiPointerSize; static const int kFullStringRepresentationMask = 0x07; static const int kExternalTwoByteRepresentationTag = 0x02; @@ -3368,7 +3373,7 @@ class Internals { } static inline int SmiValue(internal::Object* value) { - return SmiConstants<kPointerSize>::SmiToInt(value); + return SmiConstants<kApiPointerSize>::SmiToInt(value); } static inline int GetInstanceType(internal::Object* obj) { @@ -3559,7 +3564,7 @@ Local<Value> Object::UncheckedGetInternalField(int index) { // If the object is a plain JSObject, which is the common case, // we know where to find the internal fields and can return the // value directly. - int offset = I::kJSObjectHeaderSize + (internal::kPointerSize * index); + int offset = I::kJSObjectHeaderSize + (internal::kApiPointerSize * index); O* value = I::ReadField<O*>(obj, offset); O** result = HandleScope::CreateHandle(value); return Local<Value>(reinterpret_cast<Value*>(result)); @@ -3595,7 +3600,7 @@ void* Object::GetPointerFromInternalField(int index) { // If the object is a plain JSObject, which is the common case, // we know where to find the internal fields and can return the // value directly. - int offset = I::kJSObjectHeaderSize + (internal::kPointerSize * index); + int offset = I::kJSObjectHeaderSize + (internal::kApiPointerSize * index); O* value = I::ReadField<O*>(obj, offset); return I::GetExternalPointer(value); } diff --git a/preparser/preparser-process.cc b/preparser/preparser-process.cc new file mode 100644 index 00000000..706a2259 --- /dev/null +++ b/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 <stdarg.h> +#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<int>(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<uc32>(first_char); + } + unibrow::uchar codepoint = + unibrow::Utf8::CalculateValue(buffer_ + offset_, + end_offset_ - offset_, + &offset_); + pos_++; + return static_cast<uc32>(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<uint8_t>(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<uint32_t>(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<byte> buffer(NewArray<byte>(length)); + if (!ReadBuffer(stdin, *buffer, length)) { + return kErrorReading; + } + UTF8UTF16Buffer input_buffer(*buffer, static_cast<size_t>(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<unsigned> pre_data = recorder.ExtractData(); + + uint32_t size = static_cast<uint32_t>(pre_data.length() * sizeof(uint32_t)); + WriteUInt32(stdout, size, &ok); + if (!ok) return kErrorWriting; + if (!WriteBuffer(stdout, + reinterpret_cast<byte*>(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/samples/process.cc b/samples/process.cc index 9233c0df..6be4ea54 100644 --- a/samples/process.cc +++ b/samples/process.cc @@ -152,18 +152,16 @@ bool JsHttpRequestProcessor::Initialize(map<string, string>* opts, Handle<ObjectTemplate> 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 = 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<Context>::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<string, string>* opts, // The script compiled and ran correctly. Now we fetch out the // Process function from the global object. Handle<String> process_name = String::New("Process"); - Handle<Value> process_val = context->Global()->Get(process_name); + Handle<Value> 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/src/SConscript b/src/SConscript index 030c6436..89536987 100755 --- a/src/SConscript +++ b/src/SConscript @@ -41,6 +41,7 @@ SOURCES = { assembler.cc ast.cc bignum.cc + bignum-dtoa.cc bootstrapper.cc builtins.cc cached-powers.cc @@ -88,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 @@ -119,6 +122,8 @@ SOURCES = { version.cc virtual-frame.cc zone.cc + extensions/gc-extension.cc + extensions/externalize-string-extension.cc """), 'arch:arm': Split(""" jump-target-light.cc @@ -294,14 +299,8 @@ def ConfigureObjectFiles(): libraries_src, libraries_empty_src = env.JS2C(['libraries.cc', 'libraries-empty.cc'], library_files, TYPE='CORE') libraries_obj = context.ConfigureObject(env, libraries_src, CPPPATH=['.']) - # Build dtoa. - dtoa_env = env.Copy() - dtoa_env.Replace(**context.flags['dtoa']) - dtoa_files = ['dtoa-config.c'] - dtoa_obj = context.ConfigureObject(dtoa_env, dtoa_files) - source_objs = context.ConfigureObject(env, source_files) - non_snapshot_files = [dtoa_obj, source_objs] + non_snapshot_files = [source_objs] # Create snapshot if necessary. For cross compilation you should either # do without snapshots and take the performance hit or you should build a diff --git a/src/accessors.cc b/src/accessors.cc index 7c21659e..08ef41b9 100644 --- a/src/accessors.cc +++ b/src/accessors.cc @@ -316,8 +316,10 @@ MaybeObject* Accessors::ScriptGetLineEnds(Object* object, void*) { InitScriptLineEnds(script); ASSERT(script->line_ends()->IsFixedArray()); Handle<FixedArray> line_ends(FixedArray::cast(script->line_ends())); - Handle<FixedArray> copy = Factory::CopyFixedArray(line_ends); - Handle<JSArray> js_array = Factory::NewJSArrayWithElements(copy); + // We do not want anyone to modify this array from JS. + ASSERT(*line_ends == Heap::empty_fixed_array() || + line_ends->map() == Heap::fixed_cow_array_map()); + Handle<JSArray> js_array = Factory::NewJSArrayWithElements(line_ends); return *js_array; } diff --git a/src/allocation.cc b/src/allocation.cc index 678f4fd7..d74c37cd 100644 --- a/src/allocation.cc +++ b/src/allocation.cc @@ -27,16 +27,21 @@ #include <stdlib.h> -#include "v8.h" +#include "../include/v8stdint.h" +#include "globals.h" +#include "checks.h" +#include "allocation.h" +#include "utils.h" namespace v8 { namespace internal { - void* Malloced::New(size_t size) { ASSERT(NativeAllocationChecker::allocation_allowed()); void* result = malloc(size); - if (result == NULL) V8::FatalProcessOutOfMemory("Malloced operator new"); + if (result == NULL) { + v8::internal::FatalProcessOutOfMemory("Malloced operator new"); + } return result; } @@ -47,7 +52,7 @@ void Malloced::Delete(void* p) { void Malloced::FatalProcessOutOfMemory() { - V8::FatalProcessOutOfMemory("Out of memory"); + v8::internal::FatalProcessOutOfMemory("Out of memory"); } @@ -82,7 +87,7 @@ void AllStatic::operator delete(void* p) { char* StrDup(const char* str) { int length = StrLength(str); char* result = NewArray<char>(length + 1); - memcpy(result, str, length * kCharSize); + memcpy(result, str, length); result[length] = '\0'; return result; } @@ -92,7 +97,7 @@ char* StrNDup(const char* str, int n) { int length = StrLength(str); if (n < length) length = n; char* result = NewArray<char>(length + 1); - memcpy(result, str, length * kCharSize); + memcpy(result, str, length); result[length] = '\0'; return result; } @@ -124,6 +129,7 @@ void* PreallocatedStorage::New(size_t size) { } ASSERT(free_list_.next_ != &free_list_); ASSERT(free_list_.previous_ != &free_list_); + size = (size + kPointerSize - 1) & ~(kPointerSize - 1); // Search for exact fit. for (PreallocatedStorage* storage = free_list_.next_; diff --git a/src/allocation.h b/src/allocation.h index 70a3a038..6f4bd2fb 100644 --- a/src/allocation.h +++ b/src/allocation.h @@ -31,6 +31,10 @@ namespace v8 { namespace internal { +// Called when allocation routines fail to allocate. +// This function should not return, but should terminate the current +// processing. +void FatalProcessOutOfMemory(const char* message); // A class that controls whether allocation is allowed. This is for // the C++ heap only! @@ -115,7 +115,6 @@ static void DefaultFatalErrorHandler(const char* location, } - static FatalErrorCallback& GetFatalErrorHandler() { if (exception_behavior == NULL) { exception_behavior = DefaultFatalErrorHandler; @@ -124,6 +123,10 @@ static FatalErrorCallback& GetFatalErrorHandler() { } +void i::FatalProcessOutOfMemory(const char* location) { + i::V8::FatalProcessOutOfMemory(location, false); +} + // When V8 cannot allocated memory FatalProcessOutOfMemory is called. // The default fatal error handler is called and execution is stopped. @@ -2448,6 +2451,15 @@ Local<String> v8::Object::ObjectProtoToString() { } +Local<String> v8::Object::GetConstructorName() { + ON_BAILOUT("v8::Object::GetConstructorName()", return Local<v8::String>()); + ENTER_V8; + i::Handle<i::JSObject> self = Utils::OpenHandle(this); + i::Handle<i::String> name(self->constructor_name()); + return Utils::ToLocal(name); +} + + bool v8::Object::Delete(v8::Handle<String> key) { ON_BAILOUT("v8::Object::Delete()", return false); ENTER_V8; @@ -4666,9 +4678,11 @@ Handle<Value> HeapGraphEdge::GetName() const { case i::HeapGraphEdge::kContextVariable: case i::HeapGraphEdge::kInternal: case i::HeapGraphEdge::kProperty: + case i::HeapGraphEdge::kShortcut: return Handle<String>(ToApi<String>(i::Factory::LookupAsciiSymbol( edge->name()))); case i::HeapGraphEdge::kElement: + case i::HeapGraphEdge::kHidden: return Handle<Number>(ToApi<Number>(i::Factory::NewNumberFromInt( edge->index()))); default: UNREACHABLE(); @@ -4758,15 +4772,9 @@ int HeapGraphNode::GetSelfSize() const { } -int HeapGraphNode::GetReachableSize() const { - IsDeadCheck("v8::HeapSnapshot::GetReachableSize"); - return ToInternal(this)->ReachableSize(); -} - - -int HeapGraphNode::GetRetainedSize() const { +int HeapGraphNode::GetRetainedSize(bool exact) const { IsDeadCheck("v8::HeapSnapshot::GetRetainedSize"); - return ToInternal(this)->RetainedSize(); + return ToInternal(this)->RetainedSize(exact); } @@ -4809,6 +4817,12 @@ const HeapGraphPath* HeapGraphNode::GetRetainingPath(int index) const { } +const HeapGraphNode* HeapGraphNode::GetDominatorNode() const { + IsDeadCheck("v8::HeapSnapshot::GetDominatorNode"); + return reinterpret_cast<const HeapGraphNode*>(ToInternal(this)->dominator()); +} + + const HeapGraphNode* HeapSnapshotsDiff::GetAdditionsRoot() const { IsDeadCheck("v8::HeapSnapshotsDiff::GetAdditionsRoot"); i::HeapSnapshotsDiff* diff = diff --git a/src/apiutils.h b/src/apiutils.h index 1313ddaa..9683aa43 100644 --- a/src/apiutils.h +++ b/src/apiutils.h @@ -58,6 +58,9 @@ class ImplementationUtilities { static v8::Arguments NewArguments(internal::Object** implicit_args, internal::Object** argv, int argc, bool is_construct_call) { + ASSERT(implicit_args[v8::Arguments::kCalleeIndex]->IsJSFunction()); + ASSERT(implicit_args[v8::Arguments::kHolderIndex]->IsHeapObject()); + return v8::Arguments(implicit_args, argv, argc, is_construct_call); } diff --git a/src/arm/assembler-arm-inl.h b/src/arm/assembler-arm-inl.h index f72ad76a..15720c95 100644 --- a/src/arm/assembler-arm-inl.h +++ b/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<cond> <Rd>, [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/src/arm/assembler-arm.cc b/src/arm/assembler-arm.cc index 4cb421c5..cfdd1649 100644 --- a/src/arm/assembler-arm.cc +++ b/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<cond> <Rd>, [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/src/arm/assembler-arm.h b/src/arm/assembler-arm.h index de3931c2..ee4c9aa5 100644 --- a/src/arm/assembler-arm.h +++ b/src/arm/assembler-arm.h @@ -219,6 +219,11 @@ const DwVfpRegister d13 = { 13 }; const DwVfpRegister d14 = { 14 }; const DwVfpRegister d15 = { 15 }; +// VFP FPSCR constants. +static const uint32_t kVFPExceptionMask = 0xf; +static const uint32_t kVFPRoundingModeMask = 3 << 22; +static const uint32_t kVFPFlushToZeroMask = 1 << 24; +static const uint32_t kVFPRoundToMinusInfinityBits = 2 << 22; // Coprocessor register struct CRegister { @@ -1074,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); @@ -1146,7 +1166,6 @@ class Assembler : public Malloced { static void instr_at_put(byte* pc, Instr instr) { *reinterpret_cast<Instr*>(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); @@ -1163,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/src/arm/builtins-arm.cc b/src/arm/builtins-arm.cc index cf2f4262..862ef395 100644 --- a/src/arm/builtins-arm.cc +++ b/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/src/arm/code-stubs-arm.cc b/src/arm/code-stubs-arm.cc index b3b0766a..76a610b7 100644 --- a/src/arm/code-stubs-arm.cc +++ b/src/arm/code-stubs-arm.cc @@ -100,8 +100,9 @@ void FastNewClosureStub::Generate(MacroAssembler* masm) { // Create a new closure through the slower runtime call. __ bind(&gc); - __ Push(cp, r3); - __ TailCallRuntime(Runtime::kNewClosure, 2, 1); + __ LoadRoot(r4, Heap::kFalseValueRootIndex); + __ Push(cp, r3, r4); + __ TailCallRuntime(Runtime::kNewClosure, 3, 1); } diff --git a/src/arm/codegen-arm.cc b/src/arm/codegen-arm.cc index d7fd9a49..27e14df4 100644 --- a/src/arm/codegen-arm.cc +++ b/src/arm/codegen-arm.cc @@ -43,6 +43,7 @@ #include "register-allocator-inl.h" #include "runtime.h" #include "scopes.h" +#include "stub-cache.h" #include "virtual-frame-inl.h" #include "virtual-frame-arm-inl.h" @@ -557,7 +558,7 @@ void CodeGenerator::Load(Expression* expr) { void CodeGenerator::LoadGlobal() { Register reg = frame_->GetTOSRegister(); - __ ldr(reg, GlobalObject()); + __ ldr(reg, GlobalObjectOperand()); frame_->EmitPush(reg); } @@ -1891,18 +1892,15 @@ void CodeGenerator::CheckStack() { frame_->SpillAll(); Comment cmnt(masm_, "[ check stack"); __ LoadRoot(ip, Heap::kStackLimitRootIndex); - // Put the lr setup instruction in the delay slot. kInstrSize is added to - // the implicit 8 byte offset that always applies to operations with pc and - // gives a return address 12 bytes down. - masm_->add(lr, pc, Operand(Assembler::kInstrSize)); masm_->cmp(sp, Operand(ip)); StackCheckStub stub; // Call the stub if lower. - masm_->mov(pc, + masm_->mov(ip, Operand(reinterpret_cast<intptr_t>(stub.GetCode().location()), RelocInfo::CODE_TARGET), LeaveCC, lo); + masm_->Call(ip, lo); } @@ -3105,10 +3103,13 @@ void CodeGenerator::VisitDebuggerStatement(DebuggerStatement* node) { void CodeGenerator::InstantiateFunction( - Handle<SharedFunctionInfo> function_info) { + Handle<SharedFunctionInfo> function_info, + bool pretenure) { // Use the fast case closure allocation code that allocates in new // space for nested functions that don't need literals cloning. - if (scope()->is_function_scope() && function_info->num_literals() == 0) { + if (scope()->is_function_scope() && + function_info->num_literals() == 0 && + !pretenure) { FastNewClosureStub stub; frame_->EmitPush(Operand(function_info)); frame_->SpillAll(); @@ -3118,7 +3119,10 @@ void CodeGenerator::InstantiateFunction( // Create a new closure. frame_->EmitPush(cp); frame_->EmitPush(Operand(function_info)); - frame_->CallRuntime(Runtime::kNewClosure, 2); + frame_->EmitPush(Operand(pretenure + ? Factory::true_value() + : Factory::false_value())); + frame_->CallRuntime(Runtime::kNewClosure, 3); frame_->EmitPush(r0); } } @@ -3138,7 +3142,7 @@ void CodeGenerator::VisitFunctionLiteral(FunctionLiteral* node) { ASSERT(frame_->height() == original_height); return; } - InstantiateFunction(function_info); + InstantiateFunction(function_info, node->pretenure()); ASSERT_EQ(original_height + 1, frame_->height()); } @@ -3149,7 +3153,7 @@ void CodeGenerator::VisitSharedFunctionInfoLiteral( int original_height = frame_->height(); #endif Comment cmnt(masm_, "[ SharedFunctionInfoLiteral"); - InstantiateFunction(node->shared_function_info()); + InstantiateFunction(node->shared_function_info(), false); ASSERT_EQ(original_height + 1, frame_->height()); } @@ -4232,7 +4236,7 @@ void CodeGenerator::VisitCall(Call* node) { // Setup the name register and call the IC initialization code. __ mov(r2, Operand(var->name())); InLoopFlag in_loop = loop_nesting() > 0 ? IN_LOOP : NOT_IN_LOOP; - Handle<Code> stub = ComputeCallInitialize(arg_count, in_loop); + Handle<Code> stub = StubCache::ComputeCallInitialize(arg_count, in_loop); CodeForSourcePosition(node->position()); frame_->CallCodeObject(stub, RelocInfo::CODE_TARGET_CONTEXT, arg_count + 1); @@ -4326,7 +4330,8 @@ void CodeGenerator::VisitCall(Call* node) { // Set the name register and call the IC initialization code. __ mov(r2, Operand(name)); InLoopFlag in_loop = loop_nesting() > 0 ? IN_LOOP : NOT_IN_LOOP; - Handle<Code> stub = ComputeCallInitialize(arg_count, in_loop); + Handle<Code> stub = + StubCache::ComputeCallInitialize(arg_count, in_loop); CodeForSourcePosition(node->position()); frame_->CallCodeObject(stub, RelocInfo::CODE_TARGET, arg_count + 1); __ ldr(cp, frame_->Context()); @@ -4337,9 +4342,12 @@ void CodeGenerator::VisitCall(Call* node) { // ------------------------------------------- // JavaScript example: 'array[index](1, 2, 3)' // ------------------------------------------- + + // Load the receiver and name of the function. Load(property->obj()); + Load(property->key()); + if (property->is_synthetic()) { - Load(property->key()); EmitKeyedLoad(); // Put the function below the receiver. // Use the global receiver. @@ -4349,21 +4357,28 @@ void CodeGenerator::VisitCall(Call* node) { CallWithArguments(args, RECEIVER_MIGHT_BE_VALUE, node->position()); frame_->EmitPush(r0); } else { + // Swap the name of the function and the receiver on the stack to follow + // the calling convention for call ICs. + Register key = frame_->PopToRegister(); + Register receiver = frame_->PopToRegister(key); + frame_->EmitPush(key); + frame_->EmitPush(receiver); + // Load the arguments. int arg_count = args->length(); for (int i = 0; i < arg_count; i++) { Load(args->at(i)); } - // Set the name register and call the IC initialization code. - Load(property->key()); - frame_->SpillAll(); - frame_->EmitPop(r2); // Function name. - + // Load the key into r2 and call the IC initialization code. InLoopFlag in_loop = loop_nesting() > 0 ? IN_LOOP : NOT_IN_LOOP; - Handle<Code> stub = ComputeKeyedCallInitialize(arg_count, in_loop); + Handle<Code> stub = + StubCache::ComputeKeyedCallInitialize(arg_count, in_loop); CodeForSourcePosition(node->position()); + frame_->SpillAll(); + __ ldr(r2, frame_->ElementAt(arg_count + 1)); frame_->CallCodeObject(stub, RelocInfo::CODE_TARGET, arg_count + 1); + frame_->Drop(); // Drop the key still on the stack. __ ldr(cp, frame_->Context()); frame_->EmitPush(r0); } @@ -5135,11 +5150,11 @@ class DeferredIsStringWrapperSafeForDefaultValueOf : public DeferredCode { __ b(eq, &false_result); __ ldr(scratch1_, FieldMemOperand(scratch1_, HeapObject::kMapOffset)); __ ldr(scratch2_, - CodeGenerator::ContextOperand(cp, Context::GLOBAL_INDEX)); + ContextOperand(cp, Context::GLOBAL_INDEX)); __ ldr(scratch2_, FieldMemOperand(scratch2_, GlobalObject::kGlobalContextOffset)); __ ldr(scratch2_, - CodeGenerator::ContextOperand( + ContextOperand( scratch2_, Context::STRING_FUNCTION_PROTOTYPE_MAP_INDEX)); __ cmp(scratch1_, scratch2_); __ b(ne, &false_result); @@ -5807,6 +5822,15 @@ void CodeGenerator::GenerateGetCachedArrayIndex(ZoneList<Expression*>* args) { } +void CodeGenerator::GenerateFastAsciiArrayJoin(ZoneList<Expression*>* args) { + ASSERT(args->length() == 2); + Load(args->at(0)); + Register value = frame_->PopToRegister(); + __ LoadRoot(value, Heap::kUndefinedValueRootIndex); + frame_->EmitPush(value); +} + + void CodeGenerator::VisitCallRuntime(CallRuntime* node) { #ifdef DEBUG int original_height = frame_->height(); @@ -5825,7 +5849,7 @@ void CodeGenerator::VisitCallRuntime(CallRuntime* node) { // Prepare stack for calling JS runtime function. // Push the builtins object found in the current global object. Register scratch = VirtualFrame::scratch0(); - __ ldr(scratch, GlobalObject()); + __ ldr(scratch, GlobalObjectOperand()); Register builtins = frame_->GetTOSRegister(); __ ldr(builtins, FieldMemOperand(scratch, GlobalObject::kBuiltinsOffset)); frame_->EmitPush(builtins); @@ -5843,7 +5867,7 @@ void CodeGenerator::VisitCallRuntime(CallRuntime* node) { // Call the JS runtime function. __ mov(r2, Operand(node->name())); InLoopFlag in_loop = loop_nesting() > 0 ? IN_LOOP : NOT_IN_LOOP; - Handle<Code> stub = ComputeCallInitialize(arg_count, in_loop); + Handle<Code> stub = StubCache::ComputeCallInitialize(arg_count, in_loop); frame_->CallCodeObject(stub, RelocInfo::CODE_TARGET, arg_count + 1); __ ldr(cp, frame_->Context()); frame_->EmitPush(r0); @@ -6000,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(); @@ -6059,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. @@ -6073,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) { @@ -6084,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); + // If increment/decrement overflows, go to deferred code. + deferred->Branch(vs); - // 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())); - } - - // 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 @@ -6513,16 +6582,29 @@ void CodeGenerator::VisitCompareToNull(CompareToNull* node) { class DeferredReferenceGetNamedValue: public DeferredCode { public: explicit DeferredReferenceGetNamedValue(Register receiver, - Handle<String> name) - : receiver_(receiver), name_(name) { - set_comment("[ DeferredReferenceGetNamedValue"); + Handle<String> 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<String> name_; + bool is_contextual_; + bool is_dont_delete_; }; @@ -6549,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<Code> 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. @@ -6616,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 @@ -6673,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 @@ -6721,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 @@ -6739,7 +6831,14 @@ void DeferredReferenceSetNamedValue::Generate() { // Consumes the top of stack (the receiver) and pushes the result instead. void CodeGenerator::EmitNamedLoad(Handle<String> 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, @@ -6749,12 +6848,19 @@ void CodeGenerator::EmitNamedLoad(Handle<String> 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 @@ -6765,19 +6871,57 @@ void CodeGenerator::EmitNamedLoad(Handle<String> 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(); @@ -6788,12 +6932,42 @@ void CodeGenerator::EmitNamedLoad(Handle<String> 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/src/arm/codegen-arm.h b/src/arm/codegen-arm.h index 97e50b42..1930f5e1 100644 --- a/src/arm/codegen-arm.h +++ b/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 @@ -279,10 +271,6 @@ class CodeGenerator: public AstVisitor { return inlined_write_barrier_size_ + 4; } - static MemOperand ContextOperand(Register context, int index) { - return MemOperand(context, Context::SlotOffset(index)); - } - private: // Type of a member function that generates inline code for a native function. typedef void (CodeGenerator::*InlineFunctionGenerator) @@ -349,10 +337,6 @@ class CodeGenerator: public AstVisitor { JumpTarget* slow); // Expressions - static MemOperand GlobalObject() { - return ContextOperand(cp, Context::GLOBAL_INDEX); - } - void LoadCondition(Expression* x, JumpTarget* true_target, JumpTarget* false_target, @@ -452,16 +436,13 @@ class CodeGenerator: public AstVisitor { static Handle<Code> ComputeLazyCompile(int argc); void ProcessDeclarations(ZoneList<Declaration*>* declarations); - static Handle<Code> ComputeCallInitialize(int argc, InLoopFlag in_loop); - - static Handle<Code> ComputeKeyedCallInitialize(int argc, InLoopFlag in_loop); - // Declare global variables and functions in the given array of // name/value pairs. void DeclareGlobals(Handle<FixedArray> pairs); // Instantiate the function based on the shared function info. - void InstantiateFunction(Handle<SharedFunctionInfo> function_info); + void InstantiateFunction(Handle<SharedFunctionInfo> function_info, + bool pretenure); // Support for type checks. void GenerateIsSmi(ZoneList<Expression*>* args); @@ -540,6 +521,7 @@ class CodeGenerator: public AstVisitor { void GenerateHasCachedArrayIndex(ZoneList<Expression*>* args); void GenerateGetCachedArrayIndex(ZoneList<Expression*>* args); + void GenerateFastAsciiArrayJoin(ZoneList<Expression*>* args); // Simple condition analysis. enum ConditionAnalysis { diff --git a/src/arm/debug-arm.cc b/src/arm/debug-arm.cc index 8128f7de..f19e6939 100644 --- a/src/arm/debug-arm.cc +++ b/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/src/arm/disasm-arm.cc b/src/arm/disasm-arm.cc index 4e7580f8..297a2db5 100644 --- a/src/arm/disasm-arm.cc +++ b/src/arm/disasm-arm.cc @@ -1046,6 +1046,7 @@ int Decoder::DecodeType7(Instr* instr) { // Dd = vdiv(Dn, Dm) // vcmp(Dd, Dm) // vmrs +// vmsr // Dd = vsqrt(Dm) void Decoder::DecodeTypeVFP(Instr* instr) { ASSERT((instr->TypeField() == 7) && (instr->Bit(24) == 0x0) ); @@ -1111,16 +1112,22 @@ void Decoder::DecodeTypeVFP(Instr* instr) { if ((instr->VCField() == 0x0) && (instr->VAField() == 0x0)) { DecodeVMOVBetweenCoreAndSinglePrecisionRegisters(instr); - } else if ((instr->VLField() == 0x1) && - (instr->VCField() == 0x0) && + } else if ((instr->VCField() == 0x0) && (instr->VAField() == 0x7) && (instr->Bits(19, 16) == 0x1)) { - if (instr->Bits(15, 12) == 0xF) - Format(instr, "vmrs'cond APSR, FPSCR"); - else - Unknown(instr); // Not used by V8. - } else { - Unknown(instr); // Not used by V8. + if (instr->VLField() == 0) { + if (instr->Bits(15, 12) == 0xF) { + Format(instr, "vmsr'cond FPSCR, APSR"); + } else { + Format(instr, "vmsr'cond FPSCR, 'rt"); + } + } else { + if (instr->Bits(15, 12) == 0xF) { + Format(instr, "vmrs'cond APSR, FPSCR"); + } else { + Format(instr, "vmrs'cond 'rt, FPSCR"); + } + } } } } diff --git a/src/arm/full-codegen-arm.cc b/src/arm/full-codegen-arm.cc index bf27d0c7..f04015bd 100644 --- a/src/arm/full-codegen-arm.cc +++ b/src/arm/full-codegen-arm.cc @@ -36,6 +36,7 @@ #include "full-codegen.h" #include "parser.h" #include "scopes.h" +#include "stub-cache.h" namespace v8 { namespace internal { @@ -171,19 +172,16 @@ void FullCodeGenerator::Generate(CompilationInfo* info) { } // Check the stack for overflow or break request. - // Put the lr setup instruction in the delay slot. The kInstrSize is - // added to the implicit 8 byte offset that always applies to operations - // with pc and gives a return address 12 bytes down. { Comment cmnt(masm_, "[ Stack check"); __ LoadRoot(r2, Heap::kStackLimitRootIndex); - __ add(lr, pc, Operand(Assembler::kInstrSize)); __ cmp(sp, Operand(r2)); StackCheckStub stub; - __ mov(pc, + __ mov(ip, Operand(reinterpret_cast<intptr_t>(stub.GetCode().location()), RelocInfo::CODE_TARGET), LeaveCC, lo); + __ Call(ip, lo); } if (FLAG_trace) { @@ -862,18 +860,23 @@ void FullCodeGenerator::VisitForInStatement(ForInStatement* stmt) { } -void FullCodeGenerator::EmitNewClosure(Handle<SharedFunctionInfo> info) { +void FullCodeGenerator::EmitNewClosure(Handle<SharedFunctionInfo> info, + bool pretenure) { // Use the fast case closure allocation code that allocates in new // space for nested functions that don't need literals cloning. - if (scope()->is_function_scope() && info->num_literals() == 0) { + if (scope()->is_function_scope() && + info->num_literals() == 0 && + !pretenure) { FastNewClosureStub stub; __ mov(r0, Operand(info)); __ push(r0); __ CallStub(&stub); } else { __ mov(r0, Operand(info)); - __ Push(cp, r0); - __ CallRuntime(Runtime::kNewClosure, 2); + __ LoadRoot(r1, pretenure ? Heap::kTrueValueRootIndex + : Heap::kFalseValueRootIndex); + __ Push(cp, r0, r1); + __ CallRuntime(Runtime::kNewClosure, 3); } context()->Plug(r0); } @@ -1019,7 +1022,7 @@ void FullCodeGenerator::EmitLoadGlobalSlotCheckExtensions( __ bind(&fast); } - __ ldr(r0, CodeGenerator::GlobalObject()); + __ ldr(r0, GlobalObjectOperand()); __ mov(r2, Operand(slot->var()->name())); RelocInfo::Mode mode = (typeof_state == INSIDE_TYPEOF) ? RelocInfo::CODE_TARGET @@ -1040,7 +1043,7 @@ void FullCodeGenerator::EmitVariableLoad(Variable* var) { Comment cmnt(masm_, "Global variable"); // Use inline caching. Variable name is passed in r2 and the global // object (receiver) in r0. - __ ldr(r0, CodeGenerator::GlobalObject()); + __ ldr(r0, GlobalObjectOperand()); __ mov(r2, Operand(var->name())); Handle<Code> ic(Builtins::builtin(Builtins::LoadIC_Initialize)); EmitCallIC(ic, RelocInfo::CODE_TARGET_CONTEXT); @@ -1514,7 +1517,7 @@ void FullCodeGenerator::EmitVariableAssignment(Variable* var, // assignment. Right-hand-side value is passed in r0, variable name in // r2, and the global object in r1. __ mov(r2, Operand(var->name())); - __ ldr(r1, CodeGenerator::GlobalObject()); + __ ldr(r1, GlobalObjectOperand()); Handle<Code> ic(Builtins::builtin(Builtins::StoreIC_Initialize)); EmitCallIC(ic, RelocInfo::CODE_TARGET); @@ -1698,7 +1701,7 @@ void FullCodeGenerator::EmitCallWithIC(Call* expr, SetSourcePosition(expr->position(), FORCED_POSITION); // Call the IC initialization code. InLoopFlag in_loop = (loop_depth() > 0) ? IN_LOOP : NOT_IN_LOOP; - Handle<Code> ic = CodeGenerator::ComputeCallInitialize(arg_count, in_loop); + Handle<Code> ic = StubCache::ComputeCallInitialize(arg_count, in_loop); EmitCallIC(ic, mode); // Restore context register. __ ldr(cp, MemOperand(fp, StandardFrameConstants::kContextOffset)); @@ -1709,6 +1712,15 @@ void FullCodeGenerator::EmitCallWithIC(Call* expr, void FullCodeGenerator::EmitKeyedCallWithIC(Call* expr, Expression* key, RelocInfo::Mode mode) { + // Load the key. + VisitForAccumulatorValue(key); + + // Swap the name of the function and the receiver on the stack to follow + // the calling convention for call ICs. + __ pop(r1); + __ push(r0); + __ push(r1); + // Code common for calls using the IC. ZoneList<Expression*>* args = expr->arguments(); int arg_count = args->length(); @@ -1716,19 +1728,17 @@ void FullCodeGenerator::EmitKeyedCallWithIC(Call* expr, for (int i = 0; i < arg_count; i++) { VisitForStackValue(args->at(i)); } - VisitForAccumulatorValue(key); - __ mov(r2, r0); } // Record source position for debugger. SetSourcePosition(expr->position(), FORCED_POSITION); // Call the IC initialization code. InLoopFlag in_loop = (loop_depth() > 0) ? IN_LOOP : NOT_IN_LOOP; - Handle<Code> ic = CodeGenerator::ComputeKeyedCallInitialize(arg_count, - in_loop); + Handle<Code> ic = StubCache::ComputeKeyedCallInitialize(arg_count, in_loop); + __ ldr(r2, MemOperand(sp, (arg_count + 1) * kPointerSize)); // Key. EmitCallIC(ic, mode); // Restore context register. __ ldr(cp, MemOperand(fp, StandardFrameConstants::kContextOffset)); - context()->Plug(r0); + context()->DropAndPlug(1, r0); // Drop the key still on the stack. } @@ -1809,7 +1819,7 @@ void FullCodeGenerator::VisitCall(Call* expr) { context()->DropAndPlug(1, r0); } else if (var != NULL && !var->is_this() && var->is_global()) { // Push global object as receiver for the call IC. - __ ldr(r0, CodeGenerator::GlobalObject()); + __ ldr(r0, GlobalObjectOperand()); __ push(r0); EmitCallWithIC(expr, var->name(), RelocInfo::CODE_TARGET_CONTEXT); } else if (var != NULL && var->AsSlot() != NULL && @@ -1845,7 +1855,7 @@ void FullCodeGenerator::VisitCall(Call* expr) { // Push function. __ push(r0); // Push global receiver. - __ ldr(r1, CodeGenerator::GlobalObject()); + __ ldr(r1, GlobalObjectOperand()); __ ldr(r1, FieldMemOperand(r1, GlobalObject::kGlobalReceiverOffset)); __ push(r1); __ bind(&call); @@ -1879,7 +1889,7 @@ void FullCodeGenerator::VisitCall(Call* expr) { Handle<Code> ic(Builtins::builtin(Builtins::KeyedLoadIC_Initialize)); EmitCallIC(ic, RelocInfo::CODE_TARGET); - __ ldr(r1, CodeGenerator::GlobalObject()); + __ ldr(r1, GlobalObjectOperand()); __ ldr(r1, FieldMemOperand(r1, GlobalObject::kGlobalReceiverOffset)); __ Push(r0, r1); // Function, receiver. EmitCallWithStub(expr); @@ -1902,7 +1912,7 @@ void FullCodeGenerator::VisitCall(Call* expr) { VisitForStackValue(fun); } // Load global receiver object. - __ ldr(r1, CodeGenerator::GlobalObject()); + __ ldr(r1, GlobalObjectOperand()); __ ldr(r1, FieldMemOperand(r1, GlobalObject::kGlobalReceiverOffset)); __ push(r1); // Emit function call. @@ -2767,6 +2777,13 @@ void FullCodeGenerator::EmitGetCachedArrayIndex(ZoneList<Expression*>* args) { } +void FullCodeGenerator::EmitFastAsciiArrayJoin(ZoneList<Expression*>* args) { + __ LoadRoot(r0, Heap::kUndefinedValueRootIndex); + context()->Plug(r0); + return; +} + + void FullCodeGenerator::VisitCallRuntime(CallRuntime* expr) { Handle<String> name = expr->name(); if (name->length() > 0 && name->Get(0) == '_') { @@ -2780,7 +2797,7 @@ void FullCodeGenerator::VisitCallRuntime(CallRuntime* expr) { if (expr->is_jsruntime()) { // Prepare for calling JS runtime function. - __ ldr(r0, CodeGenerator::GlobalObject()); + __ ldr(r0, GlobalObjectOperand()); __ ldr(r0, FieldMemOperand(r0, GlobalObject::kBuiltinsOffset)); __ push(r0); } @@ -2794,8 +2811,7 @@ void FullCodeGenerator::VisitCallRuntime(CallRuntime* expr) { if (expr->is_jsruntime()) { // Call the JS runtime function. __ mov(r2, Operand(expr->name())); - Handle<Code> ic = CodeGenerator::ComputeCallInitialize(arg_count, - NOT_IN_LOOP); + Handle<Code> ic = StubCache::ComputeCallInitialize(arg_count, NOT_IN_LOOP); EmitCallIC(ic, RelocInfo::CODE_TARGET); // Restore context register. __ ldr(cp, MemOperand(fp, StandardFrameConstants::kContextOffset)); @@ -2832,7 +2848,7 @@ void FullCodeGenerator::VisitUnaryOperation(UnaryOperation* expr) { VisitForStackValue(prop->obj()); VisitForStackValue(prop->key()); } else if (var->is_global()) { - __ ldr(r1, CodeGenerator::GlobalObject()); + __ ldr(r1, GlobalObjectOperand()); __ mov(r0, Operand(var->name())); __ Push(r1, r0); } else { @@ -3098,7 +3114,7 @@ void FullCodeGenerator::VisitForTypeofValue(Expression* expr) { VariableProxy* proxy = expr->AsVariableProxy(); if (proxy != NULL && !proxy->var()->is_this() && proxy->var()->is_global()) { Comment cmnt(masm_, "Global variable"); - __ ldr(r0, CodeGenerator::GlobalObject()); + __ ldr(r0, GlobalObjectOperand()); __ mov(r2, Operand(proxy->name())); Handle<Code> ic(Builtins::builtin(Builtins::LoadIC_Initialize)); // Use a regular load, not a contextual load, to avoid a reference diff --git a/src/arm/ic-arm.cc b/src/arm/ic-arm.cc index 4c1f9835..ef7cf6af 100644 --- a/src/arm/ic-arm.cc +++ b/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<Address>(map)); return true; @@ -971,8 +980,41 @@ bool LoadIC::PatchInlinedContextualLoad(Address address, Object* map, Object* cell, bool is_dont_delete) { - // TODO(<bug#>): 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<Address>(map)); + // Patch the cell address. + Assembler::set_target_address_at(ldr_cell_instr_address, + reinterpret_cast<Address>(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/src/arm/macro-assembler-arm.cc b/src/arm/macro-assembler-arm.cc index d2c22af5..ea85c794 100644 --- a/src/arm/macro-assembler-arm.cc +++ b/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/src/arm/macro-assembler-arm.h b/src/arm/macro-assembler-arm.h index 48a80599..8bd134c3 100644 --- a/src/arm/macro-assembler-arm.h +++ b/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<type>, r<type> (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 <n> 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 @@ -727,6 +769,16 @@ class CodePatcher { // ----------------------------------------------------------------------------- // Static helper functions. +static MemOperand ContextOperand(Register context, int index) { + return MemOperand(context, Context::SlotOffset(index)); +} + + +static inline MemOperand GlobalObjectOperand() { + return ContextOperand(cp, Context::GLOBAL_INDEX); +} + + #ifdef GENERATED_CODE_COVERAGE #define CODE_COVERAGE_STRINGIFY(x) #x #define CODE_COVERAGE_TOSTRING(x) CODE_COVERAGE_STRINGIFY(x) diff --git a/src/arm/stub-cache-arm.cc b/src/arm/stub-cache-arm.cc index 5e29c2e4..0a5eac27 100644 --- a/src/arm/stub-cache-arm.cc +++ b/src/arm/stub-cache-arm.cc @@ -598,8 +598,8 @@ static void GenerateFastApiCall(MacroAssembler* masm, int argc) { // Get the function and setup the context. JSFunction* function = optimization.constant_function(); - __ mov(r7, Operand(Handle<JSFunction>(function))); - __ ldr(cp, FieldMemOperand(r7, JSFunction::kContextOffset)); + __ mov(r5, Operand(Handle<JSFunction>(function))); + __ ldr(cp, FieldMemOperand(r5, JSFunction::kContextOffset)); // Pass the additional arguments FastHandleApiCall expects. bool info_loaded = false; @@ -607,18 +607,18 @@ static void GenerateFastApiCall(MacroAssembler* masm, if (Heap::InNewSpace(callback)) { info_loaded = true; __ Move(r0, Handle<CallHandlerInfo>(optimization.api_call_info())); - __ ldr(r6, FieldMemOperand(r0, CallHandlerInfo::kCallbackOffset)); + __ ldr(r7, FieldMemOperand(r0, CallHandlerInfo::kCallbackOffset)); } else { - __ Move(r6, Handle<Object>(callback)); + __ Move(r7, Handle<Object>(callback)); } Object* call_data = optimization.api_call_info()->data(); if (Heap::InNewSpace(call_data)) { if (!info_loaded) { __ Move(r0, Handle<CallHandlerInfo>(optimization.api_call_info())); } - __ ldr(r5, FieldMemOperand(r0, CallHandlerInfo::kDataOffset)); + __ ldr(r6, FieldMemOperand(r0, CallHandlerInfo::kDataOffset)); } else { - __ Move(r5, Handle<Object>(call_data)); + __ Move(r6, Handle<Object>(call_data)); } __ add(sp, sp, Operand(1 * kPointerSize)); @@ -1082,10 +1082,9 @@ bool StubCompiler::GenerateLoadCallback(JSObject* object, // Push the arguments on the JS stack of the caller. __ push(receiver); // Receiver. - __ push(reg); // Holder. - __ mov(ip, Operand(Handle<AccessorInfo>(callback))); // callback data - __ ldr(reg, FieldMemOperand(ip, AccessorInfo::kDataOffset)); - __ Push(ip, reg, name_reg); + __ mov(scratch3, Operand(Handle<AccessorInfo>(callback))); // callback data + __ ldr(ip, FieldMemOperand(scratch3, AccessorInfo::kDataOffset)); + __ Push(reg, ip, scratch3, name_reg); // Do tail-call to the runtime system. ExternalReference load_callback_property = @@ -1208,15 +1207,15 @@ void StubCompiler::GenerateLoadInterceptor(JSObject* object, // holder_reg is either receiver or scratch1. if (!receiver.is(holder_reg)) { ASSERT(scratch1.is(holder_reg)); - __ Push(receiver, holder_reg, scratch2); - __ ldr(scratch1, - FieldMemOperand(holder_reg, AccessorInfo::kDataOffset)); - __ Push(scratch1, name_reg); + __ Push(receiver, holder_reg); + __ ldr(scratch3, + FieldMemOperand(scratch2, AccessorInfo::kDataOffset)); + __ Push(scratch3, scratch2, name_reg); } else { __ push(receiver); - __ ldr(scratch1, - FieldMemOperand(holder_reg, AccessorInfo::kDataOffset)); - __ Push(holder_reg, scratch2, scratch1, name_reg); + __ ldr(scratch3, + FieldMemOperand(scratch2, AccessorInfo::kDataOffset)); + __ Push(holder_reg, scratch3, scratch2, name_reg); } ExternalReference ref = @@ -1360,10 +1359,11 @@ MaybeObject* CallStubCompiler::CompileArrayPushCall(Object* object, // ----------- S t a t e ------------- // -- r2 : name // -- lr : return address + // -- sp[(argc - n - 1) * 4] : arg[n] (zero-based) + // -- ... + // -- sp[argc * 4] : receiver // ----------------------------------- - // TODO(639): faster implementation. - // If object is not an array, bail out to regular call. if (!object->IsJSArray() || cell != NULL) return Heap::undefined_value(); @@ -1371,20 +1371,133 @@ MaybeObject* CallStubCompiler::CompileArrayPushCall(Object* object, GenerateNameCheck(name, &miss); + Register receiver = r1; + // Get the receiver from the stack const int argc = arguments().immediate(); - __ ldr(r1, MemOperand(sp, argc * kPointerSize)); + __ ldr(receiver, MemOperand(sp, argc * kPointerSize)); // Check that the receiver isn't a smi. - __ tst(r1, Operand(kSmiTagMask)); - __ b(eq, &miss); + __ BranchOnSmi(receiver, &miss); // Check that the maps haven't changed. - CheckPrototypes(JSObject::cast(object), r1, holder, r3, r0, r4, name, &miss); + CheckPrototypes(JSObject::cast(object), receiver, + holder, r3, r0, r4, name, &miss); - __ TailCallExternalReference(ExternalReference(Builtins::c_ArrayPush), - argc + 1, - 1); + if (argc == 0) { + // Nothing to do, just return the length. + __ ldr(r0, FieldMemOperand(receiver, JSArray::kLengthOffset)); + __ Drop(argc + 1); + __ Ret(); + } else { + Label call_builtin; + + Register elements = r3; + Register end_elements = r5; + + // Get the elements array of the object. + __ ldr(elements, FieldMemOperand(receiver, JSArray::kElementsOffset)); + + // Check that the elements are in fast mode and writable. + __ CheckMap(elements, r0, + Heap::kFixedArrayMapRootIndex, &call_builtin, true); + + if (argc == 1) { // Otherwise fall through to call the builtin. + Label exit, with_write_barrier, attempt_to_grow_elements; + + // Get the array's length into r0 and calculate new length. + __ ldr(r0, FieldMemOperand(receiver, JSArray::kLengthOffset)); + STATIC_ASSERT(kSmiTagSize == 1); + STATIC_ASSERT(kSmiTag == 0); + __ add(r0, r0, Operand(Smi::FromInt(argc))); + + // Get the element's length. + __ ldr(r4, FieldMemOperand(elements, FixedArray::kLengthOffset)); + + // Check if we could survive without allocation. + __ cmp(r0, r4); + __ b(gt, &attempt_to_grow_elements); + + // Save new length. + __ str(r0, FieldMemOperand(receiver, JSArray::kLengthOffset)); + + // Push the element. + __ ldr(r4, MemOperand(sp, (argc - 1) * kPointerSize)); + // We may need a register containing the address end_elements below, + // so write back the value in end_elements. + __ add(end_elements, elements, + Operand(r0, LSL, kPointerSizeLog2 - kSmiTagSize)); + const int kEndElementsOffset = + FixedArray::kHeaderSize - kHeapObjectTag - argc * kPointerSize; + __ str(r4, MemOperand(end_elements, kEndElementsOffset, PreIndex)); + + // Check for a smi. + __ BranchOnNotSmi(r4, &with_write_barrier); + __ bind(&exit); + __ Drop(argc + 1); + __ Ret(); + + __ bind(&with_write_barrier); + __ InNewSpace(elements, r4, eq, &exit); + __ RecordWriteHelper(elements, end_elements, r4); + __ Drop(argc + 1); + __ Ret(); + + __ bind(&attempt_to_grow_elements); + // r0: array's length + 1. + // r4: elements' length. + + if (!FLAG_inline_new) { + __ b(&call_builtin); + } + + ExternalReference new_space_allocation_top = + ExternalReference::new_space_allocation_top_address(); + ExternalReference new_space_allocation_limit = + ExternalReference::new_space_allocation_limit_address(); + + const int kAllocationDelta = 4; + // Load top and check if it is the end of elements. + __ add(end_elements, elements, + Operand(r0, LSL, kPointerSizeLog2 - kSmiTagSize)); + __ add(end_elements, end_elements, Operand(kEndElementsOffset)); + __ mov(r7, Operand(new_space_allocation_top)); + __ ldr(r6, MemOperand(r7)); + __ cmp(end_elements, r6); + __ b(ne, &call_builtin); + + __ mov(r9, Operand(new_space_allocation_limit)); + __ ldr(r9, MemOperand(r9)); + __ add(r6, r6, Operand(kAllocationDelta * kPointerSize)); + __ cmp(r6, r9); + __ b(hi, &call_builtin); + + // We fit and could grow elements. + // Update new_space_allocation_top. + __ str(r6, MemOperand(r7)); + // Push the argument. + __ ldr(r6, MemOperand(sp, (argc - 1) * kPointerSize)); + __ str(r6, MemOperand(end_elements)); + // Fill the rest with holes. + __ LoadRoot(r6, Heap::kTheHoleValueRootIndex); + for (int i = 1; i < kAllocationDelta; i++) { + __ str(r6, MemOperand(end_elements, i * kPointerSize)); + } + + // Update elements' and array's sizes. + __ str(r0, FieldMemOperand(receiver, JSArray::kLengthOffset)); + __ add(r4, r4, Operand(Smi::FromInt(kAllocationDelta))); + __ str(r4, FieldMemOperand(elements, FixedArray::kLengthOffset)); + + // Elements are in new space, so write barrier is not required. + __ Drop(argc + 1); + __ Ret(); + } + __ bind(&call_builtin); + __ TailCallExternalReference(ExternalReference(Builtins::c_ArrayPush), + argc + 1, + 1); + } // Handle call cache miss. __ bind(&miss); @@ -1406,28 +1519,68 @@ MaybeObject* CallStubCompiler::CompileArrayPopCall(Object* object, // ----------- S t a t e ------------- // -- r2 : name // -- lr : return address + // -- sp[(argc - n - 1) * 4] : arg[n] (zero-based) + // -- ... + // -- sp[argc * 4] : receiver // ----------------------------------- - // TODO(642): faster implementation. - // If object is not an array, bail out to regular call. if (!object->IsJSArray() || cell != NULL) return Heap::undefined_value(); - Label miss; + Label miss, return_undefined, call_builtin; + + Register receiver = r1; + Register elements = r3; GenerateNameCheck(name, &miss); // Get the receiver from the stack const int argc = arguments().immediate(); - __ ldr(r1, MemOperand(sp, argc * kPointerSize)); + __ ldr(receiver, MemOperand(sp, argc * kPointerSize)); // Check that the receiver isn't a smi. - __ tst(r1, Operand(kSmiTagMask)); - __ b(eq, &miss); + __ BranchOnSmi(receiver, &miss); // Check that the maps haven't changed. - CheckPrototypes(JSObject::cast(object), r1, holder, r3, r0, r4, name, &miss); + CheckPrototypes(JSObject::cast(object), + receiver, holder, elements, r4, r0, name, &miss); + + // Get the elements array of the object. + __ ldr(elements, FieldMemOperand(receiver, JSArray::kElementsOffset)); + // Check that the elements are in fast mode and writable. + __ CheckMap(elements, r0, Heap::kFixedArrayMapRootIndex, &call_builtin, true); + + // Get the array's length into r4 and calculate new length. + __ ldr(r4, FieldMemOperand(receiver, JSArray::kLengthOffset)); + __ sub(r4, r4, Operand(Smi::FromInt(1)), SetCC); + __ b(lt, &return_undefined); + + // Get the last element. + __ LoadRoot(r6, Heap::kTheHoleValueRootIndex); + STATIC_ASSERT(kSmiTagSize == 1); + STATIC_ASSERT(kSmiTag == 0); + // We can't address the last element in one operation. Compute the more + // expensive shift first, and use an offset later on. + __ add(elements, elements, Operand(r4, LSL, kPointerSizeLog2 - kSmiTagSize)); + __ ldr(r0, MemOperand(elements, FixedArray::kHeaderSize - kHeapObjectTag)); + __ cmp(r0, r6); + __ b(eq, &call_builtin); + + // Set the array's length. + __ str(r4, FieldMemOperand(receiver, JSArray::kLengthOffset)); + + // Fill with the hole. + __ str(r6, MemOperand(elements, FixedArray::kHeaderSize - kHeapObjectTag)); + __ Drop(argc + 1); + __ Ret(); + + __ bind(&return_undefined); + __ LoadRoot(r0, Heap::kUndefinedValueRootIndex); + __ Drop(argc + 1); + __ Ret(); + + __ bind(&call_builtin); __ TailCallExternalReference(ExternalReference(Builtins::c_ArrayPop), argc + 1, 1); @@ -1676,8 +1829,143 @@ MaybeObject* CallStubCompiler::CompileMathFloorCall(Object* object, JSGlobalPropertyCell* cell, JSFunction* function, String* name) { - // TODO(872): implement this. - return Heap::undefined_value(); + // ----------- S t a t e ------------- + // -- r2 : function name + // -- lr : return address + // -- sp[(argc - n - 1) * 4] : arg[n] (zero-based) + // -- ... + // -- sp[argc * 4] : receiver + // ----------------------------------- + + if (!CpuFeatures::IsSupported(VFP3)) return Heap::undefined_value(); + CpuFeatures::Scope scope_vfp3(VFP3); + + const int argc = arguments().immediate(); + + // If the object is not a JSObject or we got an unexpected number of + // arguments, bail out to the regular call. + if (!object->IsJSObject() || argc != 1) return Heap::undefined_value(); + + Label miss, slow; + GenerateNameCheck(name, &miss); + + if (cell == NULL) { + __ ldr(r1, MemOperand(sp, 1 * kPointerSize)); + + STATIC_ASSERT(kSmiTag == 0); + __ BranchOnSmi(r1, &miss); + + CheckPrototypes(JSObject::cast(object), r1, holder, r0, r3, r4, name, + &miss); + } else { + ASSERT(cell->value() == function); + GenerateGlobalReceiverCheck(JSObject::cast(object), holder, name, &miss); + GenerateLoadFunctionFromCell(cell, function, &miss); + } + + // Load the (only) argument into r0. + __ ldr(r0, MemOperand(sp, 0 * kPointerSize)); + + // If the argument is a smi, just return. + STATIC_ASSERT(kSmiTag == 0); + __ tst(r0, Operand(kSmiTagMask)); + __ Drop(argc + 1, eq); + __ Ret(eq); + + __ CheckMap(r0, r1, Heap::kHeapNumberMapRootIndex, &slow, true); + + Label wont_fit_smi, no_vfp_exception, restore_fpscr_and_return; + + // If vfp3 is enabled, we use the fpu rounding with the RM (round towards + // minus infinity) mode. + + // Load the HeapNumber value. + // We will need access to the value in the core registers, so we load it + // with ldrd and move it to the fpu. It also spares a sub instruction for + // updating the HeapNumber value address, as vldr expects a multiple + // of 4 offset. + __ Ldrd(r4, r5, FieldMemOperand(r0, HeapNumber::kValueOffset)); + __ vmov(d1, r4, r5); + + // Backup FPSCR. + __ vmrs(r3); + // Set custom FPCSR: + // - Set rounding mode to "Round towards Minus Infinity" + // (ie bits [23:22] = 0b10). + // - Clear vfp cumulative exception flags (bits [3:0]). + // - Make sure Flush-to-zero mode control bit is unset (bit 22). + __ bic(r9, r3, + Operand(kVFPExceptionMask | kVFPRoundingModeMask | kVFPFlushToZeroMask)); + __ orr(r9, r9, Operand(kVFPRoundToMinusInfinityBits)); + __ vmsr(r9); + + // Convert the argument to an integer. + __ vcvt_s32_f64(s0, d1, Assembler::FPSCRRounding, al); + + // Use vcvt latency to start checking for special cases. + // Get the argument exponent and clear the sign bit. + __ bic(r6, r5, Operand(HeapNumber::kSignMask)); + __ mov(r6, Operand(r6, LSR, HeapNumber::kMantissaBitsInTopWord)); + + // Retrieve FPSCR and check for vfp exceptions. + __ vmrs(r9); + __ tst(r9, Operand(kVFPExceptionMask)); + __ b(&no_vfp_exception, eq); + + // Check for NaN, Infinity, and -Infinity. + // They are invariant through a Math.Floor call, so just + // return the original argument. + __ sub(r7, r6, Operand(HeapNumber::kExponentMask + >> HeapNumber::kMantissaBitsInTopWord), SetCC); + __ b(&restore_fpscr_and_return, eq); + // We had an overflow or underflow in the conversion. Check if we + // have a big exponent. + __ cmp(r7, Operand(HeapNumber::kMantissaBits)); + // If greater or equal, the argument is already round and in r0. + __ b(&restore_fpscr_and_return, ge); + __ b(&slow); + + __ bind(&no_vfp_exception); + // Move the result back to general purpose register r0. + __ vmov(r0, s0); + // Check if the result fits into a smi. + __ add(r1, r0, Operand(0x40000000), SetCC); + __ b(&wont_fit_smi, mi); + // Tag the result. + STATIC_ASSERT(kSmiTag == 0); + __ mov(r0, Operand(r0, LSL, kSmiTagSize)); + + // Check for -0. + __ cmp(r0, Operand(0)); + __ b(&restore_fpscr_and_return, ne); + // r5 already holds the HeapNumber exponent. + __ tst(r5, Operand(HeapNumber::kSignMask)); + // If our HeapNumber is negative it was -0, so load its address and return. + // Else r0 is loaded with 0, so we can also just return. + __ ldr(r0, MemOperand(sp, 0 * kPointerSize), ne); + + __ bind(&restore_fpscr_and_return); + // Restore FPSCR and return. + __ vmsr(r3); + __ Drop(argc + 1); + __ Ret(); + + __ bind(&wont_fit_smi); + __ bind(&slow); + // Restore FPCSR and fall to slow case. + __ vmsr(r3); + + // Tail call the full function. We do not have to patch the receiver + // because the function makes no use of it. + __ InvokeFunction(function, arguments(), JUMP_FUNCTION); + + __ bind(&miss); + // r2: function name. + MaybeObject* obj = GenerateMissBranch(); + if (obj->IsFailure()) return obj; + + // Return the generated code. + return (cell == NULL) ? GetCode(function) : GetCode(NORMAL, name); } @@ -2537,7 +2825,7 @@ MaybeObject* KeyedLoadStubCompiler::CompileLoadStringLength(String* name) { // -- r1 : receiver // ----------------------------------- Label miss; - __ IncrementCounter(&Counters::keyed_load_string_length, 1, r1, r3); + __ IncrementCounter(&Counters::keyed_load_string_length, 1, r2, r3); // Check the key is the cached one. __ cmp(r0, Operand(Handle<String>(name))); @@ -2545,7 +2833,7 @@ MaybeObject* KeyedLoadStubCompiler::CompileLoadStringLength(String* name) { GenerateLoadStringLength(masm(), r1, r2, r3, &miss); __ bind(&miss); - __ DecrementCounter(&Counters::keyed_load_string_length, 1, r1, r3); + __ DecrementCounter(&Counters::keyed_load_string_length, 1, r2, r3); GenerateLoadMiss(masm(), Code::KEYED_LOAD_IC); @@ -2553,13 +2841,23 @@ MaybeObject* KeyedLoadStubCompiler::CompileLoadStringLength(String* name) { } -// TODO(1224671): implement the fast case. MaybeObject* KeyedLoadStubCompiler::CompileLoadFunctionPrototype(String* name) { // ----------- S t a t e ------------- // -- lr : return address // -- r0 : key // -- r1 : receiver // ----------------------------------- + Label miss; + + __ IncrementCounter(&Counters::keyed_load_function_prototype, 1, r2, r3); + + // Check the name hasn't changed. + __ cmp(r0, Operand(Handle<String>(name))); + __ b(ne, &miss); + + GenerateLoadFunctionPrototype(masm(), r1, r2, r3, &miss); + __ bind(&miss); + __ DecrementCounter(&Counters::keyed_load_function_prototype, 1, r2, r3); GenerateLoadMiss(masm(), Code::KEYED_LOAD_IC); return GetCode(CALLBACKS, name); @@ -2604,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 @@ -2689,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; @@ -2713,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/src/arm/virtual-frame-arm.cc b/src/arm/virtual-frame-arm.cc index da76edf3..45f48767 100644 --- a/src/arm/virtual-frame-arm.cc +++ b/src/arm/virtual-frame-arm.cc @@ -245,18 +245,15 @@ void VirtualFrame::AllocateStackSlots() { __ LoadRoot(r2, Heap::kStackLimitRootIndex); } // Check the stack for overflow or a break request. - // Put the lr setup instruction in the delay slot. The kInstrSize is added - // to the implicit 8 byte offset that always applies to operations with pc - // and gives a return address 12 bytes down. - masm()->add(lr, pc, Operand(Assembler::kInstrSize)); masm()->cmp(sp, Operand(r2)); StackCheckStub stub; // Call the stub if lower. - masm()->mov(pc, + masm()->mov(ip, Operand(reinterpret_cast<intptr_t>(stub.GetCode().location()), RelocInfo::CODE_TARGET), LeaveCC, lo); + masm()->Call(ip, lo); } diff --git a/src/array.js b/src/array.js index b2ebece9..5ecf5e30 100644 --- a/src/array.js +++ b/src/array.js @@ -364,6 +364,10 @@ function ArrayJoin(separator) { } else if (!IS_STRING(separator)) { separator = ToString(separator); } + + var result = %_FastAsciiArrayJoin(this, separator); + if (typeof result != "undefined") return result; + var length = TO_UINT32(this.length); return Join(this, length, separator, ConvertToString); } @@ -1416,7 +1416,8 @@ class FunctionLiteral: public Expression { contains_loops_(contains_loops), function_token_position_(RelocInfo::kNoPosition), inferred_name_(Heap::empty_string()), - try_full_codegen_(false) { + try_full_codegen_(false), + pretenure_(false) { #ifdef DEBUG already_compiled_ = false; #endif @@ -1459,6 +1460,9 @@ class FunctionLiteral: public Expression { bool try_full_codegen() { return try_full_codegen_; } void set_try_full_codegen(bool flag) { try_full_codegen_ = flag; } + bool pretenure() { return pretenure_; } + void set_pretenure(bool value) { pretenure_ = value; } + #ifdef DEBUG void mark_as_compiled() { ASSERT(!already_compiled_); @@ -1482,6 +1486,7 @@ class FunctionLiteral: public Expression { int function_token_position_; Handle<String> inferred_name_; bool try_full_codegen_; + bool pretenure_; #ifdef DEBUG bool already_compiled_; #endif diff --git a/src/bignum-dtoa.cc b/src/bignum-dtoa.cc new file mode 100644 index 00000000..088dd79f --- /dev/null +++ b/src/bignum-dtoa.cc @@ -0,0 +1,655 @@ +// Copyright 2010 the V8 project authors. All rights reserved. +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following +// disclaimer in the documentation and/or other materials provided +// with the distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +#include <math.h> + +#include "v8.h" +#include "bignum-dtoa.h" + +#include "bignum.h" +#include "double.h" + +namespace v8 { +namespace internal { + +static int NormalizedExponent(uint64_t significand, int exponent) { + ASSERT(significand != 0); + while ((significand & Double::kHiddenBit) == 0) { + significand = significand << 1; + exponent = exponent - 1; + } + return exponent; +} + + +// Forward declarations: +// Returns an estimation of k such that 10^(k-1) <= v < 10^k. +static int EstimatePower(int exponent); +// Computes v / 10^estimated_power exactly, as a ratio of two bignums, numerator +// and denominator. +static void InitialScaledStartValues(double v, + int estimated_power, + bool need_boundary_deltas, + Bignum* numerator, + Bignum* denominator, + Bignum* delta_minus, + Bignum* delta_plus); +// Multiplies numerator/denominator so that its values lies in the range 1-10. +// Returns decimal_point s.t. +// v = numerator'/denominator' * 10^(decimal_point-1) +// where numerator' and denominator' are the values of numerator and +// denominator after the call to this function. +static void FixupMultiply10(int estimated_power, bool is_even, + int* decimal_point, + Bignum* numerator, Bignum* denominator, + Bignum* delta_minus, Bignum* delta_plus); +// Generates digits from the left to the right and stops when the generated +// digits yield the shortest decimal representation of v. +static void GenerateShortestDigits(Bignum* numerator, Bignum* denominator, + Bignum* delta_minus, Bignum* delta_plus, + bool is_even, + Vector<char> buffer, int* length); +// Generates 'requested_digits' after the decimal point. +static void BignumToFixed(int requested_digits, int* decimal_point, + Bignum* numerator, Bignum* denominator, + Vector<char>(buffer), int* length); +// Generates 'count' digits of numerator/denominator. +// Once 'count' digits have been produced rounds the result depending on the +// remainder (remainders of exactly .5 round upwards). Might update the +// decimal_point when rounding up (for example for 0.9999). +static void GenerateCountedDigits(int count, int* decimal_point, + Bignum* numerator, Bignum* denominator, + Vector<char>(buffer), int* length); + + +void BignumDtoa(double v, BignumDtoaMode mode, int requested_digits, + Vector<char> buffer, int* length, int* decimal_point) { + ASSERT(v > 0); + ASSERT(!Double(v).IsSpecial()); + uint64_t significand = Double(v).Significand(); + bool is_even = (significand & 1) == 0; + int exponent = Double(v).Exponent(); + int normalized_exponent = NormalizedExponent(significand, exponent); + // estimated_power might be too low by 1. + int estimated_power = EstimatePower(normalized_exponent); + + // Shortcut for Fixed. + // The requested digits correspond to the digits after the point. If the + // number is much too small, then there is no need in trying to get any + // digits. + if (mode == BIGNUM_DTOA_FIXED && -estimated_power - 1 > requested_digits) { + buffer[0] = '\0'; + *length = 0; + // Set decimal-point to -requested_digits. This is what Gay does. + // Note that it should not have any effect anyways since the string is + // empty. + *decimal_point = -requested_digits; + return; + } + + Bignum numerator; + Bignum denominator; + Bignum delta_minus; + Bignum delta_plus; + // Make sure the bignum can grow large enough. The smallest double equals + // 4e-324. In this case the denominator needs fewer than 324*4 binary digits. + // The maximum double is 1.7976931348623157e308 which needs fewer than + // 308*4 binary digits. + ASSERT(Bignum::kMaxSignificantBits >= 324*4); + bool need_boundary_deltas = (mode == BIGNUM_DTOA_SHORTEST); + InitialScaledStartValues(v, estimated_power, need_boundary_deltas, + &numerator, &denominator, + &delta_minus, &delta_plus); + // We now have v = (numerator / denominator) * 10^estimated_power. + FixupMultiply10(estimated_power, is_even, decimal_point, + &numerator, &denominator, + &delta_minus, &delta_plus); + // We now have v = (numerator / denominator) * 10^(decimal_point-1), and + // 1 <= (numerator + delta_plus) / denominator < 10 + switch (mode) { + case BIGNUM_DTOA_SHORTEST: + GenerateShortestDigits(&numerator, &denominator, + &delta_minus, &delta_plus, + is_even, buffer, length); + break; + case BIGNUM_DTOA_FIXED: + BignumToFixed(requested_digits, decimal_point, + &numerator, &denominator, + buffer, length); + break; + case BIGNUM_DTOA_PRECISION: + GenerateCountedDigits(requested_digits, decimal_point, + &numerator, &denominator, + buffer, length); + break; + default: + UNREACHABLE(); + } + buffer[*length] = '\0'; +} + + +// The procedure starts generating digits from the left to the right and stops +// when the generated digits yield the shortest decimal representation of v. A +// decimal representation of v is a number lying closer to v than to any other +// double, so it converts to v when read. +// +// This is true if d, the decimal representation, is between m- and m+, the +// upper and lower boundaries. d must be strictly between them if !is_even. +// m- := (numerator - delta_minus) / denominator +// m+ := (numerator + delta_plus) / denominator +// +// Precondition: 0 <= (numerator+delta_plus) / denominator < 10. +// If 1 <= (numerator+delta_plus) / denominator < 10 then no leading 0 digit +// will be produced. This should be the standard precondition. +static void GenerateShortestDigits(Bignum* numerator, Bignum* denominator, + Bignum* delta_minus, Bignum* delta_plus, + bool is_even, + Vector<char> buffer, int* length) { + // Small optimization: if delta_minus and delta_plus are the same just reuse + // one of the two bignums. + if (Bignum::Equal(*delta_minus, *delta_plus)) { + delta_plus = delta_minus; + } + *length = 0; + while (true) { + uint16_t digit; + digit = numerator->DivideModuloIntBignum(*denominator); + ASSERT(digit <= 9); // digit is a uint16_t and therefore always positive. + // digit = numerator / denominator (integer division). + // numerator = numerator % denominator. + buffer[(*length)++] = digit + '0'; + + // Can we stop already? + // If the remainder of the division is less than the distance to the lower + // boundary we can stop. In this case we simply round down (discarding the + // remainder). + // Similarly we test if we can round up (using the upper boundary). + bool in_delta_room_minus; + bool in_delta_room_plus; + if (is_even) { + in_delta_room_minus = Bignum::LessEqual(*numerator, *delta_minus); + } else { + in_delta_room_minus = Bignum::Less(*numerator, *delta_minus); + } + if (is_even) { + in_delta_room_plus = + Bignum::PlusCompare(*numerator, *delta_plus, *denominator) >= 0; + } else { + in_delta_room_plus = + Bignum::PlusCompare(*numerator, *delta_plus, *denominator) > 0; + } + if (!in_delta_room_minus && !in_delta_room_plus) { + // Prepare for next iteration. + numerator->Times10(); + delta_minus->Times10(); + // We optimized delta_plus to be equal to delta_minus (if they share the + // same value). So don't multiply delta_plus if they point to the same + // object. + if (delta_minus != delta_plus) { + delta_plus->Times10(); + } + } else if (in_delta_room_minus && in_delta_room_plus) { + // Let's see if 2*numerator < denominator. + // If yes, then the next digit would be < 5 and we can round down. + int compare = Bignum::PlusCompare(*numerator, *numerator, *denominator); + if (compare < 0) { + // Remaining digits are less than .5. -> Round down (== do nothing). + } else if (compare > 0) { + // Remaining digits are more than .5 of denominator. -> Round up. + // Note that the last digit could not be a '9' as otherwise the whole + // loop would have stopped earlier. + // We still have an assert here in case the preconditions were not + // satisfied. + ASSERT(buffer[(*length) - 1] != '9'); + buffer[(*length) - 1]++; + } else { + // Halfway case. + // TODO(floitsch): need a way to solve half-way cases. + // For now let's round towards even (since this is what Gay seems to + // do). + + if ((buffer[(*length) - 1] - '0') % 2 == 0) { + // Round down => Do nothing. + } else { + ASSERT(buffer[(*length) - 1] != '9'); + buffer[(*length) - 1]++; + } + } + return; + } else if (in_delta_room_minus) { + // Round down (== do nothing). + return; + } else { // in_delta_room_plus + // Round up. + // Note again that the last digit could not be '9' since this would have + // stopped the loop earlier. + // We still have an ASSERT here, in case the preconditions were not + // satisfied. + ASSERT(buffer[(*length) -1] != '9'); + buffer[(*length) - 1]++; + return; + } + } +} + + +// Let v = numerator / denominator < 10. +// Then we generate 'count' digits of d = x.xxxxx... (without the decimal point) +// from left to right. Once 'count' digits have been produced we decide wether +// to round up or down. Remainders of exactly .5 round upwards. Numbers such +// as 9.999999 propagate a carry all the way, and change the +// exponent (decimal_point), when rounding upwards. +static void GenerateCountedDigits(int count, int* decimal_point, + Bignum* numerator, Bignum* denominator, + Vector<char>(buffer), int* length) { + ASSERT(count >= 0); + for (int i = 0; i < count - 1; ++i) { + uint16_t digit; + digit = numerator->DivideModuloIntBignum(*denominator); + ASSERT(digit <= 9); // digit is a uint16_t and therefore always positive. + // digit = numerator / denominator (integer division). + // numerator = numerator % denominator. + buffer[i] = digit + '0'; + // Prepare for next iteration. + numerator->Times10(); + } + // Generate the last digit. + uint16_t digit; + digit = numerator->DivideModuloIntBignum(*denominator); + if (Bignum::PlusCompare(*numerator, *numerator, *denominator) >= 0) { + digit++; + } + buffer[count - 1] = digit + '0'; + // Correct bad digits (in case we had a sequence of '9's). Propagate the + // carry until we hat a non-'9' or til we reach the first digit. + for (int i = count - 1; i > 0; --i) { + if (buffer[i] != '0' + 10) break; + buffer[i] = '0'; + buffer[i - 1]++; + } + if (buffer[0] == '0' + 10) { + // Propagate a carry past the top place. + buffer[0] = '1'; + (*decimal_point)++; + } + *length = count; +} + + +// Generates 'requested_digits' after the decimal point. It might omit +// trailing '0's. If the input number is too small then no digits at all are +// generated (ex.: 2 fixed digits for 0.00001). +// +// Input verifies: 1 <= (numerator + delta) / denominator < 10. +static void BignumToFixed(int requested_digits, int* decimal_point, + Bignum* numerator, Bignum* denominator, + Vector<char>(buffer), int* length) { + // Note that we have to look at more than just the requested_digits, since + // a number could be rounded up. Example: v=0.5 with requested_digits=0. + // Even though the power of v equals 0 we can't just stop here. + if (-(*decimal_point) > requested_digits) { + // The number is definitively too small. + // Ex: 0.001 with requested_digits == 1. + // Set decimal-point to -requested_digits. This is what Gay does. + // Note that it should not have any effect anyways since the string is + // empty. + *decimal_point = -requested_digits; + *length = 0; + return; + } else if (-(*decimal_point) == requested_digits) { + // We only need to verify if the number rounds down or up. + // Ex: 0.04 and 0.06 with requested_digits == 1. + ASSERT(*decimal_point == -requested_digits); + // Initially the fraction lies in range (1, 10]. Multiply the denominator + // by 10 so that we can compare more easily. + denominator->Times10(); + if (Bignum::PlusCompare(*numerator, *numerator, *denominator) >= 0) { + // If the fraction is >= 0.5 then we have to include the rounded + // digit. + buffer[0] = '1'; + *length = 1; + (*decimal_point)++; + } else { + // Note that we caught most of similar cases earlier. + *length = 0; + } + return; + } else { + // The requested digits correspond to the digits after the point. + // The variable 'needed_digits' includes the digits before the point. + int needed_digits = (*decimal_point) + requested_digits; + GenerateCountedDigits(needed_digits, decimal_point, + numerator, denominator, + buffer, length); + } +} + + +// Returns an estimation of k such that 10^(k-1) <= v < 10^k where +// v = f * 2^exponent and 2^52 <= f < 2^53. +// v is hence a normalized double with the given exponent. The output is an +// approximation for the exponent of the decimal approimation .digits * 10^k. +// +// The result might undershoot by 1 in which case 10^k <= v < 10^k+1. +// Note: this property holds for v's upper boundary m+ too. +// 10^k <= m+ < 10^k+1. +// (see explanation below). +// +// Examples: +// EstimatePower(0) => 16 +// EstimatePower(-52) => 0 +// +// Note: e >= 0 => EstimatedPower(e) > 0. No similar claim can be made for e<0. +static int EstimatePower(int exponent) { + // This function estimates log10 of v where v = f*2^e (with e == exponent). + // Note that 10^floor(log10(v)) <= v, but v <= 10^ceil(log10(v)). + // Note that f is bounded by its container size. Let p = 53 (the double's + // significand size). Then 2^(p-1) <= f < 2^p. + // + // Given that log10(v) == log2(v)/log2(10) and e+(len(f)-1) is quite close + // to log2(v) the function is simplified to (e+(len(f)-1)/log2(10)). + // The computed number undershoots by less than 0.631 (when we compute log3 + // and not log10). + // + // Optimization: since we only need an approximated result this computation + // can be performed on 64 bit integers. On x86/x64 architecture the speedup is + // not really measurable, though. + // + // Since we want to avoid overshooting we decrement by 1e10 so that + // floating-point imprecisions don't affect us. + // + // Explanation for v's boundary m+: the computation takes advantage of + // the fact that 2^(p-1) <= f < 2^p. Boundaries still satisfy this requirement + // (even for denormals where the delta can be much more important). + + const double k1Log10 = 0.30102999566398114; // 1/lg(10) + + // For doubles len(f) == 53 (don't forget the hidden bit). + const int kSignificandSize = 53; + double estimate = ceil((exponent + kSignificandSize - 1) * k1Log10 - 1e-10); + return static_cast<int>(estimate); +} + + +// See comments for InitialScaledStartValues. +static void InitialScaledStartValuesPositiveExponent( + double v, int estimated_power, bool need_boundary_deltas, + Bignum* numerator, Bignum* denominator, + Bignum* delta_minus, Bignum* delta_plus) { + // A positive exponent implies a positive power. + ASSERT(estimated_power >= 0); + // Since the estimated_power is positive we simply multiply the denominator + // by 10^estimated_power. + + // numerator = v. + numerator->AssignUInt64(Double(v).Significand()); + numerator->ShiftLeft(Double(v).Exponent()); + // denominator = 10^estimated_power. + denominator->AssignPowerUInt16(10, estimated_power); + + if (need_boundary_deltas) { + // Introduce a common denominator so that the deltas to the boundaries are + // integers. + denominator->ShiftLeft(1); + numerator->ShiftLeft(1); + // Let v = f * 2^e, then m+ - v = 1/2 * 2^e; With the common + // denominator (of 2) delta_plus equals 2^e. + delta_plus->AssignUInt16(1); + delta_plus->ShiftLeft(Double(v).Exponent()); + // Same for delta_minus (with adjustments below if f == 2^p-1). + delta_minus->AssignUInt16(1); + delta_minus->ShiftLeft(Double(v).Exponent()); + + // If the significand (without the hidden bit) is 0, then the lower + // boundary is closer than just half a ulp (unit in the last place). + // There is only one exception: if the next lower number is a denormal then + // the distance is 1 ulp. This cannot be the case for exponent >= 0 (but we + // have to test it in the other function where exponent < 0). + uint64_t v_bits = Double(v).AsUint64(); + if ((v_bits & Double::kSignificandMask) == 0) { + // The lower boundary is closer at half the distance of "normal" numbers. + // Increase the common denominator and adapt all but the delta_minus. + denominator->ShiftLeft(1); // *2 + numerator->ShiftLeft(1); // *2 + delta_plus->ShiftLeft(1); // *2 + } + } +} + + +// See comments for InitialScaledStartValues +static void InitialScaledStartValuesNegativeExponentPositivePower( + double v, int estimated_power, bool need_boundary_deltas, + Bignum* numerator, Bignum* denominator, + Bignum* delta_minus, Bignum* delta_plus) { + uint64_t significand = Double(v).Significand(); + int exponent = Double(v).Exponent(); + // v = f * 2^e with e < 0, and with estimated_power >= 0. + // This means that e is close to 0 (have a look at how estimated_power is + // computed). + + // numerator = significand + // since v = significand * 2^exponent this is equivalent to + // numerator = v * / 2^-exponent + numerator->AssignUInt64(significand); + // denominator = 10^estimated_power * 2^-exponent (with exponent < 0) + denominator->AssignPowerUInt16(10, estimated_power); + denominator->ShiftLeft(-exponent); + + if (need_boundary_deltas) { + // Introduce a common denominator so that the deltas to the boundaries are + // integers. + denominator->ShiftLeft(1); + numerator->ShiftLeft(1); + // Let v = f * 2^e, then m+ - v = 1/2 * 2^e; With the common + // denominator (of 2) delta_plus equals 2^e. + // Given that the denominator already includes v's exponent the distance + // to the boundaries is simply 1. + delta_plus->AssignUInt16(1); + // Same for delta_minus (with adjustments below if f == 2^p-1). + delta_minus->AssignUInt16(1); + + // If the significand (without the hidden bit) is 0, then the lower + // boundary is closer than just one ulp (unit in the last place). + // There is only one exception: if the next lower number is a denormal + // then the distance is 1 ulp. Since the exponent is close to zero + // (otherwise estimated_power would have been negative) this cannot happen + // here either. + uint64_t v_bits = Double(v).AsUint64(); + if ((v_bits & Double::kSignificandMask) == 0) { + // The lower boundary is closer at half the distance of "normal" numbers. + // Increase the denominator and adapt all but the delta_minus. + denominator->ShiftLeft(1); // *2 + numerator->ShiftLeft(1); // *2 + delta_plus->ShiftLeft(1); // *2 + } + } +} + + +// See comments for InitialScaledStartValues +static void InitialScaledStartValuesNegativeExponentNegativePower( + double v, int estimated_power, bool need_boundary_deltas, + Bignum* numerator, Bignum* denominator, + Bignum* delta_minus, Bignum* delta_plus) { + const uint64_t kMinimalNormalizedExponent = + V8_2PART_UINT64_C(0x00100000, 00000000); + uint64_t significand = Double(v).Significand(); + int exponent = Double(v).Exponent(); + // Instead of multiplying the denominator with 10^estimated_power we + // multiply all values (numerator and deltas) by 10^-estimated_power. + + // Use numerator as temporary container for power_ten. + Bignum* power_ten = numerator; + power_ten->AssignPowerUInt16(10, -estimated_power); + + if (need_boundary_deltas) { + // Since power_ten == numerator we must make a copy of 10^estimated_power + // before we complete the computation of the numerator. + // delta_plus = delta_minus = 10^estimated_power + delta_plus->AssignBignum(*power_ten); + delta_minus->AssignBignum(*power_ten); + } + + // numerator = significand * 2 * 10^-estimated_power + // since v = significand * 2^exponent this is equivalent to + // numerator = v * 10^-estimated_power * 2 * 2^-exponent. + // Remember: numerator has been abused as power_ten. So no need to assign it + // to itself. + ASSERT(numerator == power_ten); + numerator->MultiplyByUInt64(significand); + + // denominator = 2 * 2^-exponent with exponent < 0. + denominator->AssignUInt16(1); + denominator->ShiftLeft(-exponent); + + if (need_boundary_deltas) { + // Introduce a common denominator so that the deltas to the boundaries are + // integers. + numerator->ShiftLeft(1); + denominator->ShiftLeft(1); + // With this shift the boundaries have their correct value, since + // delta_plus = 10^-estimated_power, and + // delta_minus = 10^-estimated_power. + // These assignments have been done earlier. + + // The special case where the lower boundary is twice as close. + // This time we have to look out for the exception too. + uint64_t v_bits = Double(v).AsUint64(); + if ((v_bits & Double::kSignificandMask) == 0 && + // The only exception where a significand == 0 has its boundaries at + // "normal" distances: + (v_bits & Double::kExponentMask) != kMinimalNormalizedExponent) { + numerator->ShiftLeft(1); // *2 + denominator->ShiftLeft(1); // *2 + delta_plus->ShiftLeft(1); // *2 + } + } +} + + +// Let v = significand * 2^exponent. +// Computes v / 10^estimated_power exactly, as a ratio of two bignums, numerator +// and denominator. The functions GenerateShortestDigits and +// GenerateCountedDigits will then convert this ratio to its decimal +// representation d, with the required accuracy. +// Then d * 10^estimated_power is the representation of v. +// (Note: the fraction and the estimated_power might get adjusted before +// generating the decimal representation.) +// +// The initial start values consist of: +// - a scaled numerator: s.t. numerator/denominator == v / 10^estimated_power. +// - a scaled (common) denominator. +// optionally (used by GenerateShortestDigits to decide if it has the shortest +// decimal converting back to v): +// - v - m-: the distance to the lower boundary. +// - m+ - v: the distance to the upper boundary. +// +// v, m+, m-, and therefore v - m- and m+ - v all share the same denominator. +// +// Let ep == estimated_power, then the returned values will satisfy: +// v / 10^ep = numerator / denominator. +// v's boundarys m- and m+: +// m- / 10^ep == v / 10^ep - delta_minus / denominator +// m+ / 10^ep == v / 10^ep + delta_plus / denominator +// Or in other words: +// m- == v - delta_minus * 10^ep / denominator; +// m+ == v + delta_plus * 10^ep / denominator; +// +// Since 10^(k-1) <= v < 10^k (with k == estimated_power) +// or 10^k <= v < 10^(k+1) +// we then have 0.1 <= numerator/denominator < 1 +// or 1 <= numerator/denominator < 10 +// +// It is then easy to kickstart the digit-generation routine. +// +// The boundary-deltas are only filled if need_boundary_deltas is set. +static void InitialScaledStartValues(double v, + int estimated_power, + bool need_boundary_deltas, + Bignum* numerator, + Bignum* denominator, + Bignum* delta_minus, + Bignum* delta_plus) { + if (Double(v).Exponent() >= 0) { + InitialScaledStartValuesPositiveExponent( + v, estimated_power, need_boundary_deltas, + numerator, denominator, delta_minus, delta_plus); + } else if (estimated_power >= 0) { + InitialScaledStartValuesNegativeExponentPositivePower( + v, estimated_power, need_boundary_deltas, + numerator, denominator, delta_minus, delta_plus); + } else { + InitialScaledStartValuesNegativeExponentNegativePower( + v, estimated_power, need_boundary_deltas, + numerator, denominator, delta_minus, delta_plus); + } +} + + +// This routine multiplies numerator/denominator so that its values lies in the +// range 1-10. That is after a call to this function we have: +// 1 <= (numerator + delta_plus) /denominator < 10. +// Let numerator the input before modification and numerator' the argument +// after modification, then the output-parameter decimal_point is such that +// numerator / denominator * 10^estimated_power == +// numerator' / denominator' * 10^(decimal_point - 1) +// In some cases estimated_power was too low, and this is already the case. We +// then simply adjust the power so that 10^(k-1) <= v < 10^k (with k == +// estimated_power) but do not touch the numerator or denominator. +// Otherwise the routine multiplies the numerator and the deltas by 10. +static void FixupMultiply10(int estimated_power, bool is_even, + int* decimal_point, + Bignum* numerator, Bignum* denominator, + Bignum* delta_minus, Bignum* delta_plus) { + bool in_range; + if (is_even) { + // For IEEE doubles half-way cases (in decimal system numbers ending with 5) + // are rounded to the closest floating-point number with even significand. + in_range = Bignum::PlusCompare(*numerator, *delta_plus, *denominator) >= 0; + } else { + in_range = Bignum::PlusCompare(*numerator, *delta_plus, *denominator) > 0; + } + if (in_range) { + // Since numerator + delta_plus >= denominator we already have + // 1 <= numerator/denominator < 10. Simply update the estimated_power. + *decimal_point = estimated_power + 1; + } else { + *decimal_point = estimated_power; + numerator->Times10(); + if (Bignum::Equal(*delta_minus, *delta_plus)) { + delta_minus->Times10(); + delta_plus->AssignBignum(*delta_minus); + } else { + delta_minus->Times10(); + delta_plus->Times10(); + } + } +} + +} } // namespace v8::internal diff --git a/src/bignum-dtoa.h b/src/bignum-dtoa.h new file mode 100644 index 00000000..ea1acbbf --- /dev/null +++ b/src/bignum-dtoa.h @@ -0,0 +1,81 @@ +// Copyright 2010 the V8 project authors. All rights reserved. +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following +// disclaimer in the documentation and/or other materials provided +// with the distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +#ifndef V8_BIGNUM_DTOA_H_ +#define V8_BIGNUM_DTOA_H_ + +namespace v8 { +namespace internal { + +enum BignumDtoaMode { + // Return the shortest correct representation. + // For example the output of 0.299999999999999988897 is (the less accurate but + // correct) 0.3. + BIGNUM_DTOA_SHORTEST, + // Return a fixed number of digits after the decimal point. + // For instance fixed(0.1, 4) becomes 0.1000 + // If the input number is big, the output will be big. + BIGNUM_DTOA_FIXED, + // Return a fixed number of digits, no matter what the exponent is. + BIGNUM_DTOA_PRECISION +}; + +// Converts the given double 'v' to ascii. +// The result should be interpreted as buffer * 10^(point-length). +// The buffer will be null-terminated. +// +// The input v must be > 0 and different from NaN, and Infinity. +// +// The output depends on the given mode: +// - SHORTEST: produce the least amount of digits for which the internal +// identity requirement is still satisfied. If the digits are printed +// (together with the correct exponent) then reading this number will give +// 'v' again. The buffer will choose the representation that is closest to +// 'v'. If there are two at the same distance, than the number is round up. +// In this mode the 'requested_digits' parameter is ignored. +// - FIXED: produces digits necessary to print a given number with +// 'requested_digits' digits after the decimal point. The produced digits +// might be too short in which case the caller has to fill the gaps with '0's. +// Example: toFixed(0.001, 5) is allowed to return buffer="1", point=-2. +// Halfway cases are rounded up. The call toFixed(0.15, 2) thus returns +// buffer="2", point=0. +// Note: the length of the returned buffer has no meaning wrt the significance +// of its digits. That is, just because it contains '0's does not mean that +// any other digit would not satisfy the internal identity requirement. +// - PRECISION: produces 'requested_digits' where the first digit is not '0'. +// Even though the length of produced digits usually equals +// 'requested_digits', the function is allowed to return fewer digits, in +// which case the caller has to fill the missing digits with '0's. +// Halfway cases are again rounded up. +// 'BignumDtoa' expects the given buffer to be big enough to hold all digits +// and a terminating null-character. +void BignumDtoa(double v, BignumDtoaMode mode, int requested_digits, + Vector<char> buffer, int* length, int* point); + +} } // namespace v8::internal + +#endif // V8_BIGNUM_DTOA_H_ diff --git a/src/bootstrapper.cc b/src/bootstrapper.cc index 0e49966b..f60a975d 100644 --- a/src/bootstrapper.cc +++ b/src/bootstrapper.cc @@ -39,6 +39,8 @@ #include "objects-visiting.h" #include "snapshot.h" #include "stub-cache.h" +#include "extensions/externalize-string-extension.h" +#include "extensions/gc-extension.h" namespace v8 { namespace internal { @@ -137,6 +139,8 @@ Handle<String> Bootstrapper::NativesSourceLookup(int index) { void Bootstrapper::Initialize(bool create_heap_objects) { extensions_cache.Initialize(create_heap_objects); + GCExtension::Register(); + ExternalizeStringExtension::Register(); } diff --git a/src/builtins.cc b/src/builtins.cc index aede3020..e88ef6f0 100644 --- a/src/builtins.cc +++ b/src/builtins.cc @@ -1081,29 +1081,22 @@ BUILTIN(FastHandleApiCall) { ASSERT(!CalledAsConstructor()); const bool is_construct = false; - // We expect four more arguments: function, callback, call data, and holder. + // We expect four more arguments: callback, function, call data, and holder. const int args_length = args.length() - 4; ASSERT(args_length >= 0); - Handle<JSFunction> function = args.at<JSFunction>(args_length); - Object* callback_obj = args[args_length + 1]; - Handle<Object> data = args.at<Object>(args_length + 2); - Handle<JSObject> checked_holder = args.at<JSObject>(args_length + 3); - -#ifdef DEBUG - VerifyTypeCheck(checked_holder, function); -#endif - - CustomArguments custom; - v8::ImplementationUtilities::PrepareArgumentsData(custom.end(), - *data, *function, *checked_holder); + Object* callback_obj = args[args_length]; v8::Arguments new_args = v8::ImplementationUtilities::NewArguments( - custom.end(), + &args[args_length + 1], &args[0] - 1, args_length - 1, is_construct); +#ifdef DEBUG + VerifyTypeCheck(Utils::OpenHandle(*new_args.Holder()), + Utils::OpenHandle(*new_args.Callee())); +#endif HandleScope scope; Object* result; v8::Handle<v8::Value> value; diff --git a/src/checks.cc b/src/checks.cc index 1ab8802e..3c3d940b 100644 --- a/src/checks.cc +++ b/src/checks.cc @@ -107,3 +107,4 @@ namespace v8 { namespace internal { intptr_t HeapObjectTagMask() { return kHeapObjectTagMask; } } } // namespace v8::internal + diff --git a/src/checks.h b/src/checks.h index e0704774..d49f97f1 100644 --- a/src/checks.h +++ b/src/checks.h @@ -30,10 +30,7 @@ #include <string.h> -#include "../include/v8stdint.h" - extern "C" void V8_Fatal(const char* file, int line, const char* format, ...); -void API_Fatal(const char* location, const char* format, ...); // The FATAL, UNREACHABLE and UNIMPLEMENTED macros are useful during // development, but they should not be relied on in the final product. @@ -224,28 +221,6 @@ static inline void CheckNonEqualsHelper(const char* file, } -namespace v8 { - class Value; - template <class T> class Handle; -} - - -void CheckNonEqualsHelper(const char* file, - int line, - const char* unexpected_source, - v8::Handle<v8::Value> unexpected, - const char* value_source, - v8::Handle<v8::Value> value); - - -void CheckEqualsHelper(const char* file, - int line, - const char* expected_source, - v8::Handle<v8::Value> expected, - const char* value_source, - v8::Handle<v8::Value> value); - - #define CHECK_EQ(expected, value) CheckEqualsHelper(__FILE__, __LINE__, \ #expected, expected, #value, value) @@ -309,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<intptr_t>(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/src/code-stubs.cc b/src/code-stubs.cc index 787ec2a7..8b9198fb 100644 --- a/src/code-stubs.cc +++ b/src/code-stubs.cc @@ -37,7 +37,6 @@ namespace v8 { namespace internal { bool CodeStub::FindCodeInCache(Code** code_out) { - if (has_custom_cache()) return GetCustomCache(code_out); int index = Heap::code_stubs()->FindEntry(GetKey()); if (index != NumberDictionary::kNotFound) { *code_out = Code::cast(Heap::code_stubs()->ValueAt(index)); @@ -105,17 +104,14 @@ Handle<Code> CodeStub::GetCode() { Handle<Code> new_object = Factory::NewCode(desc, flags, masm.CodeObject()); RecordCodeGeneration(*new_object, &masm); - if (has_custom_cache()) { - SetCustomCache(*new_object); - } else { - // Update the dictionary and the root in Heap. - Handle<NumberDictionary> dict = - Factory::DictionaryAtNumberPut( - Handle<NumberDictionary>(Heap::code_stubs()), - GetKey(), - new_object); - Heap::public_set_code_stubs(*dict); - } + // Update the dictionary and the root in Heap. + Handle<NumberDictionary> dict = + Factory::DictionaryAtNumberPut( + Handle<NumberDictionary>(Heap::code_stubs()), + GetKey(), + new_object); + Heap::public_set_code_stubs(*dict); + code = *new_object; } @@ -147,15 +143,11 @@ MaybeObject* CodeStub::TryGetCode() { code = Code::cast(new_object); RecordCodeGeneration(code, &masm); - if (has_custom_cache()) { - SetCustomCache(code); - } else { - // Try to update the code cache but do not fail if unable. - MaybeObject* maybe_new_object = - Heap::code_stubs()->AtNumberPut(GetKey(), code); - if (maybe_new_object->ToObject(&new_object)) { - Heap::public_set_code_stubs(NumberDictionary::cast(new_object)); - } + // Try to update the code cache but do not fail if unable. + MaybeObject* maybe_new_object = + Heap::code_stubs()->AtNumberPut(GetKey(), code); + if (maybe_new_object->ToObject(&new_object)) { + Heap::public_set_code_stubs(NumberDictionary::cast(new_object)); } } diff --git a/src/code-stubs.h b/src/code-stubs.h index ec643534..b156647d 100644 --- a/src/code-stubs.h +++ b/src/code-stubs.h @@ -124,12 +124,6 @@ class CodeStub BASE_EMBEDDED { virtual ~CodeStub() {} - // Override these methods to provide a custom caching mechanism for - // an individual type of code stub. - virtual bool GetCustomCache(Code** code_out) { return false; } - virtual void SetCustomCache(Code* value) { } - virtual bool has_custom_cache() { return false; } - protected: static const int kMajorBits = 5; static const int kMinorBits = kBitsPerInt - kSmiTagSize - kMajorBits; @@ -524,58 +518,6 @@ class CEntryStub : public CodeStub { }; -class ApiGetterEntryStub : public CodeStub { - public: - ApiGetterEntryStub(Handle<AccessorInfo> info, - ApiFunction* fun) - : info_(info), - fun_(fun) { } - void Generate(MacroAssembler* masm); - virtual bool has_custom_cache() { return true; } - virtual bool GetCustomCache(Code** code_out); - virtual void SetCustomCache(Code* value); - - static const int kStackSpace = 5; - static const int kArgc = 2; - private: - Handle<AccessorInfo> info() { return info_; } - ApiFunction* fun() { return fun_; } - Major MajorKey() { return NoCache; } - int MinorKey() { return 0; } - const char* GetName() { return "ApiGetterEntryStub"; } - // The accessor info associated with the function. - Handle<AccessorInfo> info_; - // The function to be called. - ApiFunction* fun_; -}; - - -class ApiCallEntryStub : public CodeStub { - public: - ApiCallEntryStub(Handle<CallHandlerInfo> info, - ApiFunction* fun) - : info_(info), - fun_(fun) { } - void Generate(MacroAssembler* masm); - virtual bool has_custom_cache() { return true; } - virtual bool GetCustomCache(Code** code_out); - virtual void SetCustomCache(Code* value); - - static const int kStackSpace = 0; - static const int kArgc = 5; - private: - Handle<CallHandlerInfo> info() { return info_; } - ApiFunction* fun() { return fun_; } - Major MajorKey() { return NoCache; } - int MinorKey() { return 0; } - const char* GetName() { return "ApiCallEntryStub"; } - // The call handler info associated with the function. - Handle<CallHandlerInfo> info_; - // The function to be called. - ApiFunction* fun_; -}; - - class JSEntryStub : public CodeStub { public: JSEntryStub() { } diff --git a/src/codegen.cc b/src/codegen.cc index e954dd66..fb8c5cd4 100644 --- a/src/codegen.cc +++ b/src/codegen.cc @@ -252,39 +252,6 @@ bool CodeGenerator::ShouldGenerateLog(Expression* type) { #endif -Handle<Code> CodeGenerator::ComputeCallInitialize( - int argc, - InLoopFlag in_loop) { - if (in_loop == IN_LOOP) { - // Force the creation of the corresponding stub outside loops, - // because it may be used when clearing the ICs later - it is - // possible for a series of IC transitions to lose the in-loop - // information, and the IC clearing code can't generate a stub - // that it needs so we need to ensure it is generated already. - ComputeCallInitialize(argc, NOT_IN_LOOP); - } - CALL_HEAP_FUNCTION( - StubCache::ComputeCallInitialize(argc, in_loop, Code::CALL_IC), - Code); -} - - -Handle<Code> CodeGenerator::ComputeKeyedCallInitialize( - int argc, - InLoopFlag in_loop) { - if (in_loop == IN_LOOP) { - // Force the creation of the corresponding stub outside loops, - // because it may be used when clearing the ICs later - it is - // possible for a series of IC transitions to lose the in-loop - // information, and the IC clearing code can't generate a stub - // that it needs so we need to ensure it is generated already. - ComputeKeyedCallInitialize(argc, NOT_IN_LOOP); - } - CALL_HEAP_FUNCTION( - StubCache::ComputeCallInitialize(argc, in_loop, Code::KEYED_CALL_IC), - Code); -} - void CodeGenerator::ProcessDeclarations(ZoneList<Declaration*>* declarations) { int length = declarations->length(); int globals = 0; @@ -482,35 +449,4 @@ int CEntryStub::MinorKey() { } -// Implementation of CodeStub::GetCustomCache. -static bool GetCustomCacheHelper(Object* cache, Code** code_out) { - if (cache->IsUndefined()) { - return false; - } else { - *code_out = Code::cast(cache); - return true; - } -} - - -bool ApiGetterEntryStub::GetCustomCache(Code** code_out) { - return GetCustomCacheHelper(info()->load_stub_cache(), code_out); -} - - -void ApiGetterEntryStub::SetCustomCache(Code* value) { - info()->set_load_stub_cache(value); -} - - -bool ApiCallEntryStub::GetCustomCache(Code** code_out) { - return GetCustomCacheHelper(info()->call_stub_cache(), code_out); -} - - -void ApiCallEntryStub::SetCustomCache(Code* value) { - info()->set_call_stub_cache(value); -} - - } } // namespace v8::internal diff --git a/src/codegen.h b/src/codegen.h index 8f923dd3..66300d6c 100644 --- a/src/codegen.h +++ b/src/codegen.h @@ -58,8 +58,6 @@ // Generate // ComputeLazyCompile // BuildFunctionInfo -// ComputeCallInitialize -// ComputeCallInitializeInLoop // ProcessDeclarations // DeclareGlobals // CheckForInlineRuntimeCall diff --git a/src/conversions.cc b/src/conversions.cc index 4cc67448..a954d6cc 100644 --- a/src/conversions.cc +++ b/src/conversions.cc @@ -33,22 +33,12 @@ #include "conversions-inl.h" #include "dtoa.h" #include "factory.h" -#include "scanner.h" +#include "scanner-base.h" #include "strtod.h" namespace v8 { namespace internal { -int HexValue(uc32 c) { - if ('0' <= c && c <= '9') - return c - '0'; - if ('a' <= c && c <= 'f') - return c - 'a' + 10; - if ('A' <= c && c <= 'F') - return c - 'A' + 10; - return -1; -} - namespace { // C++-style iterator adaptor for StringInputBuffer @@ -121,7 +111,7 @@ static const double JUNK_STRING_VALUE = OS::nan_value(); template <class Iterator, class EndMark> static inline bool AdvanceToNonspace(Iterator* current, EndMark end) { while (*current != end) { - if (!Scanner::kIsWhiteSpace.get(**current)) return true; + if (!ScannerConstants::kIsWhiteSpace.get(**current)) return true; ++*current; } return false; @@ -458,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; } @@ -654,7 +644,7 @@ static double InternalStringToDouble(Iterator current, buffer[buffer_pos] = '\0'; double converted = Strtod(Vector<const char>(buffer, buffer_pos), exponent); - return sign? -converted: converted; + return sign ? -converted : converted; } @@ -711,11 +701,6 @@ double StringToDouble(Vector<const char> str, } -extern "C" char* dtoa(double d, int mode, int ndigits, - int* decpt, int* sign, char** rve); - -extern "C" void freedtoa(char* s); - const char* DoubleToCString(double v, Vector<char> buffer) { StringBuilder builder(buffer.start(), buffer.length()); @@ -739,21 +724,13 @@ const char* DoubleToCString(double v, Vector<char> buffer) { default: { int decimal_point; int sign; - char* decimal_rep; - bool used_gay_dtoa = false; const int kV8DtoaBufferCapacity = kBase10MaximalLength + 1; - char v8_dtoa_buffer[kV8DtoaBufferCapacity]; + char decimal_rep[kV8DtoaBufferCapacity]; int length; - if (DoubleToAscii(v, DTOA_SHORTEST, 0, - Vector<char>(v8_dtoa_buffer, kV8DtoaBufferCapacity), - &sign, &length, &decimal_point)) { - decimal_rep = v8_dtoa_buffer; - } else { - decimal_rep = dtoa(v, 0, 0, &decimal_point, &sign, NULL); - used_gay_dtoa = true; - length = StrLength(decimal_rep); - } + DoubleToAscii(v, DTOA_SHORTEST, 0, + Vector<char>(decimal_rep, kV8DtoaBufferCapacity), + &sign, &length, &decimal_point); if (sign) builder.AddCharacter('-'); @@ -787,8 +764,6 @@ const char* DoubleToCString(double v, Vector<char> buffer) { if (exponent < 0) exponent = -exponent; builder.AddFormatted("%d", exponent); } - - if (used_gay_dtoa) freedtoa(decimal_rep); } } return builder.Finalize(); @@ -845,11 +820,9 @@ char* DoubleToFixedCString(double value, int f) { kMaxDigitsBeforePoint + kMaxDigitsAfterPoint + 1; char decimal_rep[kDecimalRepCapacity]; int decimal_rep_length; - bool status = DoubleToAscii(value, DTOA_FIXED, f, - Vector<char>(decimal_rep, kDecimalRepCapacity), - &sign, &decimal_rep_length, &decimal_point); - USE(status); - ASSERT(status); + DoubleToAscii(value, DTOA_FIXED, f, + Vector<char>(decimal_rep, kDecimalRepCapacity), + &sign, &decimal_rep_length, &decimal_point); // Create a representation that is padded with zeros if needed. int zero_prefix_length = 0; @@ -935,8 +908,6 @@ char* DoubleToExponentialCString(double value, int f) { // Find a sufficiently precise decimal representation of n. int decimal_point; int sign; - char* decimal_rep = NULL; - bool used_gay_dtoa = false; // f corresponds to the digits after the point. There is always one digit // before the point. The number of requested_digits equals hence f + 1. // And we have to add one character for the null-terminator. @@ -944,31 +915,18 @@ char* DoubleToExponentialCString(double value, int f) { // Make sure that the buffer is big enough, even if we fall back to the // shortest representation (which happens when f equals -1). ASSERT(kBase10MaximalLength <= kMaxDigitsAfterPoint + 1); - char v8_dtoa_buffer[kV8DtoaBufferCapacity]; + char decimal_rep[kV8DtoaBufferCapacity]; int decimal_rep_length; if (f == -1) { - if (DoubleToAscii(value, DTOA_SHORTEST, 0, - Vector<char>(v8_dtoa_buffer, kV8DtoaBufferCapacity), - &sign, &decimal_rep_length, &decimal_point)) { - f = decimal_rep_length - 1; - decimal_rep = v8_dtoa_buffer; - } else { - decimal_rep = dtoa(value, 0, 0, &decimal_point, &sign, NULL); - decimal_rep_length = StrLength(decimal_rep); - f = decimal_rep_length - 1; - used_gay_dtoa = true; - } + DoubleToAscii(value, DTOA_SHORTEST, 0, + Vector<char>(decimal_rep, kV8DtoaBufferCapacity), + &sign, &decimal_rep_length, &decimal_point); + f = decimal_rep_length - 1; } else { - if (DoubleToAscii(value, DTOA_PRECISION, f + 1, - Vector<char>(v8_dtoa_buffer, kV8DtoaBufferCapacity), - &sign, &decimal_rep_length, &decimal_point)) { - decimal_rep = v8_dtoa_buffer; - } else { - decimal_rep = dtoa(value, 2, f + 1, &decimal_point, &sign, NULL); - decimal_rep_length = StrLength(decimal_rep); - used_gay_dtoa = true; - } + DoubleToAscii(value, DTOA_PRECISION, f + 1, + Vector<char>(decimal_rep, kV8DtoaBufferCapacity), + &sign, &decimal_rep_length, &decimal_point); } ASSERT(decimal_rep_length > 0); ASSERT(decimal_rep_length <= f + 1); @@ -977,10 +935,6 @@ char* DoubleToExponentialCString(double value, int f) { char* result = CreateExponentialRepresentation(decimal_rep, exponent, negative, f+1); - if (used_gay_dtoa) { - freedtoa(decimal_rep); - } - return result; } @@ -1000,22 +954,14 @@ char* DoubleToPrecisionCString(double value, int p) { // Find a sufficiently precise decimal representation of n. int decimal_point; int sign; - char* decimal_rep = NULL; - bool used_gay_dtoa = false; // Add one for the terminating null character. const int kV8DtoaBufferCapacity = kMaximalDigits + 1; - char v8_dtoa_buffer[kV8DtoaBufferCapacity]; + char decimal_rep[kV8DtoaBufferCapacity]; int decimal_rep_length; - if (DoubleToAscii(value, DTOA_PRECISION, p, - Vector<char>(v8_dtoa_buffer, kV8DtoaBufferCapacity), - &sign, &decimal_rep_length, &decimal_point)) { - decimal_rep = v8_dtoa_buffer; - } else { - decimal_rep = dtoa(value, 2, p, &decimal_point, &sign, NULL); - decimal_rep_length = StrLength(decimal_rep); - used_gay_dtoa = true; - } + DoubleToAscii(value, DTOA_PRECISION, p, + Vector<char>(decimal_rep, kV8DtoaBufferCapacity), + &sign, &decimal_rep_length, &decimal_point); ASSERT(decimal_rep_length <= p); int exponent = decimal_point - 1; @@ -1059,9 +1005,6 @@ char* DoubleToPrecisionCString(double value, int p) { result = builder.Finalize(); } - if (used_gay_dtoa) { - freedtoa(decimal_rep); - } return result; } diff --git a/src/conversions.h b/src/conversions.h index 9e32a0cd..312e6aee 100644 --- a/src/conversions.h +++ b/src/conversions.h @@ -75,11 +75,6 @@ static inline uint32_t DoubleToUint32(double x) { } -// Returns the value (0 .. 15) of a hexadecimal character c. -// If c is not a legal hexadecimal character, returns a value < 0. -int HexValue(uc32 c); - - // Enumeration for allowing octals and ignoring junk when converting // strings to numbers. enum ConversionFlags { diff --git a/src/dateparser-inl.h b/src/dateparser-inl.h index e52cc94a..ac28c622 100644 --- a/src/dateparser-inl.h +++ b/src/dateparser-inl.h @@ -59,7 +59,7 @@ bool DateParser::Parse(Vector<Char> 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/src/dateparser.h b/src/dateparser.h index cae9b08d..40e56f30 100644 --- a/src/dateparser.h +++ b/src/dateparser.h @@ -28,7 +28,8 @@ #ifndef V8_DATEPARSER_H_ #define V8_DATEPARSER_H_ -#include "scanner.h" +#include "char-predicates-inl.h" +#include "scanner-base.h" namespace v8 { namespace internal { @@ -86,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. @@ -99,10 +112,20 @@ class DateParser : public AllStatic { } // The skip methods return whether they actually skipped something. - bool Skip(uint32_t c) { return ch_ == c ? (Next(), true) : false; } + bool Skip(uint32_t c) { + if (ch_ == c) { + Next(); + return true; + } + return false; + } bool SkipWhiteSpace() { - return Scanner::kIsWhiteSpace.get(ch_) ? (Next(), true) : false; + if (ScannerConstants::kIsWhiteSpace.get(ch_)) { + Next(); + return true; + } + return false; } bool SkipParentheses() { diff --git a/src/dtoa-config.c b/src/dtoa-config.c deleted file mode 100644 index 9c5ee331..00000000 --- a/src/dtoa-config.c +++ /dev/null @@ -1,92 +0,0 @@ -/* - * Copyright 2007-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. - */ - -/** - * Dtoa needs to have a particular environment set up for it so - * instead of using it directly you should use this file. - * - * The way it works is that when you link with it, its definitions - * of dtoa, strtod etc. override the default ones. So if you fail - * to link with this library everything will still work, it's just - * subtly wrong. - */ - -#if !(defined(__APPLE__) && defined(__MACH__)) && \ - !defined(WIN32) && !defined(__FreeBSD__) && !defined(__OpenBSD__) && \ - !defined(__sun) -#include <endian.h> -#endif -#include <math.h> -#include <float.h> - -/* The floating point word order on ARM is big endian when floating point - * emulation is used, even if the byte order is little endian */ -#if !(defined(__APPLE__) && defined(__MACH__)) && !defined(WIN32) && \ - !defined(__FreeBSD__) && !defined(__OpenBSD__) && !defined(__sun) && \ - __FLOAT_WORD_ORDER == __BIG_ENDIAN -#define IEEE_MC68k -#else -#define IEEE_8087 -#endif - -#define __MATH_H__ -#if defined(__APPLE__) && defined(__MACH__) || defined(__FreeBSD__) || \ - defined(__OpenBSD__) || defined(__sun) -/* stdlib.h on FreeBSD and Apple's 10.5 and later SDKs will mangle the - * name of strtod. If it's included after strtod is redefined as - * gay_strtod, it will mangle the name of gay_strtod, which is - * unwanted. */ -#include <stdlib.h> - -#endif -/* stdlib.h on Windows adds __declspec(dllimport) to all functions when using - * the DLL version of the CRT (compiling with /MD or /MDd). If stdlib.h is - * included after strtod is redefined as gay_strtod, it will add - * __declspec(dllimport) to gay_strtod, which causes the compilation of - * gay_strtod in dtoa.c to fail. -*/ -#if defined(WIN32) && defined(_DLL) -#include "stdlib.h" -#endif - -/* For MinGW, turn on __NO_ISOCEXT so that its strtod doesn't get added */ -#ifdef __MINGW32__ -#define __NO_ISOCEXT -#endif /* __MINGW32__ */ - -/* On 64-bit systems, we need to make sure that a Long is only 32 bits. */ -#ifdef V8_TARGET_ARCH_X64 -#define Long int -#endif /* V8_TARGET_ARCH_X64 */ - -/* Make sure we use the David M. Gay version of strtod(). On Linux, we - * cannot use the same name (maybe the function does not have weak - * linkage?). */ -#define strtod gay_strtod -#include "third_party/dtoa/dtoa.c" diff --git a/src/dtoa.cc b/src/dtoa.cc index f4141eb6..b857a5dc 100644 --- a/src/dtoa.cc +++ b/src/dtoa.cc @@ -30,6 +30,7 @@ #include "v8.h" #include "dtoa.h" +#include "bignum-dtoa.h" #include "double.h" #include "fast-dtoa.h" #include "fixed-dtoa.h" @@ -37,7 +38,19 @@ namespace v8 { namespace internal { -bool DoubleToAscii(double v, DtoaMode mode, int requested_digits, +static BignumDtoaMode DtoaToBignumDtoaMode(DtoaMode dtoa_mode) { + switch (dtoa_mode) { + case DTOA_SHORTEST: return BIGNUM_DTOA_SHORTEST; + case DTOA_FIXED: return BIGNUM_DTOA_FIXED; + case DTOA_PRECISION: return BIGNUM_DTOA_PRECISION; + default: + UNREACHABLE(); + return BIGNUM_DTOA_SHORTEST; // To silence compiler. + } +} + + +void DoubleToAscii(double v, DtoaMode mode, int requested_digits, Vector<char> buffer, int* sign, int* length, int* point) { ASSERT(!Double(v).IsSpecial()); ASSERT(mode == DTOA_SHORTEST || requested_digits >= 0); @@ -54,25 +67,37 @@ bool DoubleToAscii(double v, DtoaMode mode, int requested_digits, buffer[1] = '\0'; *length = 1; *point = 1; - return true; + return; } if (mode == DTOA_PRECISION && requested_digits == 0) { buffer[0] = '\0'; *length = 0; - return true; + return; } + bool fast_worked; switch (mode) { case DTOA_SHORTEST: - return FastDtoa(v, FAST_DTOA_SHORTEST, 0, buffer, length, point); + fast_worked = FastDtoa(v, FAST_DTOA_SHORTEST, 0, buffer, length, point); + break; case DTOA_FIXED: - return FastFixedDtoa(v, requested_digits, buffer, length, point); + fast_worked = FastFixedDtoa(v, requested_digits, buffer, length, point); + break; case DTOA_PRECISION: - return FastDtoa(v, FAST_DTOA_PRECISION, requested_digits, - buffer, length, point); + fast_worked = FastDtoa(v, FAST_DTOA_PRECISION, requested_digits, + buffer, length, point); + break; + default: + UNREACHABLE(); + fast_worked = false; } - return false; + if (fast_worked) return; + + // If the fast dtoa didn't succeed use the slower bignum version. + BignumDtoaMode bignum_mode = DtoaToBignumDtoaMode(mode); + BignumDtoa(v, bignum_mode, requested_digits, buffer, length, point); + buffer[*length] = '\0'; } } } // namespace v8::internal @@ -32,13 +32,15 @@ namespace v8 { namespace internal { enum DtoaMode { - // 0.9999999999999999 becomes 0.1 + // Return the shortest correct representation. + // For example the output of 0.299999999999999988897 is (the less accurate but + // correct) 0.3. DTOA_SHORTEST, - // Fixed number of digits after the decimal point. + // Return a fixed number of digits after the decimal point. // For instance fixed(0.1, 4) becomes 0.1000 // If the input number is big, the output will be big. DTOA_FIXED, - // Fixed number of digits (independent of the decimal point). + // Return a fixed number of digits, no matter what the exponent is. DTOA_PRECISION }; @@ -72,8 +74,10 @@ static const int kBase10MaximalLength = 17; // which case the caller has to fill the missing digits with '0's. // Halfway cases are again rounded away from 0. // 'DoubleToAscii' expects the given buffer to be big enough to hold all digits -// and a terminating null-character. -bool DoubleToAscii(double v, DtoaMode mode, int requested_digits, +// and a terminating null-character. In SHORTEST-mode it expects a buffer of +// at least kBase10MaximalLength + 1. Otherwise, the size of the output is +// limited to requested_digits digits plus the null terminator. +void DoubleToAscii(double v, DtoaMode mode, int requested_digits, Vector<char> buffer, int* sign, int* length, int* point); } } // namespace v8::internal diff --git a/src/execution.cc b/src/execution.cc index 885bf63c..691d5695 100644 --- a/src/execution.cc +++ b/src/execution.cc @@ -700,135 +700,4 @@ MaybeObject* Execution::HandleStackGuardInterrupt() { return Heap::undefined_value(); } -// --- G C E x t e n s i o n --- - -const char* const GCExtension::kSource = "native function gc();"; - - -v8::Handle<v8::FunctionTemplate> GCExtension::GetNativeFunction( - v8::Handle<v8::String> str) { - return v8::FunctionTemplate::New(GCExtension::GC); -} - - -v8::Handle<v8::Value> GCExtension::GC(const v8::Arguments& args) { - // All allocation spaces other than NEW_SPACE have the same effect. - Heap::CollectAllGarbage(false); - return v8::Undefined(); -} - - -static GCExtension gc_extension; -static v8::DeclareExtension gc_extension_declaration(&gc_extension); - - -// --- E x t e r n a l i z e S t r i n g E x t e n s i o n --- - - -template <typename Char, typename Base> -class SimpleStringResource : public Base { - public: - // Takes ownership of |data|. - SimpleStringResource(Char* data, size_t length) - : data_(data), - length_(length) {} - - virtual ~SimpleStringResource() { delete[] data_; } - - virtual const Char* data() const { return data_; } - - virtual size_t length() const { return length_; } - - private: - Char* const data_; - const size_t length_; -}; - - -typedef SimpleStringResource<char, v8::String::ExternalAsciiStringResource> - SimpleAsciiStringResource; -typedef SimpleStringResource<uc16, v8::String::ExternalStringResource> - SimpleTwoByteStringResource; - - -const char* const ExternalizeStringExtension::kSource = - "native function externalizeString();" - "native function isAsciiString();"; - - -v8::Handle<v8::FunctionTemplate> ExternalizeStringExtension::GetNativeFunction( - v8::Handle<v8::String> str) { - if (strcmp(*v8::String::AsciiValue(str), "externalizeString") == 0) { - return v8::FunctionTemplate::New(ExternalizeStringExtension::Externalize); - } else { - ASSERT(strcmp(*v8::String::AsciiValue(str), "isAsciiString") == 0); - return v8::FunctionTemplate::New(ExternalizeStringExtension::IsAscii); - } -} - - -v8::Handle<v8::Value> ExternalizeStringExtension::Externalize( - const v8::Arguments& args) { - if (args.Length() < 1 || !args[0]->IsString()) { - return v8::ThrowException(v8::String::New( - "First parameter to externalizeString() must be a string.")); - } - bool force_two_byte = false; - if (args.Length() >= 2) { - if (args[1]->IsBoolean()) { - force_two_byte = args[1]->BooleanValue(); - } else { - return v8::ThrowException(v8::String::New( - "Second parameter to externalizeString() must be a boolean.")); - } - } - bool result = false; - Handle<String> string = Utils::OpenHandle(*args[0].As<v8::String>()); - if (string->IsExternalString()) { - return v8::ThrowException(v8::String::New( - "externalizeString() can't externalize twice.")); - } - if (string->IsAsciiRepresentation() && !force_two_byte) { - char* data = new char[string->length()]; - String::WriteToFlat(*string, data, 0, string->length()); - SimpleAsciiStringResource* resource = new SimpleAsciiStringResource( - data, string->length()); - result = string->MakeExternal(resource); - if (result && !string->IsSymbol()) { - i::ExternalStringTable::AddString(*string); - } - if (!result) delete resource; - } else { - uc16* data = new uc16[string->length()]; - String::WriteToFlat(*string, data, 0, string->length()); - SimpleTwoByteStringResource* resource = new SimpleTwoByteStringResource( - data, string->length()); - result = string->MakeExternal(resource); - if (result && !string->IsSymbol()) { - i::ExternalStringTable::AddString(*string); - } - if (!result) delete resource; - } - if (!result) { - return v8::ThrowException(v8::String::New("externalizeString() failed.")); - } - return v8::Undefined(); -} - - -v8::Handle<v8::Value> ExternalizeStringExtension::IsAscii( - const v8::Arguments& args) { - if (args.Length() != 1 || !args[0]->IsString()) { - return v8::ThrowException(v8::String::New( - "isAsciiString() requires a single string argument.")); - } - return Utils::OpenHandle(*args[0].As<v8::String>())->IsAsciiRepresentation() ? - v8::True() : v8::False(); -} - - -static ExternalizeStringExtension externalize_extension; -static v8::DeclareExtension externalize_extension_declaration( - &externalize_extension); - } } // namespace v8::internal diff --git a/src/execution.h b/src/execution.h index 5547803b..a2ddc41a 100644 --- a/src/execution.h +++ b/src/execution.h @@ -189,6 +189,9 @@ class StackGuard : public AllStatic { static uintptr_t climit() { return thread_local_.climit_; } + static uintptr_t real_climit() { + return thread_local_.real_climit_; + } static uintptr_t jslimit() { return thread_local_.jslimit_; } @@ -313,29 +316,6 @@ class PostponeInterruptsScope BASE_EMBEDDED { } }; - -class GCExtension : public v8::Extension { - public: - GCExtension() : v8::Extension("v8/gc", kSource) {} - virtual v8::Handle<v8::FunctionTemplate> GetNativeFunction( - v8::Handle<v8::String> name); - static v8::Handle<v8::Value> GC(const v8::Arguments& args); - private: - static const char* const kSource; -}; - - -class ExternalizeStringExtension : public v8::Extension { - public: - ExternalizeStringExtension() : v8::Extension("v8/externalize", kSource) {} - virtual v8::Handle<v8::FunctionTemplate> GetNativeFunction( - v8::Handle<v8::String> name); - static v8::Handle<v8::Value> Externalize(const v8::Arguments& args); - static v8::Handle<v8::Value> IsAscii(const v8::Arguments& args); - private: - static const char* const kSource; -}; - } } // namespace v8::internal #endif // V8_EXECUTION_H_ diff --git a/src/extensions/externalize-string-extension.cc b/src/extensions/externalize-string-extension.cc new file mode 100644 index 00000000..8b4bdbd8 --- /dev/null +++ b/src/extensions/externalize-string-extension.cc @@ -0,0 +1,141 @@ +// Copyright 2010 the V8 project authors. All rights reserved. +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following +// disclaimer in the documentation and/or other materials provided +// with the distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +#include "externalize-string-extension.h" + +namespace v8 { +namespace internal { + +template <typename Char, typename Base> +class SimpleStringResource : public Base { + public: + // Takes ownership of |data|. + SimpleStringResource(Char* data, size_t length) + : data_(data), + length_(length) {} + + virtual ~SimpleStringResource() { delete[] data_; } + + virtual const Char* data() const { return data_; } + + virtual size_t length() const { return length_; } + + private: + Char* const data_; + const size_t length_; +}; + + +typedef SimpleStringResource<char, v8::String::ExternalAsciiStringResource> + SimpleAsciiStringResource; +typedef SimpleStringResource<uc16, v8::String::ExternalStringResource> + SimpleTwoByteStringResource; + + +const char* const ExternalizeStringExtension::kSource = + "native function externalizeString();" + "native function isAsciiString();"; + + +v8::Handle<v8::FunctionTemplate> ExternalizeStringExtension::GetNativeFunction( + v8::Handle<v8::String> str) { + if (strcmp(*v8::String::AsciiValue(str), "externalizeString") == 0) { + return v8::FunctionTemplate::New(ExternalizeStringExtension::Externalize); + } else { + ASSERT(strcmp(*v8::String::AsciiValue(str), "isAsciiString") == 0); + return v8::FunctionTemplate::New(ExternalizeStringExtension::IsAscii); + } +} + + +v8::Handle<v8::Value> ExternalizeStringExtension::Externalize( + const v8::Arguments& args) { + if (args.Length() < 1 || !args[0]->IsString()) { + return v8::ThrowException(v8::String::New( + "First parameter to externalizeString() must be a string.")); + } + bool force_two_byte = false; + if (args.Length() >= 2) { + if (args[1]->IsBoolean()) { + force_two_byte = args[1]->BooleanValue(); + } else { + return v8::ThrowException(v8::String::New( + "Second parameter to externalizeString() must be a boolean.")); + } + } + bool result = false; + Handle<String> string = Utils::OpenHandle(*args[0].As<v8::String>()); + if (string->IsExternalString()) { + return v8::ThrowException(v8::String::New( + "externalizeString() can't externalize twice.")); + } + if (string->IsAsciiRepresentation() && !force_two_byte) { + char* data = new char[string->length()]; + String::WriteToFlat(*string, data, 0, string->length()); + SimpleAsciiStringResource* resource = new SimpleAsciiStringResource( + data, string->length()); + result = string->MakeExternal(resource); + if (result && !string->IsSymbol()) { + i::ExternalStringTable::AddString(*string); + } + if (!result) delete resource; + } else { + uc16* data = new uc16[string->length()]; + String::WriteToFlat(*string, data, 0, string->length()); + SimpleTwoByteStringResource* resource = new SimpleTwoByteStringResource( + data, string->length()); + result = string->MakeExternal(resource); + if (result && !string->IsSymbol()) { + i::ExternalStringTable::AddString(*string); + } + if (!result) delete resource; + } + if (!result) { + return v8::ThrowException(v8::String::New("externalizeString() failed.")); + } + return v8::Undefined(); +} + + +v8::Handle<v8::Value> ExternalizeStringExtension::IsAscii( + const v8::Arguments& args) { + if (args.Length() != 1 || !args[0]->IsString()) { + return v8::ThrowException(v8::String::New( + "isAsciiString() requires a single string argument.")); + } + return Utils::OpenHandle(*args[0].As<v8::String>())->IsAsciiRepresentation() ? + v8::True() : v8::False(); +} + + +void ExternalizeStringExtension::Register() { + static ExternalizeStringExtension externalize_extension; + static v8::DeclareExtension externalize_extension_declaration( + &externalize_extension); +} + +} } // namespace v8::internal diff --git a/src/extensions/externalize-string-extension.h b/src/extensions/externalize-string-extension.h new file mode 100644 index 00000000..b97b4962 --- /dev/null +++ b/src/extensions/externalize-string-extension.h @@ -0,0 +1,50 @@ +// Copyright 2010 the V8 project authors. All rights reserved. +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following +// disclaimer in the documentation and/or other materials provided +// with the distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +#ifndef V8_EXTENSIONS_EXTERNALIZE_STRING_EXTENSION_H_ +#define V8_EXTENSIONS_EXTERNALIZE_STRING_EXTENSION_H_ + +#include "v8.h" + +namespace v8 { +namespace internal { + +class ExternalizeStringExtension : public v8::Extension { + public: + ExternalizeStringExtension() : v8::Extension("v8/externalize", kSource) {} + virtual v8::Handle<v8::FunctionTemplate> GetNativeFunction( + v8::Handle<v8::String> name); + static v8::Handle<v8::Value> Externalize(const v8::Arguments& args); + static v8::Handle<v8::Value> IsAscii(const v8::Arguments& args); + static void Register(); + private: + static const char* const kSource; +}; + +} } // namespace v8::internal + +#endif // V8_EXTENSIONS_EXTERNALIZE_STRING_EXTENSION_H_ diff --git a/src/extensions/gc-extension.cc b/src/extensions/gc-extension.cc new file mode 100644 index 00000000..b8f081c5 --- /dev/null +++ b/src/extensions/gc-extension.cc @@ -0,0 +1,54 @@ +// Copyright 2010 the V8 project authors. All rights reserved. +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following +// disclaimer in the documentation and/or other materials provided +// with the distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +#include "gc-extension.h" + +namespace v8 { +namespace internal { + +const char* const GCExtension::kSource = "native function gc();"; + + +v8::Handle<v8::FunctionTemplate> GCExtension::GetNativeFunction( + v8::Handle<v8::String> str) { + return v8::FunctionTemplate::New(GCExtension::GC); +} + + +v8::Handle<v8::Value> GCExtension::GC(const v8::Arguments& args) { + // All allocation spaces other than NEW_SPACE have the same effect. + Heap::CollectAllGarbage(false); + return v8::Undefined(); +} + + +void GCExtension::Register() { + static GCExtension gc_extension; + static v8::DeclareExtension gc_extension_declaration(&gc_extension); +} + +} } // namespace v8::internal diff --git a/src/extensions/gc-extension.h b/src/extensions/gc-extension.h new file mode 100644 index 00000000..06ea4ed2 --- /dev/null +++ b/src/extensions/gc-extension.h @@ -0,0 +1,49 @@ +// Copyright 2010 the V8 project authors. All rights reserved. +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following +// disclaimer in the documentation and/or other materials provided +// with the distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +#ifndef V8_EXTENSIONS_GC_EXTENSION_H_ +#define V8_EXTENSIONS_GC_EXTENSION_H_ + +#include "v8.h" + +namespace v8 { +namespace internal { + +class GCExtension : public v8::Extension { + public: + GCExtension() : v8::Extension("v8/gc", kSource) {} + virtual v8::Handle<v8::FunctionTemplate> GetNativeFunction( + v8::Handle<v8::String> name); + static v8::Handle<v8::Value> GC(const v8::Arguments& args); + static void Register(); + private: + static const char* const kSource; +}; + +} } // namespace v8::internal + +#endif // V8_EXTENSIONS_GC_EXTENSION_H_ diff --git a/src/full-codegen.cc b/src/full-codegen.cc index c770e189..a890f159 100644 --- a/src/full-codegen.cc +++ b/src/full-codegen.cc @@ -301,11 +301,6 @@ bool FullCodeGenerator::MakeCode(CompilationInfo* info) { } -MemOperand FullCodeGenerator::ContextOperand(Register context, int index) { - return CodeGenerator::ContextOperand(context, index); -} - - int FullCodeGenerator::SlotOffset(Slot* slot) { ASSERT(slot != NULL); // Offset is negative because higher indexes are at lower addresses. @@ -887,12 +882,12 @@ void FullCodeGenerator::VisitDoWhileStatement(DoWhileStatement* stmt) { Visit(stmt->body()); // Check stack before looping. + __ bind(loop_statement.continue_target()); __ StackLimitCheck(&stack_limit_hit); __ bind(&stack_check_success); // Record the position of the do while condition and make sure it is // possible to break on the condition. - __ bind(loop_statement.continue_target()); SetExpressionPosition(stmt->cond(), stmt->condition_position()); VisitForControl(stmt->cond(), &body, @@ -1173,14 +1168,14 @@ void FullCodeGenerator::VisitFunctionLiteral(FunctionLiteral* expr) { SetStackOverflow(); return; } - EmitNewClosure(function_info); + EmitNewClosure(function_info, expr->pretenure()); } void FullCodeGenerator::VisitSharedFunctionInfoLiteral( SharedFunctionInfoLiteral* expr) { Comment cmnt(masm_, "[ SharedFunctionInfoLiteral"); - EmitNewClosure(expr->shared_function_info()); + EmitNewClosure(expr->shared_function_info(), false); } diff --git a/src/full-codegen.h b/src/full-codegen.h index a3270aa7..97a56bd9 100644 --- a/src/full-codegen.h +++ b/src/full-codegen.h @@ -348,7 +348,7 @@ class FullCodeGenerator: public AstVisitor { // Platform-specific support for allocating a new closure based on // the given function info. - void EmitNewClosure(Handle<SharedFunctionInfo> info); + void EmitNewClosure(Handle<SharedFunctionInfo> info, bool pretenure); // Platform-specific support for compiling assignments. @@ -464,9 +464,6 @@ class FullCodeGenerator: public AstVisitor { // in v8::internal::Context. void LoadContextField(Register dst, int context_index); - // Create an operand for a context field. - MemOperand ContextOperand(Register context, int context_index); - // AST node visit functions. #define DECLARE_VISIT(type) virtual void Visit##type(type* node); AST_NODE_LIST(DECLARE_VISIT) diff --git a/src/globals.h b/src/globals.h index a74b6c79..88c3e780 100644 --- a/src/globals.h +++ b/src/globals.h @@ -1,4 +1,4 @@ -// Copyright 2006-2009 the V8 project authors. All rights reserved. +// Copyright 2010 the V8 project authors. All rights reserved. // Redistribution and use in source and binary forms, with or without // modification, are permitted provided that the following conditions are // met: @@ -193,9 +193,10 @@ const uint32_t kMaxUInt32 = 0xFFFFFFFFu; const int kCharSize = sizeof(char); // NOLINT const int kShortSize = sizeof(short); // NOLINT +const int kIntSize = sizeof(int); // NOLINT const int kDoubleSize = sizeof(double); // NOLINT const int kIntptrSize = sizeof(intptr_t); // NOLINT -// kIntSize and kPointerSize are defined in include/v8.h. +const int kPointerSize = sizeof(void*); // NOLINT #if V8_HOST_ARCH_64_BIT const int kPointerSizeLog2 = 3; @@ -207,38 +208,6 @@ const intptr_t kIntptrSignBit = 0x80000000; const uintptr_t kUintptrAllBitsSet = 0xFFFFFFFFu; #endif -// Mask for the sign bit in a smi. -const intptr_t kSmiSignMask = kIntptrSignBit; - -const int kObjectAlignmentBits = kPointerSizeLog2; -const intptr_t kObjectAlignment = 1 << kObjectAlignmentBits; -const intptr_t kObjectAlignmentMask = kObjectAlignment - 1; - -// Desired alignment for pointers. -const intptr_t kPointerAlignment = (1 << kPointerSizeLog2); -const intptr_t kPointerAlignmentMask = kPointerAlignment - 1; - -// Desired alignment for maps. -#if V8_HOST_ARCH_64_BIT -const intptr_t kMapAlignmentBits = kObjectAlignmentBits; -#else -const intptr_t kMapAlignmentBits = kObjectAlignmentBits + 3; -#endif -const intptr_t kMapAlignment = (1 << kMapAlignmentBits); -const intptr_t kMapAlignmentMask = kMapAlignment - 1; - -// Desired alignment for generated code is 32 bytes (to improve cache line -// utilization). -const int kCodeAlignmentBits = 5; -const intptr_t kCodeAlignment = 1 << kCodeAlignmentBits; -const intptr_t kCodeAlignmentMask = kCodeAlignment - 1; - -// Tag information for Failure. -const int kFailureTag = 3; -const int kFailureTagSize = 2; -const intptr_t kFailureTagMask = (1 << kFailureTagSize) - 1; - - const int kBitsPerByte = 8; const int kBitsPerByteLog2 = 3; const int kBitsPerPointer = kPointerSize * kBitsPerByte; @@ -254,364 +223,6 @@ const int kBinary32MinExponent = 0x01; const int kBinary32MantissaBits = 23; const int kBinary32ExponentShift = 23; -// Zap-value: The value used for zapping dead objects. -// Should be a recognizable hex value tagged as a heap object pointer. -#ifdef V8_HOST_ARCH_64_BIT -const Address kZapValue = - reinterpret_cast<Address>(V8_UINT64_C(0xdeadbeedbeadbeed)); -const Address kHandleZapValue = - reinterpret_cast<Address>(V8_UINT64_C(0x1baddead0baddead)); -const Address kFromSpaceZapValue = - reinterpret_cast<Address>(V8_UINT64_C(0x1beefdad0beefdad)); -const uint64_t kDebugZapValue = 0xbadbaddbbadbaddb; -#else -const Address kZapValue = reinterpret_cast<Address>(0xdeadbeed); -const Address kHandleZapValue = reinterpret_cast<Address>(0xbaddead); -const Address kFromSpaceZapValue = reinterpret_cast<Address>(0xbeefdad); -const uint32_t kDebugZapValue = 0xbadbaddb; -#endif - - -// Number of bits to represent the page size for paged spaces. The value of 13 -// gives 8K bytes per page. -const int kPageSizeBits = 13; - -// On Intel architecture, cache line size is 64 bytes. -// On ARM it may be less (32 bytes), but as far this constant is -// used for aligning data, it doesn't hurt to align on a greater value. -const int kProcessorCacheLineSize = 64; - -// Constants relevant to double precision floating point numbers. - -// Quiet NaNs have bits 51 to 62 set, possibly the sign bit, and no -// other bits set. -const uint64_t kQuietNaNMask = static_cast<uint64_t>(0xfff) << 51; -// If looking only at the top 32 bits, the QNaN mask is bits 19 to 30. -const uint32_t kQuietNaNHighBitsMask = 0xfff << (51 - 32); - - -// ----------------------------------------------------------------------------- -// Forward declarations for frequently used classes -// (sorted alphabetically) - -class AccessorInfo; -class Allocation; -class Arguments; -class Assembler; -class AssertNoAllocation; -class BreakableStatement; -class Code; -class CodeGenerator; -class CodeStub; -class Context; -class Debug; -class Debugger; -class DebugInfo; -class Descriptor; -class DescriptorArray; -class Expression; -class ExternalReference; -class FixedArray; -class FunctionEntry; -class FunctionLiteral; -class FunctionTemplateInfo; -class NumberDictionary; -class StringDictionary; -class FreeStoreAllocationPolicy; -template <typename T> class Handle; -class Heap; -class HeapObject; -class IC; -class InterceptorInfo; -class IterationStatement; -class JSArray; -class JSFunction; -class JSObject; -class LargeObjectSpace; -template <typename T, class P = FreeStoreAllocationPolicy> class List; -class LookupResult; -class MacroAssembler; -class Map; -class MapSpace; -class MarkCompactCollector; -class NewSpace; -class NodeVisitor; -class Object; -class MaybeObject; -class OldSpace; -class Property; -class Proxy; -class RegExpNode; -struct RegExpCompileData; -class RegExpTree; -class RegExpCompiler; -class RegExpVisitor; -class Scope; -template<class Allocator = FreeStoreAllocationPolicy> class ScopeInfo; -class SerializedScopeInfo; -class Script; -class Slot; -class Smi; -template <typename Config, class Allocator = FreeStoreAllocationPolicy> - class SplayTree; -class Statement; -class String; -class Struct; -class SwitchStatement; -class AstVisitor; -class Variable; -class VariableProxy; -class RelocInfo; -class Deserializer; -class MessageLocation; -class ObjectGroup; -class TickSample; -class VirtualMemory; -class Mutex; - -typedef bool (*WeakSlotCallback)(Object** pointer); - -// ----------------------------------------------------------------------------- -// Miscellaneous - -// NOTE: SpaceIterator depends on AllocationSpace enumeration values being -// consecutive. -enum AllocationSpace { - NEW_SPACE, // Semispaces collected with copying collector. - OLD_POINTER_SPACE, // May contain pointers to new space. - OLD_DATA_SPACE, // Must not have pointers to new space. - CODE_SPACE, // No pointers to new space, marked executable. - MAP_SPACE, // Only and all map objects. - CELL_SPACE, // Only and all cell objects. - LO_SPACE, // Promoted large objects. - - FIRST_SPACE = NEW_SPACE, - LAST_SPACE = LO_SPACE, - FIRST_PAGED_SPACE = OLD_POINTER_SPACE, - LAST_PAGED_SPACE = CELL_SPACE -}; -const int kSpaceTagSize = 3; -const int kSpaceTagMask = (1 << kSpaceTagSize) - 1; - - -// A flag that indicates whether objects should be pretenured when -// allocated (allocated directly into the old generation) or not -// (allocated in the young generation if the object size and type -// allows). -enum PretenureFlag { NOT_TENURED, TENURED }; - -enum GarbageCollector { SCAVENGER, MARK_COMPACTOR }; - -enum Executability { NOT_EXECUTABLE, EXECUTABLE }; - -enum VisitMode { VISIT_ALL, VISIT_ALL_IN_SCAVENGE, VISIT_ONLY_STRONG }; - -// Flag indicating whether code is built into the VM (one of the natives files). -enum NativesFlag { NOT_NATIVES_CODE, NATIVES_CODE }; - - -// A CodeDesc describes a buffer holding instructions and relocation -// information. The instructions start at the beginning of the buffer -// and grow forward, the relocation information starts at the end of -// the buffer and grows backward. -// -// |<--------------- buffer_size ---------------->| -// |<-- instr_size -->| |<-- reloc_size -->| -// +==================+========+==================+ -// | instructions | free | reloc info | -// +==================+========+==================+ -// ^ -// | -// buffer - -struct CodeDesc { - byte* buffer; - int buffer_size; - int instr_size; - int reloc_size; - Assembler* origin; -}; - - -// Callback function on object slots, used for iterating heap object slots in -// HeapObjects, global pointers to heap objects, etc. The callback allows the -// callback function to change the value of the slot. -typedef void (*ObjectSlotCallback)(HeapObject** pointer); - - -// Callback function used for iterating objects in heap spaces, -// for example, scanning heap objects. -typedef int (*HeapObjectCallback)(HeapObject* obj); - - -// Callback function used for checking constraints when copying/relocating -// objects. Returns true if an object can be copied/relocated from its -// old_addr to a new_addr. -typedef bool (*ConstraintCallback)(Address new_addr, Address old_addr); - - -// Callback function on inline caches, used for iterating over inline caches -// in compiled code. -typedef void (*InlineCacheCallback)(Code* code, Address ic); - - -// State for inline cache call sites. Aliased as IC::State. -enum InlineCacheState { - // Has never been executed. - UNINITIALIZED, - // Has been executed but monomorhic state has been delayed. - PREMONOMORPHIC, - // Has been executed and only one receiver type has been seen. - MONOMORPHIC, - // Like MONOMORPHIC but check failed due to prototype. - MONOMORPHIC_PROTOTYPE_FAILURE, - // Multiple receiver types have been seen. - MEGAMORPHIC, - // Special states for debug break or step in prepare stubs. - DEBUG_BREAK, - DEBUG_PREPARE_STEP_IN -}; - - -enum InLoopFlag { - NOT_IN_LOOP, - IN_LOOP -}; - - -enum CallFunctionFlags { - NO_CALL_FUNCTION_FLAGS = 0, - RECEIVER_MIGHT_BE_VALUE = 1 << 0 // Receiver might not be a JSObject. -}; - - -enum InlineCacheHolderFlag { - OWN_MAP, // For fast properties objects. - PROTOTYPE_MAP // For slow properties objects (except GlobalObjects). -}; - - -// Type of properties. -// Order of properties is significant. -// Must fit in the BitField PropertyDetails::TypeField. -// A copy of this is in mirror-debugger.js. -enum PropertyType { - NORMAL = 0, // only in slow mode - FIELD = 1, // only in fast mode - CONSTANT_FUNCTION = 2, // only in fast mode - CALLBACKS = 3, - INTERCEPTOR = 4, // only in lookup results, not in descriptors. - MAP_TRANSITION = 5, // only in fast mode - CONSTANT_TRANSITION = 6, // only in fast mode - NULL_DESCRIPTOR = 7, // only in fast mode - // All properties before MAP_TRANSITION are real. - FIRST_PHANTOM_PROPERTY_TYPE = MAP_TRANSITION, - // There are no IC stubs for NULL_DESCRIPTORS. Therefore, - // NULL_DESCRIPTOR can be used as the type flag for IC stubs for - // nonexistent properties. - NONEXISTENT = NULL_DESCRIPTOR -}; - - -// Whether to remove map transitions and constant transitions from a -// DescriptorArray. -enum TransitionFlag { - REMOVE_TRANSITIONS, - KEEP_TRANSITIONS -}; - - -// Union used for fast testing of specific double values. -union DoubleRepresentation { - double value; - int64_t bits; - DoubleRepresentation(double x) { value = x; } -}; - - -// Union used for customized checking of the IEEE double types -// inlined within v8 runtime, rather than going to the underlying -// platform headers and libraries -union IeeeDoubleLittleEndianArchType { - double d; - struct { - unsigned int man_low :32; - unsigned int man_high :20; - unsigned int exp :11; - unsigned int sign :1; - } bits; -}; - - -union IeeeDoubleBigEndianArchType { - double d; - struct { - unsigned int sign :1; - unsigned int exp :11; - unsigned int man_high :20; - unsigned int man_low :32; - } bits; -}; - - -// AccessorCallback -struct AccessorDescriptor { - MaybeObject* (*getter)(Object* object, void* data); - MaybeObject* (*setter)(JSObject* object, Object* value, void* data); - void* data; -}; - - -// Logging and profiling. -// A StateTag represents a possible state of the VM. When compiled with -// ENABLE_VMSTATE_TRACKING, the logger maintains a stack of these. -// Creating a VMState object enters a state by pushing on the stack, and -// destroying a VMState object leaves a state by popping the current state -// from the stack. - -#define STATE_TAG_LIST(V) \ - V(JS) \ - V(GC) \ - V(COMPILER) \ - V(OTHER) \ - V(EXTERNAL) - -enum StateTag { -#define DEF_STATE_TAG(name) name, - STATE_TAG_LIST(DEF_STATE_TAG) -#undef DEF_STATE_TAG - // Pseudo-types. - state_tag_count -}; - - -// ----------------------------------------------------------------------------- -// Macros - -// Testers for test. - -#define HAS_SMI_TAG(value) \ - ((reinterpret_cast<intptr_t>(value) & kSmiTagMask) == kSmiTag) - -#define HAS_FAILURE_TAG(value) \ - ((reinterpret_cast<intptr_t>(value) & kFailureTagMask) == kFailureTag) - -// OBJECT_POINTER_ALIGN returns the value aligned as a HeapObject pointer -#define OBJECT_POINTER_ALIGN(value) \ - (((value) + kObjectAlignmentMask) & ~kObjectAlignmentMask) - -// POINTER_SIZE_ALIGN returns the value aligned as a pointer. -#define POINTER_SIZE_ALIGN(value) \ - (((value) + kPointerAlignmentMask) & ~kPointerAlignmentMask) - -// MAP_POINTER_ALIGN returns the value aligned as a map pointer. -#define MAP_POINTER_ALIGN(value) \ - (((value) + kMapAlignmentMask) & ~kMapAlignmentMask) - -// CODE_POINTER_ALIGN returns the value aligned as a generated code segment. -#define CODE_POINTER_ALIGN(value) \ - (((value) + kCodeAlignmentMask) & ~kCodeAlignmentMask) - // The expression OFFSET_OF(type, field) computes the byte-offset // of the specified field relative to the containing type. This // corresponds to 'offsetof' (in stddef.h), except that it doesn't @@ -668,26 +279,6 @@ F FUNCTION_CAST(Address addr) { DISALLOW_COPY_AND_ASSIGN(TypeName) -// Support for tracking C++ memory allocation. Insert TRACK_MEMORY("Fisk") -// inside a C++ class and new and delete will be overloaded so logging is -// performed. -// This file (globals.h) is included before log.h, so we use direct calls to -// the Logger rather than the LOG macro. -#ifdef DEBUG -#define TRACK_MEMORY(name) \ - void* operator new(size_t size) { \ - void* result = ::operator new(size); \ - Logger::NewEvent(name, result, size); \ - return result; \ - } \ - void operator delete(void* object) { \ - Logger::DeleteEvent(name, object); \ - ::operator delete(object); \ - } -#else -#define TRACK_MEMORY(name) -#endif - // Define used for helping GCC to make better inlining. Don't bother for debug // builds. On GCC 3.4.5 using __attribute__((always_inline)) causes compilation // errors in debug build. @@ -711,20 +302,12 @@ F FUNCTION_CAST(Address addr) { #define MUST_USE_RESULT #endif +// ----------------------------------------------------------------------------- +// Forward declarations for frequently used classes +// (sorted alphabetically) -// Feature flags bit positions. They are mostly based on the CPUID spec. -// (We assign CPUID itself to one of the currently reserved bits -- -// feel free to change this if needed.) -// On X86/X64, values below 32 are bits in EDX, values above 32 are bits in ECX. -enum CpuFeature { SSE4_1 = 32 + 19, // x86 - SSE3 = 32 + 0, // x86 - SSE2 = 26, // x86 - CMOV = 15, // x86 - RDTSC = 4, // x86 - CPUID = 10, // x86 - VFP3 = 1, // ARM - ARMv7 = 2, // ARM - SAHF = 0}; // x86 +class FreeStoreAllocationPolicy; +template <typename T, class P = FreeStoreAllocationPolicy> class List; } } // namespace v8::internal diff --git a/src/handles.cc b/src/handles.cc index 7a46bc3e..37a5011c 100644 --- a/src/handles.cc +++ b/src/handles.cc @@ -37,6 +37,7 @@ #include "global-handles.h" #include "natives.h" #include "runtime.h" +#include "string-search.h" #include "stub-cache.h" namespace v8 { @@ -142,7 +143,7 @@ Handle<JSGlobalProxy> ReinitializeJSGlobalProxy( void SetExpectedNofProperties(Handle<JSFunction> 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. @@ -499,48 +500,59 @@ void InitScriptLineEnds(Handle<Script> script) { Handle<FixedArray> array = CalculateLineEnds(src, true); + if (*array != Heap::empty_fixed_array()) { + array->set_map(Heap::fixed_cow_array_map()); + } + script->set_line_ends(*array); ASSERT(script->line_ends()->IsFixedArray()); } -Handle<FixedArray> CalculateLineEnds(Handle<String> src, - bool with_imaginary_last_new_line) { - const int src_len = src->length(); - Handle<String> new_line = Factory::NewStringFromAscii(CStrVector("\n")); +template <typename SourceChar> +static void CalculateLineEnds(List<int>* line_ends, + Vector<const SourceChar> src, + bool with_last_line) { + const int src_len = src.length(); + StringSearch<char, SourceChar> search(CStrVector("\n")); - // Pass 1: Identify line count. - int line_count = 0; + // Find and record line ends. int position = 0; while (position != -1 && position < src_len) { - position = Runtime::StringMatch(src, new_line, position); + position = search.Search(src, position); if (position != -1) { + line_ends->Add(position); position++; - } - if (position != -1) { - line_count++; - } else if (with_imaginary_last_new_line) { + } else if (with_last_line) { // Even if the last line misses a line end, it is counted. - line_count++; + line_ends->Add(src_len); + return; } } +} - // Pass 2: Fill in line ends positions - Handle<FixedArray> array = Factory::NewFixedArray(line_count); - int array_index = 0; - position = 0; - while (position != -1 && position < src_len) { - position = Runtime::StringMatch(src, new_line, position); - if (position != -1) { - array->set(array_index++, Smi::FromInt(position++)); - } else if (with_imaginary_last_new_line) { - // If the script does not end with a line ending add the final end - // position as just past the last line ending. - array->set(array_index++, Smi::FromInt(src_len)); + +Handle<FixedArray> CalculateLineEnds(Handle<String> src, + bool with_last_line) { + src = FlattenGetString(src); + // Rough estimate of line count based on a roughly estimated average + // length of (unpacked) code. + int line_count_estimate = src->length() >> 4; + List<int> line_ends(line_count_estimate); + { + AssertNoAllocation no_heap_allocation; // ensure vectors stay valid. + // Dispatch on type of strings. + if (src->IsAsciiRepresentation()) { + CalculateLineEnds(&line_ends, src->ToAsciiVector(), with_last_line); + } else { + CalculateLineEnds(&line_ends, src->ToUC16Vector(), with_last_line); } } - ASSERT(array_index == line_count); - + int line_count = line_ends.length(); + Handle<FixedArray> array = Factory::NewFixedArray(line_count); + for (int i = 0; i < line_count; i++) { + array->set(i, Smi::FromInt(line_ends[i])); + } return array; } @@ -552,11 +564,11 @@ int GetScriptLineNumber(Handle<Script> script, int code_pos) { FixedArray* line_ends_array = FixedArray::cast(script->line_ends()); const int line_ends_len = line_ends_array->length(); - if (!line_ends_len) - return -1; + if (!line_ends_len) return -1; - if ((Smi::cast(line_ends_array->get(0)))->value() >= code_pos) + if ((Smi::cast(line_ends_array->get(0)))->value() >= code_pos) { return script->line_offset()->value(); + } int left = 0; int right = line_ends_len; diff --git a/src/hashmap.cc b/src/hashmap.cc index 3c4e5cdc..1422afdc 100644 --- a/src/hashmap.cc +++ b/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<Entry*>(allocator_->New(capacity * sizeof(Entry))); if (map_ == NULL) { - V8::FatalProcessOutOfMemory("HashMap::Initialize"); + v8::internal::FatalProcessOutOfMemory("HashMap::Initialize"); return; } capacity_ = capacity; diff --git a/src/heap-profiler.cc b/src/heap-profiler.cc index e47d66f9..91ac9867 100644 --- a/src/heap-profiler.cc +++ b/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<Value> 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<char> s_name( constructor->ToCString(DISALLOW_NULLS, ROBUST_STRING_TRAVERSAL)); LOG(HeapSampleJSProducerEvent(GetConstructorName(*s_name), @@ -788,15 +789,13 @@ void AggregatedHeapSnapshotGenerator::CalculateStringsStats() { void AggregatedHeapSnapshotGenerator::CollectStats(HeapObject* obj) { InstanceType type = obj->map()->instance_type(); ASSERT(0 <= type && type <= LAST_TYPE); - if (!FreeListNode::IsFreeListNode(obj)) { - agg_snapshot_->info()[type].increment_number(1); - agg_snapshot_->info()[type].increment_bytes(obj->Size()); - } + agg_snapshot_->info()[type].increment_number(1); + agg_snapshot_->info()[type].increment_bytes(obj->Size()); } void AggregatedHeapSnapshotGenerator::GenerateSnapshot() { - HeapIterator iterator; + HeapIterator iterator(HeapIterator::kPreciseFiltering); for (HeapObject* obj = iterator.next(); obj != NULL; obj = iterator.next()) { CollectStats(obj); agg_snapshot_->js_cons_profile()->CollectStats(obj); @@ -888,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); } } @@ -929,10 +929,16 @@ class AllocatingRetainersIterator { void Call(const JSObjectsCluster& cluster, const NumberAndSizeInfo& number_and_size) { int child_index, retainer_index; - map_->CountReference(ClusterAsHeapObject(cluster), child_, - &child_index, &retainer_index); - map_->Map(ClusterAsHeapObject(cluster))->SetElementReference( - child_index, number_and_size.number(), child_entry_, retainer_index); + map_->CountReference(ClusterAsHeapObject(cluster), + child_, + &child_index, + &retainer_index); + map_->Map(ClusterAsHeapObject(cluster))->SetIndexedReference( + HeapGraphEdge::kElement, + child_index, + number_and_size.number(), + child_entry_, + retainer_index); } private: @@ -1044,7 +1050,7 @@ void AggregatedHeapSnapshotGenerator::FillHeapSnapshot(HeapSnapshot* snapshot) { if (agg_snapshot_->info()[i].bytes() > 0) { AddEntryFromAggregatedSnapshot(snapshot, &root_child_index, - HeapEntry::kInternal, + HeapEntry::kHidden, agg_snapshot_->info()[i].name(), agg_snapshot_->info()[i].number(), agg_snapshot_->info()[i].bytes(), @@ -1060,6 +1066,8 @@ void AggregatedHeapSnapshotGenerator::FillHeapSnapshot(HeapSnapshot* snapshot) { // Fill up references. IterateRetainers<AllocatingRetainersIterator>(&entries_map); + + snapshot->SetDominatorsToSelf(); } diff --git a/src/heap.cc b/src/heap.cc index 134f40e5..26859d7c 100644 --- a/src/heap.cc +++ b/src/heap.cc @@ -38,7 +38,7 @@ #include "mark-compact.h" #include "natives.h" #include "objects-visiting.h" -#include "scanner.h" +#include "scanner-base.h" #include "scopeinfo.h" #include "snapshot.h" #include "v8threads.h" @@ -79,25 +79,34 @@ int Heap::amount_of_external_allocated_memory_at_last_global_gc_ = 0; // semispace_size_ should be a power of 2 and old_generation_size_ should be // a multiple of Page::kPageSize. #if defined(ANDROID) -int Heap::max_semispace_size_ = 2*MB; +static const int default_max_semispace_size_ = 2*MB; intptr_t Heap::max_old_generation_size_ = 192*MB; int Heap::initial_semispace_size_ = 128*KB; intptr_t Heap::code_range_size_ = 0; intptr_t Heap::max_executable_size_ = max_old_generation_size_; #elif defined(V8_TARGET_ARCH_X64) -int Heap::max_semispace_size_ = 16*MB; +static const int default_max_semispace_size_ = 16*MB; intptr_t Heap::max_old_generation_size_ = 1*GB; int Heap::initial_semispace_size_ = 1*MB; intptr_t Heap::code_range_size_ = 512*MB; intptr_t Heap::max_executable_size_ = 256*MB; #else -int Heap::max_semispace_size_ = 8*MB; +static const int default_max_semispace_size_ = 8*MB; intptr_t Heap::max_old_generation_size_ = 512*MB; int Heap::initial_semispace_size_ = 512*KB; intptr_t Heap::code_range_size_ = 0; intptr_t Heap::max_executable_size_ = 128*MB; #endif +// Allow build-time customization of the max semispace size. Building +// V8 with snapshots and a non-default max semispace size is much +// easier if you can define it as part of the build environment. +#if defined(V8_MAX_SEMISPACE_SIZE) +int Heap::max_semispace_size_ = V8_MAX_SEMISPACE_SIZE; +#else +int Heap::max_semispace_size_ = default_max_semispace_size_; +#endif + // The snapshot semispace size will be the default semispace size if // snapshotting is used and will be the requested semispace size as // set up by ConfigureHeap otherwise. @@ -395,7 +404,7 @@ intptr_t Heap::SizeOfObjects() { intptr_t total = 0; AllSpaces spaces; for (Space* space = spaces.next(); space != NULL; space = spaces.next()) { - total += space->Size(); + total += space->SizeOfObjects(); } return total; } @@ -3240,7 +3249,8 @@ MaybeObject* Heap::AllocateStringFromUtf8(Vector<const char> string, const uc32 kMaxSupportedChar = 0xFFFF; // Count the number of characters in the UTF-8 string and check if // it is an ASCII string. - Access<Scanner::Utf8Decoder> decoder(Scanner::utf8_decoder()); + Access<ScannerConstants::Utf8Decoder> + decoder(ScannerConstants::utf8_decoder()); decoder->Reset(string.start(), string.length()); int chars = 0; bool is_ascii = true; @@ -4399,13 +4409,10 @@ void Heap::RecordStats(HeapStats* stats, bool take_snapshot) { MemoryAllocator::Size() + MemoryAllocator::Available(); *stats->os_error = OS::GetLastError(); if (take_snapshot) { - HeapIterator iterator; + HeapIterator iterator(HeapIterator::kPreciseFiltering); for (HeapObject* obj = iterator.next(); obj != NULL; obj = iterator.next()) { - // Note: snapshot won't be precise because IsFreeListNode returns true - // for any bytearray. - if (FreeListNode::IsFreeListNode(obj)) continue; InstanceType type = obj->map()->instance_type(); ASSERT(0 <= type && type <= LAST_TYPE); stats->objects_per_type[type]++; @@ -4760,7 +4767,17 @@ OldSpace* OldSpaces::next() { } -SpaceIterator::SpaceIterator() : current_space_(FIRST_SPACE), iterator_(NULL) { +SpaceIterator::SpaceIterator() + : current_space_(FIRST_SPACE), + iterator_(NULL), + size_func_(NULL) { +} + + +SpaceIterator::SpaceIterator(HeapObjectCallback size_func) + : current_space_(FIRST_SPACE), + iterator_(NULL), + size_func_(size_func) { } @@ -4798,25 +4815,25 @@ ObjectIterator* SpaceIterator::CreateIterator() { switch (current_space_) { case NEW_SPACE: - iterator_ = new SemiSpaceIterator(Heap::new_space()); + iterator_ = new SemiSpaceIterator(Heap::new_space(), size_func_); break; case OLD_POINTER_SPACE: - iterator_ = new HeapObjectIterator(Heap::old_pointer_space()); + iterator_ = new HeapObjectIterator(Heap::old_pointer_space(), size_func_); break; case OLD_DATA_SPACE: - iterator_ = new HeapObjectIterator(Heap::old_data_space()); + iterator_ = new HeapObjectIterator(Heap::old_data_space(), size_func_); break; case CODE_SPACE: - iterator_ = new HeapObjectIterator(Heap::code_space()); + iterator_ = new HeapObjectIterator(Heap::code_space(), size_func_); break; case MAP_SPACE: - iterator_ = new HeapObjectIterator(Heap::map_space()); + iterator_ = new HeapObjectIterator(Heap::map_space(), size_func_); break; case CELL_SPACE: - iterator_ = new HeapObjectIterator(Heap::cell_space()); + iterator_ = new HeapObjectIterator(Heap::cell_space(), size_func_); break; case LO_SPACE: - iterator_ = new LargeObjectIterator(Heap::lo_space()); + iterator_ = new LargeObjectIterator(Heap::lo_space(), size_func_); break; } @@ -4826,7 +4843,54 @@ ObjectIterator* SpaceIterator::CreateIterator() { } -HeapIterator::HeapIterator() { +class FreeListNodesFilter { + public: + FreeListNodesFilter() { + MarkFreeListNodes(); + } + + inline bool IsFreeListNode(HeapObject* object) { + if (object->IsMarked()) { + object->ClearMark(); + return true; + } else { + return false; + } + } + + private: + void MarkFreeListNodes() { + Heap::old_pointer_space()->MarkFreeListNodes(); + Heap::old_data_space()->MarkFreeListNodes(); + MarkCodeSpaceFreeListNodes(); + Heap::map_space()->MarkFreeListNodes(); + Heap::cell_space()->MarkFreeListNodes(); + } + + void MarkCodeSpaceFreeListNodes() { + // For code space, using FreeListNode::IsFreeListNode is OK. + HeapObjectIterator iter(Heap::code_space()); + for (HeapObject* obj = iter.next_object(); + obj != NULL; + obj = iter.next_object()) { + if (FreeListNode::IsFreeListNode(obj)) obj->SetMark(); + } + } + + AssertNoAllocation no_alloc; +}; + + +HeapIterator::HeapIterator() + : filtering_(HeapIterator::kNoFiltering), + filter_(NULL) { + Init(); +} + + +HeapIterator::HeapIterator(HeapIterator::FreeListNodesFiltering filtering) + : filtering_(filtering), + filter_(NULL) { Init(); } @@ -4838,20 +4902,44 @@ HeapIterator::~HeapIterator() { void HeapIterator::Init() { // Start the iteration. - space_iterator_ = new SpaceIterator(); + if (filtering_ == kPreciseFiltering) { + filter_ = new FreeListNodesFilter; + space_iterator_ = + new SpaceIterator(MarkCompactCollector::SizeOfMarkedObject); + } else { + space_iterator_ = new SpaceIterator; + } object_iterator_ = space_iterator_->next(); } void HeapIterator::Shutdown() { +#ifdef DEBUG + // Assert that in precise mode we have iterated through all + // objects. Otherwise, heap will be left in an inconsistent state. + if (filtering_ == kPreciseFiltering) { + ASSERT(object_iterator_ == NULL); + } +#endif // Make sure the last iterator is deallocated. delete space_iterator_; space_iterator_ = NULL; object_iterator_ = NULL; + delete filter_; + filter_ = NULL; } HeapObject* HeapIterator::next() { + if (filter_ == NULL) return NextObject(); + + HeapObject* obj = NextObject(); + while (obj != NULL && filter_->IsFreeListNode(obj)) obj = NextObject(); + return obj; +} + + +HeapObject* HeapIterator::NextObject() { // No iterator means we are done. if (object_iterator_ == NULL) return NULL; @@ -1558,6 +1558,7 @@ class PagedSpaces BASE_EMBEDDED { class SpaceIterator : public Malloced { public: SpaceIterator(); + explicit SpaceIterator(HeapObjectCallback size_func); virtual ~SpaceIterator(); bool has_next(); @@ -1568,17 +1569,31 @@ class SpaceIterator : public Malloced { int current_space_; // from enum AllocationSpace. ObjectIterator* iterator_; // object iterator for the current space. + HeapObjectCallback size_func_; }; -// A HeapIterator provides iteration over the whole heap It aggregates a the -// specific iterators for the different spaces as these can only iterate over -// one space only. +// A HeapIterator provides iteration over the whole heap. It +// aggregates the specific iterators for the different spaces as +// these can only iterate over one space only. +// +// HeapIterator can skip free list nodes (that is, de-allocated heap +// objects that still remain in the heap). As implementation of free +// nodes filtering uses GC marks, it can't be used during MS/MC GC +// phases. Also, it is forbidden to interrupt iteration in this mode, +// as this will leave heap objects marked (and thus, unusable). +class FreeListNodesFilter; class HeapIterator BASE_EMBEDDED { public: - explicit HeapIterator(); - virtual ~HeapIterator(); + enum FreeListNodesFiltering { + kNoFiltering, + kPreciseFiltering + }; + + HeapIterator(); + explicit HeapIterator(FreeListNodesFiltering filtering); + ~HeapIterator(); HeapObject* next(); void reset(); @@ -1586,10 +1601,12 @@ class HeapIterator BASE_EMBEDDED { private: // Perform the initialization. void Init(); - // Perform all necessary shutdown (destruction) work. void Shutdown(); + HeapObject* NextObject(); + FreeListNodesFiltering filtering_; + FreeListNodesFilter* filter_; // Space iterator for iterating all the spaces. SpaceIterator* space_iterator_; // Object iterator for the space currently being iterated. diff --git a/src/ia32/code-stubs-ia32.cc b/src/ia32/code-stubs-ia32.cc index a7d658bd..5975ad27 100644 --- a/src/ia32/code-stubs-ia32.cc +++ b/src/ia32/code-stubs-ia32.cc @@ -80,8 +80,9 @@ void FastNewClosureStub::Generate(MacroAssembler* masm) { __ pop(edx); __ push(esi); __ push(edx); + __ push(Immediate(Factory::false_value())); __ push(ecx); // Restore return address. - __ TailCallRuntime(Runtime::kNewClosure, 2, 1); + __ TailCallRuntime(Runtime::kNewClosure, 3, 1); } @@ -3058,35 +3059,6 @@ void CEntryStub::GenerateThrowTOS(MacroAssembler* masm) { } -void ApiGetterEntryStub::Generate(MacroAssembler* masm) { - __ PrepareCallApiFunction(kStackSpace, kArgc); - STATIC_ASSERT(kArgc == 2); - __ mov(ApiParameterOperand(0), ebx); // name. - __ mov(ApiParameterOperand(1), eax); // arguments pointer. - __ CallApiFunctionAndReturn(fun(), kArgc); -} - - -void ApiCallEntryStub::Generate(MacroAssembler* masm) { - __ PrepareCallApiFunction(kStackSpace, kArgc); - STATIC_ASSERT(kArgc == 5); - - // Allocate the v8::Arguments structure in the arguments' space since - // it's not controlled by GC. - __ mov(ApiParameterOperand(1), eax); // v8::Arguments::implicit_args_. - __ mov(ApiParameterOperand(2), ebx); // v8::Arguments::values_. - __ mov(ApiParameterOperand(3), edx); // v8::Arguments::length_. - // v8::Arguments::is_construct_call_. - __ mov(ApiParameterOperand(4), Immediate(0)); - - // v8::InvocationCallback's argument. - __ lea(eax, ApiParameterOperand(1)); - __ mov(ApiParameterOperand(0), eax); - - __ CallApiFunctionAndReturn(fun(), kArgc); -} - - void CEntryStub::GenerateCore(MacroAssembler* masm, Label* throw_normal_exception, Label* throw_termination_exception, diff --git a/src/ia32/codegen-ia32.cc b/src/ia32/codegen-ia32.cc index 6f4ef87e..f5ab357f 100644 --- a/src/ia32/codegen-ia32.cc +++ b/src/ia32/codegen-ia32.cc @@ -154,7 +154,7 @@ CodeGenerator::CodeGenerator(MacroAssembler* masm) safe_int32_mode_enabled_(true), function_return_is_shadowed_(false), in_spilled_code_(false), - jit_cookie_((FLAG_mask_constants_with_cookie) ? V8::Random() : 0) { + jit_cookie_((FLAG_mask_constants_with_cookie) ? V8::RandomPrivate() : 0) { } @@ -686,10 +686,10 @@ void CodeGenerator::Load(Expression* expr) { void CodeGenerator::LoadGlobal() { if (in_spilled_code()) { - frame_->EmitPush(GlobalObject()); + frame_->EmitPush(GlobalObjectOperand()); } else { Result temp = allocator_->Allocate(); - __ mov(temp.reg(), GlobalObject()); + __ mov(temp.reg(), GlobalObjectOperand()); frame_->Push(&temp); } } @@ -698,7 +698,7 @@ void CodeGenerator::LoadGlobal() { void CodeGenerator::LoadGlobalReceiver() { Result temp = allocator_->Allocate(); Register reg = temp.reg(); - __ mov(reg, GlobalObject()); + __ mov(reg, GlobalObjectOperand()); __ mov(reg, FieldOperand(reg, GlobalObject::kGlobalReceiverOffset)); frame_->Push(&temp); } @@ -4897,7 +4897,8 @@ void CodeGenerator::VisitDebuggerStatement(DebuggerStatement* node) { Result CodeGenerator::InstantiateFunction( - Handle<SharedFunctionInfo> function_info) { + Handle<SharedFunctionInfo> function_info, + bool pretenure) { // The inevitable call will sync frame elements to memory anyway, so // we do it eagerly to allow us to push the arguments directly into // place. @@ -4905,7 +4906,9 @@ Result CodeGenerator::InstantiateFunction( // Use the fast case closure allocation code that allocates in new // space for nested functions that don't need literals cloning. - if (scope()->is_function_scope() && function_info->num_literals() == 0) { + if (scope()->is_function_scope() && + function_info->num_literals() == 0 && + !pretenure) { FastNewClosureStub stub; frame()->EmitPush(Immediate(function_info)); return frame()->CallStub(&stub, 1); @@ -4914,7 +4917,10 @@ Result CodeGenerator::InstantiateFunction( // shared function info. frame()->EmitPush(esi); frame()->EmitPush(Immediate(function_info)); - return frame()->CallRuntime(Runtime::kNewClosure, 2); + frame()->EmitPush(Immediate(pretenure + ? Factory::true_value() + : Factory::false_value())); + return frame()->CallRuntime(Runtime::kNewClosure, 3); } } @@ -4930,7 +4936,7 @@ void CodeGenerator::VisitFunctionLiteral(FunctionLiteral* node) { SetStackOverflow(); return; } - Result result = InstantiateFunction(function_info); + Result result = InstantiateFunction(function_info, node->pretenure()); frame()->Push(&result); } @@ -4939,7 +4945,7 @@ void CodeGenerator::VisitSharedFunctionInfoLiteral( SharedFunctionInfoLiteral* node) { ASSERT(!in_safe_int32_mode()); Comment cmnt(masm_, "[ SharedFunctionInfoLiteral"); - Result result = InstantiateFunction(node->shared_function_info()); + Result result = InstantiateFunction(node->shared_function_info(), false); frame()->Push(&result); } @@ -6294,6 +6300,18 @@ void CodeGenerator::VisitCall(Call* node) { // Push the receiver onto the frame. Load(property->obj()); + // Load the name of the function. + Load(property->key()); + + // Swap the name of the function and the receiver on the stack to follow + // the calling convention for call ICs. + Result key = frame_->Pop(); + Result receiver = frame_->Pop(); + frame_->Push(&key); + frame_->Push(&receiver); + key.Unuse(); + receiver.Unuse(); + // Load the arguments. int arg_count = args->length(); for (int i = 0; i < arg_count; i++) { @@ -6301,15 +6319,14 @@ void CodeGenerator::VisitCall(Call* node) { frame_->SpillTop(); } - // Load the name of the function. - Load(property->key()); - - // Call the IC initialization code. + // Place the key on top of stack and call the IC initialization code. + frame_->PushElementAt(arg_count + 1); CodeForSourcePosition(node->position()); Result result = frame_->CallKeyedCallIC(RelocInfo::CODE_TARGET, arg_count, loop_nesting()); + frame_->Drop(); // Drop the key still on the stack. frame_->RestoreContextRegister(); frame_->Push(&result); } @@ -6631,6 +6648,190 @@ void CodeGenerator::GenerateIsArray(ZoneList<Expression*>* args) { } +void CodeGenerator::GenerateFastAsciiArrayJoin(ZoneList<Expression*>* args) { + ASSERT(args->length() == 2); + Load(args->at(1)); + Load(args->at(0)); + Result array_result = frame_->Pop(); + array_result.ToRegister(eax); + frame_->SpillAll(); + + Label bailout; + Label done; + // All aliases of the same register have disjoint lifetimes. + Register array = eax; + Register result_pos = no_reg; + + Register index = edi; + + Register current_string_length = ecx; // Will be ecx when live. + + Register current_string = edx; + + Register scratch = ebx; + + Register scratch_2 = esi; + Register new_padding_chars = scratch_2; + + Operand separator = Operand(esp, 4 * kPointerSize); // Already pushed. + Operand elements = Operand(esp, 3 * kPointerSize); + Operand result = Operand(esp, 2 * kPointerSize); + Operand padding_chars = Operand(esp, 1 * kPointerSize); + Operand array_length = Operand(esp, 0); + __ sub(Operand(esp), Immediate(4 * kPointerSize)); + + // Check that eax is a JSArray + __ test(array, Immediate(kSmiTagMask)); + __ j(zero, &bailout); + __ CmpObjectType(array, JS_ARRAY_TYPE, scratch); + __ j(not_equal, &bailout); + + // Check that the array has fast elements. + __ test_b(FieldOperand(scratch, Map::kBitField2Offset), + 1 << Map::kHasFastElements); + __ j(zero, &bailout); + + // If the array is empty, return the empty string. + __ mov(scratch, FieldOperand(array, JSArray::kLengthOffset)); + __ sar(scratch, 1); + Label non_trivial; + __ j(not_zero, &non_trivial); + __ mov(result, Factory::empty_string()); + __ jmp(&done); + + __ bind(&non_trivial); + __ mov(array_length, scratch); + + __ mov(scratch, FieldOperand(array, JSArray::kElementsOffset)); + __ mov(elements, scratch); + + // End of array's live range. + result_pos = array; + array = no_reg; + + + // Check that the separator is a flat ascii string. + __ mov(current_string, separator); + __ test(current_string, Immediate(kSmiTagMask)); + __ j(zero, &bailout); + __ mov(scratch, FieldOperand(current_string, HeapObject::kMapOffset)); + __ mov_b(scratch, FieldOperand(scratch, Map::kInstanceTypeOffset)); + __ and_(scratch, Immediate( + kIsNotStringMask | kStringEncodingMask | kStringRepresentationMask)); + __ cmp(scratch, kStringTag | kAsciiStringTag | kSeqStringTag); + __ j(not_equal, &bailout); + // If the separator is the empty string, replace it with NULL. + // The test for NULL is quicker than the empty string test, in a loop. + __ cmp(FieldOperand(current_string, SeqAsciiString::kLengthOffset), + Immediate(0)); + Label separator_checked; + __ j(not_zero, &separator_checked); + __ mov(separator, Immediate(0)); + __ bind(&separator_checked); + + // Check that elements[0] is a flat ascii string, and copy it in new space. + __ mov(scratch, elements); + __ mov(current_string, FieldOperand(scratch, FixedArray::kHeaderSize)); + __ test(current_string, Immediate(kSmiTagMask)); + __ j(zero, &bailout); + __ mov(scratch, FieldOperand(current_string, HeapObject::kMapOffset)); + __ mov_b(scratch, FieldOperand(scratch, Map::kInstanceTypeOffset)); + __ and_(scratch, Immediate( + kIsNotStringMask | kStringEncodingMask | kStringRepresentationMask)); + __ cmp(scratch, kStringTag | kAsciiStringTag | kSeqStringTag); + __ j(not_equal, &bailout); + + // Allocate space to copy it. Round up the size to the alignment granularity. + __ mov(current_string_length, + FieldOperand(current_string, String::kLengthOffset)); + __ shr(current_string_length, 1); + + // Live registers and stack values: + // current_string_length: length of elements[0]. + + // New string result in new space = elements[0] + __ AllocateAsciiString(result_pos, current_string_length, scratch_2, + index, no_reg, &bailout); + __ mov(result, result_pos); + + // Adjust current_string_length to include padding bytes at end of string. + // Keep track of the number of padding bytes. + __ mov(new_padding_chars, current_string_length); + __ add(Operand(current_string_length), Immediate(kObjectAlignmentMask)); + __ and_(Operand(current_string_length), Immediate(~kObjectAlignmentMask)); + __ sub(new_padding_chars, Operand(current_string_length)); + __ neg(new_padding_chars); + __ mov(padding_chars, new_padding_chars); + + Label copy_loop_1_done; + Label copy_loop_1; + __ test(current_string_length, Operand(current_string_length)); + __ j(zero, ©_loop_1_done); + __ bind(©_loop_1); + __ sub(Operand(current_string_length), Immediate(kPointerSize)); + __ mov(scratch, FieldOperand(current_string, current_string_length, + times_1, SeqAsciiString::kHeaderSize)); + __ mov(FieldOperand(result_pos, current_string_length, + times_1, SeqAsciiString::kHeaderSize), + scratch); + __ j(not_zero, ©_loop_1); + __ bind(©_loop_1_done); + + __ mov(index, Immediate(1)); + // Loop condition: while (index < length). + Label loop; + __ bind(&loop); + __ cmp(index, array_length); + __ j(greater_equal, &done); + + // If the separator is the empty string, signalled by NULL, skip it. + Label separator_done; + __ mov(current_string, separator); + __ test(current_string, Operand(current_string)); + __ j(zero, &separator_done); + + // Append separator to result. It is known to be a flat ascii string. + __ AppendStringToTopOfNewSpace(current_string, current_string_length, + result_pos, scratch, scratch_2, result, + padding_chars, &bailout); + __ bind(&separator_done); + + // Add next element of array to the end of the result. + // Get current_string = array[index]. + __ mov(scratch, elements); + __ mov(current_string, FieldOperand(scratch, index, + times_pointer_size, + FixedArray::kHeaderSize)); + // If current != flat ascii string drop result, return undefined. + __ test(current_string, Immediate(kSmiTagMask)); + __ j(zero, &bailout); + __ mov(scratch, FieldOperand(current_string, HeapObject::kMapOffset)); + __ mov_b(scratch, FieldOperand(scratch, Map::kInstanceTypeOffset)); + __ and_(scratch, Immediate( + kIsNotStringMask | kStringEncodingMask | kStringRepresentationMask)); + __ cmp(scratch, kStringTag | kAsciiStringTag | kSeqStringTag); + __ j(not_equal, &bailout); + + // Append current to the result. + __ AppendStringToTopOfNewSpace(current_string, current_string_length, + result_pos, scratch, scratch_2, result, + padding_chars, &bailout); + __ add(Operand(index), Immediate(1)); + __ jmp(&loop); // End while (index < length). + + __ bind(&bailout); + __ mov(result, Factory::undefined_value()); + __ bind(&done); + __ mov(eax, result); + // Drop temp values from the stack, and restore context register. + __ add(Operand(esp), Immediate(4 * kPointerSize)); + + __ mov(esi, Operand(ebp, StandardFrameConstants::kContextOffset)); + frame_->Drop(1); + frame_->Push(&array_result); +} + + void CodeGenerator::GenerateIsRegExp(ZoneList<Expression*>* args) { ASSERT(args->length() == 1); Load(args->at(0)); @@ -6778,8 +6979,8 @@ class DeferredIsStringWrapperSafeForDefaultValueOf : public DeferredCode { __ mov(scratch2_, FieldOperand(scratch2_, GlobalObject::kGlobalContextOffset)); __ cmp(scratch1_, - CodeGenerator::ContextOperand( - scratch2_, Context::STRING_FUNCTION_PROTOTYPE_MAP_INDEX)); + ContextOperand(scratch2_, + Context::STRING_FUNCTION_PROTOTYPE_MAP_INDEX)); __ j(not_equal, &false_result); // Set the bit in the map to indicate that it has been checked safe for // default valueOf and set true result. @@ -7934,7 +8135,7 @@ void CodeGenerator::VisitCallRuntime(CallRuntime* node) { // Push the builtins object found in the current global object. Result temp = allocator()->Allocate(); ASSERT(temp.is_valid()); - __ mov(temp.reg(), GlobalObject()); + __ mov(temp.reg(), GlobalObjectOperand()); __ mov(temp.reg(), FieldOperand(temp.reg(), GlobalObject::kBuiltinsOffset)); frame_->Push(&temp); } diff --git a/src/ia32/codegen-ia32.h b/src/ia32/codegen-ia32.h index 5a12e10e..d1a2036c 100644 --- a/src/ia32/codegen-ia32.h +++ b/src/ia32/codegen-ia32.h @@ -352,10 +352,6 @@ class CodeGenerator: public AstVisitor { return FieldOperand(array, index_as_smi, times_half_pointer_size, offset); } - static Operand ContextOperand(Register context, int index) { - return Operand(context, Context::SlotOffset(index)); - } - private: // Type of a member function that generates inline code for a native function. typedef void (CodeGenerator::*InlineFunctionGenerator) @@ -441,10 +437,6 @@ class CodeGenerator: public AstVisitor { JumpTarget* slow); // Expressions - static Operand GlobalObject() { - return ContextOperand(esi, Context::GLOBAL_INDEX); - } - void LoadCondition(Expression* expr, ControlDestination* destination, bool force_control); @@ -628,16 +620,13 @@ class CodeGenerator: public AstVisitor { void ProcessDeclarations(ZoneList<Declaration*>* declarations); - static Handle<Code> ComputeCallInitialize(int argc, InLoopFlag in_loop); - - static Handle<Code> ComputeKeyedCallInitialize(int argc, InLoopFlag in_loop); - // Declare global variables and functions in the given array of // name/value pairs. void DeclareGlobals(Handle<FixedArray> pairs); // Instantiate the function based on the shared function info. - Result InstantiateFunction(Handle<SharedFunctionInfo> function_info); + Result InstantiateFunction(Handle<SharedFunctionInfo> function_info, + bool pretenure); // Support for types. void GenerateIsSmi(ZoneList<Expression*>* args); @@ -722,6 +711,7 @@ class CodeGenerator: public AstVisitor { void GenerateHasCachedArrayIndex(ZoneList<Expression*>* args); void GenerateGetCachedArrayIndex(ZoneList<Expression*>* args); + void GenerateFastAsciiArrayJoin(ZoneList<Expression*>* args); // Simple condition analysis. enum ConditionAnalysis { diff --git a/src/ia32/full-codegen-ia32.cc b/src/ia32/full-codegen-ia32.cc index 1ea719d7..3adc48a7 100644 --- a/src/ia32/full-codegen-ia32.cc +++ b/src/ia32/full-codegen-ia32.cc @@ -36,6 +36,7 @@ #include "full-codegen.h" #include "parser.h" #include "scopes.h" +#include "stub-cache.h" namespace v8 { namespace internal { @@ -879,17 +880,23 @@ void FullCodeGenerator::VisitForInStatement(ForInStatement* stmt) { } -void FullCodeGenerator::EmitNewClosure(Handle<SharedFunctionInfo> info) { +void FullCodeGenerator::EmitNewClosure(Handle<SharedFunctionInfo> info, + bool pretenure) { // Use the fast case closure allocation code that allocates in new // space for nested functions that don't need literals cloning. - if (scope()->is_function_scope() && info->num_literals() == 0) { + if (scope()->is_function_scope() && + info->num_literals() == 0 && + !pretenure) { FastNewClosureStub stub; __ push(Immediate(info)); __ CallStub(&stub); } else { __ push(esi); __ push(Immediate(info)); - __ CallRuntime(Runtime::kNewClosure, 2); + __ push(Immediate(pretenure + ? Factory::true_value() + : Factory::false_value())); + __ CallRuntime(Runtime::kNewClosure, 3); } context()->Plug(eax); } @@ -954,7 +961,7 @@ void FullCodeGenerator::EmitLoadGlobalSlotCheckExtensions( // All extension objects were empty and it is safe to use a global // load IC call. - __ mov(eax, CodeGenerator::GlobalObject()); + __ mov(eax, GlobalObjectOperand()); __ mov(ecx, slot->var()->name()); Handle<Code> ic(Builtins::builtin(Builtins::LoadIC_Initialize)); RelocInfo::Mode mode = (typeof_state == INSIDE_TYPEOF) @@ -1057,7 +1064,7 @@ void FullCodeGenerator::EmitVariableLoad(Variable* var) { Comment cmnt(masm_, "Global variable"); // Use inline caching. Variable name is passed in ecx and the global // object on the stack. - __ mov(eax, CodeGenerator::GlobalObject()); + __ mov(eax, GlobalObjectOperand()); __ mov(ecx, var->name()); Handle<Code> ic(Builtins::builtin(Builtins::LoadIC_Initialize)); EmitCallIC(ic, RelocInfo::CODE_TARGET_CONTEXT); @@ -1834,7 +1841,7 @@ void FullCodeGenerator::EmitVariableAssignment(Variable* var, // assignment. Right-hand-side value is passed in eax, variable name in // ecx, and the global object on the stack. __ mov(ecx, var->name()); - __ mov(edx, CodeGenerator::GlobalObject()); + __ mov(edx, GlobalObjectOperand()); Handle<Code> ic(Builtins::builtin(Builtins::StoreIC_Initialize)); EmitCallIC(ic, RelocInfo::CODE_TARGET); @@ -2005,7 +2012,7 @@ void FullCodeGenerator::EmitCallWithIC(Call* expr, // Record source position of the IC call. SetSourcePosition(expr->position(), FORCED_POSITION); InLoopFlag in_loop = (loop_depth() > 0) ? IN_LOOP : NOT_IN_LOOP; - Handle<Code> ic = CodeGenerator::ComputeCallInitialize(arg_count, in_loop); + Handle<Code> ic = StubCache::ComputeCallInitialize(arg_count, in_loop); EmitCallIC(ic, mode); // Restore context register. __ mov(esi, Operand(ebp, StandardFrameConstants::kContextOffset)); @@ -2016,25 +2023,32 @@ void FullCodeGenerator::EmitCallWithIC(Call* expr, void FullCodeGenerator::EmitKeyedCallWithIC(Call* expr, Expression* key, RelocInfo::Mode mode) { - // Code common for calls using the IC. + // Load the key. + VisitForAccumulatorValue(key); + + // Swap the name of the function and the receiver on the stack to follow + // the calling convention for call ICs. + __ pop(ecx); + __ push(eax); + __ push(ecx); + + // Load the arguments. ZoneList<Expression*>* args = expr->arguments(); int arg_count = args->length(); { PreserveStatementPositionScope scope(masm()->positions_recorder()); for (int i = 0; i < arg_count; i++) { VisitForStackValue(args->at(i)); } - VisitForAccumulatorValue(key); - __ mov(ecx, eax); } // Record source position of the IC call. SetSourcePosition(expr->position(), FORCED_POSITION); InLoopFlag in_loop = (loop_depth() > 0) ? IN_LOOP : NOT_IN_LOOP; - Handle<Code> ic = CodeGenerator::ComputeKeyedCallInitialize( - arg_count, in_loop); + Handle<Code> ic = StubCache::ComputeKeyedCallInitialize(arg_count, in_loop); + __ mov(ecx, Operand(esp, (arg_count + 1) * kPointerSize)); // Key. EmitCallIC(ic, mode); // Restore context register. __ mov(esi, Operand(ebp, StandardFrameConstants::kContextOffset)); - context()->Plug(eax); + context()->DropAndPlug(1, eax); // Drop the key still on the stack. } @@ -2109,7 +2123,7 @@ void FullCodeGenerator::VisitCall(Call* expr) { context()->DropAndPlug(1, eax); } else if (var != NULL && !var->is_this() && var->is_global()) { // Push global object as receiver for the call IC. - __ push(CodeGenerator::GlobalObject()); + __ push(GlobalObjectOperand()); EmitCallWithIC(expr, var->name(), RelocInfo::CODE_TARGET_CONTEXT); } else if (var != NULL && var->AsSlot() != NULL && var->AsSlot()->type() == Slot::LOOKUP) { @@ -2144,7 +2158,7 @@ void FullCodeGenerator::VisitCall(Call* expr) { // Push function. __ push(eax); // Push global receiver. - __ mov(ebx, CodeGenerator::GlobalObject()); + __ mov(ebx, GlobalObjectOperand()); __ push(FieldOperand(ebx, GlobalObject::kGlobalReceiverOffset)); __ bind(&call); } @@ -2178,7 +2192,7 @@ void FullCodeGenerator::VisitCall(Call* expr) { // Push result (function). __ push(eax); // Push Global receiver. - __ mov(ecx, CodeGenerator::GlobalObject()); + __ mov(ecx, GlobalObjectOperand()); __ push(FieldOperand(ecx, GlobalObject::kGlobalReceiverOffset)); EmitCallWithStub(expr); } else { @@ -2199,7 +2213,7 @@ void FullCodeGenerator::VisitCall(Call* expr) { VisitForStackValue(fun); } // Load global receiver object. - __ mov(ebx, CodeGenerator::GlobalObject()); + __ mov(ebx, GlobalObjectOperand()); __ push(FieldOperand(ebx, GlobalObject::kGlobalReceiverOffset)); // Emit function call. EmitCallWithStub(expr); @@ -3076,6 +3090,190 @@ void FullCodeGenerator::EmitGetCachedArrayIndex(ZoneList<Expression*>* args) { } +void FullCodeGenerator::EmitFastAsciiArrayJoin(ZoneList<Expression*>* args) { + Label bailout; + Label done; + + ASSERT(args->length() == 2); + // We will leave the separator on the stack until the end of the function. + VisitForStackValue(args->at(1)); + // Load this to eax (= array) + VisitForAccumulatorValue(args->at(0)); + + // All aliases of the same register have disjoint lifetimes. + Register array = eax; + Register result_pos = no_reg; + + Register index = edi; + + Register current_string_length = ecx; // Will be ecx when live. + + Register current_string = edx; + + Register scratch = ebx; + + Register scratch_2 = esi; + Register new_padding_chars = scratch_2; + + Operand separator = Operand(esp, 4 * kPointerSize); // Already pushed. + Operand elements = Operand(esp, 3 * kPointerSize); + Operand result = Operand(esp, 2 * kPointerSize); + Operand padding_chars = Operand(esp, 1 * kPointerSize); + Operand array_length = Operand(esp, 0); + __ sub(Operand(esp), Immediate(4 * kPointerSize)); + + + // Check that eax is a JSArray + __ test(array, Immediate(kSmiTagMask)); + __ j(zero, &bailout); + __ CmpObjectType(array, JS_ARRAY_TYPE, scratch); + __ j(not_equal, &bailout); + + // Check that the array has fast elements. + __ test_b(FieldOperand(scratch, Map::kBitField2Offset), + 1 << Map::kHasFastElements); + __ j(zero, &bailout); + + // If the array is empty, return the empty string. + __ mov(scratch, FieldOperand(array, JSArray::kLengthOffset)); + __ sar(scratch, 1); + Label non_trivial; + __ j(not_zero, &non_trivial); + __ mov(result, Factory::empty_string()); + __ jmp(&done); + + __ bind(&non_trivial); + __ mov(array_length, scratch); + + __ mov(scratch, FieldOperand(array, JSArray::kElementsOffset)); + __ mov(elements, scratch); + + // End of array's live range. + result_pos = array; + array = no_reg; + + + // Check that the separator is a flat ascii string. + __ mov(current_string, separator); + __ test(current_string, Immediate(kSmiTagMask)); + __ j(zero, &bailout); + __ mov(scratch, FieldOperand(current_string, HeapObject::kMapOffset)); + __ mov_b(scratch, FieldOperand(scratch, Map::kInstanceTypeOffset)); + __ and_(scratch, Immediate( + kIsNotStringMask | kStringEncodingMask | kStringRepresentationMask)); + __ cmp(scratch, kStringTag | kAsciiStringTag | kSeqStringTag); + __ j(not_equal, &bailout); + // If the separator is the empty string, replace it with NULL. + // The test for NULL is quicker than the empty string test, in a loop. + __ cmp(FieldOperand(current_string, SeqAsciiString::kLengthOffset), + Immediate(0)); + Label separator_checked; + __ j(not_zero, &separator_checked); + __ mov(separator, Immediate(0)); + __ bind(&separator_checked); + + // Check that elements[0] is a flat ascii string, and copy it in new space. + __ mov(scratch, elements); + __ mov(current_string, FieldOperand(scratch, FixedArray::kHeaderSize)); + __ test(current_string, Immediate(kSmiTagMask)); + __ j(zero, &bailout); + __ mov(scratch, FieldOperand(current_string, HeapObject::kMapOffset)); + __ mov_b(scratch, FieldOperand(scratch, Map::kInstanceTypeOffset)); + __ and_(scratch, Immediate( + kIsNotStringMask | kStringEncodingMask | kStringRepresentationMask)); + __ cmp(scratch, kStringTag | kAsciiStringTag | kSeqStringTag); + __ j(not_equal, &bailout); + + // Allocate space to copy it. Round up the size to the alignment granularity. + __ mov(current_string_length, + FieldOperand(current_string, String::kLengthOffset)); + __ shr(current_string_length, 1); + + // Live registers and stack values: + // current_string_length: length of elements[0]. + + // New string result in new space = elements[0] + __ AllocateAsciiString(result_pos, current_string_length, scratch_2, + index, no_reg, &bailout); + __ mov(result, result_pos); + + // Adjust current_string_length to include padding bytes at end of string. + // Keep track of the number of padding bytes. + __ mov(new_padding_chars, current_string_length); + __ add(Operand(current_string_length), Immediate(kObjectAlignmentMask)); + __ and_(Operand(current_string_length), Immediate(~kObjectAlignmentMask)); + __ sub(new_padding_chars, Operand(current_string_length)); + __ neg(new_padding_chars); + __ mov(padding_chars, new_padding_chars); + + Label copy_loop_1_done; + Label copy_loop_1; + __ test(current_string_length, Operand(current_string_length)); + __ j(zero, ©_loop_1_done); + __ bind(©_loop_1); + __ sub(Operand(current_string_length), Immediate(kPointerSize)); + __ mov(scratch, FieldOperand(current_string, current_string_length, + times_1, SeqAsciiString::kHeaderSize)); + __ mov(FieldOperand(result_pos, current_string_length, + times_1, SeqAsciiString::kHeaderSize), + scratch); + __ j(not_zero, ©_loop_1); + __ bind(©_loop_1_done); + + __ mov(index, Immediate(1)); + // Loop condition: while (index < length). + Label loop; + __ bind(&loop); + __ cmp(index, array_length); + __ j(greater_equal, &done); + + // If the separator is the empty string, signalled by NULL, skip it. + Label separator_done; + __ mov(current_string, separator); + __ test(current_string, Operand(current_string)); + __ j(zero, &separator_done); + + // Append separator to result. It is known to be a flat ascii string. + __ AppendStringToTopOfNewSpace(current_string, current_string_length, + result_pos, scratch, scratch_2, result, + padding_chars, &bailout); + __ bind(&separator_done); + + // Add next element of array to the end of the result. + // Get current_string = array[index]. + __ mov(scratch, elements); + __ mov(current_string, FieldOperand(scratch, index, + times_pointer_size, + FixedArray::kHeaderSize)); + // If current != flat ascii string drop result, return undefined. + __ test(current_string, Immediate(kSmiTagMask)); + __ j(zero, &bailout); + __ mov(scratch, FieldOperand(current_string, HeapObject::kMapOffset)); + __ mov_b(scratch, FieldOperand(scratch, Map::kInstanceTypeOffset)); + __ and_(scratch, Immediate( + kIsNotStringMask | kStringEncodingMask | kStringRepresentationMask)); + __ cmp(scratch, kStringTag | kAsciiStringTag | kSeqStringTag); + __ j(not_equal, &bailout); + + // Append current to the result. + __ AppendStringToTopOfNewSpace(current_string, current_string_length, + result_pos, scratch, scratch_2, result, + padding_chars, &bailout); + __ add(Operand(index), Immediate(1)); + __ jmp(&loop); // End while (index < length). + + __ bind(&bailout); + __ mov(result, Factory::undefined_value()); + __ bind(&done); + __ mov(eax, result); + // Drop temp values from the stack, and restore context register. + __ add(Operand(esp), Immediate(5 * kPointerSize)); + + __ mov(esi, Operand(ebp, StandardFrameConstants::kContextOffset)); + context()->Plug(eax); +} + + void FullCodeGenerator::VisitCallRuntime(CallRuntime* expr) { Handle<String> name = expr->name(); if (name->length() > 0 && name->Get(0) == '_') { @@ -3089,7 +3287,7 @@ void FullCodeGenerator::VisitCallRuntime(CallRuntime* expr) { if (expr->is_jsruntime()) { // Prepare for calling JS runtime function. - __ mov(eax, CodeGenerator::GlobalObject()); + __ mov(eax, GlobalObjectOperand()); __ push(FieldOperand(eax, GlobalObject::kBuiltinsOffset)); } @@ -3103,7 +3301,7 @@ void FullCodeGenerator::VisitCallRuntime(CallRuntime* expr) { // Call the JS runtime function via a call IC. __ Set(ecx, Immediate(expr->name())); InLoopFlag in_loop = (loop_depth() > 0) ? IN_LOOP : NOT_IN_LOOP; - Handle<Code> ic = CodeGenerator::ComputeCallInitialize(arg_count, in_loop); + Handle<Code> ic = StubCache::ComputeCallInitialize(arg_count, in_loop); EmitCallIC(ic, RelocInfo::CODE_TARGET); // Restore context register. __ mov(esi, Operand(ebp, StandardFrameConstants::kContextOffset)); @@ -3140,7 +3338,7 @@ void FullCodeGenerator::VisitUnaryOperation(UnaryOperation* expr) { VisitForStackValue(prop->obj()); VisitForStackValue(prop->key()); } else if (var->is_global()) { - __ push(CodeGenerator::GlobalObject()); + __ push(GlobalObjectOperand()); __ push(Immediate(var->name())); } else { // Non-global variable. Call the runtime to look up the context @@ -3418,7 +3616,7 @@ void FullCodeGenerator::VisitForTypeofValue(Expression* expr) { if (proxy != NULL && !proxy->var()->is_this() && proxy->var()->is_global()) { Comment cmnt(masm_, "Global variable"); - __ mov(eax, CodeGenerator::GlobalObject()); + __ mov(eax, GlobalObjectOperand()); __ mov(ecx, Immediate(proxy->name())); Handle<Code> ic(Builtins::builtin(Builtins::LoadIC_Initialize)); // Use a regular load, not a contextual load, to avoid a reference diff --git a/src/ia32/macro-assembler-ia32.cc b/src/ia32/macro-assembler-ia32.cc index b72f4df0..cbf93dd6 100644 --- a/src/ia32/macro-assembler-ia32.cc +++ b/src/ia32/macro-assembler-ia32.cc @@ -392,13 +392,8 @@ void MacroAssembler::EnterExitFrame() { } -void MacroAssembler::EnterApiExitFrame(int stack_space, - int argc) { +void MacroAssembler::EnterApiExitFrame(int argc) { EnterExitFramePrologue(); - - int offset = StandardFrameConstants::kCallerSPOffset - kPointerSize; - lea(esi, Operand(ebp, (stack_space * kPointerSize) + offset)); - EnterExitFrameEpilogue(argc); } @@ -411,6 +406,13 @@ void MacroAssembler::LeaveExitFrame() { // Pop the arguments and the receiver from the caller stack. lea(esp, Operand(esi, 1 * kPointerSize)); + // Push the return address to get ready to return. + push(ecx); + + LeaveExitFrameEpilogue(); +} + +void MacroAssembler::LeaveExitFrameEpilogue() { // Restore current context from top and clear it in debug mode. ExternalReference context_address(Top::k_context_address); mov(esi, Operand::StaticVariable(context_address)); @@ -418,15 +420,20 @@ void MacroAssembler::LeaveExitFrame() { mov(Operand::StaticVariable(context_address), Immediate(0)); #endif - // Push the return address to get ready to return. - push(ecx); - // Clear the top frame. ExternalReference c_entry_fp_address(Top::k_c_entry_fp_address); mov(Operand::StaticVariable(c_entry_fp_address), Immediate(0)); } +void MacroAssembler::LeaveApiExitFrame() { + mov(esp, Operand(ebp)); + pop(ebp); + + LeaveExitFrameEpilogue(); +} + + void MacroAssembler::PushTryHandler(CodeLocation try_location, HandlerType type) { // Adjust this code if not the case. @@ -530,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 = @@ -552,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)); } @@ -601,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; @@ -657,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 = @@ -698,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 = @@ -882,6 +887,57 @@ void MacroAssembler::AllocateAsciiConsString(Register result, Immediate(Factory::cons_ascii_string_map())); } +// All registers must be distinct. Only current_string needs valid contents +// on entry. All registers may be invalid on exit. result_operand is +// unchanged, padding_chars is updated correctly. +void MacroAssembler::AppendStringToTopOfNewSpace( + Register current_string, // Tagged pointer to string to copy. + Register current_string_length, + Register result_pos, + Register scratch, + Register new_padding_chars, + Operand operand_result, + Operand operand_padding_chars, + Label* bailout) { + mov(current_string_length, + FieldOperand(current_string, String::kLengthOffset)); + shr(current_string_length, 1); + sub(current_string_length, operand_padding_chars); + mov(new_padding_chars, current_string_length); + add(Operand(current_string_length), Immediate(kObjectAlignmentMask)); + and_(Operand(current_string_length), Immediate(~kObjectAlignmentMask)); + sub(new_padding_chars, Operand(current_string_length)); + neg(new_padding_chars); + // We need an allocation even if current_string_length is 0, to fetch + // result_pos. Consider using a faster fetch of result_pos in that case. + AllocateInNewSpace(current_string_length, result_pos, scratch, no_reg, + bailout, NO_ALLOCATION_FLAGS); + sub(result_pos, operand_padding_chars); + mov(operand_padding_chars, new_padding_chars); + + Register scratch_2 = new_padding_chars; // Used to compute total length. + // Copy string to the end of result. + mov(current_string_length, + FieldOperand(current_string, String::kLengthOffset)); + mov(scratch, operand_result); + mov(scratch_2, current_string_length); + add(scratch_2, FieldOperand(scratch, String::kLengthOffset)); + mov(FieldOperand(scratch, String::kLengthOffset), scratch_2); + shr(current_string_length, 1); + lea(current_string, + FieldOperand(current_string, SeqAsciiString::kHeaderSize)); + // Loop condition: while (--current_string_length >= 0). + Label copy_loop; + Label copy_loop_entry; + jmp(©_loop_entry); + bind(©_loop); + mov_b(scratch, Operand(current_string, current_string_length, times_1, 0)); + mov_b(Operand(result_pos, current_string_length, times_1, 0), scratch); + bind(©_loop_entry); + sub(Operand(current_string_length), Immediate(1)); + j(greater_equal, ©_loop); +} + void MacroAssembler::NegativeZeroTest(CodeGenerator* cgen, Register result, @@ -1110,6 +1166,17 @@ void MacroAssembler::TailCallExternalReference(const ExternalReference& ext, } +MaybeObject* MacroAssembler::TryTailCallExternalReference( + const ExternalReference& ext, int num_arguments, int result_size) { + // TODO(1236192): Most runtime routines don't need the number of + // arguments passed in because it is constant. At some point we + // should remove this need and make the runtime routine entry code + // smarter. + Set(eax, Immediate(num_arguments)); + return TryJumpToExternalReference(ext); +} + + void MacroAssembler::TailCallRuntime(Runtime::FunctionId fid, int num_arguments, int result_size) { @@ -1117,6 +1184,14 @@ void MacroAssembler::TailCallRuntime(Runtime::FunctionId fid, } +MaybeObject* MacroAssembler::TryTailCallRuntime(Runtime::FunctionId fid, + int num_arguments, + int result_size) { + return TryTailCallExternalReference( + ExternalReference(fid), num_arguments, result_size); +} + + // If true, a Handle<T> passed by value is passed and returned by // using the location_ field directly. If false, it is passed and // returned as a pointer to a handle. @@ -1132,20 +1207,15 @@ Operand ApiParameterOperand(int index) { } -void MacroAssembler::PrepareCallApiFunction(int stack_space, int argc) { +void MacroAssembler::PrepareCallApiFunction(int argc, Register scratch) { if (kPassHandlesDirectly) { - EnterApiExitFrame(stack_space, argc); + EnterApiExitFrame(argc); // When handles as passed directly we don't have to allocate extra // space for and pass an out parameter. } else { // We allocate two additional slots: return value and pointer to it. - EnterApiExitFrame(stack_space, argc + 2); - } -} - + EnterApiExitFrame(argc + 2); -void MacroAssembler::CallApiFunctionAndReturn(ApiFunction* function, int argc) { - if (!kPassHandlesDirectly) { // The argument slots are filled as follows: // // n + 1: output cell @@ -1157,11 +1227,19 @@ void MacroAssembler::CallApiFunctionAndReturn(ApiFunction* function, int argc) { // Note that this is one more "argument" than the function expects // so the out cell will have to be popped explicitly after returning // from the function. The out cell contains Handle. - lea(eax, Operand(esp, (argc + 1) * kPointerSize)); // pointer to out cell. - mov(Operand(esp, 0 * kPointerSize), eax); // output. - mov(Operand(esp, (argc + 1) * kPointerSize), Immediate(0)); // out cell. + + // pointer to out cell. + lea(scratch, Operand(esp, (argc + 1) * kPointerSize)); + mov(Operand(esp, 0 * kPointerSize), scratch); // output. + if (FLAG_debug_code) { + mov(Operand(esp, (argc + 1) * kPointerSize), Immediate(0)); // out cell. + } } +} + +MaybeObject* MacroAssembler::TryCallApiFunctionAndReturn(ApiFunction* function, + int stack_space) { ExternalReference next_address = ExternalReference::handle_scope_next_address(); ExternalReference limit_address = @@ -1210,10 +1288,14 @@ void MacroAssembler::CallApiFunctionAndReturn(ApiFunction* function, int argc) { cmp(Operand::StaticVariable(scheduled_exception_address), Immediate(Factory::the_hole_value())); j(not_equal, &promote_scheduled_exception, not_taken); - LeaveExitFrame(); - ret(0); + LeaveApiExitFrame(); + ret(stack_space * kPointerSize); bind(&promote_scheduled_exception); - TailCallRuntime(Runtime::kPromoteScheduledException, 0, 1); + MaybeObject* result = + TryTailCallRuntime(Runtime::kPromoteScheduledException, 0, 1); + if (result->IsFailure()) { + return result; + } bind(&empty_handle); // It was zero; the result is undefined. mov(eax, Factory::undefined_value()); @@ -1227,6 +1309,8 @@ void MacroAssembler::CallApiFunctionAndReturn(ApiFunction* function, int argc) { call(Operand(eax)); mov(eax, edi); jmp(&leave_exit_frame); + + return result; } @@ -1238,6 +1322,15 @@ void MacroAssembler::JumpToExternalReference(const ExternalReference& ext) { } +MaybeObject* MacroAssembler::TryJumpToExternalReference( + const ExternalReference& ext) { + // Set the entry point and jump to the C entry runtime stub. + mov(ebx, Immediate(ext)); + CEntryStub ces(1); + return TryTailCallStub(&ces); +} + + void MacroAssembler::InvokePrologue(const ParameterCount& expected, const ParameterCount& actual, Handle<Code> code_constant, diff --git a/src/ia32/macro-assembler-ia32.h b/src/ia32/macro-assembler-ia32.h index d65eebbc..d208dbe3 100644 --- a/src/ia32/macro-assembler-ia32.h +++ b/src/ia32/macro-assembler-ia32.h @@ -123,13 +123,17 @@ class MacroAssembler: public Assembler { // to the first argument in register esi. void EnterExitFrame(); - void EnterApiExitFrame(int stack_space, int argc); + void EnterApiExitFrame(int argc); // Leave the current exit frame. Expects the return value in // register eax:edx (untouched) and the pointer to the first // argument in register esi. void LeaveExitFrame(); + // Leave the current exit frame. Expects the return value in + // register eax (untouched). + void LeaveApiExitFrame(); + // Find the function context up the context chain. void LoadContext(Register dst, int context_chain_length); @@ -375,6 +379,23 @@ class MacroAssembler: public Assembler { Register scratch2, Label* gc_required); + // All registers must be distinct. Only current_string needs valid contents + // on entry. All registers may be invalid on exit. result_operand is + // unchanged, padding_chars is updated correctly. + // The top of new space must contain a sequential ascii string with + // padding_chars bytes free in its top word. The sequential ascii string + // current_string is concatenated to it, allocating the necessary amount + // of new memory. + void AppendStringToTopOfNewSpace( + Register current_string, // Tagged pointer to string to copy. + Register current_string_length, + Register result_pos, + Register scratch, + Register new_padding_chars, + Operand operand_result, + Operand operand_padding_chars, + Label* bailout); + // --------------------------------------------------------------------------- // Support functions. @@ -460,11 +481,23 @@ class MacroAssembler: public Assembler { int num_arguments, int result_size); + // Tail call of a runtime routine (jump). Try to generate the code if + // necessary. Do not perform a GC but instead return a retry after GC failure. + MUST_USE_RESULT MaybeObject* TryTailCallExternalReference( + const ExternalReference& ext, int num_arguments, int result_size); + // Convenience function: tail call a runtime routine (jump). void TailCallRuntime(Runtime::FunctionId fid, int num_arguments, int result_size); + // Convenience function: tail call a runtime routine (jump). Try to generate + // the code if necessary. Do not perform a GC but instead return a retry after + // GC failure. + MUST_USE_RESULT MaybeObject* TryTailCallRuntime(Runtime::FunctionId fid, + int num_arguments, + int result_size); + // Before calling a C-function from generated code, align arguments on stack. // After aligning the frame, arguments must be stored in esp[0], esp[4], // etc., not pushed. The argument count assumes all arguments are word sized. @@ -485,17 +518,22 @@ class MacroAssembler: public Assembler { // Prepares stack to put arguments (aligns and so on). Reserves // space for return value if needed (assumes the return value is a handle). // Uses callee-saved esi to restore stack state after call. Arguments must be - // stored in ApiParameterOperand(0), ApiParameterOperand(1) etc. - void PrepareCallApiFunction(int stack_space, int argc); + // stored in ApiParameterOperand(0), ApiParameterOperand(1) etc. Saves + // context (esi). + void PrepareCallApiFunction(int argc, Register scratch); // Calls an API function. Allocates HandleScope, extracts // returned value from handle and propagates exceptions. - // Clobbers ebx, esi, edi and caller-save registers. - void CallApiFunctionAndReturn(ApiFunction* function, int argc); + // Clobbers ebx, edi and caller-save registers. Restores context. + // On return removes stack_space * kPointerSize (GCed). + MaybeObject* TryCallApiFunctionAndReturn(ApiFunction* function, + int stack_space); // Jump to a runtime routine. void JumpToExternalReference(const ExternalReference& ext); + MaybeObject* TryJumpToExternalReference(const ExternalReference& ext); + // --------------------------------------------------------------------------- // Utilities @@ -589,9 +627,10 @@ class MacroAssembler: public Assembler { void EnterExitFramePrologue(); void EnterExitFrameEpilogue(int argc); + void LeaveExitFrameEpilogue(); + // Allocation support helpers. void LoadAllocationTopHelper(Register result, - Register result_end, Register scratch, AllocationFlags flags); void UpdateAllocationTopHelper(Register result_end, Register scratch); @@ -642,6 +681,17 @@ static inline Operand FieldOperand(Register object, return Operand(object, index, scale, offset - kHeapObjectTag); } + +static inline Operand ContextOperand(Register context, int index) { + return Operand(context, Context::SlotOffset(index)); +} + + +static inline Operand GlobalObjectOperand() { + return ContextOperand(esi, Context::GLOBAL_INDEX); +} + + // Generates an Operand for saving parameters after PrepareCallApiFunction. Operand ApiParameterOperand(int index); diff --git a/src/ia32/stub-cache-ia32.cc b/src/ia32/stub-cache-ia32.cc index f59928fe..adcb5219 100644 --- a/src/ia32/stub-cache-ia32.cc +++ b/src/ia32/stub-cache-ia32.cc @@ -417,7 +417,7 @@ static void CompileCallLoadPropertyWithInterceptor(MacroAssembler* masm, static const int kFastApiCallArguments = 3; -// Reserves space for the extra arguments to FastHandleApiCall in the +// Reserves space for the extra arguments to API function in the // caller's frame. // // These arguments are set by CheckPrototypes and GenerateFastApiCall. @@ -450,7 +450,7 @@ static void FreeSpaceForFastApiCall(MacroAssembler* masm, Register scratch) { } -// Generates call to FastHandleApiCall builtin. +// Generates call to API function. static bool GenerateFastApiCall(MacroAssembler* masm, const CallOptimization& optimization, int argc, @@ -473,7 +473,7 @@ static bool GenerateFastApiCall(MacroAssembler* masm, __ mov(edi, Immediate(Handle<JSFunction>(function))); __ mov(esi, FieldOperand(edi, JSFunction::kContextOffset)); - // Pass the additional arguments FastHandleApiCall expects. + // Pass the additional arguments. __ mov(Operand(esp, 2 * kPointerSize), edi); Object* call_data = optimization.api_call_info()->data(); Handle<CallHandlerInfo> api_call_info_handle(optimization.api_call_info()); @@ -486,31 +486,42 @@ static bool GenerateFastApiCall(MacroAssembler* masm, Immediate(Handle<Object>(call_data))); } - // Prepare arguments for ApiCallEntryStub. + // Prepare arguments. __ lea(eax, Operand(esp, 3 * kPointerSize)); - __ lea(ebx, Operand(esp, (argc + 3) * kPointerSize)); - __ Set(edx, Immediate(argc)); Object* callback = optimization.api_call_info()->callback(); Address api_function_address = v8::ToCData<Address>(callback); ApiFunction fun(api_function_address); - ApiCallEntryStub stub(api_call_info_handle, &fun); + const int kApiArgc = 1; // API function gets reference to the v8::Arguments. - __ EnterInternalFrame(); + // Allocate the v8::Arguments structure in the arguments' space since + // it's not controlled by GC. + const int kApiStackSpace = 4; + + __ PrepareCallApiFunction(kApiArgc + kApiStackSpace, ebx); + + __ mov(ApiParameterOperand(1), eax); // v8::Arguments::implicit_args_. + __ add(Operand(eax), Immediate(argc * kPointerSize)); + __ mov(ApiParameterOperand(2), eax); // v8::Arguments::values_. + __ Set(ApiParameterOperand(3), Immediate(argc)); // v8::Arguments::length_. + // v8::Arguments::is_construct_call_. + __ Set(ApiParameterOperand(4), Immediate(0)); + + // v8::InvocationCallback's argument. + __ lea(eax, ApiParameterOperand(1)); + __ mov(ApiParameterOperand(0), eax); // Emitting a stub call may try to allocate (if the code is not // already generated). Do not allow the assembler to perform a // garbage collection but instead return the allocation failure // object. - MaybeObject* result = masm->TryCallStub(&stub); + MaybeObject* result = + masm->TryCallApiFunctionAndReturn(&fun, argc + kFastApiCallArguments + 1); if (result->IsFailure()) { *failure = Failure::cast(result); return false; } - - __ LeaveInternalFrame(); - __ ret((argc + 4) * kPointerSize); return true; } @@ -1063,44 +1074,55 @@ bool StubCompiler::GenerateLoadCallback(JSObject* object, Handle<AccessorInfo> callback_handle(callback); - __ EnterInternalFrame(); - // Push the stack address where the list of arguments ends. - __ lea(scratch2, Operand(esp, -2 * kPointerSize)); - __ push(scratch2); + // Insert additional parameters into the stack frame above return address. + ASSERT(!scratch3.is(reg)); + __ pop(scratch3); // Get return address to place it below. + __ push(receiver); // receiver + __ mov(scratch2, Operand(esp)); + ASSERT(!scratch2.is(reg)); __ push(reg); // holder // Push data from AccessorInfo. if (Heap::InNewSpace(callback_handle->data())) { - __ mov(scratch2, Immediate(callback_handle)); - __ push(FieldOperand(scratch2, AccessorInfo::kDataOffset)); + __ mov(scratch1, Immediate(callback_handle)); + __ push(FieldOperand(scratch1, AccessorInfo::kDataOffset)); } else { __ push(Immediate(Handle<Object>(callback_handle->data()))); } - __ push(name_reg); // name + // Save a pointer to where we pushed the arguments pointer. // This will be passed as the const AccessorInfo& to the C++ callback. - STATIC_ASSERT(ApiGetterEntryStub::kStackSpace == 5); - __ lea(eax, Operand(esp, 4 * kPointerSize)); - __ mov(ebx, esp); + __ push(scratch2); + + __ push(name_reg); // name + __ mov(ebx, esp); // esp points to reference to name (handler). + + __ push(scratch3); // Restore return address. // Do call through the api. Address getter_address = v8::ToCData<Address>(callback->getter()); ApiFunction fun(getter_address); - ApiGetterEntryStub stub(callback_handle, &fun); + + // 3 elements array for v8::Agruments::values_, handler for name and pointer + // to the values (it considered as smi in GC). + const int kStackSpace = 5; + const int kApiArgc = 2; + + __ PrepareCallApiFunction(kApiArgc, eax); + __ mov(ApiParameterOperand(0), ebx); // name. + __ add(Operand(ebx), Immediate(kPointerSize)); + __ mov(ApiParameterOperand(1), ebx); // arguments pointer. + // Emitting a stub call may try to allocate (if the code is not // already generated). Do not allow the assembler to perform a // garbage collection but instead return the allocation failure // object. - Object* result = NULL; // Initialization to please compiler. - { MaybeObject* try_call_result = masm()->TryCallStub(&stub); - if (!try_call_result->ToObject(&result)) { - *failure = Failure::cast(try_call_result); - return false; - } + MaybeObject* result = masm()->TryCallApiFunctionAndReturn(&fun, kStackSpace); + if (result->IsFailure()) { + *failure = Failure::cast(result); + return false; } - __ LeaveInternalFrame(); - __ ret(0); return true; } @@ -1119,9 +1141,8 @@ void StubCompiler::GenerateLoadConstant(JSObject* object, __ j(zero, miss, not_taken); // Check that the maps haven't changed. - Register reg = - CheckPrototypes(object, receiver, holder, - scratch1, scratch2, scratch3, name, miss); + CheckPrototypes(object, receiver, holder, + scratch1, scratch2, scratch3, name, miss); // Return the constant value. __ mov(eax, Handle<Object>(value)); @@ -1241,8 +1262,8 @@ void StubCompiler::GenerateLoadInterceptor(JSObject* object, __ push(receiver); __ push(holder_reg); __ mov(holder_reg, Immediate(Handle<AccessorInfo>(callback))); - __ push(holder_reg); __ push(FieldOperand(holder_reg, AccessorInfo::kDataOffset)); + __ push(holder_reg); __ push(name_reg); __ push(scratch2); // restore return address @@ -2146,7 +2167,10 @@ MaybeObject* CallStubCompiler::CompileCallConstant(Object* object, if (depth != kInvalidProtoDepth) { __ IncrementCounter(&Counters::call_const_fast_api, 1); - ReserveSpaceForFastApiCall(masm(), eax); + + // Allocate space for v8::Arguments implicit values. Must be initialized + // before to call any runtime function. + __ sub(Operand(esp), Immediate(kFastApiCallArguments * kPointerSize)); } // Check that the maps haven't changed. @@ -2226,6 +2250,12 @@ MaybeObject* CallStubCompiler::CompileCallConstant(Object* object, if (depth != kInvalidProtoDepth) { Failure* failure; + // Move the return address on top of the stack. + __ mov(eax, Operand(esp, 3 * kPointerSize)); + __ mov(Operand(esp, 0 * kPointerSize), eax); + + // esp[2 * kPointerSize] is uninitialized, esp[3 * kPointerSize] contains + // duplicate of return address and will be overwritten. bool success = GenerateFastApiCall(masm(), optimization, argc, &failure); if (!success) { return failure; @@ -2237,7 +2267,7 @@ MaybeObject* CallStubCompiler::CompileCallConstant(Object* object, // Handle call cache miss. __ bind(&miss); if (depth != kInvalidProtoDepth) { - FreeSpaceForFastApiCall(masm(), eax); + __ add(Operand(esp), Immediate(kFastApiCallArguments * kPointerSize)); } __ bind(&miss_in_smi_check); Object* obj; @@ -2287,7 +2317,7 @@ MaybeObject* CallStubCompiler::CompileCallInterceptor(JSObject* object, &miss, &failure); if (!success) { - return false; + return failure; } // Restore receiver. @@ -2991,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 @@ -3068,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. @@ -3095,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/src/ia32/virtual-frame-ia32.cc b/src/ia32/virtual-frame-ia32.cc index a31f6e83..11e1aaf2 100644 --- a/src/ia32/virtual-frame-ia32.cc +++ b/src/ia32/virtual-frame-ia32.cc @@ -33,6 +33,7 @@ #include "register-allocator-inl.h" #include "scopes.h" #include "virtual-frame-inl.h" +#include "stub-cache.h" namespace v8 { namespace internal { @@ -1108,7 +1109,7 @@ Result VirtualFrame::CallCallIC(RelocInfo::Mode mode, // The IC expects the name in ecx and the rest on the stack and // drops them all. InLoopFlag in_loop = loop_nesting > 0 ? IN_LOOP : NOT_IN_LOOP; - Handle<Code> ic = cgen()->ComputeCallInitialize(arg_count, in_loop); + Handle<Code> ic = StubCache::ComputeCallInitialize(arg_count, in_loop); // Spill args, receiver, and function. The call will drop args and // receiver. Result name = Pop(); @@ -1126,7 +1127,7 @@ Result VirtualFrame::CallKeyedCallIC(RelocInfo::Mode mode, // The IC expects the name in ecx and the rest on the stack and // drops them all. InLoopFlag in_loop = loop_nesting > 0 ? IN_LOOP : NOT_IN_LOOP; - Handle<Code> ic = cgen()->ComputeKeyedCallInitialize(arg_count, in_loop); + Handle<Code> ic = StubCache::ComputeKeyedCallInitialize(arg_count, in_loop); // Spill args, receiver, and function. The call will drop args and // receiver. Result name = Pop(); @@ -148,14 +148,6 @@ class List { DISALLOW_COPY_AND_ASSIGN(List); }; -class FrameElement; - -// Add() is inlined, ResizeAdd() called by Add() is inlined except for -// Lists of FrameElements, and ResizeAddInternal() is inlined in ResizeAdd(). -template <> -void List<FrameElement, - FreeStoreAllocationPolicy>::ResizeAdd(const FrameElement& element); - } } // namespace v8::internal #endif // V8_LIST_H_ @@ -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/src/mark-compact.cc b/src/mark-compact.cc index 484497f0..40194e36 100644 --- a/src/mark-compact.cc +++ b/src/mark-compact.cc @@ -1099,13 +1099,6 @@ void MarkCompactCollector::MarkLiveObjects() { } -static int CountMarkedCallback(HeapObject* obj) { - MapWord map_word = obj->map_word(); - map_word.ClearMark(); - return obj->SizeFromMap(map_word.ToMap()); -} - - #ifdef DEBUG void MarkCompactCollector::UpdateLiveObjectCount(HeapObject* obj) { live_bytes_ += obj->Size(); @@ -1152,7 +1145,7 @@ bool MarkCompactCollector::SafeIsMap(HeapObject* object) { void MarkCompactCollector::ClearNonLiveTransitions() { - HeapObjectIterator map_iterator(Heap::map_space(), &CountMarkedCallback); + HeapObjectIterator map_iterator(Heap::map_space(), &SizeOfMarkedObject); // Iterate over the map space, setting map transitions that go from // a marked map to an unmarked map to null transitions. At the same time, // set all the prototype fields of maps back to their original value, @@ -2673,6 +2666,13 @@ void MarkCompactCollector::ReportDeleteIfNeeded(HeapObject* obj) { } +int MarkCompactCollector::SizeOfMarkedObject(HeapObject* obj) { + MapWord map_word = obj->map_word(); + map_word.ClearMark(); + return obj->SizeFromMap(map_word.ToMap()); +} + + void MarkCompactCollector::Initialize() { StaticPointersToNewGenUpdatingVisitor::Initialize(); StaticMarkingVisitor::Initialize(); diff --git a/src/mark-compact.h b/src/mark-compact.h index 7444c5bf..1b7e6002 100644 --- a/src/mark-compact.h +++ b/src/mark-compact.h @@ -119,6 +119,9 @@ class MarkCompactCollector: public AllStatic { // Determine type of object and emit deletion log event. static void ReportDeleteIfNeeded(HeapObject* obj); + // Returns size of a possibly marked object. + static int SizeOfMarkedObject(HeapObject* obj); + // Distinguishable invalid map encodings (for single word and multiple words) // that indicate free regions. static const uint32_t kSingleFreeEncoding = 0; diff --git a/src/objects-debug.cc b/src/objects-debug.cc index 2b79016a..69219ee3 100644 --- a/src/objects-debug.cc +++ b/src/objects-debug.cc @@ -982,7 +982,6 @@ void AccessorInfo::AccessorInfoVerify() { VerifyPointer(name()); VerifyPointer(data()); VerifyPointer(flag()); - VerifyPointer(load_stub_cache()); } void AccessorInfo::AccessorInfoPrint() { @@ -997,8 +996,6 @@ void AccessorInfo::AccessorInfoPrint() { data()->ShortPrint(); PrintF("\n - flag: "); flag()->ShortPrint(); - PrintF("\n - load_stub_cache: "); - load_stub_cache()->ShortPrint(); } void AccessCheckInfo::AccessCheckInfoVerify() { @@ -1048,7 +1045,6 @@ void CallHandlerInfo::CallHandlerInfoVerify() { CHECK(IsCallHandlerInfo()); VerifyPointer(callback()); VerifyPointer(data()); - VerifyPointer(call_stub_cache()); } void CallHandlerInfo::CallHandlerInfoPrint() { @@ -1058,7 +1054,6 @@ void CallHandlerInfo::CallHandlerInfoPrint() { PrintF("\n - data: "); data()->ShortPrint(); PrintF("\n - call_stub_cache: "); - call_stub_cache()->ShortPrint(); } void TemplateInfo::TemplateInfoVerify() { diff --git a/src/objects-inl.h b/src/objects-inl.h index 79d70e11..499cb91d 100644 --- a/src/objects-inl.h +++ b/src/objects-inl.h @@ -2542,7 +2542,6 @@ ACCESSORS(AccessorInfo, setter, Object, kSetterOffset) ACCESSORS(AccessorInfo, data, Object, kDataOffset) ACCESSORS(AccessorInfo, name, Object, kNameOffset) ACCESSORS(AccessorInfo, flag, Smi, kFlagOffset) -ACCESSORS(AccessorInfo, load_stub_cache, Object, kLoadStubCacheOffset) ACCESSORS(AccessCheckInfo, named_callback, Object, kNamedCallbackOffset) ACCESSORS(AccessCheckInfo, indexed_callback, Object, kIndexedCallbackOffset) @@ -2557,7 +2556,6 @@ ACCESSORS(InterceptorInfo, data, Object, kDataOffset) ACCESSORS(CallHandlerInfo, callback, Object, kCallbackOffset) ACCESSORS(CallHandlerInfo, data, Object, kDataOffset) -ACCESSORS(CallHandlerInfo, call_stub_cache, Object, kCallStubCacheOffset) ACCESSORS(TemplateInfo, tag, Object, kTagOffset) ACCESSORS(TemplateInfo, property_list, Object, kPropertyListOffset) diff --git a/src/objects.cc b/src/objects.cc index c1cb922d..f5d19e28 100644 --- a/src/objects.cc +++ b/src/objects.cc @@ -35,7 +35,7 @@ #include "objects-inl.h" #include "objects-visiting.h" #include "macro-assembler.h" -#include "scanner.h" +#include "scanner-base.h" #include "scopeinfo.h" #include "string-stream.h" #include "utils.h" @@ -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()); @@ -1208,7 +1205,8 @@ MaybeObject* JSObject::AddFastProperty(String* name, // Normalize the object if the name is an actual string (not the // hidden symbols) and is not a real identifier. StringInputBuffer buffer(name); - if (!Scanner::IsIdentifier(&buffer) && name != Heap::hidden_symbol()) { + if (!ScannerConstants::IsIdentifier(&buffer) + && name != Heap::hidden_symbol()) { Object* obj; { MaybeObject* maybe_obj = NormalizeProperties(CLEAR_INOBJECT_PROPERTIES, 0); @@ -5088,7 +5086,8 @@ bool String::MarkAsUndetectable() { bool String::IsEqualTo(Vector<const char> str) { int slen = length(); - Access<Scanner::Utf8Decoder> decoder(Scanner::utf8_decoder()); + Access<ScannerConstants::Utf8Decoder> + decoder(ScannerConstants::utf8_decoder()); decoder->Reset(str.start(), str.length()); int i; for (i = 0; i < slen && decoder->has_more(); i++) { diff --git a/src/objects.h b/src/objects.h index 9d975ec5..b52bac27 100644 --- a/src/objects.h +++ b/src/objects.h @@ -5327,7 +5327,6 @@ class AccessorInfo: public Struct { DECL_ACCESSORS(data, Object) DECL_ACCESSORS(name, Object) DECL_ACCESSORS(flag, Smi) - DECL_ACCESSORS(load_stub_cache, Object) inline bool all_can_read(); inline void set_all_can_read(bool value); @@ -5353,8 +5352,7 @@ class AccessorInfo: public Struct { static const int kDataOffset = kSetterOffset + kPointerSize; static const int kNameOffset = kDataOffset + kPointerSize; static const int kFlagOffset = kNameOffset + kPointerSize; - static const int kLoadStubCacheOffset = kFlagOffset + kPointerSize; - static const int kSize = kLoadStubCacheOffset + kPointerSize; + static const int kSize = kFlagOffset + kPointerSize; private: // Bit positions in flag. @@ -5423,7 +5421,6 @@ class CallHandlerInfo: public Struct { public: DECL_ACCESSORS(callback, Object) DECL_ACCESSORS(data, Object) - DECL_ACCESSORS(call_stub_cache, Object) static inline CallHandlerInfo* cast(Object* obj); @@ -5434,8 +5431,7 @@ class CallHandlerInfo: public Struct { static const int kCallbackOffset = HeapObject::kHeaderSize; static const int kDataOffset = kCallbackOffset + kPointerSize; - static const int kCallStubCacheOffset = kDataOffset + kPointerSize; - static const int kSize = kCallStubCacheOffset + kPointerSize; + static const int kSize = kDataOffset + kPointerSize; private: DISALLOW_IMPLICIT_CONSTRUCTORS(CallHandlerInfo); diff --git a/src/parser.cc b/src/parser.cc index a0f3b714..186d1020 100644 --- a/src/parser.cc +++ b/src/parser.cc @@ -356,65 +356,6 @@ Handle<String> Parser::LookupCachedSymbol(int symbol_id, } -Vector<unsigned> PartialParserRecorder::ExtractData() { - int function_size = function_store_.size(); - int total_size = ScriptDataImpl::kHeaderSize + function_size; - Vector<unsigned> data = Vector<unsigned>::New(total_size); - preamble_[ScriptDataImpl::kFunctionsSizeOffset] = function_size; - preamble_[ScriptDataImpl::kSymbolCountOffset] = 0; - memcpy(data.start(), preamble_, sizeof(preamble_)); - int symbol_start = ScriptDataImpl::kHeaderSize + function_size; - if (function_size > 0) { - function_store_.WriteTo(data.SubVector(ScriptDataImpl::kHeaderSize, - symbol_start)); - } - return data; -} - - -void CompleteParserRecorder::LogSymbol(int start, Vector<const char> literal) { - if (!is_recording_) return; - - int hash = vector_hash(literal); - HashMap::Entry* entry = symbol_table_.Lookup(&literal, hash, true); - int id = static_cast<int>(reinterpret_cast<intptr_t>(entry->value)); - if (id == 0) { - // Put (symbol_id_ + 1) into entry and increment it. - id = ++symbol_id_; - entry->value = reinterpret_cast<void*>(id); - Vector<Vector<const char> > symbol = symbol_entries_.AddBlock(1, literal); - entry->key = &symbol[0]; - } - WriteNumber(id - 1); -} - - -Vector<unsigned> CompleteParserRecorder::ExtractData() { - int function_size = function_store_.size(); - // Add terminator to symbols, then pad to unsigned size. - int symbol_size = symbol_store_.size(); - int padding = sizeof(unsigned) - (symbol_size % sizeof(unsigned)); - symbol_store_.AddBlock(padding, ScriptDataImpl::kNumberTerminator); - symbol_size += padding; - int total_size = ScriptDataImpl::kHeaderSize + function_size - + (symbol_size / sizeof(unsigned)); - Vector<unsigned> data = Vector<unsigned>::New(total_size); - preamble_[ScriptDataImpl::kFunctionsSizeOffset] = function_size; - preamble_[ScriptDataImpl::kSymbolCountOffset] = symbol_id_; - memcpy(data.start(), preamble_, sizeof(preamble_)); - int symbol_start = ScriptDataImpl::kHeaderSize + function_size; - if (function_size > 0) { - function_store_.WriteTo(data.SubVector(ScriptDataImpl::kHeaderSize, - symbol_start)); - } - if (!has_error()) { - symbol_store_.WriteTo( - Vector<byte>::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<int>(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<int>(store_[ScriptDataImpl::kFunctionsSizeOffset]); + static_cast<int>(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<int>(store_[ScriptDataImpl::kSymbolCountOffset]); + static_cast<int>(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<const char> 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<byte>(number >> i) | 0x80u); - number &= mask; - } - mask >>= 7; - } - symbol_store_.Add(static_cast<byte>(number)); -} - - - const char* ScriptDataImpl::ReadString(unsigned* start, int* chars) { int length = start[0]; char* result = NewArray<char>(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<const char*> 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<const char*> ScriptDataImpl::BuildArgs() { - int arg_count = Read(kMessageArgCountPos); + int arg_count = Read(PreparseDataConstants::kMessageArgCountPos); const char** array = NewArray<const char*>(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<const char*> 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]; } @@ -727,7 +607,7 @@ FunctionLiteral* Parser::ParseProgram(Handle<String> source, // Initialize parser state. source->TryFlatten(); - scanner_.Initialize(source, JAVASCRIPT); + scanner_.Initialize(source); ASSERT(target_stack_ == NULL); if (pre_data_ != NULL) pre_data_->Initialize(); @@ -790,8 +670,7 @@ FunctionLiteral* Parser::ParseLazy(Handle<SharedFunctionInfo> info) { // Initialize parser state. source->TryFlatten(); - scanner_.Initialize(source, info->start_position(), info->end_position(), - JAVASCRIPT); + scanner_.Initialize(source, info->start_position(), info->end_position()); ASSERT(target_stack_ == NULL); mode_ = PARSE_EAGERLY; @@ -1692,11 +1571,13 @@ Statement* Parser::ParseExpressionOrLabelledStatement(ZoneStringList* labels, // ExpressionStatement | LabelledStatement :: // Expression ';' // Identifier ':' Statement - + bool starts_with_idenfifier = (peek() == Token::IDENTIFIER); Expression* expr = ParseExpression(true, CHECK_OK); - if (peek() == Token::COLON && expr && + if (peek() == Token::COLON && starts_with_idenfifier && expr && expr->AsVariableProxy() != NULL && !expr->AsVariableProxy()->is_this()) { + // Expression is a single identifier, and not, e.g., a parenthesized + // identifier. VariableProxy* var = expr->AsVariableProxy(); Handle<String> label = var->name(); // TODO(1240780): We don't check for redeclaration of labels @@ -2275,6 +2156,12 @@ Expression* Parser::ParseAssignmentExpression(bool accept_IN, bool* ok) { temp_scope_->AddProperty(); } + // If we assign a function literal to a property we pretenure the + // literal so it can be added as a constant function property. + if (property != NULL && right->AsFunctionLiteral() != NULL) { + right->AsFunctionLiteral()->set_pretenure(true); + } + if (fni_ != NULL) { // Check if the right hand side is a call to avoid inferring a // name if we're dealing with "a = function(){...}();"-like @@ -3610,7 +3497,7 @@ Expression* Parser::NewThrowError(Handle<String> constructor, Handle<Object> JsonParser::ParseJson(Handle<String> source) { source->TryFlatten(); - scanner_.Initialize(source, JSON); + scanner_.Initialize(source); Handle<Object> result = ParseJsonValue(); if (result.is_null() || scanner_.Next() != Token::EOS) { if (scanner_.stack_overflow()) { @@ -4594,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<byte*>(&store_[symbol_data_offset]); } else { @@ -4618,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; } @@ -4635,31 +4523,42 @@ int ScriptDataImpl::ReadNumber(byte** source) { } +// Create a Scanner for the preparser to use as input, and preparse the source. +static ScriptDataImpl* DoPreParse(Handle<String> source, + unibrow::CharacterStream* stream, + bool allow_lazy, + ParserRecorder* recorder, + int literal_flags) { + V8JavaScriptScanner scanner; + scanner.Initialize(source, stream, literal_flags); + preparser::PreParser preparser; + if (!preparser.PreParseProgram(&scanner, recorder, allow_lazy)) { + Top::StackOverflow(); + return NULL; + } + + // Extract the accumulated data from the recorder as a single + // contiguous vector that we are responsible for disposing. + Vector<unsigned> store = recorder->ExtractData(); + return new ScriptDataImpl(store); +} + + // Preparse, but only collect data that is immediately useful, // even if the preparser data is only used once. ScriptDataImpl* ParserApi::PartialPreParse(Handle<String> source, unibrow::CharacterStream* stream, v8::Extension* extension) { - Handle<Script> no_script; bool allow_lazy = FLAG_lazy && (extension == NULL); if (!allow_lazy) { // Partial preparsing is only about lazily compiled functions. // If we don't allow lazy compilation, the log data will be empty. return NULL; } - preparser::PreParser<Scanner, PartialParserRecorder> parser; - Scanner scanner; - scanner.Initialize(source, stream, JAVASCRIPT); PartialParserRecorder recorder; - if (!parser.PreParseProgram(&scanner, &recorder, allow_lazy)) { - Top::StackOverflow(); - return NULL; - } - // Extract the accumulated data from the recorder as a single - // contiguous vector that we are responsible for disposing. - Vector<unsigned> store = recorder.ExtractData(); - return new ScriptDataImpl(store); + return DoPreParse(source, stream, allow_lazy, &recorder, + JavaScriptScanner::kNoLiterals); } @@ -4667,19 +4566,12 @@ ScriptDataImpl* ParserApi::PreParse(Handle<String> source, unibrow::CharacterStream* stream, v8::Extension* extension) { Handle<Script> no_script; - preparser::PreParser<Scanner, CompleteParserRecorder> parser; - Scanner scanner; - scanner.Initialize(source, stream, JAVASCRIPT); bool allow_lazy = FLAG_lazy && (extension == NULL); CompleteParserRecorder recorder; - if (!parser.PreParseProgram(&scanner, &recorder, allow_lazy)) { - Top::StackOverflow(); - return NULL; - } - // Extract the accumulated data from the recorder as a single - // contiguous vector that we are responsible for disposing. - Vector<unsigned> store = recorder.ExtractData(); - return new ScriptDataImpl(store); + int kPreParseLiteralsFlags = + JavaScriptScanner::kLiteralString | JavaScriptScanner::kLiteralIdentifier; + return DoPreParse(source, stream, allow_lazy, + &recorder, kPreParseLiteralsFlags); } diff --git a/src/parser.h b/src/parser.h index 667410b3..a067bd7c 100644 --- a/src/parser.h +++ b/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<const char*> 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 = 4; - - 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<unsigned> store_; @@ -177,125 +161,6 @@ class ScriptDataImpl : public ScriptData { }; -// Record only functions. -class PartialParserRecorder { - public: - PartialParserRecorder(); - - void LogFunction(int start, int end, int literals, int properties) { - function_store_.Add(start); - function_store_.Add(end); - function_store_.Add(literals); - function_store_.Add(properties); - } - - void LogSymbol(int start, const char* symbol, int length) { } - - // Logs an error message and marks the log as containing an error. - // Further logging will be ignored, and ExtractData will return a vector - // representing the error only. - void LogMessage(int start, - int end, - const char* message, - const char* argument_opt) { - Scanner::Location location(start, end); - Vector<const char*> arguments; - if (argument_opt != NULL) { - arguments = Vector<const char*>(&argument_opt, 1); - } - this->LogMessage(location, message, arguments); - } - - int function_position() { return function_store_.size(); } - - void LogMessage(Scanner::Location loc, - const char* message, - Vector<const char*> args); - - Vector<unsigned> ExtractData(); - - void PauseRecording() { - pause_count_++; - is_recording_ = false; - } - - void ResumeRecording() { - ASSERT(pause_count_ > 0); - if (--pause_count_ == 0) is_recording_ = !has_error(); - } - - int symbol_position() { return 0; } - int symbol_ids() { return 0; } - - protected: - bool has_error() { - return static_cast<bool>(preamble_[ScriptDataImpl::kHasErrorOffset]); - } - - bool is_recording() { - return is_recording_; - } - - void WriteString(Vector<const char> str); - - Collector<unsigned> function_store_; - unsigned preamble_[ScriptDataImpl::kHeaderSize]; - bool is_recording_; - int pause_count_; - -#ifdef DEBUG - int prev_start_; -#endif -}; - - -// Record both functions and symbols. -class CompleteParserRecorder: public PartialParserRecorder { - public: - CompleteParserRecorder(); - - void LogSymbol(int start, Vector<const char> literal); - - void LogSymbol(int start, const char* symbol, int length) { - LogSymbol(start, Vector<const char>(symbol, length)); - } - - Vector<unsigned> ExtractData(); - - int symbol_position() { return symbol_store_.size(); } - int symbol_ids() { return symbol_id_; } - - private: - static int vector_hash(Vector<const char> string) { - int hash = 0; - for (int i = 0; i < string.length(); i++) { - int c = string[i]; - hash += c; - hash += (hash << 10); - hash ^= (hash >> 6); - } - return hash; - } - - static bool vector_compare(void* a, void* b) { - Vector<const char>* string1 = reinterpret_cast<Vector<const char>* >(a); - Vector<const char>* string2 = reinterpret_cast<Vector<const char>* >(b); - int length = string1->length(); - if (string2->length() != length) return false; - return memcmp(string1->start(), string2->start(), length) == 0; - } - - // Write a non-negative number to the symbol store. - void WriteNumber(int number); - - Collector<byte> symbol_store_; - Collector<Vector<const char> > symbol_entries_; - HashMap symbol_table_; - int symbol_id_; -}; - - - class ParserApi { public: // Parses the source code represented by the compilation info and sets its @@ -682,7 +547,7 @@ class Parser { Expression* ParseV8Intrinsic(bool* ok); INLINE(Token::Value peek()) { return scanner_.peek(); } - INLINE(Token::Value Next()) { return scanner_.Next(); } + INLINE(Token::Value Next()) { return scanner_.NextCheckStack(); } INLINE(void Consume(Token::Value token)); void Expect(Token::Value token, bool* ok); bool Check(Token::Value token); @@ -760,7 +625,7 @@ class Parser { ZoneList<Handle<String> > symbol_cache_; Handle<Script> script_; - Scanner scanner_; + V8JavaScriptScanner scanner_; Scope* top_scope_; int with_nesting_level_; @@ -852,7 +717,7 @@ class JsonParser BASE_EMBEDDED { // Converts the currently parsed literal to a JavaScript String. Handle<String> GetString(); - Scanner scanner_; + JsonScanner scanner_; }; } } // namespace v8::internal diff --git a/src/platform-freebsd.cc b/src/platform-freebsd.cc index 6b8f2c04..0d89a16f 100644 --- a/src/platform-freebsd.cc +++ b/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/src/platform-linux.cc b/src/platform-linux.cc index 89003ba8..cb8e919e 100644 --- a/src/platform-linux.cc +++ b/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/src/platform-macos.cc b/src/platform-macos.cc index 5e0e78db..c3f21dc5 100644 --- a/src/platform-macos.cc +++ b/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/src/platform-openbsd.cc b/src/platform-openbsd.cc index e03059ad..0751fc7e 100644 --- a/src/platform-openbsd.cc +++ b/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/src/platform-solaris.cc b/src/platform-solaris.cc index fcd69deb..ff5d83b6 100644 --- a/src/platform-solaris.cc +++ b/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/src/platform-win32.cc b/src/platform-win32.cc index caea16cb..c50424e5 100644 --- a/src/platform-win32.cc +++ b/src/platform-win32.cc @@ -865,8 +865,9 @@ void* OS::Allocate(const size_t requested, // For exectutable pages try and randomize the allocation address if (prot == PAGE_EXECUTE_READWRITE && msize >= Page::kPageSize) { - address = (V8::Random() << kPageSizeBits) | kAllocationRandomAddressMin; - address &= kAllocationRandomAddressMax; + address = (V8::RandomPrivate() << kPageSizeBits) + | kAllocationRandomAddressMin; + address &= kAllocationRandomAddressMax; } LPVOID mbase = VirtualAlloc(reinterpret_cast<void *>(address), @@ -1902,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/src/platform.h b/src/platform.h index 42e6eae9..49efc3c2 100644 --- a/src/platform.h +++ b/src/platform.h @@ -554,11 +554,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. @@ -580,14 +583,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/src/preparse-data.cc b/src/preparse-data.cc new file mode 100644 index 00000000..9a367718 --- /dev/null +++ b/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<const char> 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<unsigned> PartialParserRecorder::ExtractData() { + int function_size = function_store_.size(); + int total_size = PreparseDataConstants::kHeaderSize + function_size; + Vector<unsigned> data = Vector<unsigned>::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<const char> literal(literal_chars, length); + int hash = vector_hash(literal); + HashMap::Entry* entry = symbol_table_.Lookup(&literal, hash, true); + int id = static_cast<int>(reinterpret_cast<intptr_t>(entry->value)); + if (id == 0) { + // Put (symbol_id_ + 1) into entry and increment it. + id = ++symbol_id_; + entry->value = reinterpret_cast<void*>(id); + Vector<Vector<const char> > symbol = symbol_entries_.AddBlock(1, literal); + entry->key = &symbol[0]; + } + WriteNumber(id - 1); +} + + +Vector<unsigned> CompleteParserRecorder::ExtractData() { + int function_size = function_store_.size(); + // Add terminator to symbols, then pad to unsigned size. + int symbol_size = symbol_store_.size(); + int padding = sizeof(unsigned) - (symbol_size % sizeof(unsigned)); + symbol_store_.AddBlock(padding, PreparseDataConstants::kNumberTerminator); + symbol_size += padding; + int total_size = PreparseDataConstants::kHeaderSize + function_size + + (symbol_size / sizeof(unsigned)); + Vector<unsigned> data = Vector<unsigned>::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<byte>::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<byte>(number >> i) | 0x80u); + number &= mask; + } + mask >>= 7; + } + symbol_store_.Add(static_cast<byte>(number)); +} + + +} } // namespace v8::internal. diff --git a/src/preparse-data.h b/src/preparse-data.h new file mode 100644 index 00000000..a96e50fa --- /dev/null +++ b/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<unsigned> 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<unsigned> 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<bool>(preamble_[PreparseDataConstants::kHasErrorOffset]); + } + + bool is_recording() { + return is_recording_; + } + + void WriteString(Vector<const char> str); + + Collector<unsigned> 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<unsigned> 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<unsigned> ExtractData(); + + virtual int symbol_position() { return symbol_store_.size(); } + virtual int symbol_ids() { return symbol_id_; } + + private: + static int vector_hash(Vector<const char> string) { + int hash = 0; + for (int i = 0; i < string.length(); i++) { + int c = string[i]; + hash += c; + hash += (hash << 10); + hash ^= (hash >> 6); + } + return hash; + } + + static bool vector_compare(void* a, void* b) { + Vector<const char>* string1 = reinterpret_cast<Vector<const char>* >(a); + Vector<const char>* string2 = reinterpret_cast<Vector<const char>* >(b); + int length = string1->length(); + if (string2->length() != length) return false; + return memcmp(string1->start(), string2->start(), length) == 0; + } + + // Write a non-negative number to the symbol store. + void WriteNumber(int number); + + Collector<byte> symbol_store_; + Collector<Vector<const char> > symbol_entries_; + HashMap symbol_table_; + int symbol_id_; +}; + + +} } // namespace v8::internal. + +#endif // V8_PREPARSER_DATA_H_ diff --git a/src/preparser.cc b/src/preparser.cc new file mode 100644 index 00000000..90617312 --- /dev/null +++ b/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)* <end_token> + + 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/src/preparser.h b/src/preparser.h index 44c55cf7..b783d65d 100644 --- a/src/preparser.h +++ b/src/preparser.h @@ -28,14 +28,12 @@ #ifndef V8_PREPARSER_H #define V8_PREPARSER_H -#include "unicode.h" - namespace v8 { namespace preparser { // Preparsing checks a JavaScript program and emits preparse-data that helps // a later parsing to be faster. -// See preparser-data.h for the data. +// 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. @@ -75,7 +73,6 @@ typedef int Identifier; typedef int Arguments; -template <typename Scanner, typename PreParserLog> class PreParser { public: PreParser() : scope_(NULL), allow_lazy_(true) { } @@ -85,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; @@ -234,1181 +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 <typename Scanner, typename Log> -void PreParser<Scanner, Log>::ReportUnexpectedToken(i::Token::Value token) { - // We don't report stack overflows here, to avoid increasing the - // stack depth even further. Instead we report it after parsing is - // over, in ParseProgram. - if (token == i::Token::ILLEGAL && scanner_->stack_overflow()) { - return; - } - typename Scanner::Location source_location = scanner_->location(); - - // Four of the tokens are treated specially - switch (token) { - case i::Token::EOS: - return ReportMessageAt(source_location.beg_pos, source_location.end_pos, - "unexpected_eos", NULL); - case i::Token::NUMBER: - return ReportMessageAt(source_location.beg_pos, source_location.end_pos, - "unexpected_token_number", NULL); - case i::Token::STRING: - return ReportMessageAt(source_location.beg_pos, source_location.end_pos, - "unexpected_token_string", NULL); - case i::Token::IDENTIFIER: - return ReportMessageAt(source_location.beg_pos, source_location.end_pos, - "unexpected_token_identifier", NULL); - default: - const char* name = i::Token::String(token); - ReportMessageAt(source_location.beg_pos, source_location.end_pos, - "unexpected_token", name); - } -} - - -template <typename Scanner, typename Log> -SourceElements PreParser<Scanner, Log>::ParseSourceElements(int end_token, - bool* ok) { - // SourceElements :: - // (Statement)* <end_token> - - while (peek() != end_token) { - ParseStatement(CHECK_OK); - } - return kUnknownSourceElements; -} - - -template <typename Scanner, typename Log> -Statement PreParser<Scanner, Log>::ParseStatement(bool* ok) { - // Statement :: - // Block - // VariableStatement - // EmptyStatement - // ExpressionStatement - // IfStatement - // IterationStatement - // ContinueStatement - // BreakStatement - // ReturnStatement - // WithStatement - // LabelledStatement - // SwitchStatement - // ThrowStatement - // TryStatement - // DebuggerStatement - - // Note: Since labels can only be used by 'break' and 'continue' - // statements, which themselves are only valid within blocks, - // iterations or 'switch' statements (i.e., BreakableStatements), - // labels can be simply ignored in all other cases; except for - // trivial labeled break statements 'label: break label' which is - // parsed into an empty statement. - - // Keep the source position of the statement - switch (peek()) { - case i::Token::LBRACE: - return ParseBlock(ok); - - case i::Token::CONST: - case i::Token::VAR: - return ParseVariableStatement(ok); - - case i::Token::SEMICOLON: - Next(); - return kUnknownStatement; - - case i::Token::IF: - return ParseIfStatement(ok); - - case i::Token::DO: - return ParseDoWhileStatement(ok); - - case i::Token::WHILE: - return ParseWhileStatement(ok); - - case i::Token::FOR: - return ParseForStatement(ok); - - case i::Token::CONTINUE: - return ParseContinueStatement(ok); - - case i::Token::BREAK: - return ParseBreakStatement(ok); - - case i::Token::RETURN: - return ParseReturnStatement(ok); - - case i::Token::WITH: - return ParseWithStatement(ok); - - case i::Token::SWITCH: - return ParseSwitchStatement(ok); - - case i::Token::THROW: - return ParseThrowStatement(ok); - - case i::Token::TRY: - return ParseTryStatement(ok); - - case i::Token::FUNCTION: - return ParseFunctionDeclaration(ok); - - case i::Token::NATIVE: - return ParseNativeDeclaration(ok); - - case i::Token::DEBUGGER: - return ParseDebuggerStatement(ok); - - default: - return ParseExpressionOrLabelledStatement(ok); - } -} - - -template <typename Scanner, typename Log> -Statement PreParser<Scanner, Log>::ParseFunctionDeclaration(bool* ok) { - // FunctionDeclaration :: - // 'function' Identifier '(' FormalParameterListopt ')' '{' FunctionBody '}' - Expect(i::Token::FUNCTION, CHECK_OK); - ParseIdentifier(CHECK_OK); - ParseFunctionLiteral(CHECK_OK); - return kUnknownStatement; -} - - -// Language extension which is only enabled for source files loaded -// through the API's extension mechanism. A native function -// declaration is resolved by looking up the function through a -// callback provided by the extension. -template <typename Scanner, typename Log> -Statement PreParser<Scanner, Log>::ParseNativeDeclaration(bool* ok) { - Expect(i::Token::NATIVE, CHECK_OK); - Expect(i::Token::FUNCTION, CHECK_OK); - ParseIdentifier(CHECK_OK); - Expect(i::Token::LPAREN, CHECK_OK); - bool done = (peek() == i::Token::RPAREN); - while (!done) { - ParseIdentifier(CHECK_OK); - done = (peek() == i::Token::RPAREN); - if (!done) { - Expect(i::Token::COMMA, CHECK_OK); - } - } - Expect(i::Token::RPAREN, CHECK_OK); - Expect(i::Token::SEMICOLON, CHECK_OK); - return kUnknownStatement; -} - - -template <typename Scanner, typename Log> -Statement PreParser<Scanner, Log>::ParseBlock(bool* ok) { - // Block :: - // '{' Statement* '}' - - // Note that a Block does not introduce a new execution scope! - // (ECMA-262, 3rd, 12.2) - // - Expect(i::Token::LBRACE, CHECK_OK); - while (peek() != i::Token::RBRACE) { - ParseStatement(CHECK_OK); - } - Expect(i::Token::RBRACE, CHECK_OK); - return kUnknownStatement; -} - - -template <typename Scanner, typename Log> -Statement PreParser<Scanner, Log>::ParseVariableStatement(bool* ok) { - // VariableStatement :: - // VariableDeclarations ';' - - Statement result = ParseVariableDeclarations(true, NULL, CHECK_OK); - ExpectSemicolon(CHECK_OK); - return result; -} - - -// If the variable declaration declares exactly one non-const -// variable, then *var is set to that variable. In all other cases, -// *var is untouched; in particular, it is the caller's responsibility -// to initialize it properly. This mechanism is also used for the parsing -// of 'for-in' loops. -template <typename Scanner, typename Log> -Statement PreParser<Scanner, Log>::ParseVariableDeclarations(bool accept_IN, - int* num_decl, - bool* ok) { - // VariableDeclarations :: - // ('var' | 'const') (Identifier ('=' AssignmentExpression)?)+[','] - - if (peek() == i::Token::VAR) { - Consume(i::Token::VAR); - } else if (peek() == i::Token::CONST) { - Consume(i::Token::CONST); - } else { - *ok = false; - return 0; - } - - // The scope of a variable/const declared anywhere inside a function - // is the entire function (ECMA-262, 3rd, 10.1.3, and 12.2). . - int nvars = 0; // the number of variables declared - do { - // Parse variable name. - if (nvars > 0) Consume(i::Token::COMMA); - ParseIdentifier(CHECK_OK); - nvars++; - if (peek() == i::Token::ASSIGN) { - Expect(i::Token::ASSIGN, CHECK_OK); - ParseAssignmentExpression(accept_IN, CHECK_OK); - } - } while (peek() == i::Token::COMMA); - - if (num_decl != NULL) *num_decl = nvars; - return kUnknownStatement; -} - - -template <typename Scanner, typename Log> -Statement PreParser<Scanner, Log>::ParseExpressionOrLabelledStatement( - bool* ok) { - // ExpressionStatement | LabelledStatement :: - // Expression ';' - // Identifier ':' Statement - - Expression expr = ParseExpression(true, CHECK_OK); - if (peek() == i::Token::COLON && expr == kIdentifierExpression) { - Consume(i::Token::COLON); - return ParseStatement(ok); - } - // Parsed expression statement. - ExpectSemicolon(CHECK_OK); - return kUnknownStatement; -} - - -template <typename Scanner, typename Log> -Statement PreParser<Scanner, Log>::ParseIfStatement(bool* ok) { - // IfStatement :: - // 'if' '(' Expression ')' Statement ('else' Statement)? - - Expect(i::Token::IF, CHECK_OK); - Expect(i::Token::LPAREN, CHECK_OK); - ParseExpression(true, CHECK_OK); - Expect(i::Token::RPAREN, CHECK_OK); - ParseStatement(CHECK_OK); - if (peek() == i::Token::ELSE) { - Next(); - ParseStatement(CHECK_OK); - } - return kUnknownStatement; -} - - -template <typename Scanner, typename Log> -Statement PreParser<Scanner, Log>::ParseContinueStatement(bool* ok) { - // ContinueStatement :: - // 'continue' [no line terminator] Identifier? ';' - - Expect(i::Token::CONTINUE, CHECK_OK); - i::Token::Value tok = peek(); - if (!scanner_->has_line_terminator_before_next() && - tok != i::Token::SEMICOLON && - tok != i::Token::RBRACE && - tok != i::Token::EOS) { - ParseIdentifier(CHECK_OK); - } - ExpectSemicolon(CHECK_OK); - return kUnknownStatement; -} - - -template <typename Scanner, typename Log> -Statement PreParser<Scanner, Log>::ParseBreakStatement(bool* ok) { - // BreakStatement :: - // 'break' [no line terminator] Identifier? ';' - - Expect(i::Token::BREAK, CHECK_OK); - i::Token::Value tok = peek(); - if (!scanner_->has_line_terminator_before_next() && - tok != i::Token::SEMICOLON && - tok != i::Token::RBRACE && - tok != i::Token::EOS) { - ParseIdentifier(CHECK_OK); - } - ExpectSemicolon(CHECK_OK); - return kUnknownStatement; -} - - -template <typename Scanner, typename Log> -Statement PreParser<Scanner, Log>::ParseReturnStatement(bool* ok) { - // ReturnStatement :: - // 'return' [no line terminator] Expression? ';' - - // Consume the return token. It is necessary to do the before - // reporting any errors on it, because of the way errors are - // reported (underlining). - Expect(i::Token::RETURN, CHECK_OK); - - // An ECMAScript program is considered syntactically incorrect if it - // contains a return statement that is not within the body of a - // function. See ECMA-262, section 12.9, page 67. - // This is not handled during preparsing. - - i::Token::Value tok = peek(); - if (!scanner_->has_line_terminator_before_next() && - tok != i::Token::SEMICOLON && - tok != i::Token::RBRACE && - tok != i::Token::EOS) { - ParseExpression(true, CHECK_OK); - } - ExpectSemicolon(CHECK_OK); - return kUnknownStatement; -} - - -template <typename Scanner, typename Log> -Statement PreParser<Scanner, Log>::ParseWithStatement(bool* ok) { - // WithStatement :: - // 'with' '(' Expression ')' Statement - Expect(i::Token::WITH, CHECK_OK); - Expect(i::Token::LPAREN, CHECK_OK); - ParseExpression(true, CHECK_OK); - Expect(i::Token::RPAREN, CHECK_OK); - - scope_->EnterWith(); - ParseStatement(CHECK_OK); - scope_->LeaveWith(); - return kUnknownStatement; -} - - -template <typename Scanner, typename Log> -Statement PreParser<Scanner, Log>::ParseSwitchStatement(bool* ok) { - // SwitchStatement :: - // 'switch' '(' Expression ')' '{' CaseClause* '}' - - Expect(i::Token::SWITCH, CHECK_OK); - Expect(i::Token::LPAREN, CHECK_OK); - ParseExpression(true, CHECK_OK); - Expect(i::Token::RPAREN, CHECK_OK); - - Expect(i::Token::LBRACE, CHECK_OK); - i::Token::Value token = peek(); - while (token != i::Token::RBRACE) { - if (token == i::Token::CASE) { - Expect(i::Token::CASE, CHECK_OK); - ParseExpression(true, CHECK_OK); - Expect(i::Token::COLON, CHECK_OK); - } else if (token == i::Token::DEFAULT) { - Expect(i::Token::DEFAULT, CHECK_OK); - Expect(i::Token::COLON, CHECK_OK); - } else { - ParseStatement(CHECK_OK); - } - token = peek(); - } - Expect(i::Token::RBRACE, CHECK_OK); - - return kUnknownStatement; -} - - -template <typename Scanner, typename Log> -Statement PreParser<Scanner, Log>::ParseDoWhileStatement(bool* ok) { - // DoStatement :: - // 'do' Statement 'while' '(' Expression ')' ';' - - Expect(i::Token::DO, CHECK_OK); - ParseStatement(CHECK_OK); - Expect(i::Token::WHILE, CHECK_OK); - Expect(i::Token::LPAREN, CHECK_OK); - ParseExpression(true, CHECK_OK); - Expect(i::Token::RPAREN, CHECK_OK); - return kUnknownStatement; -} - - -template <typename Scanner, typename Log> -Statement PreParser<Scanner, Log>::ParseWhileStatement(bool* ok) { - // WhileStatement :: - // 'while' '(' Expression ')' Statement - - Expect(i::Token::WHILE, CHECK_OK); - Expect(i::Token::LPAREN, CHECK_OK); - ParseExpression(true, CHECK_OK); - Expect(i::Token::RPAREN, CHECK_OK); - ParseStatement(CHECK_OK); - return kUnknownStatement; -} - - -template <typename Scanner, typename Log> -Statement PreParser<Scanner, Log>::ParseForStatement(bool* ok) { - // ForStatement :: - // 'for' '(' Expression? ';' Expression? ';' Expression? ')' Statement - - Expect(i::Token::FOR, CHECK_OK); - Expect(i::Token::LPAREN, CHECK_OK); - if (peek() != i::Token::SEMICOLON) { - if (peek() == i::Token::VAR || peek() == i::Token::CONST) { - int decl_count; - ParseVariableDeclarations(false, &decl_count, CHECK_OK); - if (peek() == i::Token::IN && decl_count == 1) { - Expect(i::Token::IN, CHECK_OK); - ParseExpression(true, CHECK_OK); - Expect(i::Token::RPAREN, CHECK_OK); - - ParseStatement(CHECK_OK); - return kUnknownStatement; - } - } else { - ParseExpression(false, CHECK_OK); - if (peek() == i::Token::IN) { - Expect(i::Token::IN, CHECK_OK); - ParseExpression(true, CHECK_OK); - Expect(i::Token::RPAREN, CHECK_OK); - - ParseStatement(CHECK_OK); - return kUnknownStatement; - } - } - } - - // Parsed initializer at this point. - Expect(i::Token::SEMICOLON, CHECK_OK); - - if (peek() != i::Token::SEMICOLON) { - ParseExpression(true, CHECK_OK); - } - Expect(i::Token::SEMICOLON, CHECK_OK); - - if (peek() != i::Token::RPAREN) { - ParseExpression(true, CHECK_OK); - } - Expect(i::Token::RPAREN, CHECK_OK); - - ParseStatement(CHECK_OK); - return kUnknownStatement; -} - - -template <typename Scanner, typename Log> -Statement PreParser<Scanner, Log>::ParseThrowStatement(bool* ok) { - // ThrowStatement :: - // 'throw' [no line terminator] Expression ';' - - Expect(i::Token::THROW, CHECK_OK); - if (scanner_->has_line_terminator_before_next()) { - typename Scanner::Location pos = scanner_->location(); - ReportMessageAt(pos.beg_pos, pos.end_pos, - "newline_after_throw", NULL); - *ok = false; - return NULL; - } - ParseExpression(true, CHECK_OK); - ExpectSemicolon(CHECK_OK); - - return kUnknownStatement; -} - - -template <typename Scanner, typename Log> -Statement PreParser<Scanner, Log>::ParseTryStatement(bool* ok) { - // TryStatement :: - // 'try' Block Catch - // 'try' Block Finally - // 'try' Block Catch Finally - // - // Catch :: - // 'catch' '(' Identifier ')' Block - // - // Finally :: - // 'finally' Block - - // In preparsing, allow any number of catch/finally blocks, including zero - // of both. - - Expect(i::Token::TRY, CHECK_OK); - - ParseBlock(CHECK_OK); - - bool catch_or_finally_seen = false; - if (peek() == i::Token::CATCH) { - Expect(i::Token::CATCH, CHECK_OK); - Expect(i::Token::LPAREN, CHECK_OK); - ParseIdentifier(CHECK_OK); - Expect(i::Token::RPAREN, CHECK_OK); - ParseBlock(CHECK_OK); - catch_or_finally_seen = true; - } - if (peek() == i::Token::FINALLY) { - Expect(i::Token::FINALLY, CHECK_OK); - ParseBlock(CHECK_OK); - catch_or_finally_seen = true; - } - if (!catch_or_finally_seen) { - *ok = false; - } - return kUnknownStatement; -} - - -template <typename Scanner, typename Log> -Statement PreParser<Scanner, Log>::ParseDebuggerStatement(bool* ok) { - // In ECMA-262 'debugger' is defined as a reserved keyword. In some browser - // contexts this is used as a statement which invokes the debugger as if a - // break point is present. - // DebuggerStatement :: - // 'debugger' ';' - - Expect(i::Token::DEBUGGER, CHECK_OK); - ExpectSemicolon(CHECK_OK); - return kUnknownStatement; -} - - -// Precedence = 1 -template <typename Scanner, typename Log> -Expression PreParser<Scanner, Log>::ParseExpression(bool accept_IN, bool* ok) { - // Expression :: - // AssignmentExpression - // Expression ',' AssignmentExpression - - Expression result = ParseAssignmentExpression(accept_IN, CHECK_OK); - while (peek() == i::Token::COMMA) { - Expect(i::Token::COMMA, CHECK_OK); - ParseAssignmentExpression(accept_IN, CHECK_OK); - result = kUnknownExpression; - } - return result; -} - - -// Precedence = 2 -template <typename Scanner, typename Log> -Expression PreParser<Scanner, Log>::ParseAssignmentExpression(bool accept_IN, - bool* ok) { - // AssignmentExpression :: - // ConditionalExpression - // LeftHandSideExpression AssignmentOperator AssignmentExpression - - Expression expression = ParseConditionalExpression(accept_IN, CHECK_OK); - - if (!i::Token::IsAssignmentOp(peek())) { - // Parsed conditional expression only (no assignment). - return expression; - } - - i::Token::Value op = Next(); // Get assignment operator. - ParseAssignmentExpression(accept_IN, CHECK_OK); - - if ((op == i::Token::ASSIGN) && (expression == kThisPropertyExpression)) { - scope_->AddProperty(); - } - - return kUnknownExpression; -} - - -// Precedence = 3 -template <typename Scanner, typename Log> -Expression PreParser<Scanner, Log>::ParseConditionalExpression(bool accept_IN, - bool* ok) { - // ConditionalExpression :: - // LogicalOrExpression - // LogicalOrExpression '?' AssignmentExpression ':' AssignmentExpression - - // We start using the binary expression parser for prec >= 4 only! - Expression expression = ParseBinaryExpression(4, accept_IN, CHECK_OK); - if (peek() != i::Token::CONDITIONAL) return expression; - Consume(i::Token::CONDITIONAL); - // In parsing the first assignment expression in conditional - // expressions we always accept the 'in' keyword; see ECMA-262, - // section 11.12, page 58. - ParseAssignmentExpression(true, CHECK_OK); - Expect(i::Token::COLON, CHECK_OK); - ParseAssignmentExpression(accept_IN, CHECK_OK); - return kUnknownExpression; -} - - -template <typename Scanner, typename Log> -int PreParser<Scanner, Log>::Precedence(i::Token::Value tok, bool accept_IN) { - if (tok == i::Token::IN && !accept_IN) - return 0; // 0 precedence will terminate binary expression parsing - - return i::Token::Precedence(tok); -} - - -// Precedence >= 4 -template <typename Scanner, typename Log> -Expression PreParser<Scanner, Log>::ParseBinaryExpression(int prec, - bool accept_IN, - bool* ok) { - Expression result = ParseUnaryExpression(CHECK_OK); - for (int prec1 = Precedence(peek(), accept_IN); prec1 >= prec; prec1--) { - // prec1 >= 4 - while (Precedence(peek(), accept_IN) == prec1) { - Next(); - ParseBinaryExpression(prec1 + 1, accept_IN, CHECK_OK); - result = kUnknownExpression; - } - } - return result; -} - - -template <typename Scanner, typename Log> -Expression PreParser<Scanner, Log>::ParseUnaryExpression(bool* ok) { - // UnaryExpression :: - // PostfixExpression - // 'delete' UnaryExpression - // 'void' UnaryExpression - // 'typeof' UnaryExpression - // '++' UnaryExpression - // '--' UnaryExpression - // '+' UnaryExpression - // '-' UnaryExpression - // '~' UnaryExpression - // '!' UnaryExpression - - i::Token::Value op = peek(); - if (i::Token::IsUnaryOp(op) || i::Token::IsCountOp(op)) { - op = Next(); - ParseUnaryExpression(ok); - return kUnknownExpression; - } else { - return ParsePostfixExpression(ok); - } -} - - -template <typename Scanner, typename Log> -Expression PreParser<Scanner, Log>::ParsePostfixExpression(bool* ok) { - // PostfixExpression :: - // LeftHandSideExpression ('++' | '--')? - - Expression expression = ParseLeftHandSideExpression(CHECK_OK); - if (!scanner_->has_line_terminator_before_next() && - i::Token::IsCountOp(peek())) { - Next(); - return kUnknownExpression; - } - return expression; -} - - -template <typename Scanner, typename Log> -Expression PreParser<Scanner, Log>::ParseLeftHandSideExpression(bool* ok) { - // LeftHandSideExpression :: - // (NewExpression | MemberExpression) ... - - Expression result; - if (peek() == i::Token::NEW) { - result = ParseNewExpression(CHECK_OK); - } else { - result = ParseMemberExpression(CHECK_OK); - } - - while (true) { - switch (peek()) { - case i::Token::LBRACK: { - Consume(i::Token::LBRACK); - ParseExpression(true, CHECK_OK); - Expect(i::Token::RBRACK, CHECK_OK); - if (result == kThisExpression) { - result = kThisPropertyExpression; - } else { - result = kUnknownExpression; - } - break; - } - - case i::Token::LPAREN: { - ParseArguments(CHECK_OK); - result = kUnknownExpression; - break; - } - - case i::Token::PERIOD: { - Consume(i::Token::PERIOD); - ParseIdentifierName(CHECK_OK); - if (result == kThisExpression) { - result = kThisPropertyExpression; - } else { - result = kUnknownExpression; - } - break; - } - - default: - return result; - } - } -} - - -template <typename Scanner, typename Log> -Expression PreParser<Scanner, Log>::ParseNewExpression(bool* ok) { - // NewExpression :: - // ('new')+ MemberExpression - - // The grammar for new expressions is pretty warped. The keyword - // 'new' can either be a part of the new expression (where it isn't - // followed by an argument list) or a part of the member expression, - // where it must be followed by an argument list. To accommodate - // this, we parse the 'new' keywords greedily and keep track of how - // many we have parsed. This information is then passed on to the - // member expression parser, which is only allowed to match argument - // lists as long as it has 'new' prefixes left - unsigned new_count = 0; - do { - Consume(i::Token::NEW); - new_count++; - } while (peek() == i::Token::NEW); - - return ParseMemberWithNewPrefixesExpression(new_count, ok); -} - - -template <typename Scanner, typename Log> -Expression PreParser<Scanner, Log>::ParseMemberExpression(bool* ok) { - return ParseMemberWithNewPrefixesExpression(0, ok); -} - - -template <typename Scanner, typename Log> -Expression PreParser<Scanner, Log>::ParseMemberWithNewPrefixesExpression( - unsigned new_count, bool* ok) { - // MemberExpression :: - // (PrimaryExpression | FunctionLiteral) - // ('[' Expression ']' | '.' Identifier | Arguments)* - - // Parse the initial primary or function expression. - Expression result = NULL; - if (peek() == i::Token::FUNCTION) { - Consume(i::Token::FUNCTION); - if (peek() == i::Token::IDENTIFIER) { - ParseIdentifier(CHECK_OK); - } - result = ParseFunctionLiteral(CHECK_OK); - } else { - result = ParsePrimaryExpression(CHECK_OK); - } - - while (true) { - switch (peek()) { - case i::Token::LBRACK: { - Consume(i::Token::LBRACK); - ParseExpression(true, CHECK_OK); - Expect(i::Token::RBRACK, CHECK_OK); - if (result == kThisExpression) { - result = kThisPropertyExpression; - } else { - result = kUnknownExpression; - } - break; - } - case i::Token::PERIOD: { - Consume(i::Token::PERIOD); - ParseIdentifierName(CHECK_OK); - if (result == kThisExpression) { - result = kThisPropertyExpression; - } else { - result = kUnknownExpression; - } - break; - } - case i::Token::LPAREN: { - if (new_count == 0) return result; - // Consume one of the new prefixes (already parsed). - ParseArguments(CHECK_OK); - new_count--; - result = kUnknownExpression; - break; - } - default: - return result; - } - } -} - - -template <typename Scanner, typename Log> -Expression PreParser<Scanner, Log>::ParsePrimaryExpression(bool* ok) { - // PrimaryExpression :: - // 'this' - // 'null' - // 'true' - // 'false' - // Identifier - // Number - // String - // ArrayLiteral - // ObjectLiteral - // RegExpLiteral - // '(' Expression ')' - - Expression result = kUnknownExpression; - switch (peek()) { - case i::Token::THIS: { - Next(); - result = kThisExpression; - break; - } - - case i::Token::IDENTIFIER: { - ParseIdentifier(CHECK_OK); - result = kIdentifierExpression; - break; - } - - case i::Token::NULL_LITERAL: - case i::Token::TRUE_LITERAL: - case i::Token::FALSE_LITERAL: - case i::Token::NUMBER: { - Next(); - break; - } - case i::Token::STRING: { - Next(); - result = GetStringSymbol(); - break; - } - - case i::Token::ASSIGN_DIV: - result = ParseRegExpLiteral(true, CHECK_OK); - break; - - case i::Token::DIV: - result = ParseRegExpLiteral(false, CHECK_OK); - break; - - case i::Token::LBRACK: - result = ParseArrayLiteral(CHECK_OK); - break; - - case i::Token::LBRACE: - result = ParseObjectLiteral(CHECK_OK); - break; - - case i::Token::LPAREN: - Consume(i::Token::LPAREN); - result = ParseExpression(true, CHECK_OK); - Expect(i::Token::RPAREN, CHECK_OK); - if (result == kIdentifierExpression) result = kUnknownExpression; - break; - - case i::Token::MOD: - result = ParseV8Intrinsic(CHECK_OK); - break; - - default: { - Next(); - *ok = false; - return kUnknownExpression; - } - } - - return result; -} - - -template <typename Scanner, typename Log> -Expression PreParser<Scanner, Log>::ParseArrayLiteral(bool* ok) { - // ArrayLiteral :: - // '[' Expression? (',' Expression?)* ']' - Expect(i::Token::LBRACK, CHECK_OK); - while (peek() != i::Token::RBRACK) { - if (peek() != i::Token::COMMA) { - ParseAssignmentExpression(true, CHECK_OK); - } - if (peek() != i::Token::RBRACK) { - Expect(i::Token::COMMA, CHECK_OK); - } - } - Expect(i::Token::RBRACK, CHECK_OK); - - scope_->NextMaterializedLiteralIndex(); - return kUnknownExpression; -} - - -template <typename Scanner, typename Log> -Expression PreParser<Scanner, Log>::ParseObjectLiteral(bool* ok) { - // ObjectLiteral :: - // '{' ( - // ((IdentifierName | String | Number) ':' AssignmentExpression) - // | (('get' | 'set') (IdentifierName | String | Number) FunctionLiteral) - // )*[','] '}' - - Expect(i::Token::LBRACE, CHECK_OK); - while (peek() != i::Token::RBRACE) { - i::Token::Value next = peek(); - switch (next) { - case i::Token::IDENTIFIER: { - bool is_getter = false; - bool is_setter = false; - ParseIdentifierOrGetOrSet(&is_getter, &is_setter, CHECK_OK); - if ((is_getter || is_setter) && peek() != i::Token::COLON) { - i::Token::Value name = Next(); - if (name != i::Token::IDENTIFIER && - name != i::Token::NUMBER && - name != i::Token::STRING && - !i::Token::IsKeyword(name)) { - *ok = false; - return kUnknownExpression; - } - ParseFunctionLiteral(CHECK_OK); - if (peek() != i::Token::RBRACE) { - Expect(i::Token::COMMA, CHECK_OK); - } - continue; // restart the while - } - break; - } - case i::Token::STRING: - Consume(next); - GetStringSymbol(); - break; - case i::Token::NUMBER: - Consume(next); - break; - default: - if (i::Token::IsKeyword(next)) { - Consume(next); - } else { - // Unexpected token. - *ok = false; - return kUnknownExpression; - } - } - - Expect(i::Token::COLON, CHECK_OK); - ParseAssignmentExpression(true, CHECK_OK); - - // TODO(1240767): Consider allowing trailing comma. - if (peek() != i::Token::RBRACE) Expect(i::Token::COMMA, CHECK_OK); - } - Expect(i::Token::RBRACE, CHECK_OK); - - scope_->NextMaterializedLiteralIndex(); - return kUnknownExpression; -} - - -template <typename Scanner, typename Log> -Expression PreParser<Scanner, Log>::ParseRegExpLiteral(bool seen_equal, - bool* ok) { - if (!scanner_->ScanRegExpPattern(seen_equal)) { - Next(); - typename Scanner::Location location = scanner_->location(); - ReportMessageAt(location.beg_pos, location.end_pos, - "unterminated_regexp", NULL); - *ok = false; - return kUnknownExpression; - } - - scope_->NextMaterializedLiteralIndex(); - - if (!scanner_->ScanRegExpFlags()) { - Next(); - typename Scanner::Location location = scanner_->location(); - ReportMessageAt(location.beg_pos, location.end_pos, - "invalid_regexp_flags", NULL); - *ok = false; - return kUnknownExpression; - } - Next(); - return kUnknownExpression; -} - - -template <typename Scanner, typename Log> -Arguments PreParser<Scanner, Log>::ParseArguments(bool* ok) { - // Arguments :: - // '(' (AssignmentExpression)*[','] ')' - - Expect(i::Token::LPAREN, CHECK_OK); - bool done = (peek() == i::Token::RPAREN); - int argc = 0; - while (!done) { - ParseAssignmentExpression(true, CHECK_OK); - argc++; - done = (peek() == i::Token::RPAREN); - if (!done) Expect(i::Token::COMMA, CHECK_OK); - } - Expect(i::Token::RPAREN, CHECK_OK); - return argc; -} - - -template <typename Scanner, typename Log> -Expression PreParser<Scanner, Log>::ParseFunctionLiteral(bool* ok) { - // Function :: - // '(' FormalParameterList? ')' '{' FunctionBody '}' - - // Parse function body. - ScopeType outer_scope_type = scope_->type(); - bool inside_with = scope_->IsInsideWith(); - Scope function_scope(&scope_, kFunctionScope); - - // FormalParameterList :: - // '(' (Identifier)*[','] ')' - Expect(i::Token::LPAREN, CHECK_OK); - bool done = (peek() == i::Token::RPAREN); - while (!done) { - ParseIdentifier(CHECK_OK); - done = (peek() == i::Token::RPAREN); - if (!done) { - Expect(i::Token::COMMA, CHECK_OK); - } - } - Expect(i::Token::RPAREN, CHECK_OK); - - Expect(i::Token::LBRACE, CHECK_OK); - int function_block_pos = scanner_->location().beg_pos; - - // Determine if the function will be lazily compiled. - // Currently only happens to top-level functions. - // Optimistically assume that all top-level functions are lazily compiled. - bool is_lazily_compiled = - (outer_scope_type == kTopLevelScope && !inside_with && allow_lazy_); - - if (is_lazily_compiled) { - log_->PauseRecording(); - ParseSourceElements(i::Token::RBRACE, ok); - log_->ResumeRecording(); - if (!*ok) return kUnknownExpression; - - Expect(i::Token::RBRACE, CHECK_OK); - - int end_pos = scanner_->location().end_pos; - log_->LogFunction(function_block_pos, end_pos, - function_scope.materialized_literal_count(), - function_scope.expected_properties()); - } else { - ParseSourceElements(i::Token::RBRACE, CHECK_OK); - Expect(i::Token::RBRACE, CHECK_OK); - } - return kUnknownExpression; -} - - -template <typename Scanner, typename Log> -Expression PreParser<Scanner, Log>::ParseV8Intrinsic(bool* ok) { - // CallRuntime :: - // '%' Identifier Arguments - - Expect(i::Token::MOD, CHECK_OK); - ParseIdentifier(CHECK_OK); - ParseArguments(CHECK_OK); - - return kUnknownExpression; -} - - -template <typename Scanner, typename Log> -void PreParser<Scanner, Log>::ExpectSemicolon(bool* ok) { - // Check for automatic semicolon insertion according to - // the rules given in ECMA-262, section 7.9, page 21. - i::Token::Value tok = peek(); - if (tok == i::Token::SEMICOLON) { - Next(); - return; - } - if (scanner_->has_line_terminator_before_next() || - tok == i::Token::RBRACE || - tok == i::Token::EOS) { - return; - } - Expect(i::Token::SEMICOLON, ok); -} - - -template <typename Scanner, typename Log> -Identifier PreParser<Scanner, Log>::GetIdentifierSymbol() { - const char* literal_chars = scanner_->literal_string(); - int literal_length = scanner_->literal_length(); - int identifier_pos = scanner_->location().beg_pos; - - log_->LogSymbol(identifier_pos, literal_chars, literal_length); - - return kUnknownExpression; -} - - -template <typename Scanner, typename Log> -Expression PreParser<Scanner, Log>::GetStringSymbol() { - const char* literal_chars = scanner_->literal_string(); - int literal_length = scanner_->literal_length(); - - int literal_position = scanner_->location().beg_pos; - log_->LogSymbol(literal_position, literal_chars, literal_length); - - return kUnknownExpression; -} - - -template <typename Scanner, typename Log> -Identifier PreParser<Scanner, Log>::ParseIdentifier(bool* ok) { - Expect(i::Token::IDENTIFIER, ok); - return GetIdentifierSymbol(); -} - - -template <typename Scanner, typename Log> -Identifier PreParser<Scanner, Log>::ParseIdentifierName(bool* ok) { - i::Token::Value next = Next(); - if (i::Token::IsKeyword(next)) { - int pos = scanner_->location().beg_pos; - const char* keyword = i::Token::String(next); - log_->LogSymbol(pos, keyword, strlen(keyword)); - return kUnknownExpression; - } - if (next == i::Token::IDENTIFIER) { - return GetIdentifierSymbol(); - } - *ok = false; - return kUnknownIdentifier; -} - - -// This function reads an identifier and determines whether or not it -// is 'get' or 'set'. The reason for not using ParseIdentifier and -// checking on the output is that this involves heap allocation which -// we can't do during preparsing. -template <typename Scanner, typename Log> -Identifier PreParser<Scanner, Log>::ParseIdentifierOrGetOrSet(bool* is_get, - bool* is_set, - bool* ok) { - Expect(i::Token::IDENTIFIER, CHECK_OK); - if (scanner_->literal_length() == 3) { - const char* token = scanner_->literal_string(); - *is_get = strncmp(token, "get", 3) == 0; - *is_set = !*is_get && strncmp(token, "set", 3) == 0; - } - return GetIdentifierSymbol(); -} - -#undef CHECK_OK } } // v8::preparser #endif // V8_PREPARSER_H diff --git a/src/profile-generator-inl.h b/src/profile-generator-inl.h index cdfa9e2d..8b5c1e21 100644 --- a/src/profile-generator-inl.h +++ b/src/profile-generator-inl.h @@ -105,24 +105,6 @@ void CodeMap::DeleteCode(Address addr) { } -template<class Visitor> -void HeapEntriesMap::UpdateEntries(Visitor* visitor) { - for (HashMap::Entry* p = entries_.Start(); - p != NULL; - p = entries_.Next(p)) { - if (!IsAlias(p->value)) { - EntryInfo* entry_info = reinterpret_cast<EntryInfo*>(p->value); - entry_info->entry = visitor->GetEntry( - reinterpret_cast<HeapObject*>(p->key), - entry_info->children_count, - entry_info->retainers_count); - entry_info->children_count = 0; - entry_info->retainers_count = 0; - } - } -} - - CodeEntry* ProfileGenerator::EntryForVMState(StateTag tag) { switch (tag) { case GC: @@ -139,6 +121,31 @@ CodeEntry* ProfileGenerator::EntryForVMState(StateTag tag) { } } + +inline uint64_t HeapEntry::id() { + union { + Id stored_id; + uint64_t returned_id; + } id_adaptor = {id_}; + return id_adaptor.returned_id; +} + + +template<class Visitor> +void HeapEntriesMap::UpdateEntries(Visitor* visitor) { + for (HashMap::Entry* p = entries_.Start(); + p != NULL; + p = entries_.Next(p)) { + EntryInfo* entry_info = reinterpret_cast<EntryInfo*>(p->value); + entry_info->entry = visitor->GetEntry( + reinterpret_cast<HeapObject*>(p->key), + entry_info->children_count, + entry_info->retainers_count); + entry_info->children_count = 0; + entry_info->retainers_count = 0; + } +} + } } // namespace v8::internal #endif // ENABLE_LOGGING_AND_PROFILING diff --git a/src/profile-generator.cc b/src/profile-generator.cc index 29f9ab4d..640f13cd 100644 --- a/src/profile-generator.cc +++ b/src/profile-generator.cc @@ -829,7 +829,10 @@ void ProfileGenerator::RecordTickSample(const TickSample& sample) { void HeapGraphEdge::Init( int child_index, Type type, const char* name, HeapEntry* to) { - ASSERT(type == kContextVariable || type == kProperty || type == kInternal); + ASSERT(type == kContextVariable + || type == kProperty + || type == kInternal + || type == kShortcut); child_index_ = child_index; type_ = type; name_ = name; @@ -837,14 +840,20 @@ void HeapGraphEdge::Init( } -void HeapGraphEdge::Init(int child_index, int index, HeapEntry* to) { +void HeapGraphEdge::Init(int child_index, Type type, int index, HeapEntry* to) { + ASSERT(type == kElement || type == kHidden); child_index_ = child_index; - type_ = kElement; + type_ = type; index_ = index; to_ = to; } +void HeapGraphEdge::Init(int child_index, int index, HeapEntry* to) { + Init(child_index, kElement, index, to); +} + + HeapEntry* HeapGraphEdge::From() { return reinterpret_cast<HeapEntry*>(this - child_index_) - 1; } @@ -860,12 +869,18 @@ void HeapEntry::Init(HeapSnapshot* snapshot, snapshot_ = snapshot; type_ = type; painted_ = kUnpainted; - calculated_data_index_ = kNoCalculatedData; name_ = name; - id_ = id; self_size_ = self_size; + retained_size_ = 0; children_count_ = children_count; retainers_count_ = retainers_count; + dominator_ = NULL; + + union { + uint64_t set_id; + Id stored_id; + } id_adaptor = {id}; + id_ = id_adaptor.stored_id; } @@ -879,9 +894,12 @@ void HeapEntry::SetNamedReference(HeapGraphEdge::Type type, } -void HeapEntry::SetElementReference( - int child_index, int index, HeapEntry* entry, int retainer_index) { - children_arr()[child_index].Init(child_index, index, entry); +void HeapEntry::SetIndexedReference(HeapGraphEdge::Type type, + int child_index, + int index, + HeapEntry* entry, + int retainer_index) { + children_arr()[child_index].Init(child_index, type, index, entry); entry->retainers_arr()[retainer_index] = children_arr() + child_index; } @@ -892,30 +910,16 @@ void HeapEntry::SetUnidirElementReference( } -int HeapEntry::ReachableSize() { - if (calculated_data_index_ == kNoCalculatedData) { - calculated_data_index_ = snapshot_->AddCalculatedData(); - } - return snapshot_->GetCalculatedData( - calculated_data_index_).ReachableSize(this); -} - - -int HeapEntry::RetainedSize() { - if (calculated_data_index_ == kNoCalculatedData) { - calculated_data_index_ = snapshot_->AddCalculatedData(); +int HeapEntry::RetainedSize(bool exact) { + if (exact && (retained_size_ & kExactRetainedSizeTag) == 0) { + CalculateExactRetainedSize(); } - return snapshot_->GetCalculatedData( - calculated_data_index_).RetainedSize(this); + return retained_size_ & (~kExactRetainedSizeTag); } List<HeapGraphPath*>* HeapEntry::GetRetainingPaths() { - if (calculated_data_index_ == kNoCalculatedData) { - calculated_data_index_ = snapshot_->AddCalculatedData(); - } - return snapshot_->GetCalculatedData( - calculated_data_index_).GetRetainingPaths(this); + return snapshot_->GetRetainingPaths(this); } @@ -929,6 +933,7 @@ void HeapEntry::ApplyAndPaintAllReachable(Visitor* visitor) { HeapEntry* entry = list.RemoveLast(); Vector<HeapGraphEdge> children = entry->children(); for (int i = 0; i < children.length(); ++i) { + if (children[i].type() == HeapGraphEdge::kShortcut) continue; HeapEntry* child = children[i].to(); if (!child->painted_reachable()) { list.Add(child); @@ -952,8 +957,7 @@ void HeapEntry::PaintAllReachable() { void HeapEntry::Print(int max_depth, int indent) { - OS::Print("%6d %6d %6d [%llu] ", - self_size(), ReachableSize(), RetainedSize(), id_); + OS::Print("%6d %6d [%llu] ", self_size(), RetainedSize(false), id()); if (type() != kString) { OS::Print("%s %.40s\n", TypeAsString(), name_); } else { @@ -985,6 +989,12 @@ void HeapEntry::Print(int max_depth, int indent) { case HeapGraphEdge::kProperty: OS::Print(" %*c %s: ", indent, ' ', edge.name()); break; + case HeapGraphEdge::kHidden: + OS::Print(" %*c $%d: ", indent, ' ', edge.index()); + break; + case HeapGraphEdge::kShortcut: + OS::Print(" %*c ^%s: ", indent, ' ', edge.name()); + break; default: OS::Print("!!! unknown edge type: %d ", edge.type()); } @@ -995,7 +1005,7 @@ void HeapEntry::Print(int max_depth, int indent) { const char* HeapEntry::TypeAsString() { switch (type()) { - case kInternal: return "/internal/"; + case kHidden: return "/hidden/"; case kObject: return "/object/"; case kClosure: return "/closure/"; case kString: return "/string/"; @@ -1017,44 +1027,6 @@ int HeapEntry::EntriesSize(int entries_count, } -static void DeleteHeapGraphPath(HeapGraphPath** path_ptr) { - delete *path_ptr; -} - -void HeapEntryCalculatedData::Dispose() { - if (retaining_paths_ != NULL) retaining_paths_->Iterate(DeleteHeapGraphPath); - delete retaining_paths_; -} - - -int HeapEntryCalculatedData::ReachableSize(HeapEntry* entry) { - if (reachable_size_ == kUnknownSize) CalculateSizes(entry); - return reachable_size_; -} - - -int HeapEntryCalculatedData::RetainedSize(HeapEntry* entry) { - if (retained_size_ == kUnknownSize) CalculateSizes(entry); - return retained_size_; -} - - -class ReachableSizeCalculator { - public: - ReachableSizeCalculator() - : reachable_size_(0) { - } - - int reachable_size() const { return reachable_size_; } - - void Apply(HeapEntry* entry) { - reachable_size_ += entry->self_size(); - } - - private: - int reachable_size_; -}; - class RetainedSizeCalculator { public: RetainedSizeCalculator() @@ -1073,20 +1045,17 @@ class RetainedSizeCalculator { int retained_size_; }; -void HeapEntryCalculatedData::CalculateSizes(HeapEntry* entry) { +void HeapEntry::CalculateExactRetainedSize() { // To calculate retained size, first we paint all reachable nodes in - // one color (and calculate reachable size as a byproduct), then we - // paint (or re-paint) all nodes reachable from other nodes with a - // different color. Then we consider only nodes painted with the - // first color for calculating the retained size. - entry->snapshot()->ClearPaint(); - ReachableSizeCalculator rch_size_calc; - entry->ApplyAndPaintAllReachable(&rch_size_calc); - reachable_size_ = rch_size_calc.reachable_size(); + // one color, then we paint (or re-paint) all nodes reachable from + // other nodes with a different color. Then we sum up self sizes of + // nodes painted with the first color. + snapshot()->ClearPaint(); + PaintAllReachable(); List<HeapEntry*> list(10); - HeapEntry* root = entry->snapshot()->root(); - if (entry != root) { + HeapEntry* root = snapshot()->root(); + if (this != root) { list.Add(root); root->paint_reachable_from_others(); } @@ -1094,8 +1063,9 @@ void HeapEntryCalculatedData::CalculateSizes(HeapEntry* entry) { HeapEntry* curr = list.RemoveLast(); Vector<HeapGraphEdge> children = curr->children(); for (int i = 0; i < children.length(); ++i) { + if (children[i].type() == HeapGraphEdge::kShortcut) continue; HeapEntry* child = children[i].to(); - if (child != entry && child->not_painted_reachable_from_others()) { + if (child != this && child->not_painted_reachable_from_others()) { list.Add(child); child->paint_reachable_from_others(); } @@ -1103,8 +1073,10 @@ void HeapEntryCalculatedData::CalculateSizes(HeapEntry* entry) { } RetainedSizeCalculator ret_size_calc; - entry->snapshot()->IterateEntries(&ret_size_calc); + snapshot()->IterateEntries(&ret_size_calc); retained_size_ = ret_size_calc.reained_size(); + ASSERT((retained_size_ & kExactRetainedSizeTag) == 0); + retained_size_ |= kExactRetainedSizeTag; } @@ -1142,32 +1114,28 @@ class CachedHeapGraphPath { }; -List<HeapGraphPath*>* HeapEntryCalculatedData::GetRetainingPaths( - HeapEntry* entry) { - if (retaining_paths_ == NULL) retaining_paths_ = new List<HeapGraphPath*>(4); - if (retaining_paths_->length() == 0 && entry->retainers().length() != 0) { - CachedHeapGraphPath path; - FindRetainingPaths(entry, &path); - } - return retaining_paths_; +List<HeapGraphPath*>* HeapEntry::CalculateRetainingPaths() { + List<HeapGraphPath*>* retaining_paths = new List<HeapGraphPath*>(4); + CachedHeapGraphPath path; + FindRetainingPaths(&path, retaining_paths); + return retaining_paths; } -void HeapEntryCalculatedData::FindRetainingPaths( - HeapEntry* entry, - CachedHeapGraphPath* prev_path) { - Vector<HeapGraphEdge*> retainers = entry->retainers(); - for (int i = 0; i < retainers.length(); ++i) { - HeapGraphEdge* ret_edge = retainers[i]; +void HeapEntry::FindRetainingPaths(CachedHeapGraphPath* prev_path, + List<HeapGraphPath*>* retaining_paths) { + Vector<HeapGraphEdge*> rets = retainers(); + for (int i = 0; i < rets.length(); ++i) { + HeapGraphEdge* ret_edge = rets[i]; if (prev_path->ContainsNode(ret_edge->From())) continue; - if (ret_edge->From() != entry->snapshot()->root()) { + if (ret_edge->From() != snapshot()->root()) { CachedHeapGraphPath path(*prev_path); path.Add(ret_edge); - FindRetainingPaths(ret_edge->From(), &path); + ret_edge->From()->FindRetainingPaths(&path, retaining_paths); } else { HeapGraphPath* ret_path = new HeapGraphPath(*prev_path->path()); ret_path->Set(0, ret_edge); - retaining_paths_->Add(ret_path); + retaining_paths->Add(ret_path); } } } @@ -1192,6 +1160,7 @@ void HeapGraphPath::Print() { OS::Print("[#%s] ", edge->name()); break; case HeapGraphEdge::kElement: + case HeapGraphEdge::kHidden: OS::Print("[%d] ", edge->index()); break; case HeapGraphEdge::kInternal: @@ -1200,6 +1169,9 @@ void HeapGraphPath::Print() { case HeapGraphEdge::kProperty: OS::Print("[%s] ", edge->name()); break; + case HeapGraphEdge::kShortcut: + OS::Print("[^%s] ", edge->name()); + break; default: OS::Print("!!! unknown edge type: %d ", edge->type()); } @@ -1211,6 +1183,8 @@ void HeapGraphPath::Print() { HeapObject *const HeapSnapshot::kInternalRootObject = reinterpret_cast<HeapObject*>(1); +HeapObject *const HeapSnapshot::kGcRootsObject = + reinterpret_cast<HeapObject*>(2); // It is very important to keep objects that form a heap snapshot @@ -1221,12 +1195,12 @@ template <size_t ptr_size> struct SnapshotSizeConstants; template <> struct SnapshotSizeConstants<4> { static const int kExpectedHeapGraphEdgeSize = 12; - static const int kExpectedHeapEntrySize = 32; + static const int kExpectedHeapEntrySize = 36; }; template <> struct SnapshotSizeConstants<8> { static const int kExpectedHeapGraphEdgeSize = 24; - static const int kExpectedHeapEntrySize = 40; + static const int kExpectedHeapEntrySize = 48; }; } // namespace @@ -1240,8 +1214,10 @@ HeapSnapshot::HeapSnapshot(HeapSnapshotsCollection* collection, title_(title), uid_(uid), root_entry_(NULL), + gc_roots_entry_(NULL), raw_entries_(NULL), - entries_sorted_(false) { + entries_sorted_(false), + retaining_paths_(HeapEntry::Match) { STATIC_ASSERT( sizeof(HeapGraphEdge) == SnapshotSizeConstants<sizeof(void*)>::kExpectedHeapGraphEdgeSize); // NOLINT @@ -1251,13 +1227,20 @@ HeapSnapshot::HeapSnapshot(HeapSnapshotsCollection* collection, } -static void DisposeCalculatedData(HeapEntryCalculatedData* cdata) { - cdata->Dispose(); +static void DeleteHeapGraphPath(HeapGraphPath** path_ptr) { + delete *path_ptr; } HeapSnapshot::~HeapSnapshot() { DeleteArray(raw_entries_); - calculated_data_.Iterate(DisposeCalculatedData); + for (HashMap::Entry* p = retaining_paths_.Start(); + p != NULL; + p = retaining_paths_.Next(p)) { + List<HeapGraphPath*>* list = + reinterpret_cast<List<HeapGraphPath*>*>(p->value); + list->Iterate(DeleteHeapGraphPath); + delete list; + } } @@ -1280,9 +1263,20 @@ HeapEntry* HeapSnapshot::AddEntry(HeapObject* object, if (object == kInternalRootObject) { ASSERT(root_entry_ == NULL); ASSERT(retainers_count == 0); - root_entry_ = AddEntry( - HeapEntry::kInternal, "", 0, 0, children_count, retainers_count); - return root_entry_; + return (root_entry_ = AddEntry(HeapEntry::kObject, + "", + HeapObjectsMap::kInternalRootObjectId, + 0, + children_count, + retainers_count)); + } else if (object == kGcRootsObject) { + ASSERT(gc_roots_entry_ == NULL); + return (gc_roots_entry_ = AddEntry(HeapEntry::kObject, + "(GC roots)", + HeapObjectsMap::kGcRootsObjectId, + 0, + children_count, + retainers_count)); } else if (object->IsJSFunction()) { JSFunction* func = JSFunction::cast(object); SharedFunctionInfo* shared = func->shared(); @@ -1301,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()) { @@ -1345,22 +1339,11 @@ HeapEntry* HeapSnapshot::AddEntry(HeapObject* object, children_count, retainers_count); } - // No interest in this object. - return NULL; -} - - -bool HeapSnapshot::WillAddEntry(HeapObject* object) { - return object == kInternalRootObject - || object->IsJSFunction() - || object->IsJSRegExp() - || object->IsJSObject() - || object->IsString() - || object->IsCode() - || object->IsSharedFunctionInfo() - || object->IsScript() - || object->IsFixedArray() - || object->IsHeapNumber(); + return AddEntry(object, + HeapEntry::kHidden, + "system", + children_count, + retainers_count); } @@ -1373,12 +1356,6 @@ void HeapSnapshot::ClearPaint() { } -int HeapSnapshot::AddCalculatedData() { - calculated_data_.Add(HeapEntryCalculatedData()); - return calculated_data_.length() - 1; -} - - HeapEntry* HeapSnapshot::AddEntry(HeapObject* object, HeapEntry::Type type, const char* name, @@ -1387,7 +1364,7 @@ HeapEntry* HeapSnapshot::AddEntry(HeapObject* object, return AddEntry(type, name, collection_->GetObjectId(object->address()), - GetObjectSize(object), + object->Size(), children_count, retainers_count); } @@ -1405,6 +1382,149 @@ HeapEntry* HeapSnapshot::AddEntry(HeapEntry::Type type, } +void HeapSnapshot::FillReversePostorderIndexes(Vector<HeapEntry*>* entries) { + ClearPaint(); + int current_entry = 0; + List<HeapEntry*> nodes_to_visit; + nodes_to_visit.Add(root()); + root()->paint_reachable(); + while (!nodes_to_visit.is_empty()) { + HeapEntry* entry = nodes_to_visit.last(); + Vector<HeapGraphEdge> children = entry->children(); + bool has_new_edges = false; + for (int i = 0; i < children.length(); ++i) { + if (children[i].type() == HeapGraphEdge::kShortcut) continue; + HeapEntry* child = children[i].to(); + if (!child->painted_reachable()) { + nodes_to_visit.Add(child); + child->paint_reachable(); + has_new_edges = true; + } + } + if (!has_new_edges) { + entry->set_ordered_index(current_entry); + entries->at(current_entry++) = entry; + nodes_to_visit.RemoveLast(); + } + } + entries->Truncate(current_entry); +} + + +static int Intersect(int i1, int i2, const Vector<HeapEntry*>& dominators) { + int finger1 = i1, finger2 = i2; + while (finger1 != finger2) { + while (finger1 < finger2) finger1 = dominators[finger1]->ordered_index(); + while (finger2 < finger1) finger2 = dominators[finger2]->ordered_index(); + } + return finger1; +} + +// The algorithm is based on the article: +// K. Cooper, T. Harvey and K. Kennedy "A Simple, Fast Dominance Algorithm" +// Softw. Pract. Exper. 4 (2001), pp. 1–10. +void HeapSnapshot::BuildDominatorTree(const Vector<HeapEntry*>& entries, + Vector<HeapEntry*>* dominators) { + if (entries.length() == 0) return; + const int root_index = entries.length() - 1; + for (int i = 0; i < root_index; ++i) dominators->at(i) = NULL; + dominators->at(root_index) = entries[root_index]; + bool changed = true; + while (changed) { + changed = false; + for (int i = root_index - 1; i >= 0; --i) { + HeapEntry* new_idom = NULL; + Vector<HeapGraphEdge*> rets = entries[i]->retainers(); + int j = 0; + for (; j < rets.length(); ++j) { + if (rets[j]->type() == HeapGraphEdge::kShortcut) continue; + HeapEntry* ret = rets[j]->From(); + if (dominators->at(ret->ordered_index()) != NULL) { + new_idom = ret; + break; + } + } + for (++j; j < rets.length(); ++j) { + if (rets[j]->type() == HeapGraphEdge::kShortcut) continue; + HeapEntry* ret = rets[j]->From(); + if (dominators->at(ret->ordered_index()) != NULL) { + new_idom = entries[Intersect(ret->ordered_index(), + new_idom->ordered_index(), + *dominators)]; + } + } + if (new_idom != NULL && dominators->at(i) != new_idom) { + dominators->at(i) = new_idom; + changed = true; + } + } + } +} + + +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<HeapEntry*> ordered_entries(entries_.length()); + FillReversePostorderIndexes(&ordered_entries); + ScopedVector<HeapEntry*> dominators(ordered_entries.length()); + BuildDominatorTree(ordered_entries, &dominators); + for (int i = 0; i < ordered_entries.length(); ++i) { + ASSERT(dominators[i] != NULL); + ordered_entries[i]->set_dominator(dominators[i]); + } + // For nodes unreachable from root, set dominator to itself. + SetDominatorsToSelf(); +} + + +void HeapSnapshot::ApproximateRetainedSizes() { + SetEntriesDominators(); + // As for the dominators tree we only know parent nodes, not + // children, to sum up total sizes we traverse the tree level by + // level upwards, starting from leaves. + for (int i = 0; i < entries_.length(); ++i) { + HeapEntry* entry = entries_[i]; + entry->set_retained_size(entry->self_size()); + entry->set_leaf(); + } + while (true) { + bool onlyLeaves = true; + for (int i = 0; i < entries_.length(); ++i) { + HeapEntry *entry = entries_[i], *dominator = entry->dominator(); + if (!entry->is_processed() && dominator != entry) { + dominator->set_non_leaf(); + onlyLeaves = false; + } + } + if (onlyLeaves) break; + + for (int i = 0; i < entries_.length(); ++i) { + HeapEntry *entry = entries_[i], *dominator = entry->dominator(); + if (entry->is_leaf() && dominator != entry) { + dominator->add_retained_size(entry->retained_size()); + } + } + + // Mark all current leaves as processed, reset non-leaves back to leaves. + for (int i = 0; i < entries_.length(); ++i) { + HeapEntry* entry = entries_[i]; + if (entry->is_leaf()) + entry->set_processed(); + else if (entry->is_non_leaf()) + entry->set_leaf(); + } + } +} + + HeapEntry* HeapSnapshot::GetNextEntryToInit() { if (entries_.length() > 0) { HeapEntry* last_entry = entries_.last(); @@ -1419,38 +1539,18 @@ HeapEntry* HeapSnapshot::GetNextEntryToInit() { } -int HeapSnapshot::GetObjectSize(HeapObject* obj) { - return obj->IsJSObject() ? - CalculateNetworkSize(JSObject::cast(obj)) : obj->Size(); +HeapSnapshotsDiff* HeapSnapshot::CompareWith(HeapSnapshot* snapshot) { + return collection_->CompareSnapshots(this, snapshot); } -int HeapSnapshot::CalculateNetworkSize(JSObject* obj) { - int size = obj->Size(); - // If 'properties' and 'elements' are non-empty (thus, non-shared), - // take their size into account. - if (obj->properties() != Heap::empty_fixed_array()) { - size += obj->properties()->Size(); - } - if (obj->elements() != Heap::empty_fixed_array()) { - size += obj->elements()->Size(); +List<HeapGraphPath*>* HeapSnapshot::GetRetainingPaths(HeapEntry* entry) { + HashMap::Entry* p = + retaining_paths_.Lookup(entry, HeapEntry::Hash(entry), true); + if (p->value == NULL) { + p->value = entry->CalculateRetainingPaths(); } - // For functions, also account non-empty context and literals sizes. - if (obj->IsJSFunction()) { - JSFunction* f = JSFunction::cast(obj); - if (f->unchecked_context()->IsContext()) { - size += f->context()->Size(); - } - if (f->literals()->length() != 0) { - size += f->literals()->Size(); - } - } - return size; -} - - -HeapSnapshotsDiff* HeapSnapshot::CompareWith(HeapSnapshot* snapshot) { - return collection_->CompareSnapshots(this, snapshot); + return reinterpret_cast<List<HeapGraphPath*>*>(p->value); } @@ -1475,9 +1575,14 @@ void HeapSnapshot::Print(int max_depth) { } +const uint64_t HeapObjectsMap::kInternalRootObjectId = 0; +const uint64_t HeapObjectsMap::kGcRootsObjectId = 1; +// Increase kFirstAvailableObjectId if new 'special' objects appear. +const uint64_t HeapObjectsMap::kFirstAvailableObjectId = 2; + HeapObjectsMap::HeapObjectsMap() : initial_fill_mode_(true), - next_id_(1), + next_id_(kFirstAvailableObjectId), entries_map_(AddressesMatch), entries_(new List<EntryInfo>()) { } @@ -1628,17 +1733,7 @@ HeapEntriesMap::HeapEntriesMap() HeapEntriesMap::~HeapEntriesMap() { for (HashMap::Entry* p = entries_.Start(); p != NULL; p = entries_.Next(p)) { - if (!IsAlias(p->value)) delete reinterpret_cast<EntryInfo*>(p->value); - } -} - - -void HeapEntriesMap::Alias(HeapObject* from, HeapObject* to) { - HashMap::Entry* from_cache_entry = entries_.Lookup(from, Hash(from), true); - HashMap::Entry* to_cache_entry = entries_.Lookup(to, Hash(to), false); - if (from_cache_entry->value == NULL) { - ASSERT(to_cache_entry != NULL); - from_cache_entry->value = MakeAlias(to_cache_entry->value); + delete reinterpret_cast<EntryInfo*>(p->value); } } @@ -1646,8 +1741,7 @@ void HeapEntriesMap::Alias(HeapObject* from, HeapObject* to) { HeapEntry* HeapEntriesMap::Map(HeapObject* object) { HashMap::Entry* cache_entry = entries_.Lookup(object, Hash(object), false); if (cache_entry != NULL) { - EntryInfo* entry_info = - reinterpret_cast<EntryInfo*>(Unalias(cache_entry->value)); + EntryInfo* entry_info = reinterpret_cast<EntryInfo*>(cache_entry->value); return entry_info->entry; } else { return NULL; @@ -1671,9 +1765,9 @@ void HeapEntriesMap::CountReference(HeapObject* from, HeapObject* to, ASSERT(from_cache_entry != NULL); ASSERT(to_cache_entry != NULL); EntryInfo* from_entry_info = - reinterpret_cast<EntryInfo*>(Unalias(from_cache_entry->value)); + reinterpret_cast<EntryInfo*>(from_cache_entry->value); EntryInfo* to_entry_info = - reinterpret_cast<EntryInfo*>(Unalias(to_cache_entry->value)); + reinterpret_cast<EntryInfo*>(to_cache_entry->value); if (prev_children_count) *prev_children_count = from_entry_info->children_count; if (prev_retainers_count) @@ -1685,6 +1779,36 @@ void HeapEntriesMap::CountReference(HeapObject* from, HeapObject* to, } +HeapObjectsSet::HeapObjectsSet() + : entries_(HeapEntriesMap::HeapObjectsMatch) { +} + + +void HeapObjectsSet::Clear() { + entries_.Clear(); +} + + +bool HeapObjectsSet::Contains(Object* obj) { + if (!obj->IsHeapObject()) return false; + HeapObject* object = HeapObject::cast(obj); + HashMap::Entry* cache_entry = + entries_.Lookup(object, HeapEntriesMap::Hash(object), false); + return cache_entry != NULL; +} + + +void HeapObjectsSet::Insert(Object* obj) { + if (!obj->IsHeapObject()) return; + HeapObject* object = HeapObject::cast(obj); + HashMap::Entry* cache_entry = + entries_.Lookup(object, HeapEntriesMap::Hash(object), true); + if (cache_entry->value == NULL) { + cache_entry->value = HeapEntriesMap::kHeapEntryPlaceholder; + } +} + + HeapSnapshotGenerator::HeapSnapshotGenerator(HeapSnapshot* snapshot) : snapshot_(snapshot), collection_(snapshot->collection()), @@ -1699,7 +1823,8 @@ class SnapshotCounter : public HeapSnapshotGenerator::SnapshotFillerInterface { entries_->Pair(obj, HeapEntriesMap::kHeapEntryPlaceholder); return HeapEntriesMap::kHeapEntryPlaceholder; } - void SetElementReference(HeapObject* parent_obj, + void SetIndexedReference(HeapGraphEdge::Type, + HeapObject* parent_obj, HeapEntry*, int, Object* child_obj, @@ -1714,10 +1839,18 @@ class SnapshotCounter : public HeapSnapshotGenerator::SnapshotFillerInterface { HeapEntry*) { entries_->CountReference(parent_obj, HeapObject::cast(child_obj)); } - void SetRootReference(Object* child_obj, HeapEntry*) { + void SetRootShortcutReference(Object* child_obj, HeapEntry*) { entries_->CountReference( HeapSnapshot::kInternalRootObject, HeapObject::cast(child_obj)); } + void SetRootGcRootsReference() { + entries_->CountReference( + HeapSnapshot::kInternalRootObject, HeapSnapshot::kGcRootsObject); + } + void SetStrongRootReference(Object* child_obj, HeapEntry*) { + entries_->CountReference( + HeapSnapshot::kGcRootsObject, HeapObject::cast(child_obj)); + } private: HeapEntriesMap* entries_; }; @@ -1733,16 +1866,19 @@ class SnapshotFiller : public HeapSnapshotGenerator::SnapshotFillerInterface { UNREACHABLE(); return NULL; } - void SetElementReference(HeapObject* parent_obj, + void SetIndexedReference(HeapGraphEdge::Type type, + HeapObject* parent_obj, HeapEntry* parent_entry, int index, Object* child_obj, HeapEntry* child_entry) { int child_index, retainer_index; - entries_->CountReference(parent_obj, HeapObject::cast(child_obj), - &child_index, &retainer_index); - parent_entry->SetElementReference( - child_index, index, child_entry, retainer_index); + entries_->CountReference(parent_obj, + HeapObject::cast(child_obj), + &child_index, + &retainer_index); + parent_entry->SetIndexedReference( + type, child_index, index, child_entry, retainer_index); } void SetNamedReference(HeapGraphEdge::Type type, HeapObject* parent_obj, @@ -1759,13 +1895,43 @@ class SnapshotFiller : public HeapSnapshotGenerator::SnapshotFillerInterface { child_entry, retainer_index); } - void SetRootReference(Object* child_obj, HeapEntry* child_entry) { + void SetRootGcRootsReference() { int child_index, retainer_index; - entries_->CountReference( - HeapSnapshot::kInternalRootObject, HeapObject::cast(child_obj), - &child_index, &retainer_index); - snapshot_->root()->SetElementReference( - child_index, child_index + 1, child_entry, retainer_index); + entries_->CountReference(HeapSnapshot::kInternalRootObject, + HeapSnapshot::kGcRootsObject, + &child_index, + &retainer_index); + snapshot_->root()->SetIndexedReference(HeapGraphEdge::kElement, + child_index, + child_index + 1, + snapshot_->gc_roots(), + retainer_index); + } + void SetRootShortcutReference(Object* child_obj, + HeapEntry* child_entry) { + int child_index, retainer_index; + entries_->CountReference(HeapSnapshot::kInternalRootObject, + HeapObject::cast(child_obj), + &child_index, + &retainer_index); + snapshot_->root()->SetNamedReference(HeapGraphEdge::kShortcut, + child_index, + collection_->GetName(child_index + 1), + child_entry, + retainer_index); + } + void SetStrongRootReference(Object* child_obj, + HeapEntry* child_entry) { + int child_index, retainer_index; + entries_->CountReference(HeapSnapshot::kGcRootsObject, + HeapObject::cast(child_obj), + &child_index, + &retainer_index); + snapshot_->gc_roots()->SetIndexedReference(HeapGraphEdge::kElement, + child_index, + child_index + 1, + child_entry, + retainer_index); } private: HeapSnapshot* snapshot_; @@ -1788,6 +1954,19 @@ class SnapshotAllocator { HeapSnapshot* snapshot_; }; +class RootsReferencesExtractor : public ObjectVisitor { + public: + explicit RootsReferencesExtractor(HeapSnapshotGenerator* generator) + : generator_(generator) { + } + void VisitPointers(Object** start, Object** end) { + for (Object** p = start; p < end; p++) generator_->SetGcRootsReference(*p); + } + private: + HeapSnapshotGenerator* generator_; +}; + + void HeapSnapshotGenerator::GenerateSnapshot() { AssertNoAllocation no_alloc; @@ -1795,12 +1974,14 @@ void HeapSnapshotGenerator::GenerateSnapshot() { SnapshotCounter counter(&entries_); filler_ = &counter; filler_->AddEntry(HeapSnapshot::kInternalRootObject); - HeapIterator iterator1; - for (HeapObject* obj = iterator1.next(); - obj != NULL; - obj = iterator1.next()) { + filler_->AddEntry(HeapSnapshot::kGcRootsObject); + HeapIterator iterator(HeapIterator::kPreciseFiltering); + for (HeapObject* obj = iterator.next(); obj != NULL; obj = iterator.next()) { ExtractReferences(obj); } + SetRootGcRootsReference(); + RootsReferencesExtractor extractor(this); + Heap::IterateRoots(&extractor, VISIT_ONLY_STRONG); // Allocate and fill entries in the snapshot, allocate references. snapshot_->AllocateEntries(entries_.entries_count(), @@ -1812,12 +1993,14 @@ void HeapSnapshotGenerator::GenerateSnapshot() { // Pass 2. Fill references. SnapshotFiller filler(snapshot_, &entries_); filler_ = &filler; - HeapIterator iterator2; - for (HeapObject* obj = iterator2.next(); - obj != NULL; - obj = iterator2.next()) { + iterator.reset(); + for (HeapObject* obj = iterator.next(); obj != NULL; obj = iterator.next()) { ExtractReferences(obj); } + SetRootGcRootsReference(); + Heap::IterateRoots(&extractor, VISIT_ONLY_STRONG); + + snapshot_->ApproximateRetainedSizes(); } @@ -1825,25 +2008,8 @@ HeapEntry* HeapSnapshotGenerator::GetEntry(Object* obj) { if (!obj->IsHeapObject()) return NULL; HeapObject* object = HeapObject::cast(obj); HeapEntry* entry = entries_.Map(object); - // A new entry. - if (entry == NULL) { - if (obj->IsJSGlobalPropertyCell()) { - Object* cell_target = JSGlobalPropertyCell::cast(obj)->value(); - entry = GetEntry(cell_target); - // If GPC references an object that we have interest in (see - // HeapSnapshot::AddEntry, WillAddEntry), add the object. We - // don't store HeapEntries for GPCs. Instead, we make our hash - // map to point to object's HeapEntry by GPCs address. - if (entry != NULL) { - entries_.Alias(object, HeapObject::cast(cell_target)); - } - return entry; - } - - if (snapshot_->WillAddEntry(object)) entry = filler_->AddEntry(object); - } - + if (entry == NULL) entry = filler_->AddEntry(object); return entry; } @@ -1852,43 +2018,44 @@ class IndexedReferencesExtractor : public ObjectVisitor { public: IndexedReferencesExtractor(HeapSnapshotGenerator* generator, HeapObject* parent_obj, - HeapEntry* parent_entry) + HeapEntry* parent_entry, + HeapObjectsSet* known_references = NULL) : generator_(generator), parent_obj_(parent_obj), parent_(parent_entry), + known_references_(known_references), next_index_(1) { } - - void VisitPointer(Object** o) { - generator_->SetElementReference(parent_obj_, parent_, next_index_++, *o); - } - void VisitPointers(Object** start, Object** end) { - for (Object** p = start; p < end; p++) VisitPointer(p); + for (Object** p = start; p < end; p++) { + if (!known_references_ || !known_references_->Contains(*p)) { + generator_->SetHiddenReference(parent_obj_, parent_, next_index_++, *p); + } + } } - private: HeapSnapshotGenerator* generator_; HeapObject* parent_obj_; HeapEntry* parent_; + HeapObjectsSet* known_references_; int next_index_; }; void HeapSnapshotGenerator::ExtractReferences(HeapObject* obj) { - // We need to reference JS global objects from snapshot's root. - // We use JSGlobalProxy because this is what embedder (e.g. browser) - // uses for the global object. - if (obj->IsJSGlobalProxy()) { - JSGlobalProxy* proxy = JSGlobalProxy::cast(obj); - SetRootReference(proxy->map()->prototype()); - return; - } - HeapEntry* entry = GetEntry(obj); if (entry == NULL) return; // No interest in this object. - if (obj->IsJSObject()) { + known_references_.Clear(); + if (obj->IsJSGlobalProxy()) { + // We need to reference JS global objects from snapshot's root. + // We use JSGlobalProxy because this is what embedder (e.g. browser) + // uses for the global object. + JSGlobalProxy* proxy = JSGlobalProxy::cast(obj); + SetRootShortcutReference(proxy->map()->prototype()); + IndexedReferencesExtractor refs_extractor(this, obj, entry); + obj->Iterate(&refs_extractor); + } else if (obj->IsJSObject()) { JSObject* js_obj = JSObject::cast(obj); ExtractClosureReferences(js_obj, entry); ExtractPropertyReferences(js_obj, entry); @@ -1903,16 +2070,16 @@ void HeapSnapshotGenerator::ExtractReferences(HeapObject* obj) { obj, entry, Heap::prototype_symbol(), js_fun->prototype()); } } + IndexedReferencesExtractor refs_extractor( + this, obj, entry, &known_references_); + obj->Iterate(&refs_extractor); } else if (obj->IsString()) { if (obj->IsConsString()) { ConsString* cs = ConsString::cast(obj); - SetInternalReference(obj, entry, "1", cs->first()); - SetInternalReference(obj, entry, "2", cs->second()); + SetInternalReference(obj, entry, 1, cs->first()); + SetInternalReference(obj, entry, 2, cs->second()); } - } else if (obj->IsCode() || obj->IsSharedFunctionInfo() || obj->IsScript()) { - IndexedReferencesExtractor refs_extractor(this, obj, entry); - obj->Iterate(&refs_extractor); - } else if (obj->IsFixedArray()) { + } else { IndexedReferencesExtractor refs_extractor(this, obj, entry); obj->Iterate(&refs_extractor); } @@ -1967,8 +2134,17 @@ void HeapSnapshotGenerator::ExtractPropertyReferences(JSObject* js_obj, for (int i = 0; i < length; ++i) { Object* k = dictionary->KeyAt(i); if (dictionary->IsKey(k)) { + Object* target = dictionary->ValueAt(i); SetPropertyReference( - js_obj, entry, String::cast(k), dictionary->ValueAt(i)); + js_obj, entry, String::cast(k), target); + // We assume that global objects can only have slow properties. + if (target->IsJSGlobalPropertyCell()) { + SetPropertyShortcutReference(js_obj, + entry, + String::cast(k), + JSGlobalPropertyCell::cast( + target)->value()); + } } } } @@ -2024,6 +2200,7 @@ void HeapSnapshotGenerator::SetClosureReference(HeapObject* parent_obj, collection_->GetName(reference_name), child_obj, child_entry); + known_references_.Insert(child_obj); } } @@ -2034,8 +2211,13 @@ void HeapSnapshotGenerator::SetElementReference(HeapObject* parent_obj, Object* child_obj) { HeapEntry* child_entry = GetEntry(child_obj); if (child_entry != NULL) { - filler_->SetElementReference( - parent_obj, parent_entry, index, child_obj, child_entry); + filler_->SetIndexedReference(HeapGraphEdge::kElement, + parent_obj, + parent_entry, + index, + child_obj, + child_entry); + known_references_.Insert(child_obj); } } @@ -2052,6 +2234,7 @@ void HeapSnapshotGenerator::SetInternalReference(HeapObject* parent_obj, reference_name, child_obj, child_entry); + known_references_.Insert(child_obj); } } @@ -2068,6 +2251,23 @@ void HeapSnapshotGenerator::SetInternalReference(HeapObject* parent_obj, collection_->GetName(index), child_obj, child_entry); + known_references_.Insert(child_obj); + } +} + + +void HeapSnapshotGenerator::SetHiddenReference(HeapObject* parent_obj, + HeapEntry* parent_entry, + int index, + Object* child_obj) { + HeapEntry* child_entry = GetEntry(child_obj); + if (child_entry != NULL) { + filler_->SetIndexedReference(HeapGraphEdge::kHidden, + parent_obj, + parent_entry, + index, + child_obj, + child_entry); } } @@ -2086,14 +2286,45 @@ void HeapSnapshotGenerator::SetPropertyReference(HeapObject* parent_obj, collection_->GetName(reference_name), child_obj, child_entry); + known_references_.Insert(child_obj); + } +} + + +void HeapSnapshotGenerator::SetPropertyShortcutReference( + HeapObject* parent_obj, + HeapEntry* parent_entry, + String* reference_name, + Object* child_obj) { + HeapEntry* child_entry = GetEntry(child_obj); + if (child_entry != NULL) { + filler_->SetNamedReference(HeapGraphEdge::kShortcut, + parent_obj, + parent_entry, + collection_->GetName(reference_name), + child_obj, + child_entry); } } -void HeapSnapshotGenerator::SetRootReference(Object* child_obj) { +void HeapSnapshotGenerator::SetRootGcRootsReference() { + filler_->SetRootGcRootsReference(); +} + + +void HeapSnapshotGenerator::SetRootShortcutReference(Object* child_obj) { HeapEntry* child_entry = GetEntry(child_obj); ASSERT(child_entry != NULL); - filler_->SetRootReference(child_obj, child_entry); + filler_->SetRootShortcutReference(child_obj, child_entry); +} + + +void HeapSnapshotGenerator::SetGcRootsReference(Object* child_obj) { + HeapEntry* child_entry = GetEntry(child_obj); + if (child_entry != NULL) { + filler_->SetStrongRootReference(child_obj, child_entry); + } } @@ -2101,11 +2332,11 @@ void HeapSnapshotsDiff::CreateRoots(int additions_count, int deletions_count) { raw_additions_root_ = NewArray<char>(HeapEntry::EntriesSize(1, additions_count, 0)); additions_root()->Init( - snapshot2_, HeapEntry::kInternal, "", 0, 0, additions_count, 0); + snapshot2_, HeapEntry::kHidden, "", 0, 0, additions_count, 0); raw_deletions_root_ = NewArray<char>(HeapEntry::EntriesSize(1, deletions_count, 0)); deletions_root()->Init( - snapshot1_, HeapEntry::kInternal, "", 0, 0, deletions_count, 0); + snapshot1_, HeapEntry::kHidden, "", 0, 0, deletions_count, 0); } @@ -2324,7 +2555,8 @@ void HeapSnapshotJSONSerializer::SerializeEdge(HeapGraphEdge* edge) { writer_->AddCharacter(','); writer_->AddNumber(edge->type()); writer_->AddCharacter(','); - if (edge->type() == HeapGraphEdge::kElement) { + if (edge->type() == HeapGraphEdge::kElement + || edge->type() == HeapGraphEdge::kHidden) { writer_->AddNumber(edge->index()); } else { writer_->AddNumber(GetStringId(edge->name())); @@ -2344,6 +2576,10 @@ void HeapSnapshotJSONSerializer::SerializeNode(HeapEntry* entry) { writer_->AddNumber(entry->id()); writer_->AddCharacter(','); writer_->AddNumber(entry->self_size()); + writer_->AddCharacter(','); + writer_->AddNumber(entry->RetainedSize(false)); + writer_->AddCharacter(','); + writer_->AddNumber(GetNodeId(entry->dominator())); Vector<HeapGraphEdge> children = entry->children(); writer_->AddCharacter(','); writer_->AddNumber(children.length()); @@ -2355,23 +2591,25 @@ void HeapSnapshotJSONSerializer::SerializeNode(HeapEntry* entry) { void HeapSnapshotJSONSerializer::SerializeNodes() { - // The first (zero) item of nodes array is a JSON-ified object - // describing node serialization layout. - // We use a set of macros to improve readability. + // The first (zero) item of nodes array is an object describing node + // serialization layout. We use a set of macros to improve + // readability. #define JSON_A(s) "["s"]" #define JSON_O(s) "{"s"}" -#define JSON_S(s) "\\\""s"\\\"" - writer_->AddString("\"" JSON_O( +#define JSON_S(s) "\""s"\"" + writer_->AddString(JSON_O( JSON_S("fields") ":" JSON_A( JSON_S("type") "," JSON_S("name") "," JSON_S("id") "," JSON_S("self_size") + "," JSON_S("retained_size") + "," JSON_S("dominator") "," JSON_S("children_count") "," JSON_S("children")) "," JSON_S("types") ":" JSON_A( JSON_A( - JSON_S("internal") + JSON_S("hidden") "," JSON_S("array") "," JSON_S("string") "," JSON_S("object") @@ -2383,6 +2621,8 @@ void HeapSnapshotJSONSerializer::SerializeNodes() { "," JSON_S("number") "," JSON_S("number") "," JSON_S("number") + "," JSON_S("number") + "," JSON_S("number") "," JSON_O( JSON_S("fields") ":" JSON_A( JSON_S("type") @@ -2393,14 +2633,17 @@ void HeapSnapshotJSONSerializer::SerializeNodes() { JSON_S("context") "," JSON_S("element") "," JSON_S("property") - "," JSON_S("internal")) + "," JSON_S("internal") + "," JSON_S("hidden") + "," JSON_S("shortcut")) "," JSON_S("string_or_number") - "," JSON_S("node"))))) "\""); + "," JSON_S("node")))))); #undef JSON_S #undef JSON_O #undef JSON_A - const int node_fields_count = 5; // type,name,id,self_size,children_count. + const int node_fields_count = 7; + // type,name,id,self_size,retained_size,dominator,children_count. const int edge_fields_count = 3; // type,name|index,to_node. List<HashMap::Entry*> sorted_nodes; SortHashMap(&nodes_, &sorted_nodes); @@ -2526,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/src/profile-generator.h b/src/profile-generator.h index b691a056..55c57fd5 100644 --- a/src/profile-generator.h +++ b/src/profile-generator.h @@ -439,22 +439,26 @@ class HeapGraphEdge BASE_EMBEDDED { kContextVariable = v8::HeapGraphEdge::kContextVariable, kElement = v8::HeapGraphEdge::kElement, kProperty = v8::HeapGraphEdge::kProperty, - kInternal = v8::HeapGraphEdge::kInternal + kInternal = v8::HeapGraphEdge::kInternal, + kHidden = v8::HeapGraphEdge::kHidden, + kShortcut = v8::HeapGraphEdge::kShortcut }; HeapGraphEdge() { } void Init(int child_index, Type type, const char* name, HeapEntry* to); + void Init(int child_index, Type type, int index, HeapEntry* to); void Init(int child_index, int index, HeapEntry* to); Type type() { return static_cast<Type>(type_); } int index() { - ASSERT(type_ == kElement); + ASSERT(type_ == kElement || type_ == kHidden); return index_; } const char* name() { ASSERT(type_ == kContextVariable || type_ == kProperty - || type_ == kInternal); + || type_ == kInternal + || type_ == kShortcut); return name_; } HeapEntry* to() { return to_; } @@ -462,8 +466,8 @@ class HeapGraphEdge BASE_EMBEDDED { HeapEntry* From(); private: - int child_index_ : 30; - unsigned type_ : 2; + int child_index_ : 29; + unsigned type_ : 3; union { int index_; const char* name_; @@ -500,7 +504,7 @@ class HeapSnapshot; class HeapEntry BASE_EMBEDDED { public: enum Type { - kInternal = v8::HeapGraphNode::kInternal, + kHidden = v8::HeapGraphNode::kHidden, kArray = v8::HeapGraphNode::kArray, kString = v8::HeapGraphNode::kString, kObject = v8::HeapGraphNode::kObject, @@ -522,14 +526,21 @@ class HeapEntry BASE_EMBEDDED { HeapSnapshot* snapshot() { return snapshot_; } Type type() { return static_cast<Type>(type_); } const char* name() { return name_; } - uint64_t id() { return id_; } + uint64_t id(); int self_size() { return self_size_; } + int retained_size() { return retained_size_; } + void add_retained_size(int size) { retained_size_ += size; } + void set_retained_size(int value) { retained_size_ = value; } + int ordered_index() { return ordered_index_; } + void set_ordered_index(int value) { ordered_index_ = value; } Vector<HeapGraphEdge> children() { return Vector<HeapGraphEdge>(children_arr(), children_count_); } Vector<HeapGraphEdge*> retainers() { return Vector<HeapGraphEdge*>(retainers_arr(), retainers_count_); } List<HeapGraphPath*>* GetRetainingPaths(); + HeapEntry* dominator() { return dominator_; } + void set_dominator(HeapEntry* entry) { dominator_ = entry; } void clear_paint() { painted_ = kUnpainted; } bool painted_reachable() { return painted_ == kPainted; } @@ -547,8 +558,18 @@ class HeapEntry BASE_EMBEDDED { void ApplyAndPaintAllReachable(Visitor* visitor); void PaintAllReachable(); - void SetElementReference( - int child_index, int index, HeapEntry* entry, int retainer_index); + bool is_leaf() { return painted_ == kLeaf; } + void set_leaf() { painted_ = kLeaf; } + bool is_non_leaf() { return painted_ == kNonLeaf; } + void set_non_leaf() { painted_ = kNonLeaf; } + bool is_processed() { return painted_ == kProcessed; } + void set_processed() { painted_ = kProcessed; } + + void SetIndexedReference(HeapGraphEdge::Type type, + int child_index, + int index, + HeapEntry* entry, + int retainer_index); void SetNamedReference(HeapGraphEdge::Type type, int child_index, const char* name, @@ -557,14 +578,19 @@ class HeapEntry BASE_EMBEDDED { void SetUnidirElementReference(int child_index, int index, HeapEntry* entry); int EntrySize() { return EntriesSize(1, children_count_, retainers_count_); } - int ReachableSize(); - int RetainedSize(); + int RetainedSize(bool exact); + List<HeapGraphPath*>* CalculateRetainingPaths(); void Print(int max_depth, int indent); static int EntriesSize(int entries_count, int children_count, int retainers_count); + static uint32_t Hash(HeapEntry* entry) { + return ComputeIntegerHash( + static_cast<uint32_t>(reinterpret_cast<uintptr_t>(entry))); + } + static bool Match(void* entry1, void* entry2) { return entry1 == entry2; } private: HeapGraphEdge* children_arr() { @@ -573,53 +599,40 @@ class HeapEntry BASE_EMBEDDED { HeapGraphEdge** retainers_arr() { return reinterpret_cast<HeapGraphEdge**>(children_arr() + children_count_); } + void CalculateExactRetainedSize(); + void FindRetainingPaths(CachedHeapGraphPath* prev_path, + List<HeapGraphPath*>* retaining_paths); const char* TypeAsString(); unsigned painted_: 2; unsigned type_: 3; - // The calculated data is stored in HeapSnapshot in HeapEntryCalculatedData - // entries. See AddCalculatedData and GetCalculatedData. - int calculated_data_index_: 27; - int self_size_; - int children_count_; + int children_count_: 27; int retainers_count_; + int self_size_; + union { + int ordered_index_; // Used during dominator tree building. + int retained_size_; // At that moment, there is no retained size yet. + }; + HeapEntry* dominator_; HeapSnapshot* snapshot_; + struct Id { + uint32_t id1_; + uint32_t id2_; + } id_; // This is to avoid extra padding of 64-bit value. const char* name_; - uint64_t id_; + // Paints used for exact retained sizes calculation. static const unsigned kUnpainted = 0; static const unsigned kPainted = 1; static const unsigned kPaintedReachableFromOthers = 2; - static const int kNoCalculatedData = -1; - - DISALLOW_COPY_AND_ASSIGN(HeapEntry); -}; - - -class HeapEntryCalculatedData { - public: - HeapEntryCalculatedData() - : retaining_paths_(NULL), - reachable_size_(kUnknownSize), - retained_size_(kUnknownSize) { - } - void Dispose(); - - List<HeapGraphPath*>* GetRetainingPaths(HeapEntry* entry); - int ReachableSize(HeapEntry* entry); - int RetainedSize(HeapEntry* entry); - - private: - void CalculateSizes(HeapEntry* entry); - void FindRetainingPaths(HeapEntry* entry, CachedHeapGraphPath* prev_path); + // Paints used for approximate retained sizes calculation. + static const unsigned kLeaf = 0; + static const unsigned kNonLeaf = 1; + static const unsigned kProcessed = 2; - List<HeapGraphPath*>* retaining_paths_; - int reachable_size_; - int retained_size_; + static const int kExactRetainedSizeTag = 1; - static const int kUnknownSize = -1; - - // Allow generated copy constructor and assignment operator. + DISALLOW_COPY_AND_ASSIGN(HeapEntry); }; @@ -668,32 +681,32 @@ class HeapSnapshot { const char* title() { return title_; } unsigned uid() { return uid_; } HeapEntry* root() { return root_entry_; } + HeapEntry* gc_roots() { return gc_roots_entry_; } void AllocateEntries( int entries_count, int children_count, int retainers_count); HeapEntry* AddEntry( HeapObject* object, int children_count, int retainers_count); - bool WillAddEntry(HeapObject* object); HeapEntry* AddEntry(HeapEntry::Type type, const char* name, uint64_t id, int size, int children_count, int retainers_count); - int AddCalculatedData(); - HeapEntryCalculatedData& GetCalculatedData(int index) { - return calculated_data_[index]; - } + void ApproximateRetainedSizes(); void ClearPaint(); HeapSnapshotsDiff* CompareWith(HeapSnapshot* snapshot); + List<HeapGraphPath*>* GetRetainingPaths(HeapEntry* entry); List<HeapEntry*>* GetSortedEntriesList(); template<class Visitor> void IterateEntries(Visitor* visitor) { entries_.Iterate(visitor); } + void SetDominatorsToSelf(); void Print(int max_depth); void PrintEntriesSize(); - static HeapObject *const kInternalRootObject; + static HeapObject* const kInternalRootObject; + static HeapObject* const kGcRootsObject; private: HeapEntry* AddEntry(HeapObject* object, @@ -702,18 +715,21 @@ class HeapSnapshot { int children_count, int retainers_count); HeapEntry* GetNextEntryToInit(); - static int GetObjectSize(HeapObject* obj); - static int CalculateNetworkSize(JSObject* obj); + void BuildDominatorTree(const Vector<HeapEntry*>& entries, + Vector<HeapEntry*>* dominators); + void FillReversePostorderIndexes(Vector<HeapEntry*>* entries); + void SetEntriesDominators(); HeapSnapshotsCollection* collection_; Type type_; const char* title_; unsigned uid_; HeapEntry* root_entry_; + HeapEntry* gc_roots_entry_; char* raw_entries_; List<HeapEntry*> entries_; bool entries_sorted_; - List<HeapEntryCalculatedData> calculated_data_; + HashMap retaining_paths_; #ifdef DEBUG int raw_entries_size_; #endif @@ -733,6 +749,10 @@ class HeapObjectsMap { uint64_t FindObject(Address addr); void MoveObject(Address from, Address to); + static const uint64_t kInternalRootObjectId; + static const uint64_t kGcRootsObjectId; + static const uint64_t kFirstAvailableObjectId; + private: struct EntryInfo { explicit EntryInfo(uint64_t id) : id(id), accessed(true) { } @@ -868,9 +888,6 @@ class HeapEntriesMap { HeapEntriesMap(); ~HeapEntriesMap(); - // Aliasing is used for skipping intermediate proxy objects, like - // JSGlobalPropertyCell. - void Alias(HeapObject* from, HeapObject* to); HeapEntry* Map(HeapObject* object); void Pair(HeapObject* object, HeapEntry* entry); void CountReference(HeapObject* from, HeapObject* to, @@ -894,41 +911,45 @@ class HeapEntriesMap { int retainers_count; }; - uint32_t Hash(HeapObject* object) { + static uint32_t Hash(HeapObject* object) { return ComputeIntegerHash( static_cast<uint32_t>(reinterpret_cast<uintptr_t>(object))); } static bool HeapObjectsMatch(void* key1, void* key2) { return key1 == key2; } - bool IsAlias(void* ptr) { - return reinterpret_cast<intptr_t>(ptr) & kAliasTag; - } - void* MakeAlias(void* ptr) { - return reinterpret_cast<void*>(reinterpret_cast<intptr_t>(ptr) | kAliasTag); - } - void* Unalias(void* ptr) { - return reinterpret_cast<void*>( - reinterpret_cast<intptr_t>(ptr) & (~kAliasTag)); - } - HashMap entries_; int entries_count_; int total_children_count_; int total_retainers_count_; - static const intptr_t kAliasTag = 1; + friend class HeapObjectsSet; DISALLOW_COPY_AND_ASSIGN(HeapEntriesMap); }; +class HeapObjectsSet { + public: + HeapObjectsSet(); + void Clear(); + bool Contains(Object* object); + void Insert(Object* obj); + + private: + HashMap entries_; + + DISALLOW_COPY_AND_ASSIGN(HeapObjectsSet); +}; + + class HeapSnapshotGenerator { public: class SnapshotFillerInterface { public: virtual ~SnapshotFillerInterface() { } virtual HeapEntry* AddEntry(HeapObject* obj) = 0; - virtual void SetElementReference(HeapObject* parent_obj, + virtual void SetIndexedReference(HeapGraphEdge::Type type, + HeapObject* parent_obj, HeapEntry* parent_entry, int index, Object* child_obj, @@ -939,8 +960,11 @@ class HeapSnapshotGenerator { const char* reference_name, Object* child_obj, HeapEntry* child_entry) = 0; - virtual void SetRootReference(Object* child_obj, - HeapEntry* child_entry) = 0; + virtual void SetRootGcRootsReference() = 0; + virtual void SetRootShortcutReference(Object* child_obj, + HeapEntry* child_entry) = 0; + virtual void SetStrongRootReference(Object* child_obj, + HeapEntry* child_entry) = 0; }; explicit HeapSnapshotGenerator(HeapSnapshot* snapshot); @@ -969,19 +993,33 @@ class HeapSnapshotGenerator { HeapEntry* parent, int index, Object* child); + void SetHiddenReference(HeapObject* parent_obj, + HeapEntry* parent, + int index, + Object* child); void SetPropertyReference(HeapObject* parent_obj, HeapEntry* parent, String* reference_name, Object* child); - void SetRootReference(Object* child); + void SetPropertyShortcutReference(HeapObject* parent_obj, + HeapEntry* parent, + String* reference_name, + Object* child); + void SetRootShortcutReference(Object* child); + void SetRootGcRootsReference(); + void SetGcRootsReference(Object* child); HeapSnapshot* snapshot_; HeapSnapshotsCollection* collection_; // Mapping from HeapObject* pointers to HeapEntry* pointers. HeapEntriesMap entries_; SnapshotFillerInterface* filler_; + // Used during references extraction to mark heap objects that + // are references via non-hidden properties. + HeapObjectsSet known_references_; friend class IndexedReferencesExtractor; + friend class RootsReferencesExtractor; DISALLOW_COPY_AND_ASSIGN(HeapSnapshotGenerator); }; @@ -1035,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/src/regexp.js b/src/regexp.js index 9e708fd0..d01d04f2 100644 --- a/src/regexp.js +++ b/src/regexp.js @@ -193,20 +193,16 @@ function RegExpExec(string) { var matchIndices = %_RegExpExec(this, s, i, lastMatchInfo); if (matchIndices === null) { - if (global) { - this.lastIndex = 0; - } + if (global) this.lastIndex = 0; return null; } // Successful match. lastMatchInfoOverride = null; - var result = BuildResultFromMatchInfo(matchIndices, s); - if (global) { this.lastIndex = lastMatchInfo[CAPTURE1]; } - return result; + return BuildResultFromMatchInfo(matchIndices, s); } @@ -244,43 +240,44 @@ function RegExpTest(string) { // algorithm, step 5) even if the value is discarded for non-global RegExps. var i = TO_INTEGER(lastIndex); - var global = this.global; - if (global) { + if (this.global) { if (i < 0 || i > s.length) { this.lastIndex = 0; return false; } - } else { - i = 0; - } - - // Remove irrelevant preceeding '.*' in a test regexp. The expression - // checks whether this.source starts with '.*' and that the third - // char is not a '?' - if (%_StringCharCodeAt(this.source, 0) == 46 && // '.' - %_StringCharCodeAt(this.source, 1) == 42 && // '*' - %_StringCharCodeAt(this.source, 2) != 63) { // '?' - if (!%_ObjectEquals(regexp_key, this)) { - regexp_key = this; - regexp_val = new $RegExp(this.source.substring(2, this.source.length), - (global ? 'g' : '') - + (this.ignoreCase ? 'i' : '') - + (this.multiline ? 'm' : '')); + %_Log('regexp', 'regexp-exec,%0r,%1S,%2i', [this, s, lastIndex]); + // matchIndices is either null or the lastMatchInfo array. + var matchIndices = %_RegExpExec(this, s, i, lastMatchInfo); + if (matchIndices === null) { + this.lastIndex = 0; + return false; } - if (!regexp_val.test(s)) return false; - } - - %_Log('regexp', 'regexp-exec,%0r,%1S,%2i', [this, s, lastIndex]); - // matchIndices is either null or the lastMatchInfo array. - var matchIndices = %_RegExpExec(this, s, i, lastMatchInfo); - - if (matchIndices === null) { - if (global) this.lastIndex = 0; - return false; + lastMatchInfoOverride = null; + this.lastIndex = lastMatchInfo[CAPTURE1]; + return true; + } else { + // Non-global regexp. + // Remove irrelevant preceeding '.*' in a non-global test regexp. + // The expression checks whether this.source starts with '.*' and + // that the third char is not a '?'. + if (%_StringCharCodeAt(this.source, 0) == 46 && // '.' + %_StringCharCodeAt(this.source, 1) == 42 && // '*' + %_StringCharCodeAt(this.source, 2) != 63) { // '?' + if (!%_ObjectEquals(regexp_key, this)) { + regexp_key = this; + regexp_val = new $RegExp(this.source.substring(2, this.source.length), + (this.ignoreCase ? 'i' : '') + + (this.multiline ? 'm' : '')); + } + if (!regexp_val.test(s)) return false; + } + %_Log('regexp', 'regexp-exec,%0r,%1S,%2i', [this, s, lastIndex]); + // matchIndices is either null or the lastMatchInfo array. + var matchIndices = %_RegExpExec(this, s, 0, lastMatchInfo); + if (matchIndices === null) return false; + lastMatchInfoOverride = null; + return true; } - lastMatchInfoOverride = null; - if (global) this.lastIndex = lastMatchInfo[CAPTURE1]; - return true; } diff --git a/src/runtime.cc b/src/runtime.cc index 5534db55..c43a1ab3 100644 --- a/src/runtime.cc +++ b/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<int>(Min<uint32_t>(s->length(), limit)); Handle<FixedArray> elements; if (s->IsFlat() && s->IsAsciiRepresentation()) { @@ -6340,15 +6341,20 @@ static MaybeObject* Runtime_NewArgumentsFast(Arguments args) { static MaybeObject* Runtime_NewClosure(Arguments args) { HandleScope scope; - ASSERT(args.length() == 2); + ASSERT(args.length() == 3); CONVERT_ARG_CHECKED(Context, context, 0); CONVERT_ARG_CHECKED(SharedFunctionInfo, shared, 1); + CONVERT_BOOLEAN_CHECKED(pretenure, args[2]); - PretenureFlag pretenure = (context->global_context() == *context) - ? TENURED // Allocate global closures in old space. - : NOT_TENURED; // Allocate local closures in new space. + // Allocate global closures in old space and allocate local closures + // in new space. Additionally pretenure closures that are assigned + // directly to properties. + pretenure = pretenure || (context->global_context() == *context); + PretenureFlag pretenure_flag = pretenure ? TENURED : NOT_TENURED; Handle<JSFunction> result = - Factory::NewFunctionFromSharedFunctionInfo(shared, context, pretenure); + Factory::NewFunctionFromSharedFunctionInfo(shared, + context, + pretenure_flag); return *result; } @@ -6386,7 +6392,7 @@ static void TrySettingInlineConstructStub(Handle<JSFunction> 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())); @@ -6454,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/src/runtime.h b/src/runtime.h index 756099b4..f9ebbc42 100644 --- a/src/runtime.h +++ b/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 */ \ @@ -262,7 +262,7 @@ namespace internal { F(CreateCatchExtensionObject, 2, 1) \ \ /* Statements */ \ - F(NewClosure, 2, 1) \ + F(NewClosure, 3, 1) \ F(NewObject, 1, 1) \ F(NewObjectFromBound, 2, 1) \ F(FinalizeInstanceSize, 1, 1) \ @@ -418,7 +418,8 @@ namespace internal { F(MathSqrt, 1, 1) \ F(IsRegExpEquivalent, 2, 1) \ F(HasCachedArrayIndex, 1, 1) \ - F(GetCachedArrayIndex, 1, 1) + F(GetCachedArrayIndex, 1, 1) \ + F(FastAsciiArrayJoin, 2, 1) // ---------------------------------------------------------------------------- diff --git a/src/scanner-base.cc b/src/scanner-base.cc index 6e9d40e0..8242f81c 100644 --- a/src/scanner-base.cc +++ b/src/scanner-base.cc @@ -27,12 +27,770 @@ // Features shared by parsing and pre-parsing scanners. +#include "../include/v8stdint.h" #include "scanner-base.h" +#include "char-predicates-inl.h" namespace v8 { namespace internal { // ---------------------------------------------------------------------------- +// UTF16Buffer + +UTF16Buffer::UTF16Buffer() + : pos_(0), end_(kNoEndPosition) { } + +// ---------------------------------------------------------------------------- +// LiteralCollector + +LiteralCollector::LiteralCollector() + : buffer_(kInitialCapacity), recording_(false) { } + + +LiteralCollector::~LiteralCollector() {} + + +void LiteralCollector::AddCharSlow(uc32 c) { + ASSERT(static_cast<unsigned>(c) > unibrow::Utf8::kMaxOneByteChar); + int length = unibrow::Utf8::Length(c); + Vector<char> block = buffer_.AddBlock(length, '\0'); +#ifdef DEBUG + int written_length = unibrow::Utf8::Encode(block.start(), c); + CHECK_EQ(length, written_length); +#else + unibrow::Utf8::Encode(block.start(), c); +#endif +} + +// ---------------------------------------------------------------------------- +// Character predicates + +unibrow::Predicate<IdentifierStart, 128> ScannerConstants::kIsIdentifierStart; +unibrow::Predicate<IdentifierPart, 128> ScannerConstants::kIsIdentifierPart; +unibrow::Predicate<unibrow::WhiteSpace, 128> ScannerConstants::kIsWhiteSpace; +unibrow::Predicate<unibrow::LineTerminator, 128> + ScannerConstants::kIsLineTerminator; + +StaticResource<ScannerConstants::Utf8Decoder> ScannerConstants::utf8_decoder_; + +// Compound predicates. + +bool ScannerConstants::IsIdentifier(unibrow::CharacterStream* buffer) { + // Checks whether the buffer contains an identifier (no escape). + if (!buffer->has_more()) return false; + if (!kIsIdentifierStart.get(buffer->GetNext())) { + return false; + } + while (buffer->has_more()) { + if (!kIsIdentifierPart.get(buffer->GetNext())) { + return false; + } + } + return true; +} + +// ---------------------------------------------------------------------------- +// Scanner + +Scanner::Scanner() : source_(NULL), stack_overflow_(false) {} + + +uc32 Scanner::ScanHexEscape(uc32 c, int length) { + ASSERT(length <= 4); // prevent overflow + + uc32 digits[4]; + uc32 x = 0; + for (int i = 0; i < length; i++) { + digits[i] = c0_; + int d = HexValue(c0_); + if (d < 0) { + // According to ECMA-262, 3rd, 7.8.4, page 18, these hex escapes + // should be illegal, but other JS VMs just return the + // non-escaped version of the original character. + + // Push back digits read, except the last one (in c0_). + for (int j = i-1; j >= 0; j--) { + PushBack(digits[j]); + } + // Notice: No handling of error - treat it as "\u"->"u". + return c; + } + x = x * 16 + d; + Advance(); + } + + return x; +} + + +// Octal escapes of the forms '\0xx' and '\xxx' are not a part of +// ECMA-262. Other JS VMs support them. +uc32 Scanner::ScanOctalEscape(uc32 c, int length) { + uc32 x = c - '0'; + for (int i = 0; i < length; i++) { + int d = c0_ - '0'; + if (d < 0 || d > 7) break; + int nx = x * 8 + d; + if (nx >= 256) break; + x = nx; + Advance(); + } + return x; +} + + +// ---------------------------------------------------------------------------- +// JavaScriptScanner + +JavaScriptScanner::JavaScriptScanner() + : has_line_terminator_before_next_(false) {} + + +Token::Value JavaScriptScanner::Next() { + current_ = next_; + has_line_terminator_before_next_ = false; + Scan(); + return current_.token; +} + + +static inline bool IsByteOrderMark(uc32 c) { + // The Unicode value U+FFFE is guaranteed never to be assigned as a + // Unicode character; this implies that in a Unicode context the + // 0xFF, 0xFE byte pattern can only be interpreted as the U+FEFF + // character expressed in little-endian byte order (since it could + // not be a U+FFFE character expressed in big-endian byte + // order). Nevertheless, we check for it to be compatible with + // Spidermonkey. + return c == 0xFEFF || c == 0xFFFE; +} + + +bool JavaScriptScanner::SkipWhiteSpace() { + int start_position = source_pos(); + + while (true) { + // We treat byte-order marks (BOMs) as whitespace for better + // compatibility with Spidermonkey and other JavaScript engines. + while (ScannerConstants::kIsWhiteSpace.get(c0_) || IsByteOrderMark(c0_)) { + // IsWhiteSpace() includes line terminators! + if (ScannerConstants::kIsLineTerminator.get(c0_)) { + // Ignore line terminators, but remember them. This is necessary + // for automatic semicolon insertion. + has_line_terminator_before_next_ = true; + } + Advance(); + } + + // If there is an HTML comment end '-->' at the beginning of a + // line (with only whitespace in front of it), we treat the rest + // of the line as a comment. This is in line with the way + // SpiderMonkey handles it. + if (c0_ == '-' && has_line_terminator_before_next_) { + Advance(); + if (c0_ == '-') { + Advance(); + if (c0_ == '>') { + // Treat the rest of the line as a comment. + SkipSingleLineComment(); + // Continue skipping white space after the comment. + continue; + } + PushBack('-'); // undo Advance() + } + PushBack('-'); // undo Advance() + } + // Return whether or not we skipped any characters. + return source_pos() != start_position; + } +} + + +Token::Value JavaScriptScanner::SkipSingleLineComment() { + Advance(); + + // The line terminator at the end of the line is not considered + // to be part of the single-line comment; it is recognized + // separately by the lexical grammar and becomes part of the + // stream of input elements for the syntactic grammar (see + // ECMA-262, section 7.4, page 12). + while (c0_ >= 0 && !ScannerConstants::kIsLineTerminator.get(c0_)) { + Advance(); + } + + return Token::WHITESPACE; +} + + +Token::Value JavaScriptScanner::SkipMultiLineComment() { + ASSERT(c0_ == '*'); + Advance(); + + while (c0_ >= 0) { + char ch = c0_; + Advance(); + // If we have reached the end of the multi-line comment, we + // consume the '/' and insert a whitespace. This way all + // multi-line comments are treated as whitespace - even the ones + // containing line terminators. This contradicts ECMA-262, section + // 7.4, page 12, that says that multi-line comments containing + // line terminators should be treated as a line terminator, but it + // matches the behaviour of SpiderMonkey and KJS. + if (ch == '*' && c0_ == '/') { + c0_ = ' '; + return Token::WHITESPACE; + } + } + + // Unterminated multi-line comment. + return Token::ILLEGAL; +} + + +Token::Value JavaScriptScanner::ScanHtmlComment() { + // Check for <!-- comments. + ASSERT(c0_ == '!'); + Advance(); + if (c0_ == '-') { + Advance(); + if (c0_ == '-') return SkipSingleLineComment(); + PushBack('-'); // undo Advance() + } + PushBack('!'); // undo Advance() + ASSERT(c0_ == '!'); + return Token::LT; +} + + +void JavaScriptScanner::Scan() { + next_.literal_chars = Vector<const char>(); + Token::Value token; + do { + // Remember the position of the next token + next_.location.beg_pos = source_pos(); + + switch (c0_) { + case ' ': + case '\t': + Advance(); + token = Token::WHITESPACE; + break; + + case '\n': + Advance(); + has_line_terminator_before_next_ = true; + token = Token::WHITESPACE; + break; + + case '"': case '\'': + token = ScanString(); + break; + + case '<': + // < <= << <<= <!-- + Advance(); + if (c0_ == '=') { + token = Select(Token::LTE); + } else if (c0_ == '<') { + token = Select('=', Token::ASSIGN_SHL, Token::SHL); + } else if (c0_ == '!') { + token = ScanHtmlComment(); + } else { + token = Token::LT; + } + break; + + case '>': + // > >= >> >>= >>> >>>= + Advance(); + if (c0_ == '=') { + token = Select(Token::GTE); + } else if (c0_ == '>') { + // >> >>= >>> >>>= + Advance(); + if (c0_ == '=') { + token = Select(Token::ASSIGN_SAR); + } else if (c0_ == '>') { + token = Select('=', Token::ASSIGN_SHR, Token::SHR); + } else { + token = Token::SAR; + } + } else { + token = Token::GT; + } + break; + + case '=': + // = == === + Advance(); + if (c0_ == '=') { + token = Select('=', Token::EQ_STRICT, Token::EQ); + } else { + token = Token::ASSIGN; + } + break; + + case '!': + // ! != !== + Advance(); + if (c0_ == '=') { + token = Select('=', Token::NE_STRICT, Token::NE); + } else { + token = Token::NOT; + } + break; + + case '+': + // + ++ += + Advance(); + if (c0_ == '+') { + token = Select(Token::INC); + } else if (c0_ == '=') { + token = Select(Token::ASSIGN_ADD); + } else { + token = Token::ADD; + } + break; + + case '-': + // - -- --> -= + Advance(); + if (c0_ == '-') { + Advance(); + if (c0_ == '>' && has_line_terminator_before_next_) { + // For compatibility with SpiderMonkey, we skip lines that + // start with an HTML comment end '-->'. + token = SkipSingleLineComment(); + } else { + token = Token::DEC; + } + } else if (c0_ == '=') { + token = Select(Token::ASSIGN_SUB); + } else { + token = Token::SUB; + } + break; + + case '*': + // * *= + token = Select('=', Token::ASSIGN_MUL, Token::MUL); + break; + + case '%': + // % %= + token = Select('=', Token::ASSIGN_MOD, Token::MOD); + break; + + case '/': + // / // /* /= + Advance(); + if (c0_ == '/') { + token = SkipSingleLineComment(); + } else if (c0_ == '*') { + token = SkipMultiLineComment(); + } else if (c0_ == '=') { + token = Select(Token::ASSIGN_DIV); + } else { + token = Token::DIV; + } + break; + + case '&': + // & && &= + Advance(); + if (c0_ == '&') { + token = Select(Token::AND); + } else if (c0_ == '=') { + token = Select(Token::ASSIGN_BIT_AND); + } else { + token = Token::BIT_AND; + } + break; + + case '|': + // | || |= + Advance(); + if (c0_ == '|') { + token = Select(Token::OR); + } else if (c0_ == '=') { + token = Select(Token::ASSIGN_BIT_OR); + } else { + token = Token::BIT_OR; + } + break; + + case '^': + // ^ ^= + token = Select('=', Token::ASSIGN_BIT_XOR, Token::BIT_XOR); + break; + + case '.': + // . Number + Advance(); + if (IsDecimalDigit(c0_)) { + token = ScanNumber(true); + } else { + token = Token::PERIOD; + } + break; + + case ':': + token = Select(Token::COLON); + break; + + case ';': + token = Select(Token::SEMICOLON); + break; + + case ',': + token = Select(Token::COMMA); + break; + + case '(': + token = Select(Token::LPAREN); + break; + + case ')': + token = Select(Token::RPAREN); + break; + + case '[': + token = Select(Token::LBRACK); + break; + + case ']': + token = Select(Token::RBRACK); + break; + + case '{': + token = Select(Token::LBRACE); + break; + + case '}': + token = Select(Token::RBRACE); + break; + + case '?': + token = Select(Token::CONDITIONAL); + break; + + case '~': + token = Select(Token::BIT_NOT); + break; + + default: + if (ScannerConstants::kIsIdentifierStart.get(c0_)) { + token = ScanIdentifierOrKeyword(); + } else if (IsDecimalDigit(c0_)) { + token = ScanNumber(false); + } else if (SkipWhiteSpace()) { + token = Token::WHITESPACE; + } else if (c0_ < 0) { + token = Token::EOS; + } else { + token = Select(Token::ILLEGAL); + } + break; + } + + // Continue scanning for tokens as long as we're just skipping + // whitespace. + } while (token == Token::WHITESPACE); + + next_.location.end_pos = source_pos(); + next_.token = token; +} + + +void JavaScriptScanner::SeekForward(int pos) { + source_->SeekForward(pos - 1); + Advance(); + // This function is only called to seek to the location + // of the end of a function (at the "}" token). It doesn't matter + // whether there was a line terminator in the part we skip. + has_line_terminator_before_next_ = false; + Scan(); +} + + +void JavaScriptScanner::ScanEscape() { + uc32 c = c0_; + Advance(); + + // Skip escaped newlines. + if (ScannerConstants::kIsLineTerminator.get(c)) { + // Allow CR+LF newlines in multiline string literals. + if (IsCarriageReturn(c) && IsLineFeed(c0_)) Advance(); + // Allow LF+CR newlines in multiline string literals. + if (IsLineFeed(c) && IsCarriageReturn(c0_)) Advance(); + return; + } + + switch (c) { + case '\'': // fall through + case '"' : // fall through + case '\\': break; + case 'b' : c = '\b'; break; + case 'f' : c = '\f'; break; + case 'n' : c = '\n'; break; + case 'r' : c = '\r'; break; + case 't' : c = '\t'; break; + case 'u' : c = ScanHexEscape(c, 4); break; + case 'v' : c = '\v'; break; + case 'x' : c = ScanHexEscape(c, 2); break; + case '0' : // fall through + case '1' : // fall through + case '2' : // fall through + case '3' : // fall through + case '4' : // fall through + case '5' : // fall through + case '6' : // fall through + case '7' : c = ScanOctalEscape(c, 2); break; + } + + // According to ECMA-262, 3rd, 7.8.4 (p 18ff) these + // should be illegal, but they are commonly handled + // as non-escaped characters by JS VMs. + AddLiteralChar(c); +} + + +Token::Value JavaScriptScanner::ScanString() { + uc32 quote = c0_; + Advance(); // consume quote + + LiteralScope literal(this, kLiteralString); + while (c0_ != quote && c0_ >= 0 + && !ScannerConstants::kIsLineTerminator.get(c0_)) { + uc32 c = c0_; + Advance(); + if (c == '\\') { + if (c0_ < 0) return Token::ILLEGAL; + ScanEscape(); + } else { + AddLiteralChar(c); + } + } + if (c0_ != quote) return Token::ILLEGAL; + literal.Complete(); + + Advance(); // consume quote + return Token::STRING; +} + + +void JavaScriptScanner::ScanDecimalDigits() { + while (IsDecimalDigit(c0_)) + AddLiteralCharAdvance(); +} + + +Token::Value JavaScriptScanner::ScanNumber(bool seen_period) { + ASSERT(IsDecimalDigit(c0_)); // the first digit of the number or the fraction + + enum { DECIMAL, HEX, OCTAL } kind = DECIMAL; + + LiteralScope literal(this, kLiteralNumber); + if (seen_period) { + // we have already seen a decimal point of the float + AddLiteralChar('.'); + ScanDecimalDigits(); // we know we have at least one digit + + } else { + // if the first character is '0' we must check for octals and hex + if (c0_ == '0') { + AddLiteralCharAdvance(); + + // either 0, 0exxx, 0Exxx, 0.xxx, an octal number, or a hex number + if (c0_ == 'x' || c0_ == 'X') { + // hex number + kind = HEX; + AddLiteralCharAdvance(); + if (!IsHexDigit(c0_)) { + // we must have at least one hex digit after 'x'/'X' + return Token::ILLEGAL; + } + while (IsHexDigit(c0_)) { + AddLiteralCharAdvance(); + } + } else if ('0' <= c0_ && c0_ <= '7') { + // (possible) octal number + kind = OCTAL; + while (true) { + if (c0_ == '8' || c0_ == '9') { + kind = DECIMAL; + break; + } + if (c0_ < '0' || '7' < c0_) break; + AddLiteralCharAdvance(); + } + } + } + + // Parse decimal digits and allow trailing fractional part. + if (kind == DECIMAL) { + ScanDecimalDigits(); // optional + if (c0_ == '.') { + AddLiteralCharAdvance(); + ScanDecimalDigits(); // optional + } + } + } + + // scan exponent, if any + if (c0_ == 'e' || c0_ == 'E') { + ASSERT(kind != HEX); // 'e'/'E' must be scanned as part of the hex number + if (kind == OCTAL) return Token::ILLEGAL; // no exponent for octals allowed + // scan exponent + AddLiteralCharAdvance(); + if (c0_ == '+' || c0_ == '-') + AddLiteralCharAdvance(); + if (!IsDecimalDigit(c0_)) { + // we must have at least one decimal digit after 'e'/'E' + return Token::ILLEGAL; + } + ScanDecimalDigits(); + } + + // The source character immediately following a numeric literal must + // not be an identifier start or a decimal digit; see ECMA-262 + // section 7.8.3, page 17 (note that we read only one decimal digit + // if the value is 0). + if (IsDecimalDigit(c0_) || ScannerConstants::kIsIdentifierStart.get(c0_)) + return Token::ILLEGAL; + + literal.Complete(); + + return Token::NUMBER; +} + + +uc32 JavaScriptScanner::ScanIdentifierUnicodeEscape() { + Advance(); + if (c0_ != 'u') return unibrow::Utf8::kBadChar; + Advance(); + uc32 c = ScanHexEscape('u', 4); + // We do not allow a unicode escape sequence to start another + // unicode escape sequence. + if (c == '\\') return unibrow::Utf8::kBadChar; + return c; +} + + +Token::Value JavaScriptScanner::ScanIdentifierOrKeyword() { + ASSERT(ScannerConstants::kIsIdentifierStart.get(c0_)); + LiteralScope literal(this, kLiteralIdentifier); + KeywordMatcher keyword_match; + // Scan identifier start character. + if (c0_ == '\\') { + uc32 c = ScanIdentifierUnicodeEscape(); + // Only allow legal identifier start characters. + if (!ScannerConstants::kIsIdentifierStart.get(c)) return Token::ILLEGAL; + AddLiteralChar(c); + return ScanIdentifierSuffix(&literal); + } + + uc32 first_char = c0_; + Advance(); + AddLiteralChar(first_char); + if (!keyword_match.AddChar(first_char)) { + return ScanIdentifierSuffix(&literal); + } + + // Scan the rest of the identifier characters. + while (ScannerConstants::kIsIdentifierPart.get(c0_)) { + if (c0_ != '\\') { + uc32 next_char = c0_; + Advance(); + AddLiteralChar(next_char); + if (keyword_match.AddChar(next_char)) continue; + } + // Fallthrough if no loner able to complete keyword. + return ScanIdentifierSuffix(&literal); + } + literal.Complete(); + + return keyword_match.token(); +} + + +Token::Value JavaScriptScanner::ScanIdentifierSuffix(LiteralScope* literal) { + // Scan the rest of the identifier characters. + while (ScannerConstants::kIsIdentifierPart.get(c0_)) { + if (c0_ == '\\') { + uc32 c = ScanIdentifierUnicodeEscape(); + // Only allow legal identifier part characters. + if (!ScannerConstants::kIsIdentifierPart.get(c)) return Token::ILLEGAL; + AddLiteralChar(c); + } else { + AddLiteralChar(c0_); + Advance(); + } + } + literal->Complete(); + + return Token::IDENTIFIER; +} + + +bool JavaScriptScanner::ScanRegExpPattern(bool seen_equal) { + // Scan: ('/' | '/=') RegularExpressionBody '/' RegularExpressionFlags + bool in_character_class = false; + + // Previous token is either '/' or '/=', in the second case, the + // pattern starts at =. + next_.location.beg_pos = source_pos() - (seen_equal ? 2 : 1); + next_.location.end_pos = source_pos() - (seen_equal ? 1 : 0); + + // Scan regular expression body: According to ECMA-262, 3rd, 7.8.5, + // the scanner should pass uninterpreted bodies to the RegExp + // constructor. + LiteralScope literal(this, kLiteralRegExp); + if (seen_equal) + AddLiteralChar('='); + + while (c0_ != '/' || in_character_class) { + if (ScannerConstants::kIsLineTerminator.get(c0_) || c0_ < 0) return false; + if (c0_ == '\\') { // escaped character + AddLiteralCharAdvance(); + if (ScannerConstants::kIsLineTerminator.get(c0_) || c0_ < 0) return false; + AddLiteralCharAdvance(); + } else { // unescaped character + if (c0_ == '[') in_character_class = true; + if (c0_ == ']') in_character_class = false; + AddLiteralCharAdvance(); + } + } + Advance(); // consume '/' + + literal.Complete(); + + return true; +} + + +bool JavaScriptScanner::ScanRegExpFlags() { + // Scan regular expression flags. + LiteralScope literal(this, kLiteralRegExpFlags); + while (ScannerConstants::kIsIdentifierPart.get(c0_)) { + if (c0_ == '\\') { + uc32 c = ScanIdentifierUnicodeEscape(); + if (c != static_cast<uc32>(unibrow::Utf8::kBadChar)) { + // We allow any escaped character, unlike the restriction on + // IdentifierPart when it is used to build an IdentifierName. + AddLiteralChar(c); + continue; + } + } + AddLiteralCharAdvance(); + } + literal.Complete(); + + next_.location.end_pos = source_pos() - 1; + return true; +} + +// ---------------------------------------------------------------------------- // Keyword Matcher KeywordMatcher::FirstState KeywordMatcher::first_states_[] = { @@ -127,9 +885,7 @@ void KeywordMatcher::Step(unibrow::uchar input) { break; case IN: token_ = Token::IDENTIFIER; - if (MatchKeywordStart(input, "instanceof", 2, Token::INSTANCEOF)) { - return; - } + if (MatchKeywordStart(input, "instanceof", 2, Token::INSTANCEOF)) return; break; case N: if (MatchKeywordStart(input, "native", 1, Token::NATIVE)) return; diff --git a/src/scanner-base.h b/src/scanner-base.h index 500870b5..3714ae2d 100644 --- a/src/scanner-base.h +++ b/src/scanner-base.h @@ -30,12 +30,402 @@ #ifndef V8_SCANNER_BASE_H_ #define V8_SCANNER_BASE_H_ +#include "globals.h" +#include "checks.h" +#include "allocation.h" #include "token.h" -#include "unicode.h" +#include "unicode-inl.h" +#include "char-predicates.h" +#include "utils.h" +#include "list-inl.h" namespace v8 { namespace internal { +// Returns the value (0 .. 15) of a hexadecimal character c. +// If c is not a legal hexadecimal character, returns a value < 0. +inline int HexValue(uc32 c) { + c -= '0'; + if (static_cast<unsigned>(c) <= 9) return c; + c = (c | 0x20) - ('a' - '0'); // detect 0x11..0x16 and 0x31..0x36. + if (static_cast<unsigned>(c) <= 5) return c + 10; + return -1; +} + +// ---------------------------------------------------------------------------- +// UTF16Buffer - scanner input source with pushback. + +class UTF16Buffer { + public: + UTF16Buffer(); + virtual ~UTF16Buffer() {} + + virtual void PushBack(uc32 ch) = 0; + // Returns a value < 0 when the buffer end is reached. + virtual uc32 Advance() = 0; + virtual void SeekForward(int pos) = 0; + + int pos() const { return pos_; } + + static const int kNoEndPosition = 1; + + protected: + // Initial value of end_ before the input stream is initialized. + + int pos_; // Current position in the buffer. + int end_; // Position where scanning should stop (EOF). +}; + + +class ScannerConstants : AllStatic { + public: + typedef unibrow::Utf8InputBuffer<1024> Utf8Decoder; + + static StaticResource<Utf8Decoder>* utf8_decoder() { + return &utf8_decoder_; + } + + static unibrow::Predicate<IdentifierStart, 128> kIsIdentifierStart; + static unibrow::Predicate<IdentifierPart, 128> kIsIdentifierPart; + static unibrow::Predicate<unibrow::LineTerminator, 128> kIsLineTerminator; + static unibrow::Predicate<unibrow::WhiteSpace, 128> kIsWhiteSpace; + + static bool IsIdentifier(unibrow::CharacterStream* buffer); + + private: + static StaticResource<Utf8Decoder> utf8_decoder_; +}; + +// ---------------------------------------------------------------------------- +// LiteralCollector - Collector of chars of literals. + +class LiteralCollector { + public: + LiteralCollector(); + ~LiteralCollector(); + + inline void AddChar(uc32 c) { + if (recording_) { + if (static_cast<unsigned>(c) <= unibrow::Utf8::kMaxOneByteChar) { + buffer_.Add(static_cast<char>(c)); + } else { + AddCharSlow(c); + } + } + } + + void StartLiteral() { + buffer_.StartSequence(); + recording_ = true; + } + + Vector<const char> EndLiteral() { + if (recording_) { + recording_ = false; + buffer_.Add(kEndMarker); + Vector<char> sequence = buffer_.EndSequence(); + return Vector<const char>(sequence.start(), sequence.length()); + } + return Vector<const char>(); + } + + void DropLiteral() { + if (recording_) { + recording_ = false; + buffer_.DropSequence(); + } + } + + void Reset() { + buffer_.Reset(); + } + + // The end marker added after a parsed literal. + // Using zero allows the usage of strlen and similar functions on + // identifiers and numbers (but not strings, since they may contain zero + // bytes). + static const char kEndMarker = '\x00'; + private: + static const int kInitialCapacity = 256; + SequenceCollector<char, 4> buffer_; + bool recording_; + void AddCharSlow(uc32 c); +}; + +// ---------------------------------------------------------------------------- +// Scanner base-class. + +// Generic functionality used by both JSON and JavaScript scanners. +class Scanner { + public: + typedef unibrow::Utf8InputBuffer<1024> Utf8Decoder; + + class LiteralScope { + public: + explicit LiteralScope(Scanner* self); + ~LiteralScope(); + void Complete(); + + private: + Scanner* scanner_; + bool complete_; + }; + + Scanner(); + + // Returns the current token again. + Token::Value current_token() { return current_.token; } + + // One token look-ahead (past the token returned by Next()). + Token::Value peek() const { return next_.token; } + + struct Location { + Location(int b, int e) : beg_pos(b), end_pos(e) { } + Location() : beg_pos(0), end_pos(0) { } + int beg_pos; + int end_pos; + }; + + // Returns the location information for the current token + // (the token returned by Next()). + Location location() const { return current_.location; } + Location peek_location() const { return next_.location; } + + // Returns the literal string, if any, for the current token (the + // token returned by Next()). The string is 0-terminated and in + // UTF-8 format; they may contain 0-characters. Literal strings are + // collected for identifiers, strings, and numbers. + // These functions only give the correct result if the literal + // was scanned between calls to StartLiteral() and TerminateLiteral(). + const char* literal_string() const { + return current_.literal_chars.start(); + } + + int literal_length() const { + // Excluding terminal '\x00' added by TerminateLiteral(). + return current_.literal_chars.length() - 1; + } + + Vector<const char> literal() const { + return Vector<const char>(literal_string(), literal_length()); + } + + // Returns the literal string for the next token (the token that + // would be returned if Next() were called). + const char* next_literal_string() const { + return next_.literal_chars.start(); + } + + + // Returns the length of the next token (that would be returned if + // Next() were called). + int next_literal_length() const { + // Excluding terminal '\x00' added by TerminateLiteral(). + return next_.literal_chars.length() - 1; + } + + Vector<const char> next_literal() const { + return Vector<const char>(next_literal_string(), next_literal_length()); + } + + bool stack_overflow() { return stack_overflow_; } + + static const int kCharacterLookaheadBufferSize = 1; + + protected: + // The current and look-ahead token. + struct TokenDesc { + Token::Value token; + Location location; + Vector<const char> literal_chars; + }; + + // Call this after setting source_ to the input. + void Init() { + // Set c0_ (one character ahead) + ASSERT(kCharacterLookaheadBufferSize == 1); + Advance(); + // Initialize current_ to not refer to a literal. + current_.literal_chars = Vector<const char>(); + // Reset literal buffer. + literal_buffer_.Reset(); + } + + // Literal buffer support + inline void StartLiteral() { + literal_buffer_.StartLiteral(); + } + + inline void AddLiteralChar(uc32 c) { + literal_buffer_.AddChar(c); + } + + // Complete scanning of a literal. + inline void TerminateLiteral() { + next_.literal_chars = literal_buffer_.EndLiteral(); + } + + // Stops scanning of a literal and drop the collected characters, + // e.g., due to an encountered error. + inline void DropLiteral() { + literal_buffer_.DropLiteral(); + } + + inline void AddLiteralCharAdvance() { + AddLiteralChar(c0_); + Advance(); + } + + // Low-level scanning support. + void Advance() { c0_ = source_->Advance(); } + void PushBack(uc32 ch) { + source_->PushBack(ch); + c0_ = ch; + } + + inline Token::Value Select(Token::Value tok) { + Advance(); + return tok; + } + + inline Token::Value Select(uc32 next, Token::Value then, Token::Value else_) { + Advance(); + if (c0_ == next) { + Advance(); + return then; + } else { + return else_; + } + } + + uc32 ScanHexEscape(uc32 c, int length); + uc32 ScanOctalEscape(uc32 c, int length); + + // Return the current source position. + int source_pos() { + return source_->pos() - kCharacterLookaheadBufferSize; + } + + TokenDesc current_; // desc for current token (as returned by Next()) + TokenDesc next_; // desc for next token (one token look-ahead) + + // Input stream. Must be initialized to an UTF16Buffer. + UTF16Buffer* source_; + + // Buffer to hold literal values (identifiers, strings, numbers) + // using '\x00'-terminated UTF-8 encoding. Handles allocation internally. + LiteralCollector literal_buffer_; + + bool stack_overflow_; + + // One Unicode character look-ahead; c0_ < 0 at the end of the input. + uc32 c0_; +}; + +// ---------------------------------------------------------------------------- +// JavaScriptScanner - base logic for JavaScript scanning. + +class JavaScriptScanner : public Scanner { + public: + + // Bit vector representing set of types of literals. + enum LiteralType { + kNoLiterals = 0, + kLiteralNumber = 1, + kLiteralIdentifier = 2, + kLiteralString = 4, + kLiteralRegExp = 8, + kLiteralRegExpFlags = 16, + kAllLiterals = 31 + }; + + // A LiteralScope that disables recording of some types of JavaScript + // literals. If the scanner is configured to not record the specific + // type of literal, the scope will not call StartLiteral. + class LiteralScope { + public: + LiteralScope(JavaScriptScanner* self, LiteralType type) + : scanner_(self), complete_(false) { + if (scanner_->RecordsLiteral(type)) { + scanner_->StartLiteral(); + } + } + ~LiteralScope() { + if (!complete_) scanner_->DropLiteral(); + } + void Complete() { + scanner_->TerminateLiteral(); + complete_ = true; + } + + private: + JavaScriptScanner* scanner_; + bool complete_; + }; + + JavaScriptScanner(); + + // Returns the next token. + Token::Value Next(); + + // Returns true if there was a line terminator before the peek'ed token. + bool has_line_terminator_before_next() const { + return has_line_terminator_before_next_; + } + + // Scans the input as a regular expression pattern, previous + // character(s) must be /(=). Returns true if a pattern is scanned. + bool ScanRegExpPattern(bool seen_equal); + // Returns true if regexp flags are scanned (always since flags can + // be empty). + bool ScanRegExpFlags(); + + // Tells whether the buffer contains an identifier (no escapes). + // Used for checking if a property name is an identifier. + static bool IsIdentifier(unibrow::CharacterStream* buffer); + + // Seek forward to the given position. This operation does not + // work in general, for instance when there are pushed back + // characters, but works for seeking forward until simple delimiter + // tokens, which is what it is used for. + void SeekForward(int pos); + + // Whether this scanner records the given literal type or not. + bool RecordsLiteral(LiteralType type) { + return (literal_flags_ & type) != 0; + } + + protected: + bool SkipWhiteSpace(); + Token::Value SkipSingleLineComment(); + Token::Value SkipMultiLineComment(); + + // Scans a single JavaScript token. + void Scan(); + + void ScanDecimalDigits(); + Token::Value ScanNumber(bool seen_period); + Token::Value ScanIdentifierOrKeyword(); + Token::Value ScanIdentifierSuffix(LiteralScope* literal); + + void ScanEscape(); + Token::Value ScanString(); + + // Scans a possible HTML comment -- begins with '<!'. + Token::Value ScanHtmlComment(); + + // Decodes a unicode escape-sequence which is part of an identifier. + // If the escape sequence cannot be decoded the result is kBadChar. + uc32 ScanIdentifierUnicodeEscape(); + + int literal_flags_; + bool has_line_terminator_before_next_; +}; + + +// ---------------------------------------------------------------------------- +// Keyword matching state machine. + class KeywordMatcher { // Incrementally recognize keywords. // @@ -45,7 +435,8 @@ class KeywordMatcher { // return switch this throw true try typeof var void while with // // *: Actually "future reserved keywords". These are the only ones we -// recognized, the remaining are allowed as identifiers. +// recognize, the remaining are allowed as identifiers. +// In ES5 strict mode, we should disallow all reserved keywords. public: KeywordMatcher() : state_(INITIAL), @@ -56,10 +447,11 @@ class KeywordMatcher { Token::Value token() { return token_; } - inline void AddChar(unibrow::uchar input) { + inline bool AddChar(unibrow::uchar input) { if (state_ != UNMATCHABLE) { Step(input); } + return state_ != UNMATCHABLE; } void Fail() { @@ -110,23 +502,23 @@ class KeywordMatcher { const char* keyword, int position, Token::Value token_if_match) { - if (input == static_cast<unibrow::uchar>(keyword[position])) { - state_ = KEYWORD_PREFIX; - this->keyword_ = keyword; - this->counter_ = position + 1; - this->keyword_token_ = token_if_match; - return true; + if (input != static_cast<unibrow::uchar>(keyword[position])) { + return false; } - return false; + state_ = KEYWORD_PREFIX; + this->keyword_ = keyword; + this->counter_ = position + 1; + this->keyword_token_ = token_if_match; + return true; } // If input equals match character, transition to new state and return true. inline bool MatchState(unibrow::uchar input, char match, State new_state) { - if (input == static_cast<unibrow::uchar>(match)) { - state_ = new_state; - return true; + if (input != static_cast<unibrow::uchar>(match)) { + return false; } - return false; + state_ = new_state; + return true; } inline bool MatchKeyword(unibrow::uchar input, @@ -156,10 +548,6 @@ class KeywordMatcher { }; - - - - } } // namespace v8::internal #endif // V8_SCANNER_BASE_H_ diff --git a/src/scanner.cc b/src/scanner.cc index a24952ac..63b2fd80 100755 --- a/src/scanner.cc +++ b/src/scanner.cc @@ -30,53 +30,14 @@ #include "ast.h" #include "handles.h" #include "scanner.h" +#include "unicode-inl.h" namespace v8 { namespace internal { // ---------------------------------------------------------------------------- -// Character predicates - - -unibrow::Predicate<IdentifierStart, 128> Scanner::kIsIdentifierStart; -unibrow::Predicate<IdentifierPart, 128> Scanner::kIsIdentifierPart; -unibrow::Predicate<unibrow::LineTerminator, 128> Scanner::kIsLineTerminator; -unibrow::Predicate<unibrow::WhiteSpace, 128> Scanner::kIsWhiteSpace; - - -StaticResource<Scanner::Utf8Decoder> Scanner::utf8_decoder_; - - -// ---------------------------------------------------------------------------- -// UTF8Buffer - -UTF8Buffer::UTF8Buffer() : buffer_(kInitialCapacity) { } - - -UTF8Buffer::~UTF8Buffer() {} - - -void UTF8Buffer::AddCharSlow(uc32 c) { - ASSERT(static_cast<unsigned>(c) > unibrow::Utf8::kMaxOneByteChar); - int length = unibrow::Utf8::Length(c); - Vector<char> block = buffer_.AddBlock(length, '\0'); -#ifdef DEBUG - int written_length = unibrow::Utf8::Encode(block.start(), c); - CHECK_EQ(length, written_length); -#else - unibrow::Utf8::Encode(block.start(), c); -#endif -} - - -// ---------------------------------------------------------------------------- // UTF16Buffer - -UTF16Buffer::UTF16Buffer() - : pos_(0), end_(Scanner::kNoEndPosition) { } - - // CharacterStreamUTF16Buffer CharacterStreamUTF16Buffer::CharacterStreamUTF16Buffer() : pushback_buffer_(0), last_(0), stream_(NULL) { } @@ -90,7 +51,7 @@ void CharacterStreamUTF16Buffer::Initialize(Handle<String> data, if (start_position > 0) { SeekForward(start_position); } - end_ = end_position != Scanner::kNoEndPosition ? end_position : kMaxInt; + end_ = end_position != kNoEndPosition ? end_position : kMaxInt; } @@ -102,7 +63,7 @@ void CharacterStreamUTF16Buffer::PushBack(uc32 ch) { uc32 CharacterStreamUTF16Buffer::Advance() { - ASSERT(end_ != Scanner::kNoEndPosition); + ASSERT(end_ != kNoEndPosition); ASSERT(end_ >= 0); // NOTE: It is of importance to Persian / Farsi resources that we do // *not* strip format control characters in the scanner; see @@ -135,55 +96,6 @@ void CharacterStreamUTF16Buffer::SeekForward(int pos) { } -// ExternalStringUTF16Buffer -template <typename StringType, typename CharType> -ExternalStringUTF16Buffer<StringType, CharType>::ExternalStringUTF16Buffer() - : raw_data_(NULL) { } - - -template <typename StringType, typename CharType> -void ExternalStringUTF16Buffer<StringType, CharType>::Initialize( - Handle<StringType> data, - int start_position, - int end_position) { - ASSERT(!data.is_null()); - raw_data_ = data->resource()->data(); - - ASSERT(end_position <= data->length()); - if (start_position > 0) { - SeekForward(start_position); - } - end_ = - end_position != Scanner::kNoEndPosition ? end_position : data->length(); -} - - -template <typename StringType, typename CharType> -uc32 ExternalStringUTF16Buffer<StringType, CharType>::Advance() { - if (pos_ < end_) { - return raw_data_[pos_++]; - } else { - // note: currently the following increment is necessary to avoid a - // test-parser problem! - pos_++; - return static_cast<uc32>(-1); - } -} - - -template <typename StringType, typename CharType> -void ExternalStringUTF16Buffer<StringType, CharType>::PushBack(uc32 ch) { - pos_--; - ASSERT(pos_ >= Scanner::kCharacterLookaheadBufferSize); - ASSERT(raw_data_[pos_ - Scanner::kCharacterLookaheadBufferSize] == ch); -} - - -template <typename StringType, typename CharType> -void ExternalStringUTF16Buffer<StringType, CharType>::SeekForward(int pos) { - pos_ = pos; -} - // ---------------------------------------------------------------------------- // Scanner::LiteralScope @@ -204,41 +116,74 @@ void Scanner::LiteralScope::Complete() { } // ---------------------------------------------------------------------------- -// Scanner - -Scanner::Scanner() - : has_line_terminator_before_next_(false), - is_parsing_json_(false), - source_(NULL), - stack_overflow_(false) {} +// V8JavaScriptScanner + +void V8JavaScriptScanner::Initialize(Handle<String> source, + int literal_flags) { + source_ = stream_initializer_.Init(source, NULL, 0, source->length()); + // Need to capture identifiers in order to recognize "get" and "set" + // in object literals. + literal_flags_ = literal_flags | 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(); +} -void Scanner::Initialize(Handle<String> source, - ParserLanguage language) { - Init(source, NULL, 0, source->length(), language); +void V8JavaScriptScanner::Initialize(Handle<String> source, + unibrow::CharacterStream* stream, + int literal_flags) { + source_ = stream_initializer_.Init(source, stream, + 0, UTF16Buffer::kNoEndPosition); + literal_flags_ = literal_flags | 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(); } -void Scanner::Initialize(Handle<String> source, - unibrow::CharacterStream* stream, - ParserLanguage language) { - Init(source, stream, 0, kNoEndPosition, language); +void V8JavaScriptScanner::Initialize(Handle<String> source, + int start_position, + int end_position, + int literal_flags) { + source_ = stream_initializer_.Init(source, NULL, + start_position, end_position); + literal_flags_ = literal_flags | 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(); } -void Scanner::Initialize(Handle<String> source, - int start_position, - int end_position, - ParserLanguage language) { - Init(source, NULL, start_position, end_position, language); +Token::Value V8JavaScriptScanner::NextCheckStack() { + // BUG 1215673: Find a thread safe way to set a stack limit in + // pre-parse mode. Otherwise, we cannot safely pre-parse from other + // threads. + StackLimitCheck check; + if (check.HasOverflowed()) { + stack_overflow_ = true; + current_ = next_; + next_.token = Token::ILLEGAL; + return current_.token; + } else { + return Next(); + } } -void Scanner::Init(Handle<String> source, - unibrow::CharacterStream* stream, - int start_position, - int end_position, - ParserLanguage language) { +UTF16Buffer* StreamInitializer::Init(Handle<String> source, + unibrow::CharacterStream* stream, + int start_position, + int end_position) { // Either initialize the scanner from a character stream or from a // string. ASSERT(source.is_null() || stream == NULL); @@ -249,13 +194,13 @@ void Scanner::Init(Handle<String> source, Handle<ExternalTwoByteString>::cast(source), start_position, end_position); - source_ = &two_byte_string_buffer_; + return &two_byte_string_buffer_; } else if (!source.is_null() && StringShape(*source).IsExternalAscii()) { ascii_string_buffer_.Initialize( Handle<ExternalAsciiString>::cast(source), start_position, end_position); - source_ = &ascii_string_buffer_; + return &ascii_string_buffer_; } else { if (!source.is_null()) { safe_string_input_buffer_.Reset(source.location()); @@ -265,28 +210,27 @@ void Scanner::Init(Handle<String> source, stream, start_position, end_position); - source_ = &char_stream_buffer_; + return &char_stream_buffer_; } +} - is_parsing_json_ = (language == JSON); +// ---------------------------------------------------------------------------- +// JsonScanner - // Set c0_ (one character ahead) - ASSERT(kCharacterLookaheadBufferSize == 1); - Advance(); - // Initialize current_ to not refer to a literal. - current_.literal_chars = Vector<const char>(); - // Reset literal buffer. - literal_buffer_.Reset(); +JsonScanner::JsonScanner() {} - // Skip initial whitespace allowing HTML comment ends just like - // after a newline and scan first token. - has_line_terminator_before_next_ = true; - SkipWhiteSpace(); - Scan(); + +void JsonScanner::Initialize(Handle<String> source) { + source_ = stream_initializer_.Init(source, NULL, 0, source->length()); + Init(); + // Skip initial whitespace. + SkipJsonWhiteSpace(); + // Preload first token as look-ahead. + ScanJson(); } -Token::Value Scanner::Next() { +Token::Value JsonScanner::Next() { // BUG 1215673: Find a thread safe way to set a stack limit in // pre-parse mode. Otherwise, we cannot safely pre-parse from other // threads. @@ -297,52 +241,13 @@ Token::Value Scanner::Next() { stack_overflow_ = true; next_.token = Token::ILLEGAL; } else { - has_line_terminator_before_next_ = false; - Scan(); + ScanJson(); } return current_.token; } -void Scanner::StartLiteral() { - literal_buffer_.StartLiteral(); -} - - -void Scanner::AddChar(uc32 c) { - literal_buffer_.AddChar(c); -} - - -void Scanner::TerminateLiteral() { - next_.literal_chars = literal_buffer_.EndLiteral(); -} - - -void Scanner::DropLiteral() { - literal_buffer_.DropLiteral(); -} - - -void Scanner::AddCharAdvance() { - AddChar(c0_); - Advance(); -} - - -static inline bool IsByteOrderMark(uc32 c) { - // The Unicode value U+FFFE is guaranteed never to be assigned as a - // Unicode character; this implies that in a Unicode context the - // 0xFF, 0xFE byte pattern can only be interpreted as the U+FEFF - // character expressed in little-endian byte order (since it could - // not be a U+FFFE character expressed in big-endian byte - // order). Nevertheless, we check for it to be compatible with - // Spidermonkey. - return c == 0xFEFF || c == 0xFFFE; -} - - -bool Scanner::SkipJsonWhiteSpace() { +bool JsonScanner::SkipJsonWhiteSpace() { int start_position = source_pos(); // JSON WhiteSpace is tab, carrige-return, newline and space. while (c0_ == ' ' || c0_ == '\n' || c0_ == '\r' || c0_ == '\t') { @@ -352,107 +257,9 @@ bool Scanner::SkipJsonWhiteSpace() { } -bool Scanner::SkipJavaScriptWhiteSpace() { - int start_position = source_pos(); - - while (true) { - // We treat byte-order marks (BOMs) as whitespace for better - // compatibility with Spidermonkey and other JavaScript engines. - while (kIsWhiteSpace.get(c0_) || IsByteOrderMark(c0_)) { - // IsWhiteSpace() includes line terminators! - if (kIsLineTerminator.get(c0_)) { - // Ignore line terminators, but remember them. This is necessary - // for automatic semicolon insertion. - has_line_terminator_before_next_ = true; - } - Advance(); - } - - // If there is an HTML comment end '-->' at the beginning of a - // line (with only whitespace in front of it), we treat the rest - // of the line as a comment. This is in line with the way - // SpiderMonkey handles it. - if (c0_ == '-' && has_line_terminator_before_next_) { - Advance(); - if (c0_ == '-') { - Advance(); - if (c0_ == '>') { - // Treat the rest of the line as a comment. - SkipSingleLineComment(); - // Continue skipping white space after the comment. - continue; - } - PushBack('-'); // undo Advance() - } - PushBack('-'); // undo Advance() - } - // Return whether or not we skipped any characters. - return source_pos() != start_position; - } -} - - -Token::Value Scanner::SkipSingleLineComment() { - Advance(); - - // The line terminator at the end of the line is not considered - // to be part of the single-line comment; it is recognized - // separately by the lexical grammar and becomes part of the - // stream of input elements for the syntactic grammar (see - // ECMA-262, section 7.4, page 12). - while (c0_ >= 0 && !kIsLineTerminator.get(c0_)) { - Advance(); - } - - return Token::WHITESPACE; -} - - -Token::Value Scanner::SkipMultiLineComment() { - ASSERT(c0_ == '*'); - Advance(); - - while (c0_ >= 0) { - char ch = c0_; - Advance(); - // If we have reached the end of the multi-line comment, we - // consume the '/' and insert a whitespace. This way all - // multi-line comments are treated as whitespace - even the ones - // containing line terminators. This contradicts ECMA-262, section - // 7.4, page 12, that says that multi-line comments containing - // line terminators should be treated as a line terminator, but it - // matches the behaviour of SpiderMonkey and KJS. - if (ch == '*' && c0_ == '/') { - c0_ = ' '; - return Token::WHITESPACE; - } - } - - // Unterminated multi-line comment. - return Token::ILLEGAL; -} - - -Token::Value Scanner::ScanHtmlComment() { - // Check for <!-- comments. - ASSERT(c0_ == '!'); - Advance(); - if (c0_ == '-') { - Advance(); - if (c0_ == '-') return SkipSingleLineComment(); - PushBack('-'); // undo Advance() - } - PushBack('!'); // undo Advance() - ASSERT(c0_ == '!'); - return Token::LT; -} - - - -void Scanner::ScanJson() { +void JsonScanner::ScanJson() { next_.literal_chars = Vector<const char>(); Token::Value token; - has_line_terminator_before_next_ = false; do { // Remember the position of the next token next_.location.beg_pos = source_pos(); @@ -529,7 +336,7 @@ void Scanner::ScanJson() { } -Token::Value Scanner::ScanJsonString() { +Token::Value JsonScanner::ScanJsonString() { ASSERT_EQ('"', c0_); Advance(); LiteralScope literal(this); @@ -537,29 +344,29 @@ Token::Value Scanner::ScanJsonString() { // Check for control character (0x00-0x1f) or unterminated string (<0). if (c0_ < 0x20) return Token::ILLEGAL; if (c0_ != '\\') { - AddCharAdvance(); + AddLiteralCharAdvance(); } else { Advance(); switch (c0_) { case '"': case '\\': case '/': - AddChar(c0_); + AddLiteralChar(c0_); break; case 'b': - AddChar('\x08'); + AddLiteralChar('\x08'); break; case 'f': - AddChar('\x0c'); + AddLiteralChar('\x0c'); break; case 'n': - AddChar('\x0a'); + AddLiteralChar('\x0a'); break; case 'r': - AddChar('\x0d'); + AddLiteralChar('\x0d'); break; case 't': - AddChar('\x09'); + AddLiteralChar('\x09'); break; case 'u': { uc32 value = 0; @@ -571,7 +378,7 @@ Token::Value Scanner::ScanJsonString() { } value = value * 16 + digit; } - AddChar(value); + AddLiteralChar(value); break; } default: @@ -589,33 +396,33 @@ Token::Value Scanner::ScanJsonString() { } -Token::Value Scanner::ScanJsonNumber() { +Token::Value JsonScanner::ScanJsonNumber() { LiteralScope literal(this); - if (c0_ == '-') AddCharAdvance(); + if (c0_ == '-') AddLiteralCharAdvance(); if (c0_ == '0') { - AddCharAdvance(); + AddLiteralCharAdvance(); // Prefix zero is only allowed if it's the only digit before // a decimal point or exponent. if ('0' <= c0_ && c0_ <= '9') return Token::ILLEGAL; } else { if (c0_ < '1' || c0_ > '9') return Token::ILLEGAL; do { - AddCharAdvance(); + AddLiteralCharAdvance(); } while (c0_ >= '0' && c0_ <= '9'); } if (c0_ == '.') { - AddCharAdvance(); + AddLiteralCharAdvance(); if (c0_ < '0' || c0_ > '9') return Token::ILLEGAL; do { - AddCharAdvance(); + AddLiteralCharAdvance(); } while (c0_ >= '0' && c0_ <= '9'); } if (AsciiAlphaToLower(c0_) == 'e') { - AddCharAdvance(); - if (c0_ == '-' || c0_ == '+') AddCharAdvance(); + AddLiteralCharAdvance(); + if (c0_ == '-' || c0_ == '+') AddLiteralCharAdvance(); if (c0_ < '0' || c0_ > '9') return Token::ILLEGAL; do { - AddCharAdvance(); + AddLiteralCharAdvance(); } while (c0_ >= '0' && c0_ <= '9'); } literal.Complete(); @@ -623,601 +430,19 @@ Token::Value Scanner::ScanJsonNumber() { } -Token::Value Scanner::ScanJsonIdentifier(const char* text, - Token::Value token) { +Token::Value JsonScanner::ScanJsonIdentifier(const char* text, + Token::Value token) { LiteralScope literal(this); while (*text != '\0') { if (c0_ != *text) return Token::ILLEGAL; Advance(); text++; } - if (kIsIdentifierPart.get(c0_)) return Token::ILLEGAL; + if (ScannerConstants::kIsIdentifierPart.get(c0_)) return Token::ILLEGAL; literal.Complete(); return token; } -void Scanner::ScanJavaScript() { - next_.literal_chars = Vector<const char>(); - Token::Value token; - do { - // Remember the position of the next token - next_.location.beg_pos = source_pos(); - - switch (c0_) { - case ' ': - case '\t': - Advance(); - token = Token::WHITESPACE; - break; - - case '\n': - Advance(); - has_line_terminator_before_next_ = true; - token = Token::WHITESPACE; - break; - - case '"': case '\'': - token = ScanString(); - break; - - case '<': - // < <= << <<= <!-- - Advance(); - if (c0_ == '=') { - token = Select(Token::LTE); - } else if (c0_ == '<') { - token = Select('=', Token::ASSIGN_SHL, Token::SHL); - } else if (c0_ == '!') { - token = ScanHtmlComment(); - } else { - token = Token::LT; - } - break; - - case '>': - // > >= >> >>= >>> >>>= - Advance(); - if (c0_ == '=') { - token = Select(Token::GTE); - } else if (c0_ == '>') { - // >> >>= >>> >>>= - Advance(); - if (c0_ == '=') { - token = Select(Token::ASSIGN_SAR); - } else if (c0_ == '>') { - token = Select('=', Token::ASSIGN_SHR, Token::SHR); - } else { - token = Token::SAR; - } - } else { - token = Token::GT; - } - break; - - case '=': - // = == === - Advance(); - if (c0_ == '=') { - token = Select('=', Token::EQ_STRICT, Token::EQ); - } else { - token = Token::ASSIGN; - } - break; - - case '!': - // ! != !== - Advance(); - if (c0_ == '=') { - token = Select('=', Token::NE_STRICT, Token::NE); - } else { - token = Token::NOT; - } - break; - - case '+': - // + ++ += - Advance(); - if (c0_ == '+') { - token = Select(Token::INC); - } else if (c0_ == '=') { - token = Select(Token::ASSIGN_ADD); - } else { - token = Token::ADD; - } - break; - - case '-': - // - -- --> -= - Advance(); - if (c0_ == '-') { - Advance(); - if (c0_ == '>' && has_line_terminator_before_next_) { - // For compatibility with SpiderMonkey, we skip lines that - // start with an HTML comment end '-->'. - token = SkipSingleLineComment(); - } else { - token = Token::DEC; - } - } else if (c0_ == '=') { - token = Select(Token::ASSIGN_SUB); - } else { - token = Token::SUB; - } - break; - - case '*': - // * *= - token = Select('=', Token::ASSIGN_MUL, Token::MUL); - break; - - case '%': - // % %= - token = Select('=', Token::ASSIGN_MOD, Token::MOD); - break; - - case '/': - // / // /* /= - Advance(); - if (c0_ == '/') { - token = SkipSingleLineComment(); - } else if (c0_ == '*') { - token = SkipMultiLineComment(); - } else if (c0_ == '=') { - token = Select(Token::ASSIGN_DIV); - } else { - token = Token::DIV; - } - break; - - case '&': - // & && &= - Advance(); - if (c0_ == '&') { - token = Select(Token::AND); - } else if (c0_ == '=') { - token = Select(Token::ASSIGN_BIT_AND); - } else { - token = Token::BIT_AND; - } - break; - - case '|': - // | || |= - Advance(); - if (c0_ == '|') { - token = Select(Token::OR); - } else if (c0_ == '=') { - token = Select(Token::ASSIGN_BIT_OR); - } else { - token = Token::BIT_OR; - } - break; - - case '^': - // ^ ^= - token = Select('=', Token::ASSIGN_BIT_XOR, Token::BIT_XOR); - break; - - case '.': - // . Number - Advance(); - if (IsDecimalDigit(c0_)) { - token = ScanNumber(true); - } else { - token = Token::PERIOD; - } - break; - - case ':': - token = Select(Token::COLON); - break; - - case ';': - token = Select(Token::SEMICOLON); - break; - - case ',': - token = Select(Token::COMMA); - break; - - case '(': - token = Select(Token::LPAREN); - break; - - case ')': - token = Select(Token::RPAREN); - break; - - case '[': - token = Select(Token::LBRACK); - break; - - case ']': - token = Select(Token::RBRACK); - break; - - case '{': - token = Select(Token::LBRACE); - break; - - case '}': - token = Select(Token::RBRACE); - break; - - case '?': - token = Select(Token::CONDITIONAL); - break; - - case '~': - token = Select(Token::BIT_NOT); - break; - - default: - if (kIsIdentifierStart.get(c0_)) { - token = ScanIdentifier(); - } else if (IsDecimalDigit(c0_)) { - token = ScanNumber(false); - } else if (SkipWhiteSpace()) { - token = Token::WHITESPACE; - } else if (c0_ < 0) { - token = Token::EOS; - } else { - token = Select(Token::ILLEGAL); - } - break; - } - - // Continue scanning for tokens as long as we're just skipping - // whitespace. - } while (token == Token::WHITESPACE); - - next_.location.end_pos = source_pos(); - next_.token = token; -} - - -void Scanner::SeekForward(int pos) { - source_->SeekForward(pos - 1); - Advance(); - // This function is only called to seek to the location - // of the end of a function (at the "}" token). It doesn't matter - // whether there was a line terminator in the part we skip. - has_line_terminator_before_next_ = false; - Scan(); -} - - -uc32 Scanner::ScanHexEscape(uc32 c, int length) { - ASSERT(length <= 4); // prevent overflow - - uc32 digits[4]; - uc32 x = 0; - for (int i = 0; i < length; i++) { - digits[i] = c0_; - int d = HexValue(c0_); - if (d < 0) { - // According to ECMA-262, 3rd, 7.8.4, page 18, these hex escapes - // should be illegal, but other JS VMs just return the - // non-escaped version of the original character. - - // Push back digits read, except the last one (in c0_). - for (int j = i-1; j >= 0; j--) { - PushBack(digits[j]); - } - // Notice: No handling of error - treat it as "\u"->"u". - return c; - } - x = x * 16 + d; - Advance(); - } - - return x; -} - - -// Octal escapes of the forms '\0xx' and '\xxx' are not a part of -// ECMA-262. Other JS VMs support them. -uc32 Scanner::ScanOctalEscape(uc32 c, int length) { - uc32 x = c - '0'; - for (int i = 0; i < length; i++) { - int d = c0_ - '0'; - if (d < 0 || d > 7) break; - int nx = x * 8 + d; - if (nx >= 256) break; - x = nx; - Advance(); - } - return x; -} - - -void Scanner::ScanEscape() { - uc32 c = c0_; - Advance(); - - // Skip escaped newlines. - if (kIsLineTerminator.get(c)) { - // Allow CR+LF newlines in multiline string literals. - if (IsCarriageReturn(c) && IsLineFeed(c0_)) Advance(); - // Allow LF+CR newlines in multiline string literals. - if (IsLineFeed(c) && IsCarriageReturn(c0_)) Advance(); - return; - } - - switch (c) { - case '\'': // fall through - case '"' : // fall through - case '\\': break; - case 'b' : c = '\b'; break; - case 'f' : c = '\f'; break; - case 'n' : c = '\n'; break; - case 'r' : c = '\r'; break; - case 't' : c = '\t'; break; - case 'u' : c = ScanHexEscape(c, 4); break; - case 'v' : c = '\v'; break; - case 'x' : c = ScanHexEscape(c, 2); break; - case '0' : // fall through - case '1' : // fall through - case '2' : // fall through - case '3' : // fall through - case '4' : // fall through - case '5' : // fall through - case '6' : // fall through - case '7' : c = ScanOctalEscape(c, 2); break; - } - - // According to ECMA-262, 3rd, 7.8.4 (p 18ff) these - // should be illegal, but they are commonly handled - // as non-escaped characters by JS VMs. - AddChar(c); -} - - -Token::Value Scanner::ScanString() { - uc32 quote = c0_; - Advance(); // consume quote - - LiteralScope literal(this); - while (c0_ != quote && c0_ >= 0 && !kIsLineTerminator.get(c0_)) { - uc32 c = c0_; - Advance(); - if (c == '\\') { - if (c0_ < 0) return Token::ILLEGAL; - ScanEscape(); - } else { - AddChar(c); - } - } - if (c0_ != quote) return Token::ILLEGAL; - literal.Complete(); - - Advance(); // consume quote - return Token::STRING; -} - - -Token::Value Scanner::Select(Token::Value tok) { - Advance(); - return tok; -} - - -Token::Value Scanner::Select(uc32 next, Token::Value then, Token::Value else_) { - Advance(); - if (c0_ == next) { - Advance(); - return then; - } else { - return else_; - } -} - - -// Returns true if any decimal digits were scanned, returns false otherwise. -void Scanner::ScanDecimalDigits() { - while (IsDecimalDigit(c0_)) - AddCharAdvance(); -} - - -Token::Value Scanner::ScanNumber(bool seen_period) { - ASSERT(IsDecimalDigit(c0_)); // the first digit of the number or the fraction - - enum { DECIMAL, HEX, OCTAL } kind = DECIMAL; - - LiteralScope literal(this); - if (seen_period) { - // we have already seen a decimal point of the float - AddChar('.'); - ScanDecimalDigits(); // we know we have at least one digit - - } else { - // if the first character is '0' we must check for octals and hex - if (c0_ == '0') { - AddCharAdvance(); - - // either 0, 0exxx, 0Exxx, 0.xxx, an octal number, or a hex number - if (c0_ == 'x' || c0_ == 'X') { - // hex number - kind = HEX; - AddCharAdvance(); - if (!IsHexDigit(c0_)) { - // we must have at least one hex digit after 'x'/'X' - return Token::ILLEGAL; - } - while (IsHexDigit(c0_)) { - AddCharAdvance(); - } - } else if ('0' <= c0_ && c0_ <= '7') { - // (possible) octal number - kind = OCTAL; - while (true) { - if (c0_ == '8' || c0_ == '9') { - kind = DECIMAL; - break; - } - if (c0_ < '0' || '7' < c0_) break; - AddCharAdvance(); - } - } - } - - // Parse decimal digits and allow trailing fractional part. - if (kind == DECIMAL) { - ScanDecimalDigits(); // optional - if (c0_ == '.') { - AddCharAdvance(); - ScanDecimalDigits(); // optional - } - } - } - - // scan exponent, if any - if (c0_ == 'e' || c0_ == 'E') { - ASSERT(kind != HEX); // 'e'/'E' must be scanned as part of the hex number - if (kind == OCTAL) return Token::ILLEGAL; // no exponent for octals allowed - // scan exponent - AddCharAdvance(); - if (c0_ == '+' || c0_ == '-') - AddCharAdvance(); - if (!IsDecimalDigit(c0_)) { - // we must have at least one decimal digit after 'e'/'E' - return Token::ILLEGAL; - } - ScanDecimalDigits(); - } - - // The source character immediately following a numeric literal must - // not be an identifier start or a decimal digit; see ECMA-262 - // section 7.8.3, page 17 (note that we read only one decimal digit - // if the value is 0). - if (IsDecimalDigit(c0_) || kIsIdentifierStart.get(c0_)) - return Token::ILLEGAL; - - literal.Complete(); - - return Token::NUMBER; -} - - -uc32 Scanner::ScanIdentifierUnicodeEscape() { - Advance(); - if (c0_ != 'u') return unibrow::Utf8::kBadChar; - Advance(); - uc32 c = ScanHexEscape('u', 4); - // We do not allow a unicode escape sequence to start another - // unicode escape sequence. - if (c == '\\') return unibrow::Utf8::kBadChar; - return c; -} - - -Token::Value Scanner::ScanIdentifier() { - ASSERT(kIsIdentifierStart.get(c0_)); - - LiteralScope literal(this); - KeywordMatcher keyword_match; - - // Scan identifier start character. - if (c0_ == '\\') { - uc32 c = ScanIdentifierUnicodeEscape(); - // Only allow legal identifier start characters. - if (!kIsIdentifierStart.get(c)) return Token::ILLEGAL; - AddChar(c); - keyword_match.Fail(); - } else { - AddChar(c0_); - keyword_match.AddChar(c0_); - Advance(); - } - - // Scan the rest of the identifier characters. - while (kIsIdentifierPart.get(c0_)) { - if (c0_ == '\\') { - uc32 c = ScanIdentifierUnicodeEscape(); - // Only allow legal identifier part characters. - if (!kIsIdentifierPart.get(c)) return Token::ILLEGAL; - AddChar(c); - keyword_match.Fail(); - } else { - AddChar(c0_); - keyword_match.AddChar(c0_); - Advance(); - } - } - literal.Complete(); - - return keyword_match.token(); -} - - - -bool Scanner::IsIdentifier(unibrow::CharacterStream* buffer) { - // Checks whether the buffer contains an identifier (no escape). - if (!buffer->has_more()) return false; - if (!kIsIdentifierStart.get(buffer->GetNext())) return false; - while (buffer->has_more()) { - if (!kIsIdentifierPart.get(buffer->GetNext())) return false; - } - return true; -} - - -bool Scanner::ScanRegExpPattern(bool seen_equal) { - // Scan: ('/' | '/=') RegularExpressionBody '/' RegularExpressionFlags - bool in_character_class = false; - - // Previous token is either '/' or '/=', in the second case, the - // pattern starts at =. - next_.location.beg_pos = source_pos() - (seen_equal ? 2 : 1); - next_.location.end_pos = source_pos() - (seen_equal ? 1 : 0); - - // Scan regular expression body: According to ECMA-262, 3rd, 7.8.5, - // the scanner should pass uninterpreted bodies to the RegExp - // constructor. - LiteralScope literal(this); - if (seen_equal) - AddChar('='); - - while (c0_ != '/' || in_character_class) { - if (kIsLineTerminator.get(c0_) || c0_ < 0) return false; - if (c0_ == '\\') { // escaped character - AddCharAdvance(); - if (kIsLineTerminator.get(c0_) || c0_ < 0) return false; - AddCharAdvance(); - } else { // unescaped character - if (c0_ == '[') in_character_class = true; - if (c0_ == ']') in_character_class = false; - AddCharAdvance(); - } - } - Advance(); // consume '/' - - literal.Complete(); - - return true; -} - -bool Scanner::ScanRegExpFlags() { - // Scan regular expression flags. - LiteralScope literal(this); - while (kIsIdentifierPart.get(c0_)) { - if (c0_ == '\\') { - uc32 c = ScanIdentifierUnicodeEscape(); - if (c != static_cast<uc32>(unibrow::Utf8::kBadChar)) { - // We allow any escaped character, unlike the restriction on - // IdentifierPart when it is used to build an IdentifierName. - AddChar(c); - continue; - } - } - AddCharAdvance(); - } - literal.Complete(); - - next_.location.end_pos = source_pos() - 1; - return true; -} } } // namespace v8::internal diff --git a/src/scanner.h b/src/scanner.h index 1f49fd0e..acb9b47b 100644 --- a/src/scanner.h +++ b/src/scanner.h @@ -35,74 +35,6 @@ namespace v8 { namespace internal { - -class UTF8Buffer { - public: - UTF8Buffer(); - ~UTF8Buffer(); - - inline void AddChar(uc32 c) { - if (static_cast<unsigned>(c) <= unibrow::Utf8::kMaxOneByteChar) { - buffer_.Add(static_cast<char>(c)); - } else { - AddCharSlow(c); - } - } - - void StartLiteral() { - buffer_.StartSequence(); - } - - Vector<const char> EndLiteral() { - buffer_.Add(kEndMarker); - Vector<char> sequence = buffer_.EndSequence(); - return Vector<const char>(sequence.start(), sequence.length()); - } - - void DropLiteral() { - buffer_.DropSequence(); - } - - void Reset() { - buffer_.Reset(); - } - - // The end marker added after a parsed literal. - // Using zero allows the usage of strlen and similar functions on - // identifiers and numbers (but not strings, since they may contain zero - // bytes). - // TODO(lrn): Use '\xff' as end marker, since it cannot occur inside - // an utf-8 string. This requires changes in all places that uses - // str-functions on the literals, but allows a single pointer to represent - // the literal, even if it contains embedded zeros. - static const char kEndMarker = '\x00'; - private: - static const int kInitialCapacity = 256; - SequenceCollector<char, 4> buffer_; - - void AddCharSlow(uc32 c); -}; - - -// Interface through which the scanner reads characters from the input source. -class UTF16Buffer { - public: - UTF16Buffer(); - virtual ~UTF16Buffer() {} - - virtual void PushBack(uc32 ch) = 0; - // Returns a value < 0 when the buffer end is reached. - virtual uc32 Advance() = 0; - virtual void SeekForward(int pos) = 0; - - int pos() const { return pos_; } - - protected: - int pos_; // Current position in the buffer. - int end_; // Position where scanning should stop (EOF). -}; - - // UTF16 buffer to read characters from a character stream. class CharacterStreamUTF16Buffer: public UTF16Buffer { public: @@ -143,182 +75,65 @@ class ExternalStringUTF16Buffer: public UTF16Buffer { }; -enum ParserLanguage { JAVASCRIPT, JSON }; - - -class Scanner { +// Initializes a UTF16Buffer as input stream, using one of a number +// of strategies depending on the available character sources. +class StreamInitializer { public: - typedef unibrow::Utf8InputBuffer<1024> Utf8Decoder; + UTF16Buffer* Init(Handle<String> source, + unibrow::CharacterStream* stream, + int start_position, + int end_position); + private: + // Different UTF16 buffers used to pull characters from. Based on input one of + // these will be initialized as the actual data source. + CharacterStreamUTF16Buffer char_stream_buffer_; + ExternalStringUTF16Buffer<ExternalTwoByteString, uint16_t> + two_byte_string_buffer_; + ExternalStringUTF16Buffer<ExternalAsciiString, char> ascii_string_buffer_; + + // Used to convert the source string into a character stream when a stream + // is not passed to the scanner. + SafeStringInputBuffer safe_string_input_buffer_; +}; - class LiteralScope { - public: - explicit LiteralScope(Scanner* self); - ~LiteralScope(); - void Complete(); +// ---------------------------------------------------------------------------- +// V8JavaScriptScanner +// JavaScript scanner getting its input from either a V8 String or a unicode +// CharacterStream. - private: - Scanner* scanner_; - bool complete_; - }; +class V8JavaScriptScanner : public JavaScriptScanner { + public: + V8JavaScriptScanner() {} - Scanner(); + Token::Value NextCheckStack(); // Initialize the Scanner to scan source. - void Initialize(Handle<String> source, - ParserLanguage language); + void Initialize(Handle<String> source, int literal_flags = kAllLiterals); void Initialize(Handle<String> source, unibrow::CharacterStream* stream, - ParserLanguage language); + int literal_flags = kAllLiterals); void Initialize(Handle<String> source, int start_position, int end_position, - ParserLanguage language); - - // Returns the next token. - Token::Value Next(); - - // Returns the current token again. - Token::Value current_token() { return current_.token; } - - // One token look-ahead (past the token returned by Next()). - Token::Value peek() const { return next_.token; } + int literal_flags = kAllLiterals); - // Returns true if there was a line terminator before the peek'ed token. - bool has_line_terminator_before_next() const { - return has_line_terminator_before_next_; - } - - struct Location { - Location(int b, int e) : beg_pos(b), end_pos(e) { } - Location() : beg_pos(0), end_pos(0) { } - int beg_pos; - int end_pos; - }; - - // Returns the location information for the current token - // (the token returned by Next()). - Location location() const { return current_.location; } - Location peek_location() const { return next_.location; } - - // Returns the literal string, if any, for the current token (the - // token returned by Next()). The string is 0-terminated and in - // UTF-8 format; they may contain 0-characters. Literal strings are - // collected for identifiers, strings, and numbers. - // These functions only give the correct result if the literal - // was scanned between calls to StartLiteral() and TerminateLiteral(). - const char* literal_string() const { - return current_.literal_chars.start(); - } - - int literal_length() const { - // Excluding terminal '\x00' added by TerminateLiteral(). - return current_.literal_chars.length() - 1; - } - - Vector<const char> literal() const { - return Vector<const char>(literal_string(), literal_length()); - } - - // Returns the literal string for the next token (the token that - // would be returned if Next() were called). - const char* next_literal_string() const { - return next_.literal_chars.start(); - } - - - // Returns the length of the next token (that would be returned if - // Next() were called). - int next_literal_length() const { - // Excluding terminal '\x00' added by TerminateLiteral(). - return next_.literal_chars.length() - 1; - } - - Vector<const char> next_literal() const { - return Vector<const char>(next_literal_string(), next_literal_length()); - } - - // Scans the input as a regular expression pattern, previous - // character(s) must be /(=). Returns true if a pattern is scanned. - bool ScanRegExpPattern(bool seen_equal); - // Returns true if regexp flags are scanned (always since flags can - // be empty). - bool ScanRegExpFlags(); - - // Seek forward to the given position. This operation does not - // work in general, for instance when there are pushed back - // characters, but works for seeking forward until simple delimiter - // tokens, which is what it is used for. - void SeekForward(int pos); - - bool stack_overflow() { return stack_overflow_; } - - static StaticResource<Utf8Decoder>* utf8_decoder() { return &utf8_decoder_; } - - // Tells whether the buffer contains an identifier (no escapes). - // Used for checking if a property name is an identifier. - static bool IsIdentifier(unibrow::CharacterStream* buffer); + protected: + StreamInitializer stream_initializer_; +}; - static unibrow::Predicate<IdentifierStart, 128> kIsIdentifierStart; - static unibrow::Predicate<IdentifierPart, 128> kIsIdentifierPart; - static unibrow::Predicate<unibrow::LineTerminator, 128> kIsLineTerminator; - static unibrow::Predicate<unibrow::WhiteSpace, 128> kIsWhiteSpace; - static const int kCharacterLookaheadBufferSize = 1; - static const int kNoEndPosition = 1; +class JsonScanner : public Scanner { + public: + JsonScanner(); - private: - // The current and look-ahead token. - struct TokenDesc { - Token::Value token; - Location location; - Vector<const char> literal_chars; - }; - - void Init(Handle<String> source, - unibrow::CharacterStream* stream, - int start_position, int end_position, - ParserLanguage language); - - // Literal buffer support - inline void StartLiteral(); - inline void AddChar(uc32 ch); - inline void AddCharAdvance(); - inline void TerminateLiteral(); - // Stops scanning of a literal, e.g., due to an encountered error. - inline void DropLiteral(); - - // Low-level scanning support. - void Advance() { c0_ = source_->Advance(); } - void PushBack(uc32 ch) { - source_->PushBack(ch); - c0_ = ch; - } + // Initialize the Scanner to scan source. + void Initialize(Handle<String> source); - bool SkipWhiteSpace() { - if (is_parsing_json_) { - return SkipJsonWhiteSpace(); - } else { - return SkipJavaScriptWhiteSpace(); - } - } + // Returns the next token. + Token::Value Next(); - bool SkipJavaScriptWhiteSpace(); + protected: + // Skip past JSON whitespace (only space, tab, newline and carrige-return). bool SkipJsonWhiteSpace(); - Token::Value SkipSingleLineComment(); - Token::Value SkipMultiLineComment(); - - inline Token::Value Select(Token::Value tok); - inline Token::Value Select(uc32 next, Token::Value then, Token::Value else_); - - inline void Scan() { - if (is_parsing_json_) { - ScanJson(); - } else { - ScanJavaScript(); - } - } - - // Scans a single JavaScript token. - void ScanJavaScript(); // Scan a single JSON token. The JSON lexical grammar is specified in the // ECMAScript 5 standard, section 15.12.1.1. @@ -347,55 +162,58 @@ class Scanner { // JSONNullLiteral). Token::Value ScanJsonIdentifier(const char* text, Token::Value token); - void ScanDecimalDigits(); - Token::Value ScanNumber(bool seen_period); - Token::Value ScanIdentifier(); - uc32 ScanHexEscape(uc32 c, int length); - uc32 ScanOctalEscape(uc32 c, int length); - void ScanEscape(); - Token::Value ScanString(); + StreamInitializer stream_initializer_; +}; - // Scans a possible HTML comment -- begins with '<!'. - Token::Value ScanHtmlComment(); - // Return the current source position. - int source_pos() { - return source_->pos() - kCharacterLookaheadBufferSize; - } +// ExternalStringUTF16Buffer +template <typename StringType, typename CharType> +ExternalStringUTF16Buffer<StringType, CharType>::ExternalStringUTF16Buffer() + : raw_data_(NULL) { } - // Decodes a unicode escape-sequence which is part of an identifier. - // If the escape sequence cannot be decoded the result is kBadRune. - uc32 ScanIdentifierUnicodeEscape(); - TokenDesc current_; // desc for current token (as returned by Next()) - TokenDesc next_; // desc for next token (one token look-ahead) - bool has_line_terminator_before_next_; - bool is_parsing_json_; +template <typename StringType, typename CharType> +void ExternalStringUTF16Buffer<StringType, CharType>::Initialize( + Handle<StringType> data, + int start_position, + int end_position) { + ASSERT(!data.is_null()); + raw_data_ = data->resource()->data(); - // Different UTF16 buffers used to pull characters from. Based on input one of - // these will be initialized as the actual data source. - CharacterStreamUTF16Buffer char_stream_buffer_; - ExternalStringUTF16Buffer<ExternalTwoByteString, uint16_t> - two_byte_string_buffer_; - ExternalStringUTF16Buffer<ExternalAsciiString, char> ascii_string_buffer_; + ASSERT(end_position <= data->length()); + if (start_position > 0) { + SeekForward(start_position); + } + end_ = + end_position != kNoEndPosition ? end_position : data->length(); +} - // Source. Will point to one of the buffers declared above. - UTF16Buffer* source_; - // Used to convert the source string into a character stream when a stream - // is not passed to the scanner. - SafeStringInputBuffer safe_string_input_buffer_; +template <typename StringType, typename CharType> +uc32 ExternalStringUTF16Buffer<StringType, CharType>::Advance() { + if (pos_ < end_) { + return raw_data_[pos_++]; + } else { + // note: currently the following increment is necessary to avoid a + // test-parser problem! + pos_++; + return static_cast<uc32>(-1); + } +} + - // Buffer to hold literal values (identifiers, strings, numbers) - // using '\x00'-terminated UTF-8 encoding. Handles allocation internally. - UTF8Buffer literal_buffer_; +template <typename StringType, typename CharType> +void ExternalStringUTF16Buffer<StringType, CharType>::PushBack(uc32 ch) { + pos_--; + ASSERT(pos_ >= Scanner::kCharacterLookaheadBufferSize); + ASSERT(raw_data_[pos_ - Scanner::kCharacterLookaheadBufferSize] == ch); +} - bool stack_overflow_; - static StaticResource<Utf8Decoder> utf8_decoder_; - // One Unicode character look-ahead; c0_ < 0 at the end of the input. - uc32 c0_; -}; +template <typename StringType, typename CharType> +void ExternalStringUTF16Buffer<StringType, CharType>::SeekForward(int pos) { + pos_ = pos; +} } } // namespace v8::internal diff --git a/src/serialize.cc b/src/serialize.cc index 763c12f4..15fed442 100644 --- a/src/serialize.cc +++ b/src/serialize.cc @@ -290,10 +290,6 @@ void ExternalReferenceTable::PopulateTable() { Add(Top::get_address_from_id((Top::AddressId)i), TOP_ADDRESS, i, chars); } - // Extensions - Add(FUNCTION_ADDR(GCExtension::GC), EXTENSION, 1, - "GCExtension::GC"); - // Accessors #define ACCESSOR_DESCRIPTOR_DECLARATION(name) \ Add((Address)&Accessors::name, \ diff --git a/src/spaces.cc b/src/spaces.cc index 2f3e41ae..239c9cd6 100644 --- a/src/spaces.cc +++ b/src/spaces.cc @@ -1898,6 +1898,18 @@ MaybeObject* OldSpaceFreeList::Allocate(int size_in_bytes, int* wasted_bytes) { } +void OldSpaceFreeList::MarkNodes() { + for (int i = 0; i < kFreeListsLength; i++) { + Address cur_addr = free_[i].head_node_; + while (cur_addr != NULL) { + FreeListNode* cur_node = FreeListNode::FromAddress(cur_addr); + cur_addr = cur_node->next(); + cur_node->SetMark(); + } + } +} + + #ifdef DEBUG bool OldSpaceFreeList::Contains(FreeListNode* node) { for (int i = 0; i < kFreeListsLength; i++) { @@ -1957,6 +1969,16 @@ MaybeObject* FixedSizeFreeList::Allocate() { } +void FixedSizeFreeList::MarkNodes() { + Address cur_addr = head_; + while (cur_addr != NULL && cur_addr != tail_) { + FreeListNode* cur_node = FreeListNode::FromAddress(cur_addr); + cur_addr = cur_node->next(); + cur_node->SetMark(); + } +} + + // ----------------------------------------------------------------------------- // OldSpace implementation @@ -2711,13 +2733,15 @@ LargeObjectSpace::LargeObjectSpace(AllocationSpace id) : Space(id, NOT_EXECUTABLE), // Managed on a per-allocation basis first_chunk_(NULL), size_(0), - page_count_(0) {} + page_count_(0), + objects_size_(0) {} bool LargeObjectSpace::Setup() { first_chunk_ = NULL; size_ = 0; page_count_ = 0; + objects_size_ = 0; return true; } @@ -2740,6 +2764,7 @@ void LargeObjectSpace::TearDown() { size_ = 0; page_count_ = 0; + objects_size_ = 0; } @@ -2786,6 +2811,7 @@ MaybeObject* LargeObjectSpace::AllocateRawInternal(int requested_size, } size_ += static_cast<int>(chunk_size); + objects_size_ += requested_size; page_count_++; chunk->set_next(first_chunk_); chunk->set_size(chunk_size); @@ -2948,6 +2974,7 @@ void LargeObjectSpace::FreeUnmarkedObjects() { // Free the chunk. MarkCompactCollector::ReportDeleteIfNeeded(object); size_ -= static_cast<int>(chunk_size); + objects_size_ -= object->Size(); page_count_--; ObjectSpace space = kObjectSpaceLoSpace; if (executable == EXECUTABLE) space = kObjectSpaceCodeSpace; @@ -3052,7 +3079,8 @@ void LargeObjectSpace::ReportStatistics() { CollectHistogramInfo(obj); } - PrintF(" number of objects %d\n", num_objects); + PrintF(" number of objects %d, " + "size of objects %" V8_PTR_PREFIX "d\n", num_objects, objects_size_); if (num_objects > 0) ReportHistogram(false); } diff --git a/src/spaces.h b/src/spaces.h index 0c10d2c7..60068c3d 100644 --- a/src/spaces.h +++ b/src/spaces.h @@ -371,8 +371,13 @@ class Space : public Malloced { // Identity used in error reporting. AllocationSpace identity() { return id_; } + // Returns allocated size. virtual intptr_t Size() = 0; + // Returns size of objects. Can differ from the allocated size + // (e.g. see LargeObjectSpace). + virtual intptr_t SizeOfObjects() { return Size(); } + #ifdef ENABLE_HEAP_PROTECTION // Protect/unprotect the space by marking it read-only/writable. virtual void Protect() = 0; @@ -591,7 +596,7 @@ class MemoryAllocator : public AllStatic { static intptr_t Size() { return size_; } // Returns the maximum available executable bytes of heaps. - static int AvailableExecutable() { + static intptr_t AvailableExecutable() { if (capacity_executable_ < size_executable_) return 0; return capacity_executable_ - size_executable_; } @@ -1715,6 +1720,8 @@ class OldSpaceFreeList BASE_EMBEDDED { // 'wasted_bytes'. The size should be a non-zero multiple of the word size. MUST_USE_RESULT MaybeObject* Allocate(int size_in_bytes, int* wasted_bytes); + void MarkNodes(); + private: // The size range of blocks, in bytes. (Smaller allocations are allowed, but // will always result in waste.) @@ -1813,6 +1820,8 @@ class FixedSizeFreeList BASE_EMBEDDED { // A failure is returned if no block is available. MUST_USE_RESULT MaybeObject* Allocate(); + void MarkNodes(); + private: // Available bytes on the free list. intptr_t available_; @@ -1884,6 +1893,8 @@ class OldSpace : public PagedSpace { virtual void PutRestOfCurrentPageOnFreeList(Page* current_page); + void MarkFreeListNodes() { free_list_.MarkNodes(); } + #ifdef DEBUG // Reports statistics for the space void ReportStatistics(); @@ -1951,6 +1962,9 @@ class FixedSpace : public PagedSpace { virtual void DeallocateBlock(Address start, int size_in_bytes, bool add_to_freelist); + + void MarkFreeListNodes() { free_list_.MarkNodes(); } + #ifdef DEBUG // Reports statistic info of the space void ReportStatistics(); @@ -2191,6 +2205,10 @@ class LargeObjectSpace : public Space { return size_; } + virtual intptr_t SizeOfObjects() { + return objects_size_; + } + int PageCount() { return page_count_; } @@ -2242,7 +2260,7 @@ class LargeObjectSpace : public Space { LargeObjectChunk* first_chunk_; intptr_t size_; // allocated bytes int page_count_; // number of chunks - + intptr_t objects_size_; // size of objects // Shared implementation of AllocateRaw, AllocateRawCode and // AllocateRawFixedArray. diff --git a/src/string.js b/src/string.js index d82ce052..3b3c82bb 100644 --- a/src/string.js +++ b/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/src/strtod.cc b/src/strtod.cc index 0523d885..cedbff91 100644 --- a/src/strtod.cc +++ b/src/strtod.cc @@ -422,9 +422,9 @@ double Strtod(Vector<const char> buffer, int exponent) { int significant_exponent; TrimToMaxSignificantDigits(trimmed, exponent, significant_buffer, &significant_exponent); - trimmed = - Vector<const char>(significant_buffer, kMaxSignificantDecimalDigits); - exponent = significant_exponent; + return Strtod(Vector<const char>(significant_buffer, + kMaxSignificantDecimalDigits), + significant_exponent); } if (exponent + trimmed.length() - 1 >= kMaxDecimalPower) return V8_INFINITY; if (exponent + trimmed.length() <= kMinDecimalPower) return 0.0; diff --git a/src/stub-cache.cc b/src/stub-cache.cc index e794f093..5cc009f7 100644 --- a/src/stub-cache.cc +++ b/src/stub-cache.cc @@ -820,6 +820,34 @@ MaybeObject* StubCache::ComputeCallInitialize(int argc, } +Handle<Code> StubCache::ComputeCallInitialize(int argc, InLoopFlag in_loop) { + if (in_loop == IN_LOOP) { + // Force the creation of the corresponding stub outside loops, + // because it may be used when clearing the ICs later - it is + // possible for a series of IC transitions to lose the in-loop + // information, and the IC clearing code can't generate a stub + // that it needs so we need to ensure it is generated already. + ComputeCallInitialize(argc, NOT_IN_LOOP); + } + CALL_HEAP_FUNCTION(ComputeCallInitialize(argc, in_loop, Code::CALL_IC), Code); +} + + +Handle<Code> StubCache::ComputeKeyedCallInitialize(int argc, + InLoopFlag in_loop) { + if (in_loop == IN_LOOP) { + // Force the creation of the corresponding stub outside loops, + // because it may be used when clearing the ICs later - it is + // possible for a series of IC transitions to lose the in-loop + // information, and the IC clearing code can't generate a stub + // that it needs so we need to ensure it is generated already. + ComputeKeyedCallInitialize(argc, NOT_IN_LOOP); + } + CALL_HEAP_FUNCTION( + ComputeCallInitialize(argc, in_loop, Code::KEYED_CALL_IC), Code); +} + + MaybeObject* StubCache::ComputeCallPreMonomorphic(int argc, InLoopFlag in_loop, Code::Kind kind) { @@ -932,14 +960,11 @@ void StubCache::Clear() { MaybeObject* LoadCallbackProperty(Arguments args) { ASSERT(args[0]->IsJSObject()); ASSERT(args[1]->IsJSObject()); - AccessorInfo* callback = AccessorInfo::cast(args[2]); + AccessorInfo* callback = AccessorInfo::cast(args[3]); Address getter_address = v8::ToCData<Address>(callback->getter()); v8::AccessorGetter fun = FUNCTION_CAST<v8::AccessorGetter>(getter_address); ASSERT(fun != NULL); - CustomArguments custom_args(callback->data(), - JSObject::cast(args[0]), - JSObject::cast(args[1])); - v8::AccessorInfo info(custom_args.end()); + v8::AccessorInfo info(&args[0]); HandleScope scope; v8::Handle<v8::Value> result; { diff --git a/src/stub-cache.h b/src/stub-cache.h index 9d947e40..cef5481c 100644 --- a/src/stub-cache.h +++ b/src/stub-cache.h @@ -204,6 +204,10 @@ class StubCache : public AllStatic { InLoopFlag in_loop, Code::Kind kind); + static Handle<Code> ComputeCallInitialize(int argc, InLoopFlag in_loop); + + static Handle<Code> ComputeKeyedCallInitialize(int argc, InLoopFlag in_loop); + MUST_USE_RESULT static MaybeObject* ComputeCallPreMonomorphic( int argc, InLoopFlag in_loop, @@ -736,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/src/third_party/dtoa/COPYING b/src/third_party/dtoa/COPYING deleted file mode 100644 index c991754d..00000000 --- a/src/third_party/dtoa/COPYING +++ /dev/null @@ -1,15 +0,0 @@ -The author of this software is David M. Gay. - -Copyright (c) 1991, 2000, 2001 by Lucent Technologies. - -Permission to use, copy, modify, and distribute this software for any -purpose without fee is hereby granted, provided that this entire -notice is included in all copies of any software which is or includes -a copy or modification of this software and in all copies of the -supporting documentation for such software. - -THIS SOFTWARE IS BEING PROVIDED "AS IS", WITHOUT ANY EXPRESS OR -IMPLIED WARRANTY. IN PARTICULAR, NEITHER THE AUTHOR NOR LUCENT MAKES -ANY REPRESENTATION OR WARRANTY OF ANY KIND CONCERNING THE -MERCHANTABILITY OF THIS SOFTWARE OR ITS FITNESS FOR ANY PARTICULAR -PURPOSE. diff --git a/src/third_party/dtoa/dtoa.c b/src/third_party/dtoa/dtoa.c deleted file mode 100644 index 068ed949..00000000 --- a/src/third_party/dtoa/dtoa.c +++ /dev/null @@ -1,3331 +0,0 @@ -/**************************************************************** - * - * The author of this software is David M. Gay. - * - * Copyright (c) 1991, 2000, 2001 by Lucent Technologies. - * - * Permission to use, copy, modify, and distribute this software for any - * purpose without fee is hereby granted, provided that this entire notice - * is included in all copies of any software which is or includes a copy - * or modification of this software and in all copies of the supporting - * documentation for such software. - * - * THIS SOFTWARE IS BEING PROVIDED "AS IS", WITHOUT ANY EXPRESS OR IMPLIED - * WARRANTY. IN PARTICULAR, NEITHER THE AUTHOR NOR LUCENT MAKES ANY - * REPRESENTATION OR WARRANTY OF ANY KIND CONCERNING THE MERCHANTABILITY - * OF THIS SOFTWARE OR ITS FITNESS FOR ANY PARTICULAR PURPOSE. - * - ***************************************************************/ - -/* Please send bug reports to David M. Gay (dmg at acm dot org, - * with " at " changed at "@" and " dot " changed to "."). */ - -/* On a machine with IEEE extended-precision registers, it is - * necessary to specify double-precision (53-bit) rounding precision - * before invoking strtod or dtoa. If the machine uses (the equivalent - * of) Intel 80x87 arithmetic, the call - * _control87(PC_53, MCW_PC); - * does this with many compilers. Whether this or another call is - * appropriate depends on the compiler; for this to work, it may be - * necessary to #include "float.h" or another system-dependent header - * file. - */ - -/* strtod for IEEE-, VAX-, and IBM-arithmetic machines. - * - * This strtod returns a nearest machine number to the input decimal - * string (or sets errno to ERANGE). With IEEE arithmetic, ties are - * broken by the IEEE round-even rule. Otherwise ties are broken by - * biased rounding (add half and chop). - * - * Inspired loosely by William D. Clinger's paper "How to Read Floating - * Point Numbers Accurately" [Proc. ACM SIGPLAN '90, pp. 92-101]. - * - * Modifications: - * - * 1. We only require IEEE, IBM, or VAX double-precision - * arithmetic (not IEEE double-extended). - * 2. We get by with floating-point arithmetic in a case that - * Clinger missed -- when we're computing d * 10^n - * for a small integer d and the integer n is not too - * much larger than 22 (the maximum integer k for which - * we can represent 10^k exactly), we may be able to - * compute (d*10^k) * 10^(e-k) with just one roundoff. - * 3. Rather than a bit-at-a-time adjustment of the binary - * result in the hard case, we use floating-point - * arithmetic to determine the adjustment to within - * one bit; only in really hard cases do we need to - * compute a second residual. - * 4. Because of 3., we don't need a large table of powers of 10 - * for ten-to-e (just some small tables, e.g. of 10^k - * for 0 <= k <= 22). - */ - -/* - * #define IEEE_8087 for IEEE-arithmetic machines where the least - * significant byte has the lowest address. - * #define IEEE_MC68k for IEEE-arithmetic machines where the most - * significant byte has the lowest address. - * #define Long int on machines with 32-bit ints and 64-bit longs. - * #define IBM for IBM mainframe-style floating-point arithmetic. - * #define VAX for VAX-style floating-point arithmetic (D_floating). - * #define No_leftright to omit left-right logic in fast floating-point - * computation of dtoa. - * #define Honor_FLT_ROUNDS if FLT_ROUNDS can assume the values 2 or 3 - * and strtod and dtoa should round accordingly. - * #define Check_FLT_ROUNDS if FLT_ROUNDS can assume the values 2 or 3 - * and Honor_FLT_ROUNDS is not #defined. - * #define RND_PRODQUOT to use rnd_prod and rnd_quot (assembly routines - * that use extended-precision instructions to compute rounded - * products and quotients) with IBM. - * #define ROUND_BIASED for IEEE-format with biased rounding. - * #define Inaccurate_Divide for IEEE-format with correctly rounded - * products but inaccurate quotients, e.g., for Intel i860. - * #define NO_LONG_LONG on machines that do not have a "long long" - * integer type (of >= 64 bits). On such machines, you can - * #define Just_16 to store 16 bits per 32-bit Long when doing - * high-precision integer arithmetic. Whether this speeds things - * up or slows things down depends on the machine and the number - * being converted. If long long is available and the name is - * something other than "long long", #define Llong to be the name, - * and if "unsigned Llong" does not work as an unsigned version of - * Llong, #define #ULLong to be the corresponding unsigned type. - * #define KR_headers for old-style C function headers. - * #define Bad_float_h if your system lacks a float.h or if it does not - * define some or all of DBL_DIG, DBL_MAX_10_EXP, DBL_MAX_EXP, - * FLT_RADIX, FLT_ROUNDS, and DBL_MAX. - * #define MALLOC your_malloc, where your_malloc(n) acts like malloc(n) - * if memory is available and otherwise does something you deem - * appropriate. If MALLOC is undefined, malloc will be invoked - * directly -- and assumed always to succeed. - * #define Omit_Private_Memory to omit logic (added Jan. 1998) for making - * memory allocations from a private pool of memory when possible. - * When used, the private pool is PRIVATE_MEM bytes long: 2304 bytes, - * unless #defined to be a different length. This default length - * suffices to get rid of MALLOC calls except for unusual cases, - * such as decimal-to-binary conversion of a very long string of - * digits. The longest string dtoa can return is about 751 bytes - * long. For conversions by strtod of strings of 800 digits and - * all dtoa conversions in single-threaded executions with 8-byte - * pointers, PRIVATE_MEM >= 7400 appears to suffice; with 4-byte - * pointers, PRIVATE_MEM >= 7112 appears adequate. - * #define INFNAN_CHECK on IEEE systems to cause strtod to check for - * Infinity and NaN (case insensitively). On some systems (e.g., - * some HP systems), it may be necessary to #define NAN_WORD0 - * appropriately -- to the most significant word of a quiet NaN. - * (On HP Series 700/800 machines, -DNAN_WORD0=0x7ff40000 works.) - * When INFNAN_CHECK is #defined and No_Hex_NaN is not #defined, - * strtod also accepts (case insensitively) strings of the form - * NaN(x), where x is a string of hexadecimal digits and spaces; - * if there is only one string of hexadecimal digits, it is taken - * for the 52 fraction bits of the resulting NaN; if there are two - * or more strings of hex digits, the first is for the high 20 bits, - * the second and subsequent for the low 32 bits, with intervening - * white space ignored; but if this results in none of the 52 - * fraction bits being on (an IEEE Infinity symbol), then NAN_WORD0 - * and NAN_WORD1 are used instead. - * #define MULTIPLE_THREADS if the system offers preemptively scheduled - * multiple threads. In this case, you must provide (or suitably - * #define) two locks, acquired by ACQUIRE_DTOA_LOCK(n) and freed - * by FREE_DTOA_LOCK(n) for n = 0 or 1. (The second lock, accessed - * in pow5mult, ensures lazy evaluation of only one copy of high - * powers of 5; omitting this lock would introduce a small - * probability of wasting memory, but would otherwise be harmless.) - * You must also invoke freedtoa(s) to free the value s returned by - * dtoa. You may do so whether or not MULTIPLE_THREADS is #defined. - * #define NO_IEEE_Scale to disable new (Feb. 1997) logic in strtod that - * avoids underflows on inputs whose result does not underflow. - * If you #define NO_IEEE_Scale on a machine that uses IEEE-format - * floating-point numbers and flushes underflows to zero rather - * than implementing gradual underflow, then you must also #define - * Sudden_Underflow. - * #define YES_ALIAS to permit aliasing certain double values with - * arrays of ULongs. This leads to slightly better code with - * some compilers and was always used prior to 19990916, but it - * is not strictly legal and can cause trouble with aggressively - * optimizing compilers (e.g., gcc 2.95.1 under -O2). - * #define USE_LOCALE to use the current locale's decimal_point value. - * #define SET_INEXACT if IEEE arithmetic is being used and extra - * computation should be done to set the inexact flag when the - * result is inexact and avoid setting inexact when the result - * is exact. In this case, dtoa.c must be compiled in - * an environment, perhaps provided by #include "dtoa.c" in a - * suitable wrapper, that defines two functions, - * int get_inexact(void); - * void clear_inexact(void); - * such that get_inexact() returns a nonzero value if the - * inexact bit is already set, and clear_inexact() sets the - * inexact bit to 0. When SET_INEXACT is #defined, strtod - * also does extra computations to set the underflow and overflow - * flags when appropriate (i.e., when the result is tiny and - * inexact or when it is a numeric value rounded to +-infinity). - * #define NO_ERRNO if strtod should not assign errno = ERANGE when - * the result overflows to +-Infinity or underflows to 0. - */ - -#ifndef Long -#if __LP64__ -#define Long int -#else -#define Long long -#endif -#endif -#ifndef ULong -typedef unsigned Long ULong; -#endif - -#ifdef DEBUG -#include "stdio.h" -#define Bug(x) {fprintf(stderr, "%s\n", x); exit(1);} -#endif - -#include "stdlib.h" -#include "string.h" - -#ifdef USE_LOCALE -#include "locale.h" -#endif - -#ifdef MALLOC -#ifdef KR_headers -extern char *MALLOC(); -#else -extern void *MALLOC(size_t); -#endif -#else -#define MALLOC malloc -#endif - -#ifndef Omit_Private_Memory -#ifndef PRIVATE_MEM -#define PRIVATE_MEM 2304 -#endif -#define PRIVATE_mem ((PRIVATE_MEM+sizeof(double)-1)/sizeof(double)) -static double private_mem[PRIVATE_mem], *pmem_next = private_mem; -#endif - -#undef IEEE_Arith -#undef Avoid_Underflow -#ifdef IEEE_MC68k -#define IEEE_Arith -#endif -#ifdef IEEE_8087 -#define IEEE_Arith -#endif - -#include "errno.h" - -#ifdef Bad_float_h - -#ifdef IEEE_Arith -#define DBL_DIG 15 -#define DBL_MAX_10_EXP 308 -#define DBL_MAX_EXP 1024 -#define FLT_RADIX 2 -#endif /*IEEE_Arith*/ - -#ifdef IBM -#define DBL_DIG 16 -#define DBL_MAX_10_EXP 75 -#define DBL_MAX_EXP 63 -#define FLT_RADIX 16 -#define DBL_MAX 7.2370055773322621e+75 -#endif - -#ifdef VAX -#define DBL_DIG 16 -#define DBL_MAX_10_EXP 38 -#define DBL_MAX_EXP 127 -#define FLT_RADIX 2 -#define DBL_MAX 1.7014118346046923e+38 -#endif - -#ifndef LONG_MAX -#define LONG_MAX 2147483647 -#endif - -#else /* ifndef Bad_float_h */ -#include "float.h" -#endif /* Bad_float_h */ - -#ifndef __MATH_H__ -#include "math.h" -#endif - -#ifdef __cplusplus -extern "C" { -#endif - -#ifndef CONST -#ifdef KR_headers -#define CONST /* blank */ -#else -#define CONST const -#endif -#endif - -#if defined(IEEE_8087) + defined(IEEE_MC68k) + defined(VAX) + defined(IBM) != 1 -Exactly one of IEEE_8087, IEEE_MC68k, VAX, or IBM should be defined. -#endif - -typedef union { double d; ULong L[2]; } U; - -#ifdef IEEE_8087 -#define word0(x) (x).L[1] -#define word1(x) (x).L[0] -#else -#define word0(x) (x).L[0] -#define word1(x) (x).L[1] -#endif -#define dval(x) (x).d - -/* The following definition of Storeinc is appropriate for MIPS processors. - * An alternative that might be better on some machines is - * #define Storeinc(a,b,c) (*a++ = b << 16 | c & 0xffff) - */ -#if defined(IEEE_8087) + defined(VAX) -#define Storeinc(a,b,c) (((unsigned short *)a)[1] = (unsigned short)b, \ -((unsigned short *)a)[0] = (unsigned short)c, a++) -#else -#define Storeinc(a,b,c) (((unsigned short *)a)[0] = (unsigned short)b, \ -((unsigned short *)a)[1] = (unsigned short)c, a++) -#endif - -/* #define P DBL_MANT_DIG */ -/* Ten_pmax = floor(P*log(2)/log(5)) */ -/* Bletch = (highest power of 2 < DBL_MAX_10_EXP) / 16 */ -/* Quick_max = floor((P-1)*log(FLT_RADIX)/log(10) - 1) */ -/* Int_max = floor(P*log(FLT_RADIX)/log(10) - 1) */ - -#ifdef IEEE_Arith -#define Exp_shift 20 -#define Exp_shift1 20 -#define Exp_msk1 0x100000 -#define Exp_msk11 0x100000 -#define Exp_mask 0x7ff00000 -#define P 53 -#define Bias 1023 -#define Emin (-1022) -#define Exp_1 0x3ff00000 -#define Exp_11 0x3ff00000 -#define Ebits 11 -#define Frac_mask 0xfffff -#define Frac_mask1 0xfffff -#define Ten_pmax 22 -#define Bletch 0x10 -#define Bndry_mask 0xfffff -#define Bndry_mask1 0xfffff -#define LSB 1 -#define Sign_bit 0x80000000 -#define Log2P 1 -#define Tiny0 0 -#define Tiny1 1 -#define Quick_max 14 -#define Int_max 14 -#ifndef NO_IEEE_Scale -#define Avoid_Underflow -#ifdef Flush_Denorm /* debugging option */ -#undef Sudden_Underflow -#endif -#endif - -#ifndef Flt_Rounds -#ifdef FLT_ROUNDS -#define Flt_Rounds FLT_ROUNDS -#else -#define Flt_Rounds 1 -#endif -#endif /*Flt_Rounds*/ - -#ifdef Honor_FLT_ROUNDS -#define Rounding rounding -#undef Check_FLT_ROUNDS -#define Check_FLT_ROUNDS -#else -#define Rounding Flt_Rounds -#endif - -#else /* ifndef IEEE_Arith */ -#undef Check_FLT_ROUNDS -#undef Honor_FLT_ROUNDS -#undef SET_INEXACT -#undef Sudden_Underflow -#define Sudden_Underflow -#ifdef IBM -#undef Flt_Rounds -#define Flt_Rounds 0 -#define Exp_shift 24 -#define Exp_shift1 24 -#define Exp_msk1 0x1000000 -#define Exp_msk11 0x1000000 -#define Exp_mask 0x7f000000 -#define P 14 -#define Bias 65 -#define Exp_1 0x41000000 -#define Exp_11 0x41000000 -#define Ebits 8 /* exponent has 7 bits, but 8 is the right value in b2d */ -#define Frac_mask 0xffffff -#define Frac_mask1 0xffffff -#define Bletch 4 -#define Ten_pmax 22 -#define Bndry_mask 0xefffff -#define Bndry_mask1 0xffffff -#define LSB 1 -#define Sign_bit 0x80000000 -#define Log2P 4 -#define Tiny0 0x100000 -#define Tiny1 0 -#define Quick_max 14 -#define Int_max 15 -#else /* VAX */ -#undef Flt_Rounds -#define Flt_Rounds 1 -#define Exp_shift 23 -#define Exp_shift1 7 -#define Exp_msk1 0x80 -#define Exp_msk11 0x800000 -#define Exp_mask 0x7f80 -#define P 56 -#define Bias 129 -#define Exp_1 0x40800000 -#define Exp_11 0x4080 -#define Ebits 8 -#define Frac_mask 0x7fffff -#define Frac_mask1 0xffff007f -#define Ten_pmax 24 -#define Bletch 2 -#define Bndry_mask 0xffff007f -#define Bndry_mask1 0xffff007f -#define LSB 0x10000 -#define Sign_bit 0x8000 -#define Log2P 1 -#define Tiny0 0x80 -#define Tiny1 0 -#define Quick_max 15 -#define Int_max 15 -#endif /* IBM, VAX */ -#endif /* IEEE_Arith */ - -#ifndef IEEE_Arith -#define ROUND_BIASED -#endif - -#ifdef RND_PRODQUOT -#define rounded_product(a,b) a = rnd_prod(a, b) -#define rounded_quotient(a,b) a = rnd_quot(a, b) -#ifdef KR_headers -extern double rnd_prod(), rnd_quot(); -#else -extern double rnd_prod(double, double), rnd_quot(double, double); -#endif -#else -#define rounded_product(a,b) a *= b -#define rounded_quotient(a,b) a /= b -#endif - -#define Big0 (Frac_mask1 | Exp_msk1*(DBL_MAX_EXP+Bias-1)) -#define Big1 0xffffffff - -#ifndef Pack_32 -#define Pack_32 -#endif - -#ifdef KR_headers -#define FFFFFFFF ((((unsigned long)0xffff)<<16)|(unsigned long)0xffff) -#else -#define FFFFFFFF 0xffffffffUL -#endif - -#ifdef NO_LONG_LONG -#undef ULLong -#ifdef Just_16 -#undef Pack_32 -/* When Pack_32 is not defined, we store 16 bits per 32-bit Long. - * This makes some inner loops simpler and sometimes saves work - * during multiplications, but it often seems to make things slightly - * slower. Hence the default is now to store 32 bits per Long. - */ -#endif -#else /* long long available */ -#ifndef Llong -#define Llong long long -#endif -#ifndef ULLong -#define ULLong unsigned Llong -#endif -#endif /* NO_LONG_LONG */ - -#ifndef MULTIPLE_THREADS -#define ACQUIRE_DTOA_LOCK(n) /*nothing*/ -#define FREE_DTOA_LOCK(n) /*nothing*/ -#endif - -#define Kmax 15 - -#ifdef __cplusplus -extern "C" double strtod(const char *s00, char **se); -extern "C" char *dtoa(double d, int mode, int ndigits, - int *decpt, int *sign, char **rve); -#endif - - struct -Bigint { - struct Bigint *next; - int k, maxwds, sign, wds; - ULong x[1]; - }; - - typedef struct Bigint Bigint; - - static Bigint *freelist[Kmax+1]; - - static Bigint * -Balloc -#ifdef KR_headers - (k) int k; -#else - (int k) -#endif -{ - int x; - Bigint *rv; -#ifndef Omit_Private_Memory - unsigned int len; -#endif - - ACQUIRE_DTOA_LOCK(0); - /* The k > Kmax case does not need ACQUIRE_DTOA_LOCK(0). */ - /* but this case seems very unlikely. */ - if (k <= Kmax && (rv = freelist[k])) { - freelist[k] = rv->next; - } - else { - x = 1 << k; -#ifdef Omit_Private_Memory - rv = (Bigint *)MALLOC(sizeof(Bigint) + (x-1)*sizeof(ULong)); -#else - len = (sizeof(Bigint) + (x-1)*sizeof(ULong) + sizeof(double) - 1) - /sizeof(double); - if (k <= Kmax && pmem_next - private_mem + len <= PRIVATE_mem) { - rv = (Bigint*)pmem_next; - pmem_next += len; - } - else - rv = (Bigint*)MALLOC(len*sizeof(double)); -#endif - rv->k = k; - rv->maxwds = x; - } - FREE_DTOA_LOCK(0); - rv->sign = rv->wds = 0; - return rv; - } - - static void -Bfree -#ifdef KR_headers - (v) Bigint *v; -#else - (Bigint *v) -#endif -{ - if (v) { - if (v->k > Kmax) - free((void*)v); - else { - ACQUIRE_DTOA_LOCK(0); - v->next = freelist[v->k]; - freelist[v->k] = v; - FREE_DTOA_LOCK(0); - } - } - } - -#define Bcopy(x,y) memcpy((char *)&x->sign, (char *)&y->sign, \ -y->wds*sizeof(Long) + 2*sizeof(int)) - - static Bigint * -multadd -#ifdef KR_headers - (b, m, a) Bigint *b; int m, a; -#else - (Bigint *b, int m, int a) /* multiply by m and add a */ -#endif -{ - int i, wds; -#ifdef ULLong - ULong *x; - ULLong carry, y; -#else - ULong carry, *x, y; -#ifdef Pack_32 - ULong xi, z; -#endif -#endif - Bigint *b1; - - wds = b->wds; - x = b->x; - i = 0; - carry = a; - do { -#ifdef ULLong - y = *x * (ULLong)m + carry; - carry = y >> 32; - *x++ = y & FFFFFFFF; -#else -#ifdef Pack_32 - xi = *x; - y = (xi & 0xffff) * m + carry; - z = (xi >> 16) * m + (y >> 16); - carry = z >> 16; - *x++ = (z << 16) + (y & 0xffff); -#else - y = *x * m + carry; - carry = y >> 16; - *x++ = y & 0xffff; -#endif -#endif - } - while(++i < wds); - if (carry) { - if (wds >= b->maxwds) { - b1 = Balloc(b->k+1); - Bcopy(b1, b); - Bfree(b); - b = b1; - } - b->x[wds++] = carry; - b->wds = wds; - } - return b; - } - - static Bigint * -s2b -#ifdef KR_headers - (s, nd0, nd, y9) CONST char *s; int nd0, nd; ULong y9; -#else - (CONST char *s, int nd0, int nd, ULong y9) -#endif -{ - Bigint *b; - int i, k; - Long x, y; - - x = (nd + 8) / 9; - for(k = 0, y = 1; x > y; y <<= 1, k++) ; -#ifdef Pack_32 - b = Balloc(k); - b->x[0] = y9; - b->wds = 1; -#else - b = Balloc(k+1); - b->x[0] = y9 & 0xffff; - b->wds = (b->x[1] = y9 >> 16) ? 2 : 1; -#endif - - i = 9; - if (9 < nd0) { - s += 9; - do b = multadd(b, 10, *s++ - '0'); - while(++i < nd0); - s++; - } - else - s += 10; - for(; i < nd; i++) - b = multadd(b, 10, *s++ - '0'); - return b; - } - - static int -hi0bits -#ifdef KR_headers - (x) register ULong x; -#else - (register ULong x) -#endif -{ - register int k = 0; - - if (!(x & 0xffff0000)) { - k = 16; - x <<= 16; - } - if (!(x & 0xff000000)) { - k += 8; - x <<= 8; - } - if (!(x & 0xf0000000)) { - k += 4; - x <<= 4; - } - if (!(x & 0xc0000000)) { - k += 2; - x <<= 2; - } - if (!(x & 0x80000000)) { - k++; - if (!(x & 0x40000000)) - return 32; - } - return k; - } - - static int -lo0bits -#ifdef KR_headers - (y) ULong *y; -#else - (ULong *y) -#endif -{ - register int k; - register ULong x = *y; - - if (x & 7) { - if (x & 1) - return 0; - if (x & 2) { - *y = x >> 1; - return 1; - } - *y = x >> 2; - return 2; - } - k = 0; - if (!(x & 0xffff)) { - k = 16; - x >>= 16; - } - if (!(x & 0xff)) { - k += 8; - x >>= 8; - } - if (!(x & 0xf)) { - k += 4; - x >>= 4; - } - if (!(x & 0x3)) { - k += 2; - x >>= 2; - } - if (!(x & 1)) { - k++; - x >>= 1; - if (!x) - return 32; - } - *y = x; - return k; - } - - static Bigint * -i2b -#ifdef KR_headers - (i) int i; -#else - (int i) -#endif -{ - Bigint *b; - - b = Balloc(1); - b->x[0] = i; - b->wds = 1; - return b; - } - - static Bigint * -mult -#ifdef KR_headers - (a, b) Bigint *a, *b; -#else - (Bigint *a, Bigint *b) -#endif -{ - Bigint *c; - int k, wa, wb, wc; - ULong *x, *xa, *xae, *xb, *xbe, *xc, *xc0; - ULong y; -#ifdef ULLong - ULLong carry, z; -#else - ULong carry, z; -#ifdef Pack_32 - ULong z2; -#endif -#endif - - if (a->wds < b->wds) { - c = a; - a = b; - b = c; - } - k = a->k; - wa = a->wds; - wb = b->wds; - wc = wa + wb; - if (wc > a->maxwds) - k++; - c = Balloc(k); - for(x = c->x, xa = x + wc; x < xa; x++) - *x = 0; - xa = a->x; - xae = xa + wa; - xb = b->x; - xbe = xb + wb; - xc0 = c->x; -#ifdef ULLong - for(; xb < xbe; xc0++) { - if ((y = *xb++)) { - x = xa; - xc = xc0; - carry = 0; - do { - z = *x++ * (ULLong)y + *xc + carry; - carry = z >> 32; - *xc++ = z & FFFFFFFF; - } - while(x < xae); - *xc = carry; - } - } -#else -#ifdef Pack_32 - for(; xb < xbe; xb++, xc0++) { - if (y = *xb & 0xffff) { - x = xa; - xc = xc0; - carry = 0; - do { - z = (*x & 0xffff) * y + (*xc & 0xffff) + carry; - carry = z >> 16; - z2 = (*x++ >> 16) * y + (*xc >> 16) + carry; - carry = z2 >> 16; - Storeinc(xc, z2, z); - } - while(x < xae); - *xc = carry; - } - if (y = *xb >> 16) { - x = xa; - xc = xc0; - carry = 0; - z2 = *xc; - do { - z = (*x & 0xffff) * y + (*xc >> 16) + carry; - carry = z >> 16; - Storeinc(xc, z, z2); - z2 = (*x++ >> 16) * y + (*xc & 0xffff) + carry; - carry = z2 >> 16; - } - while(x < xae); - *xc = z2; - } - } -#else - for(; xb < xbe; xc0++) { - if (y = *xb++) { - x = xa; - xc = xc0; - carry = 0; - do { - z = *x++ * y + *xc + carry; - carry = z >> 16; - *xc++ = z & 0xffff; - } - while(x < xae); - *xc = carry; - } - } -#endif -#endif - for(xc0 = c->x, xc = xc0 + wc; wc > 0 && !*--xc; --wc) ; - c->wds = wc; - return c; - } - - static Bigint *p5s; - - static Bigint * -pow5mult -#ifdef KR_headers - (b, k) Bigint *b; int k; -#else - (Bigint *b, int k) -#endif -{ - Bigint *b1, *p5, *p51; - int i; - static int p05[3] = { 5, 25, 125 }; - - if ((i = k & 3)) - b = multadd(b, p05[i-1], 0); - - if (!(k >>= 2)) - return b; - if (!(p5 = p5s)) { - /* first time */ -#ifdef MULTIPLE_THREADS - ACQUIRE_DTOA_LOCK(1); - if (!(p5 = p5s)) { - p5 = p5s = i2b(625); - p5->next = 0; - } - FREE_DTOA_LOCK(1); -#else - p5 = p5s = i2b(625); - p5->next = 0; -#endif - } - for(;;) { - if (k & 1) { - b1 = mult(b, p5); - Bfree(b); - b = b1; - } - if (!(k >>= 1)) - break; - if (!(p51 = p5->next)) { -#ifdef MULTIPLE_THREADS - ACQUIRE_DTOA_LOCK(1); - if (!(p51 = p5->next)) { - p51 = p5->next = mult(p5,p5); - p51->next = 0; - } - FREE_DTOA_LOCK(1); -#else - p51 = p5->next = mult(p5,p5); - p51->next = 0; -#endif - } - p5 = p51; - } - return b; - } - - static Bigint * -lshift -#ifdef KR_headers - (b, k) Bigint *b; int k; -#else - (Bigint *b, int k) -#endif -{ - int i, k1, n, n1; - Bigint *b1; - ULong *x, *x1, *xe, z; - -#ifdef Pack_32 - n = k >> 5; -#else - n = k >> 4; -#endif - k1 = b->k; - n1 = n + b->wds + 1; - for(i = b->maxwds; n1 > i; i <<= 1) - k1++; - b1 = Balloc(k1); - x1 = b1->x; - for(i = 0; i < n; i++) - *x1++ = 0; - x = b->x; - xe = x + b->wds; -#ifdef Pack_32 - if (k &= 0x1f) { - k1 = 32 - k; - z = 0; - do { - *x1++ = *x << k | z; - z = *x++ >> k1; - } - while(x < xe); - if ((*x1 = z)) - ++n1; - } -#else - if (k &= 0xf) { - k1 = 16 - k; - z = 0; - do { - *x1++ = *x << k & 0xffff | z; - z = *x++ >> k1; - } - while(x < xe); - if (*x1 = z) - ++n1; - } -#endif - else do - *x1++ = *x++; - while(x < xe); - b1->wds = n1 - 1; - Bfree(b); - return b1; - } - - static int -cmp -#ifdef KR_headers - (a, b) Bigint *a, *b; -#else - (Bigint *a, Bigint *b) -#endif -{ - ULong *xa, *xa0, *xb, *xb0; - int i, j; - - i = a->wds; - j = b->wds; -#ifdef DEBUG - if (i > 1 && !a->x[i-1]) - Bug("cmp called with a->x[a->wds-1] == 0"); - if (j > 1 && !b->x[j-1]) - Bug("cmp called with b->x[b->wds-1] == 0"); -#endif - if (i -= j) - return i; - xa0 = a->x; - xa = xa0 + j; - xb0 = b->x; - xb = xb0 + j; - for(;;) { - if (*--xa != *--xb) - return *xa < *xb ? -1 : 1; - if (xa <= xa0) - break; - } - return 0; - } - - static Bigint * -diff -#ifdef KR_headers - (a, b) Bigint *a, *b; -#else - (Bigint *a, Bigint *b) -#endif -{ - Bigint *c; - int i, wa, wb; - ULong *xa, *xae, *xb, *xbe, *xc; -#ifdef ULLong - ULLong borrow, y; -#else - ULong borrow, y; -#ifdef Pack_32 - ULong z; -#endif -#endif - - i = cmp(a,b); - if (!i) { - c = Balloc(0); - c->wds = 1; - c->x[0] = 0; - return c; - } - if (i < 0) { - c = a; - a = b; - b = c; - i = 1; - } - else - i = 0; - c = Balloc(a->k); - c->sign = i; - wa = a->wds; - xa = a->x; - xae = xa + wa; - wb = b->wds; - xb = b->x; - xbe = xb + wb; - xc = c->x; - borrow = 0; -#ifdef ULLong - do { - y = (ULLong)*xa++ - *xb++ - borrow; - borrow = y >> 32 & (ULong)1; - *xc++ = y & FFFFFFFF; - } - while(xb < xbe); - while(xa < xae) { - y = *xa++ - borrow; - borrow = y >> 32 & (ULong)1; - *xc++ = y & FFFFFFFF; - } -#else -#ifdef Pack_32 - do { - y = (*xa & 0xffff) - (*xb & 0xffff) - borrow; - borrow = (y & 0x10000) >> 16; - z = (*xa++ >> 16) - (*xb++ >> 16) - borrow; - borrow = (z & 0x10000) >> 16; - Storeinc(xc, z, y); - } - while(xb < xbe); - while(xa < xae) { - y = (*xa & 0xffff) - borrow; - borrow = (y & 0x10000) >> 16; - z = (*xa++ >> 16) - borrow; - borrow = (z & 0x10000) >> 16; - Storeinc(xc, z, y); - } -#else - do { - y = *xa++ - *xb++ - borrow; - borrow = (y & 0x10000) >> 16; - *xc++ = y & 0xffff; - } - while(xb < xbe); - while(xa < xae) { - y = *xa++ - borrow; - borrow = (y & 0x10000) >> 16; - *xc++ = y & 0xffff; - } -#endif -#endif - while(!*--xc) - wa--; - c->wds = wa; - return c; - } - - static double -ulp -#ifdef KR_headers - (dx) double dx; -#else - (double dx) -#endif -{ - register Long L; - U x, a; - - dval(x) = dx; - - L = (word0(x) & Exp_mask) - (P-1)*Exp_msk1; -#ifndef Avoid_Underflow -#ifndef Sudden_Underflow - if (L > 0) { -#endif -#endif -#ifdef IBM - L |= Exp_msk1 >> 4; -#endif - word0(a) = L; - word1(a) = 0; -#ifndef Avoid_Underflow -#ifndef Sudden_Underflow - } - else { - L = -L >> Exp_shift; - if (L < Exp_shift) { - word0(a) = 0x80000 >> L; - word1(a) = 0; - } - else { - word0(a) = 0; - L -= Exp_shift; - word1(a) = L >= 31 ? 1 : 1 << 31 - L; - } - } -#endif -#endif - return dval(a); - } - - static double -b2d -#ifdef KR_headers - (a, e) Bigint *a; int *e; -#else - (Bigint *a, int *e) -#endif -{ - ULong *xa, *xa0, w, y, z; - int k; - U d; -#ifdef VAX - ULong d0, d1; -#else -#define d0 word0(d) -#define d1 word1(d) -#endif - - xa0 = a->x; - xa = xa0 + a->wds; - y = *--xa; -#ifdef DEBUG - if (!y) Bug("zero y in b2d"); -#endif - k = hi0bits(y); - *e = 32 - k; -#ifdef Pack_32 - if (k < Ebits) { - d0 = Exp_1 | (y >> (Ebits - k)); - w = xa > xa0 ? *--xa : 0; - d1 = (y << ((32-Ebits) + k)) | (w >> (Ebits - k)); - goto ret_d; - } - z = xa > xa0 ? *--xa : 0; - if (k -= Ebits) { - d0 = Exp_1 | (y << k) | (z >> (32 - k)); - y = xa > xa0 ? *--xa : 0; - d1 = (z << k) | (y >> (32 - k)); - } - else { - d0 = Exp_1 | y; - d1 = z; - } -#else - if (k < Ebits + 16) { - z = xa > xa0 ? *--xa : 0; - d0 = Exp_1 | (y << (k - Ebits)) | (z >> (Ebits + 16 - k)); - w = xa > xa0 ? *--xa : 0; - y = xa > xa0 ? *--xa : 0; - d1 = (z << (k + 16 - Ebits)) | (w << (k - Ebits)) | (y >> (16 + Ebits - k)); - goto ret_d; - } - z = xa > xa0 ? *--xa : 0; - w = xa > xa0 ? *--xa : 0; - k -= Ebits + 16; - d0 = Exp_1 | y << k + 16 | z << k | w >> 16 - k; - y = xa > xa0 ? *--xa : 0; - d1 = w << k + 16 | y << k; -#endif - ret_d: -#ifdef VAX - word0(d) = d0 >> 16 | d0 << 16; - word1(d) = d1 >> 16 | d1 << 16; -#else -#undef d0 -#undef d1 -#endif - return dval(d); - } - - static Bigint * -d2b -#ifdef KR_headers - (dd, e, bits) double dd; int *e, *bits; -#else - (double dd, int *e, int *bits) -#endif -{ - Bigint *b; - int de, k; - ULong *x, y, z; -#ifndef Sudden_Underflow - int i; -#endif -#ifdef VAX - ULong d0, d1; - d0 = word0(d) >> 16 | word0(d) << 16; - d1 = word1(d) >> 16 | word1(d) << 16; -#else - U d; - dval(d) = dd; -#define d0 word0(d) -#define d1 word1(d) -#endif - -#ifdef Pack_32 - b = Balloc(1); -#else - b = Balloc(2); -#endif - x = b->x; - - z = d0 & Frac_mask; - d0 &= 0x7fffffff; /* clear sign bit, which we ignore */ -#ifdef Sudden_Underflow - de = (int)(d0 >> Exp_shift); -#ifndef IBM - z |= Exp_msk11; -#endif -#else - if ((de = (int)(d0 >> Exp_shift))) - z |= Exp_msk1; -#endif -#ifdef Pack_32 - if ((y = d1)) { - if ((k = lo0bits(&y))) { - x[0] = y | (z << (32 - k)); - z >>= k; - } - else - x[0] = y; -#ifndef Sudden_Underflow - i = -#endif - b->wds = (x[1] = z) ? 2 : 1; - } - else { - /* This assertion fails for "1e-500" and other very - * small numbers. It provides the right result (0) - * though. This assert has also been removed from KJS's - * version of dtoa.c. - * - * #ifdef DEBUG - * if (!z) Bug("zero z in b2d"); - * #endif - */ - k = lo0bits(&z); - x[0] = z; -#ifndef Sudden_Underflow - i = -#endif - b->wds = 1; - k += 32; - } -#else - if (y = d1) { - if (k = lo0bits(&y)) - if (k >= 16) { - x[0] = y | z << 32 - k & 0xffff; - x[1] = z >> k - 16 & 0xffff; - x[2] = z >> k; - i = 2; - } - else { - x[0] = y & 0xffff; - x[1] = y >> 16 | z << 16 - k & 0xffff; - x[2] = z >> k & 0xffff; - x[3] = z >> k+16; - i = 3; - } - else { - x[0] = y & 0xffff; - x[1] = y >> 16; - x[2] = z & 0xffff; - x[3] = z >> 16; - i = 3; - } - } - else { -#ifdef DEBUG - if (!z) - Bug("Zero passed to d2b"); -#endif - k = lo0bits(&z); - if (k >= 16) { - x[0] = z; - i = 0; - } - else { - x[0] = z & 0xffff; - x[1] = z >> 16; - i = 1; - } - k += 32; - } - while(!x[i]) - --i; - b->wds = i + 1; -#endif -#ifndef Sudden_Underflow - if (de) { -#endif -#ifdef IBM - *e = (de - Bias - (P-1) << 2) + k; - *bits = 4*P + 8 - k - hi0bits(word0(d) & Frac_mask); -#else - *e = de - Bias - (P-1) + k; - *bits = P - k; -#endif -#ifndef Sudden_Underflow - } - else { - *e = de - Bias - (P-1) + 1 + k; -#ifdef Pack_32 - *bits = 32*i - hi0bits(x[i-1]); -#else - *bits = (i+2)*16 - hi0bits(x[i]); -#endif - } -#endif - return b; - } -#undef d0 -#undef d1 - - static double -ratio -#ifdef KR_headers - (a, b) Bigint *a, *b; -#else - (Bigint *a, Bigint *b) -#endif -{ - U da, db; - int k, ka, kb; - - dval(da) = b2d(a, &ka); - dval(db) = b2d(b, &kb); -#ifdef Pack_32 - k = ka - kb + 32*(a->wds - b->wds); -#else - k = ka - kb + 16*(a->wds - b->wds); -#endif -#ifdef IBM - if (k > 0) { - word0(da) += (k >> 2)*Exp_msk1; - if (k &= 3) - dval(da) *= 1 << k; - } - else { - k = -k; - word0(db) += (k >> 2)*Exp_msk1; - if (k &= 3) - dval(db) *= 1 << k; - } -#else - if (k > 0) - word0(da) += k*Exp_msk1; - else { - k = -k; - word0(db) += k*Exp_msk1; - } -#endif - return dval(da) / dval(db); - } - - static CONST double -tens[] = { - 1e0, 1e1, 1e2, 1e3, 1e4, 1e5, 1e6, 1e7, 1e8, 1e9, - 1e10, 1e11, 1e12, 1e13, 1e14, 1e15, 1e16, 1e17, 1e18, 1e19, - 1e20, 1e21, 1e22 -#ifdef VAX - , 1e23, 1e24 -#endif - }; - - static CONST double -#ifdef IEEE_Arith -bigtens[] = { 1e16, 1e32, 1e64, 1e128, 1e256 }; -static CONST double tinytens[] = { 1e-16, 1e-32, 1e-64, 1e-128, -#ifdef Avoid_Underflow - 9007199254740992.*9007199254740992.e-256 - /* = 2^106 * 1e-53 */ -#else - 1e-256 -#endif - }; -/* The factor of 2^53 in tinytens[4] helps us avoid setting the underflow */ -/* flag unnecessarily. It leads to a song and dance at the end of strtod. */ -#define Scale_Bit 0x10 -#define n_bigtens 5 -#else -#ifdef IBM -bigtens[] = { 1e16, 1e32, 1e64 }; -static CONST double tinytens[] = { 1e-16, 1e-32, 1e-64 }; -#define n_bigtens 3 -#else -bigtens[] = { 1e16, 1e32 }; -static CONST double tinytens[] = { 1e-16, 1e-32 }; -#define n_bigtens 2 -#endif -#endif - -#ifndef IEEE_Arith -#undef INFNAN_CHECK -#endif - -#ifdef INFNAN_CHECK - -#ifndef NAN_WORD0 -#define NAN_WORD0 0x7ff80000 -#endif - -#ifndef NAN_WORD1 -#define NAN_WORD1 0 -#endif - - static int -match -#ifdef KR_headers - (sp, t) char **sp, *t; -#else - (CONST char **sp, char *t) -#endif -{ - int c, d; - CONST char *s = *sp; - - while(d = *t++) { - if ((c = *++s) >= 'A' && c <= 'Z') - c += 'a' - 'A'; - if (c != d) - return 0; - } - *sp = s + 1; - return 1; - } - -#ifndef No_Hex_NaN - static void -hexnan -#ifdef KR_headers - (rvp, sp) double *rvp; CONST char **sp; -#else - (double *rvp, CONST char **sp) -#endif -{ - ULong c, x[2]; - CONST char *s; - int havedig, udx0, xshift; - - x[0] = x[1] = 0; - havedig = xshift = 0; - udx0 = 1; - s = *sp; - while(c = *(CONST unsigned char*)++s) { - if (c >= '0' && c <= '9') - c -= '0'; - else if (c >= 'a' && c <= 'f') - c += 10 - 'a'; - else if (c >= 'A' && c <= 'F') - c += 10 - 'A'; - else if (c <= ' ') { - if (udx0 && havedig) { - udx0 = 0; - xshift = 1; - } - continue; - } - else if (/*(*/ c == ')' && havedig) { - *sp = s + 1; - break; - } - else - return; /* invalid form: don't change *sp */ - havedig = 1; - if (xshift) { - xshift = 0; - x[0] = x[1]; - x[1] = 0; - } - if (udx0) - x[0] = (x[0] << 4) | (x[1] >> 28); - x[1] = (x[1] << 4) | c; - } - if ((x[0] &= 0xfffff) || x[1]) { - word0(*rvp) = Exp_mask | x[0]; - word1(*rvp) = x[1]; - } - } -#endif /*No_Hex_NaN*/ -#endif /* INFNAN_CHECK */ - - double -strtod -#ifdef KR_headers - (s00, se) CONST char *s00; char **se; -#else - (CONST char *s00, char **se) -#endif -{ -#ifdef Avoid_Underflow - int scale; -#endif - int bb2, bb5, bbe, bd2, bd5, bbbits, bs2, c, dsign, - e, e1, esign, i, j, k, nd, nd0, nf, nz, nz0, sign; - CONST char *s, *s0, *s1; - double aadj; - U aadj1, adj, rv, rv0; - Long L; - ULong y, z; - Bigint *bb = NULL, *bb1, *bd = NULL, *bd0, *bs = NULL, *delta = NULL; -#ifdef SET_INEXACT - int inexact, oldinexact; -#endif -#ifdef Honor_FLT_ROUNDS - int rounding; -#endif -#ifdef USE_LOCALE - CONST char *s2; -#endif - - sign = nz0 = nz = 0; - dval(rv) = 0.; - for(s = s00;;s++) switch(*s) { - case '-': - sign = 1; - /* no break */ - case '+': - if (*++s) - goto break2; - /* no break */ - case 0: - goto ret0; - case '\t': - case '\n': - case '\v': - case '\f': - case '\r': - case ' ': - continue; - default: - goto break2; - } - break2: - if (*s == '0') { - nz0 = 1; - while(*++s == '0') ; - if (!*s) - goto ret; - } - s0 = s; - y = z = 0; - for(nd = nf = 0; (c = *s) >= '0' && c <= '9'; nd++, s++) - if (nd < 9) - y = 10*y + c - '0'; - else if (nd < 16) - z = 10*z + c - '0'; - nd0 = nd; -#ifdef USE_LOCALE - s1 = localeconv()->decimal_point; - if (c == *s1) { - c = '.'; - if (*++s1) { - s2 = s; - for(;;) { - if (*++s2 != *s1) { - c = 0; - break; - } - if (!*++s1) { - s = s2; - break; - } - } - } - } -#endif - if (c == '.') { - c = *++s; - if (!nd) { - for(; c == '0'; c = *++s) - nz++; - if (c > '0' && c <= '9') { - s0 = s; - nf += nz; - nz = 0; - goto have_dig; - } - goto dig_done; - } - for(; c >= '0' && c <= '9'; c = *++s) { - have_dig: - nz++; - if (c -= '0') { - nf += nz; - for(i = 1; i < nz; i++) - if (nd++ < 9) - y *= 10; - else if (nd <= DBL_DIG + 1) - z *= 10; - if (nd++ < 9) - y = 10*y + c; - else if (nd <= DBL_DIG + 1) - z = 10*z + c; - nz = 0; - } - } - } - dig_done: - e = 0; - if (c == 'e' || c == 'E') { - if (!nd && !nz && !nz0) { - goto ret0; - } - s00 = s; - esign = 0; - switch(c = *++s) { - case '-': - esign = 1; - case '+': - c = *++s; - } - if (c >= '0' && c <= '9') { - while(c == '0') - c = *++s; - if (c > '0' && c <= '9') { - L = c - '0'; - s1 = s; - while((c = *++s) >= '0' && c <= '9') - L = 10*L + c - '0'; - if (s - s1 > 8 || L > 19999) - /* Avoid confusion from exponents - * so large that e might overflow. - */ - e = 19999; /* safe for 16 bit ints */ - else - e = (int)L; - if (esign) - e = -e; - } - else - e = 0; - } - else - s = s00; - } - if (!nd) { - if (!nz && !nz0) { -#ifdef INFNAN_CHECK - /* Check for Nan and Infinity */ - switch(c) { - case 'i': - case 'I': - if (match(&s,"nf")) { - --s; - if (!match(&s,"inity")) - ++s; - word0(rv) = 0x7ff00000; - word1(rv) = 0; - goto ret; - } - break; - case 'n': - case 'N': - if (match(&s, "an")) { - word0(rv) = NAN_WORD0; - word1(rv) = NAN_WORD1; -#ifndef No_Hex_NaN - if (*s == '(') /*)*/ - hexnan(&rv, &s); -#endif - goto ret; - } - } -#endif /* INFNAN_CHECK */ - ret0: - s = s00; - sign = 0; - } - goto ret; - } - e1 = e -= nf; - - /* Now we have nd0 digits, starting at s0, followed by a - * decimal point, followed by nd-nd0 digits. The number we're - * after is the integer represented by those digits times - * 10**e */ - - if (!nd0) - nd0 = nd; - k = nd < DBL_DIG + 1 ? nd : DBL_DIG + 1; - dval(rv) = y; - if (k > 9) { -#ifdef SET_INEXACT - if (k > DBL_DIG) - oldinexact = get_inexact(); -#endif - dval(rv) = tens[k - 9] * dval(rv) + z; - } - bd0 = 0; - if (nd <= DBL_DIG -#ifndef RND_PRODQUOT -#ifndef Honor_FLT_ROUNDS - && Flt_Rounds == 1 -#endif -#endif - ) { - if (!e) - goto ret; - if (e > 0) { - if (e <= Ten_pmax) { -#ifdef VAX - goto vax_ovfl_check; -#else -#ifdef Honor_FLT_ROUNDS - /* round correctly FLT_ROUNDS = 2 or 3 */ - if (sign) { - rv = -rv; - sign = 0; - } -#endif - /* rv = */ rounded_product(dval(rv), tens[e]); - goto ret; -#endif - } - i = DBL_DIG - nd; - if (e <= Ten_pmax + i) { - /* A fancier test would sometimes let us do - * this for larger i values. - */ -#ifdef Honor_FLT_ROUNDS - /* round correctly FLT_ROUNDS = 2 or 3 */ - if (sign) { - rv = -rv; - sign = 0; - } -#endif - e -= i; - dval(rv) *= tens[i]; -#ifdef VAX - /* VAX exponent range is so narrow we must - * worry about overflow here... - */ - vax_ovfl_check: - word0(rv) -= P*Exp_msk1; - /* rv = */ rounded_product(dval(rv), tens[e]); - if ((word0(rv) & Exp_mask) - > Exp_msk1*(DBL_MAX_EXP+Bias-1-P)) - goto ovfl; - word0(rv) += P*Exp_msk1; -#else - /* rv = */ rounded_product(dval(rv), tens[e]); -#endif - goto ret; - } - } -#ifndef Inaccurate_Divide - else if (e >= -Ten_pmax) { -#ifdef Honor_FLT_ROUNDS - /* round correctly FLT_ROUNDS = 2 or 3 */ - if (sign) { - rv = -rv; - sign = 0; - } -#endif - /* rv = */ rounded_quotient(dval(rv), tens[-e]); - goto ret; - } -#endif - } - e1 += nd - k; - -#ifdef IEEE_Arith -#ifdef SET_INEXACT - inexact = 1; - if (k <= DBL_DIG) - oldinexact = get_inexact(); -#endif -#ifdef Avoid_Underflow - scale = 0; -#endif -#ifdef Honor_FLT_ROUNDS - if ((rounding = Flt_Rounds) >= 2) { - if (sign) - rounding = rounding == 2 ? 0 : 2; - else - if (rounding != 2) - rounding = 0; - } -#endif -#endif /*IEEE_Arith*/ - - /* Get starting approximation = rv * 10**e1 */ - - if (e1 > 0) { - if ((i = e1 & 15)) - dval(rv) *= tens[i]; - if (e1 &= ~15) { - if (e1 > DBL_MAX_10_EXP) { - ovfl: -#ifndef NO_ERRNO - errno = ERANGE; -#endif - /* Can't trust HUGE_VAL */ -#ifdef IEEE_Arith -#ifdef Honor_FLT_ROUNDS - switch(rounding) { - case 0: /* toward 0 */ - case 3: /* toward -infinity */ - word0(rv) = Big0; - word1(rv) = Big1; - break; - default: - word0(rv) = Exp_mask; - word1(rv) = 0; - } -#else /*Honor_FLT_ROUNDS*/ - word0(rv) = Exp_mask; - word1(rv) = 0; -#endif /*Honor_FLT_ROUNDS*/ -#ifdef SET_INEXACT - /* set overflow bit */ - dval(rv0) = 1e300; - dval(rv0) *= dval(rv0); -#endif -#else /*IEEE_Arith*/ - word0(rv) = Big0; - word1(rv) = Big1; -#endif /*IEEE_Arith*/ - if (bd0) - goto retfree; - goto ret; - } - e1 >>= 4; - for(j = 0; e1 > 1; j++, e1 >>= 1) - if (e1 & 1) - dval(rv) *= bigtens[j]; - /* The last multiplication could overflow. */ - word0(rv) -= P*Exp_msk1; - dval(rv) *= bigtens[j]; - if ((z = word0(rv) & Exp_mask) - > Exp_msk1*(DBL_MAX_EXP+Bias-P)) - goto ovfl; - if (z > Exp_msk1*(DBL_MAX_EXP+Bias-1-P)) { - /* set to largest number */ - /* (Can't trust DBL_MAX) */ - word0(rv) = Big0; - word1(rv) = Big1; - } - else - word0(rv) += P*Exp_msk1; - } - } - else if (e1 < 0) { - e1 = -e1; - if ((i = e1 & 15)) - dval(rv) /= tens[i]; - if (e1 >>= 4) { - if (e1 >= 1 << n_bigtens) - goto undfl; -#ifdef Avoid_Underflow - if (e1 & Scale_Bit) - scale = 2*P; - for(j = 0; e1 > 0; j++, e1 >>= 1) - if (e1 & 1) - dval(rv) *= tinytens[j]; - if (scale && (j = 2*P + 1 - ((word0(rv) & Exp_mask) - >> Exp_shift)) > 0) { - /* scaled rv is denormal; zap j low bits */ - if (j >= 32) { - word1(rv) = 0; - if (j >= 53) - word0(rv) = (P+2)*Exp_msk1; - else - word0(rv) &= 0xffffffff << (j-32); - } - else - word1(rv) &= 0xffffffff << j; - } -#else - for(j = 0; e1 > 1; j++, e1 >>= 1) - if (e1 & 1) - dval(rv) *= tinytens[j]; - /* The last multiplication could underflow. */ - dval(rv0) = dval(rv); - dval(rv) *= tinytens[j]; - if (!dval(rv)) { - dval(rv) = 2.*dval(rv0); - dval(rv) *= tinytens[j]; -#endif - if (!dval(rv)) { - undfl: - dval(rv) = 0.; -#ifndef NO_ERRNO - errno = ERANGE; -#endif - if (bd0) - goto retfree; - goto ret; - } -#ifndef Avoid_Underflow - word0(rv) = Tiny0; - word1(rv) = Tiny1; - /* The refinement below will clean - * this approximation up. - */ - } -#endif - } - } - - /* Now the hard part -- adjusting rv to the correct value.*/ - - /* Put digits into bd: true value = bd * 10^e */ - - bd0 = s2b(s0, nd0, nd, y); - - for(;;) { - bd = Balloc(bd0->k); - Bcopy(bd, bd0); - bb = d2b(dval(rv), &bbe, &bbbits); /* rv = bb * 2^bbe */ - bs = i2b(1); - - if (e >= 0) { - bb2 = bb5 = 0; - bd2 = bd5 = e; - } - else { - bb2 = bb5 = -e; - bd2 = bd5 = 0; - } - if (bbe >= 0) - bb2 += bbe; - else - bd2 -= bbe; - bs2 = bb2; -#ifdef Honor_FLT_ROUNDS - if (rounding != 1) - bs2++; -#endif -#ifdef Avoid_Underflow - j = bbe - scale; - i = j + bbbits - 1; /* logb(rv) */ - if (i < Emin) /* denormal */ - j += P - Emin; - else - j = P + 1 - bbbits; -#else /*Avoid_Underflow*/ -#ifdef Sudden_Underflow -#ifdef IBM - j = 1 + 4*P - 3 - bbbits + ((bbe + bbbits - 1) & 3); -#else - j = P + 1 - bbbits; -#endif -#else /*Sudden_Underflow*/ - j = bbe; - i = j + bbbits - 1; /* logb(rv) */ - if (i < Emin) /* denormal */ - j += P - Emin; - else - j = P + 1 - bbbits; -#endif /*Sudden_Underflow*/ -#endif /*Avoid_Underflow*/ - bb2 += j; - bd2 += j; -#ifdef Avoid_Underflow - bd2 += scale; -#endif - i = bb2 < bd2 ? bb2 : bd2; - if (i > bs2) - i = bs2; - if (i > 0) { - bb2 -= i; - bd2 -= i; - bs2 -= i; - } - if (bb5 > 0) { - bs = pow5mult(bs, bb5); - bb1 = mult(bs, bb); - Bfree(bb); - bb = bb1; - } - if (bb2 > 0) - bb = lshift(bb, bb2); - if (bd5 > 0) - bd = pow5mult(bd, bd5); - if (bd2 > 0) - bd = lshift(bd, bd2); - if (bs2 > 0) - bs = lshift(bs, bs2); - delta = diff(bb, bd); - dsign = delta->sign; - delta->sign = 0; - i = cmp(delta, bs); -#ifdef Honor_FLT_ROUNDS - if (rounding != 1) { - if (i < 0) { - /* Error is less than an ulp */ - if (!delta->x[0] && delta->wds <= 1) { - /* exact */ -#ifdef SET_INEXACT - inexact = 0; -#endif - break; - } - if (rounding) { - if (dsign) { - dval(adj) = 1.; - goto apply_adj; - } - } - else if (!dsign) { - dval(adj) = -1.; - if (!word1(rv) - && !(word0(rv) & Frac_mask)) { - y = word0(rv) & Exp_mask; -#ifdef Avoid_Underflow - if (!scale || y > 2*P*Exp_msk1) -#else - if (y) -#endif - { - delta = lshift(delta,Log2P); - if (cmp(delta, bs) <= 0) - dval(adj) = -0.5; - } - } - apply_adj: -#ifdef Avoid_Underflow - if (scale && (y = word0(rv) & Exp_mask) - <= 2*P*Exp_msk1) - word0(adj) += (2*P+1)*Exp_msk1 - y; -#else -#ifdef Sudden_Underflow - if ((word0(rv) & Exp_mask) <= - P*Exp_msk1) { - word0(rv) += P*Exp_msk1; - dval(rv) += dval(adj)*ulp(dval(rv)); - word0(rv) -= P*Exp_msk1; - } - else -#endif /*Sudden_Underflow*/ -#endif /*Avoid_Underflow*/ - dval(rv) += dval(adj)*ulp(dval(rv)); - } - break; - } - dval(adj) = ratio(delta, bs); - if (dval(adj) < 1.) - dval(adj) = 1.; - if (dval(adj) <= 0x7ffffffe) { - /* adj = rounding ? ceil(adj) : floor(adj); */ - y = dval(adj); - if (y != dval(adj)) { - if (!((rounding>>1) ^ dsign)) - y++; - dval(adj) = y; - } - } -#ifdef Avoid_Underflow - if (scale && (y = word0(rv) & Exp_mask) <= 2*P*Exp_msk1) - word0(adj) += (2*P+1)*Exp_msk1 - y; -#else -#ifdef Sudden_Underflow - if ((word0(rv) & Exp_mask) <= P*Exp_msk1) { - word0(rv) += P*Exp_msk1; - dval(adj) *= ulp(dval(rv)); - if (dsign) - dval(rv) += dval(adj); - else - dval(rv) -= dval(adj); - word0(rv) -= P*Exp_msk1; - goto cont; - } -#endif /*Sudden_Underflow*/ -#endif /*Avoid_Underflow*/ - dval(adj) *= ulp(dval(rv)); - if (dsign) - dval(rv) += dval(adj); - else - dval(rv) -= dval(adj); - goto cont; - } -#endif /*Honor_FLT_ROUNDS*/ - - if (i < 0) { - /* Error is less than half an ulp -- check for - * special case of mantissa a power of two. - */ - if (dsign || word1(rv) || word0(rv) & Bndry_mask -#ifdef IEEE_Arith -#ifdef Avoid_Underflow - || (word0(rv) & Exp_mask) <= (2*P+1)*Exp_msk1 -#else - || (word0(rv) & Exp_mask) <= Exp_msk1 -#endif -#endif - ) { -#ifdef SET_INEXACT - if (!delta->x[0] && delta->wds <= 1) - inexact = 0; -#endif - break; - } - if (!delta->x[0] && delta->wds <= 1) { - /* exact result */ -#ifdef SET_INEXACT - inexact = 0; -#endif - break; - } - delta = lshift(delta,Log2P); - if (cmp(delta, bs) > 0) - goto drop_down; - break; - } - if (i == 0) { - /* exactly half-way between */ - if (dsign) { - if ((word0(rv) & Bndry_mask1) == Bndry_mask1 - && word1(rv) == ( -#ifdef Avoid_Underflow - (scale && (y = word0(rv) & Exp_mask) <= 2*P*Exp_msk1) - ? (0xffffffff & (0xffffffff << (2*P+1-(y>>Exp_shift)))) : -#endif - 0xffffffff)) { - /*boundary case -- increment exponent*/ - word0(rv) = (word0(rv) & Exp_mask) - + Exp_msk1 -#ifdef IBM - | Exp_msk1 >> 4 -#endif - ; - word1(rv) = 0; -#ifdef Avoid_Underflow - dsign = 0; -#endif - break; - } - } - else if (!(word0(rv) & Bndry_mask) && !word1(rv)) { - drop_down: - /* boundary case -- decrement exponent */ -#ifdef Sudden_Underflow /*{{*/ - L = word0(rv) & Exp_mask; -#ifdef IBM - if (L < Exp_msk1) -#else -#ifdef Avoid_Underflow - if (L <= (scale ? (2*P+1)*Exp_msk1 : Exp_msk1)) -#else - if (L <= Exp_msk1) -#endif /*Avoid_Underflow*/ -#endif /*IBM*/ - goto undfl; - L -= Exp_msk1; -#else /*Sudden_Underflow}{*/ -#ifdef Avoid_Underflow - if (scale) { - L = word0(rv) & Exp_mask; - if (L <= (2*P+1)*Exp_msk1) { - if (L > (P+2)*Exp_msk1) - /* round even ==> */ - /* accept rv */ - break; - /* rv = smallest denormal */ - goto undfl; - } - } -#endif /*Avoid_Underflow*/ - L = (word0(rv) & Exp_mask) - Exp_msk1; -#endif /*Sudden_Underflow}}*/ - word0(rv) = L | Bndry_mask1; - word1(rv) = 0xffffffff; -#ifdef IBM - goto cont; -#else - break; -#endif - } -#ifndef ROUND_BIASED - if (!(word1(rv) & LSB)) - break; -#endif - if (dsign) - dval(rv) += ulp(dval(rv)); -#ifndef ROUND_BIASED - else { - dval(rv) -= ulp(dval(rv)); -#ifndef Sudden_Underflow - if (!dval(rv)) - goto undfl; -#endif - } -#ifdef Avoid_Underflow - dsign = 1 - dsign; -#endif -#endif - break; - } - if ((aadj = ratio(delta, bs)) <= 2.) { - if (dsign) - aadj = dval(aadj1) = 1.; - else if (word1(rv) || word0(rv) & Bndry_mask) { -#ifndef Sudden_Underflow - if (word1(rv) == Tiny1 && !word0(rv)) - goto undfl; -#endif - aadj = 1.; - dval(aadj1) = -1.; - } - else { - /* special case -- power of FLT_RADIX to be */ - /* rounded down... */ - - if (aadj < 2./FLT_RADIX) - aadj = 1./FLT_RADIX; - else - aadj *= 0.5; - dval(aadj1) = -aadj; - } - } - else { - aadj *= 0.5; - dval(aadj1) = dsign ? aadj : -aadj; -#ifdef Check_FLT_ROUNDS - switch(Rounding) { - case 2: /* towards +infinity */ - dval(aadj1) -= 0.5; - break; - case 0: /* towards 0 */ - case 3: /* towards -infinity */ - dval(aadj1) += 0.5; - } -#else - if (Flt_Rounds == 0) - dval(aadj1) += 0.5; -#endif /*Check_FLT_ROUNDS*/ - } - y = word0(rv) & Exp_mask; - - /* Check for overflow */ - - if (y == Exp_msk1*(DBL_MAX_EXP+Bias-1)) { - dval(rv0) = dval(rv); - word0(rv) -= P*Exp_msk1; - dval(adj) = dval(aadj1) * ulp(dval(rv)); - dval(rv) += dval(adj); - if ((word0(rv) & Exp_mask) >= - Exp_msk1*(DBL_MAX_EXP+Bias-P)) { - if (word0(rv0) == Big0 && word1(rv0) == Big1) - goto ovfl; - word0(rv) = Big0; - word1(rv) = Big1; - goto cont; - } - else - word0(rv) += P*Exp_msk1; - } - else { -#ifdef Avoid_Underflow - if (scale && y <= 2*P*Exp_msk1) { - if (aadj <= 0x7fffffff) { - if ((z = aadj) <= 0) - z = 1; - aadj = z; - dval(aadj1) = dsign ? aadj : -aadj; - } - word0(aadj1) += (2*P+1)*Exp_msk1 - y; - } - dval(adj) = dval(aadj1) * ulp(dval(rv)); - dval(rv) += dval(adj); -#else -#ifdef Sudden_Underflow - if ((word0(rv) & Exp_mask) <= P*Exp_msk1) { - dval(rv0) = dval(rv); - word0(rv) += P*Exp_msk1; - dval(adj) = dval(aadj1) * ulp(dval(rv)); - dval(rv) += dval(adj); -#ifdef IBM - if ((word0(rv) & Exp_mask) < P*Exp_msk1) -#else - if ((word0(rv) & Exp_mask) <= P*Exp_msk1) -#endif - { - if (word0(rv0) == Tiny0 - && word1(rv0) == Tiny1) - goto undfl; - word0(rv) = Tiny0; - word1(rv) = Tiny1; - goto cont; - } - else - word0(rv) -= P*Exp_msk1; - } - else { - dval(adj) = dval(aadj1) * ulp(dval(rv)); - dval(rv) += dval(adj); - } -#else /*Sudden_Underflow*/ - /* Compute adj so that the IEEE rounding rules will - * correctly round rv + adj in some half-way cases. - * If rv * ulp(rv) is denormalized (i.e., - * y <= (P-1)*Exp_msk1), we must adjust aadj to avoid - * trouble from bits lost to denormalization; - * example: 1.2e-307 . - */ - if (y <= (P-1)*Exp_msk1 && aadj > 1.) { - dval(aadj1) = (double)(int)(aadj + 0.5); - if (!dsign) - dval(aadj1) = -dval(aadj1); - } - dval(adj) = dval(aadj1) * ulp(dval(rv)); - dval(rv) += dval(adj); -#endif /*Sudden_Underflow*/ -#endif /*Avoid_Underflow*/ - } - z = word0(rv) & Exp_mask; -#ifndef SET_INEXACT -#ifdef Avoid_Underflow - if (!scale) -#endif - if (y == z) { - /* Can we stop now? */ - L = (Long)aadj; - aadj -= L; - /* The tolerances below are conservative. */ - if (dsign || word1(rv) || word0(rv) & Bndry_mask) { - if (aadj < .4999999 || aadj > .5000001) - break; - } - else if (aadj < .4999999/FLT_RADIX) - break; - } -#endif - cont: - Bfree(bb); - Bfree(bd); - Bfree(bs); - Bfree(delta); - } -#ifdef SET_INEXACT - if (inexact) { - if (!oldinexact) { - word0(rv0) = Exp_1 + (70 << Exp_shift); - word1(rv0) = 0; - dval(rv0) += 1.; - } - } - else if (!oldinexact) - clear_inexact(); -#endif -#ifdef Avoid_Underflow - if (scale) { - word0(rv0) = Exp_1 - 2*P*Exp_msk1; - word1(rv0) = 0; - dval(rv) *= dval(rv0); -#ifndef NO_ERRNO - /* try to avoid the bug of testing an 8087 register value */ - if (word0(rv) == 0 && word1(rv) == 0) - errno = ERANGE; -#endif - } -#endif /* Avoid_Underflow */ -#ifdef SET_INEXACT - if (inexact && !(word0(rv) & Exp_mask)) { - /* set underflow bit */ - dval(rv0) = 1e-300; - dval(rv0) *= dval(rv0); - } -#endif - retfree: - Bfree(bb); - Bfree(bd); - Bfree(bs); - Bfree(bd0); - Bfree(delta); - ret: - if (se) - *se = (char *)s; - return sign ? -dval(rv) : dval(rv); - } - - static int -quorem -#ifdef KR_headers - (b, S) Bigint *b, *S; -#else - (Bigint *b, Bigint *S) -#endif -{ - int n; - ULong *bx, *bxe, q, *sx, *sxe; -#ifdef ULLong - ULLong borrow, carry, y, ys; -#else - ULong borrow, carry, y, ys; -#ifdef Pack_32 - ULong si, z, zs; -#endif -#endif - - n = S->wds; -#ifdef DEBUG - /*debug*/ if (b->wds > n) - /*debug*/ Bug("oversize b in quorem"); -#endif - if (b->wds < n) - return 0; - sx = S->x; - sxe = sx + --n; - bx = b->x; - bxe = bx + n; - q = *bxe / (*sxe + 1); /* ensure q <= true quotient */ -#ifdef DEBUG - /*debug*/ if (q > 9) - /*debug*/ Bug("oversized quotient in quorem"); -#endif - if (q) { - borrow = 0; - carry = 0; - do { -#ifdef ULLong - ys = *sx++ * (ULLong)q + carry; - carry = ys >> 32; - y = *bx - (ys & FFFFFFFF) - borrow; - borrow = y >> 32 & (ULong)1; - *bx++ = y & FFFFFFFF; -#else -#ifdef Pack_32 - si = *sx++; - ys = (si & 0xffff) * q + carry; - zs = (si >> 16) * q + (ys >> 16); - carry = zs >> 16; - y = (*bx & 0xffff) - (ys & 0xffff) - borrow; - borrow = (y & 0x10000) >> 16; - z = (*bx >> 16) - (zs & 0xffff) - borrow; - borrow = (z & 0x10000) >> 16; - Storeinc(bx, z, y); -#else - ys = *sx++ * q + carry; - carry = ys >> 16; - y = *bx - (ys & 0xffff) - borrow; - borrow = (y & 0x10000) >> 16; - *bx++ = y & 0xffff; -#endif -#endif - } - while(sx <= sxe); - if (!*bxe) { - bx = b->x; - while(--bxe > bx && !*bxe) - --n; - b->wds = n; - } - } - if (cmp(b, S) >= 0) { - q++; - borrow = 0; - carry = 0; - bx = b->x; - sx = S->x; - do { -#ifdef ULLong - ys = *sx++ + carry; - carry = ys >> 32; - y = *bx - (ys & FFFFFFFF) - borrow; - borrow = y >> 32 & (ULong)1; - *bx++ = y & FFFFFFFF; -#else -#ifdef Pack_32 - si = *sx++; - ys = (si & 0xffff) + carry; - zs = (si >> 16) + (ys >> 16); - carry = zs >> 16; - y = (*bx & 0xffff) - (ys & 0xffff) - borrow; - borrow = (y & 0x10000) >> 16; - z = (*bx >> 16) - (zs & 0xffff) - borrow; - borrow = (z & 0x10000) >> 16; - Storeinc(bx, z, y); -#else - ys = *sx++ + carry; - carry = ys >> 16; - y = *bx - (ys & 0xffff) - borrow; - borrow = (y & 0x10000) >> 16; - *bx++ = y & 0xffff; -#endif -#endif - } - while(sx <= sxe); - bx = b->x; - bxe = bx + n; - if (!*bxe) { - while(--bxe > bx && !*bxe) - --n; - b->wds = n; - } - } - return q; - } - -#ifndef MULTIPLE_THREADS - static char *dtoa_result; -#endif - - static char * -#ifdef KR_headers -rv_alloc(i) int i; -#else -rv_alloc(int i) -#endif -{ - int j, k, *r; - - j = sizeof(ULong); - for(k = 0; - sizeof(Bigint) - sizeof(ULong) - sizeof(int) + j <= i; - j <<= 1) - k++; - r = (int*)Balloc(k); - *r = k; - return -#ifndef MULTIPLE_THREADS - dtoa_result = -#endif - (char *)(r+1); - } - - static char * -#ifdef KR_headers -nrv_alloc(s, rve, n) char *s, **rve; int n; -#else -nrv_alloc(const char *s, char **rve, int n) -#endif -{ - char *rv, *t; - - t = rv = rv_alloc(n); - while ((*t = *s++)) t++; - if (rve) - *rve = t; - return rv; - } - -/* freedtoa(s) must be used to free values s returned by dtoa - * when MULTIPLE_THREADS is #defined. It should be used in all cases, - * but for consistency with earlier versions of dtoa, it is optional - * when MULTIPLE_THREADS is not defined. - */ - - void -#ifdef KR_headers -freedtoa(s) char *s; -#else -freedtoa(char *s) -#endif -{ - Bigint *b = (Bigint *)((int *)s - 1); - b->maxwds = 1 << (b->k = *(int*)b); - Bfree(b); -#ifndef MULTIPLE_THREADS - if (s == dtoa_result) - dtoa_result = 0; -#endif - } - -/* dtoa for IEEE arithmetic (dmg): convert double to ASCII string. - * - * Inspired by "How to Print Floating-Point Numbers Accurately" by - * Guy L. Steele, Jr. and Jon L. White [Proc. ACM SIGPLAN '90, pp. 112-126]. - * - * Modifications: - * 1. Rather than iterating, we use a simple numeric overestimate - * to determine k = floor(log10(d)). We scale relevant - * quantities using O(log2(k)) rather than O(k) multiplications. - * 2. For some modes > 2 (corresponding to ecvt and fcvt), we don't - * try to generate digits strictly left to right. Instead, we - * compute with fewer bits and propagate the carry if necessary - * when rounding the final digit up. This is often faster. - * 3. Under the assumption that input will be rounded nearest, - * mode 0 renders 1e23 as 1e23 rather than 9.999999999999999e22. - * That is, we allow equality in stopping tests when the - * round-nearest rule will give the same floating-point value - * as would satisfaction of the stopping test with strict - * inequality. - * 4. We remove common factors of powers of 2 from relevant - * quantities. - * 5. When converting floating-point integers less than 1e16, - * we use floating-point arithmetic rather than resorting - * to multiple-precision integers. - * 6. When asked to produce fewer than 15 digits, we first try - * to get by with floating-point arithmetic; we resort to - * multiple-precision integer arithmetic only if we cannot - * guarantee that the floating-point calculation has given - * the correctly rounded result. For k requested digits and - * "uniformly" distributed input, the probability is - * something like 10^(k-15) that we must resort to the Long - * calculation. - */ - - char * -dtoa -#ifdef KR_headers - (dd, mode, ndigits, decpt, sign, rve) - double dd; int mode, ndigits, *decpt, *sign; char **rve; -#else - (double dd, int mode, int ndigits, int *decpt, int *sign, char **rve) -#endif -{ - /* Arguments ndigits, decpt, sign are similar to those - of ecvt and fcvt; trailing zeros are suppressed from - the returned string. If not null, *rve is set to point - to the end of the return value. If d is +-Infinity or NaN, - then *decpt is set to 9999. - - mode: - 0 ==> shortest string that yields d when read in - and rounded to nearest. - 1 ==> like 0, but with Steele & White stopping rule; - e.g. with IEEE P754 arithmetic , mode 0 gives - 1e23 whereas mode 1 gives 9.999999999999999e22. - 2 ==> max(1,ndigits) significant digits. This gives a - return value similar to that of ecvt, except - that trailing zeros are suppressed. - 3 ==> through ndigits past the decimal point. This - gives a return value similar to that from fcvt, - except that trailing zeros are suppressed, and - ndigits can be negative. - 4,5 ==> similar to 2 and 3, respectively, but (in - round-nearest mode) with the tests of mode 0 to - possibly return a shorter string that rounds to d. - With IEEE arithmetic and compilation with - -DHonor_FLT_ROUNDS, modes 4 and 5 behave the same - as modes 2 and 3 when FLT_ROUNDS != 1. - 6-9 ==> Debugging modes similar to mode - 4: don't try - fast floating-point estimate (if applicable). - - Values of mode other than 0-9 are treated as mode 0. - - Sufficient space is allocated to the return value - to hold the suppressed trailing zeros. - */ - - int bbits, b2, b5, be, dig, i, ieps, ilim, ilim0, ilim1, - j, j1, k, k0, k_check, leftright, m2, m5, s2, s5, - spec_case, try_quick, bias_round_up; - Long L; -#ifndef Sudden_Underflow - int denorm; - ULong x; -#endif - Bigint *b, *b1, *delta, *mlo, *mhi, *S; - double ds; - U d2, eps; - char *s, *s0; -#ifdef Honor_FLT_ROUNDS - int rounding; -#endif -#ifdef SET_INEXACT - int inexact, oldinexact; -#endif - U d; - dval(d) = dd; - - /* In mode 2 and 3 we bias rounding up when there are ties. */ - bias_round_up = mode == 2 || mode == 3; - - ilim = ilim1 = 0; /* to avoid Google3 compiler warnings */ - -#ifndef MULTIPLE_THREADS - if (dtoa_result) { - freedtoa(dtoa_result); - dtoa_result = 0; - } -#endif - - if (word0(d) & Sign_bit) { - /* set sign for everything, including 0's and NaNs */ - *sign = 1; - word0(d) &= ~Sign_bit; /* clear sign bit */ - } - else - *sign = 0; - -#if defined(IEEE_Arith) + defined(VAX) -#ifdef IEEE_Arith - if ((word0(d) & Exp_mask) == Exp_mask) -#else - if (word0(d) == 0x8000) -#endif - { - /* Infinity or NaN */ - *decpt = 9999; -#ifdef IEEE_Arith - if (!word1(d) && !(word0(d) & 0xfffff)) - return nrv_alloc("Infinity", rve, 8); -#endif - return nrv_alloc("NaN", rve, 3); - } -#endif -#ifdef IBM - dval(d) += 0; /* normalize */ -#endif - if (!dval(d)) { - *decpt = 1; - return nrv_alloc("0", rve, 1); - } - -#ifdef SET_INEXACT - try_quick = oldinexact = get_inexact(); - inexact = 1; -#endif -#ifdef Honor_FLT_ROUNDS - if ((rounding = Flt_Rounds) >= 2) { - if (*sign) - rounding = rounding == 2 ? 0 : 2; - else - if (rounding != 2) - rounding = 0; - } -#endif - - b = d2b(dval(d), &be, &bbits); -#ifdef Sudden_Underflow - i = (int)(word0(d) >> Exp_shift1 & (Exp_mask>>Exp_shift1)); -#else - if ((i = (int)(word0(d) >> Exp_shift1 & (Exp_mask>>Exp_shift1)))) { -#endif - dval(d2) = dval(d); - word0(d2) &= Frac_mask1; - word0(d2) |= Exp_11; -#ifdef IBM - if (j = 11 - hi0bits(word0(d2) & Frac_mask)) - dval(d2) /= 1 << j; -#endif - - /* log(x) ~=~ log(1.5) + (x-1.5)/1.5 - * log10(x) = log(x) / log(10) - * ~=~ log(1.5)/log(10) + (x-1.5)/(1.5*log(10)) - * log10(d) = (i-Bias)*log(2)/log(10) + log10(d2) - * - * This suggests computing an approximation k to log10(d) by - * - * k = (i - Bias)*0.301029995663981 - * + ( (d2-1.5)*0.289529654602168 + 0.176091259055681 ); - * - * We want k to be too large rather than too small. - * The error in the first-order Taylor series approximation - * is in our favor, so we just round up the constant enough - * to compensate for any error in the multiplication of - * (i - Bias) by 0.301029995663981; since |i - Bias| <= 1077, - * and 1077 * 0.30103 * 2^-52 ~=~ 7.2e-14, - * adding 1e-13 to the constant term more than suffices. - * Hence we adjust the constant term to 0.1760912590558. - * (We could get a more accurate k by invoking log10, - * but this is probably not worthwhile.) - */ - - i -= Bias; -#ifdef IBM - i <<= 2; - i += j; -#endif -#ifndef Sudden_Underflow - denorm = 0; - } - else { - /* d is denormalized */ - - i = bbits + be + (Bias + (P-1) - 1); - x = i > 32 ? (word0(d) << (64 - i)) | (word1(d) >> (i - 32)) - : word1(d) << (32 - i); - dval(d2) = x; - word0(d2) -= 31*Exp_msk1; /* adjust exponent */ - i -= (Bias + (P-1) - 1) + 1; - denorm = 1; - } -#endif - ds = (dval(d2)-1.5)*0.289529654602168 + 0.1760912590558 + i*0.301029995663981; - k = (int)ds; - if (ds < 0. && ds != k) - k--; /* want k = floor(ds) */ - k_check = 1; - if (k >= 0 && k <= Ten_pmax) { - if (dval(d) < tens[k]) - k--; - k_check = 0; - } - j = bbits - i - 1; - if (j >= 0) { - b2 = 0; - s2 = j; - } - else { - b2 = -j; - s2 = 0; - } - if (k >= 0) { - b5 = 0; - s5 = k; - s2 += k; - } - else { - b2 -= k; - b5 = -k; - s5 = 0; - } - if (mode < 0 || mode > 9) - mode = 0; - -#ifndef SET_INEXACT -#ifdef Check_FLT_ROUNDS - try_quick = Rounding == 1; -#else - try_quick = 1; -#endif -#endif /*SET_INEXACT*/ - - if (mode > 5) { - mode -= 4; - try_quick = 0; - } - leftright = 1; - switch(mode) { - case 0: - case 1: - ilim = ilim1 = -1; - i = 18; - ndigits = 0; - break; - case 2: - leftright = 0; - /* no break */ - case 4: - if (ndigits <= 0) - ndigits = 1; - ilim = ilim1 = i = ndigits; - break; - case 3: - leftright = 0; - /* no break */ - case 5: - i = ndigits + k + 1; - ilim = i; - ilim1 = i - 1; - if (i <= 0) - i = 1; - } - s = s0 = rv_alloc(i); - -#ifdef Honor_FLT_ROUNDS - if (mode > 1 && rounding != 1) - leftright = 0; -#endif - - if (ilim >= 0 && ilim <= Quick_max && try_quick) { - - /* Try to get by with floating-point arithmetic. */ - - i = 0; - dval(d2) = dval(d); - k0 = k; - ilim0 = ilim; - ieps = 2; /* conservative */ - if (k > 0) { - ds = tens[k&0xf]; - j = k >> 4; - if (j & Bletch) { - /* prevent overflows */ - j &= Bletch - 1; - dval(d) /= bigtens[n_bigtens-1]; - ieps++; - } - for(; j; j >>= 1, i++) - if (j & 1) { - ieps++; - ds *= bigtens[i]; - } - dval(d) /= ds; - } - else if ((j1 = -k)) { - dval(d) *= tens[j1 & 0xf]; - for(j = j1 >> 4; j; j >>= 1, i++) - if (j & 1) { - ieps++; - dval(d) *= bigtens[i]; - } - } - if (k_check && dval(d) < 1. && ilim > 0) { - if (ilim1 <= 0) - goto fast_failed; - ilim = ilim1; - k--; - dval(d) *= 10.; - ieps++; - } - dval(eps) = ieps*dval(d) + 7.; - word0(eps) -= (P-1)*Exp_msk1; - if (ilim == 0) { - S = mhi = 0; - dval(d) -= 5.; - if (dval(d) > dval(eps)) - goto one_digit; - if (dval(d) < -dval(eps)) - goto no_digits; - goto fast_failed; - } -#ifndef No_leftright - if (leftright) { - /* Use Steele & White method of only - * generating digits needed. - */ - dval(eps) = 0.5/tens[ilim-1] - dval(eps); - for(i = 0;;) { - L = dval(d); - dval(d) -= L; - *s++ = '0' + (int)L; - if (dval(d) < dval(eps)) - goto ret1; - if (1. - dval(d) < dval(eps)) - goto bump_up; - if (++i >= ilim) - break; - dval(eps) *= 10.; - dval(d) *= 10.; - } - } - else { -#endif - /* Generate ilim digits, then fix them up. */ - dval(eps) *= tens[ilim-1]; - for(i = 1;; i++, dval(d) *= 10.) { - L = (Long)(dval(d)); - if (!(dval(d) -= L)) - ilim = i; - *s++ = '0' + (int)L; - if (i == ilim) { - if (dval(d) > 0.5 + dval(eps)) - goto bump_up; - else if (dval(d) < 0.5 - dval(eps)) { - while(*--s == '0'); - s++; - goto ret1; - } - break; - } - } -#ifndef No_leftright - } -#endif - fast_failed: - s = s0; - dval(d) = dval(d2); - k = k0; - ilim = ilim0; - } - - /* Do we have a "small" integer? */ - - if (be >= 0 && k <= Int_max) { - /* Yes. */ - ds = tens[k]; - if (ndigits < 0 && ilim <= 0) { - S = mhi = 0; - if (ilim < 0 || dval(d) < 5*ds || ((dval(d) == 5*ds) && !bias_round_up)) - goto no_digits; - goto one_digit; - } - - /* Limit looping by the number of digits to produce. - * Firefox had a crash bug because some plugins reduce - * the precision of double arithmetic. With reduced - * precision "dval(d) -= L*ds" might be imprecise and - * d might not become zero and the loop might not - * terminate. - * - * See https://bugzilla.mozilla.org/show_bug.cgi?id=358569 - */ - for(i = 1; i <= k+1; i++, dval(d) *= 10.) { - L = (Long)(dval(d) / ds); - dval(d) -= L*ds; -#ifdef Check_FLT_ROUNDS - /* If FLT_ROUNDS == 2, L will usually be high by 1 */ - if (dval(d) < 0) { - L--; - dval(d) += ds; - } -#endif - *s++ = '0' + (int)L; - if (!dval(d)) { -#ifdef SET_INEXACT - inexact = 0; -#endif - break; - } - if (i == ilim) { -#ifdef Honor_FLT_ROUNDS - if (mode > 1) - switch(rounding) { - case 0: goto ret1; - case 2: goto bump_up; - } -#endif - dval(d) += dval(d); - if (dval(d) > ds || (dval(d) == ds && ((L & 1) || bias_round_up))) { - bump_up: - while(*--s == '9') - if (s == s0) { - k++; - *s = '0'; - break; - } - ++*s++; - } - break; - } - } - goto ret1; - } - - m2 = b2; - m5 = b5; - mhi = mlo = 0; - if (leftright) { - i = -#ifndef Sudden_Underflow - denorm ? be + (Bias + (P-1) - 1 + 1) : -#endif -#ifdef IBM - 1 + 4*P - 3 - bbits + ((bbits + be - 1) & 3); -#else - 1 + P - bbits; -#endif - b2 += i; - s2 += i; - mhi = i2b(1); - } - if (m2 > 0 && s2 > 0) { - i = m2 < s2 ? m2 : s2; - b2 -= i; - m2 -= i; - s2 -= i; - } - if (b5 > 0) { - if (leftright) { - if (m5 > 0) { - mhi = pow5mult(mhi, m5); - b1 = mult(mhi, b); - Bfree(b); - b = b1; - } - if ((j = b5 - m5)) - b = pow5mult(b, j); - } - else - b = pow5mult(b, b5); - } - S = i2b(1); - if (s5 > 0) - S = pow5mult(S, s5); - - /* Check for special case that d is a normalized power of 2. */ - - spec_case = 0; - if ((mode < 2 || leftright) -#ifdef Honor_FLT_ROUNDS - && rounding == 1 -#endif - ) { - if (!word1(d) && !(word0(d) & Bndry_mask) -#ifndef Sudden_Underflow - && word0(d) & (Exp_mask & ~Exp_msk1) -#endif - ) { - /* The special case */ - b2 += Log2P; - s2 += Log2P; - spec_case = 1; - } - } - - /* Arrange for convenient computation of quotients: - * shift left if necessary so divisor has 4 leading 0 bits. - * - * Perhaps we should just compute leading 28 bits of S once - * and for all and pass them and a shift to quorem, so it - * can do shifts and ors to compute the numerator for q. - */ -#ifdef Pack_32 - if ((i = ((s5 ? 32 - hi0bits(S->x[S->wds-1]) : 1) + s2) & 0x1f)) - i = 32 - i; -#else - if ((i = ((s5 ? 32 - hi0bits(S->x[S->wds-1]) : 1) + s2) & 0xf)) - i = 16 - i; -#endif - if (i > 4) { - i -= 4; - b2 += i; - m2 += i; - s2 += i; - } - else if (i < 4) { - i += 28; - b2 += i; - m2 += i; - s2 += i; - } - if (b2 > 0) - b = lshift(b, b2); - if (s2 > 0) - S = lshift(S, s2); - if (k_check) { - if (cmp(b,S) < 0) { - k--; - b = multadd(b, 10, 0); /* we botched the k estimate */ - if (leftright) - mhi = multadd(mhi, 10, 0); - ilim = ilim1; - } - } - if (ilim <= 0 && (mode == 3 || mode == 5)) { - S = multadd(S, 5, 0); - if (ilim < 0 || cmp(b, S) < 0 || ((cmp(b, S) == 0) && !bias_round_up)) { - /* no digits, fcvt style */ - no_digits: - k = -1 - ndigits; - goto ret; - } - one_digit: - *s++ = '1'; - k++; - goto ret; - } - if (leftright) { - if (m2 > 0) - mhi = lshift(mhi, m2); - - /* Compute mlo -- check for special case - * that d is a normalized power of 2. - */ - - mlo = mhi; - if (spec_case) { - mhi = Balloc(mhi->k); - Bcopy(mhi, mlo); - mhi = lshift(mhi, Log2P); - } - - for(i = 1;;i++) { - dig = quorem(b,S) + '0'; - /* Do we yet have the shortest decimal string - * that will round to d? - */ - j = cmp(b, mlo); - delta = diff(S, mhi); - j1 = delta->sign ? 1 : cmp(b, delta); - Bfree(delta); -#ifndef ROUND_BIASED - if (j1 == 0 && mode != 1 && !(word1(d) & 1) -#ifdef Honor_FLT_ROUNDS - && rounding >= 1 -#endif - ) { - if (dig == '9') - goto round_9_up; - if (j > 0) - dig++; -#ifdef SET_INEXACT - else if (!b->x[0] && b->wds <= 1) - inexact = 0; -#endif - *s++ = dig; - goto ret; - } -#endif - if (j < 0 || (j == 0 && mode != 1 -#ifndef ROUND_BIASED - && !(word1(d) & 1) -#endif - )) { - if (!b->x[0] && b->wds <= 1) { -#ifdef SET_INEXACT - inexact = 0; -#endif - goto accept_dig; - } -#ifdef Honor_FLT_ROUNDS - if (mode > 1) - switch(rounding) { - case 0: goto accept_dig; - case 2: goto keep_dig; - } -#endif /*Honor_FLT_ROUNDS*/ - if (j1 > 0) { - b = lshift(b, 1); - j1 = cmp(b, S); - if ((j1 > 0 || (j1 == 0 && ((dig & 1) || bias_round_up))) - && dig++ == '9') - goto round_9_up; - } - accept_dig: - *s++ = dig; - goto ret; - } - if (j1 > 0) { -#ifdef Honor_FLT_ROUNDS - if (!rounding) - goto accept_dig; -#endif - if (dig == '9') { /* possible if i == 1 */ - round_9_up: - *s++ = '9'; - goto roundoff; - } - *s++ = dig + 1; - goto ret; - } -#ifdef Honor_FLT_ROUNDS - keep_dig: -#endif - *s++ = dig; - if (i == ilim) - break; - b = multadd(b, 10, 0); - if (mlo == mhi) - mlo = mhi = multadd(mhi, 10, 0); - else { - mlo = multadd(mlo, 10, 0); - mhi = multadd(mhi, 10, 0); - } - } - } - else - for(i = 1;; i++) { - *s++ = dig = quorem(b,S) + '0'; - if (!b->x[0] && b->wds <= 1) { -#ifdef SET_INEXACT - inexact = 0; -#endif - goto ret; - } - if (i >= ilim) - break; - b = multadd(b, 10, 0); - } - - /* Round off last digit */ - -#ifdef Honor_FLT_ROUNDS - switch(rounding) { - case 0: goto trimzeros; - case 2: goto roundoff; - } -#endif - b = lshift(b, 1); - j = cmp(b, S); - if (j > 0 || (j == 0 && ((dig & 1) || bias_round_up))) { - roundoff: - while(*--s == '9') - if (s == s0) { - k++; - *s++ = '1'; - goto ret; - } - ++*s++; - } - else { -/* trimzeros: (never used) */ - while(*--s == '0'); - s++; - } - ret: - Bfree(S); - if (mhi) { - if (mlo && mlo != mhi) - Bfree(mlo); - Bfree(mhi); - } - ret1: -#ifdef SET_INEXACT - if (inexact) { - if (!oldinexact) { - word0(d) = Exp_1 + (70 << Exp_shift); - word1(d) = 0; - dval(d) += 1.; - } - } - else if (!oldinexact) - clear_inexact(); -#endif - Bfree(b); - *s = 0; - *decpt = k + 1; - if (rve) - *rve = s; - return s0; - } -#ifdef __cplusplus -} -#endif diff --git a/src/token.cc b/src/token.cc index 21fa9ee4..488e9097 100644 --- a/src/token.cc +++ b/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/src/utils.h b/src/utils.h index 069be4f8..69c062fb 100644 --- a/src/utils.h +++ b/src/utils.h @@ -31,7 +31,9 @@ #include <stdlib.h> #include <string.h> +#include "globals.h" #include "checks.h" +#include "allocation.h" namespace v8 { namespace internal { @@ -324,6 +326,8 @@ class Vector { return start_[index]; } + T& at(int i) const { return operator[](i); } + T& first() { return start_[0]; } T& last() { return start_[length_ - 1]; } @@ -44,6 +44,7 @@ bool V8::has_been_setup_ = false; bool V8::has_been_disposed_ = false; bool V8::has_fatal_error_ = false; + bool V8::Initialize(Deserializer* des) { bool create_heap_objects = des == NULL; if (has_been_disposed_ || has_fatal_error_) return false; @@ -176,22 +177,41 @@ static uint32_t random_seed() { } -uint32_t V8::Random() { - // Random number generator using George Marsaglia's MWC algorithm. - static uint32_t hi = 0; - static uint32_t lo = 0; +typedef struct { + uint32_t hi; + uint32_t lo; +} random_state; + +// Random number generator using George Marsaglia's MWC algorithm. +static uint32_t random_base(random_state *state) { // Initialize seed using the system random(). If one of the seeds // should ever become zero again, or if random() returns zero, we // avoid getting stuck with zero bits in hi or lo by re-initializing // them on demand. - if (hi == 0) hi = random_seed(); - if (lo == 0) lo = random_seed(); + if (state->hi == 0) state->hi = random_seed(); + if (state->lo == 0) state->lo = random_seed(); // Mix the bits. - hi = 36969 * (hi & 0xFFFF) + (hi >> 16); - lo = 18273 * (lo & 0xFFFF) + (lo >> 16); - return (hi << 16) + (lo & 0xFFFF); + state->hi = 36969 * (state->hi & 0xFFFF) + (state->hi >> 16); + state->lo = 18273 * (state->lo & 0xFFFF) + (state->lo >> 16); + return (state->hi << 16) + (state->lo & 0xFFFF); +} + + +// Used by JavaScript APIs +uint32_t V8::Random() { + static random_state state = {0, 0}; + return random_base(&state); +} + + +// Used internally by the JIT and memory allocator for security +// purposes. So, we keep a different state to prevent informations +// leaks that could be used in an exploit. +uint32_t V8::RandomPrivate() { + static random_state state = {0, 0}; + return random_base(&state); } @@ -53,8 +53,8 @@ // Basic includes #include "../include/v8.h" -#include "globals.h" -#include "checks.h" +#include "v8globals.h" +#include "v8checks.h" #include "allocation.h" #include "v8utils.h" #include "flags.h" @@ -94,6 +94,11 @@ class V8 : public AllStatic { // Random number generation support. Not cryptographically safe. static uint32_t Random(); + // We use random numbers internally in memory allocation and in the + // compilers for security. In order to prevent information leaks we + // use a separate random state for internal random number + // generation. + static uint32_t RandomPrivate(); static Object* FillHeapNumberWithRandom(Object* heap_number); // Idle notification directly from the API. diff --git a/src/v8checks.h b/src/v8checks.h new file mode 100644 index 00000000..9857f73d --- /dev/null +++ b/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 T> class Handle; + +namespace internal { + intptr_t HeapObjectTagMask(); + +} } // namespace v8::internal + + +void CheckNonEqualsHelper(const char* file, + int line, + const char* unexpected_source, + v8::Handle<v8::Value> unexpected, + const char* value_source, + v8::Handle<v8::Value> value); + +void CheckEqualsHelper(const char* file, + int line, + const char* expected_source, + v8::Handle<v8::Value> expected, + const char* value_source, + v8::Handle<v8::Value> value); + +#define ASSERT_TAG_ALIGNED(address) \ + ASSERT((reinterpret_cast<intptr_t>(address) & HeapObjectTagMask()) == 0) + +#define ASSERT_SIZE_TAG_ALIGNED(size) ASSERT((size & HeapObjectTagMask()) == 0) + +#endif // V8_V8CHECKS_H_ diff --git a/src/v8globals.h b/src/v8globals.h new file mode 100644 index 00000000..2815771a --- /dev/null +++ b/src/v8globals.h @@ -0,0 +1,464 @@ +// Copyright 2010 the V8 project authors. All rights reserved. +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following +// disclaimer in the documentation and/or other materials provided +// with the distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +#ifndef V8_V8GLOBALS_H_ +#define V8_V8GLOBALS_H_ + +#include "globals.h" + +namespace v8 { +namespace internal { + +// This file contains constants and global declarations related to the +// V8 system. + +// Mask for the sign bit in a smi. +const intptr_t kSmiSignMask = kIntptrSignBit; + +const int kObjectAlignmentBits = kPointerSizeLog2; +const intptr_t kObjectAlignment = 1 << kObjectAlignmentBits; +const intptr_t kObjectAlignmentMask = kObjectAlignment - 1; + +// Desired alignment for pointers. +const intptr_t kPointerAlignment = (1 << kPointerSizeLog2); +const intptr_t kPointerAlignmentMask = kPointerAlignment - 1; + +// Desired alignment for maps. +#if V8_HOST_ARCH_64_BIT +const intptr_t kMapAlignmentBits = kObjectAlignmentBits; +#else +const intptr_t kMapAlignmentBits = kObjectAlignmentBits + 3; +#endif +const intptr_t kMapAlignment = (1 << kMapAlignmentBits); +const intptr_t kMapAlignmentMask = kMapAlignment - 1; + +// Desired alignment for generated code is 32 bytes (to improve cache line +// utilization). +const int kCodeAlignmentBits = 5; +const intptr_t kCodeAlignment = 1 << kCodeAlignmentBits; +const intptr_t kCodeAlignmentMask = kCodeAlignment - 1; + +// Tag information for Failure. +const int kFailureTag = 3; +const int kFailureTagSize = 2; +const intptr_t kFailureTagMask = (1 << kFailureTagSize) - 1; + + +// Zap-value: The value used for zapping dead objects. +// Should be a recognizable hex value tagged as a heap object pointer. +#ifdef V8_HOST_ARCH_64_BIT +const Address kZapValue = + reinterpret_cast<Address>(V8_UINT64_C(0xdeadbeedbeadbeed)); +const Address kHandleZapValue = + reinterpret_cast<Address>(V8_UINT64_C(0x1baddead0baddead)); +const Address kFromSpaceZapValue = + reinterpret_cast<Address>(V8_UINT64_C(0x1beefdad0beefdad)); +const uint64_t kDebugZapValue = 0xbadbaddbbadbaddb; +#else +const Address kZapValue = reinterpret_cast<Address>(0xdeadbeed); +const Address kHandleZapValue = reinterpret_cast<Address>(0xbaddead); +const Address kFromSpaceZapValue = reinterpret_cast<Address>(0xbeefdad); +const uint32_t kDebugZapValue = 0xbadbaddb; +#endif + + +// Number of bits to represent the page size for paged spaces. The value of 13 +// gives 8K bytes per page. +const int kPageSizeBits = 13; + +// On Intel architecture, cache line size is 64 bytes. +// On ARM it may be less (32 bytes), but as far this constant is +// used for aligning data, it doesn't hurt to align on a greater value. +const int kProcessorCacheLineSize = 64; + +// Constants relevant to double precision floating point numbers. + +// Quiet NaNs have bits 51 to 62 set, possibly the sign bit, and no +// other bits set. +const uint64_t kQuietNaNMask = static_cast<uint64_t>(0xfff) << 51; +// If looking only at the top 32 bits, the QNaN mask is bits 19 to 30. +const uint32_t kQuietNaNHighBitsMask = 0xfff << (51 - 32); + + +// ----------------------------------------------------------------------------- +// Forward declarations for frequently used classes +// (sorted alphabetically) + +class AccessorInfo; +class Allocation; +class Arguments; +class Assembler; +class AssertNoAllocation; +class BreakableStatement; +class Code; +class CodeGenerator; +class CodeStub; +class Context; +class Debug; +class Debugger; +class DebugInfo; +class Descriptor; +class DescriptorArray; +class Expression; +class ExternalReference; +class FixedArray; +class FunctionEntry; +class FunctionLiteral; +class FunctionTemplateInfo; +class NumberDictionary; +class StringDictionary; +template <typename T> class Handle; +class Heap; +class HeapObject; +class IC; +class InterceptorInfo; +class IterationStatement; +class JSArray; +class JSFunction; +class JSObject; +class LargeObjectSpace; +class LookupResult; +class MacroAssembler; +class Map; +class MapSpace; +class MarkCompactCollector; +class NewSpace; +class NodeVisitor; +class Object; +class MaybeObject; +class OldSpace; +class Property; +class Proxy; +class RegExpNode; +struct RegExpCompileData; +class RegExpTree; +class RegExpCompiler; +class RegExpVisitor; +class Scope; +template<class Allocator = FreeStoreAllocationPolicy> class ScopeInfo; +class SerializedScopeInfo; +class Script; +class Slot; +class Smi; +template <typename Config, class Allocator = FreeStoreAllocationPolicy> + class SplayTree; +class Statement; +class String; +class Struct; +class SwitchStatement; +class AstVisitor; +class Variable; +class VariableProxy; +class RelocInfo; +class Deserializer; +class MessageLocation; +class ObjectGroup; +class TickSample; +class VirtualMemory; +class Mutex; + +typedef bool (*WeakSlotCallback)(Object** pointer); + +// ----------------------------------------------------------------------------- +// Miscellaneous + +// NOTE: SpaceIterator depends on AllocationSpace enumeration values being +// consecutive. +enum AllocationSpace { + NEW_SPACE, // Semispaces collected with copying collector. + OLD_POINTER_SPACE, // May contain pointers to new space. + OLD_DATA_SPACE, // Must not have pointers to new space. + CODE_SPACE, // No pointers to new space, marked executable. + MAP_SPACE, // Only and all map objects. + CELL_SPACE, // Only and all cell objects. + LO_SPACE, // Promoted large objects. + + FIRST_SPACE = NEW_SPACE, + LAST_SPACE = LO_SPACE, + FIRST_PAGED_SPACE = OLD_POINTER_SPACE, + LAST_PAGED_SPACE = CELL_SPACE +}; +const int kSpaceTagSize = 3; +const int kSpaceTagMask = (1 << kSpaceTagSize) - 1; + + +// A flag that indicates whether objects should be pretenured when +// allocated (allocated directly into the old generation) or not +// (allocated in the young generation if the object size and type +// allows). +enum PretenureFlag { NOT_TENURED, TENURED }; + +enum GarbageCollector { SCAVENGER, MARK_COMPACTOR }; + +enum Executability { NOT_EXECUTABLE, EXECUTABLE }; + +enum VisitMode { VISIT_ALL, VISIT_ALL_IN_SCAVENGE, VISIT_ONLY_STRONG }; + +// Flag indicating whether code is built into the VM (one of the natives files). +enum NativesFlag { NOT_NATIVES_CODE, NATIVES_CODE }; + + +// A CodeDesc describes a buffer holding instructions and relocation +// information. The instructions start at the beginning of the buffer +// and grow forward, the relocation information starts at the end of +// the buffer and grows backward. +// +// |<--------------- buffer_size ---------------->| +// |<-- instr_size -->| |<-- reloc_size -->| +// +==================+========+==================+ +// | instructions | free | reloc info | +// +==================+========+==================+ +// ^ +// | +// buffer + +struct CodeDesc { + byte* buffer; + int buffer_size; + int instr_size; + int reloc_size; + Assembler* origin; +}; + + +// Callback function on object slots, used for iterating heap object slots in +// HeapObjects, global pointers to heap objects, etc. The callback allows the +// callback function to change the value of the slot. +typedef void (*ObjectSlotCallback)(HeapObject** pointer); + + +// Callback function used for iterating objects in heap spaces, +// for example, scanning heap objects. +typedef int (*HeapObjectCallback)(HeapObject* obj); + + +// Callback function used for checking constraints when copying/relocating +// objects. Returns true if an object can be copied/relocated from its +// old_addr to a new_addr. +typedef bool (*ConstraintCallback)(Address new_addr, Address old_addr); + + +// Callback function on inline caches, used for iterating over inline caches +// in compiled code. +typedef void (*InlineCacheCallback)(Code* code, Address ic); + + +// State for inline cache call sites. Aliased as IC::State. +enum InlineCacheState { + // Has never been executed. + UNINITIALIZED, + // Has been executed but monomorhic state has been delayed. + PREMONOMORPHIC, + // Has been executed and only one receiver type has been seen. + MONOMORPHIC, + // Like MONOMORPHIC but check failed due to prototype. + MONOMORPHIC_PROTOTYPE_FAILURE, + // Multiple receiver types have been seen. + MEGAMORPHIC, + // Special states for debug break or step in prepare stubs. + DEBUG_BREAK, + DEBUG_PREPARE_STEP_IN +}; + + +enum InLoopFlag { + NOT_IN_LOOP, + IN_LOOP +}; + + +enum CallFunctionFlags { + NO_CALL_FUNCTION_FLAGS = 0, + RECEIVER_MIGHT_BE_VALUE = 1 << 0 // Receiver might not be a JSObject. +}; + + +enum InlineCacheHolderFlag { + OWN_MAP, // For fast properties objects. + PROTOTYPE_MAP // For slow properties objects (except GlobalObjects). +}; + + +// Type of properties. +// Order of properties is significant. +// Must fit in the BitField PropertyDetails::TypeField. +// A copy of this is in mirror-debugger.js. +enum PropertyType { + NORMAL = 0, // only in slow mode + FIELD = 1, // only in fast mode + CONSTANT_FUNCTION = 2, // only in fast mode + CALLBACKS = 3, + INTERCEPTOR = 4, // only in lookup results, not in descriptors. + MAP_TRANSITION = 5, // only in fast mode + CONSTANT_TRANSITION = 6, // only in fast mode + NULL_DESCRIPTOR = 7, // only in fast mode + // All properties before MAP_TRANSITION are real. + FIRST_PHANTOM_PROPERTY_TYPE = MAP_TRANSITION, + // There are no IC stubs for NULL_DESCRIPTORS. Therefore, + // NULL_DESCRIPTOR can be used as the type flag for IC stubs for + // nonexistent properties. + NONEXISTENT = NULL_DESCRIPTOR +}; + + +// Whether to remove map transitions and constant transitions from a +// DescriptorArray. +enum TransitionFlag { + REMOVE_TRANSITIONS, + KEEP_TRANSITIONS +}; + + +// Union used for fast testing of specific double values. +union DoubleRepresentation { + double value; + int64_t bits; + DoubleRepresentation(double x) { value = x; } +}; + + +// Union used for customized checking of the IEEE double types +// inlined within v8 runtime, rather than going to the underlying +// platform headers and libraries +union IeeeDoubleLittleEndianArchType { + double d; + struct { + unsigned int man_low :32; + unsigned int man_high :20; + unsigned int exp :11; + unsigned int sign :1; + } bits; +}; + + +union IeeeDoubleBigEndianArchType { + double d; + struct { + unsigned int sign :1; + unsigned int exp :11; + unsigned int man_high :20; + unsigned int man_low :32; + } bits; +}; + + +// AccessorCallback +struct AccessorDescriptor { + MaybeObject* (*getter)(Object* object, void* data); + MaybeObject* (*setter)(JSObject* object, Object* value, void* data); + void* data; +}; + + +// Logging and profiling. +// A StateTag represents a possible state of the VM. When compiled with +// ENABLE_VMSTATE_TRACKING, the logger maintains a stack of these. +// Creating a VMState object enters a state by pushing on the stack, and +// destroying a VMState object leaves a state by popping the current state +// from the stack. + +#define STATE_TAG_LIST(V) \ + V(JS) \ + V(GC) \ + V(COMPILER) \ + V(OTHER) \ + V(EXTERNAL) + +enum StateTag { +#define DEF_STATE_TAG(name) name, + STATE_TAG_LIST(DEF_STATE_TAG) +#undef DEF_STATE_TAG + // Pseudo-types. + state_tag_count +}; + + +// ----------------------------------------------------------------------------- +// Macros + +// Testers for test. + +#define HAS_SMI_TAG(value) \ + ((reinterpret_cast<intptr_t>(value) & kSmiTagMask) == kSmiTag) + +#define HAS_FAILURE_TAG(value) \ + ((reinterpret_cast<intptr_t>(value) & kFailureTagMask) == kFailureTag) + +// OBJECT_POINTER_ALIGN returns the value aligned as a HeapObject pointer +#define OBJECT_POINTER_ALIGN(value) \ + (((value) + kObjectAlignmentMask) & ~kObjectAlignmentMask) + +// POINTER_SIZE_ALIGN returns the value aligned as a pointer. +#define POINTER_SIZE_ALIGN(value) \ + (((value) + kPointerAlignmentMask) & ~kPointerAlignmentMask) + +// MAP_POINTER_ALIGN returns the value aligned as a map pointer. +#define MAP_POINTER_ALIGN(value) \ + (((value) + kMapAlignmentMask) & ~kMapAlignmentMask) + +// CODE_POINTER_ALIGN returns the value aligned as a generated code segment. +#define CODE_POINTER_ALIGN(value) \ + (((value) + kCodeAlignmentMask) & ~kCodeAlignmentMask) + +// Support for tracking C++ memory allocation. Insert TRACK_MEMORY("Fisk") +// inside a C++ class and new and delete will be overloaded so logging is +// performed. +// This file (globals.h) is included before log.h, so we use direct calls to +// the Logger rather than the LOG macro. +#ifdef DEBUG +#define TRACK_MEMORY(name) \ + void* operator new(size_t size) { \ + void* result = ::operator new(size); \ + Logger::NewEvent(name, result, size); \ + return result; \ + } \ + void operator delete(void* object) { \ + Logger::DeleteEvent(name, object); \ + ::operator delete(object); \ + } +#else +#define TRACK_MEMORY(name) +#endif + + +// Feature flags bit positions. They are mostly based on the CPUID spec. +// (We assign CPUID itself to one of the currently reserved bits -- +// feel free to change this if needed.) +// On X86/X64, values below 32 are bits in EDX, values above 32 are bits in ECX. +enum CpuFeature { SSE4_1 = 32 + 19, // x86 + SSE3 = 32 + 0, // x86 + SSE2 = 26, // x86 + CMOV = 15, // x86 + RDTSC = 4, // x86 + CPUID = 10, // x86 + VFP3 = 1, // ARM + ARMv7 = 2, // ARM + SAHF = 0}; // x86 + +} } // namespace v8::internal + +#endif // V8_V8GLOBALS_H_ diff --git a/src/version.cc b/src/version.cc index b45510ca..6e5b68f2 100644 --- a/src/version.cc +++ b/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 6 -#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/src/virtual-frame.h b/src/virtual-frame.h index 220823ed..65d10098 100644 --- a/src/virtual-frame.h +++ b/src/virtual-frame.h @@ -31,6 +31,9 @@ #include "frame-element.h" #include "macro-assembler.h" +#include "list-inl.h" +#include "utils.h" + #if V8_TARGET_ARCH_IA32 #include "ia32/virtual-frame-ia32.h" #elif V8_TARGET_ARCH_X64 @@ -43,4 +46,14 @@ #error Unsupported target architecture. #endif +namespace v8 { +namespace internal { + +// Add() on List is inlined, ResizeAdd() called by Add() is inlined except for +// Lists of FrameElements, and ResizeAddInternal() is inlined in ResizeAdd(). +template <> +void List<FrameElement, + FreeStoreAllocationPolicy>::ResizeAdd(const FrameElement& element); +} } // namespace v8::internal + #endif // V8_VIRTUAL_FRAME_H_ diff --git a/src/x64/code-stubs-x64.cc b/src/x64/code-stubs-x64.cc index c179769b..14e35273 100644 --- a/src/x64/code-stubs-x64.cc +++ b/src/x64/code-stubs-x64.cc @@ -80,8 +80,9 @@ void FastNewClosureStub::Generate(MacroAssembler* masm) { __ pop(rdx); __ push(rsi); __ push(rdx); + __ Push(Factory::false_value()); __ push(rcx); // Restore return address. - __ TailCallRuntime(Runtime::kNewClosure, 2, 1); + __ TailCallRuntime(Runtime::kNewClosure, 3, 1); } @@ -2483,20 +2484,6 @@ void CEntryStub::GenerateThrowTOS(MacroAssembler* masm) { } -void ApiGetterEntryStub::Generate(MacroAssembler* masm) { - __ PrepareCallApiFunction(kStackSpace); -#ifdef _WIN64 - // All the parameters should be set up by a caller. -#else - // Set 1st parameter register with property name. - __ movq(rsi, rdx); - // Second parameter register rdi should be set with pointer to AccessorInfo - // by a caller. -#endif - __ CallApiFunctionAndReturn(fun()); -} - - void CEntryStub::GenerateCore(MacroAssembler* masm, Label* throw_normal_exception, Label* throw_termination_exception, @@ -2549,18 +2536,18 @@ void CEntryStub::GenerateCore(MacroAssembler* masm, #ifdef _WIN64 // Windows 64-bit ABI passes arguments in rcx, rdx, r8, r9 // Store Arguments object on stack, below the 4 WIN64 ABI parameter slots. - __ movq(Operand(rsp, 4 * kPointerSize), r14); // argc. - __ movq(Operand(rsp, 5 * kPointerSize), r12); // argv. + __ movq(StackSpaceOperand(0), r14); // argc. + __ movq(StackSpaceOperand(1), r12); // argv. if (result_size_ < 2) { // Pass a pointer to the Arguments object as the first argument. // Return result in single register (rax). - __ lea(rcx, Operand(rsp, 4 * kPointerSize)); + __ lea(rcx, StackSpaceOperand(0)); } else { ASSERT_EQ(2, result_size_); // Pass a pointer to the result location as the first argument. - __ lea(rcx, Operand(rsp, 6 * kPointerSize)); + __ lea(rcx, StackSpaceOperand(2)); // Pass a pointer to the Arguments object as the second argument. - __ lea(rdx, Operand(rsp, 4 * kPointerSize)); + __ lea(rdx, StackSpaceOperand(0)); } #else // _WIN64 @@ -2596,7 +2583,7 @@ void CEntryStub::GenerateCore(MacroAssembler* masm, __ j(zero, &failure_returned); // Exit the JavaScript to C++ exit frame. - __ LeaveExitFrame(result_size_); + __ LeaveExitFrame(); __ ret(0); // Handling of failure. @@ -2700,7 +2687,12 @@ void CEntryStub::Generate(MacroAssembler* masm) { // builtin once. // Enter the exit frame that transitions from JavaScript to C++. - __ EnterExitFrame(result_size_); +#ifdef _WIN64 + int arg_stack_space = (result_size_ < 2 ? 2 : 4); +#else + int arg_stack_space = 0; +#endif + __ EnterExitFrame(arg_stack_space); // rax: Holds the context at this point, but should not be used. // On entry to code generated by GenerateCore, it must hold diff --git a/src/x64/codegen-x64.cc b/src/x64/codegen-x64.cc index e0e40950..5abf3c83 100644 --- a/src/x64/codegen-x64.cc +++ b/src/x64/codegen-x64.cc @@ -568,10 +568,10 @@ void CodeGenerator::Load(Expression* expr) { void CodeGenerator::LoadGlobal() { if (in_spilled_code()) { - frame_->EmitPush(GlobalObject()); + frame_->EmitPush(GlobalObjectOperand()); } else { Result temp = allocator_->Allocate(); - __ movq(temp.reg(), GlobalObject()); + __ movq(temp.reg(), GlobalObjectOperand()); frame_->Push(&temp); } } @@ -580,7 +580,7 @@ void CodeGenerator::LoadGlobal() { void CodeGenerator::LoadGlobalReceiver() { Result temp = allocator_->Allocate(); Register reg = temp.reg(); - __ movq(reg, GlobalObject()); + __ movq(reg, GlobalObjectOperand()); __ movq(reg, FieldOperand(reg, GlobalObject::kGlobalReceiverOffset)); frame_->Push(&temp); } @@ -4244,7 +4244,8 @@ void CodeGenerator::VisitDebuggerStatement(DebuggerStatement* node) { void CodeGenerator::InstantiateFunction( - Handle<SharedFunctionInfo> function_info) { + Handle<SharedFunctionInfo> function_info, + bool pretenure) { // The inevitable call will sync frame elements to memory anyway, so // we do it eagerly to allow us to push the arguments directly into // place. @@ -4252,7 +4253,9 @@ void CodeGenerator::InstantiateFunction( // Use the fast case closure allocation code that allocates in new // space for nested functions that don't need literals cloning. - if (scope()->is_function_scope() && function_info->num_literals() == 0) { + if (scope()->is_function_scope() && + function_info->num_literals() == 0 && + !pretenure) { FastNewClosureStub stub; frame_->Push(function_info); Result answer = frame_->CallStub(&stub, 1); @@ -4262,7 +4265,10 @@ void CodeGenerator::InstantiateFunction( // shared function info. frame_->EmitPush(rsi); frame_->EmitPush(function_info); - Result result = frame_->CallRuntime(Runtime::kNewClosure, 2); + frame_->EmitPush(pretenure + ? Factory::true_value() + : Factory::false_value()); + Result result = frame_->CallRuntime(Runtime::kNewClosure, 3); frame_->Push(&result); } } @@ -4279,14 +4285,14 @@ void CodeGenerator::VisitFunctionLiteral(FunctionLiteral* node) { SetStackOverflow(); return; } - InstantiateFunction(function_info); + InstantiateFunction(function_info, node->pretenure()); } void CodeGenerator::VisitSharedFunctionInfoLiteral( SharedFunctionInfoLiteral* node) { Comment cmnt(masm_, "[ SharedFunctionInfoLiteral"); - InstantiateFunction(node->shared_function_info()); + InstantiateFunction(node->shared_function_info(), false); } @@ -5592,6 +5598,18 @@ void CodeGenerator::VisitCall(Call* node) { // Push the receiver onto the frame. Load(property->obj()); + // Load the name of the function. + Load(property->key()); + + // Swap the name of the function and the receiver on the stack to follow + // the calling convention for call ICs. + Result key = frame_->Pop(); + Result receiver = frame_->Pop(); + frame_->Push(&key); + frame_->Push(&receiver); + key.Unuse(); + receiver.Unuse(); + // Load the arguments. int arg_count = args->length(); for (int i = 0; i < arg_count; i++) { @@ -5599,14 +5617,13 @@ void CodeGenerator::VisitCall(Call* node) { frame_->SpillTop(); } - // Load the name of the function. - Load(property->key()); - - // Call the IC initialization code. + // Place the key on top of stack and call the IC initialization code. + frame_->PushElementAt(arg_count + 1); CodeForSourcePosition(node->position()); Result result = frame_->CallKeyedCallIC(RelocInfo::CODE_TARGET, arg_count, loop_nesting()); + frame_->Drop(); // Drop the key still on the stack. frame_->RestoreContextRegister(); frame_->Push(&result); } @@ -6062,7 +6079,7 @@ class DeferredIsStringWrapperSafeForDefaultValueOf : public DeferredCode { __ movq(scratch2_, FieldOperand(scratch2_, GlobalObject::kGlobalContextOffset)); __ cmpq(scratch1_, - CodeGenerator::ContextOperand( + ContextOperand( scratch2_, Context::STRING_FUNCTION_PROTOTYPE_MAP_INDEX)); __ j(not_equal, &false_result); // Set the bit in the map to indicate that it has been checked safe for @@ -7206,6 +7223,11 @@ void CodeGenerator::GenerateGetCachedArrayIndex(ZoneList<Expression*>* args) { } +void CodeGenerator::GenerateFastAsciiArrayJoin(ZoneList<Expression*>* args) { + frame_->Push(Factory::undefined_value()); +} + + void CodeGenerator::VisitCallRuntime(CallRuntime* node) { if (CheckForInlineRuntimeCall(node)) { return; @@ -7219,7 +7241,7 @@ void CodeGenerator::VisitCallRuntime(CallRuntime* node) { // Push the builtins object found in the current global object. Result temp = allocator()->Allocate(); ASSERT(temp.is_valid()); - __ movq(temp.reg(), GlobalObject()); + __ movq(temp.reg(), GlobalObjectOperand()); __ movq(temp.reg(), FieldOperand(temp.reg(), GlobalObject::kBuiltinsOffset)); frame_->Push(&temp); diff --git a/src/x64/codegen-x64.h b/src/x64/codegen-x64.h index 1853c832..1a5e7df3 100644 --- a/src/x64/codegen-x64.h +++ b/src/x64/codegen-x64.h @@ -341,10 +341,6 @@ class CodeGenerator: public AstVisitor { bool in_spilled_code() const { return in_spilled_code_; } void set_in_spilled_code(bool flag) { in_spilled_code_ = flag; } - static Operand ContextOperand(Register context, int index) { - return Operand(context, Context::SlotOffset(index)); - } - private: // Type of a member function that generates inline code for a native function. typedef void (CodeGenerator::*InlineFunctionGenerator) @@ -417,10 +413,6 @@ class CodeGenerator: public AstVisitor { JumpTarget* slow); // Expressions - static Operand GlobalObject() { - return ContextOperand(rsi, Context::GLOBAL_INDEX); - } - void LoadCondition(Expression* x, ControlDestination* destination, bool force_control); @@ -588,16 +580,13 @@ class CodeGenerator: public AstVisitor { void ProcessDeclarations(ZoneList<Declaration*>* declarations); - static Handle<Code> ComputeCallInitialize(int argc, InLoopFlag in_loop); - - static Handle<Code> ComputeKeyedCallInitialize(int argc, InLoopFlag in_loop); - // Declare global variables and functions in the given array of // name/value pairs. void DeclareGlobals(Handle<FixedArray> pairs); // Instantiate the function based on the shared function info. - void InstantiateFunction(Handle<SharedFunctionInfo> function_info); + void InstantiateFunction(Handle<SharedFunctionInfo> function_info, + bool pretenure); // Support for type checks. void GenerateIsSmi(ZoneList<Expression*>* args); @@ -680,6 +669,7 @@ class CodeGenerator: public AstVisitor { void GenerateHasCachedArrayIndex(ZoneList<Expression*>* args); void GenerateGetCachedArrayIndex(ZoneList<Expression*>* args); + void GenerateFastAsciiArrayJoin(ZoneList<Expression*>* args); // Simple condition analysis. enum ConditionAnalysis { diff --git a/src/x64/full-codegen-x64.cc b/src/x64/full-codegen-x64.cc index 00ea6845..ee80169b 100644 --- a/src/x64/full-codegen-x64.cc +++ b/src/x64/full-codegen-x64.cc @@ -36,6 +36,7 @@ #include "full-codegen.h" #include "parser.h" #include "scopes.h" +#include "stub-cache.h" namespace v8 { namespace internal { @@ -836,17 +837,21 @@ void FullCodeGenerator::VisitForInStatement(ForInStatement* stmt) { } -void FullCodeGenerator::EmitNewClosure(Handle<SharedFunctionInfo> info) { +void FullCodeGenerator::EmitNewClosure(Handle<SharedFunctionInfo> info, + bool pretenure) { // Use the fast case closure allocation code that allocates in new // space for nested functions that don't need literals cloning. - if (scope()->is_function_scope() && info->num_literals() == 0) { + if (scope()->is_function_scope() && + info->num_literals() == 0 && + !pretenure) { FastNewClosureStub stub; __ Push(info); __ CallStub(&stub); } else { __ push(rsi); __ Push(info); - __ CallRuntime(Runtime::kNewClosure, 2); + __ Push(pretenure ? Factory::true_value() : Factory::false_value()); + __ CallRuntime(Runtime::kNewClosure, 3); } context()->Plug(rax); } @@ -912,7 +917,7 @@ void FullCodeGenerator::EmitLoadGlobalSlotCheckExtensions( // All extension objects were empty and it is safe to use a global // load IC call. - __ movq(rax, CodeGenerator::GlobalObject()); + __ movq(rax, GlobalObjectOperand()); __ Move(rcx, slot->var()->name()); Handle<Code> ic(Builtins::builtin(Builtins::LoadIC_Initialize)); RelocInfo::Mode mode = (typeof_state == INSIDE_TYPEOF) @@ -1016,7 +1021,7 @@ void FullCodeGenerator::EmitVariableLoad(Variable* var) { // Use inline caching. Variable name is passed in rcx and the global // object on the stack. __ Move(rcx, var->name()); - __ movq(rax, CodeGenerator::GlobalObject()); + __ movq(rax, GlobalObjectOperand()); Handle<Code> ic(Builtins::builtin(Builtins::LoadIC_Initialize)); EmitCallIC(ic, RelocInfo::CODE_TARGET_CONTEXT); context()->Plug(rax); @@ -1555,7 +1560,7 @@ void FullCodeGenerator::EmitVariableAssignment(Variable* var, // assignment. Right-hand-side value is passed in rax, variable name in // rcx, and the global object on the stack. __ Move(rcx, var->name()); - __ movq(rdx, CodeGenerator::GlobalObject()); + __ movq(rdx, GlobalObjectOperand()); Handle<Code> ic(Builtins::builtin(Builtins::StoreIC_Initialize)); EmitCallIC(ic, RelocInfo::CODE_TARGET); @@ -1727,8 +1732,7 @@ void FullCodeGenerator::EmitCallWithIC(Call* expr, SetSourcePosition(expr->position(), FORCED_POSITION); // Call the IC initialization code. InLoopFlag in_loop = (loop_depth() > 0) ? IN_LOOP : NOT_IN_LOOP; - Handle<Code> ic = CodeGenerator::ComputeCallInitialize(arg_count, - in_loop); + Handle<Code> ic = StubCache::ComputeCallInitialize(arg_count, in_loop); EmitCallIC(ic, mode); // Restore context register. __ movq(rsi, Operand(rbp, StandardFrameConstants::kContextOffset)); @@ -1739,26 +1743,33 @@ void FullCodeGenerator::EmitCallWithIC(Call* expr, void FullCodeGenerator::EmitKeyedCallWithIC(Call* expr, Expression* key, RelocInfo::Mode mode) { - // Code common for calls using the IC. + // Load the key. + VisitForAccumulatorValue(key); + + // Swap the name of the function and the receiver on the stack to follow + // the calling convention for call ICs. + __ pop(rcx); + __ push(rax); + __ push(rcx); + + // Load the arguments. ZoneList<Expression*>* args = expr->arguments(); int arg_count = args->length(); { PreserveStatementPositionScope scope(masm()->positions_recorder()); for (int i = 0; i < arg_count; i++) { VisitForStackValue(args->at(i)); } - VisitForAccumulatorValue(key); - __ movq(rcx, rax); } // Record source position for debugger. SetSourcePosition(expr->position(), FORCED_POSITION); // Call the IC initialization code. InLoopFlag in_loop = (loop_depth() > 0) ? IN_LOOP : NOT_IN_LOOP; - Handle<Code> ic = CodeGenerator::ComputeKeyedCallInitialize(arg_count, - in_loop); + Handle<Code> ic = StubCache::ComputeKeyedCallInitialize(arg_count, in_loop); + __ movq(rcx, Operand(rsp, (arg_count + 1) * kPointerSize)); // Key. EmitCallIC(ic, mode); // Restore context register. __ movq(rsi, Operand(rbp, StandardFrameConstants::kContextOffset)); - context()->Plug(rax); + context()->DropAndPlug(1, rax); // Drop the key still on the stack. } @@ -1834,7 +1845,7 @@ void FullCodeGenerator::VisitCall(Call* expr) { } else if (var != NULL && !var->is_this() && var->is_global()) { // Call to a global variable. // Push global object as receiver for the call IC lookup. - __ push(CodeGenerator::GlobalObject()); + __ push(GlobalObjectOperand()); EmitCallWithIC(expr, var->name(), RelocInfo::CODE_TARGET_CONTEXT); } else if (var != NULL && var->AsSlot() != NULL && var->AsSlot()->type() == Slot::LOOKUP) { @@ -1868,7 +1879,7 @@ void FullCodeGenerator::VisitCall(Call* expr) { // Push function. __ push(rax); // Push global receiver. - __ movq(rbx, CodeGenerator::GlobalObject()); + __ movq(rbx, GlobalObjectOperand()); __ push(FieldOperand(rbx, GlobalObject::kGlobalReceiverOffset)); __ bind(&call); } @@ -1907,7 +1918,7 @@ void FullCodeGenerator::VisitCall(Call* expr) { // Push result (function). __ push(rax); // Push receiver object on stack. - __ movq(rcx, CodeGenerator::GlobalObject()); + __ movq(rcx, GlobalObjectOperand()); __ push(FieldOperand(rcx, GlobalObject::kGlobalReceiverOffset)); EmitCallWithStub(expr); } else { @@ -1928,7 +1939,7 @@ void FullCodeGenerator::VisitCall(Call* expr) { VisitForStackValue(fun); } // Load global receiver object. - __ movq(rbx, CodeGenerator::GlobalObject()); + __ movq(rbx, GlobalObjectOperand()); __ push(FieldOperand(rbx, GlobalObject::kGlobalReceiverOffset)); // Emit function call. EmitCallWithStub(expr); @@ -2788,6 +2799,11 @@ void FullCodeGenerator::EmitGetCachedArrayIndex(ZoneList<Expression*>* args) { } +void FullCodeGenerator::EmitFastAsciiArrayJoin(ZoneList<Expression*>* args) { + context()->Plug(Heap::kUndefinedValueRootIndex); +} + + void FullCodeGenerator::VisitCallRuntime(CallRuntime* expr) { Handle<String> name = expr->name(); if (name->length() > 0 && name->Get(0) == '_') { @@ -2801,7 +2817,7 @@ void FullCodeGenerator::VisitCallRuntime(CallRuntime* expr) { if (expr->is_jsruntime()) { // Prepare for calling JS runtime function. - __ movq(rax, CodeGenerator::GlobalObject()); + __ movq(rax, GlobalObjectOperand()); __ push(FieldOperand(rax, GlobalObject::kBuiltinsOffset)); } @@ -2815,7 +2831,7 @@ void FullCodeGenerator::VisitCallRuntime(CallRuntime* expr) { // Call the JS runtime function using a call IC. __ Move(rcx, expr->name()); InLoopFlag in_loop = (loop_depth() > 0) ? IN_LOOP : NOT_IN_LOOP; - Handle<Code> ic = CodeGenerator::ComputeCallInitialize(arg_count, in_loop); + Handle<Code> ic = StubCache::ComputeCallInitialize(arg_count, in_loop); EmitCallIC(ic, RelocInfo::CODE_TARGET); // Restore context register. __ movq(rsi, Operand(rbp, StandardFrameConstants::kContextOffset)); @@ -2851,7 +2867,7 @@ void FullCodeGenerator::VisitUnaryOperation(UnaryOperation* expr) { VisitForStackValue(prop->obj()); VisitForStackValue(prop->key()); } else if (var->is_global()) { - __ push(CodeGenerator::GlobalObject()); + __ push(GlobalObjectOperand()); __ Push(var->name()); } else { // Non-global variable. Call the runtime to look up the context @@ -3122,7 +3138,7 @@ void FullCodeGenerator::VisitForTypeofValue(Expression* expr) { if (proxy != NULL && !proxy->var()->is_this() && proxy->var()->is_global()) { Comment cmnt(masm_, "Global variable"); __ Move(rcx, proxy->name()); - __ movq(rax, CodeGenerator::GlobalObject()); + __ movq(rax, GlobalObjectOperand()); Handle<Code> ic(Builtins::builtin(Builtins::LoadIC_Initialize)); // Use a regular load, not a contextual load, to avoid a reference // error. diff --git a/src/x64/macro-assembler-x64.cc b/src/x64/macro-assembler-x64.cc index 293d8a56..d9198338 100644 --- a/src/x64/macro-assembler-x64.cc +++ b/src/x64/macro-assembler-x64.cc @@ -327,7 +327,7 @@ MaybeObject* MacroAssembler::TryCallStub(CodeStub* stub) { void MacroAssembler::TailCallStub(CodeStub* stub) { - ASSERT(allow_stub_calls()); // calls are not allowed in some stubs + ASSERT(allow_stub_calls()); // Calls are not allowed in some stubs. Jump(stub->GetCode(), RelocInfo::CODE_TARGET); } @@ -456,6 +456,24 @@ void MacroAssembler::TailCallExternalReference(const ExternalReference& ext, } +MaybeObject* MacroAssembler::TryTailCallExternalReference( + const ExternalReference& ext, int num_arguments, int result_size) { + // ----------- S t a t e ------------- + // -- rsp[0] : return address + // -- rsp[8] : argument num_arguments - 1 + // ... + // -- rsp[8 * num_arguments] : argument 0 (receiver) + // ----------------------------------- + + // TODO(1236192): Most runtime routines don't need the number of + // arguments passed in because it is constant. At some point we + // should remove this need and make the runtime routine entry code + // smarter. + Set(rax, num_arguments); + return TryJumpToExternalReference(ext, result_size); +} + + void MacroAssembler::TailCallRuntime(Runtime::FunctionId fid, int num_arguments, int result_size) { @@ -463,6 +481,15 @@ void MacroAssembler::TailCallRuntime(Runtime::FunctionId fid, } +MaybeObject* MacroAssembler::TryTailCallRuntime(Runtime::FunctionId fid, + int num_arguments, + int result_size) { + return TryTailCallExternalReference(ExternalReference(fid), + num_arguments, + result_size); +} + + static int Offset(ExternalReference ref0, ExternalReference ref1) { int64_t offset = (ref0.address() - ref1.address()); // Check that fits into int. @@ -471,12 +498,22 @@ static int Offset(ExternalReference ref0, ExternalReference ref1) { } -void MacroAssembler::PrepareCallApiFunction(int stack_space) { - EnterApiExitFrame(stack_space, 0); +void MacroAssembler::PrepareCallApiFunction(int arg_stack_space) { +#ifdef _WIN64 + // We need to prepare a slot for result handle on stack and put + // a pointer to it into 1st arg register. + EnterApiExitFrame(arg_stack_space + 1); + + // rcx must be used to pass the pointer to the return value slot. + lea(rcx, StackSpaceOperand(arg_stack_space)); +#else + EnterApiExitFrame(arg_stack_space); +#endif } -void MacroAssembler::CallApiFunctionAndReturn(ApiFunction* function) { +MaybeObject* MacroAssembler::TryCallApiFunctionAndReturn( + ApiFunction* function, int stack_space) { Label empty_result; Label prologue; Label promote_scheduled_exception; @@ -499,7 +536,7 @@ void MacroAssembler::CallApiFunctionAndReturn(ApiFunction* function) { // Allocate HandleScope in callee-save registers. Register prev_next_address_reg = r14; Register prev_limit_reg = rbx; - Register base_reg = kSmiConstantRegister; + Register base_reg = r12; movq(base_reg, next_address); movq(prev_next_address_reg, Operand(base_reg, kNextOffset)); movq(prev_limit_reg, Operand(base_reg, kLimitOffset)); @@ -528,18 +565,21 @@ void MacroAssembler::CallApiFunctionAndReturn(ApiFunction* function) { cmpq(prev_limit_reg, Operand(base_reg, kLimitOffset)); j(not_equal, &delete_allocated_handles); bind(&leave_exit_frame); - InitializeSmiConstantRegister(); // Check if the function scheduled an exception. movq(rsi, scheduled_exception_address); Cmp(Operand(rsi, 0), Factory::the_hole_value()); j(not_equal, &promote_scheduled_exception); - LeaveExitFrame(); - ret(0); + LeaveApiExitFrame(); + ret(stack_space * kPointerSize); bind(&promote_scheduled_exception); - TailCallRuntime(Runtime::kPromoteScheduledException, 0, 1); + MaybeObject* result = TryTailCallRuntime(Runtime::kPromoteScheduledException, + 0, 1); + if (result->IsFailure()) { + return result; + } bind(&empty_result); // It was zero; the result is undefined. @@ -554,6 +594,8 @@ void MacroAssembler::CallApiFunctionAndReturn(ApiFunction* function) { call(rax); movq(rax, prev_limit_reg); jmp(&leave_exit_frame); + + return result; } @@ -566,6 +608,15 @@ void MacroAssembler::JumpToExternalReference(const ExternalReference& ext, } +MaybeObject* MacroAssembler::TryJumpToExternalReference( + const ExternalReference& ext, int result_size) { + // Set the entry point and jump to the C entry runtime stub. + movq(rbx, ext); + CEntryStub ces(result_size); + return TryTailCallStub(&ces); +} + + void MacroAssembler::InvokeBuiltin(Builtins::JavaScript id, InvokeFlag flag) { // Calls are not allowed in some stubs. ASSERT(flag == JUMP_FUNCTION || allow_stub_calls()); @@ -1690,22 +1741,15 @@ void MacroAssembler::EnterExitFramePrologue(bool save_rax) { store_rax(context_address); } -void MacroAssembler::EnterExitFrameEpilogue(int result_size, - int argc) { + +void MacroAssembler::EnterExitFrameEpilogue(int arg_stack_space) { #ifdef _WIN64 - // Reserve space on stack for result and argument structures, if necessary. - int result_stack_space = (result_size < 2) ? 0 : result_size * kPointerSize; - // Reserve space for the Arguments object. The Windows 64-bit ABI - // requires us to pass this structure as a pointer to its location on - // the stack. The structure contains 2 values. - int argument_stack_space = argc * kPointerSize; - // We also need backing space for 4 parameters, even though - // we only pass one or two parameter, and it is in a register. - int argument_mirror_space = 4 * kPointerSize; - int total_stack_space = - argument_mirror_space + argument_stack_space + result_stack_space; - subq(rsp, Immediate(total_stack_space)); + const int kShaddowSpace = 4; + arg_stack_space += kShaddowSpace; #endif + if (arg_stack_space > 0) { + subq(rsp, Immediate(arg_stack_space * kPointerSize)); + } // Get the required frame alignment for the OS. static const int kFrameAlignment = OS::ActivationFrameAlignment(); @@ -1720,7 +1764,7 @@ void MacroAssembler::EnterExitFrameEpilogue(int result_size, } -void MacroAssembler::EnterExitFrame(int result_size) { +void MacroAssembler::EnterExitFrame(int arg_stack_space) { EnterExitFramePrologue(true); // Setup argv in callee-saved register r12. It is reused in LeaveExitFrame, @@ -1728,25 +1772,17 @@ void MacroAssembler::EnterExitFrame(int result_size) { int offset = StandardFrameConstants::kCallerSPOffset - kPointerSize; lea(r12, Operand(rbp, r14, times_pointer_size, offset)); - EnterExitFrameEpilogue(result_size, 2); + EnterExitFrameEpilogue(arg_stack_space); } -void MacroAssembler::EnterApiExitFrame(int stack_space, - int argc, - int result_size) { +void MacroAssembler::EnterApiExitFrame(int arg_stack_space) { EnterExitFramePrologue(false); - - // Setup argv in callee-saved register r12. It is reused in LeaveExitFrame, - // so it must be retained across the C-call. - int offset = StandardFrameConstants::kCallerSPOffset - kPointerSize; - lea(r12, Operand(rbp, (stack_space * kPointerSize) + offset)); - - EnterExitFrameEpilogue(result_size, argc); + EnterExitFrameEpilogue(arg_stack_space); } -void MacroAssembler::LeaveExitFrame(int result_size) { +void MacroAssembler::LeaveExitFrame() { // Registers: // r12 : argv @@ -1758,6 +1794,22 @@ void MacroAssembler::LeaveExitFrame(int result_size) { // from the caller stack. lea(rsp, Operand(r12, 1 * kPointerSize)); + // Push the return address to get ready to return. + push(rcx); + + LeaveExitFrameEpilogue(); +} + + +void MacroAssembler::LeaveApiExitFrame() { + movq(rsp, rbp); + pop(rbp); + + LeaveExitFrameEpilogue(); +} + + +void MacroAssembler::LeaveExitFrameEpilogue() { // Restore current context from top and clear it in debug mode. ExternalReference context_address(Top::k_context_address); movq(kScratchRegister, context_address); @@ -1766,9 +1818,6 @@ void MacroAssembler::LeaveExitFrame(int result_size) { movq(Operand(kScratchRegister, 0), Immediate(0)); #endif - // Push the return address to get ready to return. - push(rcx); - // Clear the top frame. ExternalReference c_entry_fp_address(Top::k_c_entry_fp_address); movq(kScratchRegister, c_entry_fp_address); @@ -1840,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 = @@ -1862,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)) { @@ -1923,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 = @@ -1980,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 = @@ -2022,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/src/x64/macro-assembler-x64.h b/src/x64/macro-assembler-x64.h index 2f6e9561..0b7e6018 100644 --- a/src/x64/macro-assembler-x64.h +++ b/src/x64/macro-assembler-x64.h @@ -155,17 +155,23 @@ class MacroAssembler: public Assembler { // debug mode. Expects the number of arguments in register rax and // sets up the number of arguments in register rdi and the pointer // to the first argument in register rsi. - void EnterExitFrame(int result_size = 1); + // + // Allocates arg_stack_space * kPointerSize memory (not GCed) on the stack + // accessible via StackSpaceOperand. + void EnterExitFrame(int arg_stack_space = 0); - void EnterApiExitFrame(int stack_space, - int argc, - int result_size = 1); + // Enter specific kind of exit frame. Allocates arg_stack_space * kPointerSize + // memory (not GCed) on the stack accessible via StackSpaceOperand. + void EnterApiExitFrame(int arg_stack_space); // Leave the current exit frame. Expects/provides the return value in // register rax:rdx (untouched) and the pointer to the first // argument in register rsi. - void LeaveExitFrame(int result_size = 1); + void LeaveExitFrame(); + // Leave the current exit frame. Expects/provides the return value in + // register rax (untouched). + void LeaveApiExitFrame(); // --------------------------------------------------------------------------- // JavaScript invokes @@ -813,22 +819,38 @@ class MacroAssembler: public Assembler { int num_arguments, int result_size); + MUST_USE_RESULT MaybeObject* TryTailCallExternalReference( + const ExternalReference& ext, int num_arguments, int result_size); + // Convenience function: tail call a runtime routine (jump). void TailCallRuntime(Runtime::FunctionId fid, int num_arguments, int result_size); + MUST_USE_RESULT MaybeObject* TryTailCallRuntime(Runtime::FunctionId fid, + int num_arguments, + int result_size); + // Jump to a runtime routine. void JumpToExternalReference(const ExternalReference& ext, int result_size); - // Prepares stack to put arguments (aligns and so on). - // Uses calle-saved esi to restore stack state after call. - void PrepareCallApiFunction(int stack_space); + // Jump to a runtime routine. + MaybeObject* TryJumpToExternalReference(const ExternalReference& ext, + int result_size); - // Tail call an API function (jump). Allocates HandleScope, extracts - // returned value from handle and propogates exceptions. - // Clobbers ebx, edi and caller-save registers. - void CallApiFunctionAndReturn(ApiFunction* function); + // Prepares stack to put arguments (aligns and so on). + // WIN64 calling convention requires to put the pointer to the return value + // slot into rcx (rcx must be preserverd until TryCallApiFunctionAndReturn). + // Saves context (rsi). Clobbers rax. Allocates arg_stack_space * kPointerSize + // inside the exit frame (not GCed) accessible via StackSpaceOperand. + void PrepareCallApiFunction(int arg_stack_space); + + // Calls an API function. Allocates HandleScope, extracts + // returned value from handle and propagates exceptions. + // Clobbers r12, r14, rbx and caller-save registers. Restores context. + // On return removes stack_space * kPointerSize (GCed). + MUST_USE_RESULT MaybeObject* TryCallApiFunctionAndReturn( + ApiFunction* function, int stack_space); // Before calling a C-function from generated code, align arguments on stack. // After aligning the frame, arguments must be stored in esp[0], esp[4], @@ -919,16 +941,18 @@ class MacroAssembler: public Assembler { void LeaveFrame(StackFrame::Type type); void EnterExitFramePrologue(bool save_rax); - void EnterExitFrameEpilogue(int result_size, int argc); + + // Allocates arg_stack_space * kPointerSize memory (not GCed) on the stack + // accessible via StackSpaceOperand. + void EnterExitFrameEpilogue(int arg_stack_space); + + void LeaveExitFrameEpilogue(); // Allocation support helpers. // Loads the top of new-space into the result register. - // 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. @@ -982,6 +1006,28 @@ static inline Operand FieldOperand(Register object, } +static inline Operand ContextOperand(Register context, int index) { + return Operand(context, Context::SlotOffset(index)); +} + + +static inline Operand GlobalObjectOperand() { + return ContextOperand(rsi, Context::GLOBAL_INDEX); +} + + +// Provides access to exit frame stack space (not GCed). +static inline Operand StackSpaceOperand(int index) { +#ifdef _WIN64 + const int kShaddowSpace = 4; + return Operand(rsp, (index + kShaddowSpace) * kPointerSize); +#else + return Operand(rsp, index * kPointerSize); +#endif +} + + + #ifdef GENERATED_CODE_COVERAGE extern void LogGeneratedCodeCoverage(const char* file_line); #define CODE_COVERAGE_STRINGIFY(x) #x diff --git a/src/x64/stub-cache-x64.cc b/src/x64/stub-cache-x64.cc index 24609bf6..7ba482c8 100644 --- a/src/x64/stub-cache-x64.cc +++ b/src/x64/stub-cache-x64.cc @@ -497,8 +497,10 @@ void StubCompiler::GenerateLoadFunctionPrototype(MacroAssembler* masm, __ ret(0); } +// Number of pointers to be reserved on stack for fast API call. +static const int kFastApiCallArguments = 3; -// Reserves space for the extra arguments to FastHandleApiCall in the +// Reserves space for the extra arguments to API function in the // caller's frame. // // These arguments are set by CheckPrototypes and GenerateFastApiCall. @@ -508,48 +510,48 @@ static void ReserveSpaceForFastApiCall(MacroAssembler* masm, Register scratch) { // -- rsp[8] : last argument in the internal frame of the caller // ----------------------------------- __ movq(scratch, Operand(rsp, 0)); - __ subq(rsp, Immediate(4 * kPointerSize)); + __ subq(rsp, Immediate(kFastApiCallArguments * kPointerSize)); __ movq(Operand(rsp, 0), scratch); __ Move(scratch, Smi::FromInt(0)); - __ movq(Operand(rsp, 1 * kPointerSize), scratch); - __ movq(Operand(rsp, 2 * kPointerSize), scratch); - __ movq(Operand(rsp, 3 * kPointerSize), scratch); - __ movq(Operand(rsp, 4 * kPointerSize), scratch); + for (int i = 1; i <= kFastApiCallArguments; i++) { + __ movq(Operand(rsp, i * kPointerSize), scratch); + } } // Undoes the effects of ReserveSpaceForFastApiCall. static void FreeSpaceForFastApiCall(MacroAssembler* masm, Register scratch) { // ----------- S t a t e ------------- - // -- rsp[0] : return address - // -- rsp[8] : last fast api call extra argument + // -- rsp[0] : return address. + // -- rsp[8] : last fast api call extra argument. // -- ... - // -- rsp[32] : first fast api call extra argument - // -- rsp[40] : last argument in the internal frame + // -- rsp[kFastApiCallArguments * 8] : first fast api call extra argument. + // -- rsp[kFastApiCallArguments * 8 + 8] : last argument in the internal + // frame. // ----------------------------------- __ movq(scratch, Operand(rsp, 0)); - __ movq(Operand(rsp, 4 * kPointerSize), scratch); - __ addq(rsp, Immediate(kPointerSize * 4)); + __ movq(Operand(rsp, kFastApiCallArguments * kPointerSize), scratch); + __ addq(rsp, Immediate(kPointerSize * kFastApiCallArguments)); } -// Generates call to FastHandleApiCall builtin. -static void GenerateFastApiCall(MacroAssembler* masm, +// Generates call to API function. +static bool GenerateFastApiCall(MacroAssembler* masm, const CallOptimization& optimization, - int argc) { + int argc, + Failure** failure) { // ----------- S t a t e ------------- // -- rsp[0] : return address // -- rsp[8] : object passing the type check // (last fast api call extra argument, // set by CheckPrototypes) - // -- rsp[16] : api call data - // -- rsp[24] : api callback - // -- rsp[32] : api function + // -- rsp[16] : api function // (first fast api call extra argument) - // -- rsp[40] : last argument + // -- rsp[24] : api call data + // -- rsp[32] : last argument // -- ... - // -- rsp[(argc + 5) * 8] : first argument - // -- rsp[(argc + 6) * 8] : receiver + // -- rsp[(argc + 3) * 8] : first argument + // -- rsp[(argc + 4) * 8] : receiver // ----------------------------------- // Get the function and setup the context. @@ -557,38 +559,58 @@ static void GenerateFastApiCall(MacroAssembler* masm, __ Move(rdi, Handle<JSFunction>(function)); __ movq(rsi, FieldOperand(rdi, JSFunction::kContextOffset)); - // Pass the additional arguments FastHandleApiCall expects. - __ movq(Operand(rsp, 4 * kPointerSize), rdi); - bool info_loaded = false; - Object* callback = optimization.api_call_info()->callback(); - if (Heap::InNewSpace(callback)) { - info_loaded = true; - __ Move(rcx, Handle<CallHandlerInfo>(optimization.api_call_info())); - __ movq(rbx, FieldOperand(rcx, CallHandlerInfo::kCallbackOffset)); - __ movq(Operand(rsp, 3 * kPointerSize), rbx); - } else { - __ Move(Operand(rsp, 3 * kPointerSize), Handle<Object>(callback)); - } + // Pass the additional arguments. + __ movq(Operand(rsp, 2 * kPointerSize), rdi); Object* call_data = optimization.api_call_info()->data(); + Handle<CallHandlerInfo> api_call_info_handle(optimization.api_call_info()); if (Heap::InNewSpace(call_data)) { - if (!info_loaded) { - __ Move(rcx, Handle<CallHandlerInfo>(optimization.api_call_info())); - } + __ Move(rcx, api_call_info_handle); __ movq(rbx, FieldOperand(rcx, CallHandlerInfo::kDataOffset)); - __ movq(Operand(rsp, 2 * kPointerSize), rbx); + __ movq(Operand(rsp, 3 * kPointerSize), rbx); } else { - __ Move(Operand(rsp, 2 * kPointerSize), Handle<Object>(call_data)); + __ Move(Operand(rsp, 3 * kPointerSize), Handle<Object>(call_data)); } - // Set the number of arguments. - __ movq(rax, Immediate(argc + 4)); + // Prepare arguments. + __ lea(rbx, Operand(rsp, 3 * kPointerSize)); - // Jump to the fast api call builtin (tail call). - Handle<Code> code = Handle<Code>( - Builtins::builtin(Builtins::FastHandleApiCall)); - ParameterCount expected(0); - __ InvokeCode(code, expected, expected, - RelocInfo::CODE_TARGET, JUMP_FUNCTION); + Object* callback = optimization.api_call_info()->callback(); + Address api_function_address = v8::ToCData<Address>(callback); + ApiFunction fun(api_function_address); + +#ifdef _WIN64 + // Win64 uses first register--rcx--for returned value. + Register arguments_arg = rdx; +#else + Register arguments_arg = rdi; +#endif + + // Allocate the v8::Arguments structure in the arguments' space since + // it's not controlled by GC. + const int kApiStackSpace = 4; + + __ PrepareCallApiFunction(kApiStackSpace); + + __ movq(StackSpaceOperand(0), rbx); // v8::Arguments::implicit_args_. + __ addq(rbx, Immediate(argc * kPointerSize)); + __ movq(StackSpaceOperand(1), rbx); // v8::Arguments::values_. + __ Set(StackSpaceOperand(2), argc); // v8::Arguments::length_. + // v8::Arguments::is_construct_call_. + __ Set(StackSpaceOperand(3), 0); + + // v8::InvocationCallback's argument. + __ lea(arguments_arg, StackSpaceOperand(0)); + // Emitting a stub call may try to allocate (if the code is not + // already generated). Do not allow the assembler to perform a + // garbage collection but instead return the allocation failure + // object. + MaybeObject* result = + masm->TryCallApiFunctionAndReturn(&fun, argc + kFastApiCallArguments + 1); + if (result->IsFailure()) { + *failure = Failure::cast(result); + return false; + } + return true; } @@ -601,7 +623,7 @@ class CallInterceptorCompiler BASE_EMBEDDED { arguments_(arguments), name_(name) {} - void Compile(MacroAssembler* masm, + bool Compile(MacroAssembler* masm, JSObject* object, JSObject* holder, String* name, @@ -610,7 +632,8 @@ class CallInterceptorCompiler BASE_EMBEDDED { Register scratch1, Register scratch2, Register scratch3, - Label* miss) { + Label* miss, + Failure** failure) { ASSERT(holder->HasNamedInterceptor()); ASSERT(!holder->GetNamedInterceptor()->getter()->IsUndefined()); @@ -620,17 +643,18 @@ class CallInterceptorCompiler BASE_EMBEDDED { CallOptimization optimization(lookup); if (optimization.is_constant_call()) { - CompileCacheable(masm, - object, - receiver, - scratch1, - scratch2, - scratch3, - holder, - lookup, - name, - optimization, - miss); + return CompileCacheable(masm, + object, + receiver, + scratch1, + scratch2, + scratch3, + holder, + lookup, + name, + optimization, + miss, + failure); } else { CompileRegular(masm, object, @@ -641,11 +665,12 @@ class CallInterceptorCompiler BASE_EMBEDDED { name, holder, miss); + return true; } } private: - void CompileCacheable(MacroAssembler* masm, + bool CompileCacheable(MacroAssembler* masm, JSObject* object, Register receiver, Register scratch1, @@ -655,7 +680,8 @@ class CallInterceptorCompiler BASE_EMBEDDED { LookupResult* lookup, String* name, const CallOptimization& optimization, - Label* miss_label) { + Label* miss_label, + Failure** failure) { ASSERT(optimization.is_constant_call()); ASSERT(!lookup->holder()->IsGlobalObject()); @@ -717,7 +743,13 @@ class CallInterceptorCompiler BASE_EMBEDDED { // Invoke function. if (can_do_fast_api_call) { - GenerateFastApiCall(masm, optimization, arguments_.immediate()); + bool success = GenerateFastApiCall(masm, + optimization, + arguments_.immediate(), + failure); + if (!success) { + return false; + } } else { __ InvokeFunction(optimization.constant_function(), arguments_, JUMP_FUNCTION); @@ -735,6 +767,8 @@ class CallInterceptorCompiler BASE_EMBEDDED { if (can_do_fast_api_call) { FreeSpaceForFastApiCall(masm, scratch1); } + + return true; } void CompileRegular(MacroAssembler* masm, @@ -958,7 +992,9 @@ MaybeObject* CallStubCompiler::CompileCallConstant( if (depth != kInvalidProtoDepth) { __ IncrementCounter(&Counters::call_const_fast_api, 1); - ReserveSpaceForFastApiCall(masm(), rax); + // Allocate space for v8::Arguments implicit values. Must be initialized + // before to call any runtime function. + __ subq(rsp, Immediate(kFastApiCallArguments * kPointerSize)); } // Check that the maps haven't changed. @@ -1036,7 +1072,17 @@ MaybeObject* CallStubCompiler::CompileCallConstant( } if (depth != kInvalidProtoDepth) { - GenerateFastApiCall(masm(), optimization, argc); + Failure* failure; + // Move the return address on top of the stack. + __ movq(rax, Operand(rsp, 3 * kPointerSize)); + __ movq(Operand(rsp, 0 * kPointerSize), rax); + + // rsp[2 * kPointerSize] is uninitialized, rsp[3 * kPointerSize] contains + // duplicate of return address and will be overwritten. + bool success = GenerateFastApiCall(masm(), optimization, argc, &failure); + if (!success) { + return failure; + } } else { __ InvokeFunction(function, arguments(), JUMP_FUNCTION); } @@ -1044,7 +1090,7 @@ MaybeObject* CallStubCompiler::CompileCallConstant( // Handle call cache miss. __ bind(&miss); if (depth != kInvalidProtoDepth) { - FreeSpaceForFastApiCall(masm(), rax); + __ addq(rsp, Immediate(kFastApiCallArguments * kPointerSize)); } // Handle call cache miss. @@ -1723,16 +1769,21 @@ MaybeObject* CallStubCompiler::CompileCallInterceptor(JSObject* object, __ movq(rdx, Operand(rsp, (argc + 1) * kPointerSize)); CallInterceptorCompiler compiler(this, arguments(), rcx); - compiler.Compile(masm(), - object, - holder, - name, - &lookup, - rdx, - rbx, - rdi, - rax, - &miss); + Failure* failure; + bool success = compiler.Compile(masm(), + object, + holder, + name, + &lookup, + rdx, + rbx, + rdi, + rax, + &miss, + &failure); + if (!success) { + return failure; + } // Restore receiver. __ movq(rdx, Operand(rsp, (argc + 1) * kPointerSize)); @@ -1844,7 +1895,7 @@ MaybeObject* LoadStubCompiler::CompileLoadCallback(String* name, Label miss; Failure* failure = Failure::InternalError(); - bool success = GenerateLoadCallback(object, holder, rax, rcx, rbx, rdx, rdi, + bool success = GenerateLoadCallback(object, holder, rax, rcx, rdx, rbx, rdi, callback, name, &miss, &failure); if (!success) { miss.Unuse(); @@ -2537,8 +2588,8 @@ void StubCompiler::GenerateLoadInterceptor(JSObject* object, __ push(receiver); __ push(holder_reg); __ Move(holder_reg, Handle<AccessorInfo>(callback)); - __ push(holder_reg); __ push(FieldOperand(holder_reg, AccessorInfo::kDataOffset)); + __ push(holder_reg); __ push(name_reg); __ push(scratch2); // restore return address @@ -2585,16 +2636,15 @@ bool StubCompiler::GenerateLoadCallback(JSObject* object, Handle<AccessorInfo> callback_handle(callback); - __ EnterInternalFrame(); - // Push the stack address where the list of arguments ends. - __ movq(scratch2, rsp); - __ subq(scratch2, Immediate(2 * kPointerSize)); - __ push(scratch2); + // Insert additional parameters into the stack frame above return address. + ASSERT(!scratch2.is(reg)); + __ pop(scratch2); // Get return address to place it below. + __ push(receiver); // receiver __ push(reg); // holder if (Heap::InNewSpace(callback_handle->data())) { - __ Move(scratch2, callback_handle); - __ push(FieldOperand(scratch2, AccessorInfo::kDataOffset)); // data + __ Move(scratch1, callback_handle); + __ push(FieldOperand(scratch1, AccessorInfo::kDataOffset)); // data } else { __ Push(Handle<Object>(callback_handle->data())); } @@ -2607,42 +2657,43 @@ bool StubCompiler::GenerateLoadCallback(JSObject* object, Register accessor_info_arg = r8; Register name_arg = rdx; #else - Register accessor_info_arg = rdx; // temporary, copied to rsi by the stub. + Register accessor_info_arg = rsi; Register name_arg = rdi; #endif - __ movq(accessor_info_arg, rsp); - __ addq(accessor_info_arg, Immediate(4 * kPointerSize)); + ASSERT(!name_arg.is(scratch2)); __ movq(name_arg, rsp); + __ push(scratch2); // Restore return address. // Do call through the api. - ASSERT_EQ(5, ApiGetterEntryStub::kStackSpace); Address getter_address = v8::ToCData<Address>(callback->getter()); ApiFunction fun(getter_address); - ApiGetterEntryStub stub(callback_handle, &fun); -#ifdef _WIN64 - // We need to prepare a slot for result handle on stack and put - // a pointer to it into 1st arg register. - __ push(Immediate(0)); - __ movq(rcx, rsp); -#endif + + // 3 elements array for v8::Agruments::values_ and handler for name. + const int kStackSpace = 4; + + // Allocate v8::AccessorInfo in non-GCed stack space. + const int kArgStackSpace = 1; + + __ PrepareCallApiFunction(kArgStackSpace); + __ lea(rax, Operand(name_arg, 3 * kPointerSize)); + + // v8::AccessorInfo::args_. + __ movq(StackSpaceOperand(0), rax); + + // The context register (rsi) has been saved in PrepareCallApiFunction and + // could be used to pass arguments. + __ lea(accessor_info_arg, StackSpaceOperand(0)); + // Emitting a stub call may try to allocate (if the code is not // already generated). Do not allow the assembler to perform a // garbage collection but instead return the allocation failure // object. - MaybeObject* result = masm()->TryCallStub(&stub); + MaybeObject* result = masm()->TryCallApiFunctionAndReturn(&fun, kStackSpace); if (result->IsFailure()) { *failure = Failure::cast(result); return false; } -#ifdef _WIN64 - // Discard allocated slot. - __ addq(rsp, Immediate(kPointerSize)); -#endif - __ LeaveInternalFrame(); - - __ ret(0); - return true; } @@ -2839,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 @@ -2913,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. @@ -2932,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/src/x64/virtual-frame-x64.cc b/src/x64/virtual-frame-x64.cc index e88a993b..3f7b1db7 100644 --- a/src/x64/virtual-frame-x64.cc +++ b/src/x64/virtual-frame-x64.cc @@ -32,6 +32,7 @@ #include "codegen-inl.h" #include "register-allocator-inl.h" #include "scopes.h" +#include "stub-cache.h" #include "virtual-frame-inl.h" namespace v8 { @@ -1194,7 +1195,7 @@ Result VirtualFrame::CallCallIC(RelocInfo::Mode mode, // and dropped by the call. The IC expects the name in rcx and the rest // on the stack, and drops them all. InLoopFlag in_loop = loop_nesting > 0 ? IN_LOOP : NOT_IN_LOOP; - Handle<Code> ic = cgen()->ComputeCallInitialize(arg_count, in_loop); + Handle<Code> ic = StubCache::ComputeCallInitialize(arg_count, in_loop); Result name = Pop(); // Spill args, receiver, and function. The call will drop args and // receiver. @@ -1213,7 +1214,7 @@ Result VirtualFrame::CallKeyedCallIC(RelocInfo::Mode mode, // on the stack, and drops them all. InLoopFlag in_loop = loop_nesting > 0 ? IN_LOOP : NOT_IN_LOOP; Handle<Code> ic = - cgen()->ComputeKeyedCallInitialize(arg_count, in_loop); + StubCache::ComputeKeyedCallInitialize(arg_count, in_loop); Result name = Pop(); // Spill args, receiver, and function. The call will drop args and // receiver. diff --git a/test/cctest/SConscript b/test/cctest/SConscript index 620cd825..ba3466de 100644 --- a/test/cctest/SConscript +++ b/test/cctest/SConscript @@ -42,6 +42,7 @@ SOURCES = { 'test-api.cc', 'test-ast.cc', 'test-bignum.cc', + 'test-bignum-dtoa.cc', 'test-circular-queue.cc', 'test-compiler.cc', 'test-conversions.cc', @@ -51,6 +52,7 @@ SOURCES = { 'test-decls.cc', 'test-diy-fp.cc', 'test-double.cc', + 'test-dtoa.cc', 'test-fast-dtoa.cc', 'test-fixed-dtoa.cc', 'test-flags.cc', diff --git a/test/cctest/test-api.cc b/test/cctest/test-api.cc index 6eb15d8f..8ce7a79a 100644 --- a/test/cctest/test-api.cc +++ b/test/cctest/test-api.cc @@ -38,6 +38,7 @@ #include "utils.h" #include "cctest.h" #include "parser.h" +#include "unicode-inl.h" static const bool kLogThreading = true; @@ -6323,7 +6324,7 @@ static void CheckInterceptorLoadIC(NamedPropertyGetter getter, int expected) { v8::HandleScope scope; v8::Handle<v8::ObjectTemplate> templ = ObjectTemplate::New(); - templ->SetNamedPropertyHandler(getter); + templ->SetNamedPropertyHandler(getter, 0, 0, 0, 0, v8_str("data")); LocalContext context; context->Global()->Set(v8_str("o"), templ->NewInstance()); v8::Handle<Value> value = CompileRun(source); @@ -6334,7 +6335,8 @@ static void CheckInterceptorLoadIC(NamedPropertyGetter getter, static v8::Handle<Value> InterceptorLoadICGetter(Local<String> name, const AccessorInfo& info) { ApiTestFuzzer::Fuzz(); - CHECK(v8_str("x")->Equals(name)); + CHECK_EQ(v8_str("data"), info.Data()); + CHECK_EQ(v8_str("x"), name); return v8::Integer::New(42); } @@ -6732,7 +6734,8 @@ THREADED_TEST(InterceptorStoreIC) { v8::HandleScope scope; v8::Handle<v8::ObjectTemplate> templ = ObjectTemplate::New(); templ->SetNamedPropertyHandler(InterceptorLoadICGetter, - InterceptorStoreICSetter); + InterceptorStoreICSetter, + 0, 0, 0, v8_str("data")); LocalContext context; context->Global()->Set(v8_str("o"), templ->NewInstance()); v8::Handle<Value> value = CompileRun( @@ -7816,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<v8::Value> p = context->Global()->Get(v8_str("p")); + CHECK(p->IsObject() && p->ToObject()->GetConstructorName()->Equals( + v8_str("Parent"))); + + Local<v8::Value> c = context->Global()->Get(v8_str("c")); + CHECK(c->IsObject() && c->ToObject()->GetConstructorName()->Equals( + v8_str("Child"))); + + Local<v8::Value> 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); @@ -8731,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; @@ -10629,7 +10657,7 @@ THREADED_TEST(IdleNotification) { static uint32_t* stack_limit; static v8::Handle<Value> GetStackLimitCallback(const v8::Arguments& args) { - stack_limit = reinterpret_cast<uint32_t*>(i::StackGuard::climit()); + stack_limit = reinterpret_cast<uint32_t*>(i::StackGuard::real_climit()); return v8::Undefined(); } diff --git a/test/cctest/test-bignum-dtoa.cc b/test/cctest/test-bignum-dtoa.cc new file mode 100644 index 00000000..51a90574 --- /dev/null +++ b/test/cctest/test-bignum-dtoa.cc @@ -0,0 +1,315 @@ +// 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 <stdlib.h> + +#include "v8.h" + +#include "bignum-dtoa.h" + +#include "cctest.h" +#include "double.h" +#include "gay-fixed.h" +#include "gay-precision.h" +#include "gay-shortest.h" +#include "platform.h" + +using namespace v8::internal; + + +// Removes trailing '0' digits. +// Can return the empty string if all digits are 0. +static void TrimRepresentation(Vector<char> representation) { + int len = strlen(representation.start()); + int i; + for (i = len - 1; i >= 0; --i) { + if (representation[i] != '0') break; + } + representation[i + 1] = '\0'; +} + + +static const int kBufferSize = 100; + + +TEST(BignumDtoaVariousDoubles) { + char buffer_container[kBufferSize]; + Vector<char> buffer(buffer_container, kBufferSize); + int length; + int point; + + BignumDtoa(1.0, BIGNUM_DTOA_SHORTEST, 0, buffer, &length, &point); + CHECK_EQ("1", buffer.start()); + CHECK_EQ(1, point); + + BignumDtoa(1.0, BIGNUM_DTOA_FIXED, 3, buffer, &length, &point); + CHECK_GE(3, length - point); + TrimRepresentation(buffer); + CHECK_EQ("1", buffer.start()); + CHECK_EQ(1, point); + + BignumDtoa(1.0, BIGNUM_DTOA_PRECISION, 3, buffer, &length, &point); + CHECK_GE(3, length); + TrimRepresentation(buffer); + CHECK_EQ("1", buffer.start()); + CHECK_EQ(1, point); + + BignumDtoa(1.5, BIGNUM_DTOA_SHORTEST, 0, buffer, &length, &point); + CHECK_EQ("15", buffer.start()); + CHECK_EQ(1, point); + + BignumDtoa(1.5, BIGNUM_DTOA_FIXED, 10, buffer, &length, &point); + CHECK_GE(10, length - point); + TrimRepresentation(buffer); + CHECK_EQ("15", buffer.start()); + CHECK_EQ(1, point); + + BignumDtoa(1.5, BIGNUM_DTOA_PRECISION, 10, buffer, &length, &point); + CHECK_GE(10, length); + TrimRepresentation(buffer); + CHECK_EQ("15", buffer.start()); + CHECK_EQ(1, point); + + double min_double = 5e-324; + BignumDtoa(min_double, BIGNUM_DTOA_SHORTEST, 0, buffer, &length, &point); + CHECK_EQ("5", buffer.start()); + CHECK_EQ(-323, point); + + BignumDtoa(min_double, BIGNUM_DTOA_FIXED, 5, buffer, &length, &point); + CHECK_GE(5, length - point); + TrimRepresentation(buffer); + CHECK_EQ("", buffer.start()); + + BignumDtoa(min_double, BIGNUM_DTOA_PRECISION, 5, buffer, &length, &point); + CHECK_GE(5, length); + TrimRepresentation(buffer); + CHECK_EQ("49407", buffer.start()); + CHECK_EQ(-323, point); + + double max_double = 1.7976931348623157e308; + BignumDtoa(max_double, BIGNUM_DTOA_SHORTEST, 0, buffer, &length, &point); + CHECK_EQ("17976931348623157", buffer.start()); + CHECK_EQ(309, point); + + BignumDtoa(max_double, BIGNUM_DTOA_PRECISION, 7, buffer, &length, &point); + CHECK_GE(7, length); + TrimRepresentation(buffer); + CHECK_EQ("1797693", buffer.start()); + CHECK_EQ(309, point); + + BignumDtoa(4294967272.0, BIGNUM_DTOA_SHORTEST, 0, buffer, &length, &point); + CHECK_EQ("4294967272", buffer.start()); + CHECK_EQ(10, point); + + BignumDtoa(4294967272.0, BIGNUM_DTOA_FIXED, 5, buffer, &length, &point); + CHECK_EQ("429496727200000", buffer.start()); + CHECK_EQ(10, point); + + + BignumDtoa(4294967272.0, BIGNUM_DTOA_PRECISION, 14, buffer, &length, &point); + CHECK_GE(14, length); + TrimRepresentation(buffer); + CHECK_EQ("4294967272", buffer.start()); + CHECK_EQ(10, point); + + BignumDtoa(4.1855804968213567e298, BIGNUM_DTOA_SHORTEST, 0, + buffer, &length, &point); + CHECK_EQ("4185580496821357", buffer.start()); + CHECK_EQ(299, point); + + BignumDtoa(4.1855804968213567e298, BIGNUM_DTOA_PRECISION, 20, + buffer, &length, &point); + CHECK_GE(20, length); + TrimRepresentation(buffer); + CHECK_EQ("41855804968213567225", buffer.start()); + CHECK_EQ(299, point); + + BignumDtoa(5.5626846462680035e-309, BIGNUM_DTOA_SHORTEST, 0, + buffer, &length, &point); + CHECK_EQ("5562684646268003", buffer.start()); + CHECK_EQ(-308, point); + + BignumDtoa(5.5626846462680035e-309, BIGNUM_DTOA_PRECISION, 1, + buffer, &length, &point); + CHECK_GE(1, length); + TrimRepresentation(buffer); + CHECK_EQ("6", buffer.start()); + CHECK_EQ(-308, point); + + BignumDtoa(2147483648.0, BIGNUM_DTOA_SHORTEST, 0, + buffer, &length, &point); + CHECK_EQ("2147483648", buffer.start()); + CHECK_EQ(10, point); + + + BignumDtoa(2147483648.0, BIGNUM_DTOA_FIXED, 2, + buffer, &length, &point); + CHECK_GE(2, length - point); + TrimRepresentation(buffer); + CHECK_EQ("2147483648", buffer.start()); + CHECK_EQ(10, point); + + BignumDtoa(2147483648.0, BIGNUM_DTOA_PRECISION, 5, + buffer, &length, &point); + CHECK_GE(5, length); + TrimRepresentation(buffer); + CHECK_EQ("21475", buffer.start()); + CHECK_EQ(10, point); + + BignumDtoa(3.5844466002796428e+298, BIGNUM_DTOA_SHORTEST, 0, + buffer, &length, &point); + CHECK_EQ("35844466002796428", buffer.start()); + CHECK_EQ(299, point); + + BignumDtoa(3.5844466002796428e+298, BIGNUM_DTOA_PRECISION, 10, + buffer, &length, &point); + CHECK_GE(10, length); + TrimRepresentation(buffer); + CHECK_EQ("35844466", buffer.start()); + CHECK_EQ(299, point); + + uint64_t smallest_normal64 = V8_2PART_UINT64_C(0x00100000, 00000000); + double v = Double(smallest_normal64).value(); + BignumDtoa(v, BIGNUM_DTOA_SHORTEST, 0, buffer, &length, &point); + CHECK_EQ("22250738585072014", buffer.start()); + CHECK_EQ(-307, point); + + BignumDtoa(v, BIGNUM_DTOA_PRECISION, 20, buffer, &length, &point); + CHECK_GE(20, length); + TrimRepresentation(buffer); + CHECK_EQ("22250738585072013831", buffer.start()); + CHECK_EQ(-307, point); + + uint64_t largest_denormal64 = V8_2PART_UINT64_C(0x000FFFFF, FFFFFFFF); + v = Double(largest_denormal64).value(); + BignumDtoa(v, BIGNUM_DTOA_SHORTEST, 0, buffer, &length, &point); + CHECK_EQ("2225073858507201", buffer.start()); + CHECK_EQ(-307, point); + + BignumDtoa(v, BIGNUM_DTOA_PRECISION, 20, buffer, &length, &point); + CHECK_GE(20, length); + TrimRepresentation(buffer); + CHECK_EQ("2225073858507200889", buffer.start()); + CHECK_EQ(-307, point); + + BignumDtoa(4128420500802942e-24, BIGNUM_DTOA_SHORTEST, 0, + buffer, &length, &point); + CHECK_EQ("4128420500802942", buffer.start()); + CHECK_EQ(-8, point); + + v = 3.9292015898194142585311918e-10; + BignumDtoa(v, BIGNUM_DTOA_SHORTEST, 0, buffer, &length, &point); + CHECK_EQ("39292015898194143", buffer.start()); + + v = 4194304.0; + BignumDtoa(v, BIGNUM_DTOA_FIXED, 5, buffer, &length, &point); + CHECK_GE(5, length - point); + TrimRepresentation(buffer); + CHECK_EQ("4194304", buffer.start()); + + v = 3.3161339052167390562200598e-237; + BignumDtoa(v, BIGNUM_DTOA_PRECISION, 19, buffer, &length, &point); + CHECK_GE(19, length); + TrimRepresentation(buffer); + CHECK_EQ("3316133905216739056", buffer.start()); + CHECK_EQ(-236, point); + + v = 7.9885183916008099497815232e+191; + BignumDtoa(v, BIGNUM_DTOA_PRECISION, 4, buffer, &length, &point); + CHECK_GE(4, length); + TrimRepresentation(buffer); + CHECK_EQ("7989", buffer.start()); + CHECK_EQ(192, point); + + v = 1.0000000000000012800000000e+17; + BignumDtoa(v, BIGNUM_DTOA_FIXED, 1, buffer, &length, &point); + CHECK_GE(1, length - point); + TrimRepresentation(buffer); + CHECK_EQ("100000000000000128", buffer.start()); + CHECK_EQ(18, point); +} + + +TEST(BignumDtoaGayShortest) { + char buffer_container[kBufferSize]; + Vector<char> buffer(buffer_container, kBufferSize); + int length; + int point; + + Vector<const PrecomputedShortest> precomputed = + PrecomputedShortestRepresentations(); + for (int i = 0; i < precomputed.length(); ++i) { + const PrecomputedShortest current_test = precomputed[i]; + double v = current_test.v; + BignumDtoa(v, BIGNUM_DTOA_SHORTEST, 0, buffer, &length, &point); + CHECK_EQ(current_test.decimal_point, point); + CHECK_EQ(current_test.representation, buffer.start()); + } +} + + +TEST(BignumDtoaGayFixed) { + char buffer_container[kBufferSize]; + Vector<char> buffer(buffer_container, kBufferSize); + int length; + int point; + + Vector<const PrecomputedFixed> precomputed = + PrecomputedFixedRepresentations(); + for (int i = 0; i < precomputed.length(); ++i) { + const PrecomputedFixed current_test = precomputed[i]; + double v = current_test.v; + int number_digits = current_test.number_digits; + BignumDtoa(v, BIGNUM_DTOA_FIXED, number_digits, buffer, &length, &point); + CHECK_EQ(current_test.decimal_point, point); + CHECK_GE(number_digits, length - point); + TrimRepresentation(buffer); + CHECK_EQ(current_test.representation, buffer.start()); + } +} + + +TEST(BignumDtoaGayPrecision) { + char buffer_container[kBufferSize]; + Vector<char> buffer(buffer_container, kBufferSize); + int length; + int point; + + Vector<const PrecomputedPrecision> precomputed = + PrecomputedPrecisionRepresentations(); + for (int i = 0; i < precomputed.length(); ++i) { + const PrecomputedPrecision current_test = precomputed[i]; + double v = current_test.v; + int number_digits = current_test.number_digits; + BignumDtoa(v, BIGNUM_DTOA_PRECISION, number_digits, + buffer, &length, &point); + CHECK_EQ(current_test.decimal_point, point); + CHECK_GE(number_digits, length); + TrimRepresentation(buffer); + CHECK_EQ(current_test.representation, buffer.start()); + } +} diff --git a/test/cctest/test-conversions.cc b/test/cctest/test-conversions.cc index c62bbaaa..1b5cc2de 100644 --- a/test/cctest/test-conversions.cc +++ b/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)); @@ -141,9 +143,6 @@ TEST(LongNumberStr) { } -extern "C" double gay_strtod(const char* s00, const char** se); - - TEST(MaximumSignificantDigits) { char num[] = "4.4501477170144020250819966727949918635852426585926051135169509" @@ -160,12 +159,12 @@ TEST(MaximumSignificantDigits) { "847003580761626016356864581135848683152156368691976240370422601" "6998291015625000000000000000000000000000000000e-308"; - CHECK_EQ(gay_strtod(num, NULL), StringToDouble(num, NO_FLAGS)); + CHECK_EQ(4.4501477170144017780491e-308, StringToDouble(num, NO_FLAGS)); // Changes the result of strtod (at least in glibc implementation). num[sizeof(num) - 8] = '1'; - CHECK_EQ(gay_strtod(num, NULL), StringToDouble(num, NO_FLAGS)); + CHECK_EQ(4.4501477170144022721148e-308, StringToDouble(num, NO_FLAGS)); } TEST(MinimumExponent) { @@ -185,19 +184,19 @@ TEST(MinimumExponent) { "470035807616260163568645811358486831521563686919762403704226016" "998291015625000000000000000000000000000000000e-1108"; - CHECK_EQ(gay_strtod(num, NULL), StringToDouble(num, NO_FLAGS)); + CHECK_EQ(4.4501477170144017780491e-308, StringToDouble(num, NO_FLAGS)); // Changes the result of strtod (at least in glibc implementation). num[sizeof(num) - 8] = '1'; - CHECK_EQ(gay_strtod(num, NULL), StringToDouble(num, NO_FLAGS)); + CHECK_EQ(4.4501477170144022721148e-308, StringToDouble(num, NO_FLAGS)); } TEST(MaximumExponent) { char num[] = "0.16e309"; - CHECK_EQ(gay_strtod(num, NULL), StringToDouble(num, NO_FLAGS)); + CHECK_EQ(1.59999999999999997765e+308, StringToDouble(num, NO_FLAGS)); } diff --git a/test/cctest/test-debug.cc b/test/cctest/test-debug.cc index 748e3e8d..7791185c 100644 --- a/test/cctest/test-debug.cc +++ b/test/cctest/test-debug.cc @@ -6900,26 +6900,71 @@ TEST(DebugEventBreakData) { // Test that setting the terminate execution flag during debug break processing. +static void TestDebugBreakInLoop(const char* loop_head, + const char** loop_bodies, + const char* loop_tail) { + // Receive 100 breaks for each test and then terminate JavaScript execution. + static int count = 0; + + for (int i = 0; loop_bodies[i] != NULL; i++) { + count++; + max_break_point_hit_count = count * 100; + terminate_after_max_break_point_hit = true; + + EmbeddedVector<char, 1024> buffer; + OS::SNPrintF(buffer, + "function f() {%s%s%s}", + loop_head, loop_bodies[i], loop_tail); + + // Function with infinite loop. + CompileRun(buffer.start()); + + // Set the debug break to enter the debugger as soon as possible. + v8::Debug::DebugBreak(); + + // Call function with infinite loop. + CompileRun("f();"); + CHECK_EQ(count * 100, break_point_hit_count); + + CHECK(!v8::V8::IsExecutionTerminating()); + } +} + + TEST(DebugBreakLoop) { v8::HandleScope scope; DebugLocalContext env; - // Receive 100 breaks and terminate. - max_break_point_hit_count = 100; - terminate_after_max_break_point_hit = true; - // Register a debug event listener which sets the break flag and counts. v8::Debug::SetDebugEventListener(DebugEventBreakMax); - // Function with infinite loop. - CompileRun("function f() { while (true) { } }"); + CompileRun("var a = 1;"); + CompileRun("function g() { }"); + CompileRun("function h() { }"); + + const char* loop_bodies[] = { + "", + "g()", + "if (a == 0) { g() }", + "if (a == 1) { g() }", + "if (a == 0) { g() } else { h() }", + "if (a == 0) { continue }", + "if (a == 1) { continue }", + "switch (a) { case 1: g(); }", + "switch (a) { case 1: continue; }", + "switch (a) { case 1: g(); break; default: h() }", + "switch (a) { case 1: continue; break; default: h() }", + NULL + }; + + TestDebugBreakInLoop("while (true) {", loop_bodies, "}"); + TestDebugBreakInLoop("while (a == 1) {", loop_bodies, "}"); - // Set the debug break to enter the debugger as soon as possible. - v8::Debug::DebugBreak(); + TestDebugBreakInLoop("do {", loop_bodies, "} while (true)"); + TestDebugBreakInLoop("do {", loop_bodies, "} while (a == 1)"); - // Call function with infinite loop. - CompileRun("f();"); - CHECK_EQ(100, break_point_hit_count); + TestDebugBreakInLoop("for (;;) {", loop_bodies, "}"); + TestDebugBreakInLoop("for (;a == 1;) {", loop_bodies, "}"); // Get rid of the debug event listener. v8::Debug::SetDebugEventListener(NULL); diff --git a/test/cctest/test-disasm-arm.cc b/test/cctest/test-disasm-arm.cc index 61f5ffc7..c375831f 100644 --- a/test/cctest/test-disasm-arm.cc +++ b/test/cctest/test-disasm-arm.cc @@ -87,9 +87,9 @@ bool DisassembleAndCompare(byte* pc, const char* compare_string) { #define COMPARE(asm_, compare_string) \ { \ int pc_offset = assm.pc_offset(); \ - byte *pc = &buffer[pc_offset]; \ + byte *progcounter = &buffer[pc_offset]; \ assm.asm_; \ - if (!DisassembleAndCompare(pc, compare_string)) failure = true; \ + if (!DisassembleAndCompare(progcounter, compare_string)) failure = true; \ } @@ -499,6 +499,19 @@ TEST(Vfp) { "ed811b01 vstr d1, [r1 + 4*1]"); COMPARE(vstr(d15, r10, 1020), "ed8afbff vstr d15, [r10 + 4*255]"); + + COMPARE(vmsr(r5), + "eee15a10 vmsr FPSCR, r5"); + COMPARE(vmsr(r10, pl), + "5ee1aa10 vmsrpl FPSCR, r10"); + COMPARE(vmsr(pc), + "eee1fa10 vmsr FPSCR, APSR"); + COMPARE(vmrs(r5), + "eef15a10 vmrs r5, FPSCR"); + COMPARE(vmrs(r10, ge), + "aef1aa10 vmrsge r10, FPSCR"); + COMPARE(vmrs(pc), + "eef1fa10 vmrs APSR, FPSCR"); } VERIFY_RUN(); diff --git a/test/cctest/test-dtoa.cc b/test/cctest/test-dtoa.cc new file mode 100644 index 00000000..ff0b6607 --- /dev/null +++ b/test/cctest/test-dtoa.cc @@ -0,0 +1,331 @@ +// 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 <stdlib.h> + +#include "v8.h" + +#include "dtoa.h" + +#include "cctest.h" +#include "double.h" +#include "gay-fixed.h" +#include "gay-precision.h" +#include "gay-shortest.h" +#include "platform.h" + + +using namespace v8::internal; + + +// Removes trailing '0' digits. +static void TrimRepresentation(Vector<char> representation) { + int len = strlen(representation.start()); + int i; + for (i = len - 1; i >= 0; --i) { + if (representation[i] != '0') break; + } + representation[i + 1] = '\0'; +} + + +static const int kBufferSize = 100; + + +TEST(DtoaVariousDoubles) { + char buffer_container[kBufferSize]; + Vector<char> buffer(buffer_container, kBufferSize); + int length; + int point; + int sign; + + DoubleToAscii(0.0, DTOA_SHORTEST, 0, buffer, &sign, &length, &point); + CHECK_EQ("0", buffer.start()); + CHECK_EQ(1, point); + + DoubleToAscii(0.0, DTOA_FIXED, 2, buffer, &sign, &length, &point); + CHECK_EQ(1, length); + CHECK_EQ("0", buffer.start()); + CHECK_EQ(1, point); + + DoubleToAscii(0.0, DTOA_PRECISION, 3, buffer, &sign, &length, &point); + CHECK_EQ(1, length); + CHECK_EQ("0", buffer.start()); + CHECK_EQ(1, point); + + DoubleToAscii(1.0, DTOA_SHORTEST, 0, buffer, &sign, &length, &point); + CHECK_EQ("1", buffer.start()); + CHECK_EQ(1, point); + + DoubleToAscii(1.0, DTOA_FIXED, 3, buffer, &sign, &length, &point); + CHECK_GE(3, length - point); + TrimRepresentation(buffer); + CHECK_EQ("1", buffer.start()); + CHECK_EQ(1, point); + + DoubleToAscii(1.0, DTOA_PRECISION, 3, buffer, &sign, &length, &point); + CHECK_GE(3, length); + TrimRepresentation(buffer); + CHECK_EQ("1", buffer.start()); + CHECK_EQ(1, point); + + DoubleToAscii(1.5, DTOA_SHORTEST, 0, buffer, &sign, &length, &point); + CHECK_EQ("15", buffer.start()); + CHECK_EQ(1, point); + + DoubleToAscii(1.5, DTOA_FIXED, 10, buffer, &sign, &length, &point); + CHECK_GE(10, length - point); + TrimRepresentation(buffer); + CHECK_EQ("15", buffer.start()); + CHECK_EQ(1, point); + + DoubleToAscii(1.5, DTOA_PRECISION, 10, buffer, &sign, &length, &point); + CHECK_GE(10, length); + TrimRepresentation(buffer); + CHECK_EQ("15", buffer.start()); + CHECK_EQ(1, point); + + double min_double = 5e-324; + DoubleToAscii(min_double, DTOA_SHORTEST, 0, buffer, &sign, &length, &point); + CHECK_EQ("5", buffer.start()); + CHECK_EQ(-323, point); + + DoubleToAscii(min_double, DTOA_FIXED, 5, buffer, &sign, &length, &point); + CHECK_GE(5, length - point); + TrimRepresentation(buffer); + CHECK_EQ("", buffer.start()); + CHECK_GE(-5, point); + + DoubleToAscii(min_double, DTOA_PRECISION, 5, buffer, &sign, &length, &point); + CHECK_GE(5, length); + TrimRepresentation(buffer); + CHECK_EQ("49407", buffer.start()); + CHECK_EQ(-323, point); + + double max_double = 1.7976931348623157e308; + DoubleToAscii(max_double, DTOA_SHORTEST, 0, buffer, &sign, &length, &point); + CHECK_EQ("17976931348623157", buffer.start()); + CHECK_EQ(309, point); + + DoubleToAscii(max_double, DTOA_PRECISION, 7, buffer, &sign, &length, &point); + CHECK_GE(7, length); + TrimRepresentation(buffer); + CHECK_EQ("1797693", buffer.start()); + CHECK_EQ(309, point); + + DoubleToAscii(4294967272.0, DTOA_SHORTEST, 0, buffer, &sign, &length, &point); + CHECK_EQ("4294967272", buffer.start()); + CHECK_EQ(10, point); + + DoubleToAscii(4294967272.0, DTOA_FIXED, 5, buffer, &sign, &length, &point); + CHECK_GE(5, length - point); + TrimRepresentation(buffer); + CHECK_EQ("4294967272", buffer.start()); + CHECK_EQ(10, point); + + + DoubleToAscii(4294967272.0, DTOA_PRECISION, 14, + buffer, &sign, &length, &point); + CHECK_GE(14, length); + TrimRepresentation(buffer); + CHECK_EQ("4294967272", buffer.start()); + CHECK_EQ(10, point); + + DoubleToAscii(4.1855804968213567e298, DTOA_SHORTEST, 0, + buffer, &sign, &length, &point); + CHECK_EQ("4185580496821357", buffer.start()); + CHECK_EQ(299, point); + + DoubleToAscii(4.1855804968213567e298, DTOA_PRECISION, 20, + buffer, &sign, &length, &point); + CHECK_GE(20, length); + TrimRepresentation(buffer); + CHECK_EQ("41855804968213567225", buffer.start()); + CHECK_EQ(299, point); + + DoubleToAscii(5.5626846462680035e-309, DTOA_SHORTEST, 0, + buffer, &sign, &length, &point); + CHECK_EQ("5562684646268003", buffer.start()); + CHECK_EQ(-308, point); + + DoubleToAscii(5.5626846462680035e-309, DTOA_PRECISION, 1, + buffer, &sign, &length, &point); + CHECK_GE(1, length); + TrimRepresentation(buffer); + CHECK_EQ("6", buffer.start()); + CHECK_EQ(-308, point); + + DoubleToAscii(-2147483648.0, DTOA_SHORTEST, 0, + buffer, &sign, &length, &point); + CHECK_EQ(1, sign); + CHECK_EQ("2147483648", buffer.start()); + CHECK_EQ(10, point); + + + DoubleToAscii(-2147483648.0, DTOA_FIXED, 2, buffer, &sign, &length, &point); + CHECK_GE(2, length - point); + TrimRepresentation(buffer); + CHECK_EQ(1, sign); + CHECK_EQ("2147483648", buffer.start()); + CHECK_EQ(10, point); + + DoubleToAscii(-2147483648.0, DTOA_PRECISION, 5, + buffer, &sign, &length, &point); + CHECK_GE(5, length); + TrimRepresentation(buffer); + CHECK_EQ(1, sign); + CHECK_EQ("21475", buffer.start()); + CHECK_EQ(10, point); + + DoubleToAscii(-3.5844466002796428e+298, DTOA_SHORTEST, 0, + buffer, &sign, &length, &point); + CHECK_EQ(1, sign); + CHECK_EQ("35844466002796428", buffer.start()); + CHECK_EQ(299, point); + + DoubleToAscii(-3.5844466002796428e+298, DTOA_PRECISION, 10, + buffer, &sign, &length, &point); + CHECK_EQ(1, sign); + CHECK_GE(10, length); + TrimRepresentation(buffer); + CHECK_EQ("35844466", buffer.start()); + CHECK_EQ(299, point); + + uint64_t smallest_normal64 = V8_2PART_UINT64_C(0x00100000, 00000000); + double v = Double(smallest_normal64).value(); + DoubleToAscii(v, DTOA_SHORTEST, 0, buffer, &sign, &length, &point); + CHECK_EQ("22250738585072014", buffer.start()); + CHECK_EQ(-307, point); + + DoubleToAscii(v, DTOA_PRECISION, 20, buffer, &sign, &length, &point); + CHECK_GE(20, length); + TrimRepresentation(buffer); + CHECK_EQ("22250738585072013831", buffer.start()); + CHECK_EQ(-307, point); + + uint64_t largest_denormal64 = V8_2PART_UINT64_C(0x000FFFFF, FFFFFFFF); + v = Double(largest_denormal64).value(); + DoubleToAscii(v, DTOA_SHORTEST, 0, buffer, &sign, &length, &point); + CHECK_EQ("2225073858507201", buffer.start()); + CHECK_EQ(-307, point); + + DoubleToAscii(v, DTOA_PRECISION, 20, buffer, &sign, &length, &point); + CHECK_GE(20, length); + TrimRepresentation(buffer); + CHECK_EQ("2225073858507200889", buffer.start()); + CHECK_EQ(-307, point); + + DoubleToAscii(4128420500802942e-24, DTOA_SHORTEST, 0, + buffer, &sign, &length, &point); + CHECK_EQ(0, sign); + CHECK_EQ("4128420500802942", buffer.start()); + CHECK_EQ(-8, point); + + v = -3.9292015898194142585311918e-10; + DoubleToAscii(v, DTOA_SHORTEST, 0, buffer, &sign, &length, &point); + CHECK_EQ("39292015898194143", buffer.start()); + + v = 4194304.0; + DoubleToAscii(v, DTOA_FIXED, 5, buffer, &sign, &length, &point); + CHECK_GE(5, length - point); + TrimRepresentation(buffer); + CHECK_EQ("4194304", buffer.start()); + + v = 3.3161339052167390562200598e-237; + DoubleToAscii(v, DTOA_PRECISION, 19, buffer, &sign, &length, &point); + CHECK_GE(19, length); + TrimRepresentation(buffer); + CHECK_EQ("3316133905216739056", buffer.start()); + CHECK_EQ(-236, point); +} + + +TEST(DtoaGayShortest) { + char buffer_container[kBufferSize]; + Vector<char> buffer(buffer_container, kBufferSize); + int sign; + int length; + int point; + + Vector<const PrecomputedShortest> precomputed = + PrecomputedShortestRepresentations(); + for (int i = 0; i < precomputed.length(); ++i) { + const PrecomputedShortest current_test = precomputed[i]; + double v = current_test.v; + DoubleToAscii(v, DTOA_SHORTEST, 0, buffer, &sign, &length, &point); + CHECK_EQ(0, sign); // All precomputed numbers are positive. + CHECK_EQ(current_test.decimal_point, point); + CHECK_EQ(current_test.representation, buffer.start()); + } +} + + +TEST(DtoaGayFixed) { + char buffer_container[kBufferSize]; + Vector<char> buffer(buffer_container, kBufferSize); + int sign; + int length; + int point; + + Vector<const PrecomputedFixed> precomputed = + PrecomputedFixedRepresentations(); + for (int i = 0; i < precomputed.length(); ++i) { + const PrecomputedFixed current_test = precomputed[i]; + double v = current_test.v; + int number_digits = current_test.number_digits; + DoubleToAscii(v, DTOA_FIXED, number_digits, buffer, &sign, &length, &point); + CHECK_EQ(0, sign); // All precomputed numbers are positive. + CHECK_EQ(current_test.decimal_point, point); + CHECK_GE(number_digits, length - point); + TrimRepresentation(buffer); + CHECK_EQ(current_test.representation, buffer.start()); + } +} + + +TEST(DtoaGayPrecision) { + char buffer_container[kBufferSize]; + Vector<char> buffer(buffer_container, kBufferSize); + int sign; + int length; + int point; + + Vector<const PrecomputedPrecision> precomputed = + PrecomputedPrecisionRepresentations(); + for (int i = 0; i < precomputed.length(); ++i) { + const PrecomputedPrecision current_test = precomputed[i]; + double v = current_test.v; + int number_digits = current_test.number_digits; + DoubleToAscii(v, DTOA_PRECISION, number_digits, + buffer, &sign, &length, &point); + CHECK_EQ(0, sign); // All precomputed numbers are positive. + CHECK_EQ(current_test.decimal_point, point); + CHECK_GE(number_digits, length); + TrimRepresentation(buffer); + CHECK_EQ(current_test.representation, buffer.start()); + } +} diff --git a/test/cctest/test-heap-profiler.cc b/test/cctest/test-heap-profiler.cc index b165190b..95314d74 100644 --- a/test/cctest/test-heap-profiler.cc +++ b/test/cctest/test-heap-profiler.cc @@ -411,8 +411,12 @@ class NamedEntriesDetector { static const v8::HeapGraphNode* GetGlobalObject( const v8::HeapSnapshot* snapshot) { - CHECK_EQ(1, snapshot->GetRoot()->GetChildrenCount()); - return snapshot->GetRoot()->GetChild(0)->GetToNode(); + CHECK_EQ(2, snapshot->GetRoot()->GetChildrenCount()); + const v8::HeapGraphNode* global_obj = + snapshot->GetRoot()->GetChild(0)->GetToNode(); + CHECK_EQ("Object", const_cast<i::HeapEntry*>( + reinterpret_cast<const i::HeapEntry*>(global_obj))->name()); + return global_obj; } @@ -479,21 +483,24 @@ TEST(HeapSnapshot) { // Verify, that JS global object of env2 has '..2' properties. const v8::HeapGraphNode* a2_node = - GetProperty(global_env2, v8::HeapGraphEdge::kProperty, "a2"); + GetProperty(global_env2, v8::HeapGraphEdge::kShortcut, "a2"); CHECK_NE(NULL, a2_node); CHECK_NE( - NULL, GetProperty(global_env2, v8::HeapGraphEdge::kProperty, "b2_1")); + NULL, GetProperty(global_env2, v8::HeapGraphEdge::kShortcut, "b2_1")); CHECK_NE( - NULL, GetProperty(global_env2, v8::HeapGraphEdge::kProperty, "b2_2")); - CHECK_NE(NULL, GetProperty(global_env2, v8::HeapGraphEdge::kProperty, "c2")); + NULL, GetProperty(global_env2, v8::HeapGraphEdge::kShortcut, "b2_2")); + CHECK_NE(NULL, GetProperty(global_env2, v8::HeapGraphEdge::kShortcut, "c2")); - // Verify that anything related to '[ABC]1' is not reachable. NamedEntriesDetector det; i_snapshot_env2->IterateEntries(&det); CHECK(det.has_A2); CHECK(det.has_B2); CHECK(det.has_C2); + /* + // Currently disabled. Too many retaining paths emerge, need to + // reduce the amount. + // Verify 'a2' object retainers. They are: // - (global object).a2 // - c2.x1, c2.x2, c2[1] @@ -538,6 +545,7 @@ TEST(HeapSnapshot) { CHECK(has_c2_1_ref); CHECK(has_b2_1_x_ref); CHECK(has_b2_2_x_ref); + */ } @@ -550,37 +558,28 @@ TEST(HeapSnapshotObjectSizes) { CompileRun( "function X(a, b) { this.a = a; this.b = b; }\n" "x = new X(new X(), new X());\n" - "x.a.a = x.b;"); + "(function() { x.a.a = x.b; })();"); const v8::HeapSnapshot* snapshot = v8::HeapProfiler::TakeSnapshot(v8::String::New("sizes")); const v8::HeapGraphNode* global = GetGlobalObject(snapshot); const v8::HeapGraphNode* x = - GetProperty(global, v8::HeapGraphEdge::kProperty, "x"); + GetProperty(global, v8::HeapGraphEdge::kShortcut, "x"); CHECK_NE(NULL, x); - const v8::HeapGraphNode* x_prototype = - GetProperty(x, v8::HeapGraphEdge::kProperty, "__proto__"); - CHECK_NE(NULL, x_prototype); const v8::HeapGraphNode* x1 = GetProperty(x, v8::HeapGraphEdge::kProperty, "a"); CHECK_NE(NULL, x1); const v8::HeapGraphNode* x2 = GetProperty(x, v8::HeapGraphEdge::kProperty, "b"); CHECK_NE(NULL, x2); - CHECK_EQ( - x->GetSelfSize() * 3, - x->GetReachableSize() - x_prototype->GetReachableSize()); - CHECK_EQ( - x->GetSelfSize() * 3, x->GetRetainedSize()); - CHECK_EQ( - x1->GetSelfSize() * 2, - x1->GetReachableSize() - x_prototype->GetReachableSize()); - CHECK_EQ( - x1->GetSelfSize(), x1->GetRetainedSize()); - CHECK_EQ( - x2->GetSelfSize(), - x2->GetReachableSize() - x_prototype->GetReachableSize()); - CHECK_EQ( - x2->GetSelfSize(), x2->GetRetainedSize()); + + // Test approximate sizes. + CHECK_EQ(x->GetSelfSize() * 3, x->GetRetainedSize(false)); + CHECK_EQ(x1->GetSelfSize(), x1->GetRetainedSize(false)); + CHECK_EQ(x2->GetSelfSize(), x2->GetRetainedSize(false)); + // Test exact sizes. + CHECK_EQ(x->GetSelfSize() * 3, x->GetRetainedSize(true)); + CHECK_EQ(x1->GetSelfSize(), x1->GetRetainedSize(true)); + CHECK_EQ(x2->GetSelfSize(), x2->GetRetainedSize(true)); } @@ -622,15 +621,15 @@ TEST(HeapSnapshotCodeObjects) { const v8::HeapGraphNode* global = GetGlobalObject(snapshot); const v8::HeapGraphNode* compiled = - GetProperty(global, v8::HeapGraphEdge::kProperty, "compiled"); + GetProperty(global, v8::HeapGraphEdge::kShortcut, "compiled"); CHECK_NE(NULL, compiled); CHECK_EQ(v8::HeapGraphNode::kClosure, compiled->GetType()); const v8::HeapGraphNode* lazy = - GetProperty(global, v8::HeapGraphEdge::kProperty, "lazy"); + GetProperty(global, v8::HeapGraphEdge::kShortcut, "lazy"); CHECK_NE(NULL, lazy); CHECK_EQ(v8::HeapGraphNode::kClosure, lazy->GetType()); const v8::HeapGraphNode* anonymous = - GetProperty(global, v8::HeapGraphEdge::kProperty, "anonymous"); + GetProperty(global, v8::HeapGraphEdge::kShortcut, "anonymous"); CHECK_NE(NULL, anonymous); CHECK_EQ(v8::HeapGraphNode::kClosure, anonymous->GetType()); v8::String::AsciiValue anonymous_name(anonymous->GetName()); @@ -682,9 +681,9 @@ TEST(HeapSnapshotHeapNumbers) { const v8::HeapSnapshot* snapshot = v8::HeapProfiler::TakeSnapshot(v8::String::New("numbers")); const v8::HeapGraphNode* global = GetGlobalObject(snapshot); - CHECK_EQ(NULL, GetProperty(global, v8::HeapGraphEdge::kProperty, "a")); + CHECK_EQ(NULL, GetProperty(global, v8::HeapGraphEdge::kShortcut, "a")); const v8::HeapGraphNode* b = - GetProperty(global, v8::HeapGraphEdge::kProperty, "b"); + GetProperty(global, v8::HeapGraphEdge::kShortcut, "b"); CHECK_NE(NULL, b); CHECK_EQ(v8::HeapGraphNode::kHeapNumber, b->GetType()); } @@ -808,12 +807,12 @@ TEST(HeapSnapshotsDiff) { if (node->GetType() == v8::HeapGraphNode::kObject) { v8::String::AsciiValue node_name(node->GetName()); if (strcmp(*node_name, "A2") == 0) { - CHECK(IsNodeRetainedAs(node, v8::HeapGraphEdge::kProperty, "a")); + CHECK(IsNodeRetainedAs(node, v8::HeapGraphEdge::kShortcut, "a")); CHECK(!found_A); found_A = true; s1_A_id = node->GetId(); } else if (strcmp(*node_name, "B") == 0) { - CHECK(IsNodeRetainedAs(node, v8::HeapGraphEdge::kProperty, "b2")); + CHECK(IsNodeRetainedAs(node, v8::HeapGraphEdge::kShortcut, "b2")); CHECK(!found_B); found_B = true; } @@ -832,7 +831,7 @@ TEST(HeapSnapshotsDiff) { if (node->GetType() == v8::HeapGraphNode::kObject) { v8::String::AsciiValue node_name(node->GetName()); if (strcmp(*node_name, "A") == 0) { - CHECK(IsNodeRetainedAs(node, v8::HeapGraphEdge::kProperty, "a")); + CHECK(IsNodeRetainedAs(node, v8::HeapGraphEdge::kShortcut, "a")); CHECK(!found_A_del); found_A_del = true; s2_A_id = node->GetId(); @@ -858,37 +857,6 @@ TEST(HeapSnapshotRootPreservedAfterSorting) { } -namespace v8 { -namespace internal { - -class HeapSnapshotTester { - public: - static int CalculateNetworkSize(JSObject* obj) { - return HeapSnapshot::CalculateNetworkSize(obj); - } -}; - -} } // namespace v8::internal - -// http://code.google.com/p/v8/issues/detail?id=822 -// Trying to call CalculateNetworkSize on an object with elements set -// to non-FixedArray may cause an assertion error in debug builds. -TEST(Issue822) { - v8::HandleScope scope; - LocalContext context; - const int kElementCount = 260; - uint8_t* pixel_data = reinterpret_cast<uint8_t*>(malloc(kElementCount)); - i::Handle<i::PixelArray> pixels = i::Factory::NewPixelArray(kElementCount, - pixel_data); - v8::Handle<v8::Object> obj = v8::Object::New(); - // Set the elements to be the pixels. - obj->SetIndexedPropertiesToPixelData(pixel_data, kElementCount); - i::Handle<i::JSObject> jsobj = v8::Utils::OpenHandle(*obj); - // This call must not cause an assertion error in debug builds. - i::HeapSnapshotTester::CalculateNetworkSize(*jsobj); -} - - static const v8::HeapGraphNode* GetChild( const v8::HeapGraphNode* node, v8::HeapGraphNode::Type type, @@ -932,13 +900,13 @@ TEST(AggregatedHeapSnapshot) { v8::HeapProfiler::TakeSnapshot( v8::String::New("agg"), v8::HeapSnapshot::kAggregated); const v8::HeapGraphNode* strings = GetChild(snapshot->GetRoot(), - v8::HeapGraphNode::kInternal, + v8::HeapGraphNode::kHidden, "STRING_TYPE"); CHECK_NE(NULL, strings); CHECK_NE(0, strings->GetSelfSize()); CHECK_NE(0, strings->GetInstancesCount()); const v8::HeapGraphNode* maps = GetChild(snapshot->GetRoot(), - v8::HeapGraphNode::kInternal, + v8::HeapGraphNode::kHidden, "MAP_TYPE"); CHECK_NE(NULL, maps); CHECK_NE(0, maps->GetSelfSize()); @@ -998,6 +966,67 @@ TEST(AggregatedHeapSnapshot) { CHECK(IsNodeRetainedAs(a_from_b, 1)); // B has 1 ref to A. } + +TEST(HeapEntryDominator) { + // The graph looks like this: + // + // -> node1 + // a |^ + // -> node5 ba + // a v| + // node6 -> node2 + // b a |^ + // -> node4 ba + // b v| + // -> node3 + // + // The dominator for all nodes is node6. + + v8::HandleScope scope; + LocalContext env; + + CompileRun( + "function X(a, b) { this.a = a; this.b = b; }\n" + "node6 = new X(new X(new X()), new X(new X(),new X()));\n" + "(function(){\n" + "node6.a.a.b = node6.b.a; // node1 -> node2\n" + "node6.b.a.a = node6.a.a; // node2 -> node1\n" + "node6.b.a.b = node6.b.b; // node2 -> node3\n" + "node6.b.b.a = node6.b.a; // node3 -> node2\n" + "})();"); + + const v8::HeapSnapshot* snapshot = + v8::HeapProfiler::TakeSnapshot(v8::String::New("dominators")); + + const v8::HeapGraphNode* global = GetGlobalObject(snapshot); + CHECK_NE(NULL, global); + const v8::HeapGraphNode* node6 = + GetProperty(global, v8::HeapGraphEdge::kShortcut, "node6"); + CHECK_NE(NULL, node6); + const v8::HeapGraphNode* node5 = + GetProperty(node6, v8::HeapGraphEdge::kProperty, "a"); + CHECK_NE(NULL, node5); + const v8::HeapGraphNode* node4 = + GetProperty(node6, v8::HeapGraphEdge::kProperty, "b"); + CHECK_NE(NULL, node4); + const v8::HeapGraphNode* node3 = + GetProperty(node4, v8::HeapGraphEdge::kProperty, "b"); + CHECK_NE(NULL, node3); + const v8::HeapGraphNode* node2 = + GetProperty(node4, v8::HeapGraphEdge::kProperty, "a"); + CHECK_NE(NULL, node2); + const v8::HeapGraphNode* node1 = + GetProperty(node5, v8::HeapGraphEdge::kProperty, "a"); + CHECK_NE(NULL, node1); + + CHECK_EQ(node6, node1->GetDominatorNode()); + CHECK_EQ(node6, node2->GetDominatorNode()); + CHECK_EQ(node6, node3->GetDominatorNode()); + CHECK_EQ(node6, node4->GetDominatorNode()); + CHECK_EQ(node6, node5->GetDominatorNode()); +} + + namespace { class TestJSONStream : public v8::OutputStream { @@ -1073,13 +1102,9 @@ TEST(HeapSnapshotJSONSerialization) { CHECK(parsed_snapshot->Has(v8::String::New("nodes"))); CHECK(parsed_snapshot->Has(v8::String::New("strings"))); - // Verify that nodes meta-info is valid JSON. - v8::Local<v8::Value> nodes_meta_parse_result = CompileRun( - "var parsed_meta = JSON.parse(parsed.nodes[0]); true;"); - CHECK(!nodes_meta_parse_result.IsEmpty()); - // Get node and edge "member" offsets. v8::Local<v8::Value> meta_analysis_result = CompileRun( + "var parsed_meta = parsed.nodes[0];\n" "var children_count_offset =" " parsed_meta.fields.indexOf('children_count');\n" "var children_offset =" @@ -1094,19 +1119,21 @@ TEST(HeapSnapshotJSONSerialization) { "var child_to_node_offset =" " children_meta.fields.indexOf('to_node');\n" "var property_type =" - " children_meta.types[child_type_offset].indexOf('property');"); + " children_meta.types[child_type_offset].indexOf('property');\n" + "var shortcut_type =" + " children_meta.types[child_type_offset].indexOf('shortcut');"); CHECK(!meta_analysis_result.IsEmpty()); // A helper function for processing encoded nodes. CompileRun( - "function GetChildPosByProperty(pos, prop_name) {\n" + "function GetChildPosByProperty(pos, prop_name, prop_type) {\n" " var nodes = parsed.nodes;\n" " var strings = parsed.strings;\n" " for (var i = 0,\n" " count = nodes[pos + children_count_offset] * child_fields_count;\n" " i < count; i += child_fields_count) {\n" " var child_pos = pos + children_offset + i;\n" - " if (nodes[child_pos + child_type_offset] === property_type\n" + " if (nodes[child_pos + child_type_offset] === prop_type\n" " && strings[nodes[child_pos + child_name_offset]] === prop_name)\n" " return nodes[child_pos + child_to_node_offset];\n" " }\n" @@ -1117,9 +1144,10 @@ TEST(HeapSnapshotJSONSerialization) { "GetChildPosByProperty(\n" " GetChildPosByProperty(\n" " GetChildPosByProperty(" - " parsed.nodes[1 + children_offset + child_to_node_offset],\"b\"),\n" - " \"x\")," - " \"s\")"); + " parsed.nodes[1 + children_offset + child_to_node_offset]," + " \"b\",shortcut_type),\n" + " \"x\", property_type)," + " \"s\", property_type)"); CHECK(!string_obj_pos_val.IsEmpty()); int string_obj_pos = static_cast<int>(string_obj_pos_val->ToNumber()->Value()); @@ -1150,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/test/cctest/test-heap.cc b/test/cctest/test-heap.cc index 94d05555..fbe66ecd 100644 --- a/test/cctest/test-heap.cc +++ b/test/cctest/test-heap.cc @@ -1095,3 +1095,32 @@ TEST(TestInternalWeakListsTraverseWithGC) { ctx[i]->Exit(); } } + + +TEST(TestSizeOfObjectsVsHeapIteratorPrecision) { + InitializeVM(); + intptr_t size_of_objects_1 = Heap::SizeOfObjects(); + HeapIterator iterator(HeapIterator::kPreciseFiltering); + intptr_t size_of_objects_2 = 0; + for (HeapObject* obj = iterator.next(); + obj != NULL; + obj = iterator.next()) { + size_of_objects_2 += obj->Size(); + } + // Delta must be within 1% of the larger result. + if (size_of_objects_1 > size_of_objects_2) { + intptr_t delta = size_of_objects_1 - size_of_objects_2; + PrintF("Heap::SizeOfObjects: %" V8_PTR_PREFIX "d, " + "Iterator: %" V8_PTR_PREFIX "d, " + "delta: %" V8_PTR_PREFIX "d\n", + size_of_objects_1, size_of_objects_2, delta); + CHECK_GT(size_of_objects_1 / 100, delta); + } else { + intptr_t delta = size_of_objects_2 - size_of_objects_1; + PrintF("Heap::SizeOfObjects: %" V8_PTR_PREFIX "d, " + "Iterator: %" V8_PTR_PREFIX "d, " + "delta: %" V8_PTR_PREFIX "d\n", + size_of_objects_1, size_of_objects_2, delta); + CHECK_GT(size_of_objects_2 / 100, delta); + } +} diff --git a/test/cctest/test-log.cc b/test/cctest/test-log.cc index 71687374..710c10e9 100644 --- a/test/cctest/test-log.cc +++ b/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<Sampler*>(Logger::ticker_)->ResetSamplesTaken(); + } + static bool has_samples_taken() { + return reinterpret_cast<Sampler*>(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<char, 100> 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/test/cctest/test-parsing.cc b/test/cctest/test-parsing.cc index 7ae8dcfa..a93fc271 100755 --- a/test/cctest/test-parsing.cc +++ b/test/cctest/test-parsing.cc @@ -27,6 +27,7 @@ #include <stdlib.h> #include <stdio.h> +#include <string.h> #include "v8.h" @@ -35,7 +36,6 @@ #include "parser.h" #include "utils.h" #include "execution.h" -#include "scanner.h" #include "preparser.h" #include "cctest.h" @@ -261,12 +261,69 @@ TEST(StandAlonePreParser) { const char* program = programs[i]; unibrow::Utf8InputBuffer<256> stream(program, strlen(program)); i::CompleteParserRecorder log; - i::Scanner scanner; - scanner.Initialize(i::Handle<i::String>::null(), &stream, i::JAVASCRIPT); - v8::preparser::PreParser<i::Scanner, i::CompleteParserRecorder> preparser; + i::V8JavaScriptScanner scanner; + scanner.Initialize(i::Handle<i::String>::null(), &stream); + v8::preparser::PreParser preparser; bool result = preparser.PreParseProgram(&scanner, &log, true); CHECK(result); i::ScriptDataImpl data(log.ExtractData()); CHECK(!data.has_error()); } } + + +TEST(RegressChromium62639) { + int marker; + i::StackGuard::SetStackLimit( + reinterpret_cast<uintptr_t>(&marker) - 128 * 1024); + + const char* program = "var x = 'something';\n" + "escape: function() {}"; + // Fails parsing expecting an identifier after "function". + // Before fix, didn't check *ok after Expect(Token::Identifier, ok), + // and then used the invalid currently scanned literal. This always + // failed in debug mode, and sometimes crashed in release mode. + + unibrow::Utf8InputBuffer<256> stream(program, strlen(program)); + i::ScriptDataImpl* data = + i::ParserApi::PreParse(i::Handle<i::String>::null(), &stream, NULL); + CHECK(data->HasError()); + delete data; +} + + +TEST(Regress928) { + // Preparsing didn't consider the catch clause of a try statement + // as with-content, which made it assume that a function inside + // the block could be lazily compiled, and an extra, unexpected, + // entry was added to the data. + int marker; + i::StackGuard::SetStackLimit( + reinterpret_cast<uintptr_t>(&marker) - 128 * 1024); + + const char* program = + "try { } catch (e) { var foo = function () { /* first */ } }" + "var bar = function () { /* second */ }"; + + unibrow::Utf8InputBuffer<256> stream(program, strlen(program)); + i::ScriptDataImpl* data = + i::ParserApi::PartialPreParse(i::Handle<i::String>::null(), + &stream, NULL); + CHECK(!data->HasError()); + + data->Initialize(); + + int first_function = strstr(program, "function") - program; + int first_lbrace = first_function + strlen("function () "); + CHECK_EQ('{', program[first_lbrace]); + i::FunctionEntry entry1 = data->GetFunctionEntry(first_lbrace); + CHECK(!entry1.is_valid()); + + int second_function = strstr(program + first_lbrace, "function") - program; + int second_lbrace = second_function + strlen("function () "); + CHECK_EQ('{', program[second_lbrace]); + i::FunctionEntry entry2 = data->GetFunctionEntry(second_lbrace); + CHECK(entry2.is_valid()); + CHECK_EQ('}', program[entry2.end_pos() - 1]); + delete data; +} diff --git a/test/cctest/test-strtod.cc b/test/cctest/test-strtod.cc index d71d126b..f5547dbc 100644 --- a/test/cctest/test-strtod.cc +++ b/test/cctest/test-strtod.cc @@ -259,6 +259,23 @@ TEST(Strtod) { CHECK_EQ(1234567890123456789052345e115, StrtodChar("1234567890123456789052345", 115)); + CHECK_EQ(5.445618932859895e-255, + StrtodChar("5445618932859895362967233318697132813618813095743952975" + "4392982234069699615600475529427176366709107287468930197" + "8628345413991790019316974825934906752493984055268219809" + "5012176093045431437495773903922425632551857520884625114" + "6241265881735209066709685420744388526014389929047617597" + "0302268848374508109029268898695825171158085457567481507" + "4162979705098246243690189880319928315307816832576838178" + "2563074014542859888710209237525873301724479666744537857" + "9026553346649664045621387124193095870305991178772256504" + "4368663670643970181259143319016472430928902201239474588" + "1392338901353291306607057623202353588698746085415097902" + "6640064319118728664842287477491068264828851624402189317" + "2769161449825765517353755844373640588822904791244190695" + "2998382932630754670573838138825217065450843010498555058" + "88186560731", -1035)); + // Boundary cases. Boundaries themselves should round to even. // // 0x1FFFFFFFFFFFF * 2^3 = 72057594037927928 diff --git a/test/mjsunit/compiler/literals.js b/test/mjsunit/compiler/literals.js index 6775401d..d846cf5b 100644 --- a/test/mjsunit/compiler/literals.js +++ b/test/mjsunit/compiler/literals.js @@ -34,6 +34,43 @@ assertEquals("abc", eval("'abc'")); assertEquals(8, eval("6;'abc';8")); +// Characters just outside the ranges of hex-escapes. +// "/" comes just before "0". +assertEquals("x1/", "\x1/"); +assertEquals("u111/", "\u111/"); +assertEquals("\\x1/", RegExp("\\x1/").source); +assertEquals("\\u111/", RegExp("\\u111/").source); + +// ":" comes just after "9". +assertEquals("x1:", "\x1:"); +assertEquals("u111:", "\u111:"); +assertEquals("\\x1:", /\x1:/.source); +assertEquals("\\u111:", /\u111:/.source); + +// "`" comes just before "a". +assertEquals("x1`", "\x1`"); +assertEquals("u111`", "\u111`"); +assertEquals("\\x1`", /\x1`/.source); +assertEquals("\\u111`", /\u111`/.source); + +// "g" comes just before "f". +assertEquals("x1g", "\x1g"); +assertEquals("u111g", "\u111g"); +assertEquals("\\x1g", /\x1g/.source); +assertEquals("\\u111g", /\u111g/.source); + +// "@" comes just before "A". +assertEquals("x1@", "\x1@"); +assertEquals("u111@", "\u111@"); +assertEquals("\\x1@", /\x1@/.source); +assertEquals("\\u111@", /\u111@/.source); + +// "G" comes just after "F". +assertEquals("x1G", "\x1G"); +assertEquals("u111G", "\u111G"); +assertEquals("\\x1G", /\x1G/.source); +assertEquals("\\u111G", /\u111G/.source); + // Test some materialized array literals. assertEquals([1,2,3,4], eval('[1,2,3,4]')); assertEquals([[1,2],3,4], eval('[[1,2],3,4]')); @@ -50,3 +87,4 @@ assertEquals([2,4,6,8], eval(s)); assertEquals(17, eval('[1,2,3,4]; 17')); assertEquals(19, eval('var a=1, b=2; [a,b,3,4]; 19')); assertEquals(23, eval('var a=1, b=2; c=23; [a,b,3,4]; c')); + diff --git a/test/mjsunit/regress/regress-918.js b/test/mjsunit/regress/regress-918.js new file mode 100644 index 00000000..4b6ddbac --- /dev/null +++ b/test/mjsunit/regress/regress-918.js @@ -0,0 +1,33 @@ +// 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. + +// Parser should not accept parentheses around labels. +// See http://code.google.com/p/v8/issues/detail?id=918 + +// The label was parsed as an expression and then tested for being a +// single identifier. This threw away the parentheses. +assertThrows("(label):42;"); diff --git a/test/mjsunit/regress/regress-931.js b/test/mjsunit/regress/regress-931.js new file mode 100644 index 00000000..d2fb8cc2 --- /dev/null +++ b/test/mjsunit/regress/regress-931.js @@ -0,0 +1,48 @@ +// Copyright 2009 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. + +// See http://code.google.com/p/v8/issues/detail?id=931. + +var sequence = ''; + +var o = { f: function (x, y) { return x + y; }, + 2: function (x, y) { return x - y} }; + +function first() { sequence += "1"; return o; } +function second() { sequence += "2"; return "f"; } +function third() { sequence += "3"; return 3; } +function fourth() { sequence += "4"; return 4; } + +var result = (first()[second()](third(), fourth())) +assertEquals(7, result); +assertEquals("1234", sequence); + +function second_prime() { sequence += "2'"; return 2; } + +var result = (first()[second_prime()](third(), fourth())) +assertEquals(-1, result); +assertEquals("123412'34", sequence); diff --git a/test/mjsunit/regress/regress-944.js b/test/mjsunit/regress/regress-944.js new file mode 100644 index 00000000..d165336f --- /dev/null +++ b/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/test/mjsunit/string-split.js b/test/mjsunit/string-split.js index c741f6a3..6fcf5579 100644 --- a/test/mjsunit/string-split.js +++ b/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/tools/gyp/v8.gyp b/tools/gyp/v8.gyp index 65b86203..c1a5aab1 100644 --- a/tools/gyp/v8.gyp +++ b/tools/gyp/v8.gyp @@ -282,6 +282,8 @@ '../../src/ast.h', '../../src/bignum.cc', '../../src/bignum.h', + '../../src/bignum-dtoa.cc', + '../../src/bignum-dtoa.h', '../../src/bootstrapper.cc', '../../src/bootstrapper.h', '../../src/builtins.cc', @@ -331,7 +333,6 @@ '../../src/disassembler.h', '../../src/dtoa.cc', '../../src/dtoa.h', - '../../src/dtoa-config.c', '../../src/diy-fp.cc', '../../src/diy-fp.h', '../../src/double.h', @@ -405,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', @@ -470,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', @@ -484,6 +492,10 @@ '../../src/zone-inl.h', '../../src/zone.cc', '../../src/zone.h', + '../../src/extensions/externalize-string-extension.cc', + '../../src/extensions/externalize-string-extension.h', + '../../src/extensions/gc-extension.cc', + '../../src/extensions/gc-extension.h', ], 'conditions': [ ['v8_target_arch=="arm"', { @@ -651,11 +663,7 @@ '../../src/platform-win32.cc', ], # 4355, 4800 came from common.vsprops - # 4018, 4244 were a per file config on dtoa-config.c - # TODO: It's probably possible and desirable to stop disabling the - # dtoa-specific warnings by modifying dtoa as was done in Chromium - # r9255. Refer to that revision for details. - 'msvs_disabled_warnings': [4355, 4800, 4018, 4244], + 'msvs_disabled_warnings': [4355, 4800], 'link_settings': { 'libraries': [ '-lwinmm.lib' ], }, diff --git a/tools/presubmit.py b/tools/presubmit.py index ebf8bd89..1d80f92b 100755 --- a/tools/presubmit.py +++ b/tools/presubmit.py @@ -195,7 +195,7 @@ class CppLintProcessor(SourceFileProcessor): or (name in CppLintProcessor.IGNORE_LINT)) def GetPathsToSearch(self): - return ['src', '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/tools/v8.xcodeproj/project.pbxproj b/tools/v8.xcodeproj/project.pbxproj index 5f93c78a..08558cc5 100644 --- a/tools/v8.xcodeproj/project.pbxproj +++ b/tools/v8.xcodeproj/project.pbxproj @@ -115,7 +115,6 @@ 89A88E180E71A6960043BA31 /* property.cc in Sources */ = {isa = PBXBuildFile; fileRef = 897FF16D0E719B8F00D62E90 /* property.cc */; }; 89A88E190E71A6970043BA31 /* rewriter.cc in Sources */ = {isa = PBXBuildFile; fileRef = 897FF16F0E719B8F00D62E90 /* rewriter.cc */; }; 89A88E1A0E71A69B0043BA31 /* runtime.cc in Sources */ = {isa = PBXBuildFile; fileRef = 897FF1710E719B8F00D62E90 /* runtime.cc */; }; - 89A88E1B0E71A69D0043BA31 /* scanner-base.cc in Sources */ = {isa = PBXBuildFile; fileRef = 897FF1730E719B8F00D62E90 /* scanner-base.cc */; }; 89A88E1B0E71A69D0043BA31 /* scanner.cc in Sources */ = {isa = PBXBuildFile; fileRef = 897FF1730E719B8F00D62E90 /* scanner.cc */; }; 89A88E1C0E71A69E0043BA31 /* scopeinfo.cc in Sources */ = {isa = PBXBuildFile; fileRef = 897FF1760E719B8F00D62E90 /* scopeinfo.cc */; }; 89A88E1D0E71A6A00043BA31 /* scopes.cc in Sources */ = {isa = PBXBuildFile; fileRef = 897FF1780E719B8F00D62E90 /* scopes.cc */; }; @@ -178,7 +177,6 @@ 89F23C6C0E78D5B2006B2466 /* property.cc in Sources */ = {isa = PBXBuildFile; fileRef = 897FF16D0E719B8F00D62E90 /* property.cc */; }; 89F23C6D0E78D5B2006B2466 /* rewriter.cc in Sources */ = {isa = PBXBuildFile; fileRef = 897FF16F0E719B8F00D62E90 /* rewriter.cc */; }; 89F23C6E0E78D5B2006B2466 /* runtime.cc in Sources */ = {isa = PBXBuildFile; fileRef = 897FF1710E719B8F00D62E90 /* runtime.cc */; }; - 89F23C6F0E78D5B2006B2466 /* scanner-base.cc in Sources */ = {isa = PBXBuildFile; fileRef = 897FF1730E719B8F00D62E90 /* scanner-base.cc */; }; 89F23C6F0E78D5B2006B2466 /* scanner.cc in Sources */ = {isa = PBXBuildFile; fileRef = 897FF1730E719B8F00D62E90 /* scanner.cc */; }; 89F23C700E78D5B2006B2466 /* scopeinfo.cc in Sources */ = {isa = PBXBuildFile; fileRef = 897FF1760E719B8F00D62E90 /* scopeinfo.cc */; }; 89F23C710E78D5B2006B2466 /* scopes.cc in Sources */ = {isa = PBXBuildFile; fileRef = 897FF1780E719B8F00D62E90 /* scopes.cc */; }; @@ -483,8 +481,6 @@ 897FF1700E719B8F00D62E90 /* rewriter.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = rewriter.h; sourceTree = "<group>"; }; 897FF1710E719B8F00D62E90 /* runtime.cc */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = runtime.cc; sourceTree = "<group>"; }; 897FF1720E719B8F00D62E90 /* runtime.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = runtime.h; sourceTree = "<group>"; }; - 897FF1730E719B8F00D62E90 /* scanner-base.cc */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = scanner-base.cc; sourceTree = "<group>"; }; - 897FF1740E719B8F00D62E90 /* scanner-base.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = scanner-base.h; sourceTree = "<group>"; }; 897FF1730E719B8F00D62E90 /* scanner.cc */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = scanner.cc; sourceTree = "<group>"; }; 897FF1740E719B8F00D62E90 /* scanner.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = scanner.h; sourceTree = "<group>"; }; 897FF1750E719B8F00D62E90 /* SConscript */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = SConscript; sourceTree = "<group>"; }; @@ -947,8 +943,6 @@ 897FF1700E719B8F00D62E90 /* rewriter.h */, 897FF1710E719B8F00D62E90 /* runtime.cc */, 897FF1720E719B8F00D62E90 /* runtime.h */, - 897FF1730E719B8F00D62E90 /* scanner-base.cc */, - 897FF1740E719B8F00D62E90 /* scanner-base.h */, 897FF1730E719B8F00D62E90 /* scanner.cc */, 897FF1740E719B8F00D62E90 /* scanner.h */, 897FF1760E719B8F00D62E90 /* scopeinfo.cc */, @@ -1354,7 +1348,6 @@ 58950D630F5551AF00F3E8BA /* register-allocator.cc in Sources */, 89A88E190E71A6970043BA31 /* rewriter.cc in Sources */, 89A88E1A0E71A69B0043BA31 /* runtime.cc in Sources */, - 89A88E1B0E71A69D0043BA31 /* scanner-base.cc in Sources */, 89A88E1B0E71A69D0043BA31 /* scanner.cc in Sources */, 89A88E1C0E71A69E0043BA31 /* scopeinfo.cc in Sources */, 89A88E1D0E71A6A00043BA31 /* scopes.cc in Sources */, @@ -1479,7 +1472,6 @@ 58950D640F5551B500F3E8BA /* register-allocator.cc in Sources */, 89F23C6D0E78D5B2006B2466 /* rewriter.cc in Sources */, 89F23C6E0E78D5B2006B2466 /* runtime.cc in Sources */, - 89F23C6F0E78D5B2006B2466 /* scanner-base.cc in Sources */, 89F23C6F0E78D5B2006B2466 /* scanner.cc in Sources */, 89F23C700E78D5B2006B2466 /* scopeinfo.cc in Sources */, 89F23C710E78D5B2006B2466 /* scopes.cc in Sources */, diff --git a/tools/visual_studio/README.txt b/tools/visual_studio/README.txt index dd9802b4..c46aa371 100644 --- a/tools/visual_studio/README.txt +++ b/tools/visual_studio/README.txt @@ -7,8 +7,7 @@ be performed by Visual Studio. v8_base.vcproj -------------- -Base V8 library containing all the V8 code but no JavaScript library code. This -includes third party code for string/number convertions (dtoa). +Base V8 library containing all the V8 code but no JavaScript library code. v8.vcproj --------- diff --git a/tools/visual_studio/v8_base.vcproj b/tools/visual_studio/v8_base.vcproj index d1ee48d6..902faff6 100644 --- a/tools/visual_studio/v8_base.vcproj +++ b/tools/visual_studio/v8_base.vcproj @@ -122,70 +122,6 @@ </References> <Files> <Filter - Name="dtoa" - > - <File - RelativePath="..\..\src\dtoa-config.c" - > - <FileConfiguration - Name="Debug|Win32" - > - <Tool - Name="VCCLCompilerTool" - DisableSpecificWarnings="4018;4244" - /> - </FileConfiguration> - <FileConfiguration - Name="Release|Win32" - > - <Tool - Name="VCCLCompilerTool" - DisableSpecificWarnings="4018;4244" - /> - </FileConfiguration> - </File> - <File - RelativePath="..\..\src\bignum.cc" - > - </File> - <File - RelativePath="..\..\src\bignum.h" - > - </File> - <File - RelativePath="..\..\src\bignum-dtoa.cc" - > - </File> - <File - RelativePath="..\..\src\bignum-dtoa.h" - > - </File> - <File - RelativePath="..\..\src\dtoa.cc" - > - </File> - <File - RelativePath="..\..\src\dtoa.h" - > - </File> - <File - RelativePath="..\..\src\fast-dtoa.cc" - > - </File> - <File - RelativePath="..\..\src\fast-dtoa.h" - > - </File> - <File - RelativePath="..\..\src\fixed-dtoa.cc" - > - </File> - <File - RelativePath="..\..\src\fixed-dtoa.h" - > - </File> - </Filter> - <Filter Name="src" > <File @@ -297,6 +233,10 @@ > </File> <File + RelativePath="..\..\src\cached-powers.cc" + > + </File> + <File RelativePath="..\..\src\cached-powers.h" > </File> @@ -489,6 +429,14 @@ > </File> <File + RelativePath="..\..\src\dtoa.cc" + > + </File> + <File + RelativePath="..\..\src\dtoa.h" + > + </File> + <File RelativePath="..\..\src\execution.cc" > </File> @@ -513,6 +461,14 @@ > </File> <File + RelativePath="..\..\src\fixed-dtoa.cc" + > + </File> + <File + RelativePath="..\..\src\fixed-dtoa.h" + > + </File> + <File RelativePath="..\..\src\flags.cc" > </File> @@ -806,6 +762,22 @@ > </File> <File + RelativePath="..\..\src\preparser.cc" + > + </File> + <File + RelativePath="..\..\src\preparser.h" + > + </File> + <File + RelativePath="..\..\src\preparse-data.cc" + > + </File> + <File + RelativePath="..\..\src\preparse-data.h" + > + </File> + <File RelativePath="..\..\src\profile-generator.cc" > </File> @@ -994,6 +966,14 @@ > </File> <File + RelativePath="..\..\src\strtod.cc" + > + </File> + <File + RelativePath="..\..\src\strtod.h" + > + </File> + <File RelativePath="..\..\src\ia32\stub-cache-ia32.cc" > </File> @@ -1070,6 +1050,14 @@ > </File> <File + RelativePath="..\..\src\v8checks.h" + > + </File> + <File + RelativePath="..\..\src\v8globals.h" + > + </File> + <File RelativePath="..\..\src\v8threads.cc" > </File> @@ -1078,6 +1066,10 @@ > </File> <File + RelativePath="..\..\src\v8utils.h" + > + </File> + <File RelativePath="..\..\src\variables.cc" > </File> @@ -1141,6 +1133,22 @@ RelativePath="..\..\src\zone.h" > </File> + <File + RelativePath="..\..\src\extensions\externalize-string-extension.cc" + > + </File> + <File + RelativePath="..\..\src\extensions\externalize-string-extension.h" + > + </File> + <File + RelativePath="..\..\src\extensions\gc-extension.cc" + > + </File> + <File + RelativePath="..\..\src\extensions\gc-extension.h" + > + </File> <Filter Name="third party" > @@ -1177,6 +1185,10 @@ RelativePath="..\..\include\v8.h" > </File> + <File + RelativePath="..\..\include\v8stdint.h" + > + </File> </Filter> </Files> <Globals> diff --git a/tools/visual_studio/v8_base_arm.vcproj b/tools/visual_studio/v8_base_arm.vcproj index 4f9ff4ca..b87fdf8d 100644 --- a/tools/visual_studio/v8_base_arm.vcproj +++ b/tools/visual_studio/v8_base_arm.vcproj @@ -122,30 +122,6 @@ </References> <Files> <Filter - Name="dtoa" - > - <File - RelativePath="..\..\src\dtoa-config.c" - > - <FileConfiguration - Name="Debug|Win32" - > - <Tool - Name="VCCLCompilerTool" - DisableSpecificWarnings="4018;4244" - /> - </FileConfiguration> - <FileConfiguration - Name="Release|Win32" - > - <Tool - Name="VCCLCompilerTool" - DisableSpecificWarnings="4018;4244" - /> - </FileConfiguration> - </File> - </Filter> - <Filter Name="src" > <File @@ -229,6 +205,22 @@ > </File> <File + RelativePath="..\..\src\bignum.cc" + > + </File> + <File + RelativePath="..\..\src\bignum.h" + > + </File> + <File + RelativePath="..\..\src\bignum-dtoa.cc" + > + </File> + <File + RelativePath="..\..\src\bignum-dtoa.h" + > + </File> + <File RelativePath="..\..\src\builtins.cc" > </File> @@ -241,6 +233,14 @@ > </File> <File + RelativePath="..\..\src\cached-powers.cc" + > + </File> + <File + RelativePath="..\..\src\cached-powers.h" + > + </File> + <File RelativePath="..\..\src\char-predicates-inl.h" > </File> @@ -425,6 +425,26 @@ > </File> <File + RelativePath="..\..\src\diy-fp.cc" + > + </File> + <File + RelativePath="..\..\src\diy-fp.h" + > + </File> + <File + RelativePath="..\..\src\double.h" + > + </File> + <File + RelativePath="..\..\src\dtoa.cc" + > + </File> + <File + RelativePath="..\..\src\dtoa.h" + > + </File> + <File RelativePath="..\..\src\execution.cc" > </File> @@ -441,6 +461,22 @@ > </File> <File + RelativePath="..\..\src\fast-dtoa.cc" + > + </File> + <File + RelativePath="..\..\src\fast-dtoa.h" + > + </File> + <File + RelativePath="..\..\src\fixed-dtoa.cc" + > + </File> + <File + RelativePath="..\..\src\fixed-dtoa.h" + > + </File> + <File RelativePath="..\..\src\flags.cc" > </File> @@ -928,6 +964,14 @@ > </File> <File + RelativePath="..\..\src\strtod.cc" + > + </File> + <File + RelativePath="..\..\src\strtod.h" + > + </File> + <File RelativePath="..\..\src\arm\stub-cache-arm.cc" > </File> diff --git a/tools/visual_studio/v8_base_x64.vcproj b/tools/visual_studio/v8_base_x64.vcproj index c84bce2d..6d27472a 100644 --- a/tools/visual_studio/v8_base_x64.vcproj +++ b/tools/visual_studio/v8_base_x64.vcproj @@ -122,30 +122,6 @@ </References> <Files> <Filter - Name="dtoa" - > - <File - RelativePath="..\..\src\dtoa-config.c" - > - <FileConfiguration - Name="Debug|x64" - > - <Tool - Name="VCCLCompilerTool" - DisableSpecificWarnings="4018;4244" - /> - </FileConfiguration> - <FileConfiguration - Name="Release|x64" - > - <Tool - Name="VCCLCompilerTool" - DisableSpecificWarnings="4018;4244" - /> - </FileConfiguration> - </File> - </Filter> - <Filter Name="src" > <File @@ -217,6 +193,22 @@ > </File> <File + RelativePath="..\..\src\bignum.cc" + > + </File> + <File + RelativePath="..\..\src\bignum.h" + > + </File> + <File + RelativePath="..\..\src\bignum-dtoa.cc" + > + </File> + <File + RelativePath="..\..\src\bignum-dtoa.h" + > + </File> + <File RelativePath="..\..\src\bootstrapper.cc" > </File> @@ -241,6 +233,14 @@ > </File> <File + RelativePath="..\..\src\cached-powers.cc" + > + </File> + <File + RelativePath="..\..\src\cached-powers.h" + > + </File> + <File RelativePath="..\..\src\char-predicates-inl.h" > </File> @@ -417,6 +417,26 @@ > </File> <File + RelativePath="..\..\src\diy-fp.cc" + > + </File> + <File + RelativePath="..\..\src\diy-fp.h" + > + </File> + <File + RelativePath="..\..\src\double.h" + > + </File> + <File + RelativePath="..\..\src\dtoa.cc" + > + </File> + <File + RelativePath="..\..\src\dtoa.h" + > + </File> + <File RelativePath="..\..\src\execution.cc" > </File> @@ -433,6 +453,22 @@ > </File> <File + RelativePath="..\..\src\fast-dtoa.cc" + > + </File> + <File + RelativePath="..\..\src\fast-dtoa.h" + > + </File> + <File + RelativePath="..\..\src\fixed-dtoa.cc" + > + </File> + <File + RelativePath="..\..\src\fixed-dtoa.h" + > + </File> + <File RelativePath="..\..\src\flags.cc" > </File> @@ -914,6 +950,14 @@ > </File> <File + RelativePath="..\..\src\strtod.cc" + > + </File> + <File + RelativePath="..\..\src\strtod.h" + > + </File> + <File RelativePath="..\..\src\x64\stub-cache-x64.cc" > </File> |