aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--.gitignore26
-rw-r--r--AUTHORS1
-rw-r--r--Android.v8common.mk8
-rw-r--r--ChangeLog58
-rw-r--r--LICENSE3
-rw-r--r--V8_MERGE_REVISION4
-rw-r--r--include/v8-profiler.h29
-rw-r--r--include/v8.h31
-rw-r--r--preparser/preparser-process.cc227
-rw-r--r--samples/process.cc18
-rwxr-xr-xsrc/SConscript13
-rw-r--r--src/accessors.cc6
-rw-r--r--src/allocation.cc18
-rw-r--r--src/allocation.h4
-rw-r--r--src/api.cc32
-rw-r--r--src/apiutils.h3
-rw-r--r--src/arm/assembler-arm-inl.h6
-rw-r--r--src/arm/assembler-arm.cc25
-rw-r--r--src/arm/assembler-arm.h25
-rw-r--r--src/arm/builtins-arm.cc125
-rw-r--r--src/arm/code-stubs-arm.cc5
-rw-r--r--src/arm/codegen-arm.cc360
-rw-r--r--src/arm/codegen-arm.h24
-rw-r--r--src/arm/debug-arm.cc2
-rw-r--r--src/arm/disasm-arm.cc23
-rw-r--r--src/arm/full-codegen-arm.cc70
-rw-r--r--src/arm/ic-arm.cc79
-rw-r--r--src/arm/macro-assembler-arm.cc27
-rw-r--r--src/arm/macro-assembler-arm.h52
-rw-r--r--src/arm/stub-cache-arm.cc381
-rw-r--r--src/arm/virtual-frame-arm.cc7
-rw-r--r--src/array.js4
-rw-r--r--src/ast.h7
-rw-r--r--src/bignum-dtoa.cc655
-rw-r--r--src/bignum-dtoa.h81
-rw-r--r--src/bootstrapper.cc4
-rw-r--r--src/builtins.cc21
-rw-r--r--src/checks.cc1
-rw-r--r--src/checks.h36
-rw-r--r--src/code-stubs.cc34
-rw-r--r--src/code-stubs.h58
-rw-r--r--src/codegen.cc64
-rw-r--r--src/codegen.h2
-rw-r--r--src/conversions.cc107
-rw-r--r--src/conversions.h5
-rw-r--r--src/dateparser-inl.h2
-rw-r--r--src/dateparser.h29
-rw-r--r--src/dtoa-config.c92
-rw-r--r--src/dtoa.cc41
-rw-r--r--src/dtoa.h14
-rw-r--r--src/execution.cc131
-rw-r--r--src/execution.h26
-rw-r--r--src/extensions/externalize-string-extension.cc141
-rw-r--r--src/extensions/externalize-string-extension.h50
-rw-r--r--src/extensions/gc-extension.cc54
-rw-r--r--src/extensions/gc-extension.h49
-rw-r--r--src/full-codegen.cc11
-rw-r--r--src/full-codegen.h5
-rw-r--r--src/globals.h433
-rw-r--r--src/handles.cc72
-rw-r--r--src/hashmap.cc8
-rw-r--r--src/heap-profiler.cc34
-rw-r--r--src/heap.cc128
-rw-r--r--src/heap.h29
-rw-r--r--src/ia32/code-stubs-ia32.cc32
-rw-r--r--src/ia32/codegen-ia32.cc233
-rw-r--r--src/ia32/codegen-ia32.h16
-rw-r--r--src/ia32/full-codegen-ia32.cc240
-rw-r--r--src/ia32/macro-assembler-ia32.cc149
-rw-r--r--src/ia32/macro-assembler-ia32.h62
-rw-r--r--src/ia32/stub-cache-ia32.cc113
-rw-r--r--src/ia32/virtual-frame-ia32.cc5
-rw-r--r--src/list.h8
-rw-r--r--src/log.cc11
-rw-r--r--src/mark-compact.cc16
-rw-r--r--src/mark-compact.h3
-rw-r--r--src/objects-debug.cc5
-rw-r--r--src/objects-inl.h2
-rw-r--r--src/objects.cc11
-rw-r--r--src/objects.h8
-rw-r--r--src/parser.cc258
-rw-r--r--src/parser.h155
-rw-r--r--src/platform-freebsd.cc3
-rw-r--r--src/platform-linux.cc3
-rw-r--r--src/platform-macos.cc3
-rw-r--r--src/platform-openbsd.cc6
-rw-r--r--src/platform-solaris.cc3
-rw-r--r--src/platform-win32.cc8
-rw-r--r--src/platform.h17
-rw-r--r--src/preparse-data.cc180
-rw-r--r--src/preparse-data.h223
-rw-r--r--src/preparser.cc1184
-rw-r--r--src/preparser.h1183
-rw-r--r--src/profile-generator-inl.h43
-rw-r--r--src/profile-generator.cc761
-rw-r--r--src/profile-generator.h189
-rw-r--r--src/regexp.js71
-rw-r--r--src/runtime.cc23
-rw-r--r--src/runtime.h7
-rw-r--r--src/scanner-base.cc762
-rw-r--r--src/scanner-base.h424
-rwxr-xr-xsrc/scanner.cc975
-rw-r--r--src/scanner.h350
-rw-r--r--src/serialize.cc4
-rw-r--r--src/spaces.cc32
-rw-r--r--src/spaces.h22
-rw-r--r--src/string.js2
-rw-r--r--src/strtod.cc6
-rw-r--r--src/stub-cache.cc35
-rw-r--r--src/stub-cache.h6
-rw-r--r--src/third_party/dtoa/COPYING15
-rw-r--r--src/third_party/dtoa/dtoa.c3331
-rw-r--r--src/token.cc3
-rw-r--r--src/utils.h4
-rw-r--r--src/v8.cc38
-rw-r--r--src/v8.h9
-rw-r--r--src/v8checks.h64
-rw-r--r--src/v8globals.h464
-rw-r--r--src/version.cc4
-rw-r--r--src/virtual-frame.h13
-rw-r--r--src/x64/code-stubs-x64.cc36
-rw-r--r--src/x64/codegen-x64.cc50
-rw-r--r--src/x64/codegen-x64.h16
-rw-r--r--src/x64/full-codegen-x64.cc60
-rw-r--r--src/x64/macro-assembler-x64.cc135
-rw-r--r--src/x64/macro-assembler-x64.h78
-rw-r--r--src/x64/stub-cache-x64.cc266
-rw-r--r--src/x64/virtual-frame-x64.cc5
-rw-r--r--test/cctest/SConscript2
-rw-r--r--test/cctest/test-api.cc38
-rw-r--r--test/cctest/test-bignum-dtoa.cc315
-rw-r--r--test/cctest/test-conversions.cc19
-rw-r--r--test/cctest/test-debug.cc67
-rw-r--r--test/cctest/test-disasm-arm.cc17
-rw-r--r--test/cctest/test-dtoa.cc331
-rw-r--r--test/cctest/test-heap-profiler.cc201
-rw-r--r--test/cctest/test-heap.cc29
-rw-r--r--test/cctest/test-log.cc42
-rwxr-xr-xtest/cctest/test-parsing.cc65
-rw-r--r--test/cctest/test-strtod.cc17
-rw-r--r--test/mjsunit/compiler/literals.js38
-rw-r--r--test/mjsunit/regress/regress-918.js33
-rw-r--r--test/mjsunit/regress/regress-931.js48
-rw-r--r--test/mjsunit/regress/regress-944.js46
-rw-r--r--test/mjsunit/string-split.js19
-rw-r--r--tools/gyp/v8.gyp20
-rwxr-xr-xtools/presubmit.py2
-rw-r--r--tools/v8.xcodeproj/project.pbxproj8
-rw-r--r--tools/visual_studio/README.txt3
-rw-r--r--tools/visual_studio/v8_base.vcproj140
-rw-r--r--tools/visual_studio/v8_base_arm.vcproj92
-rw-r--r--tools/visual_studio/v8_base_x64.vcproj92
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
diff --git a/AUTHORS b/AUTHORS
index 68f9b63b..3749cebc 100644
--- a/AUTHORS
+++ b/AUTHORS
@@ -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.
diff --git a/ChangeLog b/ChangeLog
index 573ebb34..86e41e17 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -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.
diff --git a/LICENSE b/LICENSE
index e3ed242d..c1fcb1a1 100644
--- a/LICENSE
+++ b/LICENSE
@@ -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!
diff --git a/src/api.cc b/src/api.cc
index 9da3346d..19af866c 100644
--- a/src/api.cc
+++ b/src/api.cc
@@ -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?
+ &not_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(&not_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);
}
diff --git a/src/ast.h b/src/ast.h
index 04c29775..0846dbc5 100644
--- a/src/ast.h
+++ b/src/ast.h
@@ -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(&current, end)) return JUNK_STRING_VALUE;
+ if (current == end) return JUNK_STRING_VALUE;
} else if (*current == '-') {
++current;
- if (!AdvanceToNonspace(&current, 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
diff --git a/src/dtoa.h b/src/dtoa.h
index be0d5456..b3e79afa 100644
--- a/src/dtoa.h
+++ b/src/dtoa.h
@@ -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;
diff --git a/src/heap.h b/src/heap.h
index c37ced39..93caf3bd 100644
--- a/src/heap.h
+++ b/src/heap.h
@@ -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, &copy_loop_1_done);
+ __ bind(&copy_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, &copy_loop_1);
+ __ bind(&copy_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, &copy_loop_1_done);
+ __ bind(&copy_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, &copy_loop_1);
+ __ bind(&copy_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(&copy_loop_entry);
+ bind(&copy_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(&copy_loop_entry);
+ sub(Operand(current_string_length), Immediate(1));
+ j(greater_equal, &copy_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();
diff --git a/src/list.h b/src/list.h
index 5a08212b..24f34945 100644
--- a/src/list.h
+++ b/src/list.h
@@ -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_
diff --git a/src/log.cc b/src/log.cc
index d12aafb6..55f15deb 100644
--- a/src/log.cc
+++ b/src/log.cc
@@ -194,11 +194,6 @@ class Ticker: public Sampler {
~Ticker() { if (IsActive()) Stop(); }
- virtual void SampleStack(TickSample* sample) {
- ASSERT(IsSynchronous());
- StackTracer::Trace(sample);
- }
-
virtual void Tick(TickSample* sample) {
if (profiler_) profiler_->Insert(sample);
if (window_) window_->AddState(sample->state);
@@ -224,6 +219,12 @@ class Ticker: public Sampler {
if (!window_ && IsActive()) Stop();
}
+ protected:
+ virtual void DoSampleStack(TickSample* sample) {
+ ASSERT(IsSynchronous());
+ StackTracer::Trace(sample);
+ }
+
private:
SlidingStateWindow* window_;
Profiler* profiler_;
diff --git a/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]; }
diff --git a/src/v8.cc b/src/v8.cc
index 0623400a..c8d719b1 100644
--- a/src/v8.cc
+++ b/src/v8.cc
@@ -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);
}
diff --git a/src/v8.h b/src/v8.h
index 1cb8d2f1..a2313b0e 100644
--- a/src/v8.h
+++ b/src/v8.h
@@ -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>