aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorTeng-Hui Zhu <ztenghui@google.com>2010-11-09 16:16:48 -0800
committerTeng-Hui Zhu <ztenghui@google.com>2010-11-15 17:07:50 -0800
commit3e5fa29ddb82551500b118e9bf37af3966277b70 (patch)
treea74a16cc186a742dd182289692dfbe9ce1c3c5d4
parent5913587db4c6bab03d97bfe44b06289fd6d7270d (diff)
downloadv8-3e5fa29ddb82551500b118e9bf37af3966277b70.tar.gz
Update V8 to r5780 as required by WebKit r71558
Change-Id: Ie3936550b99967a13755930d0dac0a59c3562625
-rw-r--r--Android.v8common.mk1
-rw-r--r--ChangeLog23
-rw-r--r--V8_MERGE_REVISION4
-rwxr-xr-xinclude/v8-debug.h4
-rw-r--r--include/v8.h100
-rw-r--r--include/v8stdint.h53
-rwxr-xr-xsrc/SConscript1
-rw-r--r--src/api.cc5
-rw-r--r--src/apiutils.h22
-rw-r--r--src/arguments.h9
-rw-r--r--src/arm/assembler-arm.cc105
-rw-r--r--src/arm/assembler-arm.h35
-rw-r--r--src/arm/codegen-arm.cc93
-rw-r--r--src/arm/codegen-arm.h2
-rw-r--r--src/arm/constants-arm.h10
-rw-r--r--src/arm/cpu-arm.cc10
-rw-r--r--src/arm/disasm-arm.cc113
-rw-r--r--src/arm/full-codegen-arm.cc136
-rw-r--r--src/arm/ic-arm.cc8
-rw-r--r--src/arm/macro-assembler-arm.cc26
-rw-r--r--src/arm/simulator-arm.cc299
-rw-r--r--src/arm/simulator-arm.h27
-rw-r--r--src/arm/stub-cache-arm.cc59
-rw-r--r--src/assembler.cc49
-rw-r--r--src/assembler.h61
-rw-r--r--src/ast.cc74
-rw-r--r--src/ast.h10
-rw-r--r--src/builtins.cc72
-rw-r--r--src/checks.cc9
-rw-r--r--src/checks.h19
-rw-r--r--src/codegen.cc13
-rwxr-xr-xsrc/compiler.cc34
-rw-r--r--src/compiler.h15
-rw-r--r--src/conversions.cc6
-rw-r--r--src/debug-debugger.js34
-rw-r--r--src/debug.cc7
-rw-r--r--src/execution.cc2
-rw-r--r--src/flag-definitions.h3
-rw-r--r--src/full-codegen.cc12
-rw-r--r--src/full-codegen.h4
-rw-r--r--src/global-handles.cc6
-rw-r--r--src/global-handles.h3
-rw-r--r--src/globals.h3
-rw-r--r--src/heap-inl.h7
-rw-r--r--src/heap.cc64
-rw-r--r--src/heap.h17
-rw-r--r--src/ia32/assembler-ia32.cc54
-rw-r--r--src/ia32/assembler-ia32.h16
-rw-r--r--src/ia32/codegen-ia32.cc138
-rw-r--r--src/ia32/codegen-ia32.h10
-rw-r--r--src/ia32/full-codegen-ia32.cc123
-rw-r--r--src/ia32/ic-ia32.cc1
-rw-r--r--src/ia32/stub-cache-ia32.cc12
-rw-r--r--src/json.js3
-rw-r--r--src/jsregexp.cc6
-rw-r--r--src/jump-target-heavy.cc5
-rw-r--r--src/jump-target-light.cc5
-rw-r--r--src/liveedit.cc2
-rw-r--r--src/log.cc5
-rw-r--r--src/mips/stub-cache-mips.cc3
-rw-r--r--src/objects-inl.h45
-rw-r--r--src/objects.h3
-rw-r--r--src/parser.cc1704
-rw-r--r--src/parser.h518
-rw-r--r--src/platform-linux.cc73
-rw-r--r--src/preparser.h1414
-rw-r--r--src/regexp.js115
-rw-r--r--src/runtime.cc98
-rw-r--r--src/runtime.h10
-rw-r--r--src/scanner-base.cc167
-rw-r--r--src/scanner-base.h165
-rwxr-xr-xsrc/scanner.cc136
-rw-r--r--src/scanner.h125
-rw-r--r--src/string.js82
-rw-r--r--src/stub-cache.h6
-rw-r--r--src/token.h2
-rw-r--r--src/top.h4
-rw-r--r--src/utils.cc36
-rw-r--r--src/utils.h315
-rw-r--r--src/v8.h2
-rw-r--r--src/v8natives.js6
-rw-r--r--src/v8utils.h301
-rw-r--r--src/version.cc2
-rw-r--r--src/x64/assembler-x64.cc54
-rw-r--r--src/x64/assembler-x64.h15
-rw-r--r--src/x64/codegen-x64.cc113
-rw-r--r--src/x64/codegen-x64.h2
-rw-r--r--src/x64/full-codegen-x64.cc174
-rw-r--r--src/x64/ic-x64.cc1
-rw-r--r--src/x64/stub-cache-x64.cc10
-rw-r--r--test/cctest/test-debug.cc45
-rw-r--r--test/cctest/test-lock.cc1
-rwxr-xr-xtest/cctest/test-parsing.cc33
-rw-r--r--test/cctest/test-regexp.cc13
-rw-r--r--test/cctest/test-serialize.cc6
-rw-r--r--test/mjsunit/debug-compile-event.js18
-rw-r--r--test/mjsunit/mirror-script.js4
-rw-r--r--test/mjsunit/mjsunit.status9
-rw-r--r--test/mjsunit/object-literal-conversions.js46
-rw-r--r--test/mjsunit/object-literal-overwrite.js118
-rw-r--r--test/mjsunit/regress/regress-conditional-position.js95
-rw-r--r--test/mjsunit/string-externalize.js6
-rw-r--r--test/mjsunit/string-replace-with-empty.js64
-rw-r--r--test/mjsunit/string-split.js91
-rw-r--r--tools/gyp/v8.gyp2
-rwxr-xr-xtools/ll_prof.py4
-rwxr-xr-xtools/presubmit.py2
-rw-r--r--tools/v8.xcodeproj/project.pbxproj8
-rw-r--r--tools/visual_studio/v8_base.vcproj8
109 files changed, 4959 insertions, 3364 deletions
diff --git a/Android.v8common.mk b/Android.v8common.mk
index 59ea39de..67b0c3cf 100644
--- a/Android.v8common.mk
+++ b/Android.v8common.mk
@@ -61,6 +61,7 @@ V8_LOCAL_SRC_FILES := \
src/rewriter.cc \
src/runtime.cc \
src/scanner.cc \
+ src/scanner-base.cc \
src/scopeinfo.cc \
src/scopes.cc \
src/serialize.cc \
diff --git a/ChangeLog b/ChangeLog
index 07859597..ea07009f 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,26 @@
+2010-11-08: Version 2.5.5
+
+ Added more aggressive GC of external objects in near out-of-memory
+ situations.
+
+ Fixed a bug that gave the incorrect result for String.split called
+ on the empty string (issue 924).
+
+
+2010-11-03: Version 2.5.4
+
+ Improved V8 VFPv3 runtime detection to address issue 914.
+
+
+2010-11-01: Version 2.5.3
+
+ Fixed a bug that prevents constants from overwriting function values
+ in object literals (issue 907).
+
+ Fixed a bug with reporting of impossible nested calls of DOM functions
+ (issue http://crbug.com/60753).
+
+
2010-10-27: Version 2.5.2
Improved sampler resolution on Linux.
diff --git a/V8_MERGE_REVISION b/V8_MERGE_REVISION
index bf2944f6..f12be983 100644
--- a/V8_MERGE_REVISION
+++ b/V8_MERGE_REVISION
@@ -1,4 +1,4 @@
We use a V8 revision that has been used for a Chromium release.
-http://src.chromium.org/svn/releases/9.0.569.0/DEPS
-http://v8.googlecode.com/svn/trunk@5716
+http://src.chromium.org/svn/releases/9.0.577.0/DEPS
+http://v8.googlecode.com/svn/trunk@5780
diff --git a/include/v8-debug.h b/include/v8-debug.h
index 4314727a..f17b8485 100755
--- a/include/v8-debug.h
+++ b/include/v8-debug.h
@@ -142,7 +142,7 @@ class EXPORT Debug {
virtual ~Message() {}
};
-
+
/**
* An event details object passed to the debug event listener.
@@ -300,7 +300,7 @@ class EXPORT Debug {
* get access to information otherwise not available during normal JavaScript
* execution e.g. details on stack frames. Receiver of the function call will
* be the debugger context global object, however this is a subject to change.
- * The following example show a JavaScript function which when passed to
+ * The following example show a JavaScript function which when passed to
* v8::Debug::Call will return the current line of JavaScript execution.
*
* \code
diff --git a/include/v8.h b/include/v8.h
index 89502cb9..8c730df8 100644
--- a/include/v8.h
+++ b/include/v8.h
@@ -38,23 +38,9 @@
#ifndef V8_H_
#define V8_H_
-#include <stdio.h>
+#include "v8stdint.h"
#ifdef _WIN32
-// When compiling on MinGW stdint.h is available.
-#ifdef __MINGW32__
-#include <stdint.h>
-#else // __MINGW32__
-typedef signed char int8_t;
-typedef unsigned char uint8_t;
-typedef short int16_t; // NOLINT
-typedef unsigned short uint16_t; // NOLINT
-typedef int int32_t;
-typedef unsigned int uint32_t;
-typedef __int64 int64_t;
-typedef unsigned __int64 uint64_t;
-// intptr_t and friends are defined in crtdefs.h through stdio.h.
-#endif // __MINGW32__
// Setup for Windows DLL export/import. When building the V8 DLL the
// BUILDING_V8_SHARED needs to be defined. When building a program which uses
@@ -76,8 +62,6 @@ typedef unsigned __int64 uint64_t;
#else // _WIN32
-#include <stdint.h>
-
// Setup for Linux shared library export. There is no need to distinguish
// between building or using the V8 shared library, but we should not
// export symbols when we are building a static library.
@@ -127,7 +111,6 @@ class Arguments;
class Object;
class Heap;
class Top;
-
}
@@ -476,10 +459,10 @@ class V8EXPORT HandleScope {
level = 0;
}
};
-
+
void Leave();
-
+
internal::Object** prev_next_;
internal::Object** prev_limit_;
@@ -1055,7 +1038,7 @@ class String : public Primitive {
*/
V8EXPORT bool IsExternalAscii() const;
- class V8EXPORT ExternalStringResourceBase {
+ class V8EXPORT ExternalStringResourceBase { // NOLINT
public:
virtual ~ExternalStringResourceBase() {}
@@ -1790,18 +1773,19 @@ class Arguments {
inline bool IsConstructCall() const;
inline Local<Value> Data() const;
private:
+ static const int kDataIndex = 0;
+ static const int kCalleeIndex = -1;
+ static const int kHolderIndex = -2;
+
friend class ImplementationUtilities;
- inline Arguments(Local<Value> data,
- Local<Object> holder,
- Local<Function> callee,
- bool is_construct_call,
- void** values, int length);
- Local<Value> data_;
- Local<Object> holder_;
- Local<Function> callee_;
- bool is_construct_call_;
- void** values_;
+ inline Arguments(internal::Object** implicit_args,
+ internal::Object** values,
+ int length,
+ bool is_construct_call);
+ internal::Object** implicit_args_;
+ internal::Object** values_;
int length_;
+ bool is_construct_call_;
};
@@ -3259,8 +3243,8 @@ class V8EXPORT Locker {
/**
* An interface for exporting data from V8, using "push" model.
*/
-class V8EXPORT OutputStream {
-public:
+class V8EXPORT OutputStream { // NOLINT
+ public:
enum OutputEncoding {
kAscii = 0 // 7-bit ASCII.
};
@@ -3290,6 +3274,8 @@ public:
namespace internal {
+const int kPointerSize = sizeof(void*); // NOLINT
+const int kIntSize = sizeof(int); // NOLINT
// Tag information for HeapObject.
const int kHeapObjectTag = 1;
@@ -3325,19 +3311,19 @@ template <> struct SmiConstants<8> {
}
};
-const int kSmiShiftSize = SmiConstants<sizeof(void*)>::kSmiShiftSize;
-const int kSmiValueSize = SmiConstants<sizeof(void*)>::kSmiValueSize;
+const int kSmiShiftSize = SmiConstants<kPointerSize>::kSmiShiftSize;
+const int kSmiValueSize = SmiConstants<kPointerSize>::kSmiValueSize;
template <size_t ptr_size> struct InternalConstants;
// Internal constants for 32-bit systems.
template <> struct InternalConstants<4> {
- static const int kStringResourceOffset = 3 * sizeof(void*);
+ static const int kStringResourceOffset = 3 * kPointerSize;
};
// Internal constants for 64-bit systems.
template <> struct InternalConstants<8> {
- static const int kStringResourceOffset = 3 * sizeof(void*);
+ static const int kStringResourceOffset = 3 * kPointerSize;
};
/**
@@ -3351,12 +3337,12 @@ class Internals {
// These values match non-compiler-dependent values defined within
// the implementation of v8.
static const int kHeapObjectMapOffset = 0;
- static const int kMapInstanceTypeOffset = sizeof(void*) + sizeof(int);
+ static const int kMapInstanceTypeOffset = kPointerSize + kIntSize;
static const int kStringResourceOffset =
- InternalConstants<sizeof(void*)>::kStringResourceOffset;
+ InternalConstants<kPointerSize>::kStringResourceOffset;
- static const int kProxyProxyOffset = sizeof(void*);
- static const int kJSObjectHeaderSize = 3 * sizeof(void*);
+ static const int kProxyProxyOffset = kPointerSize;
+ static const int kJSObjectHeaderSize = 3 * kPointerSize;
static const int kFullStringRepresentationMask = 0x07;
static const int kExternalTwoByteRepresentationTag = 0x02;
@@ -3374,7 +3360,7 @@ class Internals {
}
static inline int SmiValue(internal::Object* value) {
- return SmiConstants<sizeof(void*)>::SmiToInt(value);
+ return SmiConstants<kPointerSize>::SmiToInt(value);
}
static inline int GetInstanceType(internal::Object* obj) {
@@ -3403,10 +3389,9 @@ class Internals {
uint8_t* addr = reinterpret_cast<uint8_t*>(ptr) + offset - kHeapObjectTag;
return *reinterpret_cast<T*>(addr);
}
-
};
-}
+} // namespace internal
template <class T>
@@ -3470,14 +3455,13 @@ void Persistent<T>::ClearWeak() {
}
-Arguments::Arguments(v8::Local<v8::Value> data,
- v8::Local<v8::Object> holder,
- v8::Local<v8::Function> callee,
- bool is_construct_call,
- void** values, int length)
- : data_(data), holder_(holder), callee_(callee),
- is_construct_call_(is_construct_call),
- values_(values), length_(length) { }
+Arguments::Arguments(internal::Object** implicit_args,
+ internal::Object** values, int length,
+ bool is_construct_call)
+ : implicit_args_(implicit_args),
+ values_(values),
+ length_(length),
+ is_construct_call_(is_construct_call) { }
Local<Value> Arguments::operator[](int i) const {
@@ -3487,7 +3471,8 @@ Local<Value> Arguments::operator[](int i) const {
Local<Function> Arguments::Callee() const {
- return callee_;
+ return Local<Function>(reinterpret_cast<Function*>(
+ &implicit_args_[kCalleeIndex]));
}
@@ -3497,12 +3482,13 @@ Local<Object> Arguments::This() const {
Local<Object> Arguments::Holder() const {
- return holder_;
+ return Local<Object>(reinterpret_cast<Object*>(
+ &implicit_args_[kHolderIndex]));
}
Local<Value> Arguments::Data() const {
- return data_;
+ return Local<Value>(reinterpret_cast<Value*>(&implicit_args_[kDataIndex]));
}
@@ -3565,7 +3551,7 @@ Local<Value> Object::UncheckedGetInternalField(int index) {
// If the object is a plain JSObject, which is the common case,
// we know where to find the internal fields and can return the
// value directly.
- int offset = I::kJSObjectHeaderSize + (sizeof(void*) * index);
+ int offset = I::kJSObjectHeaderSize + (internal::kPointerSize * index);
O* value = I::ReadField<O*>(obj, offset);
O** result = HandleScope::CreateHandle(value);
return Local<Value>(reinterpret_cast<Value*>(result));
@@ -3601,7 +3587,7 @@ void* Object::GetPointerFromInternalField(int index) {
// If the object is a plain JSObject, which is the common case,
// we know where to find the internal fields and can return the
// value directly.
- int offset = I::kJSObjectHeaderSize + (sizeof(void*) * index);
+ int offset = I::kJSObjectHeaderSize + (internal::kPointerSize * index);
O* value = I::ReadField<O*>(obj, offset);
return I::GetExternalPointer(value);
}
diff --git a/include/v8stdint.h b/include/v8stdint.h
new file mode 100644
index 00000000..50b4f29a
--- /dev/null
+++ b/include/v8stdint.h
@@ -0,0 +1,53 @@
+// Copyright 2010 the V8 project authors. All rights reserved.
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following
+// disclaimer in the documentation and/or other materials provided
+// with the distribution.
+// * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived
+// from this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+// Load definitions of standard types.
+
+#ifndef V8STDINT_H_
+#define V8STDINT_H_
+
+#include <stdio.h>
+
+#if defined(_WIN32) && !defined(__MINGW32__)
+
+typedef signed char int8_t;
+typedef unsigned char uint8_t;
+typedef short int16_t; // NOLINT
+typedef unsigned short uint16_t; // NOLINT
+typedef int int32_t;
+typedef unsigned int uint32_t;
+typedef __int64 int64_t;
+typedef unsigned __int64 uint64_t;
+// intptr_t and friends are defined in crtdefs.h through stdio.h.
+
+#else
+
+#include <stdint.h>
+
+#endif
+
+#endif // V8STDINT_H_
diff --git a/src/SConscript b/src/SConscript
index 8995d482..596caf7f 100755
--- a/src/SConscript
+++ b/src/SConscript
@@ -95,6 +95,7 @@ SOURCES = {
register-allocator.cc
rewriter.cc
runtime.cc
+ scanner-base.cc
scanner.cc
scopeinfo.cc
scopes.cc
diff --git a/src/api.cc b/src/api.cc
index 2df31df3..ee7ad3a5 100644
--- a/src/api.cc
+++ b/src/api.cc
@@ -43,7 +43,6 @@
#include "serialize.h"
#include "snapshot.h"
#include "top.h"
-#include "utils.h"
#include "v8threads.h"
#include "version.h"
@@ -1155,13 +1154,13 @@ void ObjectTemplate::SetInternalFieldCount(int value) {
ScriptData* ScriptData::PreCompile(const char* input, int length) {
unibrow::Utf8InputBuffer<> buf(input, length);
- return i::Parser::PreParse(i::Handle<i::String>(), &buf, NULL);
+ return i::ParserApi::PreParse(i::Handle<i::String>(), &buf, NULL);
}
ScriptData* ScriptData::PreCompile(v8::Handle<String> source) {
i::Handle<i::String> str = Utils::OpenHandle(*source);
- return i::Parser::PreParse(str, NULL, NULL);
+ return i::ParserApi::PreParse(str, NULL, NULL);
}
diff --git a/src/apiutils.h b/src/apiutils.h
index 8c791ebd..1313ddaa 100644
--- a/src/apiutils.h
+++ b/src/apiutils.h
@@ -29,7 +29,6 @@
#define V8_APIUTILS_H_
namespace v8 {
-
class ImplementationUtilities {
public:
static v8::Handle<v8::Primitive> Undefined();
@@ -45,12 +44,21 @@ class ImplementationUtilities {
return that->names_;
}
- static v8::Arguments NewArguments(Local<Value> data,
- Local<Object> holder,
- Local<Function> callee,
- bool is_construct_call,
- void** argv, int argc) {
- return v8::Arguments(data, holder, callee, is_construct_call, argv, argc);
+ // Packs additional parameters for the NewArguments function. |implicit_args|
+ // is a pointer to the last element of 3-elements array controlled by GC.
+ static void PrepareArgumentsData(internal::Object** implicit_args,
+ internal::Object* data,
+ internal::JSFunction* callee,
+ internal::Object* holder) {
+ implicit_args[v8::Arguments::kDataIndex] = data;
+ implicit_args[v8::Arguments::kCalleeIndex] = callee;
+ implicit_args[v8::Arguments::kHolderIndex] = holder;
+ }
+
+ static v8::Arguments NewArguments(internal::Object** implicit_args,
+ internal::Object** argv, int argc,
+ bool is_construct_call) {
+ return v8::Arguments(implicit_args, argv, argc, is_construct_call);
}
// Introduce an alias for the handle scope data to allow non-friends
diff --git a/src/arguments.h b/src/arguments.h
index c17f4cf8..d51c9e4c 100644
--- a/src/arguments.h
+++ b/src/arguments.h
@@ -84,6 +84,15 @@ class CustomArguments : public Relocatable {
values_[1] = holder;
values_[0] = data;
}
+
+ inline CustomArguments() {
+#ifdef DEBUG
+ for (size_t i = 0; i < ARRAY_SIZE(values_); i++) {
+ values_[i] = reinterpret_cast<Object*>(kZapValue);
+ }
+#endif
+ }
+
void IterateInstance(ObjectVisitor* v);
Object** end() { return values_ + ARRAY_SIZE(values_) - 1; }
private:
diff --git a/src/arm/assembler-arm.cc b/src/arm/assembler-arm.cc
index 7d368bf4..72835ba3 100644
--- a/src/arm/assembler-arm.cc
+++ b/src/arm/assembler-arm.cc
@@ -317,7 +317,8 @@ static const Instr kLdrStrOffsetMask = 0x00000fff;
static const int kMinimalBufferSize = 4*KB;
static byte* spare_buffer_ = NULL;
-Assembler::Assembler(void* buffer, int buffer_size) {
+Assembler::Assembler(void* buffer, int buffer_size)
+ : positions_recorder_(this) {
if (buffer == NULL) {
// Do our own buffer management.
if (buffer_size <= kMinimalBufferSize) {
@@ -354,10 +355,6 @@ Assembler::Assembler(void* buffer, int buffer_size) {
no_const_pool_before_ = 0;
last_const_pool_end_ = 0;
last_bound_pos_ = 0;
- current_statement_position_ = RelocInfo::kNoPosition;
- current_position_ = RelocInfo::kNoPosition;
- written_statement_position_ = current_statement_position_;
- written_position_ = current_position_;
}
@@ -752,15 +749,15 @@ static bool fits_shifter(uint32_t imm32,
// if they can be encoded in the ARM's 12 bits of immediate-offset instruction
// space. There is no guarantee that the relocated location can be similarly
// encoded.
-static bool MustUseConstantPool(RelocInfo::Mode rmode) {
- if (rmode == RelocInfo::EXTERNAL_REFERENCE) {
+bool Operand::must_use_constant_pool() const {
+ if (rmode_ == RelocInfo::EXTERNAL_REFERENCE) {
#ifdef DEBUG
if (!Serializer::enabled()) {
Serializer::TooLateToEnableNow();
}
#endif // def DEBUG
return Serializer::enabled();
- } else if (rmode == RelocInfo::NONE) {
+ } else if (rmode_ == RelocInfo::NONE) {
return false;
}
return true;
@@ -769,7 +766,7 @@ static bool MustUseConstantPool(RelocInfo::Mode rmode) {
bool Operand::is_single_instruction() const {
if (rm_.is_valid()) return true;
- if (MustUseConstantPool(rmode_)) return false;
+ if (must_use_constant_pool()) return false;
uint32_t dummy1, dummy2;
return fits_shifter(imm32_, &dummy1, &dummy2, NULL);
}
@@ -785,7 +782,7 @@ void Assembler::addrmod1(Instr instr,
// Immediate.
uint32_t rotate_imm;
uint32_t immed_8;
- if (MustUseConstantPool(x.rmode_) ||
+ if (x.must_use_constant_pool() ||
!fits_shifter(x.imm32_, &rotate_imm, &immed_8, &instr)) {
// The immediate operand cannot be encoded as a shifter operand, so load
// it first to register ip and change the original instruction to use ip.
@@ -794,8 +791,7 @@ void Assembler::addrmod1(Instr instr,
CHECK(!rn.is(ip)); // rn should never be ip, or will be trashed
Condition cond = static_cast<Condition>(instr & CondMask);
if ((instr & ~CondMask) == 13*B21) { // mov, S not set
- if (MustUseConstantPool(x.rmode_) ||
- !CpuFeatures::IsSupported(ARMv7)) {
+ if (x.must_use_constant_pool() || !CpuFeatures::IsSupported(ARMv7)) {
RecordRelocInfo(x.rmode_, x.imm32_);
ldr(rd, MemOperand(pc, 0), cond);
} else {
@@ -806,7 +802,7 @@ void Assembler::addrmod1(Instr instr,
} else {
// If this is not a mov or mvn instruction we may still be able to avoid
// a constant pool entry by using mvn or movw.
- if (!MustUseConstantPool(x.rmode_) &&
+ if (!x.must_use_constant_pool() &&
(instr & kMovMvnMask) != kMovMvnPattern) {
mov(ip, x, LeaveCC, cond);
} else {
@@ -999,24 +995,24 @@ void Assembler::bl(int branch_offset, Condition cond) {
void Assembler::blx(int branch_offset) { // v5 and above
- WriteRecordedPositions();
+ positions_recorder()->WriteRecordedPositions();
ASSERT((branch_offset & 1) == 0);
int h = ((branch_offset & 2) >> 1)*B24;
int imm24 = branch_offset >> 2;
ASSERT(is_int24(imm24));
- emit(15 << 28 | B27 | B25 | h | (imm24 & Imm24Mask));
+ emit(nv | B27 | B25 | h | (imm24 & Imm24Mask));
}
void Assembler::blx(Register target, Condition cond) { // v5 and above
- WriteRecordedPositions();
+ positions_recorder()->WriteRecordedPositions();
ASSERT(!target.is(pc));
emit(cond | B24 | B21 | 15*B16 | 15*B12 | 15*B8 | 3*B4 | target.code());
}
void Assembler::bx(Register target, Condition cond) { // v5 and above, plus v4t
- WriteRecordedPositions();
+ positions_recorder()->WriteRecordedPositions();
ASSERT(!target.is(pc)); // use of pc is actually allowed, but discouraged
emit(cond | B24 | B21 | 15*B16 | 15*B12 | 15*B8 | B4 | target.code());
}
@@ -1114,7 +1110,7 @@ void Assembler::orr(Register dst, Register src1, const Operand& src2,
void Assembler::mov(Register dst, const Operand& src, SBit s, Condition cond) {
if (dst.is(pc)) {
- WriteRecordedPositions();
+ positions_recorder()->WriteRecordedPositions();
}
// Don't allow nop instructions in the form mov rn, rn to be generated using
// the mov instruction. They must be generated using nop(int)
@@ -1339,7 +1335,7 @@ void Assembler::msr(SRegisterFieldMask fields, const Operand& src,
// Immediate.
uint32_t rotate_imm;
uint32_t immed_8;
- if (MustUseConstantPool(src.rmode_) ||
+ if (src.must_use_constant_pool() ||
!fits_shifter(src.imm32_, &rotate_imm, &immed_8, NULL)) {
// Immediate operand cannot be encoded, load it first to register ip.
RecordRelocInfo(src.rmode_, src.imm32_);
@@ -1359,7 +1355,7 @@ void Assembler::msr(SRegisterFieldMask fields, const Operand& src,
// Load/Store instructions.
void Assembler::ldr(Register dst, const MemOperand& src, Condition cond) {
if (dst.is(pc)) {
- WriteRecordedPositions();
+ positions_recorder()->WriteRecordedPositions();
}
addrmod2(cond | B26 | L, dst, src);
@@ -1634,15 +1630,29 @@ void Assembler::stm(BlockAddrMode am,
// Exception-generating instructions and debugging support.
-void Assembler::stop(const char* msg) {
+// Stops with a non-negative code less than kNumOfWatchedStops support
+// enabling/disabling and a counter feature. See simulator-arm.h .
+void Assembler::stop(const char* msg, Condition cond, int32_t code) {
#ifndef __arm__
- // The simulator handles these special instructions and stops execution.
- emit(15 << 28 | ((intptr_t) msg));
+ // See constants-arm.h SoftwareInterruptCodes. Unluckily the Assembler and
+ // Simulator do not share constants declaration.
+ ASSERT(code >= kDefaultStopCode);
+ static const uint32_t kStopInterruptCode = 1 << 23;
+ static const uint32_t kMaxStopCode = kStopInterruptCode - 1;
+ // The Simulator will handle the stop instruction and get the message address.
+ // It expects to find the address just after the svc instruction.
+ BlockConstPoolFor(2);
+ if (code >= 0) {
+ svc(kStopInterruptCode + code, cond);
+ } else {
+ svc(kStopInterruptCode + kMaxStopCode, cond);
+ }
+ emit(reinterpret_cast<Instr>(msg));
#else // def __arm__
#ifdef CAN_USE_ARMV5_INSTRUCTIONS
bkpt(0);
#else // ndef CAN_USE_ARMV5_INSTRUCTIONS
- swi(0x9f0001);
+ svc(0x9f0001);
#endif // ndef CAN_USE_ARMV5_INSTRUCTIONS
#endif // def __arm__
}
@@ -1654,7 +1664,7 @@ void Assembler::bkpt(uint32_t imm16) { // v5 and above
}
-void Assembler::swi(uint32_t imm24, Condition cond) {
+void Assembler::svc(uint32_t imm24, Condition cond) {
ASSERT(is_uint24(imm24));
emit(cond | 15*B24 | imm24);
}
@@ -2363,14 +2373,14 @@ void Assembler::BlockConstPoolFor(int instructions) {
// Debugging.
void Assembler::RecordJSReturn() {
- WriteRecordedPositions();
+ positions_recorder()->WriteRecordedPositions();
CheckBuffer();
RecordRelocInfo(RelocInfo::JS_RETURN);
}
void Assembler::RecordDebugBreakSlot() {
- WriteRecordedPositions();
+ positions_recorder()->WriteRecordedPositions();
CheckBuffer();
RecordRelocInfo(RelocInfo::DEBUG_BREAK_SLOT);
}
@@ -2384,47 +2394,6 @@ void Assembler::RecordComment(const char* msg) {
}
-void Assembler::RecordPosition(int pos) {
- if (pos == RelocInfo::kNoPosition) return;
- ASSERT(pos >= 0);
- current_position_ = pos;
-}
-
-
-void Assembler::RecordStatementPosition(int pos) {
- if (pos == RelocInfo::kNoPosition) return;
- ASSERT(pos >= 0);
- current_statement_position_ = pos;
-}
-
-
-bool Assembler::WriteRecordedPositions() {
- bool written = false;
-
- // Write the statement position if it is different from what was written last
- // time.
- if (current_statement_position_ != written_statement_position_) {
- CheckBuffer();
- RecordRelocInfo(RelocInfo::STATEMENT_POSITION, current_statement_position_);
- written_statement_position_ = current_statement_position_;
- written = true;
- }
-
- // Write the position if it is different from what was written last time and
- // also different from the written statement position.
- if (current_position_ != written_position_ &&
- current_position_ != written_statement_position_) {
- CheckBuffer();
- RecordRelocInfo(RelocInfo::POSITION, current_position_);
- written_position_ = current_position_;
- written = true;
- }
-
- // Return whether something was written.
- return written;
-}
-
-
void Assembler::GrowBuffer() {
if (!own_buffer_) FATAL("external code buffer is too small");
diff --git a/src/arm/assembler-arm.h b/src/arm/assembler-arm.h
index 1c4fd609..05792354 100644
--- a/src/arm/assembler-arm.h
+++ b/src/arm/assembler-arm.h
@@ -448,6 +448,7 @@ class Operand BASE_EMBEDDED {
// Return true of this operand fits in one instruction so that no
// 2-instruction solution with a load into the ip register is necessary.
bool is_single_instruction() const;
+ bool must_use_constant_pool() const;
inline int32_t immediate() const {
ASSERT(!rm_.is_valid());
@@ -904,10 +905,13 @@ class Assembler : public Malloced {
void stm(BlockAddrMode am, Register base, RegList src, Condition cond = al);
// Exception-generating instructions and debugging support
- void stop(const char* msg);
+ static const int kDefaultStopCode = -1;
+ void stop(const char* msg,
+ Condition cond = al,
+ int32_t code = kDefaultStopCode);
void bkpt(uint32_t imm16); // v5 and above
- void swi(uint32_t imm24, Condition cond = al);
+ void svc(uint32_t imm24, Condition cond = al);
// Coprocessor instructions
@@ -1114,13 +1118,9 @@ class Assembler : public Malloced {
// Use --debug_code to enable.
void RecordComment(const char* msg);
- void RecordPosition(int pos);
- void RecordStatementPosition(int pos);
- bool WriteRecordedPositions();
-
int pc_offset() const { return pc_ - buffer_; }
- int current_position() const { return current_position_; }
- int current_statement_position() const { return current_statement_position_; }
+
+ PositionsRecorder* positions_recorder() { return &positions_recorder_; }
bool can_peephole_optimize(int instructions) {
if (!FLAG_peephole_optimization) return false;
@@ -1256,12 +1256,6 @@ class Assembler : public Malloced {
// The bound position, before this we cannot do instruction elimination.
int last_bound_pos_;
- // source position information
- int current_position_;
- int current_statement_position_;
- int written_position_;
- int written_statement_position_;
-
// Code emission
inline void CheckBuffer();
void GrowBuffer();
@@ -1287,8 +1281,21 @@ class Assembler : public Malloced {
friend class RelocInfo;
friend class CodePatcher;
friend class BlockConstPoolScope;
+
+ PositionsRecorder positions_recorder_;
+ friend class PositionsRecorder;
+ friend class EnsureSpace;
+};
+
+
+class EnsureSpace BASE_EMBEDDED {
+ public:
+ explicit EnsureSpace(Assembler* assembler) {
+ assembler->CheckBuffer();
+ }
};
+
} } // namespace v8::internal
#endif // V8_ARM_ASSEMBLER_ARM_H_
diff --git a/src/arm/codegen-arm.cc b/src/arm/codegen-arm.cc
index 0c060f0f..d7fd9a49 100644
--- a/src/arm/codegen-arm.cc
+++ b/src/arm/codegen-arm.cc
@@ -3596,6 +3596,12 @@ void CodeGenerator::VisitObjectLiteral(ObjectLiteral* node) {
frame_->CallRuntime(Runtime::kCreateObjectLiteralShallow, 4);
}
frame_->EmitPush(r0); // save the result
+
+ // Mark all computed expressions that are bound to a key that
+ // is shadowed by a later occurrence of the same key. For the
+ // marked expressions, no store code is emitted.
+ node->CalculateEmitStore();
+
for (int i = 0; i < node->properties()->length(); i++) {
// At the start of each iteration, the top of stack contains
// the newly created object literal.
@@ -3612,11 +3618,15 @@ void CodeGenerator::VisitObjectLiteral(ObjectLiteral* node) {
if (key->handle()->IsSymbol()) {
Handle<Code> ic(Builtins::builtin(Builtins::StoreIC_Initialize));
Load(value);
- frame_->PopToR0();
- // Fetch the object literal.
- frame_->SpillAllButCopyTOSToR1();
- __ mov(r2, Operand(key->handle()));
- frame_->CallCodeObject(ic, RelocInfo::CODE_TARGET, 0);
+ if (property->emit_store()) {
+ frame_->PopToR0();
+ // Fetch the object literal.
+ frame_->SpillAllButCopyTOSToR1();
+ __ mov(r2, Operand(key->handle()));
+ frame_->CallCodeObject(ic, RelocInfo::CODE_TARGET, 0);
+ } else {
+ frame_->Drop();
+ }
break;
}
// else fall through
@@ -3624,7 +3634,11 @@ void CodeGenerator::VisitObjectLiteral(ObjectLiteral* node) {
frame_->Dup();
Load(key);
Load(value);
- frame_->CallRuntime(Runtime::kSetProperty, 3);
+ if (property->emit_store()) {
+ frame_->CallRuntime(Runtime::kSetProperty, 3);
+ } else {
+ frame_->Drop(3);
+ }
break;
}
case ObjectLiteral::Property::SETTER: {
@@ -5482,73 +5496,6 @@ void CodeGenerator::GenerateRegExpConstructResult(ZoneList<Expression*>* args) {
}
-void CodeGenerator::GenerateRegExpCloneResult(ZoneList<Expression*>* args) {
- ASSERT_EQ(1, args->length());
-
- Load(args->at(0));
- frame_->PopToR0();
- {
- VirtualFrame::SpilledScope spilled_scope(frame_);
-
- Label done;
- Label call_runtime;
- __ BranchOnSmi(r0, &done);
-
- // Load JSRegExp map into r1. Check that argument object has this map.
- // Arguments to this function should be results of calling RegExp exec,
- // which is either an unmodified JSRegExpResult or null. Anything not having
- // the unmodified JSRegExpResult map is returned unmodified.
- // This also ensures that elements are fast.
-
- __ ldr(r1, ContextOperand(cp, Context::GLOBAL_INDEX));
- __ ldr(r1, FieldMemOperand(r1, GlobalObject::kGlobalContextOffset));
- __ ldr(r1, ContextOperand(r1, Context::REGEXP_RESULT_MAP_INDEX));
- __ ldr(ip, FieldMemOperand(r0, HeapObject::kMapOffset));
- __ cmp(r1, Operand(ip));
- __ b(ne, &done);
-
- if (FLAG_debug_code) {
- __ LoadRoot(r2, Heap::kEmptyFixedArrayRootIndex);
- __ ldr(ip, FieldMemOperand(r0, JSObject::kPropertiesOffset));
- __ cmp(ip, r2);
- __ Check(eq, "JSRegExpResult: default map but non-empty properties.");
- }
-
- // All set, copy the contents to a new object.
- __ AllocateInNewSpace(JSRegExpResult::kSize,
- r2,
- r3,
- r4,
- &call_runtime,
- NO_ALLOCATION_FLAGS);
- // Store RegExpResult map as map of allocated object.
- ASSERT(JSRegExpResult::kSize == 6 * kPointerSize);
- // Copy all fields (map is already in r1) from (untagged) r0 to r2.
- // Change map of elements array (ends up in r4) to be a FixedCOWArray.
- __ bic(r0, r0, Operand(kHeapObjectTagMask));
- __ ldm(ib, r0, r3.bit() | r4.bit() | r5.bit() | r6.bit() | r7.bit());
- __ stm(ia, r2,
- r1.bit() | r3.bit() | r4.bit() | r5.bit() | r6.bit() | r7.bit());
- ASSERT(JSRegExp::kElementsOffset == 2 * kPointerSize);
- // Check whether elements array is empty fixed array, and otherwise make
- // it copy-on-write (it never should be empty unless someone is messing
- // with the arguments to the runtime function).
- __ LoadRoot(ip, Heap::kEmptyFixedArrayRootIndex);
- __ add(r0, r2, Operand(kHeapObjectTag)); // Tag result and move it to r0.
- __ cmp(r4, ip);
- __ b(eq, &done);
- __ LoadRoot(ip, Heap::kFixedCOWArrayMapRootIndex);
- __ str(ip, FieldMemOperand(r4, HeapObject::kMapOffset));
- __ b(&done);
- __ bind(&call_runtime);
- __ push(r0);
- __ CallRuntime(Runtime::kRegExpCloneResult, 1);
- __ bind(&done);
- }
- frame_->EmitPush(r0);
-}
-
-
class DeferredSearchCache: public DeferredCode {
public:
DeferredSearchCache(Register dst, Register cache, Register key)
diff --git a/src/arm/codegen-arm.h b/src/arm/codegen-arm.h
index e6fd6071..97e50b42 100644
--- a/src/arm/codegen-arm.h
+++ b/src/arm/codegen-arm.h
@@ -518,8 +518,6 @@ class CodeGenerator: public AstVisitor {
void GenerateRegExpConstructResult(ZoneList<Expression*>* args);
- void GenerateRegExpCloneResult(ZoneList<Expression*>* args);
-
// Support for fast native caches.
void GenerateGetFromCache(ZoneList<Expression*>* args);
diff --git a/src/arm/constants-arm.h b/src/arm/constants-arm.h
index b2b5cb56..123c5e79 100644
--- a/src/arm/constants-arm.h
+++ b/src/arm/constants-arm.h
@@ -186,12 +186,18 @@ enum Shift {
// Special Software Interrupt codes when used in the presence of the ARM
// simulator.
+// svc (formerly swi) provides a 24bit immediate value. Use bits 22:0 for
+// standard SoftwareInterrupCode. Bit 23 is reserved for the stop feature.
enum SoftwareInterruptCodes {
// transition to C code
call_rt_redirected = 0x10,
// break point
- break_point = 0x20
+ break_point = 0x20,
+ // stop
+ stop = 1 << 23
};
+static const int32_t kStopCodeMask = stop - 1;
+static const uint32_t kMaxStopCode = stop - 1;
// Type of VFP register. Determines register encoding.
@@ -325,7 +331,7 @@ class Instr {
inline int SImmed24Field() const { return ((InstructionBits() << 8) >> 8); }
// Fields used in Software interrupt instructions
- inline SoftwareInterruptCodes SwiField() const {
+ inline SoftwareInterruptCodes SvcField() const {
return static_cast<SoftwareInterruptCodes>(Bits(23, 0));
}
diff --git a/src/arm/cpu-arm.cc b/src/arm/cpu-arm.cc
index a3bf4832..e998b6f5 100644
--- a/src/arm/cpu-arm.cc
+++ b/src/arm/cpu-arm.cc
@@ -70,7 +70,7 @@ void CPU::FlushICache(void* start, size_t size) {
// __arm__ may be defined in thumb mode.
register uint32_t scno asm("r7") = __ARM_NR_cacheflush;
asm volatile(
- "swi 0x0"
+ "svc 0x0"
: "=r" (beg)
: "0" (beg), "r" (end), "r" (flg), "r" (scno));
#else
@@ -83,7 +83,7 @@ void CPU::FlushICache(void* start, size_t size) {
".ARM \n"
"1: push {r7} \n\t"
"mov r7, %4 \n\t"
- "swi 0x0 \n\t"
+ "svc 0x0 \n\t"
"pop {r7} \n\t"
"@ Enter THUMB Mode\n\t"
"adr r3, 2f+1 \n\t"
@@ -98,20 +98,20 @@ void CPU::FlushICache(void* start, size_t size) {
#if defined (__arm__) && !defined(__thumb__)
// __arm__ may be defined in thumb mode.
asm volatile(
- "swi %1"
+ "svc %1"
: "=r" (beg)
: "i" (__ARM_NR_cacheflush), "0" (beg), "r" (end), "r" (flg));
#else
// Do not use the value of __ARM_NR_cacheflush in the inline assembly
// below, because the thumb mode value would be used, which would be
- // wrong, since we switch to ARM mode before executing the swi instruction
+ // wrong, since we switch to ARM mode before executing the svc instruction
asm volatile(
"@ Enter ARM Mode \n\t"
"adr r3, 1f \n\t"
"bx r3 \n\t"
".ALIGN 4 \n\t"
".ARM \n"
- "1: swi 0x9f0002 \n"
+ "1: svc 0x9f0002 \n"
"@ Enter THUMB Mode\n\t"
"adr r3, 2f+1 \n\t"
"bx r3 \n\t"
diff --git a/src/arm/disasm-arm.cc b/src/arm/disasm-arm.cc
index 5122f437..4e7580f8 100644
--- a/src/arm/disasm-arm.cc
+++ b/src/arm/disasm-arm.cc
@@ -108,7 +108,7 @@ class Decoder {
void PrintShiftImm(Instr* instr);
void PrintShiftSat(Instr* instr);
void PrintPU(Instr* instr);
- void PrintSoftwareInterrupt(SoftwareInterruptCodes swi);
+ void PrintSoftwareInterrupt(SoftwareInterruptCodes svc);
// Handle formatting of instructions and their options.
int FormatRegister(Instr* instr, const char* option);
@@ -126,8 +126,8 @@ class Decoder {
void DecodeType4(Instr* instr);
void DecodeType5(Instr* instr);
void DecodeType6(Instr* instr);
- void DecodeType7(Instr* instr);
- void DecodeUnconditional(Instr* instr);
+ // Type 7 includes special Debugger instructions.
+ int DecodeType7(Instr* instr);
// For VFP support.
void DecodeTypeVFP(Instr* instr);
void DecodeType6CoprocessorIns(Instr* instr);
@@ -290,8 +290,8 @@ void Decoder::PrintPU(Instr* instr) {
// Print SoftwareInterrupt codes. Factoring this out reduces the complexity of
// the FormatOption method.
-void Decoder::PrintSoftwareInterrupt(SoftwareInterruptCodes swi) {
- switch (swi) {
+void Decoder::PrintSoftwareInterrupt(SoftwareInterruptCodes svc) {
+ switch (svc) {
case call_rt_redirected:
Print("call_rt_redirected");
return;
@@ -299,9 +299,16 @@ void Decoder::PrintSoftwareInterrupt(SoftwareInterruptCodes swi) {
Print("break_point");
return;
default:
- out_buffer_pos_ += v8i::OS::SNPrintF(out_buffer_ + out_buffer_pos_,
- "%d",
- swi);
+ if (svc >= stop) {
+ out_buffer_pos_ += v8i::OS::SNPrintF(out_buffer_ + out_buffer_pos_,
+ "%d - 0x%x",
+ svc & kStopCodeMask,
+ svc & kStopCodeMask);
+ } else {
+ out_buffer_pos_ += v8i::OS::SNPrintF(out_buffer_ + out_buffer_pos_,
+ "%d",
+ svc);
+ }
return;
}
}
@@ -553,9 +560,9 @@ int Decoder::FormatOption(Instr* instr, const char* format) {
PrintShiftRm(instr);
return 8;
}
- } else if (format[1] == 'w') { // 'swi
- ASSERT(STRING_STARTS_WITH(format, "swi"));
- PrintSoftwareInterrupt(instr->SwiField());
+ } else if (format[1] == 'v') { // 'svc
+ ASSERT(STRING_STARTS_WITH(format, "svc"));
+ PrintSoftwareInterrupt(instr->SvcField());
return 3;
} else if (format[1] == 'i') { // 'sign: signed extra loads and stores
ASSERT(STRING_STARTS_WITH(format, "sign"));
@@ -1004,72 +1011,27 @@ void Decoder::DecodeType6(Instr* instr) {
}
-void Decoder::DecodeType7(Instr* instr) {
+int Decoder::DecodeType7(Instr* instr) {
if (instr->Bit(24) == 1) {
- Format(instr, "swi'cond 'swi");
+ if (instr->SvcField() >= stop) {
+ Format(instr, "stop'cond 'svc");
+ // Also print the stop message. Its address is encoded
+ // in the following 4 bytes.
+ out_buffer_pos_ +=
+ v8i::OS::SNPrintF(out_buffer_ + out_buffer_pos_,
+ "\n %p %08x stop message: %s",
+ reinterpret_cast<int32_t*>(instr + Instr::kInstrSize),
+ *reinterpret_cast<char**>(instr + Instr::kInstrSize),
+ *reinterpret_cast<char**>(instr + Instr::kInstrSize));
+ // We have decoded 2 * Instr::kInstrSize bytes.
+ return 2 * Instr::kInstrSize;
+ } else {
+ Format(instr, "svc'cond 'svc");
+ }
} else {
DecodeTypeVFP(instr);
}
-}
-
-void Decoder::DecodeUnconditional(Instr* instr) {
- if (instr->Bits(7, 4) == 0xB && instr->Bits(27, 25) == 0 && instr->HasL()) {
- Format(instr, "'memop'h'pu 'rd, ");
- bool immediate = instr->HasB();
- switch (instr->PUField()) {
- case 0: {
- // Post index, negative.
- if (instr->HasW()) {
- Unknown(instr);
- break;
- }
- if (immediate) {
- Format(instr, "['rn], #-'imm12");
- } else {
- Format(instr, "['rn], -'rm");
- }
- break;
- }
- case 1: {
- // Post index, positive.
- if (instr->HasW()) {
- Unknown(instr);
- break;
- }
- if (immediate) {
- Format(instr, "['rn], #+'imm12");
- } else {
- Format(instr, "['rn], +'rm");
- }
- break;
- }
- case 2: {
- // Pre index or offset, negative.
- if (immediate) {
- Format(instr, "['rn, #-'imm12]'w");
- } else {
- Format(instr, "['rn, -'rm]'w");
- }
- break;
- }
- case 3: {
- // Pre index or offset, positive.
- if (immediate) {
- Format(instr, "['rn, #+'imm12]'w");
- } else {
- Format(instr, "['rn, +'rm]'w");
- }
- break;
- }
- default: {
- // The PU field is a 2-bit field.
- UNREACHABLE();
- break;
- }
- }
- return;
- }
- Format(instr, "break 'msg");
+ return Instr::kInstrSize;
}
@@ -1332,7 +1294,7 @@ int Decoder::InstructionDecode(byte* instr_ptr) {
"%08x ",
instr->InstructionBits());
if (instr->ConditionField() == special_condition) {
- DecodeUnconditional(instr);
+ UNIMPLEMENTED();
return Instr::kInstrSize;
}
switch (instr->TypeField()) {
@@ -1362,8 +1324,7 @@ int Decoder::InstructionDecode(byte* instr_ptr) {
break;
}
case 7: {
- DecodeType7(instr);
- break;
+ return DecodeType7(instr);
}
default: {
// The type field is 3-bits in the ARM encoding.
diff --git a/src/arm/full-codegen-arm.cc b/src/arm/full-codegen-arm.cc
index 2855ca4f..bf27d0c7 100644
--- a/src/arm/full-codegen-arm.cc
+++ b/src/arm/full-codegen-arm.cc
@@ -1169,6 +1169,11 @@ void FullCodeGenerator::VisitObjectLiteral(ObjectLiteral* expr) {
// result_saved is false the result is in r0.
bool result_saved = false;
+ // Mark all computed expressions that are bound to a key that
+ // is shadowed by a later occurrence of the same key. For the
+ // marked expressions, no store code is emitted.
+ expr->CalculateEmitStore();
+
for (int i = 0; i < expr->properties()->length(); i++) {
ObjectLiteral::Property* property = expr->properties()->at(i);
if (property->IsCompileTimeValue()) continue;
@@ -1190,8 +1195,10 @@ void FullCodeGenerator::VisitObjectLiteral(ObjectLiteral* expr) {
VisitForAccumulatorValue(value);
__ mov(r2, Operand(key->handle()));
__ ldr(r1, MemOperand(sp));
- Handle<Code> ic(Builtins::builtin(Builtins::StoreIC_Initialize));
- EmitCallIC(ic, RelocInfo::CODE_TARGET);
+ if (property->emit_store()) {
+ Handle<Code> ic(Builtins::builtin(Builtins::StoreIC_Initialize));
+ EmitCallIC(ic, RelocInfo::CODE_TARGET);
+ }
break;
}
// Fall through.
@@ -1201,7 +1208,11 @@ void FullCodeGenerator::VisitObjectLiteral(ObjectLiteral* expr) {
__ push(r0);
VisitForStackValue(key);
VisitForStackValue(value);
- __ CallRuntime(Runtime::kSetProperty, 3);
+ if (property->emit_store()) {
+ __ CallRuntime(Runtime::kSetProperty, 3);
+ } else {
+ __ Drop(3);
+ }
break;
case ObjectLiteral::Property::GETTER:
case ObjectLiteral::Property::SETTER:
@@ -1677,12 +1688,14 @@ void FullCodeGenerator::EmitCallWithIC(Call* expr,
// Code common for calls using the IC.
ZoneList<Expression*>* args = expr->arguments();
int arg_count = args->length();
- for (int i = 0; i < arg_count; i++) {
- VisitForStackValue(args->at(i));
+ { PreserveStatementPositionScope scope(masm()->positions_recorder());
+ for (int i = 0; i < arg_count; i++) {
+ VisitForStackValue(args->at(i));
+ }
+ __ mov(r2, Operand(name));
}
- __ mov(r2, Operand(name));
// Record source position for debugger.
- SetSourcePosition(expr->position());
+ SetSourcePosition(expr->position(), FORCED_POSITION);
// Call the IC initialization code.
InLoopFlag in_loop = (loop_depth() > 0) ? IN_LOOP : NOT_IN_LOOP;
Handle<Code> ic = CodeGenerator::ComputeCallInitialize(arg_count, in_loop);
@@ -1699,13 +1712,15 @@ void FullCodeGenerator::EmitKeyedCallWithIC(Call* expr,
// Code common for calls using the IC.
ZoneList<Expression*>* args = expr->arguments();
int arg_count = args->length();
- for (int i = 0; i < arg_count; i++) {
- VisitForStackValue(args->at(i));
+ { PreserveStatementPositionScope scope(masm()->positions_recorder());
+ for (int i = 0; i < arg_count; i++) {
+ VisitForStackValue(args->at(i));
+ }
+ VisitForAccumulatorValue(key);
+ __ mov(r2, r0);
}
- VisitForAccumulatorValue(key);
- __ mov(r2, r0);
// Record source position for debugger.
- SetSourcePosition(expr->position());
+ SetSourcePosition(expr->position(), FORCED_POSITION);
// Call the IC initialization code.
InLoopFlag in_loop = (loop_depth() > 0) ? IN_LOOP : NOT_IN_LOOP;
Handle<Code> ic = CodeGenerator::ComputeKeyedCallInitialize(arg_count,
@@ -1721,11 +1736,13 @@ void FullCodeGenerator::EmitCallWithStub(Call* expr) {
// Code common for calls using the call stub.
ZoneList<Expression*>* args = expr->arguments();
int arg_count = args->length();
- for (int i = 0; i < arg_count; i++) {
- VisitForStackValue(args->at(i));
+ { PreserveStatementPositionScope scope(masm()->positions_recorder());
+ for (int i = 0; i < arg_count; i++) {
+ VisitForStackValue(args->at(i));
+ }
}
// Record source position for debugger.
- SetSourcePosition(expr->position());
+ SetSourcePosition(expr->position(), FORCED_POSITION);
InLoopFlag in_loop = (loop_depth() > 0) ? IN_LOOP : NOT_IN_LOOP;
CallFunctionStub stub(arg_count, in_loop, RECEIVER_MIGHT_BE_VALUE);
__ CallStub(&stub);
@@ -1745,41 +1762,45 @@ void FullCodeGenerator::VisitCall(Call* expr) {
// resolve the function we need to call and the receiver of the
// call. Then we call the resolved function using the given
// arguments.
- VisitForStackValue(fun);
- __ LoadRoot(r2, Heap::kUndefinedValueRootIndex);
- __ push(r2); // Reserved receiver slot.
-
- // Push the arguments.
ZoneList<Expression*>* args = expr->arguments();
int arg_count = args->length();
- for (int i = 0; i < arg_count; i++) {
- VisitForStackValue(args->at(i));
- }
- // Push copy of the function - found below the arguments.
- __ ldr(r1, MemOperand(sp, (arg_count + 1) * kPointerSize));
- __ push(r1);
+ { PreserveStatementPositionScope pos_scope(masm()->positions_recorder());
+ VisitForStackValue(fun);
+ __ LoadRoot(r2, Heap::kUndefinedValueRootIndex);
+ __ push(r2); // Reserved receiver slot.
- // Push copy of the first argument or undefined if it doesn't exist.
- if (arg_count > 0) {
- __ ldr(r1, MemOperand(sp, arg_count * kPointerSize));
+ // Push the arguments.
+ for (int i = 0; i < arg_count; i++) {
+ VisitForStackValue(args->at(i));
+ }
+
+ // Push copy of the function - found below the arguments.
+ __ ldr(r1, MemOperand(sp, (arg_count + 1) * kPointerSize));
__ push(r1);
- } else {
- __ push(r2);
- }
- // Push the receiver of the enclosing function and do runtime call.
- __ ldr(r1, MemOperand(fp, (2 + scope()->num_parameters()) * kPointerSize));
- __ push(r1);
- __ CallRuntime(Runtime::kResolvePossiblyDirectEval, 3);
+ // Push copy of the first argument or undefined if it doesn't exist.
+ if (arg_count > 0) {
+ __ ldr(r1, MemOperand(sp, arg_count * kPointerSize));
+ __ push(r1);
+ } else {
+ __ push(r2);
+ }
- // The runtime call returns a pair of values in r0 (function) and
- // r1 (receiver). Touch up the stack with the right values.
- __ str(r0, MemOperand(sp, (arg_count + 1) * kPointerSize));
- __ str(r1, MemOperand(sp, arg_count * kPointerSize));
+ // Push the receiver of the enclosing function and do runtime call.
+ __ ldr(r1,
+ MemOperand(fp, (2 + scope()->num_parameters()) * kPointerSize));
+ __ push(r1);
+ __ CallRuntime(Runtime::kResolvePossiblyDirectEval, 3);
+
+ // The runtime call returns a pair of values in r0 (function) and
+ // r1 (receiver). Touch up the stack with the right values.
+ __ str(r0, MemOperand(sp, (arg_count + 1) * kPointerSize));
+ __ str(r1, MemOperand(sp, arg_count * kPointerSize));
+ }
// Record source position for debugger.
- SetSourcePosition(expr->position());
+ SetSourcePosition(expr->position(), FORCED_POSITION);
InLoopFlag in_loop = (loop_depth() > 0) ? IN_LOOP : NOT_IN_LOOP;
CallFunctionStub stub(arg_count, in_loop, RECEIVER_MIGHT_BE_VALUE);
__ CallStub(&stub);
@@ -1796,12 +1817,14 @@ void FullCodeGenerator::VisitCall(Call* expr) {
// Call to a lookup slot (dynamically introduced variable).
Label slow, done;
- // Generate code for loading from variables potentially shadowed
- // by eval-introduced variables.
- EmitDynamicLoadFromSlotFastCase(var->AsSlot(),
- NOT_INSIDE_TYPEOF,
- &slow,
- &done);
+ { PreserveStatementPositionScope scope(masm()->positions_recorder());
+ // Generate code for loading from variables potentially shadowed
+ // by eval-introduced variables.
+ EmitDynamicLoadFromSlotFastCase(var->AsSlot(),
+ NOT_INSIDE_TYPEOF,
+ &slow,
+ &done);
+ }
__ bind(&slow);
// Call the runtime to find the function to call (returned in r0)
@@ -1835,17 +1858,23 @@ void FullCodeGenerator::VisitCall(Call* expr) {
Literal* key = prop->key()->AsLiteral();
if (key != NULL && key->handle()->IsSymbol()) {
// Call to a named property, use call IC.
- VisitForStackValue(prop->obj());
+ { PreserveStatementPositionScope scope(masm()->positions_recorder());
+ VisitForStackValue(prop->obj());
+ }
EmitCallWithIC(expr, key->handle(), RelocInfo::CODE_TARGET);
} else {
// Call to a keyed property.
// For a synthetic property use keyed load IC followed by function call,
// for a regular property use keyed CallIC.
- VisitForStackValue(prop->obj());
+ { PreserveStatementPositionScope scope(masm()->positions_recorder());
+ VisitForStackValue(prop->obj());
+ }
if (prop->is_synthetic()) {
- VisitForAccumulatorValue(prop->key());
+ { PreserveStatementPositionScope scope(masm()->positions_recorder());
+ VisitForAccumulatorValue(prop->key());
+ }
// Record source code position for IC call.
- SetSourcePosition(prop->position());
+ SetSourcePosition(prop->position(), FORCED_POSITION);
__ pop(r1); // We do not need to keep the receiver.
Handle<Code> ic(Builtins::builtin(Builtins::KeyedLoadIC_Initialize));
@@ -1868,7 +1897,10 @@ void FullCodeGenerator::VisitCall(Call* expr) {
loop_depth() == 0) {
lit->set_try_full_codegen(true);
}
- VisitForStackValue(fun);
+
+ { PreserveStatementPositionScope scope(masm()->positions_recorder());
+ VisitForStackValue(fun);
+ }
// Load global receiver object.
__ ldr(r1, CodeGenerator::GlobalObject());
__ ldr(r1, FieldMemOperand(r1, GlobalObject::kGlobalReceiverOffset));
diff --git a/src/arm/ic-arm.cc b/src/arm/ic-arm.cc
index c460f9ca..a09afdf7 100644
--- a/src/arm/ic-arm.cc
+++ b/src/arm/ic-arm.cc
@@ -544,7 +544,7 @@ static void GenerateMonomorphicCacheProbe(MacroAssembler* masm,
// Probe the stub cache.
Code::Flags flags =
Code::ComputeFlags(kind, NOT_IN_LOOP, MONOMORPHIC, NORMAL, argc);
- StubCache::GenerateProbe(masm, flags, r1, r2, r3, no_reg);
+ StubCache::GenerateProbe(masm, flags, r1, r2, r3, r4, r5);
// If the stub cache probing failed, the receiver might be a value.
// For value objects, we use the map of the prototype objects for
@@ -583,7 +583,7 @@ static void GenerateMonomorphicCacheProbe(MacroAssembler* masm,
// Probe the stub cache for the value object.
__ bind(&probe);
- StubCache::GenerateProbe(masm, flags, r1, r2, r3, no_reg);
+ StubCache::GenerateProbe(masm, flags, r1, r2, r3, r4, r5);
__ bind(&miss);
}
@@ -858,7 +858,7 @@ void LoadIC::GenerateMegamorphic(MacroAssembler* masm) {
Code::Flags flags = Code::ComputeFlags(Code::LOAD_IC,
NOT_IN_LOOP,
MONOMORPHIC);
- StubCache::GenerateProbe(masm, flags, r0, r2, r3, no_reg);
+ StubCache::GenerateProbe(masm, flags, r0, r2, r3, r4, r5);
// Cache miss: Jump to runtime.
GenerateMiss(masm);
@@ -2163,7 +2163,7 @@ void StoreIC::GenerateMegamorphic(MacroAssembler* masm) {
Code::Flags flags = Code::ComputeFlags(Code::STORE_IC,
NOT_IN_LOOP,
MONOMORPHIC);
- StubCache::GenerateProbe(masm, flags, r1, r2, r3, no_reg);
+ StubCache::GenerateProbe(masm, flags, r1, r2, r3, r4, r5);
// Cache miss: Jump to runtime.
GenerateMiss(masm);
diff --git a/src/arm/macro-assembler-arm.cc b/src/arm/macro-assembler-arm.cc
index 7f6090bc..d2c22af5 100644
--- a/src/arm/macro-assembler-arm.cc
+++ b/src/arm/macro-assembler-arm.cc
@@ -129,7 +129,7 @@ void MacroAssembler::Call(intptr_t target, RelocInfo::Mode rmode,
// address is loaded. The mov method will automatically record
// positions when pc is the target, since this is not the case here
// we have to do it explicitly.
- WriteRecordedPositions();
+ positions_recorder()->WriteRecordedPositions();
mov(ip, Operand(target, rmode), LeaveCC, cond);
blx(ip, cond);
@@ -220,20 +220,20 @@ void MacroAssembler::Move(Register dst, Register src) {
void MacroAssembler::And(Register dst, Register src1, const Operand& src2,
Condition cond) {
- if (!CpuFeatures::IsSupported(ARMv7) || src2.is_single_instruction()) {
- and_(dst, src1, src2, LeaveCC, cond);
- return;
- }
- int32_t immediate = src2.immediate();
- if (immediate == 0) {
+ if (!src2.is_reg() &&
+ !src2.must_use_constant_pool() &&
+ src2.immediate() == 0) {
mov(dst, Operand(0, RelocInfo::NONE), LeaveCC, cond);
- return;
- }
- if (IsPowerOf2(immediate + 1) && ((immediate & 1) != 0)) {
- ubfx(dst, src1, 0, WhichPowerOf2(immediate + 1), cond);
- return;
+
+ } else if (!src2.is_single_instruction() &&
+ !src2.must_use_constant_pool() &&
+ CpuFeatures::IsSupported(ARMv7) &&
+ IsPowerOf2(src2.immediate() + 1)) {
+ ubfx(dst, src1, 0, WhichPowerOf2(src2.immediate() + 1), cond);
+
+ } else {
+ and_(dst, src1, src2, LeaveCC, cond);
}
- and_(dst, src1, src2, LeaveCC, cond);
}
diff --git a/src/arm/simulator-arm.cc b/src/arm/simulator-arm.cc
index 534e394a..cb91520f 100644
--- a/src/arm/simulator-arm.cc
+++ b/src/arm/simulator-arm.cc
@@ -112,15 +112,29 @@ static void InitializeCoverage() {
void Debugger::Stop(Instr* instr) {
- char* str = reinterpret_cast<char*>(instr->InstructionBits() & 0x0fffffff);
- if (strlen(str) > 0) {
+ // Get the stop code.
+ uint32_t code = instr->SvcField() & kStopCodeMask;
+ // Retrieve the encoded address, which comes just after this stop.
+ char** msg_address =
+ reinterpret_cast<char**>(sim_->get_pc() + Instr::kInstrSize);
+ char* msg = *msg_address;
+ ASSERT(msg != NULL);
+
+ // Update this stop description.
+ if (isWatchedStop(code) && !watched_stops[code].desc) {
+ watched_stops[code].desc = msg;
+ }
+
+ if (strlen(msg) > 0) {
if (coverage_log != NULL) {
- fprintf(coverage_log, "%s\n", str);
+ fprintf(coverage_log, "%s\n", msg);
fflush(coverage_log);
}
- instr->SetInstructionBits(0xe1a00000); // Overwrite with nop.
+ // Overwrite the instruction and address with nops.
+ instr->SetInstructionBits(kNopInstr);
+ reinterpret_cast<Instr*>(msg_address)->SetInstructionBits(kNopInstr);
}
- sim_->set_pc(sim_->get_pc() + Instr::kInstrSize);
+ sim_->set_pc(sim_->get_pc() + 2 * Instr::kInstrSize);
}
#else // ndef GENERATED_CODE_COVERAGE
@@ -130,9 +144,16 @@ static void InitializeCoverage() {
void Debugger::Stop(Instr* instr) {
- const char* str = (const char*)(instr->InstructionBits() & 0x0fffffff);
- PrintF("Simulator hit %s\n", str);
- sim_->set_pc(sim_->get_pc() + Instr::kInstrSize);
+ // Get the stop code.
+ uint32_t code = instr->SvcField() & kStopCodeMask;
+ // Retrieve the encoded address, which comes just after this stop.
+ char* msg = *reinterpret_cast<char**>(sim_->get_pc() + Instr::kInstrSize);
+ // Update this stop description.
+ if (sim_->isWatchedStop(code) && !sim_->watched_stops[code].desc) {
+ sim_->watched_stops[code].desc = msg;
+ }
+ PrintF("Simulator hit %s\n", msg);
+ sim_->set_pc(sim_->get_pc() + 2 * Instr::kInstrSize);
Debug();
}
#endif
@@ -359,6 +380,7 @@ void Debugger::Debug() {
// use a reasonably large buffer
v8::internal::EmbeddedVector<char, 256> buffer;
+ byte* prev = NULL;
byte* cur = NULL;
byte* end = NULL;
@@ -368,9 +390,9 @@ void Debugger::Debug() {
} else if (argc == 2) {
int32_t value;
if (GetValue(arg1, &value)) {
- cur = reinterpret_cast<byte*>(value);
- // no length parameter passed, assume 10 instructions
- end = cur + (10 * Instr::kInstrSize);
+ cur = reinterpret_cast<byte*>(sim_->get_pc());
+ // Disassemble <arg1> instructions.
+ end = cur + (value * Instr::kInstrSize);
}
} else {
int32_t value1;
@@ -382,10 +404,10 @@ void Debugger::Debug() {
}
while (cur < end) {
- dasm.InstructionDecode(buffer, cur);
+ prev = cur;
+ cur += dasm.InstructionDecode(buffer, cur);
PrintF(" 0x%08x %s\n",
- reinterpret_cast<intptr_t>(cur), buffer.start());
- cur += Instr::kInstrSize;
+ reinterpret_cast<intptr_t>(prev), buffer.start());
}
} else if (strcmp(cmd, "gdb") == 0) {
PrintF("relinquishing control to gdb\n");
@@ -418,13 +440,58 @@ void Debugger::Debug() {
PrintF("OVERFLOW flag: %d; ", sim_->overflow_vfp_flag_);
PrintF("UNDERFLOW flag: %d; ", sim_->underflow_vfp_flag_);
PrintF("INEXACT flag: %d; ", sim_->inexact_vfp_flag_);
- } else if (strcmp(cmd, "unstop") == 0) {
- intptr_t stop_pc = sim_->get_pc() - Instr::kInstrSize;
+ } else if (strcmp(cmd, "stop") == 0) {
+ int32_t value;
+ intptr_t stop_pc = sim_->get_pc() - 2 * Instr::kInstrSize;
Instr* stop_instr = reinterpret_cast<Instr*>(stop_pc);
- if (stop_instr->ConditionField() == special_condition) {
- stop_instr->SetInstructionBits(kNopInstr);
+ Instr* msg_address =
+ reinterpret_cast<Instr*>(stop_pc + Instr::kInstrSize);
+ if ((argc == 2) && (strcmp(arg1, "unstop") == 0)) {
+ // Remove the current stop.
+ if (sim_->isStopInstruction(stop_instr)) {
+ stop_instr->SetInstructionBits(kNopInstr);
+ msg_address->SetInstructionBits(kNopInstr);
+ } else {
+ PrintF("Not at debugger stop.\n");
+ }
+ } else if (argc == 3) {
+ // Print information about all/the specified breakpoint(s).
+ if (strcmp(arg1, "info") == 0) {
+ if (strcmp(arg2, "all") == 0) {
+ PrintF("Stop information:\n");
+ for (uint32_t i = 0; i < sim_->kNumOfWatchedStops; i++) {
+ sim_->PrintStopInfo(i);
+ }
+ } else if (GetValue(arg2, &value)) {
+ sim_->PrintStopInfo(value);
+ } else {
+ PrintF("Unrecognized argument.\n");
+ }
+ } else if (strcmp(arg1, "enable") == 0) {
+ // Enable all/the specified breakpoint(s).
+ if (strcmp(arg2, "all") == 0) {
+ for (uint32_t i = 0; i < sim_->kNumOfWatchedStops; i++) {
+ sim_->EnableStop(i);
+ }
+ } else if (GetValue(arg2, &value)) {
+ sim_->EnableStop(value);
+ } else {
+ PrintF("Unrecognized argument.\n");
+ }
+ } else if (strcmp(arg1, "disable") == 0) {
+ // Disable all/the specified breakpoint(s).
+ if (strcmp(arg2, "all") == 0) {
+ for (uint32_t i = 0; i < sim_->kNumOfWatchedStops; i++) {
+ sim_->DisableStop(i);
+ }
+ } else if (GetValue(arg2, &value)) {
+ sim_->DisableStop(value);
+ } else {
+ PrintF("Unrecognized argument.\n");
+ }
+ }
} else {
- PrintF("Not at debugger stop.");
+ PrintF("Wrong usage. Use help command for more information.\n");
}
} else if ((strcmp(cmd, "t") == 0) || strcmp(cmd, "trace") == 0) {
::v8::internal::FLAG_trace_sim = !::v8::internal::FLAG_trace_sim;
@@ -455,11 +522,29 @@ void Debugger::Debug() {
PrintF(" set a break point on the address\n");
PrintF("del\n");
PrintF(" delete the breakpoint\n");
- PrintF("unstop\n");
- PrintF(" ignore the stop instruction at the current location");
- PrintF(" from now on\n");
PrintF("trace (alias 't')\n");
PrintF(" toogle the tracing of all executed statements\n");
+ PrintF("stop feature:\n");
+ PrintF(" Description:\n");
+ PrintF(" Stops are debug instructions inserted by\n");
+ PrintF(" the Assembler::stop() function.\n");
+ PrintF(" When hitting a stop, the Simulator will\n");
+ PrintF(" stop and and give control to the Debugger.\n");
+ PrintF(" The first %d stop codes are watched:\n",
+ Simulator::kNumOfWatchedStops);
+ PrintF(" - They can be enabled / disabled: the Simulator\n");
+ PrintF(" will / won't stop when hitting them.\n");
+ PrintF(" - The Simulator keeps track of how many times they \n");
+ PrintF(" are met. (See the info command.) Going over a\n");
+ PrintF(" disabled stop still increases its counter. \n");
+ PrintF(" Commands:\n");
+ PrintF(" stop info all/<code> : print infos about number <code>\n");
+ PrintF(" or all stop(s).\n");
+ PrintF(" stop enable/disable all/<code> : enables / disables\n");
+ PrintF(" all or number <code> stop(s)\n");
+ PrintF(" stop unstop\n");
+ PrintF(" ignore the stop instruction at the current location\n");
+ PrintF(" from now on\n");
} else {
PrintF("Unknown command: %s\n", cmd);
}
@@ -643,9 +728,9 @@ Simulator::Simulator() {
// the simulator. The external reference will be a function compiled for the
// host architecture. We need to call that function instead of trying to
// execute it with the simulator. We do that by redirecting the external
-// reference to a swi (software-interrupt) instruction that is handled by
+// reference to a svc (Supervisor Call) instruction that is handled by
// the simulator. We write the original destination of the jump just at a known
-// offset from the swi instruction so the simulator knows what to call.
+// offset from the svc instruction so the simulator knows what to call.
class Redirection {
public:
Redirection(void* external_function, bool fp_return)
@@ -1434,8 +1519,8 @@ typedef double (*SimulatorRuntimeFPCall)(int32_t arg0,
// Software interrupt instructions are used by the simulator to call into the
// C-based V8 runtime.
void Simulator::SoftwareInterrupt(Instr* instr) {
- int swi = instr->SwiField();
- switch (swi) {
+ int svc = instr->SvcField();
+ switch (svc) {
case call_rt_redirected: {
// Check if stack is aligned. Error if not aligned is reported below to
// include information on the function called.
@@ -1505,9 +1590,98 @@ void Simulator::SoftwareInterrupt(Instr* instr) {
dbg.Debug();
break;
}
+ // stop uses all codes greater than 1 << 23.
default: {
- UNREACHABLE();
- break;
+ if (svc >= (1 << 23)) {
+ uint32_t code = svc & kStopCodeMask;
+ if (isWatchedStop(code)) {
+ IncreaseStopCounter(code);
+ }
+ // Stop if it is enabled, otherwise go on jumping over the stop
+ // and the message address.
+ if (isEnabledStop(code)) {
+ Debugger dbg(this);
+ dbg.Stop(instr);
+ } else {
+ set_pc(get_pc() + 2 * Instr::kInstrSize);
+ }
+ } else {
+ // This is not a valid svc code.
+ UNREACHABLE();
+ break;
+ }
+ }
+ }
+}
+
+
+// Stop helper functions.
+bool Simulator::isStopInstruction(Instr* instr) {
+ return (instr->Bits(27, 24) == 0xF) && (instr->SvcField() >= stop);
+}
+
+
+bool Simulator::isWatchedStop(uint32_t code) {
+ ASSERT(code <= kMaxStopCode);
+ return code < kNumOfWatchedStops;
+}
+
+
+bool Simulator::isEnabledStop(uint32_t code) {
+ ASSERT(code <= kMaxStopCode);
+ // Unwatched stops are always enabled.
+ return !isWatchedStop(code) ||
+ !(watched_stops[code].count & kStopDisabledBit);
+}
+
+
+void Simulator::EnableStop(uint32_t code) {
+ ASSERT(isWatchedStop(code));
+ if (!isEnabledStop(code)) {
+ watched_stops[code].count &= ~kStopDisabledBit;
+ }
+}
+
+
+void Simulator::DisableStop(uint32_t code) {
+ ASSERT(isWatchedStop(code));
+ if (isEnabledStop(code)) {
+ watched_stops[code].count |= kStopDisabledBit;
+ }
+}
+
+
+void Simulator::IncreaseStopCounter(uint32_t code) {
+ ASSERT(code <= kMaxStopCode);
+ ASSERT(isWatchedStop(code));
+ if ((watched_stops[code].count & ~(1 << 31)) == 0x7fffffff) {
+ PrintF("Stop counter for code %i has overflowed.\n"
+ "Enabling this code and reseting the counter to 0.\n", code);
+ watched_stops[code].count = 0;
+ EnableStop(code);
+ } else {
+ watched_stops[code].count++;
+ }
+}
+
+
+// Print a stop status.
+void Simulator::PrintStopInfo(uint32_t code) {
+ ASSERT(code <= kMaxStopCode);
+ if (!isWatchedStop(code)) {
+ PrintF("Stop not watched.");
+ } else {
+ const char* state = isEnabledStop(code) ? "Enabled" : "Disabled";
+ int32_t count = watched_stops[code].count & ~kStopDisabledBit;
+ // Don't print the state of unused breakpoints.
+ if (count != 0) {
+ if (watched_stops[code].desc) {
+ PrintF("stop %i - 0x%x: \t%s, \tcounter = %i, \t%s\n",
+ code, code, state, count, watched_stops[code].desc);
+ } else {
+ PrintF("stop %i - 0x%x: \t%s, \tcounter = %i\n",
+ code, code, state, count);
+ }
}
}
}
@@ -2216,73 +2390,6 @@ void Simulator::DecodeType7(Instr* instr) {
}
-void Simulator::DecodeUnconditional(Instr* instr) {
- if (instr->Bits(7, 4) == 0x0B && instr->Bits(27, 25) == 0 && instr->HasL()) {
- // Load halfword instruction, either register or immediate offset.
- int rd = instr->RdField();
- int rn = instr->RnField();
- int32_t rn_val = get_register(rn);
- int32_t addr = 0;
- int32_t offset;
- if (instr->Bit(22) == 0) {
- // Register offset.
- int rm = instr->RmField();
- offset = get_register(rm);
- } else {
- // Immediate offset
- offset = instr->Bits(3, 0) + (instr->Bits(11, 8) << 4);
- }
- switch (instr->PUField()) {
- case 0: {
- // Post index, negative.
- ASSERT(!instr->HasW());
- addr = rn_val;
- rn_val -= offset;
- set_register(rn, rn_val);
- break;
- }
- case 1: {
- // Post index, positive.
- ASSERT(!instr->HasW());
- addr = rn_val;
- rn_val += offset;
- set_register(rn, rn_val);
- break;
- }
- case 2: {
- // Pre index or offset, negative.
- rn_val -= offset;
- addr = rn_val;
- if (instr->HasW()) {
- set_register(rn, rn_val);
- }
- break;
- }
- case 3: {
- // Pre index or offset, positive.
- rn_val += offset;
- addr = rn_val;
- if (instr->HasW()) {
- set_register(rn, rn_val);
- }
- break;
- }
- default: {
- // The PU field is a 2-bit field.
- UNREACHABLE();
- break;
- }
- }
- // Not sign extending, so load as unsigned.
- uint16_t halfword = ReadH(addr, instr);
- set_register(rd, halfword);
- } else {
- Debugger dbg(this);
- dbg.Stop(instr);
- }
-}
-
-
// void Simulator::DecodeTypeVFP(Instr* instr)
// The Following ARMv7 VFPv instructions are currently supported.
// vmov :Sn = Rt
@@ -2655,7 +2762,7 @@ void Simulator::InstructionDecode(Instr* instr) {
PrintF(" 0x%08x %s\n", reinterpret_cast<intptr_t>(instr), buffer.start());
}
if (instr->ConditionField() == special_condition) {
- DecodeUnconditional(instr);
+ UNIMPLEMENTED();
} else if (ConditionallyExecute(instr)) {
switch (instr->TypeField()) {
case 0:
diff --git a/src/arm/simulator-arm.h b/src/arm/simulator-arm.h
index e0658fc9..3e023489 100644
--- a/src/arm/simulator-arm.h
+++ b/src/arm/simulator-arm.h
@@ -226,6 +226,15 @@ class Simulator {
void HandleRList(Instr* instr, bool load);
void SoftwareInterrupt(Instr* instr);
+ // Stop helper functions.
+ inline bool isStopInstruction(Instr* instr);
+ inline bool isWatchedStop(uint32_t bkpt_code);
+ inline bool isEnabledStop(uint32_t bkpt_code);
+ inline void EnableStop(uint32_t bkpt_code);
+ inline void DisableStop(uint32_t bkpt_code);
+ inline void IncreaseStopCounter(uint32_t bkpt_code);
+ void PrintStopInfo(uint32_t code);
+
// Read and write memory.
inline uint8_t ReadBU(int32_t addr);
inline int8_t ReadB(int32_t addr);
@@ -252,7 +261,6 @@ class Simulator {
void DecodeType5(Instr* instr);
void DecodeType6(Instr* instr);
void DecodeType7(Instr* instr);
- void DecodeUnconditional(Instr* instr);
// Support for VFP.
void DecodeTypeVFP(Instr* instr);
@@ -317,6 +325,23 @@ class Simulator {
// Registered breakpoints.
Instr* break_pc_;
instr_t break_instr_;
+
+ // A stop is watched if its code is less than kNumOfWatchedStops.
+ // Only watched stops support enabling/disabling and the counter feature.
+ static const uint32_t kNumOfWatchedStops = 256;
+
+ // Breakpoint is disabled if bit 31 is set.
+ static const uint32_t kStopDisabledBit = 1 << 31;
+
+ // A stop is enabled, meaning the simulator will stop when meeting the
+ // instruction, if bit 31 of watched_stops[code].count is unset.
+ // The value watched_stops[code].count & ~(1 << 31) indicates how many times
+ // the breakpoint was hit or gone through.
+ struct StopCoundAndDesc {
+ uint32_t count;
+ char* desc;
+ };
+ StopCoundAndDesc watched_stops[kNumOfWatchedStops];
};
} } // namespace assembler::arm
diff --git a/src/arm/stub-cache-arm.cc b/src/arm/stub-cache-arm.cc
index fbad6698..5e29c2e4 100644
--- a/src/arm/stub-cache-arm.cc
+++ b/src/arm/stub-cache-arm.cc
@@ -43,43 +43,49 @@ static void ProbeTable(MacroAssembler* masm,
Code::Flags flags,
StubCache::Table table,
Register name,
- Register offset) {
+ Register offset,
+ Register scratch,
+ Register scratch2) {
ExternalReference key_offset(SCTableReference::keyReference(table));
ExternalReference value_offset(SCTableReference::valueReference(table));
- Label miss;
+ uint32_t key_off_addr = reinterpret_cast<uint32_t>(key_offset.address());
+ uint32_t value_off_addr = reinterpret_cast<uint32_t>(value_offset.address());
+
+ // Check the relative positions of the address fields.
+ ASSERT(value_off_addr > key_off_addr);
+ ASSERT((value_off_addr - key_off_addr) % 4 == 0);
+ ASSERT((value_off_addr - key_off_addr) < (256 * 4));
- // Save the offset on the stack.
- __ push(offset);
+ Label miss;
+ Register offsets_base_addr = scratch;
// Check that the key in the entry matches the name.
- __ mov(ip, Operand(key_offset));
- __ ldr(ip, MemOperand(ip, offset, LSL, 1));
+ __ mov(offsets_base_addr, Operand(key_offset));
+ __ ldr(ip, MemOperand(offsets_base_addr, offset, LSL, 1));
__ cmp(name, ip);
__ b(ne, &miss);
// Get the code entry from the cache.
- __ mov(ip, Operand(value_offset));
- __ ldr(offset, MemOperand(ip, offset, LSL, 1));
+ __ add(offsets_base_addr, offsets_base_addr,
+ Operand(value_off_addr - key_off_addr));
+ __ ldr(scratch2, MemOperand(offsets_base_addr, offset, LSL, 1));
// Check that the flags match what we're looking for.
- __ ldr(offset, FieldMemOperand(offset, Code::kFlagsOffset));
- __ and_(offset, offset, Operand(~Code::kFlagsNotUsedInLookup));
- __ cmp(offset, Operand(flags));
+ __ ldr(scratch2, FieldMemOperand(scratch2, Code::kFlagsOffset));
+ __ bic(scratch2, scratch2, Operand(Code::kFlagsNotUsedInLookup));
+ __ cmp(scratch2, Operand(flags));
__ b(ne, &miss);
- // Restore offset and re-load code entry from cache.
- __ pop(offset);
- __ mov(ip, Operand(value_offset));
- __ ldr(offset, MemOperand(ip, offset, LSL, 1));
+ // Re-load code entry from cache.
+ __ ldr(offset, MemOperand(offsets_base_addr, offset, LSL, 1));
// Jump to the first instruction in the code stub.
__ add(offset, offset, Operand(Code::kHeaderSize - kHeapObjectTag));
__ Jump(offset);
- // Miss: Restore offset and fall through.
+ // Miss: fall through.
__ bind(&miss);
- __ pop(offset);
}
@@ -201,7 +207,8 @@ void StubCache::GenerateProbe(MacroAssembler* masm,
Register receiver,
Register name,
Register scratch,
- Register extra) {
+ Register extra,
+ Register extra2) {
Label miss;
// Make sure that code is valid. The shifting code relies on the
@@ -214,6 +221,18 @@ void StubCache::GenerateProbe(MacroAssembler* masm,
// Make sure that there are no register conflicts.
ASSERT(!scratch.is(receiver));
ASSERT(!scratch.is(name));
+ ASSERT(!extra.is(receiver));
+ ASSERT(!extra.is(name));
+ ASSERT(!extra.is(scratch));
+ ASSERT(!extra2.is(receiver));
+ ASSERT(!extra2.is(name));
+ ASSERT(!extra2.is(scratch));
+ ASSERT(!extra2.is(extra));
+
+ // Check scratch, extra and extra2 registers are valid.
+ ASSERT(!scratch.is(no_reg));
+ ASSERT(!extra.is(no_reg));
+ ASSERT(!extra2.is(no_reg));
// Check that the receiver isn't a smi.
__ tst(receiver, Operand(kSmiTagMask));
@@ -229,7 +248,7 @@ void StubCache::GenerateProbe(MacroAssembler* masm,
Operand((kPrimaryTableSize - 1) << kHeapObjectTagSize));
// Probe the primary table.
- ProbeTable(masm, flags, kPrimary, name, scratch);
+ ProbeTable(masm, flags, kPrimary, name, scratch, extra, extra2);
// Primary miss: Compute hash for secondary probe.
__ sub(scratch, scratch, Operand(name));
@@ -239,7 +258,7 @@ void StubCache::GenerateProbe(MacroAssembler* masm,
Operand((kSecondaryTableSize - 1) << kHeapObjectTagSize));
// Probe the secondary table.
- ProbeTable(masm, flags, kSecondary, name, scratch);
+ ProbeTable(masm, flags, kSecondary, name, scratch, extra, extra2);
// Cache miss: Fall-through and let caller handle the miss by
// entering the runtime system.
diff --git a/src/assembler.cc b/src/assembler.cc
index ce90dcea..7493673e 100644
--- a/src/assembler.cc
+++ b/src/assembler.cc
@@ -804,4 +804,53 @@ ExternalReference ExternalReference::debug_step_in_fp_address() {
}
#endif
+
+void PositionsRecorder::RecordPosition(int pos,
+ PositionRecordingType recording_type) {
+ ASSERT(pos != RelocInfo::kNoPosition);
+ ASSERT(pos >= 0);
+ current_position_ = pos;
+ current_position_recording_type_ = recording_type;
+}
+
+
+void PositionsRecorder::RecordStatementPosition(int pos) {
+ ASSERT(pos != RelocInfo::kNoPosition);
+ ASSERT(pos >= 0);
+ current_statement_position_ = pos;
+}
+
+
+bool PositionsRecorder::WriteRecordedPositions() {
+ bool written = false;
+
+ // Write the statement position if it is different from what was written last
+ // time.
+ if (current_statement_position_ != written_statement_position_) {
+ EnsureSpace ensure_space(assembler_);
+ assembler_->RecordRelocInfo(RelocInfo::STATEMENT_POSITION,
+ current_statement_position_);
+ written_statement_position_ = current_statement_position_;
+ written = true;
+ }
+
+ // Write the position if it is different from what was written last time and
+ // also different from the written statement position or was forced.
+ if (current_position_ != written_position_ &&
+ (current_position_ != current_statement_position_ || !written) &&
+ (current_position_ != written_statement_position_
+ || current_position_recording_type_ == FORCED_POSITION)) {
+ EnsureSpace ensure_space(assembler_);
+ assembler_->RecordRelocInfo(RelocInfo::POSITION, current_position_);
+ written_position_ = current_position_;
+ written = true;
+ }
+
+ current_position_recording_type_ = NORMAL_POSITION;
+
+ // Return whether something was written.
+ return written;
+}
+
+
} } // namespace v8::internal
diff --git a/src/assembler.h b/src/assembler.h
index 66811777..09159fed 100644
--- a/src/assembler.h
+++ b/src/assembler.h
@@ -585,6 +585,67 @@ class ExternalReference BASE_EMBEDDED {
// -----------------------------------------------------------------------------
+// Position recording support
+
+enum PositionRecordingType { FORCED_POSITION, NORMAL_POSITION };
+
+class PositionsRecorder BASE_EMBEDDED {
+ public:
+ explicit PositionsRecorder(Assembler* assembler)
+ : assembler_(assembler),
+ current_position_(RelocInfo::kNoPosition),
+ current_position_recording_type_(NORMAL_POSITION),
+ written_position_(RelocInfo::kNoPosition),
+ current_statement_position_(RelocInfo::kNoPosition),
+ written_statement_position_(RelocInfo::kNoPosition) { }
+
+ // Set current position to pos. If recording_type is FORCED_POSITION then
+ // WriteRecordedPositions will write this position even if it is equal to
+ // statement position previously written for another pc.
+ void RecordPosition(int pos,
+ PositionRecordingType recording_type = NORMAL_POSITION);
+
+ // Set current statement position to pos.
+ void RecordStatementPosition(int pos);
+
+ // Write recorded positions to relocation information.
+ bool WriteRecordedPositions();
+
+ int current_position() const { return current_position_; }
+
+ int current_statement_position() const { return current_statement_position_; }
+
+ private:
+ Assembler* assembler_;
+
+ int current_position_;
+ PositionRecordingType current_position_recording_type_;
+ int written_position_;
+
+ int current_statement_position_;
+ int written_statement_position_;
+};
+
+
+class PreserveStatementPositionScope BASE_EMBEDDED {
+ public:
+ explicit PreserveStatementPositionScope(PositionsRecorder* positions_recorder)
+ : positions_recorder_(positions_recorder),
+ statement_position_(positions_recorder->current_statement_position()) {}
+
+ ~PreserveStatementPositionScope() {
+ if (statement_position_ != RelocInfo::kNoPosition) {
+ positions_recorder_->RecordStatementPosition(statement_position_);
+ }
+ }
+
+ private:
+ PositionsRecorder* positions_recorder_;
+ int statement_position_;
+};
+
+
+// -----------------------------------------------------------------------------
// Utility functions
static inline bool is_intn(int x, int n) {
diff --git a/src/ast.cc b/src/ast.cc
index 92f14961..bb445c4d 100644
--- a/src/ast.cc
+++ b/src/ast.cc
@@ -140,6 +140,7 @@ bool FunctionLiteral::AllowsLazyCompilation() {
ObjectLiteral::Property::Property(Literal* key, Expression* value) {
+ emit_store_ = true;
key_ = key;
value_ = value;
Object* k = *key->handle();
@@ -156,6 +157,7 @@ ObjectLiteral::Property::Property(Literal* key, Expression* value) {
ObjectLiteral::Property::Property(bool is_getter, FunctionLiteral* value) {
+ emit_store_ = true;
key_ = new Literal(value->name());
value_ = value;
kind_ = is_getter ? GETTER : SETTER;
@@ -169,6 +171,78 @@ bool ObjectLiteral::Property::IsCompileTimeValue() {
}
+void ObjectLiteral::Property::set_emit_store(bool emit_store) {
+ emit_store_ = emit_store;
+}
+
+
+bool ObjectLiteral::Property::emit_store() {
+ return emit_store_;
+}
+
+
+bool IsEqualString(void* first, void* second) {
+ Handle<String> h1(reinterpret_cast<String**>(first));
+ Handle<String> h2(reinterpret_cast<String**>(second));
+ return (*h1)->Equals(*h2);
+}
+
+bool IsEqualSmi(void* first, void* second) {
+ Handle<Smi> h1(reinterpret_cast<Smi**>(first));
+ Handle<Smi> h2(reinterpret_cast<Smi**>(second));
+ return (*h1)->value() == (*h2)->value();
+}
+
+void ObjectLiteral::CalculateEmitStore() {
+ HashMap properties(&IsEqualString);
+ HashMap elements(&IsEqualSmi);
+ for (int i = this->properties()->length() - 1; i >= 0; i--) {
+ ObjectLiteral::Property* property = this->properties()->at(i);
+ Literal* literal = property->key();
+ Handle<Object> handle = literal->handle();
+
+ if (handle->IsNull()) {
+ continue;
+ }
+
+ uint32_t hash;
+ HashMap* table;
+ void* key;
+ uint32_t index;
+ if (handle->IsSymbol()) {
+ Handle<String> name(String::cast(*handle));
+ ASSERT(!name->AsArrayIndex(&index));
+ key = name.location();
+ hash = name->Hash();
+ table = &properties;
+ } else if (handle->ToArrayIndex(&index)) {
+ key = handle.location();
+ hash = index;
+ table = &elements;
+ } else {
+ ASSERT(handle->IsNumber());
+ double num = handle->Number();
+ char arr[100];
+ Vector<char> buffer(arr, ARRAY_SIZE(arr));
+ const char* str = DoubleToCString(num, buffer);
+ Handle<String> name = Factory::NewStringFromAscii(CStrVector(str));
+ key = name.location();
+ hash = name->Hash();
+ table = &properties;
+ }
+ // If the key of a computed property is in the table, do not emit
+ // a store for the property later.
+ if (property->kind() == ObjectLiteral::Property::COMPUTED) {
+ if (table->Lookup(literal, hash, false) != NULL) {
+ property->set_emit_store(false);
+ }
+ }
+ // Add key to the table.
+ table->Lookup(literal, hash, true);
+ }
+}
+
+
void TargetCollector::AddTarget(BreakTarget* target) {
// Add the label to the collector, but discard duplicates.
int length = targets_->length();
diff --git a/src/ast.h b/src/ast.h
index a01e48da..04c29775 100644
--- a/src/ast.h
+++ b/src/ast.h
@@ -832,10 +832,14 @@ class ObjectLiteral: public MaterializedLiteral {
bool IsCompileTimeValue();
+ void set_emit_store(bool emit_store);
+ bool emit_store();
+
private:
Literal* key_;
Expression* value_;
Kind kind_;
+ bool emit_store_;
};
ObjectLiteral(Handle<FixedArray> constant_properties,
@@ -858,6 +862,12 @@ class ObjectLiteral: public MaterializedLiteral {
bool fast_elements() const { return fast_elements_; }
+
+ // Mark all computed expressions that are bound to a key that
+ // is shadowed by a later occurrence of the same key. For the
+ // marked expressions, no store code is emitted.
+ void CalculateEmitStore();
+
private:
Handle<FixedArray> constant_properties_;
ZoneList<Property*>* properties_;
diff --git a/src/builtins.cc b/src/builtins.cc
index 52d5530c..aede3020 100644
--- a/src/builtins.cc
+++ b/src/builtins.cc
@@ -1014,20 +1014,18 @@ MUST_USE_RESULT static MaybeObject* HandleApiCallHelper(
Object* data_obj = call_data->data();
Object* result;
- Handle<Object> data_handle(data_obj);
- v8::Local<v8::Value> data = v8::Utils::ToLocal(data_handle);
- ASSERT(raw_holder->IsJSObject());
- v8::Local<v8::Function> callee = v8::Utils::ToLocal(function);
- Handle<JSObject> holder_handle(JSObject::cast(raw_holder));
- v8::Local<v8::Object> holder = v8::Utils::ToLocal(holder_handle);
LOG(ApiObjectAccess("call", JSObject::cast(*args.receiver())));
+ ASSERT(raw_holder->IsJSObject());
+
+ CustomArguments custom;
+ v8::ImplementationUtilities::PrepareArgumentsData(custom.end(),
+ data_obj, *function, raw_holder);
+
v8::Arguments new_args = v8::ImplementationUtilities::NewArguments(
- data,
- holder,
- callee,
- is_construct,
- reinterpret_cast<void**>(&args[0] - 1),
- args.length() - 1);
+ custom.end(),
+ &args[0] - 1,
+ args.length() - 1,
+ is_construct);
v8::Handle<v8::Value> value;
{
@@ -1089,26 +1087,22 @@ BUILTIN(FastHandleApiCall) {
Handle<JSFunction> function = args.at<JSFunction>(args_length);
Object* callback_obj = args[args_length + 1];
- Handle<Object> data_handle = args.at<Object>(args_length + 2);
+ Handle<Object> data = args.at<Object>(args_length + 2);
Handle<JSObject> checked_holder = args.at<JSObject>(args_length + 3);
#ifdef DEBUG
VerifyTypeCheck(checked_holder, function);
#endif
- v8::Local<v8::Object> holder = v8::Utils::ToLocal(checked_holder);
- v8::Local<v8::Function> callee = v8::Utils::ToLocal(function);
- v8::InvocationCallback callback =
- v8::ToCData<v8::InvocationCallback>(callback_obj);
- v8::Local<v8::Value> data = v8::Utils::ToLocal(data_handle);
+ CustomArguments custom;
+ v8::ImplementationUtilities::PrepareArgumentsData(custom.end(),
+ *data, *function, *checked_holder);
v8::Arguments new_args = v8::ImplementationUtilities::NewArguments(
- data,
- holder,
- callee,
- is_construct,
- reinterpret_cast<void**>(&args[0] - 1),
- args_length - 1);
+ custom.end(),
+ &args[0] - 1,
+ args_length - 1,
+ is_construct);
HandleScope scope;
Object* result;
@@ -1119,6 +1113,9 @@ BUILTIN(FastHandleApiCall) {
#ifdef ENABLE_LOGGING_AND_PROFILING
state.set_external_callback(v8::ToCData<Address>(callback_obj));
#endif
+ v8::InvocationCallback callback =
+ v8::ToCData<v8::InvocationCallback>(callback_obj);
+
value = callback(new_args);
}
if (value.IsEmpty()) {
@@ -1161,23 +1158,20 @@ MUST_USE_RESULT static MaybeObject* HandleApiCallAsFunctionOrConstructor(
v8::ToCData<v8::InvocationCallback>(callback_obj);
// Get the data for the call and perform the callback.
- Object* data_obj = call_data->data();
Object* result;
- { HandleScope scope;
- v8::Local<v8::Object> self =
- v8::Utils::ToLocal(Handle<JSObject>::cast(args.receiver()));
- Handle<Object> data_handle(data_obj);
- v8::Local<v8::Value> data = v8::Utils::ToLocal(data_handle);
- Handle<JSFunction> callee_handle(constructor);
- v8::Local<v8::Function> callee = v8::Utils::ToLocal(callee_handle);
- LOG(ApiObjectAccess("call non-function", JSObject::cast(*args.receiver())));
+ {
+ HandleScope scope;
+
+ LOG(ApiObjectAccess("call non-function", obj));
+
+ CustomArguments custom;
+ v8::ImplementationUtilities::PrepareArgumentsData(custom.end(),
+ call_data->data(), constructor, obj);
v8::Arguments new_args = v8::ImplementationUtilities::NewArguments(
- data,
- self,
- callee,
- is_construct_call,
- reinterpret_cast<void**>(&args[0] - 1),
- args.length() - 1);
+ custom.end(),
+ &args[0] - 1,
+ args.length() - 1,
+ is_construct_call);
v8::Handle<v8::Value> value;
{
// Leaving JavaScript.
diff --git a/src/checks.cc b/src/checks.cc
index b5df316d..1ab8802e 100644
--- a/src/checks.cc
+++ b/src/checks.cc
@@ -98,3 +98,12 @@ void API_Fatal(const char* location, const char* format, ...) {
i::OS::PrintError("\n#\n\n");
i::OS::Abort();
}
+
+
+namespace v8 { namespace internal {
+
+ bool EnableSlowAsserts() { return FLAG_enable_slow_asserts; }
+
+ intptr_t HeapObjectTagMask() { return kHeapObjectTagMask; }
+
+} } // namespace v8::internal
diff --git a/src/checks.h b/src/checks.h
index 5ea59920..e0704774 100644
--- a/src/checks.h
+++ b/src/checks.h
@@ -30,7 +30,7 @@
#include <string.h>
-#include "flags.h"
+#include "../include/v8stdint.h"
extern "C" void V8_Fatal(const char* file, int line, const char* format, ...);
void API_Fatal(const char* location, const char* format, ...);
@@ -279,6 +279,12 @@ template <int> class StaticAssertionHelper { };
SEMI_STATIC_JOIN(__StaticAssertTypedef__, __LINE__)
+namespace v8 { namespace internal {
+
+bool EnableSlowAsserts();
+
+} } // namespace v8::internal
+
// The ASSERT macro is equivalent to CHECK except that it only
// generates code in debug builds.
#ifdef DEBUG
@@ -287,7 +293,7 @@ template <int> class StaticAssertionHelper { };
#define ASSERT_EQ(v1, v2) CHECK_EQ(v1, v2)
#define ASSERT_NE(v1, v2) CHECK_NE(v1, v2)
#define ASSERT_GE(v1, v2) CHECK_GE(v1, v2)
-#define SLOW_ASSERT(condition) if (FLAG_enable_slow_asserts) CHECK(condition)
+#define SLOW_ASSERT(condition) if (EnableSlowAsserts()) CHECK(condition)
#else
#define ASSERT_RESULT(expr) (expr)
#define ASSERT(condition) ((void) 0)
@@ -303,11 +309,16 @@ template <int> class StaticAssertionHelper { };
// and release compilation modes behaviour.
#define STATIC_ASSERT(test) STATIC_CHECK(test)
+namespace v8 { namespace internal {
+
+intptr_t HeapObjectTagMask();
+
+} } // namespace v8::internal
#define ASSERT_TAG_ALIGNED(address) \
- ASSERT((reinterpret_cast<intptr_t>(address) & kHeapObjectTagMask) == 0)
+ ASSERT((reinterpret_cast<intptr_t>(address) & HeapObjectTagMask()) == 0)
-#define ASSERT_SIZE_TAG_ALIGNED(size) ASSERT((size & kHeapObjectTagMask) == 0)
+#define ASSERT_SIZE_TAG_ALIGNED(size) ASSERT((size & HeapObjectTagMask()) == 0)
#define ASSERT_NOT_NULL(p) ASSERT_NE(NULL, p)
diff --git a/src/codegen.cc b/src/codegen.cc
index bda697ab..2e324180 100644
--- a/src/codegen.cc
+++ b/src/codegen.cc
@@ -70,9 +70,10 @@ void CodeGenerator::ProcessDeferred() {
DeferredCode* code = deferred_.RemoveLast();
ASSERT(masm_ == code->masm());
// Record position of deferred code stub.
- masm_->RecordStatementPosition(code->statement_position());
+ masm_->positions_recorder()->RecordStatementPosition(
+ code->statement_position());
if (code->position() != RelocInfo::kNoPosition) {
- masm_->RecordPosition(code->position());
+ masm_->positions_recorder()->RecordPosition(code->position());
}
// Generate the code.
Comment cmnt(masm_, code->comment());
@@ -402,10 +403,10 @@ bool CodeGenerator::RecordPositions(MacroAssembler* masm,
int pos,
bool right_here) {
if (pos != RelocInfo::kNoPosition) {
- masm->RecordStatementPosition(pos);
- masm->RecordPosition(pos);
+ masm->positions_recorder()->RecordStatementPosition(pos);
+ masm->positions_recorder()->RecordPosition(pos);
if (right_here) {
- return masm->WriteRecordedPositions();
+ return masm->positions_recorder()->WriteRecordedPositions();
}
}
return false;
@@ -435,7 +436,7 @@ void CodeGenerator::CodeForDoWhileConditionPosition(DoWhileStatement* stmt) {
void CodeGenerator::CodeForSourcePosition(int pos) {
if (FLAG_debug_info && pos != RelocInfo::kNoPosition) {
- masm()->RecordPosition(pos);
+ masm()->positions_recorder()->RecordPosition(pos);
}
}
diff --git a/src/compiler.cc b/src/compiler.cc
index 6cc09713..29bbbc70 100755
--- a/src/compiler.cc
+++ b/src/compiler.cc
@@ -152,10 +152,8 @@ static Handle<SharedFunctionInfo> MakeFunctionInfo(CompilationInfo* info) {
script->set_context_data((*i::Top::global_context())->data());
#ifdef ENABLE_DEBUGGER_SUPPORT
- if (info->is_eval() || info->is_json()) {
- Script::CompilationType compilation_type = info->is_json()
- ? Script::COMPILATION_TYPE_JSON
- : Script::COMPILATION_TYPE_EVAL;
+ if (info->is_eval()) {
+ Script::CompilationType compilation_type = Script::COMPILATION_TYPE_EVAL;
script->set_compilation_type(Smi::FromInt(compilation_type));
// For eval scripts add information on the function from which eval was
// called.
@@ -178,7 +176,7 @@ static Handle<SharedFunctionInfo> MakeFunctionInfo(CompilationInfo* info) {
// Only allow non-global compiles for eval.
ASSERT(info->is_eval() || info->is_global());
- if (!Parser::Parse(info)) return Handle<SharedFunctionInfo>::null();
+ if (!ParserApi::Parse(info)) return Handle<SharedFunctionInfo>::null();
// Measure how long it takes to do the compilation; only take the
// rest of the function into account to avoid overlap with the
@@ -281,9 +279,8 @@ Handle<SharedFunctionInfo> Compiler::Compile(Handle<String> source,
// in that case too.
ScriptDataImpl* pre_data = input_pre_data;
if (pre_data == NULL
- && FLAG_lazy
&& source_length >= FLAG_min_preparse_length) {
- pre_data = Parser::PartialPreParse(source, NULL, extension);
+ pre_data = ParserApi::PartialPreParse(source, NULL, extension);
}
// Create a script object describing the script to be compiled.
@@ -323,13 +320,7 @@ Handle<SharedFunctionInfo> Compiler::Compile(Handle<String> source,
Handle<SharedFunctionInfo> Compiler::CompileEval(Handle<String> source,
Handle<Context> context,
- bool is_global,
- ValidationState validate) {
- // Note that if validation is required then no path through this function
- // is allowed to return a value without validating that the input is legal
- // json.
- bool is_json = (validate == VALIDATE_JSON);
-
+ bool is_global) {
int source_length = source->length();
Counters::total_eval_size.Increment(source_length);
Counters::total_compile_size.Increment(source_length);
@@ -338,13 +329,9 @@ Handle<SharedFunctionInfo> Compiler::CompileEval(Handle<String> source,
VMState state(COMPILER);
// Do a lookup in the compilation cache; if the entry is not there, invoke
- // the compiler and add the result to the cache. If we're evaluating json
- // we bypass the cache since we can't be sure a potential value in the
- // cache has been validated.
+ // the compiler and add the result to the cache.
Handle<SharedFunctionInfo> result;
- if (!is_json) {
- result = CompilationCache::LookupEval(source, context, is_global);
- }
+ result = CompilationCache::LookupEval(source, context, is_global);
if (result.is_null()) {
// Create a script object describing the script to be compiled.
@@ -352,12 +339,9 @@ Handle<SharedFunctionInfo> Compiler::CompileEval(Handle<String> source,
CompilationInfo info(script);
info.MarkAsEval();
if (is_global) info.MarkAsGlobal();
- if (is_json) info.MarkAsJson();
info.SetCallingContext(context);
result = MakeFunctionInfo(&info);
- if (!result.is_null() && !is_json) {
- // For json it's unlikely that we'll ever see exactly the same string
- // again so we don't use the compilation cache.
+ if (!result.is_null()) {
CompilationCache::PutEval(source, context, is_global, result);
}
}
@@ -379,7 +363,7 @@ bool Compiler::CompileLazy(CompilationInfo* info) {
Counters::total_compile_size.Increment(compiled_size);
// Generate the AST for the lazily compiled function.
- if (Parser::Parse(info)) {
+ if (ParserApi::Parse(info)) {
// Measure how long it takes to do the lazy compilation; only take the
// rest of the function into account to avoid overlap with the lazy
// parsing statistics.
diff --git a/src/compiler.h b/src/compiler.h
index d6f4e69d..20868e54 100644
--- a/src/compiler.h
+++ b/src/compiler.h
@@ -49,7 +49,6 @@ class CompilationInfo BASE_EMBEDDED {
bool is_lazy() const { return (flags_ & IsLazy::mask()) != 0; }
bool is_eval() const { return (flags_ & IsEval::mask()) != 0; }
bool is_global() const { return (flags_ & IsGlobal::mask()) != 0; }
- bool is_json() const { return (flags_ & IsJson::mask()) != 0; }
bool is_in_loop() const { return (flags_ & IsInLoop::mask()) != 0; }
FunctionLiteral* function() const { return function_; }
Scope* scope() const { return scope_; }
@@ -69,10 +68,6 @@ class CompilationInfo BASE_EMBEDDED {
ASSERT(!is_lazy());
flags_ |= IsGlobal::encode(true);
}
- void MarkAsJson() {
- ASSERT(!is_lazy());
- flags_ |= IsJson::encode(true);
- }
void MarkAsInLoop() {
ASSERT(is_lazy());
flags_ |= IsInLoop::encode(true);
@@ -108,16 +103,15 @@ class CompilationInfo BASE_EMBEDDED {
// Flags that can be set for eager compilation.
class IsEval: public BitField<bool, 1, 1> {};
class IsGlobal: public BitField<bool, 2, 1> {};
- class IsJson: public BitField<bool, 3, 1> {};
// Flags that can be set for lazy compilation.
- class IsInLoop: public BitField<bool, 4, 1> {};
+ class IsInLoop: public BitField<bool, 3, 1> {};
unsigned flags_;
// Fields filled in by the compilation pipeline.
// AST filled in by the parser.
FunctionLiteral* function_;
- // The scope of the function literal as a convenience. Set to indidicate
+ // The scope of the function literal as a convenience. Set to indicate
// that scopes have been analyzed.
Scope* scope_;
// The compiled code.
@@ -153,8 +147,6 @@ class CompilationInfo BASE_EMBEDDED {
class Compiler : public AllStatic {
public:
- enum ValidationState { DONT_VALIDATE_JSON, VALIDATE_JSON };
-
// All routines return a JSFunction.
// If an error occurs an exception is raised and
// the return handle contains NULL.
@@ -172,8 +164,7 @@ class Compiler : public AllStatic {
// Compile a String source within a context for Eval.
static Handle<SharedFunctionInfo> CompileEval(Handle<String> source,
Handle<Context> context,
- bool is_global,
- ValidationState validation);
+ bool is_global);
// Compile from function info (used for lazy compilation). Returns true on
// success and false if the compilation resulted in a stack overflow.
diff --git a/src/conversions.cc b/src/conversions.cc
index 790e807a..4cc67448 100644
--- a/src/conversions.cc
+++ b/src/conversions.cc
@@ -816,7 +816,7 @@ const char* IntToCString(int n, Vector<char> buffer) {
char* DoubleToFixedCString(double value, int f) {
- const int kMaxDigitsBeforePoint = 20;
+ const int kMaxDigitsBeforePoint = 21;
const double kFirstNonFixed = 1e21;
const int kMaxDigitsAfterPoint = 20;
ASSERT(f >= 0);
@@ -840,9 +840,9 @@ char* DoubleToFixedCString(double value, int f) {
// Find a sufficiently precise decimal representation of n.
int decimal_point;
int sign;
- // Add space for the '.' and the '\0' byte.
+ // Add space for the '\0' byte.
const int kDecimalRepCapacity =
- kMaxDigitsBeforePoint + kMaxDigitsAfterPoint + 2;
+ kMaxDigitsBeforePoint + kMaxDigitsAfterPoint + 1;
char decimal_rep[kDecimalRepCapacity];
int decimal_rep_length;
bool status = DoubleToAscii(value, DTOA_FIXED, f,
diff --git a/src/debug-debugger.js b/src/debug-debugger.js
index a0c68081..d091991a 100644
--- a/src/debug-debugger.js
+++ b/src/debug-debugger.js
@@ -897,10 +897,6 @@ ExecutionState.prototype.frame = function(opt_index) {
return new FrameMirror(this.break_id, opt_index);
};
-ExecutionState.prototype.cframesValue = function(opt_from_index, opt_to_index) {
- return %GetCFrames(this.break_id);
-};
-
ExecutionState.prototype.setSelectedFrame = function(index) {
var i = %ToNumber(index);
if (i < 0 || i >= this.frameCount()) throw new Error('Illegal frame index.');
@@ -1301,7 +1297,7 @@ DebugCommandProcessor.prototype.processDebugJSONRequest = function(json_request)
try {
try {
// Convert the JSON string to an object.
- request = %CompileString('(' + json_request + ')', false)();
+ request = %CompileString('(' + json_request + ')')();
// Create an initial response.
response = this.createResponse(request);
@@ -1751,11 +1747,6 @@ DebugCommandProcessor.prototype.backtraceRequest_ = function(request, response)
};
-DebugCommandProcessor.prototype.backtracec = function(cmd, args) {
- return this.exec_state_.cframesValue();
-};
-
-
DebugCommandProcessor.prototype.frameRequest_ = function(request, response) {
// No frames no source.
if (this.exec_state_.frameCount() == 0) {
@@ -2205,29 +2196,6 @@ function NumberToHex8Str(n) {
return r;
};
-DebugCommandProcessor.prototype.formatCFrames = function(cframes_value) {
- var result = "";
- if (cframes_value == null || cframes_value.length == 0) {
- result += "(stack empty)";
- } else {
- for (var i = 0; i < cframes_value.length; ++i) {
- if (i != 0) result += "\n";
- result += this.formatCFrame(cframes_value[i]);
- }
- }
- return result;
-};
-
-
-DebugCommandProcessor.prototype.formatCFrame = function(cframe_value) {
- var result = "";
- result += "0x" + NumberToHex8Str(cframe_value.address);
- if (!IS_UNDEFINED(cframe_value.text)) {
- result += " " + cframe_value.text;
- }
- return result;
-}
-
/**
* Convert an Object to its debugger protocol representation. The representation
diff --git a/src/debug.cc b/src/debug.cc
index 5c6ddbe3..f3bf954d 100644
--- a/src/debug.cc
+++ b/src/debug.cc
@@ -1464,8 +1464,7 @@ bool Debug::IsSourceBreakStub(Code* code) {
// location.
bool Debug::IsBreakStub(Code* code) {
CodeStub::Major major_key = CodeStub::GetMajorKey(code);
- return major_key == CodeStub::CallFunction ||
- major_key == CodeStub::StackCheck;
+ return major_key == CodeStub::CallFunction;
}
@@ -1503,8 +1502,7 @@ Handle<Code> Debug::FindDebugBreak(Handle<Code> code, RelocInfo::Mode mode) {
return result;
}
if (code->kind() == Code::STUB) {
- ASSERT(code->major_key() == CodeStub::CallFunction ||
- code->major_key() == CodeStub::StackCheck);
+ ASSERT(code->major_key() == CodeStub::CallFunction);
Handle<Code> result =
Handle<Code>(Builtins::builtin(Builtins::StubNoRegisters_DebugBreak));
return result;
@@ -1841,6 +1839,7 @@ bool Debug::IsDebugGlobal(GlobalObject* global) {
void Debug::ClearMirrorCache() {
+ PostponeInterruptsScope postpone;
HandleScope scope;
ASSERT(Top::context() == *Debug::debug_context());
diff --git a/src/execution.cc b/src/execution.cc
index 3bbac0fa..885bf63c 100644
--- a/src/execution.cc
+++ b/src/execution.cc
@@ -797,6 +797,7 @@ v8::Handle<v8::Value> ExternalizeStringExtension::Externalize(
if (result && !string->IsSymbol()) {
i::ExternalStringTable::AddString(*string);
}
+ if (!result) delete resource;
} else {
uc16* data = new uc16[string->length()];
String::WriteToFlat(*string, data, 0, string->length());
@@ -806,6 +807,7 @@ v8::Handle<v8::Value> ExternalizeStringExtension::Externalize(
if (result && !string->IsSymbol()) {
i::ExternalStringTable::AddString(*string);
}
+ if (!result) delete resource;
}
if (!result) {
return v8::ThrowException(v8::String::New("externalizeString() failed."));
diff --git a/src/flag-definitions.h b/src/flag-definitions.h
index 2474c62b..54501ec9 100644
--- a/src/flag-definitions.h
+++ b/src/flag-definitions.h
@@ -140,6 +140,9 @@ DEFINE_bool(stack_trace_on_abort, true,
// codegen-ia32.cc / codegen-arm.cc
DEFINE_bool(trace, false, "trace function calls")
DEFINE_bool(defer_negation, true, "defer negation operation")
+DEFINE_bool(mask_constants_with_cookie,
+ true,
+ "use random jit cookie to mask large constants")
// codegen.cc
DEFINE_bool(lazy, true, "use lazy compilation")
diff --git a/src/full-codegen.cc b/src/full-codegen.cc
index 97987c27..c770e189 100644
--- a/src/full-codegen.cc
+++ b/src/full-codegen.cc
@@ -563,9 +563,10 @@ void FullCodeGenerator::SetStatementPosition(int pos) {
}
-void FullCodeGenerator::SetSourcePosition(int pos) {
+void FullCodeGenerator::SetSourcePosition(
+ int pos, PositionRecordingType recording_type) {
if (FLAG_debug_info && pos != RelocInfo::kNoPosition) {
- masm_->RecordPosition(pos);
+ masm_->positions_recorder()->RecordPosition(pos, recording_type);
}
}
@@ -1225,13 +1226,6 @@ int FullCodeGenerator::TryCatch::Exit(int stack_depth) {
}
-void FullCodeGenerator::EmitRegExpCloneResult(ZoneList<Expression*>* args) {
- ASSERT(args->length() == 1);
- VisitForStackValue(args->at(0));
- __ CallRuntime(Runtime::kRegExpCloneResult, 1);
- context()->Plug(result_register());
-}
-
#undef __
diff --git a/src/full-codegen.h b/src/full-codegen.h
index 201507b2..a3270aa7 100644
--- a/src/full-codegen.h
+++ b/src/full-codegen.h
@@ -423,7 +423,9 @@ class FullCodeGenerator: public AstVisitor {
void SetStatementPosition(Statement* stmt);
void SetExpressionPosition(Expression* expr, int pos);
void SetStatementPosition(int pos);
- void SetSourcePosition(int pos);
+ void SetSourcePosition(
+ int pos,
+ PositionRecordingType recording_type = NORMAL_POSITION);
// Non-local control flow support.
void EnterFinallyBlock();
diff --git a/src/global-handles.cc b/src/global-handles.cc
index 9ede9085..53398409 100644
--- a/src/global-handles.cc
+++ b/src/global-handles.cc
@@ -372,13 +372,14 @@ void GlobalHandles::IdentifyWeakHandles(WeakSlotCallback f) {
int post_gc_processing_count = 0;
-void GlobalHandles::PostGarbageCollectionProcessing() {
+bool GlobalHandles::PostGarbageCollectionProcessing() {
// Process weak global handle callbacks. This must be done after the
// GC is completely done, because the callbacks may invoke arbitrary
// API functions.
// At the same time deallocate all DESTROYED nodes.
ASSERT(Heap::gc_state() == Heap::NOT_IN_GC);
const int initial_post_gc_processing_count = ++post_gc_processing_count;
+ bool next_gc_likely_to_collect_more = false;
Node** p = &head_;
while (*p != NULL) {
if ((*p)->PostGarbageCollectionProcessing()) {
@@ -399,6 +400,7 @@ void GlobalHandles::PostGarbageCollectionProcessing() {
}
node->set_next_free(first_deallocated());
set_first_deallocated(node);
+ next_gc_likely_to_collect_more = true;
} else {
p = (*p)->next_addr();
}
@@ -407,6 +409,8 @@ void GlobalHandles::PostGarbageCollectionProcessing() {
if (first_deallocated()) {
first_deallocated()->set_next(head());
}
+
+ return next_gc_likely_to_collect_more;
}
diff --git a/src/global-handles.h b/src/global-handles.h
index 659f86ec..37b2b445 100644
--- a/src/global-handles.h
+++ b/src/global-handles.h
@@ -96,7 +96,8 @@ class GlobalHandles : public AllStatic {
static bool IsWeak(Object** location);
// Process pending weak handles.
- static void PostGarbageCollectionProcessing();
+ // Returns true if next major GC is likely to collect more garbage.
+ static bool PostGarbageCollectionProcessing();
// Iterates over all strong handles.
static void IterateStrongRoots(ObjectVisitor* v);
diff --git a/src/globals.h b/src/globals.h
index c218f80d..a74b6c79 100644
--- a/src/globals.h
+++ b/src/globals.h
@@ -193,10 +193,9 @@ const uint32_t kMaxUInt32 = 0xFFFFFFFFu;
const int kCharSize = sizeof(char); // NOLINT
const int kShortSize = sizeof(short); // NOLINT
-const int kIntSize = sizeof(int); // NOLINT
const int kDoubleSize = sizeof(double); // NOLINT
-const int kPointerSize = sizeof(void*); // NOLINT
const int kIntptrSize = sizeof(intptr_t); // NOLINT
+// kIntSize and kPointerSize are defined in include/v8.h.
#if V8_HOST_ARCH_64_BIT
const int kPointerSizeLog2 = 3;
diff --git a/src/heap-inl.h b/src/heap-inl.h
index 15feb9d5..ba50c0f7 100644
--- a/src/heap-inl.h
+++ b/src/heap-inl.h
@@ -330,6 +330,11 @@ void Heap::ScavengeObject(HeapObject** p, HeapObject* object) {
}
+bool Heap::CollectGarbage(AllocationSpace space) {
+ return CollectGarbage(space, SelectGarbageCollector(space));
+}
+
+
MaybeObject* Heap::PrepareForCompare(String* str) {
// Always flatten small strings and force flattening of long strings
// after we have accumulated a certain amount we failed to flatten.
@@ -413,7 +418,7 @@ void Heap::SetLastScriptId(Object* last_script_id) {
} \
if (!__maybe_object__->IsRetryAfterGC()) RETURN_EMPTY; \
Counters::gc_last_resort_from_handles.Increment(); \
- Heap::CollectAllGarbage(false); \
+ Heap::CollectAllAvailableGarbage(); \
{ \
AlwaysAllocateScope __scope__; \
__maybe_object__ = FUNCTION_CALL; \
diff --git a/src/heap.cc b/src/heap.cc
index fc908665..226a2022 100644
--- a/src/heap.cc
+++ b/src/heap.cc
@@ -429,7 +429,31 @@ void Heap::CollectAllGarbage(bool force_compaction) {
}
-void Heap::CollectGarbage(AllocationSpace space) {
+void Heap::CollectAllAvailableGarbage() {
+ // Since we are ignoring the return value, the exact choice of space does
+ // not matter, so long as we do not specify NEW_SPACE, which would not
+ // cause a full GC.
+ MarkCompactCollector::SetForceCompaction(true);
+
+ // Major GC would invoke weak handle callbacks on weakly reachable
+ // handles, but won't collect weakly reachable objects until next
+ // major GC. Therefore if we collect aggressively and weak handle callback
+ // has been invoked, we rerun major GC to release objects which become
+ // garbage.
+ // Note: as weak callbacks can execute arbitrary code, we cannot
+ // hope that eventually there will be no weak callbacks invocations.
+ // Therefore stop recollecting after several attempts.
+ const int kMaxNumberOfAttempts = 7;
+ for (int attempt = 0; attempt < kMaxNumberOfAttempts; attempt++) {
+ if (!CollectGarbage(OLD_POINTER_SPACE, MARK_COMPACTOR)) {
+ break;
+ }
+ }
+ MarkCompactCollector::SetForceCompaction(false);
+}
+
+
+bool Heap::CollectGarbage(AllocationSpace space, GarbageCollector collector) {
// The VM is in the GC state until exiting this function.
VMState state(GC);
@@ -442,13 +466,14 @@ void Heap::CollectGarbage(AllocationSpace space) {
allocation_timeout_ = Max(6, FLAG_gc_interval);
#endif
+ bool next_gc_likely_to_collect_more = false;
+
{ GCTracer tracer;
GarbageCollectionPrologue();
// The GC count was incremented in the prologue. Tell the tracer about
// it.
tracer.set_gc_count(gc_count_);
- GarbageCollector collector = SelectGarbageCollector(space);
// Tell the tracer which collector we've selected.
tracer.set_collector(collector);
@@ -456,7 +481,8 @@ void Heap::CollectGarbage(AllocationSpace space) {
? &Counters::gc_scavenger
: &Counters::gc_compactor;
rate->Start();
- PerformGarbageCollection(collector, &tracer);
+ next_gc_likely_to_collect_more =
+ PerformGarbageCollection(collector, &tracer);
rate->Stop();
GarbageCollectionEpilogue();
@@ -467,6 +493,8 @@ void Heap::CollectGarbage(AllocationSpace space) {
if (FLAG_log_gc) HeapProfiler::WriteSample();
if (CpuProfiler::is_profiling()) CpuProfiler::ProcessMovedFunctions();
#endif
+
+ return next_gc_likely_to_collect_more;
}
@@ -581,25 +609,22 @@ void Heap::EnsureFromSpaceIsCommitted() {
}
-class ClearThreadJSFunctionResultCachesVisitor: public ThreadVisitor {
- virtual void VisitThread(ThreadLocalTop* top) {
- Context* context = top->context_;
- if (context == NULL) return;
+void Heap::ClearJSFunctionResultCaches() {
+ if (Bootstrapper::IsActive()) return;
+ Object* context = global_contexts_list_;
+ while (!context->IsUndefined()) {
+ // Get the caches for this context:
FixedArray* caches =
- context->global()->global_context()->jsfunction_result_caches();
+ Context::cast(context)->jsfunction_result_caches();
+ // Clear the caches:
int length = caches->length();
for (int i = 0; i < length; i++) {
JSFunctionResultCache::cast(caches->get(i))->Clear();
}
+ // Get the next context:
+ context = Context::cast(context)->get(Context::NEXT_CONTEXT_LINK);
}
-};
-
-
-void Heap::ClearJSFunctionResultCaches() {
- if (Bootstrapper::IsActive()) return;
- ClearThreadJSFunctionResultCachesVisitor visitor;
- ThreadManager::IterateArchivedThreads(&visitor);
}
@@ -656,8 +681,10 @@ void Heap::UpdateSurvivalRateTrend(int start_new_space_size) {
survival_rate_ = survival_rate;
}
-void Heap::PerformGarbageCollection(GarbageCollector collector,
+bool Heap::PerformGarbageCollection(GarbageCollector collector,
GCTracer* tracer) {
+ bool next_gc_likely_to_collect_more = false;
+
if (collector != SCAVENGER) {
PROFILE(CodeMovingGCEvent());
}
@@ -723,7 +750,8 @@ void Heap::PerformGarbageCollection(GarbageCollector collector,
if (collector == MARK_COMPACTOR) {
DisableAssertNoAllocation allow_allocation;
GCTracer::Scope scope(tracer, GCTracer::Scope::EXTERNAL);
- GlobalHandles::PostGarbageCollectionProcessing();
+ next_gc_likely_to_collect_more =
+ GlobalHandles::PostGarbageCollectionProcessing();
}
// Update relocatables.
@@ -750,6 +778,8 @@ void Heap::PerformGarbageCollection(GarbageCollector collector,
global_gc_epilogue_callback_();
}
VerifySymbolTable();
+
+ return next_gc_likely_to_collect_more;
}
diff --git a/src/heap.h b/src/heap.h
index 8ff2f5f3..714bf0dc 100644
--- a/src/heap.h
+++ b/src/heap.h
@@ -705,13 +705,22 @@ class Heap : public AllStatic {
static void GarbageCollectionEpilogue();
// Performs garbage collection operation.
- // Returns whether required_space bytes are available after the collection.
- static void CollectGarbage(AllocationSpace space);
+ // Returns whether there is a chance that another major GC could
+ // collect more garbage.
+ static bool CollectGarbage(AllocationSpace space, GarbageCollector collector);
+
+ // Performs garbage collection operation.
+ // Returns whether there is a chance that another major GC could
+ // collect more garbage.
+ inline static bool CollectGarbage(AllocationSpace space);
// Performs a full garbage collection. Force compaction if the
// parameter is true.
static void CollectAllGarbage(bool force_compaction);
+ // Last hope GC, should try to squeeze as much as possible.
+ static void CollectAllAvailableGarbage();
+
// Notify the heap that a context has been disposed.
static int NotifyContextDisposed() { return ++contexts_disposed_; }
@@ -1246,7 +1255,9 @@ class Heap : public AllStatic {
static GarbageCollector SelectGarbageCollector(AllocationSpace space);
// Performs garbage collection
- static void PerformGarbageCollection(GarbageCollector collector,
+ // Returns whether there is a chance another major GC could
+ // collect more garbage.
+ static bool PerformGarbageCollection(GarbageCollector collector,
GCTracer* tracer);
// Allocate an uninitialized object in map space. The behavior is identical
diff --git a/src/ia32/assembler-ia32.cc b/src/ia32/assembler-ia32.cc
index 019f478a..125f503b 100644
--- a/src/ia32/assembler-ia32.cc
+++ b/src/ia32/assembler-ia32.cc
@@ -298,7 +298,8 @@ static void InitCoverageLog();
// Spare buffer.
byte* Assembler::spare_buffer_ = NULL;
-Assembler::Assembler(void* buffer, int buffer_size) {
+Assembler::Assembler(void* buffer, int buffer_size)
+ : positions_recorder_(this) {
if (buffer == NULL) {
// Do our own buffer management.
if (buffer_size <= kMinimalBufferSize) {
@@ -339,10 +340,6 @@ Assembler::Assembler(void* buffer, int buffer_size) {
reloc_info_writer.Reposition(buffer_ + buffer_size, pc_);
last_pc_ = NULL;
- current_statement_position_ = RelocInfo::kNoPosition;
- current_position_ = RelocInfo::kNoPosition;
- written_statement_position_ = current_statement_position_;
- written_position_ = current_position_;
#ifdef GENERATED_CODE_COVERAGE
InitCoverageLog();
#endif
@@ -1581,7 +1578,7 @@ void Assembler::call(const Operand& adr) {
void Assembler::call(Handle<Code> code, RelocInfo::Mode rmode) {
- WriteRecordedPositions();
+ positions_recorder()->WriteRecordedPositions();
EnsureSpace ensure_space(this);
last_pc_ = pc_;
ASSERT(RelocInfo::IsCodeTarget(rmode));
@@ -2464,14 +2461,14 @@ void Assembler::Print() {
void Assembler::RecordJSReturn() {
- WriteRecordedPositions();
+ positions_recorder()->WriteRecordedPositions();
EnsureSpace ensure_space(this);
RecordRelocInfo(RelocInfo::JS_RETURN);
}
void Assembler::RecordDebugBreakSlot() {
- WriteRecordedPositions();
+ positions_recorder()->WriteRecordedPositions();
EnsureSpace ensure_space(this);
RecordRelocInfo(RelocInfo::DEBUG_BREAK_SLOT);
}
@@ -2485,47 +2482,6 @@ void Assembler::RecordComment(const char* msg) {
}
-void Assembler::RecordPosition(int pos) {
- ASSERT(pos != RelocInfo::kNoPosition);
- ASSERT(pos >= 0);
- current_position_ = pos;
-}
-
-
-void Assembler::RecordStatementPosition(int pos) {
- ASSERT(pos != RelocInfo::kNoPosition);
- ASSERT(pos >= 0);
- current_statement_position_ = pos;
-}
-
-
-bool Assembler::WriteRecordedPositions() {
- bool written = false;
-
- // Write the statement position if it is different from what was written last
- // time.
- if (current_statement_position_ != written_statement_position_) {
- EnsureSpace ensure_space(this);
- RecordRelocInfo(RelocInfo::STATEMENT_POSITION, current_statement_position_);
- written_statement_position_ = current_statement_position_;
- written = true;
- }
-
- // Write the position if it is different from what was written last time and
- // also different from the written statement position.
- if (current_position_ != written_position_ &&
- current_position_ != written_statement_position_) {
- EnsureSpace ensure_space(this);
- RecordRelocInfo(RelocInfo::POSITION, current_position_);
- written_position_ = current_position_;
- written = true;
- }
-
- // Return whether something was written.
- return written;
-}
-
-
void Assembler::GrowBuffer() {
ASSERT(overflow());
if (!own_buffer_) FATAL("external code buffer is too small");
diff --git a/src/ia32/assembler-ia32.h b/src/ia32/assembler-ia32.h
index 5286788f..624be0c3 100644
--- a/src/ia32/assembler-ia32.h
+++ b/src/ia32/assembler-ia32.h
@@ -847,17 +847,11 @@ class Assembler : public Malloced {
// Use --debug_code to enable.
void RecordComment(const char* msg);
- void RecordPosition(int pos);
- void RecordStatementPosition(int pos);
- bool WriteRecordedPositions();
-
// Writes a single word of data in the code stream.
// Used for inline tables, e.g., jump-tables.
void dd(uint32_t data, RelocInfo::Mode reloc_info);
int pc_offset() const { return pc_ - buffer_; }
- int current_statement_position() const { return current_statement_position_; }
- int current_position() const { return current_position_; }
// Check if there is less than kGap bytes available in the buffer.
// If this is the case, we need to grow the buffer before emitting
@@ -869,6 +863,8 @@ class Assembler : public Malloced {
static bool IsNop(Address addr) { return *addr == 0x90; }
+ PositionsRecorder* positions_recorder() { return &positions_recorder_; }
+
// Avoid overflows for displacements etc.
static const int kMaximalBufferSize = 512*MB;
static const int kMinimalBufferSize = 4*KB;
@@ -947,11 +943,9 @@ class Assembler : public Malloced {
// push-pop elimination
byte* last_pc_;
- // source position information
- int current_statement_position_;
- int current_position_;
- int written_statement_position_;
- int written_position_;
+ PositionsRecorder positions_recorder_;
+
+ friend class PositionsRecorder;
};
diff --git a/src/ia32/codegen-ia32.cc b/src/ia32/codegen-ia32.cc
index f2ac7f70..72953407 100644
--- a/src/ia32/codegen-ia32.cc
+++ b/src/ia32/codegen-ia32.cc
@@ -153,7 +153,8 @@ CodeGenerator::CodeGenerator(MacroAssembler* masm)
in_safe_int32_mode_(false),
safe_int32_mode_enabled_(true),
function_return_is_shadowed_(false),
- in_spilled_code_(false) {
+ in_spilled_code_(false),
+ jit_cookie_((FLAG_mask_constants_with_cookie) ? V8::Random() : 0) {
}
@@ -3733,7 +3734,7 @@ void CodeGenerator::VisitReturnStatement(ReturnStatement* node) {
CodeForStatementPosition(node);
Load(node->expression());
Result return_value = frame_->Pop();
- masm()->WriteRecordedPositions();
+ masm()->positions_recorder()->WriteRecordedPositions();
if (function_return_is_shadowed_) {
function_return_.Jump(&return_value);
} else {
@@ -5363,16 +5364,16 @@ void CodeGenerator::VisitLiteral(Literal* node) {
void CodeGenerator::PushUnsafeSmi(Handle<Object> value) {
ASSERT(value->IsSmi());
int bits = reinterpret_cast<int>(*value);
- __ push(Immediate(bits & 0x0000FFFF));
- __ or_(Operand(esp, 0), Immediate(bits & 0xFFFF0000));
+ __ push(Immediate(bits ^ jit_cookie_));
+ __ xor_(Operand(esp, 0), Immediate(jit_cookie_));
}
void CodeGenerator::StoreUnsafeSmiToLocal(int offset, Handle<Object> value) {
ASSERT(value->IsSmi());
int bits = reinterpret_cast<int>(*value);
- __ mov(Operand(ebp, offset), Immediate(bits & 0x0000FFFF));
- __ or_(Operand(ebp, offset), Immediate(bits & 0xFFFF0000));
+ __ mov(Operand(ebp, offset), Immediate(bits ^ jit_cookie_));
+ __ xor_(Operand(ebp, offset), Immediate(jit_cookie_));
}
@@ -5380,8 +5381,8 @@ void CodeGenerator::MoveUnsafeSmi(Register target, Handle<Object> value) {
ASSERT(target.is_valid());
ASSERT(value->IsSmi());
int bits = reinterpret_cast<int>(*value);
- __ Set(target, Immediate(bits & 0x0000FFFF));
- __ or_(target, bits & 0xFFFF0000);
+ __ Set(target, Immediate(bits ^ jit_cookie_));
+ __ xor_(target, jit_cookie_);
}
@@ -5559,6 +5560,11 @@ void CodeGenerator::VisitObjectLiteral(ObjectLiteral* node) {
}
frame_->Push(&clone);
+ // Mark all computed expressions that are bound to a key that
+ // is shadowed by a later occurrence of the same key. For the
+ // marked expressions, no store code is emitted.
+ node->CalculateEmitStore();
+
for (int i = 0; i < node->properties()->length(); i++) {
ObjectLiteral::Property* property = node->properties()->at(i);
switch (property->kind()) {
@@ -5573,24 +5579,32 @@ void CodeGenerator::VisitObjectLiteral(ObjectLiteral* node) {
// Duplicate the object as the IC receiver.
frame_->Dup();
Load(property->value());
- Result ignored =
- frame_->CallStoreIC(Handle<String>::cast(key), false);
- // A test eax instruction following the store IC call would
- // indicate the presence of an inlined version of the
- // store. Add a nop to indicate that there is no such
- // inlined version.
- __ nop();
+ if (property->emit_store()) {
+ Result ignored =
+ frame_->CallStoreIC(Handle<String>::cast(key), false);
+ // A test eax instruction following the store IC call would
+ // indicate the presence of an inlined version of the
+ // store. Add a nop to indicate that there is no such
+ // inlined version.
+ __ nop();
+ } else {
+ frame_->Drop(2);
+ }
break;
}
// Fall through
}
case ObjectLiteral::Property::PROTOTYPE: {
- // Duplicate the object as an argument to the runtime call.
- frame_->Dup();
- Load(property->key());
- Load(property->value());
- Result ignored = frame_->CallRuntime(Runtime::kSetProperty, 3);
- // Ignore the result.
+ // Duplicate the object as an argument to the runtime call.
+ frame_->Dup();
+ Load(property->key());
+ Load(property->value());
+ if (property->emit_store()) {
+ // Ignore the result.
+ Result ignored = frame_->CallRuntime(Runtime::kSetProperty, 3);
+ } else {
+ frame_->Drop(3);
+ }
break;
}
case ObjectLiteral::Property::SETTER: {
@@ -7278,88 +7292,6 @@ void CodeGenerator::GenerateRegExpConstructResult(ZoneList<Expression*>* args) {
}
-void CodeGenerator::GenerateRegExpCloneResult(ZoneList<Expression*>* args) {
- ASSERT_EQ(1, args->length());
-
- Load(args->at(0));
- Result object_result = frame_->Pop();
- object_result.ToRegister(eax);
- object_result.Unuse();
- {
- VirtualFrame::SpilledScope spilled_scope;
-
- Label done;
-
- __ test(eax, Immediate(kSmiTagMask));
- __ j(zero, &done);
-
- // Load JSRegExpResult map into edx.
- // Arguments to this function should be results of calling RegExp exec,
- // which is either an unmodified JSRegExpResult or null. Anything not having
- // the unmodified JSRegExpResult map is returned unmodified.
- // This also ensures that elements are fast.
- __ mov(edx, ContextOperand(esi, Context::GLOBAL_INDEX));
- __ mov(edx, FieldOperand(edx, GlobalObject::kGlobalContextOffset));
- __ mov(edx, ContextOperand(edx, Context::REGEXP_RESULT_MAP_INDEX));
- __ cmp(edx, FieldOperand(eax, HeapObject::kMapOffset));
- __ j(not_equal, &done);
-
- if (FLAG_debug_code) {
- // Check that object really has empty properties array, as the map
- // should guarantee.
- __ cmp(FieldOperand(eax, JSObject::kPropertiesOffset),
- Immediate(Factory::empty_fixed_array()));
- __ Check(equal, "JSRegExpResult: default map but non-empty properties.");
- }
-
- DeferredAllocateInNewSpace* allocate_fallback =
- new DeferredAllocateInNewSpace(JSRegExpResult::kSize,
- ebx,
- edx.bit() | eax.bit());
-
- // All set, copy the contents to a new object.
- __ AllocateInNewSpace(JSRegExpResult::kSize,
- ebx,
- ecx,
- no_reg,
- allocate_fallback->entry_label(),
- TAG_OBJECT);
- __ bind(allocate_fallback->exit_label());
-
- // Copy all fields from eax to ebx.
- STATIC_ASSERT(JSRegExpResult::kSize % (2 * kPointerSize) == 0);
- // There is an even number of fields, so unroll the loop once
- // for efficiency.
- for (int i = 0; i < JSRegExpResult::kSize; i += 2 * kPointerSize) {
- STATIC_ASSERT(JSObject::kMapOffset % (2 * kPointerSize) == 0);
- if (i != JSObject::kMapOffset) {
- // The map was already loaded into edx.
- __ mov(edx, FieldOperand(eax, i));
- }
- __ mov(ecx, FieldOperand(eax, i + kPointerSize));
-
- STATIC_ASSERT(JSObject::kElementsOffset % (2 * kPointerSize) == 0);
- if (i == JSObject::kElementsOffset) {
- // If the elements array isn't empty, make it copy-on-write
- // before copying it.
- Label empty;
- __ cmp(Operand(edx), Immediate(Factory::empty_fixed_array()));
- __ j(equal, &empty);
- __ mov(FieldOperand(edx, HeapObject::kMapOffset),
- Immediate(Factory::fixed_cow_array_map()));
- __ bind(&empty);
- }
- __ mov(FieldOperand(ebx, i), edx);
- __ mov(FieldOperand(ebx, i + kPointerSize), ecx);
- }
- __ mov(eax, ebx);
-
- __ bind(&done);
- }
- frame_->Push(eax);
-}
-
-
class DeferredSearchCache: public DeferredCode {
public:
DeferredSearchCache(Register dst, Register cache, Register key)
diff --git a/src/ia32/codegen-ia32.h b/src/ia32/codegen-ia32.h
index b0724092..5a12e10e 100644
--- a/src/ia32/codegen-ia32.h
+++ b/src/ia32/codegen-ia32.h
@@ -697,11 +697,6 @@ class CodeGenerator: public AstVisitor {
// Construct a RegExp exec result with two in-object properties.
void GenerateRegExpConstructResult(ZoneList<Expression*>* args);
- // Clone the result of a regexp function.
- // Must be an object created by GenerateRegExpConstructResult with
- // no extra properties.
- void GenerateRegExpCloneResult(ZoneList<Expression*>* args);
-
// Support for fast native caches.
void GenerateGetFromCache(ZoneList<Expression*>* args);
@@ -785,6 +780,11 @@ class CodeGenerator: public AstVisitor {
// in a spilled state.
bool in_spilled_code_;
+ // A cookie that is used for JIT IMM32 Encoding. Initialized to a
+ // random number when the command-line
+ // FLAG_mask_constants_with_cookie is true, zero otherwise.
+ int jit_cookie_;
+
friend class VirtualFrame;
friend class JumpTarget;
friend class Reference;
diff --git a/src/ia32/full-codegen-ia32.cc b/src/ia32/full-codegen-ia32.cc
index 150df995..1ea719d7 100644
--- a/src/ia32/full-codegen-ia32.cc
+++ b/src/ia32/full-codegen-ia32.cc
@@ -1202,6 +1202,11 @@ void FullCodeGenerator::VisitObjectLiteral(ObjectLiteral* expr) {
// result_saved is false the result is in eax.
bool result_saved = false;
+ // Mark all computed expressions that are bound to a key that
+ // is shadowed by a later occurrence of the same key. For the
+ // marked expressions, no store code is emitted.
+ expr->CalculateEmitStore();
+
for (int i = 0; i < expr->properties()->length(); i++) {
ObjectLiteral::Property* property = expr->properties()->at(i);
if (property->IsCompileTimeValue()) continue;
@@ -1221,8 +1226,10 @@ void FullCodeGenerator::VisitObjectLiteral(ObjectLiteral* expr) {
VisitForAccumulatorValue(value);
__ mov(ecx, Immediate(key->handle()));
__ mov(edx, Operand(esp, 0));
- Handle<Code> ic(Builtins::builtin(Builtins::StoreIC_Initialize));
- EmitCallIC(ic, RelocInfo::CODE_TARGET);
+ if (property->emit_store()) {
+ Handle<Code> ic(Builtins::builtin(Builtins::StoreIC_Initialize));
+ EmitCallIC(ic, RelocInfo::CODE_TARGET);
+ }
break;
}
// Fall through.
@@ -1230,7 +1237,11 @@ void FullCodeGenerator::VisitObjectLiteral(ObjectLiteral* expr) {
__ push(Operand(esp, 0)); // Duplicate receiver.
VisitForStackValue(key);
VisitForStackValue(value);
- __ CallRuntime(Runtime::kSetProperty, 3);
+ if (property->emit_store()) {
+ __ CallRuntime(Runtime::kSetProperty, 3);
+ } else {
+ __ Drop(3);
+ }
break;
case ObjectLiteral::Property::SETTER:
case ObjectLiteral::Property::GETTER:
@@ -1985,12 +1996,14 @@ void FullCodeGenerator::EmitCallWithIC(Call* expr,
// Code common for calls using the IC.
ZoneList<Expression*>* args = expr->arguments();
int arg_count = args->length();
- for (int i = 0; i < arg_count; i++) {
- VisitForStackValue(args->at(i));
+ { PreserveStatementPositionScope scope(masm()->positions_recorder());
+ for (int i = 0; i < arg_count; i++) {
+ VisitForStackValue(args->at(i));
+ }
+ __ Set(ecx, Immediate(name));
}
- __ Set(ecx, Immediate(name));
// Record source position of the IC call.
- SetSourcePosition(expr->position());
+ SetSourcePosition(expr->position(), FORCED_POSITION);
InLoopFlag in_loop = (loop_depth() > 0) ? IN_LOOP : NOT_IN_LOOP;
Handle<Code> ic = CodeGenerator::ComputeCallInitialize(arg_count, in_loop);
EmitCallIC(ic, mode);
@@ -2006,13 +2019,15 @@ void FullCodeGenerator::EmitKeyedCallWithIC(Call* expr,
// Code common for calls using the IC.
ZoneList<Expression*>* args = expr->arguments();
int arg_count = args->length();
- for (int i = 0; i < arg_count; i++) {
- VisitForStackValue(args->at(i));
+ { PreserveStatementPositionScope scope(masm()->positions_recorder());
+ for (int i = 0; i < arg_count; i++) {
+ VisitForStackValue(args->at(i));
+ }
+ VisitForAccumulatorValue(key);
+ __ mov(ecx, eax);
}
- VisitForAccumulatorValue(key);
- __ mov(ecx, eax);
// Record source position of the IC call.
- SetSourcePosition(expr->position());
+ SetSourcePosition(expr->position(), FORCED_POSITION);
InLoopFlag in_loop = (loop_depth() > 0) ? IN_LOOP : NOT_IN_LOOP;
Handle<Code> ic = CodeGenerator::ComputeKeyedCallInitialize(
arg_count, in_loop);
@@ -2027,11 +2042,13 @@ void FullCodeGenerator::EmitCallWithStub(Call* expr) {
// Code common for calls using the call stub.
ZoneList<Expression*>* args = expr->arguments();
int arg_count = args->length();
- for (int i = 0; i < arg_count; i++) {
- VisitForStackValue(args->at(i));
+ { PreserveStatementPositionScope scope(masm()->positions_recorder());
+ for (int i = 0; i < arg_count; i++) {
+ VisitForStackValue(args->at(i));
+ }
}
// Record source position for debugger.
- SetSourcePosition(expr->position());
+ SetSourcePosition(expr->position(), FORCED_POSITION);
InLoopFlag in_loop = (loop_depth() > 0) ? IN_LOOP : NOT_IN_LOOP;
CallFunctionStub stub(arg_count, in_loop, RECEIVER_MIGHT_BE_VALUE);
__ CallStub(&stub);
@@ -2051,37 +2068,39 @@ void FullCodeGenerator::VisitCall(Call* expr) {
// resolve the function we need to call and the receiver of the
// call. Then we call the resolved function using the given
// arguments.
- VisitForStackValue(fun);
- __ push(Immediate(Factory::undefined_value())); // Reserved receiver slot.
-
- // Push the arguments.
ZoneList<Expression*>* args = expr->arguments();
int arg_count = args->length();
- for (int i = 0; i < arg_count; i++) {
- VisitForStackValue(args->at(i));
- }
+ { PreserveStatementPositionScope pos_scope(masm()->positions_recorder());
+ VisitForStackValue(fun);
+ // Reserved receiver slot.
+ __ push(Immediate(Factory::undefined_value()));
- // Push copy of the function - found below the arguments.
- __ push(Operand(esp, (arg_count + 1) * kPointerSize));
+ // Push the arguments.
+ for (int i = 0; i < arg_count; i++) {
+ VisitForStackValue(args->at(i));
+ }
- // Push copy of the first argument or undefined if it doesn't exist.
- if (arg_count > 0) {
- __ push(Operand(esp, arg_count * kPointerSize));
- } else {
- __ push(Immediate(Factory::undefined_value()));
- }
+ // Push copy of the function - found below the arguments.
+ __ push(Operand(esp, (arg_count + 1) * kPointerSize));
- // Push the receiver of the enclosing function and do runtime call.
- __ push(Operand(ebp, (2 + scope()->num_parameters()) * kPointerSize));
- __ CallRuntime(Runtime::kResolvePossiblyDirectEval, 3);
+ // Push copy of the first argument or undefined if it doesn't exist.
+ if (arg_count > 0) {
+ __ push(Operand(esp, arg_count * kPointerSize));
+ } else {
+ __ push(Immediate(Factory::undefined_value()));
+ }
- // The runtime call returns a pair of values in eax (function) and
- // edx (receiver). Touch up the stack with the right values.
- __ mov(Operand(esp, (arg_count + 0) * kPointerSize), edx);
- __ mov(Operand(esp, (arg_count + 1) * kPointerSize), eax);
+ // Push the receiver of the enclosing function and do runtime call.
+ __ push(Operand(ebp, (2 + scope()->num_parameters()) * kPointerSize));
+ __ CallRuntime(Runtime::kResolvePossiblyDirectEval, 3);
+ // The runtime call returns a pair of values in eax (function) and
+ // edx (receiver). Touch up the stack with the right values.
+ __ mov(Operand(esp, (arg_count + 0) * kPointerSize), edx);
+ __ mov(Operand(esp, (arg_count + 1) * kPointerSize), eax);
+ }
// Record source position for debugger.
- SetSourcePosition(expr->position());
+ SetSourcePosition(expr->position(), FORCED_POSITION);
InLoopFlag in_loop = (loop_depth() > 0) ? IN_LOOP : NOT_IN_LOOP;
CallFunctionStub stub(arg_count, in_loop, RECEIVER_MIGHT_BE_VALUE);
__ CallStub(&stub);
@@ -2097,12 +2116,14 @@ void FullCodeGenerator::VisitCall(Call* expr) {
// Call to a lookup slot (dynamically introduced variable).
Label slow, done;
- // Generate code for loading from variables potentially shadowed
- // by eval-introduced variables.
- EmitDynamicLoadFromSlotFastCase(var->AsSlot(),
- NOT_INSIDE_TYPEOF,
- &slow,
- &done);
+ { PreserveStatementPositionScope scope(masm()->positions_recorder());
+ // Generate code for loading from variables potentially shadowed
+ // by eval-introduced variables.
+ EmitDynamicLoadFromSlotFastCase(var->AsSlot(),
+ NOT_INSIDE_TYPEOF,
+ &slow,
+ &done);
+ }
__ bind(&slow);
// Call the runtime to find the function to call (returned in eax)
@@ -2141,11 +2162,15 @@ void FullCodeGenerator::VisitCall(Call* expr) {
// Call to a keyed property.
// For a synthetic property use keyed load IC followed by function call,
// for a regular property use keyed EmitCallIC.
- VisitForStackValue(prop->obj());
+ { PreserveStatementPositionScope scope(masm()->positions_recorder());
+ VisitForStackValue(prop->obj());
+ }
if (prop->is_synthetic()) {
- VisitForAccumulatorValue(prop->key());
+ { PreserveStatementPositionScope scope(masm()->positions_recorder());
+ VisitForAccumulatorValue(prop->key());
+ }
// Record source code position for IC call.
- SetSourcePosition(prop->position());
+ SetSourcePosition(prop->position(), FORCED_POSITION);
__ pop(edx); // We do not need to keep the receiver.
Handle<Code> ic(Builtins::builtin(Builtins::KeyedLoadIC_Initialize));
@@ -2170,7 +2195,9 @@ void FullCodeGenerator::VisitCall(Call* expr) {
loop_depth() == 0) {
lit->set_try_full_codegen(true);
}
- VisitForStackValue(fun);
+ { PreserveStatementPositionScope scope(masm()->positions_recorder());
+ VisitForStackValue(fun);
+ }
// Load global receiver object.
__ mov(ebx, CodeGenerator::GlobalObject());
__ push(FieldOperand(ebx, GlobalObject::kGlobalReceiverOffset));
diff --git a/src/ia32/ic-ia32.cc b/src/ia32/ic-ia32.cc
index b5f4deef..a0bc086d 100644
--- a/src/ia32/ic-ia32.cc
+++ b/src/ia32/ic-ia32.cc
@@ -33,7 +33,6 @@
#include "ic-inl.h"
#include "runtime.h"
#include "stub-cache.h"
-#include "utils.h"
namespace v8 {
namespace internal {
diff --git a/src/ia32/stub-cache-ia32.cc b/src/ia32/stub-cache-ia32.cc
index 90dabed0..042335ae 100644
--- a/src/ia32/stub-cache-ia32.cc
+++ b/src/ia32/stub-cache-ia32.cc
@@ -206,8 +206,10 @@ void StubCache::GenerateProbe(MacroAssembler* masm,
Register receiver,
Register name,
Register scratch,
- Register extra) {
+ Register extra,
+ Register extra2) {
Label miss;
+ USE(extra2); // The register extra2 is not used on the ia32 platform.
// Make sure that code is valid. The shifting code relies on the
// entry size being 8.
@@ -223,6 +225,10 @@ void StubCache::GenerateProbe(MacroAssembler* masm,
ASSERT(!extra.is(name));
ASSERT(!extra.is(scratch));
+ // Check scratch and extra registers are valid, and extra2 is unused.
+ ASSERT(!scratch.is(no_reg));
+ ASSERT(extra2.is(no_reg));
+
// Check that the receiver isn't a smi.
__ test(receiver, Immediate(kSmiTagMask));
__ j(zero, &miss, not_taken);
@@ -899,7 +905,7 @@ Register StubCompiler::CheckPrototypes(JSObject* object,
MaybeObject* maybe_lookup_result = Heap::LookupSymbol(name);
Object* lookup_result = NULL; // Initialization to please compiler.
if (!maybe_lookup_result->ToObject(&lookup_result)) {
- set_failure(Failure::cast(lookup_result));
+ set_failure(Failure::cast(maybe_lookup_result));
return reg;
}
name = String::cast(lookup_result);
@@ -1071,7 +1077,7 @@ bool StubCompiler::GenerateLoadCallback(JSObject* object,
Object* result = NULL; // Initialization to please compiler.
{ MaybeObject* try_call_result = masm()->TryCallStub(&stub);
if (!try_call_result->ToObject(&result)) {
- *failure = Failure::cast(result);
+ *failure = Failure::cast(try_call_result);
return false;
}
}
diff --git a/src/json.js b/src/json.js
index a39d7c4a..5993100f 100644
--- a/src/json.js
+++ b/src/json.js
@@ -29,8 +29,7 @@ var $JSON = global.JSON;
function ParseJSONUnfiltered(text) {
var s = $String(text);
- var f = %CompileString(s, true);
- return f();
+ return %ParseJson(s);
}
function Revive(holder, name, reviver) {
diff --git a/src/jsregexp.cc b/src/jsregexp.cc
index 3c5ddfbe..8cd13bc4 100644
--- a/src/jsregexp.cc
+++ b/src/jsregexp.cc
@@ -125,7 +125,8 @@ Handle<Object> RegExpImpl::Compile(Handle<JSRegExp> re,
PostponeInterruptsScope postpone;
RegExpCompileData parse_result;
FlatStringReader reader(pattern);
- if (!Parser::ParseRegExp(&reader, flags.is_multiline(), &parse_result)) {
+ if (!RegExpParser::ParseRegExp(&reader, flags.is_multiline(),
+ &parse_result)) {
// Throw an exception if we fail to parse the pattern.
ThrowRegExpException(re,
pattern,
@@ -267,7 +268,8 @@ bool RegExpImpl::CompileIrregexp(Handle<JSRegExp> re, bool is_ascii) {
RegExpCompileData compile_data;
FlatStringReader reader(pattern);
- if (!Parser::ParseRegExp(&reader, flags.is_multiline(), &compile_data)) {
+ if (!RegExpParser::ParseRegExp(&reader, flags.is_multiline(),
+ &compile_data)) {
// Throw an exception if we fail to parse the pattern.
// THIS SHOULD NOT HAPPEN. We already pre-parsed it successfully once.
ThrowRegExpException(re,
diff --git a/src/jump-target-heavy.cc b/src/jump-target-heavy.cc
index e0585e79..c3c22f1a 100644
--- a/src/jump-target-heavy.cc
+++ b/src/jump-target-heavy.cc
@@ -414,8 +414,9 @@ void BreakTarget::Branch(Condition cc, Hint hint) {
DeferredCode::DeferredCode()
: masm_(CodeGeneratorScope::Current()->masm()),
- statement_position_(masm_->current_statement_position()),
- position_(masm_->current_position()),
+ statement_position_(masm_->positions_recorder()->
+ current_statement_position()),
+ position_(masm_->positions_recorder()->current_position()),
frame_state_(CodeGeneratorScope::Current()->frame()) {
ASSERT(statement_position_ != RelocInfo::kNoPosition);
ASSERT(position_ != RelocInfo::kNoPosition);
diff --git a/src/jump-target-light.cc b/src/jump-target-light.cc
index 19f7bfec..36dc176b 100644
--- a/src/jump-target-light.cc
+++ b/src/jump-target-light.cc
@@ -36,8 +36,9 @@ namespace internal {
DeferredCode::DeferredCode()
: masm_(CodeGeneratorScope::Current()->masm()),
- statement_position_(masm_->current_statement_position()),
- position_(masm_->current_position()),
+ statement_position_(masm_->positions_recorder()->
+ current_statement_position()),
+ position_(masm_->positions_recorder()->current_position()),
frame_state_(*CodeGeneratorScope::Current()->frame()) {
ASSERT(statement_position_ != RelocInfo::kNoPosition);
ASSERT(position_ != RelocInfo::kNoPosition);
diff --git a/src/liveedit.cc b/src/liveedit.cc
index 49f221a4..642b3e6a 100644
--- a/src/liveedit.cc
+++ b/src/liveedit.cc
@@ -404,7 +404,7 @@ static void CompileScriptForTracker(Handle<Script> script) {
// Build AST.
CompilationInfo info(script);
info.MarkAsGlobal();
- if (Parser::Parse(&info)) {
+ if (ParserApi::Parse(&info)) {
// Compile the code.
LiveEditFunctionTracker tracker(info.function());
if (Compiler::MakeCodeForLiveEdit(&info)) {
diff --git a/src/log.cc b/src/log.cc
index 2cc2b8ff..d12aafb6 100644
--- a/src/log.cc
+++ b/src/log.cc
@@ -164,7 +164,10 @@ void StackTracer::Trace(TickSample* sample) {
int i = 0;
const Address callback = VMState::external_callback();
- if (callback != NULL) {
+ // Surprisingly, PC can point _exactly_ to callback start, with good
+ // probability, and this will result in reporting fake nested
+ // callback call.
+ if (callback != NULL && callback != sample->pc) {
sample->stack[i++] = callback;
}
diff --git a/src/mips/stub-cache-mips.cc b/src/mips/stub-cache-mips.cc
index faaacbc4..91dec175 100644
--- a/src/mips/stub-cache-mips.cc
+++ b/src/mips/stub-cache-mips.cc
@@ -44,7 +44,8 @@ void StubCache::GenerateProbe(MacroAssembler* masm,
Register receiver,
Register name,
Register scratch,
- Register extra) {
+ Register extra,
+ Register extra2) {
UNIMPLEMENTED_MIPS();
}
diff --git a/src/objects-inl.h b/src/objects-inl.h
index 4d210172..399ef35c 100644
--- a/src/objects-inl.h
+++ b/src/objects-inl.h
@@ -1952,7 +1952,9 @@ void JSFunctionResultCache::MakeZeroSize() {
void JSFunctionResultCache::Clear() {
int cache_size = Smi::cast(get(kCacheSizeIndex))->value();
Object** entries_start = RawField(this, OffsetOfElementAt(kEntriesIndex));
- MemsetPointer(entries_start, Heap::the_hole_value(), cache_size);
+ MemsetPointer(entries_start,
+ Heap::the_hole_value(),
+ cache_size - kEntriesIndex);
MakeZeroSize();
}
@@ -2669,6 +2671,7 @@ SMI_ACCESSORS(SharedFunctionInfo, this_property_assignments_count,
#else
#define PSEUDO_SMI_ACCESSORS_LO(holder, name, offset) \
+ STATIC_ASSERT(holder::offset % kPointerSize == 0); \
int holder::name() { \
int value = READ_INT_FIELD(this, offset); \
ASSERT(kHeapObjectTag == 1); \
@@ -2684,30 +2687,36 @@ SMI_ACCESSORS(SharedFunctionInfo, this_property_assignments_count,
(value << 1) & ~kHeapObjectTag); \
}
-#define PSEUDO_SMI_ACCESSORS_HI(holder, name, offset) \
+#define PSEUDO_SMI_ACCESSORS_HI(holder, name, offset) \
+ STATIC_ASSERT(holder::offset % kPointerSize == kIntSize); \
INT_ACCESSORS(holder, name, offset)
-
PSEUDO_SMI_ACCESSORS_LO(SharedFunctionInfo, length, kLengthOffset)
-PSEUDO_SMI_ACCESSORS_HI(SharedFunctionInfo, formal_parameter_count,
- kFormalParameterCountOffset)
+PSEUDO_SMI_ACCESSORS_HI(SharedFunctionInfo,
+ formal_parameter_count,
+ kFormalParameterCountOffset)
-PSEUDO_SMI_ACCESSORS_LO(SharedFunctionInfo, expected_nof_properties,
- kExpectedNofPropertiesOffset)
+PSEUDO_SMI_ACCESSORS_LO(SharedFunctionInfo,
+ expected_nof_properties,
+ kExpectedNofPropertiesOffset)
PSEUDO_SMI_ACCESSORS_HI(SharedFunctionInfo, num_literals, kNumLiteralsOffset)
-PSEUDO_SMI_ACCESSORS_LO(SharedFunctionInfo, start_position_and_type,
- kStartPositionAndTypeOffset)
-PSEUDO_SMI_ACCESSORS_HI(SharedFunctionInfo, end_position, kEndPositionOffset)
-
-PSEUDO_SMI_ACCESSORS_LO(SharedFunctionInfo, function_token_position,
- kFunctionTokenPositionOffset)
-PSEUDO_SMI_ACCESSORS_HI(SharedFunctionInfo, compiler_hints,
- kCompilerHintsOffset)
-
-PSEUDO_SMI_ACCESSORS_LO(SharedFunctionInfo, this_property_assignments_count,
- kThisPropertyAssignmentsCountOffset)
+PSEUDO_SMI_ACCESSORS_LO(SharedFunctionInfo, end_position, kEndPositionOffset)
+PSEUDO_SMI_ACCESSORS_HI(SharedFunctionInfo,
+ start_position_and_type,
+ kStartPositionAndTypeOffset)
+
+PSEUDO_SMI_ACCESSORS_LO(SharedFunctionInfo,
+ function_token_position,
+ kFunctionTokenPositionOffset)
+PSEUDO_SMI_ACCESSORS_HI(SharedFunctionInfo,
+ compiler_hints,
+ kCompilerHintsOffset)
+
+PSEUDO_SMI_ACCESSORS_LO(SharedFunctionInfo,
+ this_property_assignments_count,
+ kThisPropertyAssignmentsCountOffset)
#endif
diff --git a/src/objects.h b/src/objects.h
index 87234ea2..6029ad54 100644
--- a/src/objects.h
+++ b/src/objects.h
@@ -3409,8 +3409,7 @@ class Script: public Struct {
// Script compilation types.
enum CompilationType {
COMPILATION_TYPE_HOST = 0,
- COMPILATION_TYPE_EVAL = 1,
- COMPILATION_TYPE_JSON = 2
+ COMPILATION_TYPE_EVAL = 1
};
// [source]: the script source.
diff --git a/src/parser.cc b/src/parser.cc
index 180d0d24..a0f3b714 100644
--- a/src/parser.cc
+++ b/src/parser.cc
@@ -36,9 +36,9 @@
#include "messages.h"
#include "parser.h"
#include "platform.h"
+#include "preparser.h"
#include "runtime.h"
#include "scopeinfo.h"
-#include "scopes.h"
#include "string-stream.h"
#include "ast-inl.h"
@@ -87,112 +87,6 @@ class PositionStack {
};
-template <typename T, int initial_size>
-class BufferedZoneList {
- public:
- BufferedZoneList() : list_(NULL), last_(NULL) {}
-
- // Adds element at end of list. This element is buffered and can
- // be read using last() or removed using RemoveLast until a new Add or until
- // RemoveLast or GetList has been called.
- void Add(T* value) {
- if (last_ != NULL) {
- if (list_ == NULL) {
- list_ = new ZoneList<T*>(initial_size);
- }
- list_->Add(last_);
- }
- last_ = value;
- }
-
- T* last() {
- ASSERT(last_ != NULL);
- return last_;
- }
-
- T* RemoveLast() {
- ASSERT(last_ != NULL);
- T* result = last_;
- if (list_ != NULL && list_->length() > 0)
- last_ = list_->RemoveLast();
- else
- last_ = NULL;
- return result;
- }
-
- T* Get(int i) {
- ASSERT(0 <= i && i < length());
- if (list_ == NULL) {
- ASSERT_EQ(0, i);
- return last_;
- } else {
- if (i == list_->length()) {
- ASSERT(last_ != NULL);
- return last_;
- } else {
- return list_->at(i);
- }
- }
- }
-
- void Clear() {
- list_ = NULL;
- last_ = NULL;
- }
-
- int length() {
- int length = (list_ == NULL) ? 0 : list_->length();
- return length + ((last_ == NULL) ? 0 : 1);
- }
-
- ZoneList<T*>* GetList() {
- if (list_ == NULL) {
- list_ = new ZoneList<T*>(initial_size);
- }
- if (last_ != NULL) {
- list_->Add(last_);
- last_ = NULL;
- }
- return list_;
- }
-
- private:
- ZoneList<T*>* list_;
- T* last_;
-};
-
-
-// Accumulates RegExp atoms and assertions into lists of terms and alternatives.
-class RegExpBuilder: public ZoneObject {
- public:
- RegExpBuilder();
- void AddCharacter(uc16 character);
- // "Adds" an empty expression. Does nothing except consume a
- // following quantifier
- void AddEmpty();
- void AddAtom(RegExpTree* tree);
- void AddAssertion(RegExpTree* tree);
- void NewAlternative(); // '|'
- void AddQuantifierToAtom(int min, int max, RegExpQuantifier::Type type);
- RegExpTree* ToRegExp();
- private:
- void FlushCharacters();
- void FlushText();
- void FlushTerms();
- bool pending_empty_;
- ZoneList<uc16>* characters_;
- BufferedZoneList<RegExpTree, 2> terms_;
- BufferedZoneList<RegExpTree, 2> text_;
- BufferedZoneList<RegExpTree, 2> alternatives_;
-#ifdef DEBUG
- enum {ADD_NONE, ADD_CHAR, ADD_TERM, ADD_ASSERT, ADD_ATOM} last_added_;
-#define LAST(x) last_added_ = x;
-#else
-#define LAST(x)
-#endif
-};
-
-
RegExpBuilder::RegExpBuilder()
: pending_empty_(false),
characters_(NULL),
@@ -352,124 +246,13 @@ void RegExpBuilder::AddQuantifierToAtom(int min,
}
-class RegExpParser {
- public:
- RegExpParser(FlatStringReader* in,
- Handle<String>* error,
- bool multiline_mode);
- RegExpTree* ParsePattern();
- RegExpTree* ParseDisjunction();
- RegExpTree* ParseGroup();
- RegExpTree* ParseCharacterClass();
-
- // Parses a {...,...} quantifier and stores the range in the given
- // out parameters.
- bool ParseIntervalQuantifier(int* min_out, int* max_out);
-
- // Parses and returns a single escaped character. The character
- // must not be 'b' or 'B' since they are usually handle specially.
- uc32 ParseClassCharacterEscape();
-
- // Checks whether the following is a length-digit hexadecimal number,
- // and sets the value if it is.
- bool ParseHexEscape(int length, uc32* value);
-
- uc32 ParseControlLetterEscape();
- uc32 ParseOctalLiteral();
-
- // Tries to parse the input as a back reference. If successful it
- // stores the result in the output parameter and returns true. If
- // it fails it will push back the characters read so the same characters
- // can be reparsed.
- bool ParseBackReferenceIndex(int* index_out);
-
- CharacterRange ParseClassAtom(uc16* char_class);
- RegExpTree* ReportError(Vector<const char> message);
- void Advance();
- void Advance(int dist);
- void Reset(int pos);
-
- // Reports whether the pattern might be used as a literal search string.
- // Only use if the result of the parse is a single atom node.
- bool simple();
- bool contains_anchor() { return contains_anchor_; }
- void set_contains_anchor() { contains_anchor_ = true; }
- int captures_started() { return captures_ == NULL ? 0 : captures_->length(); }
- int position() { return next_pos_ - 1; }
- bool failed() { return failed_; }
-
- static const int kMaxCaptures = 1 << 16;
- static const uc32 kEndMarker = (1 << 21);
-
- private:
- enum SubexpressionType {
- INITIAL,
- CAPTURE, // All positive values represent captures.
- POSITIVE_LOOKAHEAD,
- NEGATIVE_LOOKAHEAD,
- GROUPING
- };
-
- class RegExpParserState : public ZoneObject {
- public:
- RegExpParserState(RegExpParserState* previous_state,
- SubexpressionType group_type,
- int disjunction_capture_index)
- : previous_state_(previous_state),
- builder_(new RegExpBuilder()),
- group_type_(group_type),
- disjunction_capture_index_(disjunction_capture_index) {}
- // Parser state of containing expression, if any.
- RegExpParserState* previous_state() { return previous_state_; }
- bool IsSubexpression() { return previous_state_ != NULL; }
- // RegExpBuilder building this regexp's AST.
- RegExpBuilder* builder() { return builder_; }
- // Type of regexp being parsed (parenthesized group or entire regexp).
- SubexpressionType group_type() { return group_type_; }
- // Index in captures array of first capture in this sub-expression, if any.
- // Also the capture index of this sub-expression itself, if group_type
- // is CAPTURE.
- int capture_index() { return disjunction_capture_index_; }
- private:
- // Linked list implementation of stack of states.
- RegExpParserState* previous_state_;
- // Builder for the stored disjunction.
- RegExpBuilder* builder_;
- // Stored disjunction type (capture, look-ahead or grouping), if any.
- SubexpressionType group_type_;
- // Stored disjunction's capture index (if any).
- int disjunction_capture_index_;
- };
-
- uc32 current() { return current_; }
- bool has_more() { return has_more_; }
- bool has_next() { return next_pos_ < in()->length(); }
- uc32 Next();
- FlatStringReader* in() { return in_; }
- void ScanForCaptures();
- uc32 current_;
- bool has_more_;
- bool multiline_;
- int next_pos_;
- FlatStringReader* in_;
- Handle<String>* error_;
- bool simple_;
- bool contains_anchor_;
- ZoneList<RegExpCapture*>* captures_;
- bool is_scanned_for_captures_;
- // The capture count is only valid after we have scanned for captures.
- int capture_count_;
- bool failed_;
-};
-
-
// A temporary scope stores information during parsing, just like
// a plain scope. However, temporary scopes are not kept around
// after parsing or referenced by syntax trees so they can be stack-
// allocated and hence used by the pre-parser.
class TemporaryScope BASE_EMBEDDED {
public:
- explicit TemporaryScope(Parser* parser);
+ explicit TemporaryScope(TemporaryScope** variable);
~TemporaryScope();
int NextMaterializedLiteralIndex() {
@@ -518,326 +301,118 @@ class TemporaryScope BASE_EMBEDDED {
int loop_count_;
// Bookkeeping
- Parser* parser_;
+ TemporaryScope** variable_;
TemporaryScope* parent_;
-
- friend class Parser;
};
-TemporaryScope::TemporaryScope(Parser* parser)
+TemporaryScope::TemporaryScope(TemporaryScope** variable)
: materialized_literal_count_(0),
expected_property_count_(0),
only_simple_this_property_assignments_(false),
this_property_assignments_(Factory::empty_fixed_array()),
loop_count_(0),
- parser_(parser),
- parent_(parser->temp_scope_) {
- parser->temp_scope_ = this;
+ variable_(variable),
+ parent_(*variable) {
+ *variable = this;
}
TemporaryScope::~TemporaryScope() {
- parser_->temp_scope_ = parent_;
+ *variable_ = parent_;
}
-// A zone list wrapper lets code either access a access a zone list
-// or appear to do so while actually ignoring all operations.
-template <typename T>
-class ZoneListWrapper {
- public:
- ZoneListWrapper() : list_(NULL) { }
- explicit ZoneListWrapper(int size) : list_(new ZoneList<T*>(size)) { }
- void Add(T* that) { if (list_) list_->Add(that); }
- int length() { return list_->length(); }
- ZoneList<T*>* elements() { return list_; }
- T* at(int index) { return list_->at(index); }
- private:
- ZoneList<T*>* list_;
-};
-
-
-// Allocation macro that should be used to allocate objects that must
-// only be allocated in real parsing mode. Note that in preparse mode
-// not only is the syntax tree not created but the constructor
-// arguments are not evaluated.
-#define NEW(expr) (is_pre_parsing_ ? NULL : new expr)
-
-
-class ParserFactory BASE_EMBEDDED {
- public:
- explicit ParserFactory(bool is_pre_parsing) :
- is_pre_parsing_(is_pre_parsing) { }
-
- virtual ~ParserFactory() { }
-
- virtual Scope* NewScope(Scope* parent, Scope::Type type, bool inside_with);
-
- virtual Handle<String> LookupSymbol(int index, Vector<const char> string) {
- return Handle<String>();
- }
-
- virtual Handle<String> EmptySymbol() {
- return Handle<String>();
- }
-
- virtual Expression* NewProperty(Expression* obj, Expression* key, int pos) {
- if (obj == VariableProxySentinel::this_proxy()) {
- return Property::this_property();
- } else {
- return ValidLeftHandSideSentinel::instance();
- }
- }
-
- virtual Expression* NewCall(Expression* expression,
- ZoneList<Expression*>* arguments,
- int pos) {
- return Call::sentinel();
- }
-
- virtual Statement* EmptyStatement() {
- return NULL;
- }
-
- template <typename T> ZoneListWrapper<T> NewList(int size) {
- return is_pre_parsing_ ? ZoneListWrapper<T>() : ZoneListWrapper<T>(size);
- }
-
- private:
- bool is_pre_parsing_;
-};
-
-
-class ParserLog BASE_EMBEDDED {
- public:
- virtual ~ParserLog() { }
-
- // Records the occurrence of a function.
- virtual FunctionEntry LogFunction(int start) { return FunctionEntry(); }
- virtual void LogSymbol(int start, Vector<const char> symbol) {}
- virtual void LogError() { }
- // Return the current position in the function entry log.
- virtual int function_position() { return 0; }
- virtual int symbol_position() { return 0; }
- virtual int symbol_ids() { return 0; }
- virtual void PauseRecording() {}
- virtual void ResumeRecording() {}
- virtual Vector<unsigned> ExtractData() {
- return Vector<unsigned>();
- };
-};
-
-
-
-class ConditionalLogPauseScope {
- public:
- ConditionalLogPauseScope(bool pause, ParserLog* log)
- : log_(log), pause_(pause) {
- if (pause) log->PauseRecording();
- }
- ~ConditionalLogPauseScope() {
- if (pause_) log_->ResumeRecording();
- }
- private:
- ParserLog* log_;
- bool pause_;
-};
-
-
-class AstBuildingParserFactory : public ParserFactory {
- public:
- explicit AstBuildingParserFactory(int expected_symbols)
- : ParserFactory(false), symbol_cache_(expected_symbols) { }
-
- virtual Scope* NewScope(Scope* parent, Scope::Type type, bool inside_with);
-
- virtual Handle<String> LookupSymbol(int symbol_id,
- Vector<const char> string) {
- // Length of symbol cache is the number of identified symbols.
- // If we are larger than that, or negative, it's not a cached symbol.
- // This might also happen if there is no preparser symbol data, even
- // if there is some preparser data.
- if (static_cast<unsigned>(symbol_id)
- >= static_cast<unsigned>(symbol_cache_.length())) {
- return Factory::LookupSymbol(string);
- }
- return LookupCachedSymbol(symbol_id, string);
- }
-
- Handle<String> LookupCachedSymbol(int symbol_id,
+Handle<String> Parser::LookupSymbol(int symbol_id,
Vector<const char> string) {
- // Make sure the cache is large enough to hold the symbol identifier.
- if (symbol_cache_.length() <= symbol_id) {
- // Increase length to index + 1.
- symbol_cache_.AddBlock(Handle<String>::null(),
- symbol_id + 1 - symbol_cache_.length());
- }
- Handle<String> result = symbol_cache_.at(symbol_id);
- if (result.is_null()) {
- result = Factory::LookupSymbol(string);
- symbol_cache_.at(symbol_id) = result;
- return result;
- }
- Counters::total_preparse_symbols_skipped.Increment();
+ // Length of symbol cache is the number of identified symbols.
+ // If we are larger than that, or negative, it's not a cached symbol.
+ // This might also happen if there is no preparser symbol data, even
+ // if there is some preparser data.
+ if (static_cast<unsigned>(symbol_id)
+ >= static_cast<unsigned>(symbol_cache_.length())) {
+ return Factory::LookupSymbol(string);
+ }
+ return LookupCachedSymbol(symbol_id, string);
+}
+
+
+Handle<String> Parser::LookupCachedSymbol(int symbol_id,
+ Vector<const char> string) {
+ // Make sure the cache is large enough to hold the symbol identifier.
+ if (symbol_cache_.length() <= symbol_id) {
+ // Increase length to index + 1.
+ symbol_cache_.AddBlock(Handle<String>::null(),
+ symbol_id + 1 - symbol_cache_.length());
+ }
+ Handle<String> result = symbol_cache_.at(symbol_id);
+ if (result.is_null()) {
+ result = Factory::LookupSymbol(string);
+ symbol_cache_.at(symbol_id) = result;
return result;
}
-
- virtual Handle<String> EmptySymbol() {
- return Factory::empty_symbol();
- }
-
- virtual Expression* NewProperty(Expression* obj, Expression* key, int pos) {
- return new Property(obj, key, pos);
- }
-
- virtual Expression* NewCall(Expression* expression,
- ZoneList<Expression*>* arguments,
- int pos) {
- return new Call(expression, arguments, pos);
- }
-
- virtual Statement* EmptyStatement();
- private:
- List<Handle<String> > symbol_cache_;
-};
+ Counters::total_preparse_symbols_skipped.Increment();
+ return result;
+}
-// Record only functions.
-class PartialParserRecorder: public ParserLog {
- public:
- PartialParserRecorder();
- virtual FunctionEntry LogFunction(int start);
-
- virtual int function_position() { return function_store_.size(); }
-
- virtual void LogError() { }
-
- virtual void LogMessage(Scanner::Location loc,
- const char* message,
- Vector<const char*> args);
-
- virtual Vector<unsigned> ExtractData() {
- int function_size = function_store_.size();
- int total_size = ScriptDataImpl::kHeaderSize + function_size;
- Vector<unsigned> data = Vector<unsigned>::New(total_size);
- preamble_[ScriptDataImpl::kFunctionsSizeOffset] = function_size;
- preamble_[ScriptDataImpl::kSymbolCountOffset] = 0;
- memcpy(data.start(), preamble_, sizeof(preamble_));
- int symbol_start = ScriptDataImpl::kHeaderSize + function_size;
- if (function_size > 0) {
- function_store_.WriteTo(data.SubVector(ScriptDataImpl::kHeaderSize,
- symbol_start));
- }
- return data;
+Vector<unsigned> PartialParserRecorder::ExtractData() {
+ int function_size = function_store_.size();
+ int total_size = ScriptDataImpl::kHeaderSize + function_size;
+ Vector<unsigned> data = Vector<unsigned>::New(total_size);
+ preamble_[ScriptDataImpl::kFunctionsSizeOffset] = function_size;
+ preamble_[ScriptDataImpl::kSymbolCountOffset] = 0;
+ memcpy(data.start(), preamble_, sizeof(preamble_));
+ int symbol_start = ScriptDataImpl::kHeaderSize + function_size;
+ if (function_size > 0) {
+ function_store_.WriteTo(data.SubVector(ScriptDataImpl::kHeaderSize,
+ symbol_start));
}
+ return data;
+}
- virtual void PauseRecording() {
- pause_count_++;
- is_recording_ = false;
- }
- virtual void ResumeRecording() {
- ASSERT(pause_count_ > 0);
- if (--pause_count_ == 0) is_recording_ = !has_error();
- }
+void CompleteParserRecorder::LogSymbol(int start, Vector<const char> literal) {
+ if (!is_recording_) return;
- protected:
- bool has_error() {
- return static_cast<bool>(preamble_[ScriptDataImpl::kHasErrorOffset]);
- }
- bool is_recording() {
- return is_recording_;
+ int hash = vector_hash(literal);
+ HashMap::Entry* entry = symbol_table_.Lookup(&literal, hash, true);
+ int id = static_cast<int>(reinterpret_cast<intptr_t>(entry->value));
+ if (id == 0) {
+ // Put (symbol_id_ + 1) into entry and increment it.
+ id = ++symbol_id_;
+ entry->value = reinterpret_cast<void*>(id);
+ Vector<Vector<const char> > symbol = symbol_entries_.AddBlock(1, literal);
+ entry->key = &symbol[0];
}
+ WriteNumber(id - 1);
+}
- void WriteString(Vector<const char> str);
-
- Collector<unsigned> function_store_;
- unsigned preamble_[ScriptDataImpl::kHeaderSize];
- bool is_recording_;
- int pause_count_;
-
-#ifdef DEBUG
- int prev_start;
-#endif
-};
-
-
-// Record both functions and symbols.
-class CompleteParserRecorder: public PartialParserRecorder {
- public:
- CompleteParserRecorder();
-
- virtual void LogSymbol(int start, Vector<const char> literal) {
- if (!is_recording_) return;
- int hash = vector_hash(literal);
- HashMap::Entry* entry = symbol_table_.Lookup(&literal, hash, true);
- int id = static_cast<int>(reinterpret_cast<intptr_t>(entry->value));
- if (id == 0) {
- // Put (symbol_id_ + 1) into entry and increment it.
- id = ++symbol_id_;
- entry->value = reinterpret_cast<void*>(id);
- Vector<Vector<const char> > symbol = symbol_entries_.AddBlock(1, literal);
- entry->key = &symbol[0];
- }
- WriteNumber(id - 1);
- }
-
- virtual Vector<unsigned> ExtractData() {
- int function_size = function_store_.size();
- // Add terminator to symbols, then pad to unsigned size.
- int symbol_size = symbol_store_.size();
- int padding = sizeof(unsigned) - (symbol_size % sizeof(unsigned));
- symbol_store_.AddBlock(padding, ScriptDataImpl::kNumberTerminator);
- symbol_size += padding;
- int total_size = ScriptDataImpl::kHeaderSize + function_size
- + (symbol_size / sizeof(unsigned));
- Vector<unsigned> data = Vector<unsigned>::New(total_size);
- preamble_[ScriptDataImpl::kFunctionsSizeOffset] = function_size;
- preamble_[ScriptDataImpl::kSymbolCountOffset] = symbol_id_;
- memcpy(data.start(), preamble_, sizeof(preamble_));
- int symbol_start = ScriptDataImpl::kHeaderSize + function_size;
- if (function_size > 0) {
- function_store_.WriteTo(data.SubVector(ScriptDataImpl::kHeaderSize,
- symbol_start));
- }
- if (!has_error()) {
- symbol_store_.WriteTo(
- Vector<byte>::cast(data.SubVector(symbol_start, total_size)));
- }
- return data;
- }
- virtual int symbol_position() { return symbol_store_.size(); }
- virtual int symbol_ids() { return symbol_id_; }
- private:
- static int vector_hash(Vector<const char> string) {
- int hash = 0;
- for (int i = 0; i < string.length(); i++) {
- int c = string[i];
- hash += c;
- hash += (hash << 10);
- hash ^= (hash >> 6);
- }
- return hash;
+Vector<unsigned> CompleteParserRecorder::ExtractData() {
+ int function_size = function_store_.size();
+ // Add terminator to symbols, then pad to unsigned size.
+ int symbol_size = symbol_store_.size();
+ int padding = sizeof(unsigned) - (symbol_size % sizeof(unsigned));
+ symbol_store_.AddBlock(padding, ScriptDataImpl::kNumberTerminator);
+ symbol_size += padding;
+ int total_size = ScriptDataImpl::kHeaderSize + function_size
+ + (symbol_size / sizeof(unsigned));
+ Vector<unsigned> data = Vector<unsigned>::New(total_size);
+ preamble_[ScriptDataImpl::kFunctionsSizeOffset] = function_size;
+ preamble_[ScriptDataImpl::kSymbolCountOffset] = symbol_id_;
+ memcpy(data.start(), preamble_, sizeof(preamble_));
+ int symbol_start = ScriptDataImpl::kHeaderSize + function_size;
+ if (function_size > 0) {
+ function_store_.WriteTo(data.SubVector(ScriptDataImpl::kHeaderSize,
+ symbol_start));
}
-
- static bool vector_compare(void* a, void* b) {
- Vector<const char>* string1 = reinterpret_cast<Vector<const char>* >(a);
- Vector<const char>* string2 = reinterpret_cast<Vector<const char>* >(b);
- int length = string1->length();
- if (string2->length() != length) return false;
- return memcmp(string1->start(), string2->start(), length) == 0;
+ if (!has_error()) {
+ symbol_store_.WriteTo(
+ Vector<byte>::cast(data.SubVector(symbol_start, total_size)));
}
-
- // Write a non-negative number to the symbol store.
- void WriteNumber(int number);
-
- Collector<byte> symbol_store_;
- Collector<Vector<const char> > symbol_entries_;
- HashMap symbol_table_;
- int symbol_id_;
-};
+ return data;
+}
FunctionEntry ScriptDataImpl::GetFunctionEntry(int start) {
@@ -910,7 +485,7 @@ PartialParserRecorder::PartialParserRecorder()
preamble_[ScriptDataImpl::kSizeOffset] = 0;
ASSERT_EQ(6, ScriptDataImpl::kHeaderSize);
#ifdef DEBUG
- prev_start = -1;
+ prev_start_ = -1;
#endif
}
@@ -961,8 +536,8 @@ const char* ScriptDataImpl::ReadString(unsigned* start, int* chars) {
void PartialParserRecorder::LogMessage(Scanner::Location loc,
- const char* message,
- Vector<const char*> args) {
+ const char* message,
+ Vector<const char*> args) {
if (has_error()) return;
preamble_[ScriptDataImpl::kHasErrorOffset] = true;
function_store_.Reset();
@@ -1019,120 +594,12 @@ unsigned* ScriptDataImpl::ReadAddress(int position) {
}
-FunctionEntry PartialParserRecorder::LogFunction(int start) {
-#ifdef DEBUG
- ASSERT(start > prev_start);
- prev_start = start;
-#endif
- if (!is_recording_) return FunctionEntry();
- FunctionEntry result(function_store_.AddBlock(FunctionEntry::kSize, 0));
- result.set_start_pos(start);
- return result;
-}
-
-
-class AstBuildingParser : public Parser {
- public:
- AstBuildingParser(Handle<Script> script, bool allow_natives_syntax,
- v8::Extension* extension, ScriptDataImpl* pre_data)
- : Parser(script,
- allow_natives_syntax,
- extension,
- PARSE,
- factory(),
- log(),
- pre_data),
- factory_(pre_data ? pre_data->symbol_count() : 0) { }
- virtual void ReportMessageAt(Scanner::Location loc, const char* message,
- Vector<const char*> args);
- virtual VariableProxy* Declare(Handle<String> name, Variable::Mode mode,
- FunctionLiteral* fun, bool resolve, bool* ok);
- AstBuildingParserFactory* factory() { return &factory_; }
- ParserLog* log() { return &log_; }
-
- private:
- ParserLog log_;
- AstBuildingParserFactory factory_;
-};
-
-
-class PreParser : public Parser {
- public:
- PreParser(Handle<Script> script, bool allow_natives_syntax,
- v8::Extension* extension, ParserLog* recorder)
- : Parser(script, allow_natives_syntax, extension, PREPARSE,
- factory(), recorder, NULL),
- factory_(true) { }
- virtual void ReportMessageAt(Scanner::Location loc, const char* message,
- Vector<const char*> args);
- virtual VariableProxy* Declare(Handle<String> name, Variable::Mode mode,
- FunctionLiteral* fun, bool resolve, bool* ok);
- ParserFactory* factory() { return &factory_; }
- virtual PartialParserRecorder* recorder() = 0;
-
- private:
- ParserFactory factory_;
-};
-
-
-class CompletePreParser : public PreParser {
- public:
- CompletePreParser(Handle<Script> script, bool allow_natives_syntax,
- v8::Extension* extension)
- : PreParser(script, allow_natives_syntax, extension, &recorder_),
- recorder_() { }
- virtual PartialParserRecorder* recorder() { return &recorder_; }
- private:
- CompleteParserRecorder recorder_;
-};
-
-
-class PartialPreParser : public PreParser {
- public:
- PartialPreParser(Handle<Script> script, bool allow_natives_syntax,
- v8::Extension* extension)
- : PreParser(script, allow_natives_syntax, extension, &recorder_),
- recorder_() { }
- virtual PartialParserRecorder* recorder() { return &recorder_; }
- private:
- PartialParserRecorder recorder_;
-};
-
-
-Scope* AstBuildingParserFactory::NewScope(Scope* parent, Scope::Type type,
- bool inside_with) {
+Scope* Parser::NewScope(Scope* parent, Scope::Type type, bool inside_with) {
Scope* result = new Scope(parent, type);
result->Initialize(inside_with);
return result;
}
-
-Statement* AstBuildingParserFactory::EmptyStatement() {
- // Use a statically allocated empty statement singleton to avoid
- // allocating lots and lots of empty statements.
- static v8::internal::EmptyStatement empty;
- return &empty;
-}
-
-
-Scope* ParserFactory::NewScope(Scope* parent, Scope::Type type,
- bool inside_with) {
- ASSERT(parent != NULL);
- parent->type_ = type;
- // Initialize function is hijacked by DummyScope to increment scope depth.
- parent->Initialize(inside_with);
- return parent;
-}
-
-
-VariableProxy* PreParser::Declare(Handle<String> name, Variable::Mode mode,
- FunctionLiteral* fun, bool resolve,
- bool* ok) {
- return NULL;
-}
-
-
-
// ----------------------------------------------------------------------------
// Target is a support class to facilitate manipulation of the
// Parser's target_stack_ (the stack of potential 'break' and
@@ -1141,20 +608,20 @@ VariableProxy* PreParser::Declare(Handle<String> name, Variable::Mode mode,
class Target BASE_EMBEDDED {
public:
- Target(Parser* parser, AstNode* node)
- : parser_(parser), node_(node), previous_(parser_->target_stack_) {
- parser_->target_stack_ = this;
+ Target(Target** variable, AstNode* node)
+ : variable_(variable), node_(node), previous_(*variable) {
+ *variable = this;
}
~Target() {
- parser_->target_stack_ = previous_;
+ *variable_ = previous_;
}
Target* previous() { return previous_; }
AstNode* node() { return node_; }
private:
- Parser* parser_;
+ Target** variable_;
AstNode* node_;
Target* previous_;
};
@@ -1162,17 +629,17 @@ class Target BASE_EMBEDDED {
class TargetScope BASE_EMBEDDED {
public:
- explicit TargetScope(Parser* parser)
- : parser_(parser), previous_(parser->target_stack_) {
- parser->target_stack_ = NULL;
+ explicit TargetScope(Target** variable)
+ : variable_(variable), previous_(*variable) {
+ *variable = NULL;
}
~TargetScope() {
- parser_->target_stack_ = previous_;
+ *variable_ = previous_;
}
private:
- Parser* parser_;
+ Target** variable_;
Target* previous_;
};
@@ -1184,22 +651,26 @@ class TargetScope BASE_EMBEDDED {
class LexicalScope BASE_EMBEDDED {
public:
- LexicalScope(Parser* parser, Scope* scope)
- : parser_(parser),
- prev_scope_(parser->top_scope_),
- prev_level_(parser->with_nesting_level_) {
- parser_->top_scope_ = scope;
- parser_->with_nesting_level_ = 0;
+ LexicalScope(Scope** scope_variable,
+ int* with_nesting_level_variable,
+ Scope* scope)
+ : scope_variable_(scope_variable),
+ with_nesting_level_variable_(with_nesting_level_variable),
+ prev_scope_(*scope_variable),
+ prev_level_(*with_nesting_level_variable) {
+ *scope_variable = scope;
+ *with_nesting_level_variable = 0;
}
~LexicalScope() {
- parser_->top_scope_->Leave();
- parser_->top_scope_ = prev_scope_;
- parser_->with_nesting_level_ = prev_level_;
+ (*scope_variable_)->Leave();
+ *scope_variable_ = prev_scope_;
+ *with_nesting_level_variable_ = prev_level_;
}
private:
- Parser* parser_;
+ Scope** scope_variable_;
+ int* with_nesting_level_variable_;
Scope* prev_scope_;
int prev_level_;
};
@@ -1231,11 +702,9 @@ class LexicalScope BASE_EMBEDDED {
Parser::Parser(Handle<Script> script,
bool allow_natives_syntax,
v8::Extension* extension,
- ParserMode is_pre_parsing,
- ParserFactory* factory,
- ParserLog* log,
ScriptDataImpl* pre_data)
- : script_(script),
+ : symbol_cache_(pre_data ? pre_data->symbol_count() : 0),
+ script_(script),
scanner_(),
top_scope_(NULL),
with_nesting_level_(0),
@@ -1243,34 +712,11 @@ Parser::Parser(Handle<Script> script,
target_stack_(NULL),
allow_natives_syntax_(allow_natives_syntax),
extension_(extension),
- factory_(factory),
- log_(log),
- is_pre_parsing_(is_pre_parsing == PREPARSE),
pre_data_(pre_data),
fni_(NULL) {
}
-bool Parser::PreParseProgram(Handle<String> source,
- unibrow::CharacterStream* stream) {
- HistogramTimerScope timer(&Counters::pre_parse);
- AssertNoZoneAllocation assert_no_zone_allocation;
- AssertNoAllocation assert_no_allocation;
- NoHandleAllocation no_handle_allocation;
- scanner_.Initialize(source, stream, JAVASCRIPT);
- ASSERT(target_stack_ == NULL);
- mode_ = FLAG_lazy ? PARSE_LAZILY : PARSE_EAGERLY;
- if (allow_natives_syntax_ || extension_ != NULL) mode_ = PARSE_EAGERLY;
- DummyScope top_scope;
- LexicalScope scope(this, &top_scope);
- TemporaryScope temp_scope(this);
- ZoneListWrapper<Statement> processor;
- bool ok = true;
- ParseSourceElements(&processor, Token::EOS, &ok);
- return !scanner().stack_overflow();
-}
-
-
FunctionLiteral* Parser::ParseProgram(Handle<String> source,
bool in_global_context) {
CompilationZoneScope zone_scope(DONT_DELETE_ON_EXIT);
@@ -1293,20 +739,21 @@ FunctionLiteral* Parser::ParseProgram(Handle<String> source,
in_global_context
? Scope::GLOBAL_SCOPE
: Scope::EVAL_SCOPE;
- Handle<String> no_name = factory()->EmptySymbol();
+ Handle<String> no_name = Factory::empty_symbol();
FunctionLiteral* result = NULL;
- { Scope* scope = factory()->NewScope(top_scope_, type, inside_with());
- LexicalScope lexical_scope(this, scope);
- TemporaryScope temp_scope(this);
- ZoneListWrapper<Statement> body(16);
+ { Scope* scope = NewScope(top_scope_, type, inside_with());
+ LexicalScope lexical_scope(&this->top_scope_, &this->with_nesting_level_,
+ scope);
+ TemporaryScope temp_scope(&this->temp_scope_);
+ ZoneList<Statement*>* body = new ZoneList<Statement*>(16);
bool ok = true;
- ParseSourceElements(&body, Token::EOS, &ok);
+ ParseSourceElements(body, Token::EOS, &ok);
if (ok) {
- result = NEW(FunctionLiteral(
+ result = new FunctionLiteral(
no_name,
top_scope_,
- body.elements(),
+ body,
temp_scope.materialized_literal_count(),
temp_scope.expected_property_count(),
temp_scope.only_simple_this_property_assignments(),
@@ -1315,7 +762,7 @@ FunctionLiteral* Parser::ParseProgram(Handle<String> source,
0,
source->length(),
false,
- temp_scope.ContainsLoops()));
+ temp_scope.ContainsLoops());
} else if (scanner().stack_overflow()) {
Top::StackOverflow();
}
@@ -1353,11 +800,12 @@ FunctionLiteral* Parser::ParseLazy(Handle<SharedFunctionInfo> info) {
{
// Parse the function literal.
- Handle<String> no_name = factory()->EmptySymbol();
+ Handle<String> no_name = Factory::empty_symbol();
Scope* scope =
- factory()->NewScope(top_scope_, Scope::GLOBAL_SCOPE, inside_with());
- LexicalScope lexical_scope(this, scope);
- TemporaryScope temp_scope(this);
+ NewScope(top_scope_, Scope::GLOBAL_SCOPE, inside_with());
+ LexicalScope lexical_scope(&this->top_scope_, &this->with_nesting_level_,
+ scope);
+ TemporaryScope temp_scope(&this->temp_scope_);
FunctionLiteralType type =
info->is_expression() ? EXPRESSION : DECLARATION;
@@ -1382,78 +830,24 @@ FunctionLiteral* Parser::ParseLazy(Handle<SharedFunctionInfo> info) {
}
-FunctionLiteral* Parser::ParseJson(Handle<String> source) {
- CompilationZoneScope zone_scope(DONT_DELETE_ON_EXIT);
-
- HistogramTimerScope timer(&Counters::parse);
- Counters::total_parse_size.Increment(source->length());
-
- // Initialize parser state.
- source->TryFlatten(TENURED);
- scanner_.Initialize(source, JSON);
- ASSERT(target_stack_ == NULL);
-
- FunctionLiteral* result = NULL;
- Handle<String> no_name = factory()->EmptySymbol();
-
- {
- Scope* scope = factory()->NewScope(top_scope_, Scope::GLOBAL_SCOPE, false);
- LexicalScope lexical_scope(this, scope);
- TemporaryScope temp_scope(this);
- bool ok = true;
- Expression* expression = ParseJson(&ok);
- if (ok) {
- ZoneListWrapper<Statement> statement = factory()->NewList<Statement>(1);
- statement.Add(new ExpressionStatement(expression));
- result = NEW(FunctionLiteral(
- no_name,
- top_scope_,
- statement.elements(),
- temp_scope.materialized_literal_count(),
- temp_scope.expected_property_count(),
- temp_scope.only_simple_this_property_assignments(),
- temp_scope.this_property_assignments(),
- 0,
- 0,
- source->length(),
- false,
- temp_scope.ContainsLoops()));
- } else if (scanner().stack_overflow()) {
- Top::StackOverflow();
- }
+Handle<String> Parser::GetSymbol(bool* ok) {
+ int symbol_id = -1;
+ if (pre_data() != NULL) {
+ symbol_id = pre_data()->GetSymbolIdentifier();
}
-
- // Make sure the target stack is empty.
- ASSERT(target_stack_ == NULL);
-
- // If there was a syntax error we have to get rid of the AST
- // and it is not safe to do so before the scope has been deleted.
- if (result == NULL) zone_scope.DeleteOnExit();
- return result;
+ return LookupSymbol(symbol_id, scanner_.literal());
}
+
void Parser::ReportMessage(const char* type, Vector<const char*> args) {
Scanner::Location source_location = scanner_.location();
ReportMessageAt(source_location, type, args);
}
-Handle<String> Parser::GetSymbol(bool* ok) {
- if (is_pre_parsing_) {
- log()->LogSymbol(scanner_.location().beg_pos, scanner_.literal());
- return Handle<String>::null();
- }
- int symbol_id = -1;
- if (pre_data() != NULL) {
- symbol_id = pre_data()->GetSymbolIdentifier();
- }
- return factory()->LookupSymbol(symbol_id, scanner_.literal());
-}
-
-
-void AstBuildingParser::ReportMessageAt(Scanner::Location source_location,
- const char* type,
- Vector<const char*> args) {
+void Parser::ReportMessageAt(Scanner::Location source_location,
+ const char* type,
+ Vector<const char*> args) {
MessageLocation location(script_,
source_location.beg_pos, source_location.end_pos);
Handle<JSArray> array = Factory::NewJSArray(args.length());
@@ -1465,13 +859,6 @@ void AstBuildingParser::ReportMessageAt(Scanner::Location source_location,
}
-void PreParser::ReportMessageAt(Scanner::Location source_location,
- const char* type,
- Vector<const char*> args) {
- recorder()->LogMessage(source_location, type, args);
-}
-
-
// Base class containing common code for the different finder classes used by
// the parser.
class ParserFinder {
@@ -1513,6 +900,11 @@ class InitializationBlockFinder : public ParserFinder {
}
private:
+ // The minimum number of contiguous assignment that will
+ // be treated as an initialization block. Benchmarks show that
+ // the overhead exceeds the savings below this limit.
+ static const int kMinInitializationBlock = 3;
+
// Returns true if the expressions appear to denote the same object.
// In the context of initialization blocks, we only consider expressions
// of the form 'expr.x' or expr["x"].
@@ -1565,7 +957,7 @@ class InitializationBlockFinder : public ParserFinder {
}
void EndBlock() {
- if (block_size_ >= Parser::kMinInitializationBlock) {
+ if (block_size_ >= kMinInitializationBlock) {
first_in_block_->mark_block_start();
last_in_block_->mark_block_end();
}
@@ -1723,7 +1115,7 @@ class ThisNamedPropertyAssigmentFinder : public ParserFinder {
};
-void* Parser::ParseSourceElements(ZoneListWrapper<Statement>* processor,
+void* Parser::ParseSourceElements(ZoneList<Statement*>* processor,
int end_token,
bool* ok) {
// SourceElements ::
@@ -1733,7 +1125,7 @@ void* Parser::ParseSourceElements(ZoneListWrapper<Statement>* processor,
// elements. This way, all scripts and functions get their own
// target stack thus avoiding illegal breaks and continues across
// functions.
- TargetScope scope(this);
+ TargetScope scope(&this->target_stack_);
ASSERT(processor != NULL);
InitializationBlockFinder block_finder;
@@ -1755,7 +1147,7 @@ void* Parser::ParseSourceElements(ZoneListWrapper<Statement>* processor,
}
// Propagate the collected information on this property assignments.
- if (!is_pre_parsing_ && top_scope_->is_function_scope()) {
+ if (top_scope_->is_function_scope()) {
bool only_simple_this_property_assignments =
this_property_assignment_finder.only_simple_this_property_assignments()
&& top_scope_->declarations()->length() == 0;
@@ -1808,7 +1200,7 @@ Statement* Parser::ParseStatement(ZoneStringList* labels, bool* ok) {
case Token::SEMICOLON:
Next();
- return factory()->EmptyStatement();
+ return EmptyStatement();
case Token::IF:
stmt = ParseIfStatement(labels, ok);
@@ -1856,8 +1248,8 @@ Statement* Parser::ParseStatement(ZoneStringList* labels, bool* ok) {
// one must take great care not to treat it as a
// fall-through. It is much easier just to wrap the entire
// try-statement in a statement block and put the labels there
- Block* result = NEW(Block(labels, 1, false));
- Target target(this, result);
+ Block* result = new Block(labels, 1, false);
+ Target target(&this->target_stack_, result);
TryStatement* statement = ParseTryStatement(CHECK_OK);
if (statement) {
statement->set_statement_pos(statement_pos);
@@ -1886,11 +1278,11 @@ Statement* Parser::ParseStatement(ZoneStringList* labels, bool* ok) {
}
-VariableProxy* AstBuildingParser::Declare(Handle<String> name,
- Variable::Mode mode,
- FunctionLiteral* fun,
- bool resolve,
- bool* ok) {
+VariableProxy* Parser::Declare(Handle<String> name,
+ Variable::Mode mode,
+ FunctionLiteral* fun,
+ bool resolve,
+ bool* ok) {
Variable* var = NULL;
// If we are inside a function, a declaration of a variable
// is a truly local variable, and the scope of the variable
@@ -1945,13 +1337,13 @@ VariableProxy* AstBuildingParser::Declare(Handle<String> name,
// a performance issue since it may lead to repeated
// Runtime::DeclareContextSlot() calls.
VariableProxy* proxy = top_scope_->NewUnresolved(name, inside_with());
- top_scope_->AddDeclaration(NEW(Declaration(proxy, mode, fun)));
+ top_scope_->AddDeclaration(new Declaration(proxy, mode, fun));
// For global const variables we bind the proxy to a variable.
if (mode == Variable::CONST && top_scope_->is_global_scope()) {
ASSERT(resolve); // should be set by all callers
Variable::Kind kind = Variable::NORMAL;
- var = NEW(Variable(top_scope_, name, Variable::CONST, true, kind));
+ var = new Variable(top_scope_, name, Variable::CONST, true, kind);
}
// If requested and we have a local variable, bind the proxy to the variable
@@ -2003,13 +1395,13 @@ Statement* Parser::ParseNativeDeclaration(bool* ok) {
while (!done) {
ParseIdentifier(CHECK_OK);
done = (peek() == Token::RPAREN);
- if (!done) Expect(Token::COMMA, CHECK_OK);
+ if (!done) {
+ Expect(Token::COMMA, CHECK_OK);
+ }
}
Expect(Token::RPAREN, CHECK_OK);
Expect(Token::SEMICOLON, CHECK_OK);
- if (is_pre_parsing_) return NULL;
-
// Make sure that the function containing the native declaration
// isn't lazily compiled. The extension structures are only
// accessible while parsing the first time not when reparsing
@@ -2039,10 +1431,10 @@ Statement* Parser::ParseNativeDeclaration(bool* ok) {
// TODO(1240846): It's weird that native function declarations are
// introduced dynamically when we meet their declarations, whereas
// other functions are setup when entering the surrounding scope.
- SharedFunctionInfoLiteral* lit = NEW(SharedFunctionInfoLiteral(shared));
+ SharedFunctionInfoLiteral* lit = new SharedFunctionInfoLiteral(shared);
VariableProxy* var = Declare(name, Variable::VAR, NULL, true, CHECK_OK);
- return NEW(ExpressionStatement(
- new Assignment(Token::INIT_VAR, var, lit, RelocInfo::kNoPosition)));
+ return new ExpressionStatement(
+ new Assignment(Token::INIT_VAR, var, lit, RelocInfo::kNoPosition));
}
@@ -2060,7 +1452,7 @@ Statement* Parser::ParseFunctionDeclaration(bool* ok) {
// scope, we treat is as such and introduce the function with it's
// initial value upon entering the corresponding scope.
Declare(name, Variable::VAR, fun, true, CHECK_OK);
- return factory()->EmptyStatement();
+ return EmptyStatement();
}
@@ -2072,8 +1464,8 @@ Block* Parser::ParseBlock(ZoneStringList* labels, bool* ok) {
// (ECMA-262, 3rd, 12.2)
//
// Construct block expecting 16 statements.
- Block* result = NEW(Block(labels, 16, false));
- Target target(this, result);
+ Block* result = new Block(labels, 16, false);
+ Target target(&this->target_stack_, result);
Expect(Token::LBRACE, CHECK_OK);
while (peek() != Token::RBRACE) {
Statement* stat = ParseStatement(NULL, CHECK_OK);
@@ -2131,7 +1523,7 @@ Block* Parser::ParseVariableDeclarations(bool accept_IN,
// is inside an initializer block, it is ignored.
//
// Create new block with one expected declaration.
- Block* block = NEW(Block(NULL, 1, true));
+ Block* block = new Block(NULL, 1, true);
VariableProxy* last_var = NULL; // the last variable declared
int nvars = 0; // the number of variables declared
do {
@@ -2222,14 +1614,14 @@ Block* Parser::ParseVariableDeclarations(bool accept_IN,
// browsers where the global object (window) has lots of
// properties defined in prototype objects.
- if (!is_pre_parsing_ && top_scope_->is_global_scope()) {
+ if (top_scope_->is_global_scope()) {
// Compute the arguments for the runtime call.
ZoneList<Expression*>* arguments = new ZoneList<Expression*>(2);
// Be careful not to assign a value to the global variable if
// we're in a with. The initialization value should not
// necessarily be stored in the global object in that case,
// which is why we need to generate a separate assignment node.
- arguments->Add(NEW(Literal(name))); // we have at least 1 parameter
+ arguments->Add(new Literal(name)); // we have at least 1 parameter
if (is_const || (value != NULL && !inside_with())) {
arguments->Add(value);
value = NULL; // zap the value to avoid the unnecessary assignment
@@ -2241,18 +1633,18 @@ Block* Parser::ParseVariableDeclarations(bool accept_IN,
CallRuntime* initialize;
if (is_const) {
initialize =
- NEW(CallRuntime(
+ new CallRuntime(
Factory::InitializeConstGlobal_symbol(),
Runtime::FunctionForId(Runtime::kInitializeConstGlobal),
- arguments));
+ arguments);
} else {
initialize =
- NEW(CallRuntime(
+ new CallRuntime(
Factory::InitializeVarGlobal_symbol(),
Runtime::FunctionForId(Runtime::kInitializeVarGlobal),
- arguments));
+ arguments);
}
- block->AddStatement(NEW(ExpressionStatement(initialize)));
+ block->AddStatement(new ExpressionStatement(initialize));
}
// Add an assignment node to the initialization statement block if
@@ -2267,8 +1659,8 @@ Block* Parser::ParseVariableDeclarations(bool accept_IN,
// the top context for variables). Sigh...
if (value != NULL) {
Token::Value op = (is_const ? Token::INIT_CONST : Token::INIT_VAR);
- Assignment* assignment = NEW(Assignment(op, last_var, value, position));
- if (block) block->AddStatement(NEW(ExpressionStatement(assignment)));
+ Assignment* assignment = new Assignment(op, last_var, value, position);
+ if (block) block->AddStatement(new ExpressionStatement(assignment));
}
if (fni_ != NULL) fni_->Leave();
@@ -2276,14 +1668,8 @@ Block* Parser::ParseVariableDeclarations(bool accept_IN,
if (!is_const && nvars == 1) {
// We have a single, non-const variable.
- if (is_pre_parsing_) {
- // If we're preparsing then we need to set the var to something
- // in order for for-in loops to parse correctly.
- *var = ValidLeftHandSideSentinel::instance();
- } else {
- ASSERT(last_var != NULL);
- *var = last_var;
- }
+ ASSERT(last_var != NULL);
+ *var = last_var;
}
return block;
@@ -2318,29 +1704,27 @@ Statement* Parser::ParseExpressionOrLabelledStatement(ZoneStringList* labels,
// labels requires nontrivial changes to the way scopes are
// structured. However, these are probably changes we want to
// make later anyway so we should go back and fix this then.
- if (!is_pre_parsing_) {
- if (ContainsLabel(labels, label) || TargetStackContainsLabel(label)) {
- SmartPointer<char> c_string = label->ToCString(DISALLOW_NULLS);
- const char* elms[2] = { "Label", *c_string };
- Vector<const char*> args(elms, 2);
- ReportMessage("redeclaration", args);
- *ok = false;
- return NULL;
- }
- if (labels == NULL) labels = new ZoneStringList(4);
- labels->Add(label);
- // Remove the "ghost" variable that turned out to be a label
- // from the top scope. This way, we don't try to resolve it
- // during the scope processing.
- top_scope_->RemoveUnresolved(var);
+ if (ContainsLabel(labels, label) || TargetStackContainsLabel(label)) {
+ SmartPointer<char> c_string = label->ToCString(DISALLOW_NULLS);
+ const char* elms[2] = { "Label", *c_string };
+ Vector<const char*> args(elms, 2);
+ ReportMessage("redeclaration", args);
+ *ok = false;
+ return NULL;
}
+ if (labels == NULL) labels = new ZoneStringList(4);
+ labels->Add(label);
+ // Remove the "ghost" variable that turned out to be a label
+ // from the top scope. This way, we don't try to resolve it
+ // during the scope processing.
+ top_scope_->RemoveUnresolved(var);
Expect(Token::COLON, CHECK_OK);
return ParseStatement(labels, ok);
}
// Parsed expression statement.
ExpectSemicolon(CHECK_OK);
- return NEW(ExpressionStatement(expr));
+ return new ExpressionStatement(expr);
}
@@ -2357,10 +1741,10 @@ IfStatement* Parser::ParseIfStatement(ZoneStringList* labels, bool* ok) {
if (peek() == Token::ELSE) {
Next();
else_statement = ParseStatement(labels, CHECK_OK);
- } else if (!is_pre_parsing_) {
- else_statement = factory()->EmptyStatement();
+ } else {
+ else_statement = EmptyStatement();
}
- return NEW(IfStatement(condition, then_statement, else_statement));
+ return new IfStatement(condition, then_statement, else_statement);
}
@@ -2376,19 +1760,17 @@ Statement* Parser::ParseContinueStatement(bool* ok) {
label = ParseIdentifier(CHECK_OK);
}
IterationStatement* target = NULL;
- if (!is_pre_parsing_) {
- target = LookupContinueTarget(label, CHECK_OK);
- if (target == NULL) {
- // Illegal continue statement. To be consistent with KJS we delay
- // reporting of the syntax error until runtime.
- Handle<String> error_type = Factory::illegal_continue_symbol();
- if (!label.is_null()) error_type = Factory::unknown_label_symbol();
- Expression* throw_error = NewThrowSyntaxError(error_type, label);
- return NEW(ExpressionStatement(throw_error));
- }
+ target = LookupContinueTarget(label, CHECK_OK);
+ if (target == NULL) {
+ // Illegal continue statement. To be consistent with KJS we delay
+ // reporting of the syntax error until runtime.
+ Handle<String> error_type = Factory::illegal_continue_symbol();
+ if (!label.is_null()) error_type = Factory::unknown_label_symbol();
+ Expression* throw_error = NewThrowSyntaxError(error_type, label);
+ return new ExpressionStatement(throw_error);
}
ExpectSemicolon(CHECK_OK);
- return NEW(ContinueStatement(target));
+ return new ContinueStatement(target);
}
@@ -2406,22 +1788,20 @@ Statement* Parser::ParseBreakStatement(ZoneStringList* labels, bool* ok) {
// Parse labeled break statements that target themselves into
// empty statements, e.g. 'l1: l2: l3: break l2;'
if (!label.is_null() && ContainsLabel(labels, label)) {
- return factory()->EmptyStatement();
+ return EmptyStatement();
}
BreakableStatement* target = NULL;
- if (!is_pre_parsing_) {
- target = LookupBreakTarget(label, CHECK_OK);
- if (target == NULL) {
- // Illegal break statement. To be consistent with KJS we delay
- // reporting of the syntax error until runtime.
- Handle<String> error_type = Factory::illegal_break_symbol();
- if (!label.is_null()) error_type = Factory::unknown_label_symbol();
- Expression* throw_error = NewThrowSyntaxError(error_type, label);
- return NEW(ExpressionStatement(throw_error));
- }
+ target = LookupBreakTarget(label, CHECK_OK);
+ if (target == NULL) {
+ // Illegal break statement. To be consistent with KJS we delay
+ // reporting of the syntax error until runtime.
+ Handle<String> error_type = Factory::illegal_break_symbol();
+ if (!label.is_null()) error_type = Factory::unknown_label_symbol();
+ Expression* throw_error = NewThrowSyntaxError(error_type, label);
+ return new ExpressionStatement(throw_error);
}
ExpectSemicolon(CHECK_OK);
- return NEW(BreakStatement(target));
+ return new BreakStatement(target);
}
@@ -2439,10 +1819,10 @@ Statement* Parser::ParseReturnStatement(bool* ok) {
// function. See ECMA-262, section 12.9, page 67.
//
// To be consistent with KJS we report the syntax error at runtime.
- if (!is_pre_parsing_ && !top_scope_->is_function_scope()) {
+ if (!top_scope_->is_function_scope()) {
Handle<String> type = Factory::illegal_return_symbol();
Expression* throw_error = NewThrowSyntaxError(type, Handle<Object>::null());
- return NEW(ExpressionStatement(throw_error));
+ return new ExpressionStatement(throw_error);
}
Token::Value tok = peek();
@@ -2451,12 +1831,12 @@ Statement* Parser::ParseReturnStatement(bool* ok) {
tok == Token::RBRACE ||
tok == Token::EOS) {
ExpectSemicolon(CHECK_OK);
- return NEW(ReturnStatement(GetLiteralUndefined()));
+ return new ReturnStatement(GetLiteralUndefined());
}
Expression* expr = ParseExpression(true, CHECK_OK);
ExpectSemicolon(CHECK_OK);
- return NEW(ReturnStatement(expr));
+ return new ReturnStatement(expr);
}
@@ -2465,10 +1845,10 @@ Block* Parser::WithHelper(Expression* obj,
bool is_catch_block,
bool* ok) {
// Parse the statement and collect escaping labels.
- ZoneList<BreakTarget*>* target_list = NEW(ZoneList<BreakTarget*>(0));
+ ZoneList<BreakTarget*>* target_list = new ZoneList<BreakTarget*>(0);
TargetCollector collector(target_list);
Statement* stat;
- { Target target(this, &collector);
+ { Target target(&this->target_stack_, &collector);
with_nesting_level_++;
top_scope_->RecordWithStatement();
stat = ParseStatement(labels, CHECK_OK);
@@ -2477,21 +1857,21 @@ Block* Parser::WithHelper(Expression* obj,
// Create resulting block with two statements.
// 1: Evaluate the with expression.
// 2: The try-finally block evaluating the body.
- Block* result = NEW(Block(NULL, 2, false));
+ Block* result = new Block(NULL, 2, false);
if (result != NULL) {
- result->AddStatement(NEW(WithEnterStatement(obj, is_catch_block)));
+ result->AddStatement(new WithEnterStatement(obj, is_catch_block));
// Create body block.
- Block* body = NEW(Block(NULL, 1, false));
+ Block* body = new Block(NULL, 1, false);
body->AddStatement(stat);
// Create exit block.
- Block* exit = NEW(Block(NULL, 1, false));
- exit->AddStatement(NEW(WithExitStatement()));
+ Block* exit = new Block(NULL, 1, false);
+ exit->AddStatement(new WithExitStatement());
// Return a try-finally statement.
- TryFinallyStatement* wrapper = NEW(TryFinallyStatement(body, exit));
+ TryFinallyStatement* wrapper = new TryFinallyStatement(body, exit);
wrapper->set_escaping_targets(collector.targets());
result->AddStatement(wrapper);
}
@@ -2533,15 +1913,15 @@ CaseClause* Parser::ParseCaseClause(bool* default_seen_ptr, bool* ok) {
}
Expect(Token::COLON, CHECK_OK);
- ZoneListWrapper<Statement> statements = factory()->NewList<Statement>(5);
+ ZoneList<Statement*>* statements = new ZoneList<Statement*>(5);
while (peek() != Token::CASE &&
peek() != Token::DEFAULT &&
peek() != Token::RBRACE) {
Statement* stat = ParseStatement(NULL, CHECK_OK);
- statements.Add(stat);
+ statements->Add(stat);
}
- return NEW(CaseClause(label, statements.elements()));
+ return new CaseClause(label, statements);
}
@@ -2550,8 +1930,8 @@ SwitchStatement* Parser::ParseSwitchStatement(ZoneStringList* labels,
// SwitchStatement ::
// 'switch' '(' Expression ')' '{' CaseClause* '}'
- SwitchStatement* statement = NEW(SwitchStatement(labels));
- Target target(this, statement);
+ SwitchStatement* statement = new SwitchStatement(labels);
+ Target target(&this->target_stack_, statement);
Expect(Token::SWITCH, CHECK_OK);
Expect(Token::LPAREN, CHECK_OK);
@@ -2559,15 +1939,15 @@ SwitchStatement* Parser::ParseSwitchStatement(ZoneStringList* labels,
Expect(Token::RPAREN, CHECK_OK);
bool default_seen = false;
- ZoneListWrapper<CaseClause> cases = factory()->NewList<CaseClause>(4);
+ ZoneList<CaseClause*>* cases = new ZoneList<CaseClause*>(4);
Expect(Token::LBRACE, CHECK_OK);
while (peek() != Token::RBRACE) {
CaseClause* clause = ParseCaseClause(&default_seen, CHECK_OK);
- cases.Add(clause);
+ cases->Add(clause);
}
Expect(Token::RBRACE, CHECK_OK);
- if (statement) statement->Initialize(tag, cases.elements());
+ if (statement) statement->Initialize(tag, cases);
return statement;
}
@@ -2586,7 +1966,7 @@ Statement* Parser::ParseThrowStatement(bool* ok) {
Expression* exception = ParseExpression(true, CHECK_OK);
ExpectSemicolon(CHECK_OK);
- return NEW(ExpressionStatement(new Throw(exception, pos)));
+ return new ExpressionStatement(new Throw(exception, pos));
}
@@ -2604,11 +1984,11 @@ TryStatement* Parser::ParseTryStatement(bool* ok) {
Expect(Token::TRY, CHECK_OK);
- ZoneList<BreakTarget*>* target_list = NEW(ZoneList<BreakTarget*>(0));
+ ZoneList<BreakTarget*>* target_list = new ZoneList<BreakTarget*>(0);
TargetCollector collector(target_list);
Block* try_block;
- { Target target(this, &collector);
+ { Target target(&this->target_stack_, &collector);
try_block = ParseBlock(NULL, CHECK_OK);
}
@@ -2627,7 +2007,7 @@ TryStatement* Parser::ParseTryStatement(bool* ok) {
// then we will need to collect jump targets from the catch block. Since
// we don't know yet if there will be a finally block, we always collect
// the jump targets.
- ZoneList<BreakTarget*>* catch_target_list = NEW(ZoneList<BreakTarget*>(0));
+ ZoneList<BreakTarget*>* catch_target_list = new ZoneList<BreakTarget*>(0);
TargetCollector catch_collector(catch_target_list);
bool has_catch = false;
if (tok == Token::CATCH) {
@@ -2642,9 +2022,9 @@ TryStatement* Parser::ParseTryStatement(bool* ok) {
// Allocate a temporary for holding the finally state while
// executing the finally block.
catch_var = top_scope_->NewTemporary(Factory::catch_var_symbol());
- Literal* name_literal = NEW(Literal(name));
- Expression* obj = NEW(CatchExtensionObject(name_literal, catch_var));
- { Target target(this, &catch_collector);
+ Literal* name_literal = new Literal(name);
+ Expression* obj = new CatchExtensionObject(name_literal, catch_var);
+ { Target target(&this->target_stack_, &catch_collector);
catch_block = WithHelper(obj, NULL, true, CHECK_OK);
}
} else {
@@ -2666,30 +2046,28 @@ TryStatement* Parser::ParseTryStatement(bool* ok) {
// to:
// 'try { try { } catch { } } finally { }'
- if (!is_pre_parsing_ && catch_block != NULL && finally_block != NULL) {
+ if (catch_block != NULL && finally_block != NULL) {
TryCatchStatement* statement =
- NEW(TryCatchStatement(try_block, catch_var, catch_block));
+ new TryCatchStatement(try_block, catch_var, catch_block);
statement->set_escaping_targets(collector.targets());
- try_block = NEW(Block(NULL, 1, false));
+ try_block = new Block(NULL, 1, false);
try_block->AddStatement(statement);
catch_block = NULL;
}
TryStatement* result = NULL;
- if (!is_pre_parsing_) {
- if (catch_block != NULL) {
- ASSERT(finally_block == NULL);
- result = NEW(TryCatchStatement(try_block, catch_var, catch_block));
- result->set_escaping_targets(collector.targets());
- } else {
- ASSERT(finally_block != NULL);
- result = NEW(TryFinallyStatement(try_block, finally_block));
- // Add the jump targets of the try block and the catch block.
- for (int i = 0; i < collector.targets()->length(); i++) {
- catch_collector.AddTarget(collector.targets()->at(i));
- }
- result->set_escaping_targets(catch_collector.targets());
+ if (catch_block != NULL) {
+ ASSERT(finally_block == NULL);
+ result = new TryCatchStatement(try_block, catch_var, catch_block);
+ result->set_escaping_targets(collector.targets());
+ } else {
+ ASSERT(finally_block != NULL);
+ result = new TryFinallyStatement(try_block, finally_block);
+ // Add the jump targets of the try block and the catch block.
+ for (int i = 0; i < collector.targets()->length(); i++) {
+ catch_collector.AddTarget(collector.targets()->at(i));
}
+ result->set_escaping_targets(catch_collector.targets());
}
return result;
@@ -2702,8 +2080,8 @@ DoWhileStatement* Parser::ParseDoWhileStatement(ZoneStringList* labels,
// 'do' Statement 'while' '(' Expression ')' ';'
temp_scope_->AddLoop();
- DoWhileStatement* loop = NEW(DoWhileStatement(labels));
- Target target(this, loop);
+ DoWhileStatement* loop = new DoWhileStatement(labels);
+ Target target(&this->target_stack_, loop);
Expect(Token::DO, CHECK_OK);
Statement* body = ParseStatement(NULL, CHECK_OK);
@@ -2735,8 +2113,8 @@ WhileStatement* Parser::ParseWhileStatement(ZoneStringList* labels, bool* ok) {
// 'while' '(' Expression ')' Statement
temp_scope_->AddLoop();
- WhileStatement* loop = NEW(WhileStatement(labels));
- Target target(this, loop);
+ WhileStatement* loop = new WhileStatement(labels);
+ Target target(&this->target_stack_, loop);
Expect(Token::WHILE, CHECK_OK);
Expect(Token::LPAREN, CHECK_OK);
@@ -2765,25 +2143,20 @@ Statement* Parser::ParseForStatement(ZoneStringList* labels, bool* ok) {
Block* variable_statement =
ParseVariableDeclarations(false, &each, CHECK_OK);
if (peek() == Token::IN && each != NULL) {
- ForInStatement* loop = NEW(ForInStatement(labels));
- Target target(this, loop);
+ ForInStatement* loop = new ForInStatement(labels);
+ Target target(&this->target_stack_, loop);
Expect(Token::IN, CHECK_OK);
Expression* enumerable = ParseExpression(true, CHECK_OK);
Expect(Token::RPAREN, CHECK_OK);
Statement* body = ParseStatement(NULL, CHECK_OK);
- if (is_pre_parsing_) {
- return NULL;
- } else {
- loop->Initialize(each, enumerable, body);
- Block* result = NEW(Block(NULL, 2, false));
- result->AddStatement(variable_statement);
- result->AddStatement(loop);
- // Parsed for-in loop w/ variable/const declaration.
- return result;
- }
-
+ loop->Initialize(each, enumerable, body);
+ Block* result = new Block(NULL, 2, false);
+ result->AddStatement(variable_statement);
+ result->AddStatement(loop);
+ // Parsed for-in loop w/ variable/const declaration.
+ return result;
} else {
init = variable_statement;
}
@@ -2799,8 +2172,8 @@ Statement* Parser::ParseForStatement(ZoneStringList* labels, bool* ok) {
Handle<String> type = Factory::invalid_lhs_in_for_in_symbol();
expression = NewThrowReferenceError(type);
}
- ForInStatement* loop = NEW(ForInStatement(labels));
- Target target(this, loop);
+ ForInStatement* loop = new ForInStatement(labels);
+ Target target(&this->target_stack_, loop);
Expect(Token::IN, CHECK_OK);
Expression* enumerable = ParseExpression(true, CHECK_OK);
@@ -2812,14 +2185,14 @@ Statement* Parser::ParseForStatement(ZoneStringList* labels, bool* ok) {
return loop;
} else {
- init = NEW(ExpressionStatement(expression));
+ init = new ExpressionStatement(expression);
}
}
}
// Standard 'for' loop
- ForStatement* loop = NEW(ForStatement(labels));
- Target target(this, loop);
+ ForStatement* loop = new ForStatement(labels);
+ Target target(&this->target_stack_, loop);
// Parsed initializer at this point.
Expect(Token::SEMICOLON, CHECK_OK);
@@ -2834,7 +2207,7 @@ Statement* Parser::ParseForStatement(ZoneStringList* labels, bool* ok) {
Statement* next = NULL;
if (peek() != Token::RPAREN) {
Expression* exp = ParseExpression(true, CHECK_OK);
- next = NEW(ExpressionStatement(exp));
+ next = new ExpressionStatement(exp);
}
Expect(Token::RPAREN, CHECK_OK);
@@ -2855,7 +2228,7 @@ Expression* Parser::ParseExpression(bool accept_IN, bool* ok) {
Expect(Token::COMMA, CHECK_OK);
int position = scanner().location().beg_pos;
Expression* right = ParseAssignmentExpression(accept_IN, CHECK_OK);
- result = NEW(BinaryOperation(Token::COMMA, result, right, position));
+ result = new BinaryOperation(Token::COMMA, result, right, position);
}
return result;
}
@@ -2915,7 +2288,7 @@ Expression* Parser::ParseAssignmentExpression(bool accept_IN, bool* ok) {
fni_->Leave();
}
- return NEW(Assignment(op, expression, right, pos));
+ return new Assignment(op, expression, right, pos);
}
@@ -2937,8 +2310,8 @@ Expression* Parser::ParseConditionalExpression(bool accept_IN, bool* ok) {
Expect(Token::COLON, CHECK_OK);
int right_position = scanner().peek_location().beg_pos;
Expression* right = ParseAssignmentExpression(accept_IN, CHECK_OK);
- return NEW(Conditional(expression, left, right,
- left_position, right_position));
+ return new Conditional(expression, left, right,
+ left_position, right_position);
}
@@ -3045,12 +2418,12 @@ Expression* Parser::ParseBinaryExpression(int prec, bool accept_IN, bool* ok) {
x = NewCompareNode(cmp, x, y, position);
if (cmp != op) {
// The comparison was negated - add a NOT.
- x = NEW(UnaryOperation(Token::NOT, x));
+ x = new UnaryOperation(Token::NOT, x);
}
} else {
// We have a "normal" binary operation.
- x = NEW(BinaryOperation(op, x, y, position));
+ x = new BinaryOperation(op, x, y, position);
}
}
}
@@ -3063,19 +2436,19 @@ Expression* Parser::NewCompareNode(Token::Value op,
Expression* y,
int position) {
ASSERT(op != Token::NE && op != Token::NE_STRICT);
- if (!is_pre_parsing_ && (op == Token::EQ || op == Token::EQ_STRICT)) {
+ if (op == Token::EQ || op == Token::EQ_STRICT) {
bool is_strict = (op == Token::EQ_STRICT);
Literal* x_literal = x->AsLiteral();
if (x_literal != NULL && x_literal->IsNull()) {
- return NEW(CompareToNull(is_strict, y));
+ return new CompareToNull(is_strict, y);
}
Literal* y_literal = y->AsLiteral();
if (y_literal != NULL && y_literal->IsNull()) {
- return NEW(CompareToNull(is_strict, x));
+ return new CompareToNull(is_strict, x);
}
}
- return NEW(CompareOperation(op, x, y, position));
+ return new CompareOperation(op, x, y, position);
}
@@ -3112,7 +2485,7 @@ Expression* Parser::ParseUnaryExpression(bool* ok) {
}
}
- return NEW(UnaryOperation(op, expression));
+ return new UnaryOperation(op, expression);
} else if (Token::IsCountOp(op)) {
op = Next();
@@ -3126,8 +2499,8 @@ Expression* Parser::ParseUnaryExpression(bool* ok) {
expression = NewThrowReferenceError(type);
}
int position = scanner().location().beg_pos;
- IncrementOperation* increment = NEW(IncrementOperation(op, expression));
- return NEW(CountOperation(true /* prefix */, increment, position));
+ IncrementOperation* increment = new IncrementOperation(op, expression);
+ return new CountOperation(true /* prefix */, increment, position);
} else {
return ParsePostfixExpression(ok);
@@ -3151,8 +2524,8 @@ Expression* Parser::ParsePostfixExpression(bool* ok) {
}
Token::Value next = Next();
int position = scanner().location().beg_pos;
- IncrementOperation* increment = NEW(IncrementOperation(next, expression));
- expression = NEW(CountOperation(false /* postfix */, increment, position));
+ IncrementOperation* increment = new IncrementOperation(next, expression);
+ expression = new CountOperation(false /* postfix */, increment, position);
}
return expression;
}
@@ -3175,7 +2548,7 @@ Expression* Parser::ParseLeftHandSideExpression(bool* ok) {
Consume(Token::LBRACK);
int pos = scanner().location().beg_pos;
Expression* index = ParseExpression(true, CHECK_OK);
- result = factory()->NewProperty(result, index, pos);
+ result = new Property(result, index, pos);
Expect(Token::RBRACK, CHECK_OK);
break;
}
@@ -3192,17 +2565,15 @@ Expression* Parser::ParseLeftHandSideExpression(bool* ok) {
// declared in the current scope chain. These calls are marked as
// potentially direct eval calls. Whether they are actually direct calls
// to eval is determined at run time.
- if (!is_pre_parsing_) {
- VariableProxy* callee = result->AsVariableProxy();
- if (callee != NULL && callee->IsVariable(Factory::eval_symbol())) {
- Handle<String> name = callee->name();
- Variable* var = top_scope_->Lookup(name);
- if (var == NULL) {
- top_scope_->RecordEvalCall();
- }
+ VariableProxy* callee = result->AsVariableProxy();
+ if (callee != NULL && callee->IsVariable(Factory::eval_symbol())) {
+ Handle<String> name = callee->name();
+ Variable* var = top_scope_->Lookup(name);
+ if (var == NULL) {
+ top_scope_->RecordEvalCall();
}
}
- result = factory()->NewCall(result, args, pos);
+ result = NewCall(result, args, pos);
break;
}
@@ -3210,7 +2581,7 @@ Expression* Parser::ParseLeftHandSideExpression(bool* ok) {
Consume(Token::PERIOD);
int pos = scanner().location().beg_pos;
Handle<String> name = ParseIdentifierName(CHECK_OK);
- result = factory()->NewProperty(result, NEW(Literal(name)), pos);
+ result = new Property(result, new Literal(name), pos);
if (fni_ != NULL) fni_->PushLiteralName(name);
break;
}
@@ -3222,7 +2593,6 @@ Expression* Parser::ParseLeftHandSideExpression(bool* ok) {
}
-
Expression* Parser::ParseNewPrefix(PositionStack* stack, bool* ok) {
// NewExpression ::
// ('new')+ MemberExpression
@@ -3247,7 +2617,7 @@ Expression* Parser::ParseNewPrefix(PositionStack* stack, bool* ok) {
if (!stack->is_empty()) {
int last = stack->pop();
- result = NEW(CallNew(result, new ZoneList<Expression*>(0), last));
+ result = new CallNew(result, new ZoneList<Expression*>(0), last);
}
return result;
}
@@ -3289,7 +2659,7 @@ Expression* Parser::ParseMemberWithNewPrefixesExpression(PositionStack* stack,
Consume(Token::LBRACK);
int pos = scanner().location().beg_pos;
Expression* index = ParseExpression(true, CHECK_OK);
- result = factory()->NewProperty(result, index, pos);
+ result = new Property(result, index, pos);
Expect(Token::RBRACK, CHECK_OK);
break;
}
@@ -3297,7 +2667,7 @@ Expression* Parser::ParseMemberWithNewPrefixesExpression(PositionStack* stack,
Consume(Token::PERIOD);
int pos = scanner().location().beg_pos;
Handle<String> name = ParseIdentifierName(CHECK_OK);
- result = factory()->NewProperty(result, NEW(Literal(name)), pos);
+ result = new Property(result, new Literal(name), pos);
if (fni_ != NULL) fni_->PushLiteralName(name);
break;
}
@@ -3306,7 +2676,7 @@ Expression* Parser::ParseMemberWithNewPrefixesExpression(PositionStack* stack,
// Consume one of the new prefixes (already parsed).
ZoneList<Expression*>* args = ParseArguments(CHECK_OK);
int last = stack->pop();
- result = NEW(CallNew(result, args, last));
+ result = new CallNew(result, args, last);
break;
}
default:
@@ -3325,7 +2695,7 @@ DebuggerStatement* Parser::ParseDebuggerStatement(bool* ok) {
Expect(Token::DEBUGGER, CHECK_OK);
ExpectSemicolon(CHECK_OK);
- return NEW(DebuggerStatement());
+ return new DebuggerStatement();
}
@@ -3383,38 +2753,30 @@ Expression* Parser::ParsePrimaryExpression(bool* ok) {
switch (peek()) {
case Token::THIS: {
Consume(Token::THIS);
- if (is_pre_parsing_) {
- result = VariableProxySentinel::this_proxy();
- } else {
- VariableProxy* recv = top_scope_->receiver();
- result = recv;
- }
+ VariableProxy* recv = top_scope_->receiver();
+ result = recv;
break;
}
case Token::NULL_LITERAL:
Consume(Token::NULL_LITERAL);
- result = NEW(Literal(Factory::null_value()));
+ result = new Literal(Factory::null_value());
break;
case Token::TRUE_LITERAL:
Consume(Token::TRUE_LITERAL);
- result = NEW(Literal(Factory::true_value()));
+ result = new Literal(Factory::true_value());
break;
case Token::FALSE_LITERAL:
Consume(Token::FALSE_LITERAL);
- result = NEW(Literal(Factory::false_value()));
+ result = new Literal(Factory::false_value());
break;
case Token::IDENTIFIER: {
Handle<String> name = ParseIdentifier(CHECK_OK);
if (fni_ != NULL) fni_->PushVariableName(name);
- if (is_pre_parsing_) {
- result = VariableProxySentinel::identifier_proxy();
- } else {
- result = top_scope_->NewUnresolved(name, inside_with());
- }
+ result = top_scope_->NewUnresolved(name, inside_with());
break;
}
@@ -3429,7 +2791,7 @@ Expression* Parser::ParsePrimaryExpression(bool* ok) {
case Token::STRING: {
Consume(Token::STRING);
Handle<String> symbol = GetSymbol(CHECK_OK);
- result = NEW(Literal(symbol));
+ result = new Literal(symbol);
if (fni_ != NULL) fni_->PushLiteralName(symbol);
break;
}
@@ -3507,7 +2869,7 @@ Expression* Parser::ParseArrayLiteral(bool* ok) {
// ArrayLiteral ::
// '[' Expression? (',' Expression?)* ']'
- ZoneListWrapper<Expression> values = factory()->NewList<Expression>(4);
+ ZoneList<Expression*>* values = new ZoneList<Expression*>(4);
Expect(Token::LBRACK, CHECK_OK);
while (peek() != Token::RBRACK) {
Expression* elem;
@@ -3516,7 +2878,7 @@ Expression* Parser::ParseArrayLiteral(bool* ok) {
} else {
elem = ParseAssignmentExpression(true, CHECK_OK);
}
- values.Add(elem);
+ values->Add(elem);
if (peek() != Token::RBRACK) {
Expect(Token::COMMA, CHECK_OK);
}
@@ -3526,21 +2888,19 @@ Expression* Parser::ParseArrayLiteral(bool* ok) {
// Update the scope information before the pre-parsing bailout.
int literal_index = temp_scope_->NextMaterializedLiteralIndex();
- if (is_pre_parsing_) return NULL;
-
// Allocate a fixed array with all the literals.
Handle<FixedArray> literals =
- Factory::NewFixedArray(values.length(), TENURED);
+ Factory::NewFixedArray(values->length(), TENURED);
// Fill in the literals.
bool is_simple = true;
int depth = 1;
- for (int i = 0; i < values.length(); i++) {
- MaterializedLiteral* m_literal = values.at(i)->AsMaterializedLiteral();
+ for (int i = 0, n = values->length(); i < n; i++) {
+ MaterializedLiteral* m_literal = values->at(i)->AsMaterializedLiteral();
if (m_literal != NULL && m_literal->depth() + 1 > depth) {
depth = m_literal->depth() + 1;
}
- Handle<Object> boilerplate_value = GetBoilerplateValue(values.at(i));
+ Handle<Object> boilerplate_value = GetBoilerplateValue(values->at(i));
if (boilerplate_value->IsUndefined()) {
literals->set_the_hole(i);
is_simple = false;
@@ -3551,12 +2911,12 @@ Expression* Parser::ParseArrayLiteral(bool* ok) {
// Simple and shallow arrays can be lazily copied, we transform the
// elements array to a copy-on-write array.
- if (is_simple && depth == 1 && values.length() > 0) {
+ if (is_simple && depth == 1 && values->length() > 0) {
literals->set_map(Heap::fixed_cow_array_map());
}
- return NEW(ArrayLiteral(literals, values.elements(),
- literal_index, is_simple, depth));
+ return new ArrayLiteral(literals, values,
+ literal_index, is_simple, depth);
}
@@ -3703,7 +3063,7 @@ ObjectLiteral::Property* Parser::ParseObjectLiteralGetSet(bool is_getter,
DECLARATION,
CHECK_OK);
ObjectLiteral::Property* property =
- NEW(ObjectLiteral::Property(is_getter, value));
+ new ObjectLiteral::Property(is_getter, value);
return property;
} else {
ReportUnexpectedToken(next);
@@ -3720,8 +3080,8 @@ Expression* Parser::ParseObjectLiteral(bool* ok) {
// | (('get' | 'set') (IdentifierName | String | Number) FunctionLiteral)
// )*[','] '}'
- ZoneListWrapper<ObjectLiteral::Property> properties =
- factory()->NewList<ObjectLiteral::Property>(4);
+ ZoneList<ObjectLiteral::Property*>* properties =
+ new ZoneList<ObjectLiteral::Property*>(4);
int number_of_boilerplate_properties = 0;
Expect(Token::LBRACE, CHECK_OK);
@@ -3744,7 +3104,7 @@ Expression* Parser::ParseObjectLiteral(bool* ok) {
if (IsBoilerplateProperty(property)) {
number_of_boilerplate_properties++;
}
- properties.Add(property);
+ properties->Add(property);
if (peek() != Token::RBRACE) Expect(Token::COMMA, CHECK_OK);
if (fni_ != NULL) {
@@ -3755,7 +3115,7 @@ Expression* Parser::ParseObjectLiteral(bool* ok) {
}
// Failed to parse as get/set property, so it's just a property
// called "get" or "set".
- key = NEW(Literal(id));
+ key = new Literal(id);
break;
}
case Token::STRING: {
@@ -3767,7 +3127,7 @@ Expression* Parser::ParseObjectLiteral(bool* ok) {
key = NewNumberLiteral(index);
break;
}
- key = NEW(Literal(string));
+ key = new Literal(string);
break;
}
case Token::NUMBER: {
@@ -3781,7 +3141,7 @@ Expression* Parser::ParseObjectLiteral(bool* ok) {
if (Token::IsKeyword(next)) {
Consume(next);
Handle<String> string = GetSymbol(CHECK_OK);
- key = NEW(Literal(string));
+ key = new Literal(string);
} else {
// Unexpected token.
Token::Value next = Next();
@@ -3795,11 +3155,11 @@ Expression* Parser::ParseObjectLiteral(bool* ok) {
Expression* value = ParseAssignmentExpression(true, CHECK_OK);
ObjectLiteral::Property* property =
- NEW(ObjectLiteral::Property(key, value));
+ new ObjectLiteral::Property(key, value);
// Count CONSTANT or COMPUTED properties to maintain the enumeration order.
if (IsBoilerplateProperty(property)) number_of_boilerplate_properties++;
- properties.Add(property);
+ properties->Add(property);
// TODO(1240767): Consider allowing trailing comma.
if (peek() != Token::RBRACE) Expect(Token::COMMA, CHECK_OK);
@@ -3812,7 +3172,6 @@ Expression* Parser::ParseObjectLiteral(bool* ok) {
Expect(Token::RBRACE, CHECK_OK);
// Computation of literal_index must happen before pre parse bailout.
int literal_index = temp_scope_->NextMaterializedLiteralIndex();
- if (is_pre_parsing_) return NULL;
Handle<FixedArray> constant_properties =
Factory::NewFixedArray(number_of_boilerplate_properties * 2, TENURED);
@@ -3820,13 +3179,13 @@ Expression* Parser::ParseObjectLiteral(bool* ok) {
bool is_simple = true;
bool fast_elements = true;
int depth = 1;
- BuildObjectLiteralConstantProperties(properties.elements(),
+ BuildObjectLiteralConstantProperties(properties,
constant_properties,
&is_simple,
&fast_elements,
&depth);
return new ObjectLiteral(constant_properties,
- properties.elements(),
+ properties,
literal_index,
is_simple,
fast_elements,
@@ -3844,19 +3203,6 @@ Expression* Parser::ParseRegExpLiteral(bool seen_equal, bool* ok) {
int literal_index = temp_scope_->NextMaterializedLiteralIndex();
- if (is_pre_parsing_) {
- // If we're preparsing we just do all the parsing stuff without
- // building anything.
- if (!scanner_.ScanRegExpFlags()) {
- Next();
- ReportMessage("invalid_regexp_flags", Vector<const char*>::empty());
- *ok = false;
- return NULL;
- }
- Next();
- return NULL;
- }
-
Handle<String> js_pattern =
Factory::NewStringFromUtf8(scanner_.next_literal(), TENURED);
scanner_.ScanRegExpFlags();
@@ -3872,17 +3218,17 @@ ZoneList<Expression*>* Parser::ParseArguments(bool* ok) {
// Arguments ::
// '(' (AssignmentExpression)*[','] ')'
- ZoneListWrapper<Expression> result = factory()->NewList<Expression>(4);
+ ZoneList<Expression*>* result = new ZoneList<Expression*>(4);
Expect(Token::LPAREN, CHECK_OK);
bool done = (peek() == Token::RPAREN);
while (!done) {
Expression* argument = ParseAssignmentExpression(true, CHECK_OK);
- result.Add(argument);
+ result->Add(argument);
done = (peek() == Token::RPAREN);
if (!done) Expect(Token::COMMA, CHECK_OK);
}
Expect(Token::RPAREN, CHECK_OK);
- return result.elements();
+ return result;
}
@@ -3898,9 +3244,9 @@ FunctionLiteral* Parser::ParseFunctionLiteral(Handle<String> var_name,
// this is the actual function name, otherwise this is the name of the
// variable declared and initialized with the function (expression). In
// that case, we don't have a function name (it's empty).
- Handle<String> name = is_named ? var_name : factory()->EmptySymbol();
+ Handle<String> name = is_named ? var_name : Factory::empty_symbol();
// The function name, if any.
- Handle<String> function_name = factory()->EmptySymbol();
+ Handle<String> function_name = Factory::empty_symbol();
if (is_named && (type == EXPRESSION || type == NESTED)) {
function_name = name;
}
@@ -3908,9 +3254,10 @@ FunctionLiteral* Parser::ParseFunctionLiteral(Handle<String> var_name,
int num_parameters = 0;
// Parse function body.
{ Scope* scope =
- factory()->NewScope(top_scope_, Scope::FUNCTION_SCOPE, inside_with());
- LexicalScope lexical_scope(this, scope);
- TemporaryScope temp_scope(this);
+ NewScope(top_scope_, Scope::FUNCTION_SCOPE, inside_with());
+ LexicalScope lexical_scope(&this->top_scope_, &this->with_nesting_level_,
+ scope);
+ TemporaryScope temp_scope(&this->temp_scope_);
top_scope_->SetScopeName(name);
// FormalParameterList ::
@@ -3920,18 +3267,16 @@ FunctionLiteral* Parser::ParseFunctionLiteral(Handle<String> var_name,
bool done = (peek() == Token::RPAREN);
while (!done) {
Handle<String> param_name = ParseIdentifier(CHECK_OK);
- if (!is_pre_parsing_) {
- top_scope_->AddParameter(top_scope_->DeclareLocal(param_name,
- Variable::VAR));
- num_parameters++;
- }
+ top_scope_->AddParameter(top_scope_->DeclareLocal(param_name,
+ Variable::VAR));
+ num_parameters++;
done = (peek() == Token::RPAREN);
if (!done) Expect(Token::COMMA, CHECK_OK);
}
Expect(Token::RPAREN, CHECK_OK);
Expect(Token::LBRACE, CHECK_OK);
- ZoneListWrapper<Statement> body = factory()->NewList<Statement>(8);
+ ZoneList<Statement*>* body = new ZoneList<Statement*>(8);
// If we have a named function expression, we add a local variable
// declaration to the body of the function with the name of the
@@ -3939,17 +3284,15 @@ FunctionLiteral* Parser::ParseFunctionLiteral(Handle<String> var_name,
// NOTE: We create a proxy and resolve it here so that in the
// future we can change the AST to only refer to VariableProxies
// instead of Variables and Proxis as is the case now.
- if (!is_pre_parsing_
- && !function_name.is_null()
- && function_name->length() > 0) {
+ if (!function_name.is_null() && function_name->length() > 0) {
Variable* fvar = top_scope_->DeclareFunctionVar(function_name);
VariableProxy* fproxy =
top_scope_->NewUnresolved(function_name, inside_with());
fproxy->BindTo(fvar);
- body.Add(new ExpressionStatement(
- new Assignment(Token::INIT_CONST, fproxy,
- NEW(ThisFunction()),
- RelocInfo::kNoPosition)));
+ body->Add(new ExpressionStatement(
+ new Assignment(Token::INIT_CONST, fproxy,
+ new ThisFunction(),
+ RelocInfo::kNoPosition)));
}
// Determine if the function will be lazily compiled. The mode can
@@ -3981,12 +3324,8 @@ FunctionLiteral* Parser::ParseFunctionLiteral(Handle<String> var_name,
this_property_assignments = Factory::empty_fixed_array();
Expect(Token::RBRACE, CHECK_OK);
} else {
- FunctionEntry entry;
- if (is_lazily_compiled) entry = log()->LogFunction(function_block_pos);
- {
- ConditionalLogPauseScope pause_if(is_lazily_compiled, log());
- ParseSourceElements(&body, Token::RBRACE, CHECK_OK);
- }
+ ParseSourceElements(body, Token::RBRACE, CHECK_OK);
+
materialized_literal_count = temp_scope.materialized_literal_count();
expected_property_count = temp_scope.expected_property_count();
only_simple_this_property_assignments =
@@ -3995,19 +3334,12 @@ FunctionLiteral* Parser::ParseFunctionLiteral(Handle<String> var_name,
Expect(Token::RBRACE, CHECK_OK);
end_pos = scanner_.location().end_pos;
- if (entry.is_valid()) {
- ASSERT(is_lazily_compiled);
- ASSERT(is_pre_parsing_);
- entry.set_end_pos(end_pos);
- entry.set_literal_count(materialized_literal_count);
- entry.set_property_count(expected_property_count);
- }
}
FunctionLiteral* function_literal =
- NEW(FunctionLiteral(name,
+ new FunctionLiteral(name,
top_scope_,
- body.elements(),
+ body,
materialized_literal_count,
expected_property_count,
only_simple_this_property_assignments,
@@ -4016,10 +3348,8 @@ FunctionLiteral* Parser::ParseFunctionLiteral(Handle<String> var_name,
start_pos,
end_pos,
function_name->length() > 0,
- temp_scope.ContainsLoops()));
- if (!is_pre_parsing_) {
- function_literal->set_function_token_position(function_token_position);
- }
+ temp_scope.ContainsLoops());
+ function_literal->set_function_token_position(function_token_position);
if (fni_ != NULL && !is_named) fni_->AddFunction(function_literal);
return function_literal;
@@ -4034,7 +3364,6 @@ Expression* Parser::ParseV8Intrinsic(bool* ok) {
Expect(Token::MOD, CHECK_OK);
Handle<String> name = ParseIdentifier(CHECK_OK);
ZoneList<Expression*>* args = ParseArguments(CHECK_OK);
- if (is_pre_parsing_) return NULL;
if (extension_ != NULL) {
// The extension structures are only accessible while parsing the
@@ -4070,7 +3399,7 @@ Expression* Parser::ParseV8Intrinsic(bool* ok) {
}
// We have a valid intrinsics call or a call to a builtin.
- return NEW(CallRuntime(name, function, args));
+ return new CallRuntime(name, function, args);
}
@@ -4118,12 +3447,12 @@ void Parser::ExpectSemicolon(bool* ok) {
Literal* Parser::GetLiteralUndefined() {
- return NEW(Literal(Factory::undefined_value()));
+ return new Literal(Factory::undefined_value());
}
Literal* Parser::GetLiteralTheHole() {
- return NEW(Literal(Factory::the_hole_value()));
+ return new Literal(Factory::the_hole_value());
}
@@ -4226,7 +3555,7 @@ void Parser::RegisterTargetUse(BreakTarget* target, Target* stop) {
Literal* Parser::NewNumberLiteral(double number) {
- return NEW(Literal(Factory::NewNumber(number, TENURED)));
+ return new Literal(Factory::NewNumber(number, TENURED));
}
@@ -4258,8 +3587,6 @@ Expression* Parser::NewThrowTypeError(Handle<String> type,
Expression* Parser::NewThrowError(Handle<String> constructor,
Handle<String> type,
Vector< Handle<Object> > arguments) {
- if (is_pre_parsing_) return NULL;
-
int argc = arguments.length();
Handle<JSArray> array = Factory::NewJSArray(argc, TENURED);
ASSERT(array->IsJSArray() && array->HasFastElements());
@@ -4281,145 +3608,165 @@ Expression* Parser::NewThrowError(Handle<String> constructor,
// ----------------------------------------------------------------------------
// JSON
-Expression* Parser::ParseJson(bool* ok) {
- Expression* result = ParseJsonValue(CHECK_OK);
- Expect(Token::EOS, CHECK_OK);
+Handle<Object> JsonParser::ParseJson(Handle<String> source) {
+ source->TryFlatten();
+ scanner_.Initialize(source, JSON);
+ Handle<Object> result = ParseJsonValue();
+ if (result.is_null() || scanner_.Next() != Token::EOS) {
+ if (scanner_.stack_overflow()) {
+ // Scanner failed.
+ Top::StackOverflow();
+ } else {
+ // Parse failed. Scanner's current token is the unexpected token.
+ Token::Value token = scanner_.current_token();
+
+ const char* message;
+ const char* name_opt = NULL;
+
+ switch (token) {
+ case Token::EOS:
+ message = "unexpected_eos";
+ break;
+ case Token::NUMBER:
+ message = "unexpected_token_number";
+ break;
+ case Token::STRING:
+ message = "unexpected_token_string";
+ break;
+ case Token::IDENTIFIER:
+ message = "unexpected_token_identifier";
+ break;
+ default:
+ message = "unexpected_token";
+ name_opt = Token::String(token);
+ ASSERT(name_opt != NULL);
+ break;
+ }
+
+ Scanner::Location source_location = scanner_.location();
+ MessageLocation location(Factory::NewScript(source),
+ source_location.beg_pos,
+ source_location.end_pos);
+ int argc = (name_opt == NULL) ? 0 : 1;
+ Handle<JSArray> array = Factory::NewJSArray(argc);
+ if (name_opt != NULL) {
+ SetElement(array,
+ 0,
+ Factory::NewStringFromUtf8(CStrVector(name_opt)));
+ }
+ Handle<Object> result = Factory::NewSyntaxError(message, array);
+ Top::Throw(*result, &location);
+ return Handle<Object>::null();
+ }
+ }
return result;
}
+Handle<String> JsonParser::GetString() {
+ int literal_length = scanner_.literal_length();
+ if (literal_length == 0) {
+ return Factory::empty_string();
+ }
+ const char* literal_string = scanner_.literal_string();
+ Vector<const char> literal(literal_string, literal_length);
+ return Factory::NewStringFromUtf8(literal);
+}
+
+
// Parse any JSON value.
-Expression* Parser::ParseJsonValue(bool* ok) {
- Token::Value token = peek();
+Handle<Object> JsonParser::ParseJsonValue() {
+ Token::Value token = scanner_.Next();
switch (token) {
case Token::STRING: {
- Consume(Token::STRING);
- int literal_length = scanner_.literal_length();
- const char* literal_string = scanner_.literal_string();
- if (literal_length == 0) {
- return NEW(Literal(Factory::empty_string()));
- }
- Vector<const char> literal(literal_string, literal_length);
- return NEW(Literal(Factory::NewStringFromUtf8(literal, TENURED)));
+ return GetString();
}
case Token::NUMBER: {
- Consume(Token::NUMBER);
- ASSERT(scanner_.literal_length() > 0);
double value = StringToDouble(scanner_.literal(),
NO_FLAGS, // Hex, octal or trailing junk.
OS::nan_value());
- return NewNumberLiteral(value);
+ return Factory::NewNumber(value);
}
case Token::FALSE_LITERAL:
- Consume(Token::FALSE_LITERAL);
- return NEW(Literal(Factory::false_value()));
+ return Factory::false_value();
case Token::TRUE_LITERAL:
- Consume(Token::TRUE_LITERAL);
- return NEW(Literal(Factory::true_value()));
+ return Factory::true_value();
case Token::NULL_LITERAL:
- Consume(Token::NULL_LITERAL);
- return NEW(Literal(Factory::null_value()));
- case Token::LBRACE: {
- Expression* result = ParseJsonObject(CHECK_OK);
- return result;
- }
- case Token::LBRACK: {
- Expression* result = ParseJsonArray(CHECK_OK);
- return result;
- }
+ return Factory::null_value();
+ case Token::LBRACE:
+ return ParseJsonObject();
+ case Token::LBRACK:
+ return ParseJsonArray();
default:
- *ok = false;
- ReportUnexpectedToken(token);
- return NULL;
+ return ReportUnexpectedToken();
}
}
// Parse a JSON object. Scanner must be right after '{' token.
-Expression* Parser::ParseJsonObject(bool* ok) {
- Consume(Token::LBRACE);
- ZoneListWrapper<ObjectLiteral::Property> properties =
- factory()->NewList<ObjectLiteral::Property>(4);
- int boilerplate_properties = 0;
- if (peek() != Token::RBRACE) {
+Handle<Object> JsonParser::ParseJsonObject() {
+ Handle<JSFunction> object_constructor(
+ Top::global_context()->object_function());
+ Handle<JSObject> json_object = Factory::NewJSObject(object_constructor);
+ if (scanner_.peek() == Token::RBRACE) {
+ scanner_.Next();
+ } else {
do {
- Expect(Token::STRING, CHECK_OK);
- Handle<String> key = GetSymbol(CHECK_OK);
- Expect(Token::COLON, CHECK_OK);
- Expression* value = ParseJsonValue(CHECK_OK);
- Literal* key_literal;
+ if (scanner_.Next() != Token::STRING) {
+ return ReportUnexpectedToken();
+ }
+ Handle<String> key = GetString();
+ if (scanner_.Next() != Token::COLON) {
+ return ReportUnexpectedToken();
+ }
+ Handle<Object> value = ParseJsonValue();
+ if (value.is_null()) return Handle<Object>::null();
uint32_t index;
if (key->AsArrayIndex(&index)) {
- key_literal = NewNumberLiteral(index);
+ SetElement(json_object, index, value);
} else {
- key_literal = NEW(Literal(key));
- }
- ObjectLiteral::Property* property =
- NEW(ObjectLiteral::Property(key_literal, value));
- properties.Add(property);
-
- if (IsBoilerplateProperty(property)) {
- boilerplate_properties++;
+ SetProperty(json_object, key, value, NONE);
}
- } while (Check(Token::COMMA));
+ } while (scanner_.Next() == Token::COMMA);
+ if (scanner_.current_token() != Token::RBRACE) {
+ return ReportUnexpectedToken();
+ }
}
- Expect(Token::RBRACE, CHECK_OK);
-
- int literal_index = temp_scope_->NextMaterializedLiteralIndex();
- if (is_pre_parsing_) return NULL;
-
- Handle<FixedArray> constant_properties =
- Factory::NewFixedArray(boilerplate_properties * 2, TENURED);
- bool is_simple = true;
- bool fast_elements = true;
- int depth = 1;
- BuildObjectLiteralConstantProperties(properties.elements(),
- constant_properties,
- &is_simple,
- &fast_elements,
- &depth);
- return new ObjectLiteral(constant_properties,
- properties.elements(),
- literal_index,
- is_simple,
- fast_elements,
- depth);
+ return json_object;
}
// Parse a JSON array. Scanner must be right after '[' token.
-Expression* Parser::ParseJsonArray(bool* ok) {
- Consume(Token::LBRACK);
+Handle<Object> JsonParser::ParseJsonArray() {
+ ZoneScope zone_scope(DELETE_ON_EXIT);
+ ZoneList<Handle<Object> > elements(4);
- ZoneListWrapper<Expression> values = factory()->NewList<Expression>(4);
- if (peek() != Token::RBRACK) {
+ Token::Value token = scanner_.peek();
+ if (token == Token::RBRACK) {
+ scanner_.Next();
+ } else {
do {
- Expression* exp = ParseJsonValue(CHECK_OK);
- values.Add(exp);
- } while (Check(Token::COMMA));
+ Handle<Object> element = ParseJsonValue();
+ if (element.is_null()) return Handle<Object>::null();
+ elements.Add(element);
+ token = scanner_.Next();
+ } while (token == Token::COMMA);
+ if (token != Token::RBRACK) {
+ return ReportUnexpectedToken();
+ }
}
- Expect(Token::RBRACK, CHECK_OK);
-
- // Update the scope information before the pre-parsing bailout.
- int literal_index = temp_scope_->NextMaterializedLiteralIndex();
- if (is_pre_parsing_) return NULL;
+ // Allocate a fixed array with all the elements.
+ Handle<FixedArray> fast_elements =
+ Factory::NewFixedArray(elements.length());
- // Allocate a fixed array with all the literals.
- Handle<FixedArray> literals =
- Factory::NewFixedArray(values.length(), TENURED);
+ for (int i = 0, n = elements.length(); i < n; i++) {
+ fast_elements->set(i, *elements[i]);
+ }
- bool is_simple;
- int depth;
- BuildArrayLiteralBoilerplateLiterals(values.elements(),
- literals,
- &is_simple,
- &depth);
- return NEW(ArrayLiteral(literals, values.elements(),
- literal_index, is_simple, depth));
+ return Factory::NewJSArrayWithElements(fast_elements);
}
-
// ----------------------------------------------------------------------------
// Regular expressions
@@ -4427,17 +3774,17 @@ Expression* Parser::ParseJsonArray(bool* ok) {
RegExpParser::RegExpParser(FlatStringReader* in,
Handle<String>* error,
bool multiline)
- : current_(kEndMarker),
+ : error_(error),
+ captures_(NULL),
+ in_(in),
+ current_(kEndMarker),
+ next_pos_(0),
+ capture_count_(0),
has_more_(true),
multiline_(multiline),
- next_pos_(0),
- in_(in),
- error_(error),
simple_(false),
contains_anchor_(false),
- captures_(NULL),
is_scanned_for_captures_(false),
- capture_count_(0),
failed_(false) {
Advance(1);
}
@@ -5245,23 +4592,6 @@ bool ScriptDataImpl::HasError() {
}
-// Preparse, but only collect data that is immediately useful,
-// even if the preparser data is only used once.
-ScriptDataImpl* Parser::PartialPreParse(Handle<String> source,
- unibrow::CharacterStream* stream,
- v8::Extension* extension) {
- Handle<Script> no_script;
- bool allow_natives_syntax =
- FLAG_allow_natives_syntax || Bootstrapper::IsActive();
- PartialPreParser parser(no_script, allow_natives_syntax, extension);
- if (!parser.PreParseProgram(source, stream)) return NULL;
- // Extract the accumulated data from the recorder as a single
- // contiguous vector that we are responsible for disposing.
- Vector<unsigned> store = parser.recorder()->ExtractData();
- return new ScriptDataImpl(store);
-}
-
-
void ScriptDataImpl::Initialize() {
// Prepares state for use.
if (store_.length() >= kHeaderSize) {
@@ -5305,24 +4635,57 @@ int ScriptDataImpl::ReadNumber(byte** source) {
}
-ScriptDataImpl* Parser::PreParse(Handle<String> source,
- unibrow::CharacterStream* stream,
- v8::Extension* extension) {
+// Preparse, but only collect data that is immediately useful,
+// even if the preparser data is only used once.
+ScriptDataImpl* ParserApi::PartialPreParse(Handle<String> source,
+ unibrow::CharacterStream* stream,
+ v8::Extension* extension) {
+ Handle<Script> no_script;
+ bool allow_lazy = FLAG_lazy && (extension == NULL);
+ if (!allow_lazy) {
+ // Partial preparsing is only about lazily compiled functions.
+ // If we don't allow lazy compilation, the log data will be empty.
+ return NULL;
+ }
+ preparser::PreParser<Scanner, PartialParserRecorder> parser;
+ Scanner scanner;
+ scanner.Initialize(source, stream, JAVASCRIPT);
+ PartialParserRecorder recorder;
+ if (!parser.PreParseProgram(&scanner, &recorder, allow_lazy)) {
+ Top::StackOverflow();
+ return NULL;
+ }
+
+ // Extract the accumulated data from the recorder as a single
+ // contiguous vector that we are responsible for disposing.
+ Vector<unsigned> store = recorder.ExtractData();
+ return new ScriptDataImpl(store);
+}
+
+
+ScriptDataImpl* ParserApi::PreParse(Handle<String> source,
+ unibrow::CharacterStream* stream,
+ v8::Extension* extension) {
Handle<Script> no_script;
- bool allow_natives_syntax =
- FLAG_allow_natives_syntax || Bootstrapper::IsActive();
- CompletePreParser parser(no_script, allow_natives_syntax, extension);
- if (!parser.PreParseProgram(source, stream)) return NULL;
+ preparser::PreParser<Scanner, CompleteParserRecorder> parser;
+ Scanner scanner;
+ scanner.Initialize(source, stream, JAVASCRIPT);
+ bool allow_lazy = FLAG_lazy && (extension == NULL);
+ CompleteParserRecorder recorder;
+ if (!parser.PreParseProgram(&scanner, &recorder, allow_lazy)) {
+ Top::StackOverflow();
+ return NULL;
+ }
// Extract the accumulated data from the recorder as a single
// contiguous vector that we are responsible for disposing.
- Vector<unsigned> store = parser.recorder()->ExtractData();
+ Vector<unsigned> store = recorder.ExtractData();
return new ScriptDataImpl(store);
}
-bool Parser::ParseRegExp(FlatStringReader* input,
- bool multiline,
- RegExpCompileData* result) {
+bool RegExpParser::ParseRegExp(FlatStringReader* input,
+ bool multiline,
+ RegExpCompileData* result) {
ASSERT(result != NULL);
RegExpParser parser(input, &result->error, multiline);
RegExpTree* tree = parser.ParsePattern();
@@ -5342,19 +4705,18 @@ bool Parser::ParseRegExp(FlatStringReader* input,
}
-bool Parser::Parse(CompilationInfo* info) {
+bool ParserApi::Parse(CompilationInfo* info) {
ASSERT(info->function() == NULL);
FunctionLiteral* result = NULL;
Handle<Script> script = info->script();
if (info->is_lazy()) {
- AstBuildingParser parser(script, true, NULL, NULL);
+ Parser parser(script, true, NULL, NULL);
result = parser.ParseLazy(info->shared_info());
} else {
bool allow_natives_syntax =
FLAG_allow_natives_syntax || Bootstrapper::IsActive();
ScriptDataImpl* pre_data = info->pre_parse_data();
- AstBuildingParser parser(script, allow_natives_syntax, info->extension(),
- pre_data);
+ Parser parser(script, allow_natives_syntax, info->extension(), pre_data);
if (pre_data != NULL && pre_data->has_error()) {
Scanner::Location loc = pre_data->MessageLocation();
const char* message = pre_data->BuildMessage();
@@ -5368,11 +4730,7 @@ bool Parser::Parse(CompilationInfo* info) {
ASSERT(Top::has_pending_exception());
} else {
Handle<String> source = Handle<String>(String::cast(script->source()));
- // JSON is always global.
- ASSERT(!info->is_json() || info->is_global());
- result = info->is_json()
- ? parser.ParseJson(source)
- : parser.ParseProgram(source, info->is_global());
+ result = parser.ParseProgram(source, info->is_global());
}
}
@@ -5380,6 +4738,4 @@ bool Parser::Parse(CompilationInfo* info) {
return (result != NULL);
}
-#undef NEW
-
} } // namespace v8::internal
diff --git a/src/parser.h b/src/parser.h
index 7142551c..667410b3 100644
--- a/src/parser.h
+++ b/src/parser.h
@@ -31,13 +31,13 @@
#include "allocation.h"
#include "ast.h"
#include "scanner.h"
+#include "scopes.h"
namespace v8 {
namespace internal {
class CompilationInfo;
class FuncNameInferrer;
-class ParserFactory;
class ParserLog;
class PositionStack;
class Target;
@@ -177,13 +177,127 @@ class ScriptDataImpl : public ScriptData {
};
-class Parser {
+// Record only functions.
+class PartialParserRecorder {
public:
- Parser(Handle<Script> script, bool allow_natives_syntax,
- v8::Extension* extension, ParserMode is_pre_parsing,
- ParserFactory* factory, ParserLog* log, ScriptDataImpl* pre_data);
- virtual ~Parser() { }
+ PartialParserRecorder();
+
+ void LogFunction(int start, int end, int literals, int properties) {
+ function_store_.Add(start);
+ function_store_.Add(end);
+ function_store_.Add(literals);
+ function_store_.Add(properties);
+ }
+
+ void LogSymbol(int start, const char* symbol, int length) { }
+
+ // Logs an error message and marks the log as containing an error.
+ // Further logging will be ignored, and ExtractData will return a vector
+ // representing the error only.
+ void LogMessage(int start,
+ int end,
+ const char* message,
+ const char* argument_opt) {
+ Scanner::Location location(start, end);
+ Vector<const char*> arguments;
+ if (argument_opt != NULL) {
+ arguments = Vector<const char*>(&argument_opt, 1);
+ }
+ this->LogMessage(location, message, arguments);
+ }
+
+ int function_position() { return function_store_.size(); }
+
+ void LogMessage(Scanner::Location loc,
+ const char* message,
+ Vector<const char*> args);
+
+ Vector<unsigned> ExtractData();
+
+ void PauseRecording() {
+ pause_count_++;
+ is_recording_ = false;
+ }
+
+ void ResumeRecording() {
+ ASSERT(pause_count_ > 0);
+ if (--pause_count_ == 0) is_recording_ = !has_error();
+ }
+
+ int symbol_position() { return 0; }
+ int symbol_ids() { return 0; }
+
+ protected:
+ bool has_error() {
+ return static_cast<bool>(preamble_[ScriptDataImpl::kHasErrorOffset]);
+ }
+
+ bool is_recording() {
+ return is_recording_;
+ }
+
+ void WriteString(Vector<const char> str);
+
+ Collector<unsigned> function_store_;
+ unsigned preamble_[ScriptDataImpl::kHeaderSize];
+ bool is_recording_;
+ int pause_count_;
+
+#ifdef DEBUG
+ int prev_start_;
+#endif
+};
+
+
+// Record both functions and symbols.
+class CompleteParserRecorder: public PartialParserRecorder {
+ public:
+ CompleteParserRecorder();
+
+ void LogSymbol(int start, Vector<const char> literal);
+
+ void LogSymbol(int start, const char* symbol, int length) {
+ LogSymbol(start, Vector<const char>(symbol, length));
+ }
+
+ Vector<unsigned> ExtractData();
+
+ int symbol_position() { return symbol_store_.size(); }
+ int symbol_ids() { return symbol_id_; }
+
+ private:
+ static int vector_hash(Vector<const char> string) {
+ int hash = 0;
+ for (int i = 0; i < string.length(); i++) {
+ int c = string[i];
+ hash += c;
+ hash += (hash << 10);
+ hash ^= (hash >> 6);
+ }
+ return hash;
+ }
+
+ static bool vector_compare(void* a, void* b) {
+ Vector<const char>* string1 = reinterpret_cast<Vector<const char>* >(a);
+ Vector<const char>* string2 = reinterpret_cast<Vector<const char>* >(b);
+ int length = string1->length();
+ if (string2->length() != length) return false;
+ return memcmp(string1->start(), string2->start(), length) == 0;
+ }
+
+ // Write a non-negative number to the symbol store.
+ void WriteNumber(int number);
+
+ Collector<byte> symbol_store_;
+ Collector<Vector<const char> > symbol_entries_;
+ HashMap symbol_table_;
+ int symbol_id_;
+};
+
+
+class ParserApi {
+ public:
// Parses the source code represented by the compilation info and sets its
// function literal. Returns false (and deallocates any allocated AST
// nodes) if parsing failed.
@@ -199,34 +313,263 @@ class Parser {
static ScriptDataImpl* PartialPreParse(Handle<String> source,
unibrow::CharacterStream* stream,
v8::Extension* extension);
+};
+
+// ----------------------------------------------------------------------------
+// REGEXP PARSING
+
+// A BuffferedZoneList is an automatically growing list, just like (and backed
+// by) a ZoneList, that is optimized for the case of adding and removing
+// a single element. The last element added is stored outside the backing list,
+// and if no more than one element is ever added, the ZoneList isn't even
+// allocated.
+// Elements must not be NULL pointers.
+template <typename T, int initial_size>
+class BufferedZoneList {
+ public:
+ BufferedZoneList() : list_(NULL), last_(NULL) {}
+
+ // Adds element at end of list. This element is buffered and can
+ // be read using last() or removed using RemoveLast until a new Add or until
+ // RemoveLast or GetList has been called.
+ void Add(T* value) {
+ if (last_ != NULL) {
+ if (list_ == NULL) {
+ list_ = new ZoneList<T*>(initial_size);
+ }
+ list_->Add(last_);
+ }
+ last_ = value;
+ }
+
+ T* last() {
+ ASSERT(last_ != NULL);
+ return last_;
+ }
+
+ T* RemoveLast() {
+ ASSERT(last_ != NULL);
+ T* result = last_;
+ if ((list_ != NULL) && (list_->length() > 0))
+ last_ = list_->RemoveLast();
+ else
+ last_ = NULL;
+ return result;
+ }
+
+ T* Get(int i) {
+ ASSERT((0 <= i) && (i < length()));
+ if (list_ == NULL) {
+ ASSERT_EQ(0, i);
+ return last_;
+ } else {
+ if (i == list_->length()) {
+ ASSERT(last_ != NULL);
+ return last_;
+ } else {
+ return list_->at(i);
+ }
+ }
+ }
+
+ void Clear() {
+ list_ = NULL;
+ last_ = NULL;
+ }
+
+ int length() {
+ int length = (list_ == NULL) ? 0 : list_->length();
+ return length + ((last_ == NULL) ? 0 : 1);
+ }
+
+ ZoneList<T*>* GetList() {
+ if (list_ == NULL) {
+ list_ = new ZoneList<T*>(initial_size);
+ }
+ if (last_ != NULL) {
+ list_->Add(last_);
+ last_ = NULL;
+ }
+ return list_;
+ }
+
+ private:
+ ZoneList<T*>* list_;
+ T* last_;
+};
+
+
+// Accumulates RegExp atoms and assertions into lists of terms and alternatives.
+class RegExpBuilder: public ZoneObject {
+ public:
+ RegExpBuilder();
+ void AddCharacter(uc16 character);
+ // "Adds" an empty expression. Does nothing except consume a
+ // following quantifier
+ void AddEmpty();
+ void AddAtom(RegExpTree* tree);
+ void AddAssertion(RegExpTree* tree);
+ void NewAlternative(); // '|'
+ void AddQuantifierToAtom(int min, int max, RegExpQuantifier::Type type);
+ RegExpTree* ToRegExp();
+
+ private:
+ void FlushCharacters();
+ void FlushText();
+ void FlushTerms();
+ bool pending_empty_;
+ ZoneList<uc16>* characters_;
+ BufferedZoneList<RegExpTree, 2> terms_;
+ BufferedZoneList<RegExpTree, 2> text_;
+ BufferedZoneList<RegExpTree, 2> alternatives_;
+#ifdef DEBUG
+ enum {ADD_NONE, ADD_CHAR, ADD_TERM, ADD_ASSERT, ADD_ATOM} last_added_;
+#define LAST(x) last_added_ = x;
+#else
+#define LAST(x)
+#endif
+};
+
+
+class RegExpParser {
+ public:
+ RegExpParser(FlatStringReader* in,
+ Handle<String>* error,
+ bool multiline_mode);
static bool ParseRegExp(FlatStringReader* input,
bool multiline,
RegExpCompileData* result);
- // Pre-parse the program from the character stream; returns true on
- // success, false if a stack-overflow happened during parsing.
- bool PreParseProgram(Handle<String> source, unibrow::CharacterStream* stream);
+ RegExpTree* ParsePattern();
+ RegExpTree* ParseDisjunction();
+ RegExpTree* ParseGroup();
+ RegExpTree* ParseCharacterClass();
+
+ // Parses a {...,...} quantifier and stores the range in the given
+ // out parameters.
+ bool ParseIntervalQuantifier(int* min_out, int* max_out);
+
+ // Parses and returns a single escaped character. The character
+ // must not be 'b' or 'B' since they are usually handle specially.
+ uc32 ParseClassCharacterEscape();
+
+ // Checks whether the following is a length-digit hexadecimal number,
+ // and sets the value if it is.
+ bool ParseHexEscape(int length, uc32* value);
+
+ uc32 ParseControlLetterEscape();
+ uc32 ParseOctalLiteral();
+
+ // Tries to parse the input as a back reference. If successful it
+ // stores the result in the output parameter and returns true. If
+ // it fails it will push back the characters read so the same characters
+ // can be reparsed.
+ bool ParseBackReferenceIndex(int* index_out);
+
+ CharacterRange ParseClassAtom(uc16* char_class);
+ RegExpTree* ReportError(Vector<const char> message);
+ void Advance();
+ void Advance(int dist);
+ void Reset(int pos);
+
+ // Reports whether the pattern might be used as a literal search string.
+ // Only use if the result of the parse is a single atom node.
+ bool simple();
+ bool contains_anchor() { return contains_anchor_; }
+ void set_contains_anchor() { contains_anchor_ = true; }
+ int captures_started() { return captures_ == NULL ? 0 : captures_->length(); }
+ int position() { return next_pos_ - 1; }
+ bool failed() { return failed_; }
+
+ static const int kMaxCaptures = 1 << 16;
+ static const uc32 kEndMarker = (1 << 21);
- void ReportMessage(const char* message, Vector<const char*> args);
- virtual void ReportMessageAt(Scanner::Location loc,
- const char* message,
- Vector<const char*> args) = 0;
+ private:
+ enum SubexpressionType {
+ INITIAL,
+ CAPTURE, // All positive values represent captures.
+ POSITIVE_LOOKAHEAD,
+ NEGATIVE_LOOKAHEAD,
+ GROUPING
+ };
+
+ class RegExpParserState : public ZoneObject {
+ public:
+ RegExpParserState(RegExpParserState* previous_state,
+ SubexpressionType group_type,
+ int disjunction_capture_index)
+ : previous_state_(previous_state),
+ builder_(new RegExpBuilder()),
+ group_type_(group_type),
+ disjunction_capture_index_(disjunction_capture_index) {}
+ // Parser state of containing expression, if any.
+ RegExpParserState* previous_state() { return previous_state_; }
+ bool IsSubexpression() { return previous_state_ != NULL; }
+ // RegExpBuilder building this regexp's AST.
+ RegExpBuilder* builder() { return builder_; }
+ // Type of regexp being parsed (parenthesized group or entire regexp).
+ SubexpressionType group_type() { return group_type_; }
+ // Index in captures array of first capture in this sub-expression, if any.
+ // Also the capture index of this sub-expression itself, if group_type
+ // is CAPTURE.
+ int capture_index() { return disjunction_capture_index_; }
+
+ private:
+ // Linked list implementation of stack of states.
+ RegExpParserState* previous_state_;
+ // Builder for the stored disjunction.
+ RegExpBuilder* builder_;
+ // Stored disjunction type (capture, look-ahead or grouping), if any.
+ SubexpressionType group_type_;
+ // Stored disjunction's capture index (if any).
+ int disjunction_capture_index_;
+ };
+
+ uc32 current() { return current_; }
+ bool has_more() { return has_more_; }
+ bool has_next() { return next_pos_ < in()->length(); }
+ uc32 Next();
+ FlatStringReader* in() { return in_; }
+ void ScanForCaptures();
+
+ Handle<String>* error_;
+ ZoneList<RegExpCapture*>* captures_;
+ FlatStringReader* in_;
+ uc32 current_;
+ int next_pos_;
+ // The capture count is only valid after we have scanned for captures.
+ int capture_count_;
+ bool has_more_;
+ bool multiline_;
+ bool simple_;
+ bool contains_anchor_;
+ bool is_scanned_for_captures_;
+ bool failed_;
+};
+
+// ----------------------------------------------------------------------------
+// JAVASCRIPT PARSING
+class Parser {
+ public:
+ Parser(Handle<Script> script,
+ bool allow_natives_syntax,
+ v8::Extension* extension,
+ ScriptDataImpl* pre_data);
+ virtual ~Parser() { }
// Returns NULL if parsing failed.
FunctionLiteral* ParseProgram(Handle<String> source,
bool in_global_context);
+
FunctionLiteral* ParseLazy(Handle<SharedFunctionInfo> info);
- FunctionLiteral* ParseJson(Handle<String> source);
- // The minimum number of contiguous assignment that will
- // be treated as an initialization block. Benchmarks show that
- // the overhead exceeds the savings below this limit.
- static const int kMinInitializationBlock = 3;
+ void ReportMessageAt(Scanner::Location loc,
+ const char* message,
+ Vector<const char*> args);
protected:
-
enum Mode {
PARSE_LAZILY,
PARSE_EAGERLY
@@ -235,28 +578,9 @@ class Parser {
// Report syntax error
void ReportUnexpectedToken(Token::Value token);
void ReportInvalidPreparseData(Handle<String> name, bool* ok);
-
- Handle<Script> script_;
- Scanner scanner_;
-
- Scope* top_scope_;
- int with_nesting_level_;
-
- TemporaryScope* temp_scope_;
- Mode mode_;
-
- Target* target_stack_; // for break, continue statements
- bool allow_natives_syntax_;
- v8::Extension* extension_;
- ParserFactory* factory_;
- ParserLog* log_;
- bool is_pre_parsing_;
- ScriptDataImpl* pre_data_;
- FuncNameInferrer* fni_;
+ void ReportMessage(const char* message, Vector<const char*> args);
bool inside_with() const { return with_nesting_level_ > 0; }
- ParserFactory* factory() const { return factory_; }
- ParserLog* log() const { return log_; }
Scanner& scanner() { return scanner_; }
Mode mode() const { return mode_; }
ScriptDataImpl* pre_data() const { return pre_data_; }
@@ -265,7 +589,7 @@ class Parser {
// which is set to false if parsing failed; it is unchanged otherwise.
// By making the 'exception handling' explicit, we are forced to check
// for failure at the call sites.
- void* ParseSourceElements(ZoneListWrapper<Statement>* processor,
+ void* ParseSourceElements(ZoneList<Statement*>* processor,
int end_token, bool* ok);
Statement* ParseStatement(ZoneStringList* labels, bool* ok);
Statement* ParseFunctionDeclaration(bool* ok);
@@ -378,10 +702,10 @@ class Parser {
bool* ok);
// Parser support
- virtual VariableProxy* Declare(Handle<String> name, Variable::Mode mode,
- FunctionLiteral* fun,
- bool resolve,
- bool* ok) = 0;
+ VariableProxy* Declare(Handle<String> name, Variable::Mode mode,
+ FunctionLiteral* fun,
+ bool resolve,
+ bool* ok);
bool TargetStackContainsLabel(Handle<String> label);
BreakableStatement* LookupBreakTarget(Handle<String> label, bool* ok);
@@ -389,6 +713,28 @@ class Parser {
void RegisterTargetUse(BreakTarget* target, Target* stop);
+ // Factory methods.
+
+ Statement* EmptyStatement() {
+ static v8::internal::EmptyStatement empty;
+ return &empty;
+ }
+
+ Scope* NewScope(Scope* parent, Scope::Type type, bool inside_with);
+
+ Handle<String> LookupSymbol(int symbol_id,
+ Vector<const char> string);
+
+ Handle<String> LookupCachedSymbol(int symbol_id,
+ Vector<const char> string);
+
+ Expression* NewCall(Expression* expression,
+ ZoneList<Expression*>* arguments,
+ int pos) {
+ return new Call(expression, arguments, pos);
+ }
+
+
// Create a number literal.
Literal* NewNumberLiteral(double value);
@@ -411,33 +757,23 @@ class Parser {
Handle<String> type,
Vector< Handle<Object> > arguments);
- // JSON is a subset of JavaScript, as specified in, e.g., the ECMAScript 5
- // specification section 15.12.1 (and appendix A.8).
- // The grammar is given section 15.12.1.2 (and appendix A.8.2).
+ ZoneList<Handle<String> > symbol_cache_;
- // Parse JSON input as a single JSON value.
- Expression* ParseJson(bool* ok);
+ Handle<Script> script_;
+ Scanner scanner_;
- // Parse a single JSON value from input (grammar production JSONValue).
- // A JSON value is either a (double-quoted) string literal, a number literal,
- // one of "true", "false", or "null", or an object or array literal.
- Expression* ParseJsonValue(bool* ok);
- // Parse a JSON object literal (grammar production JSONObject).
- // An object literal is a squiggly-braced and comma separated sequence
- // (possibly empty) of key/value pairs, where the key is a JSON string
- // literal, the value is a JSON value, and the two are spearated by a colon.
- // A JavaScript object also allows numbers and identifiers as keys.
- Expression* ParseJsonObject(bool* ok);
- // Parses a JSON array literal (grammar production JSONArray). An array
- // literal is a square-bracketed and comma separated sequence (possibly empty)
- // of JSON values.
- // A JavaScript array allows leaving out values from the sequence.
- Expression* ParseJsonArray(bool* ok);
+ Scope* top_scope_;
+ int with_nesting_level_;
- friend class Target;
- friend class TargetScope;
- friend class LexicalScope;
- friend class TemporaryScope;
+ TemporaryScope* temp_scope_;
+ Mode mode_;
+
+ Target* target_stack_; // for break, continue statements
+ bool allow_natives_syntax_;
+ v8::Extension* extension_;
+ bool is_pre_parsing_;
+ ScriptDataImpl* pre_data_;
+ FuncNameInferrer* fni_;
};
@@ -472,6 +808,52 @@ class CompileTimeValue: public AllStatic {
};
+// ----------------------------------------------------------------------------
+// JSON PARSING
+
+// JSON is a subset of JavaScript, as specified in, e.g., the ECMAScript 5
+// specification section 15.12.1 (and appendix A.8).
+// The grammar is given section 15.12.1.2 (and appendix A.8.2).
+class JsonParser BASE_EMBEDDED {
+ public:
+ // Parse JSON input as a single JSON value.
+ // Returns null handle and sets exception if parsing failed.
+ static Handle<Object> Parse(Handle<String> source) {
+ return JsonParser().ParseJson(source);
+ }
+
+ private:
+ JsonParser() { }
+ ~JsonParser() { }
+
+ // Parse a string containing a single JSON value.
+ Handle<Object> ParseJson(Handle<String>);
+ // Parse a single JSON value from input (grammar production JSONValue).
+ // A JSON value is either a (double-quoted) string literal, a number literal,
+ // one of "true", "false", or "null", or an object or array literal.
+ Handle<Object> ParseJsonValue();
+ // Parse a JSON object literal (grammar production JSONObject).
+ // An object literal is a squiggly-braced and comma separated sequence
+ // (possibly empty) of key/value pairs, where the key is a JSON string
+ // literal, the value is a JSON value, and the two are separated by a colon.
+ // A JSON array dosn't allow numbers and identifiers as keys, like a
+ // JavaScript array.
+ Handle<Object> ParseJsonObject();
+ // Parses a JSON array literal (grammar production JSONArray). An array
+ // literal is a square-bracketed and comma separated sequence (possibly empty)
+ // of JSON values.
+ // A JSON array doesn't allow leaving out values from the sequence, nor does
+ // it allow a terminal comma, like a JavaScript array does.
+ Handle<Object> ParseJsonArray();
+
+ // Mark that a parsing error has happened at the current token, and
+ // return a null handle. Primarily for readability.
+ Handle<Object> ReportUnexpectedToken() { return Handle<Object>::null(); }
+ // Converts the currently parsed literal to a JavaScript String.
+ Handle<String> GetString();
+
+ Scanner scanner_;
+};
} } // namespace v8::internal
#endif // V8_PARSER_H_
diff --git a/src/platform-linux.cc b/src/platform-linux.cc
index eefaec90..89003ba8 100644
--- a/src/platform-linux.cc
+++ b/src/platform-linux.cc
@@ -99,30 +99,12 @@ uint64_t OS::CpuFeaturesImpliedByPlatform() {
#ifdef __arm__
-bool OS::ArmCpuHasFeature(CpuFeature feature) {
- const char* search_string = NULL;
+static bool CPUInfoContainsString(const char * search_string) {
const char* file_name = "/proc/cpuinfo";
- // Simple detection of VFP at runtime for Linux.
- // It is based on /proc/cpuinfo, which reveals hardware configuration
- // to user-space applications. According to ARM (mid 2009), no similar
- // facility is universally available on the ARM architectures,
- // so it's up to individual OSes to provide such.
- //
// This is written as a straight shot one pass parser
// and not using STL string and ifstream because,
// on Linux, it's reading from a (non-mmap-able)
// character special device.
- switch (feature) {
- case VFP3:
- search_string = "vfp";
- break;
- case ARMv7:
- search_string = "ARMv7";
- break;
- default:
- UNREACHABLE();
- }
-
FILE* f = NULL;
const char* what = search_string;
@@ -149,6 +131,43 @@ bool OS::ArmCpuHasFeature(CpuFeature feature) {
// Did not find string in the proc file.
return false;
}
+
+bool OS::ArmCpuHasFeature(CpuFeature feature) {
+ const int max_items = 2;
+ const char* search_strings[max_items] = { NULL, NULL };
+ int search_items = 0;
+ // Simple detection of VFP at runtime for Linux.
+ // It is based on /proc/cpuinfo, which reveals hardware configuration
+ // to user-space applications. According to ARM (mid 2009), no similar
+ // facility is universally available on the ARM architectures,
+ // so it's up to individual OSes to provide such.
+ switch (feature) {
+ case VFP3:
+ search_strings[0] = "vfpv3";
+ // Some old kernels will report vfp for A8, not vfpv3, so we check for
+ // A8 explicitely. The cpuinfo file report the CPU Part which for Cortex
+ // A8 is 0xc08.
+ search_strings[1] = "0xc08";
+ search_items = 2;
+ ASSERT(search_items <= max_items);
+ break;
+ case ARMv7:
+ search_strings[0] = "ARMv7" ;
+ search_items = 1;
+ ASSERT(search_items <= max_items);
+ break;
+ default:
+ UNREACHABLE();
+ }
+
+ for (int i = 0; i < search_items; ++i) {
+ if (CPUInfoContainsString(search_strings[i])) {
+ return true;
+ }
+ }
+
+ return false;
+}
#endif // def __arm__
@@ -809,8 +828,17 @@ class Sampler::PlatformData : public Malloced {
syscall(SYS_tgkill, vm_tgid_, vm_tid_, SIGPROF);
// Convert ms to us and subtract 100 us to compensate delays
// occuring during signal delivery.
- int result = usleep(sampler_->interval_ * 1000 - 100);
- ASSERT(result == 0 || errno == EINTR);
+ const useconds_t interval = sampler_->interval_ * 1000 - 100;
+ int result = usleep(interval);
+#ifdef DEBUG
+ if (result != 0 && errno != EINTR) {
+ fprintf(stderr,
+ "SignalSender usleep error; interval = %u, errno = %d\n",
+ interval,
+ errno);
+ ASSERT(result == 0 || errno == EINTR);
+ }
+#endif
USE(result);
}
}
@@ -843,6 +871,7 @@ Sampler::Sampler(int interval, bool profiling)
Sampler::~Sampler() {
+ ASSERT(!data_->signal_sender_launched_);
delete data_;
}
@@ -856,7 +885,7 @@ void Sampler::Start() {
struct sigaction sa;
sa.sa_sigaction = ProfilerSignalHandler;
sigemptyset(&sa.sa_mask);
- sa.sa_flags = SA_SIGINFO;
+ sa.sa_flags = SA_RESTART | SA_SIGINFO;
if (sigaction(SIGPROF, &sa, &data_->old_signal_handler_) != 0) return;
data_->signal_handler_installed_ = true;
diff --git a/src/preparser.h b/src/preparser.h
new file mode 100644
index 00000000..44c55cf7
--- /dev/null
+++ b/src/preparser.h
@@ -0,0 +1,1414 @@
+// Copyright 2010 the V8 project authors. All rights reserved.
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following
+// disclaimer in the documentation and/or other materials provided
+// with the distribution.
+// * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived
+// from this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+#ifndef V8_PREPARSER_H
+#define V8_PREPARSER_H
+
+#include "unicode.h"
+
+namespace v8 {
+namespace preparser {
+
+// Preparsing checks a JavaScript program and emits preparse-data that helps
+// a later parsing to be faster.
+// See preparser-data.h for the data.
+
+// The PreParser checks that the syntax follows the grammar for JavaScript,
+// and collects some information about the program along the way.
+// The grammar check is only performed in order to understand the program
+// sufficiently to deduce some information about it, that can be used
+// to speed up later parsing. Finding errors is not the goal of pre-parsing,
+// rather it is to speed up properly written and correct programs.
+// That means that contextual checks (like a label being declared where
+// it is used) are generally omitted.
+
+namespace i = v8::internal;
+
+enum StatementType {
+ kUnknownStatement
+};
+
+enum ExpressionType {
+ kUnknownExpression,
+ kIdentifierExpression, // Used to detect labels.
+ kThisExpression,
+ kThisPropertyExpression
+};
+
+enum IdentifierType {
+ kUnknownIdentifier
+};
+
+enum SourceElementTypes {
+ kUnknownSourceElements
+};
+
+
+typedef int SourceElements;
+typedef int Expression;
+typedef int Statement;
+typedef int Identifier;
+typedef int Arguments;
+
+
+template <typename Scanner, typename PreParserLog>
+class PreParser {
+ public:
+ PreParser() : scope_(NULL), allow_lazy_(true) { }
+ ~PreParser() { }
+
+ // Pre-parse the program from the character stream; returns true on
+ // success (even if parsing failed, the pre-parse data successfully
+ // captured the syntax error), and false if a stack-overflow happened
+ // during parsing.
+ bool PreParseProgram(Scanner* scanner,
+ PreParserLog* log,
+ bool allow_lazy) {
+ allow_lazy_ = allow_lazy;
+ scanner_ = scanner;
+ log_ = log;
+ Scope top_scope(&scope_, kTopLevelScope);
+ bool ok = true;
+ ParseSourceElements(i::Token::EOS, &ok);
+ bool stack_overflow = scanner_->stack_overflow();
+ if (!ok && !stack_overflow) {
+ ReportUnexpectedToken(scanner_->current_token());
+ }
+ return !stack_overflow;
+ }
+
+ private:
+ enum ScopeType {
+ kTopLevelScope,
+ kFunctionScope
+ };
+
+ class Scope {
+ public:
+ Scope(Scope** variable, ScopeType type)
+ : variable_(variable),
+ prev_(*variable),
+ type_(type),
+ materialized_literal_count_(0),
+ expected_properties_(0),
+ with_nesting_count_(0) {
+ *variable = this;
+ }
+ ~Scope() { *variable_ = prev_; }
+ void NextMaterializedLiteralIndex() { materialized_literal_count_++; }
+ void AddProperty() { expected_properties_++; }
+ ScopeType type() { return type_; }
+ int expected_properties() { return expected_properties_; }
+ int materialized_literal_count() { return materialized_literal_count_; }
+ bool IsInsideWith() { return with_nesting_count_ != 0; }
+ void EnterWith() { with_nesting_count_++; }
+ void LeaveWith() { with_nesting_count_--; }
+
+ private:
+ Scope** const variable_;
+ Scope* const prev_;
+ const ScopeType type_;
+ int materialized_literal_count_;
+ int expected_properties_;
+ int with_nesting_count_;
+ };
+
+ // Types that allow us to recognize simple this-property assignments.
+ // A simple this-property assignment is a statement on the form
+ // "this.propertyName = {primitive constant or function parameter name);"
+ // where propertyName isn't "__proto__".
+ // The result is only relevant if the function body contains only
+ // simple this-property assignments.
+
+ // Report syntax error
+ void ReportUnexpectedToken(i::Token::Value token);
+ void ReportMessageAt(int start_pos,
+ int end_pos,
+ const char* type,
+ const char* name_opt) {
+ log_->LogMessage(start_pos, end_pos, type, name_opt);
+ }
+
+ // All ParseXXX functions take as the last argument an *ok parameter
+ // which is set to false if parsing failed; it is unchanged otherwise.
+ // By making the 'exception handling' explicit, we are forced to check
+ // for failure at the call sites.
+ SourceElements ParseSourceElements(int end_token, bool* ok);
+ Statement ParseStatement(bool* ok);
+ Statement ParseFunctionDeclaration(bool* ok);
+ Statement ParseNativeDeclaration(bool* ok);
+ Statement ParseBlock(bool* ok);
+ Statement ParseVariableStatement(bool* ok);
+ Statement ParseVariableDeclarations(bool accept_IN, int* num_decl, bool* ok);
+ Statement ParseExpressionOrLabelledStatement(bool* ok);
+ Statement ParseIfStatement(bool* ok);
+ Statement ParseContinueStatement(bool* ok);
+ Statement ParseBreakStatement(bool* ok);
+ Statement ParseReturnStatement(bool* ok);
+ Statement ParseWithStatement(bool* ok);
+ Statement ParseSwitchStatement(bool* ok);
+ Statement ParseDoWhileStatement(bool* ok);
+ Statement ParseWhileStatement(bool* ok);
+ Statement ParseForStatement(bool* ok);
+ Statement ParseThrowStatement(bool* ok);
+ Statement ParseTryStatement(bool* ok);
+ Statement ParseDebuggerStatement(bool* ok);
+
+ Expression ParseExpression(bool accept_IN, bool* ok);
+ Expression ParseAssignmentExpression(bool accept_IN, bool* ok);
+ Expression ParseConditionalExpression(bool accept_IN, bool* ok);
+ Expression ParseBinaryExpression(int prec, bool accept_IN, bool* ok);
+ Expression ParseUnaryExpression(bool* ok);
+ Expression ParsePostfixExpression(bool* ok);
+ Expression ParseLeftHandSideExpression(bool* ok);
+ Expression ParseNewExpression(bool* ok);
+ Expression ParseMemberExpression(bool* ok);
+ Expression ParseMemberWithNewPrefixesExpression(unsigned new_count, bool* ok);
+ Expression ParsePrimaryExpression(bool* ok);
+ Expression ParseArrayLiteral(bool* ok);
+ Expression ParseObjectLiteral(bool* ok);
+ Expression ParseRegExpLiteral(bool seen_equal, bool* ok);
+ Expression ParseV8Intrinsic(bool* ok);
+
+ Arguments ParseArguments(bool* ok);
+ Expression ParseFunctionLiteral(bool* ok);
+
+ Identifier ParseIdentifier(bool* ok);
+ Identifier ParseIdentifierName(bool* ok);
+ Identifier ParseIdentifierOrGetOrSet(bool* is_get, bool* is_set, bool* ok);
+
+ Identifier GetIdentifierSymbol();
+ unsigned int HexDigitValue(char digit);
+ Expression GetStringSymbol();
+
+
+ i::Token::Value peek() { return scanner_->peek(); }
+ i::Token::Value Next() {
+ i::Token::Value next = scanner_->Next();
+ return next;
+ }
+
+ void Consume(i::Token::Value token) {
+ Next();
+ }
+
+ void Expect(i::Token::Value token, bool* ok) {
+ if (Next() != token) {
+ *ok = false;
+ }
+ }
+
+ bool Check(i::Token::Value token) {
+ i::Token::Value next = peek();
+ if (next == token) {
+ Consume(next);
+ return true;
+ }
+ return false;
+ }
+ void ExpectSemicolon(bool* ok);
+
+ static int Precedence(i::Token::Value tok, bool accept_IN);
+
+ Scanner* scanner_;
+ PreParserLog* log_;
+ Scope* scope_;
+ bool allow_lazy_;
+};
+
+
+#define CHECK_OK ok); \
+ if (!*ok) return -1; \
+ ((void)0
+#define DUMMY ) // to make indentation work
+#undef DUMMY
+
+
+template <typename Scanner, typename Log>
+void PreParser<Scanner, Log>::ReportUnexpectedToken(i::Token::Value token) {
+ // We don't report stack overflows here, to avoid increasing the
+ // stack depth even further. Instead we report it after parsing is
+ // over, in ParseProgram.
+ if (token == i::Token::ILLEGAL && scanner_->stack_overflow()) {
+ return;
+ }
+ typename Scanner::Location source_location = scanner_->location();
+
+ // Four of the tokens are treated specially
+ switch (token) {
+ case i::Token::EOS:
+ return ReportMessageAt(source_location.beg_pos, source_location.end_pos,
+ "unexpected_eos", NULL);
+ case i::Token::NUMBER:
+ return ReportMessageAt(source_location.beg_pos, source_location.end_pos,
+ "unexpected_token_number", NULL);
+ case i::Token::STRING:
+ return ReportMessageAt(source_location.beg_pos, source_location.end_pos,
+ "unexpected_token_string", NULL);
+ case i::Token::IDENTIFIER:
+ return ReportMessageAt(source_location.beg_pos, source_location.end_pos,
+ "unexpected_token_identifier", NULL);
+ default:
+ const char* name = i::Token::String(token);
+ ReportMessageAt(source_location.beg_pos, source_location.end_pos,
+ "unexpected_token", name);
+ }
+}
+
+
+template <typename Scanner, typename Log>
+SourceElements PreParser<Scanner, Log>::ParseSourceElements(int end_token,
+ bool* ok) {
+ // SourceElements ::
+ // (Statement)* <end_token>
+
+ while (peek() != end_token) {
+ ParseStatement(CHECK_OK);
+ }
+ return kUnknownSourceElements;
+}
+
+
+template <typename Scanner, typename Log>
+Statement PreParser<Scanner, Log>::ParseStatement(bool* ok) {
+ // Statement ::
+ // Block
+ // VariableStatement
+ // EmptyStatement
+ // ExpressionStatement
+ // IfStatement
+ // IterationStatement
+ // ContinueStatement
+ // BreakStatement
+ // ReturnStatement
+ // WithStatement
+ // LabelledStatement
+ // SwitchStatement
+ // ThrowStatement
+ // TryStatement
+ // DebuggerStatement
+
+ // Note: Since labels can only be used by 'break' and 'continue'
+ // statements, which themselves are only valid within blocks,
+ // iterations or 'switch' statements (i.e., BreakableStatements),
+ // labels can be simply ignored in all other cases; except for
+ // trivial labeled break statements 'label: break label' which is
+ // parsed into an empty statement.
+
+ // Keep the source position of the statement
+ switch (peek()) {
+ case i::Token::LBRACE:
+ return ParseBlock(ok);
+
+ case i::Token::CONST:
+ case i::Token::VAR:
+ return ParseVariableStatement(ok);
+
+ case i::Token::SEMICOLON:
+ Next();
+ return kUnknownStatement;
+
+ case i::Token::IF:
+ return ParseIfStatement(ok);
+
+ case i::Token::DO:
+ return ParseDoWhileStatement(ok);
+
+ case i::Token::WHILE:
+ return ParseWhileStatement(ok);
+
+ case i::Token::FOR:
+ return ParseForStatement(ok);
+
+ case i::Token::CONTINUE:
+ return ParseContinueStatement(ok);
+
+ case i::Token::BREAK:
+ return ParseBreakStatement(ok);
+
+ case i::Token::RETURN:
+ return ParseReturnStatement(ok);
+
+ case i::Token::WITH:
+ return ParseWithStatement(ok);
+
+ case i::Token::SWITCH:
+ return ParseSwitchStatement(ok);
+
+ case i::Token::THROW:
+ return ParseThrowStatement(ok);
+
+ case i::Token::TRY:
+ return ParseTryStatement(ok);
+
+ case i::Token::FUNCTION:
+ return ParseFunctionDeclaration(ok);
+
+ case i::Token::NATIVE:
+ return ParseNativeDeclaration(ok);
+
+ case i::Token::DEBUGGER:
+ return ParseDebuggerStatement(ok);
+
+ default:
+ return ParseExpressionOrLabelledStatement(ok);
+ }
+}
+
+
+template <typename Scanner, typename Log>
+Statement PreParser<Scanner, Log>::ParseFunctionDeclaration(bool* ok) {
+ // FunctionDeclaration ::
+ // 'function' Identifier '(' FormalParameterListopt ')' '{' FunctionBody '}'
+ Expect(i::Token::FUNCTION, CHECK_OK);
+ ParseIdentifier(CHECK_OK);
+ ParseFunctionLiteral(CHECK_OK);
+ return kUnknownStatement;
+}
+
+
+// Language extension which is only enabled for source files loaded
+// through the API's extension mechanism. A native function
+// declaration is resolved by looking up the function through a
+// callback provided by the extension.
+template <typename Scanner, typename Log>
+Statement PreParser<Scanner, Log>::ParseNativeDeclaration(bool* ok) {
+ Expect(i::Token::NATIVE, CHECK_OK);
+ Expect(i::Token::FUNCTION, CHECK_OK);
+ ParseIdentifier(CHECK_OK);
+ Expect(i::Token::LPAREN, CHECK_OK);
+ bool done = (peek() == i::Token::RPAREN);
+ while (!done) {
+ ParseIdentifier(CHECK_OK);
+ done = (peek() == i::Token::RPAREN);
+ if (!done) {
+ Expect(i::Token::COMMA, CHECK_OK);
+ }
+ }
+ Expect(i::Token::RPAREN, CHECK_OK);
+ Expect(i::Token::SEMICOLON, CHECK_OK);
+ return kUnknownStatement;
+}
+
+
+template <typename Scanner, typename Log>
+Statement PreParser<Scanner, Log>::ParseBlock(bool* ok) {
+ // Block ::
+ // '{' Statement* '}'
+
+ // Note that a Block does not introduce a new execution scope!
+ // (ECMA-262, 3rd, 12.2)
+ //
+ Expect(i::Token::LBRACE, CHECK_OK);
+ while (peek() != i::Token::RBRACE) {
+ ParseStatement(CHECK_OK);
+ }
+ Expect(i::Token::RBRACE, CHECK_OK);
+ return kUnknownStatement;
+}
+
+
+template <typename Scanner, typename Log>
+Statement PreParser<Scanner, Log>::ParseVariableStatement(bool* ok) {
+ // VariableStatement ::
+ // VariableDeclarations ';'
+
+ Statement result = ParseVariableDeclarations(true, NULL, CHECK_OK);
+ ExpectSemicolon(CHECK_OK);
+ return result;
+}
+
+
+// If the variable declaration declares exactly one non-const
+// variable, then *var is set to that variable. In all other cases,
+// *var is untouched; in particular, it is the caller's responsibility
+// to initialize it properly. This mechanism is also used for the parsing
+// of 'for-in' loops.
+template <typename Scanner, typename Log>
+Statement PreParser<Scanner, Log>::ParseVariableDeclarations(bool accept_IN,
+ int* num_decl,
+ bool* ok) {
+ // VariableDeclarations ::
+ // ('var' | 'const') (Identifier ('=' AssignmentExpression)?)+[',']
+
+ if (peek() == i::Token::VAR) {
+ Consume(i::Token::VAR);
+ } else if (peek() == i::Token::CONST) {
+ Consume(i::Token::CONST);
+ } else {
+ *ok = false;
+ return 0;
+ }
+
+ // The scope of a variable/const declared anywhere inside a function
+ // is the entire function (ECMA-262, 3rd, 10.1.3, and 12.2). .
+ int nvars = 0; // the number of variables declared
+ do {
+ // Parse variable name.
+ if (nvars > 0) Consume(i::Token::COMMA);
+ ParseIdentifier(CHECK_OK);
+ nvars++;
+ if (peek() == i::Token::ASSIGN) {
+ Expect(i::Token::ASSIGN, CHECK_OK);
+ ParseAssignmentExpression(accept_IN, CHECK_OK);
+ }
+ } while (peek() == i::Token::COMMA);
+
+ if (num_decl != NULL) *num_decl = nvars;
+ return kUnknownStatement;
+}
+
+
+template <typename Scanner, typename Log>
+Statement PreParser<Scanner, Log>::ParseExpressionOrLabelledStatement(
+ bool* ok) {
+ // ExpressionStatement | LabelledStatement ::
+ // Expression ';'
+ // Identifier ':' Statement
+
+ Expression expr = ParseExpression(true, CHECK_OK);
+ if (peek() == i::Token::COLON && expr == kIdentifierExpression) {
+ Consume(i::Token::COLON);
+ return ParseStatement(ok);
+ }
+ // Parsed expression statement.
+ ExpectSemicolon(CHECK_OK);
+ return kUnknownStatement;
+}
+
+
+template <typename Scanner, typename Log>
+Statement PreParser<Scanner, Log>::ParseIfStatement(bool* ok) {
+ // IfStatement ::
+ // 'if' '(' Expression ')' Statement ('else' Statement)?
+
+ Expect(i::Token::IF, CHECK_OK);
+ Expect(i::Token::LPAREN, CHECK_OK);
+ ParseExpression(true, CHECK_OK);
+ Expect(i::Token::RPAREN, CHECK_OK);
+ ParseStatement(CHECK_OK);
+ if (peek() == i::Token::ELSE) {
+ Next();
+ ParseStatement(CHECK_OK);
+ }
+ return kUnknownStatement;
+}
+
+
+template <typename Scanner, typename Log>
+Statement PreParser<Scanner, Log>::ParseContinueStatement(bool* ok) {
+ // ContinueStatement ::
+ // 'continue' [no line terminator] Identifier? ';'
+
+ Expect(i::Token::CONTINUE, CHECK_OK);
+ i::Token::Value tok = peek();
+ if (!scanner_->has_line_terminator_before_next() &&
+ tok != i::Token::SEMICOLON &&
+ tok != i::Token::RBRACE &&
+ tok != i::Token::EOS) {
+ ParseIdentifier(CHECK_OK);
+ }
+ ExpectSemicolon(CHECK_OK);
+ return kUnknownStatement;
+}
+
+
+template <typename Scanner, typename Log>
+Statement PreParser<Scanner, Log>::ParseBreakStatement(bool* ok) {
+ // BreakStatement ::
+ // 'break' [no line terminator] Identifier? ';'
+
+ Expect(i::Token::BREAK, CHECK_OK);
+ i::Token::Value tok = peek();
+ if (!scanner_->has_line_terminator_before_next() &&
+ tok != i::Token::SEMICOLON &&
+ tok != i::Token::RBRACE &&
+ tok != i::Token::EOS) {
+ ParseIdentifier(CHECK_OK);
+ }
+ ExpectSemicolon(CHECK_OK);
+ return kUnknownStatement;
+}
+
+
+template <typename Scanner, typename Log>
+Statement PreParser<Scanner, Log>::ParseReturnStatement(bool* ok) {
+ // ReturnStatement ::
+ // 'return' [no line terminator] Expression? ';'
+
+ // Consume the return token. It is necessary to do the before
+ // reporting any errors on it, because of the way errors are
+ // reported (underlining).
+ Expect(i::Token::RETURN, CHECK_OK);
+
+ // An ECMAScript program is considered syntactically incorrect if it
+ // contains a return statement that is not within the body of a
+ // function. See ECMA-262, section 12.9, page 67.
+ // This is not handled during preparsing.
+
+ i::Token::Value tok = peek();
+ if (!scanner_->has_line_terminator_before_next() &&
+ tok != i::Token::SEMICOLON &&
+ tok != i::Token::RBRACE &&
+ tok != i::Token::EOS) {
+ ParseExpression(true, CHECK_OK);
+ }
+ ExpectSemicolon(CHECK_OK);
+ return kUnknownStatement;
+}
+
+
+template <typename Scanner, typename Log>
+Statement PreParser<Scanner, Log>::ParseWithStatement(bool* ok) {
+ // WithStatement ::
+ // 'with' '(' Expression ')' Statement
+ Expect(i::Token::WITH, CHECK_OK);
+ Expect(i::Token::LPAREN, CHECK_OK);
+ ParseExpression(true, CHECK_OK);
+ Expect(i::Token::RPAREN, CHECK_OK);
+
+ scope_->EnterWith();
+ ParseStatement(CHECK_OK);
+ scope_->LeaveWith();
+ return kUnknownStatement;
+}
+
+
+template <typename Scanner, typename Log>
+Statement PreParser<Scanner, Log>::ParseSwitchStatement(bool* ok) {
+ // SwitchStatement ::
+ // 'switch' '(' Expression ')' '{' CaseClause* '}'
+
+ Expect(i::Token::SWITCH, CHECK_OK);
+ Expect(i::Token::LPAREN, CHECK_OK);
+ ParseExpression(true, CHECK_OK);
+ Expect(i::Token::RPAREN, CHECK_OK);
+
+ Expect(i::Token::LBRACE, CHECK_OK);
+ i::Token::Value token = peek();
+ while (token != i::Token::RBRACE) {
+ if (token == i::Token::CASE) {
+ Expect(i::Token::CASE, CHECK_OK);
+ ParseExpression(true, CHECK_OK);
+ Expect(i::Token::COLON, CHECK_OK);
+ } else if (token == i::Token::DEFAULT) {
+ Expect(i::Token::DEFAULT, CHECK_OK);
+ Expect(i::Token::COLON, CHECK_OK);
+ } else {
+ ParseStatement(CHECK_OK);
+ }
+ token = peek();
+ }
+ Expect(i::Token::RBRACE, CHECK_OK);
+
+ return kUnknownStatement;
+}
+
+
+template <typename Scanner, typename Log>
+Statement PreParser<Scanner, Log>::ParseDoWhileStatement(bool* ok) {
+ // DoStatement ::
+ // 'do' Statement 'while' '(' Expression ')' ';'
+
+ Expect(i::Token::DO, CHECK_OK);
+ ParseStatement(CHECK_OK);
+ Expect(i::Token::WHILE, CHECK_OK);
+ Expect(i::Token::LPAREN, CHECK_OK);
+ ParseExpression(true, CHECK_OK);
+ Expect(i::Token::RPAREN, CHECK_OK);
+ return kUnknownStatement;
+}
+
+
+template <typename Scanner, typename Log>
+Statement PreParser<Scanner, Log>::ParseWhileStatement(bool* ok) {
+ // WhileStatement ::
+ // 'while' '(' Expression ')' Statement
+
+ Expect(i::Token::WHILE, CHECK_OK);
+ Expect(i::Token::LPAREN, CHECK_OK);
+ ParseExpression(true, CHECK_OK);
+ Expect(i::Token::RPAREN, CHECK_OK);
+ ParseStatement(CHECK_OK);
+ return kUnknownStatement;
+}
+
+
+template <typename Scanner, typename Log>
+Statement PreParser<Scanner, Log>::ParseForStatement(bool* ok) {
+ // ForStatement ::
+ // 'for' '(' Expression? ';' Expression? ';' Expression? ')' Statement
+
+ Expect(i::Token::FOR, CHECK_OK);
+ Expect(i::Token::LPAREN, CHECK_OK);
+ if (peek() != i::Token::SEMICOLON) {
+ if (peek() == i::Token::VAR || peek() == i::Token::CONST) {
+ int decl_count;
+ ParseVariableDeclarations(false, &decl_count, CHECK_OK);
+ if (peek() == i::Token::IN && decl_count == 1) {
+ Expect(i::Token::IN, CHECK_OK);
+ ParseExpression(true, CHECK_OK);
+ Expect(i::Token::RPAREN, CHECK_OK);
+
+ ParseStatement(CHECK_OK);
+ return kUnknownStatement;
+ }
+ } else {
+ ParseExpression(false, CHECK_OK);
+ if (peek() == i::Token::IN) {
+ Expect(i::Token::IN, CHECK_OK);
+ ParseExpression(true, CHECK_OK);
+ Expect(i::Token::RPAREN, CHECK_OK);
+
+ ParseStatement(CHECK_OK);
+ return kUnknownStatement;
+ }
+ }
+ }
+
+ // Parsed initializer at this point.
+ Expect(i::Token::SEMICOLON, CHECK_OK);
+
+ if (peek() != i::Token::SEMICOLON) {
+ ParseExpression(true, CHECK_OK);
+ }
+ Expect(i::Token::SEMICOLON, CHECK_OK);
+
+ if (peek() != i::Token::RPAREN) {
+ ParseExpression(true, CHECK_OK);
+ }
+ Expect(i::Token::RPAREN, CHECK_OK);
+
+ ParseStatement(CHECK_OK);
+ return kUnknownStatement;
+}
+
+
+template <typename Scanner, typename Log>
+Statement PreParser<Scanner, Log>::ParseThrowStatement(bool* ok) {
+ // ThrowStatement ::
+ // 'throw' [no line terminator] Expression ';'
+
+ Expect(i::Token::THROW, CHECK_OK);
+ if (scanner_->has_line_terminator_before_next()) {
+ typename Scanner::Location pos = scanner_->location();
+ ReportMessageAt(pos.beg_pos, pos.end_pos,
+ "newline_after_throw", NULL);
+ *ok = false;
+ return NULL;
+ }
+ ParseExpression(true, CHECK_OK);
+ ExpectSemicolon(CHECK_OK);
+
+ return kUnknownStatement;
+}
+
+
+template <typename Scanner, typename Log>
+Statement PreParser<Scanner, Log>::ParseTryStatement(bool* ok) {
+ // TryStatement ::
+ // 'try' Block Catch
+ // 'try' Block Finally
+ // 'try' Block Catch Finally
+ //
+ // Catch ::
+ // 'catch' '(' Identifier ')' Block
+ //
+ // Finally ::
+ // 'finally' Block
+
+ // In preparsing, allow any number of catch/finally blocks, including zero
+ // of both.
+
+ Expect(i::Token::TRY, CHECK_OK);
+
+ ParseBlock(CHECK_OK);
+
+ bool catch_or_finally_seen = false;
+ if (peek() == i::Token::CATCH) {
+ Expect(i::Token::CATCH, CHECK_OK);
+ Expect(i::Token::LPAREN, CHECK_OK);
+ ParseIdentifier(CHECK_OK);
+ Expect(i::Token::RPAREN, CHECK_OK);
+ ParseBlock(CHECK_OK);
+ catch_or_finally_seen = true;
+ }
+ if (peek() == i::Token::FINALLY) {
+ Expect(i::Token::FINALLY, CHECK_OK);
+ ParseBlock(CHECK_OK);
+ catch_or_finally_seen = true;
+ }
+ if (!catch_or_finally_seen) {
+ *ok = false;
+ }
+ return kUnknownStatement;
+}
+
+
+template <typename Scanner, typename Log>
+Statement PreParser<Scanner, Log>::ParseDebuggerStatement(bool* ok) {
+ // In ECMA-262 'debugger' is defined as a reserved keyword. In some browser
+ // contexts this is used as a statement which invokes the debugger as if a
+ // break point is present.
+ // DebuggerStatement ::
+ // 'debugger' ';'
+
+ Expect(i::Token::DEBUGGER, CHECK_OK);
+ ExpectSemicolon(CHECK_OK);
+ return kUnknownStatement;
+}
+
+
+// Precedence = 1
+template <typename Scanner, typename Log>
+Expression PreParser<Scanner, Log>::ParseExpression(bool accept_IN, bool* ok) {
+ // Expression ::
+ // AssignmentExpression
+ // Expression ',' AssignmentExpression
+
+ Expression result = ParseAssignmentExpression(accept_IN, CHECK_OK);
+ while (peek() == i::Token::COMMA) {
+ Expect(i::Token::COMMA, CHECK_OK);
+ ParseAssignmentExpression(accept_IN, CHECK_OK);
+ result = kUnknownExpression;
+ }
+ return result;
+}
+
+
+// Precedence = 2
+template <typename Scanner, typename Log>
+Expression PreParser<Scanner, Log>::ParseAssignmentExpression(bool accept_IN,
+ bool* ok) {
+ // AssignmentExpression ::
+ // ConditionalExpression
+ // LeftHandSideExpression AssignmentOperator AssignmentExpression
+
+ Expression expression = ParseConditionalExpression(accept_IN, CHECK_OK);
+
+ if (!i::Token::IsAssignmentOp(peek())) {
+ // Parsed conditional expression only (no assignment).
+ return expression;
+ }
+
+ i::Token::Value op = Next(); // Get assignment operator.
+ ParseAssignmentExpression(accept_IN, CHECK_OK);
+
+ if ((op == i::Token::ASSIGN) && (expression == kThisPropertyExpression)) {
+ scope_->AddProperty();
+ }
+
+ return kUnknownExpression;
+}
+
+
+// Precedence = 3
+template <typename Scanner, typename Log>
+Expression PreParser<Scanner, Log>::ParseConditionalExpression(bool accept_IN,
+ bool* ok) {
+ // ConditionalExpression ::
+ // LogicalOrExpression
+ // LogicalOrExpression '?' AssignmentExpression ':' AssignmentExpression
+
+ // We start using the binary expression parser for prec >= 4 only!
+ Expression expression = ParseBinaryExpression(4, accept_IN, CHECK_OK);
+ if (peek() != i::Token::CONDITIONAL) return expression;
+ Consume(i::Token::CONDITIONAL);
+ // In parsing the first assignment expression in conditional
+ // expressions we always accept the 'in' keyword; see ECMA-262,
+ // section 11.12, page 58.
+ ParseAssignmentExpression(true, CHECK_OK);
+ Expect(i::Token::COLON, CHECK_OK);
+ ParseAssignmentExpression(accept_IN, CHECK_OK);
+ return kUnknownExpression;
+}
+
+
+template <typename Scanner, typename Log>
+int PreParser<Scanner, Log>::Precedence(i::Token::Value tok, bool accept_IN) {
+ if (tok == i::Token::IN && !accept_IN)
+ return 0; // 0 precedence will terminate binary expression parsing
+
+ return i::Token::Precedence(tok);
+}
+
+
+// Precedence >= 4
+template <typename Scanner, typename Log>
+Expression PreParser<Scanner, Log>::ParseBinaryExpression(int prec,
+ bool accept_IN,
+ bool* ok) {
+ Expression result = ParseUnaryExpression(CHECK_OK);
+ for (int prec1 = Precedence(peek(), accept_IN); prec1 >= prec; prec1--) {
+ // prec1 >= 4
+ while (Precedence(peek(), accept_IN) == prec1) {
+ Next();
+ ParseBinaryExpression(prec1 + 1, accept_IN, CHECK_OK);
+ result = kUnknownExpression;
+ }
+ }
+ return result;
+}
+
+
+template <typename Scanner, typename Log>
+Expression PreParser<Scanner, Log>::ParseUnaryExpression(bool* ok) {
+ // UnaryExpression ::
+ // PostfixExpression
+ // 'delete' UnaryExpression
+ // 'void' UnaryExpression
+ // 'typeof' UnaryExpression
+ // '++' UnaryExpression
+ // '--' UnaryExpression
+ // '+' UnaryExpression
+ // '-' UnaryExpression
+ // '~' UnaryExpression
+ // '!' UnaryExpression
+
+ i::Token::Value op = peek();
+ if (i::Token::IsUnaryOp(op) || i::Token::IsCountOp(op)) {
+ op = Next();
+ ParseUnaryExpression(ok);
+ return kUnknownExpression;
+ } else {
+ return ParsePostfixExpression(ok);
+ }
+}
+
+
+template <typename Scanner, typename Log>
+Expression PreParser<Scanner, Log>::ParsePostfixExpression(bool* ok) {
+ // PostfixExpression ::
+ // LeftHandSideExpression ('++' | '--')?
+
+ Expression expression = ParseLeftHandSideExpression(CHECK_OK);
+ if (!scanner_->has_line_terminator_before_next() &&
+ i::Token::IsCountOp(peek())) {
+ Next();
+ return kUnknownExpression;
+ }
+ return expression;
+}
+
+
+template <typename Scanner, typename Log>
+Expression PreParser<Scanner, Log>::ParseLeftHandSideExpression(bool* ok) {
+ // LeftHandSideExpression ::
+ // (NewExpression | MemberExpression) ...
+
+ Expression result;
+ if (peek() == i::Token::NEW) {
+ result = ParseNewExpression(CHECK_OK);
+ } else {
+ result = ParseMemberExpression(CHECK_OK);
+ }
+
+ while (true) {
+ switch (peek()) {
+ case i::Token::LBRACK: {
+ Consume(i::Token::LBRACK);
+ ParseExpression(true, CHECK_OK);
+ Expect(i::Token::RBRACK, CHECK_OK);
+ if (result == kThisExpression) {
+ result = kThisPropertyExpression;
+ } else {
+ result = kUnknownExpression;
+ }
+ break;
+ }
+
+ case i::Token::LPAREN: {
+ ParseArguments(CHECK_OK);
+ result = kUnknownExpression;
+ break;
+ }
+
+ case i::Token::PERIOD: {
+ Consume(i::Token::PERIOD);
+ ParseIdentifierName(CHECK_OK);
+ if (result == kThisExpression) {
+ result = kThisPropertyExpression;
+ } else {
+ result = kUnknownExpression;
+ }
+ break;
+ }
+
+ default:
+ return result;
+ }
+ }
+}
+
+
+template <typename Scanner, typename Log>
+Expression PreParser<Scanner, Log>::ParseNewExpression(bool* ok) {
+ // NewExpression ::
+ // ('new')+ MemberExpression
+
+ // The grammar for new expressions is pretty warped. The keyword
+ // 'new' can either be a part of the new expression (where it isn't
+ // followed by an argument list) or a part of the member expression,
+ // where it must be followed by an argument list. To accommodate
+ // this, we parse the 'new' keywords greedily and keep track of how
+ // many we have parsed. This information is then passed on to the
+ // member expression parser, which is only allowed to match argument
+ // lists as long as it has 'new' prefixes left
+ unsigned new_count = 0;
+ do {
+ Consume(i::Token::NEW);
+ new_count++;
+ } while (peek() == i::Token::NEW);
+
+ return ParseMemberWithNewPrefixesExpression(new_count, ok);
+}
+
+
+template <typename Scanner, typename Log>
+Expression PreParser<Scanner, Log>::ParseMemberExpression(bool* ok) {
+ return ParseMemberWithNewPrefixesExpression(0, ok);
+}
+
+
+template <typename Scanner, typename Log>
+Expression PreParser<Scanner, Log>::ParseMemberWithNewPrefixesExpression(
+ unsigned new_count, bool* ok) {
+ // MemberExpression ::
+ // (PrimaryExpression | FunctionLiteral)
+ // ('[' Expression ']' | '.' Identifier | Arguments)*
+
+ // Parse the initial primary or function expression.
+ Expression result = NULL;
+ if (peek() == i::Token::FUNCTION) {
+ Consume(i::Token::FUNCTION);
+ if (peek() == i::Token::IDENTIFIER) {
+ ParseIdentifier(CHECK_OK);
+ }
+ result = ParseFunctionLiteral(CHECK_OK);
+ } else {
+ result = ParsePrimaryExpression(CHECK_OK);
+ }
+
+ while (true) {
+ switch (peek()) {
+ case i::Token::LBRACK: {
+ Consume(i::Token::LBRACK);
+ ParseExpression(true, CHECK_OK);
+ Expect(i::Token::RBRACK, CHECK_OK);
+ if (result == kThisExpression) {
+ result = kThisPropertyExpression;
+ } else {
+ result = kUnknownExpression;
+ }
+ break;
+ }
+ case i::Token::PERIOD: {
+ Consume(i::Token::PERIOD);
+ ParseIdentifierName(CHECK_OK);
+ if (result == kThisExpression) {
+ result = kThisPropertyExpression;
+ } else {
+ result = kUnknownExpression;
+ }
+ break;
+ }
+ case i::Token::LPAREN: {
+ if (new_count == 0) return result;
+ // Consume one of the new prefixes (already parsed).
+ ParseArguments(CHECK_OK);
+ new_count--;
+ result = kUnknownExpression;
+ break;
+ }
+ default:
+ return result;
+ }
+ }
+}
+
+
+template <typename Scanner, typename Log>
+Expression PreParser<Scanner, Log>::ParsePrimaryExpression(bool* ok) {
+ // PrimaryExpression ::
+ // 'this'
+ // 'null'
+ // 'true'
+ // 'false'
+ // Identifier
+ // Number
+ // String
+ // ArrayLiteral
+ // ObjectLiteral
+ // RegExpLiteral
+ // '(' Expression ')'
+
+ Expression result = kUnknownExpression;
+ switch (peek()) {
+ case i::Token::THIS: {
+ Next();
+ result = kThisExpression;
+ break;
+ }
+
+ case i::Token::IDENTIFIER: {
+ ParseIdentifier(CHECK_OK);
+ result = kIdentifierExpression;
+ break;
+ }
+
+ case i::Token::NULL_LITERAL:
+ case i::Token::TRUE_LITERAL:
+ case i::Token::FALSE_LITERAL:
+ case i::Token::NUMBER: {
+ Next();
+ break;
+ }
+ case i::Token::STRING: {
+ Next();
+ result = GetStringSymbol();
+ break;
+ }
+
+ case i::Token::ASSIGN_DIV:
+ result = ParseRegExpLiteral(true, CHECK_OK);
+ break;
+
+ case i::Token::DIV:
+ result = ParseRegExpLiteral(false, CHECK_OK);
+ break;
+
+ case i::Token::LBRACK:
+ result = ParseArrayLiteral(CHECK_OK);
+ break;
+
+ case i::Token::LBRACE:
+ result = ParseObjectLiteral(CHECK_OK);
+ break;
+
+ case i::Token::LPAREN:
+ Consume(i::Token::LPAREN);
+ result = ParseExpression(true, CHECK_OK);
+ Expect(i::Token::RPAREN, CHECK_OK);
+ if (result == kIdentifierExpression) result = kUnknownExpression;
+ break;
+
+ case i::Token::MOD:
+ result = ParseV8Intrinsic(CHECK_OK);
+ break;
+
+ default: {
+ Next();
+ *ok = false;
+ return kUnknownExpression;
+ }
+ }
+
+ return result;
+}
+
+
+template <typename Scanner, typename Log>
+Expression PreParser<Scanner, Log>::ParseArrayLiteral(bool* ok) {
+ // ArrayLiteral ::
+ // '[' Expression? (',' Expression?)* ']'
+ Expect(i::Token::LBRACK, CHECK_OK);
+ while (peek() != i::Token::RBRACK) {
+ if (peek() != i::Token::COMMA) {
+ ParseAssignmentExpression(true, CHECK_OK);
+ }
+ if (peek() != i::Token::RBRACK) {
+ Expect(i::Token::COMMA, CHECK_OK);
+ }
+ }
+ Expect(i::Token::RBRACK, CHECK_OK);
+
+ scope_->NextMaterializedLiteralIndex();
+ return kUnknownExpression;
+}
+
+
+template <typename Scanner, typename Log>
+Expression PreParser<Scanner, Log>::ParseObjectLiteral(bool* ok) {
+ // ObjectLiteral ::
+ // '{' (
+ // ((IdentifierName | String | Number) ':' AssignmentExpression)
+ // | (('get' | 'set') (IdentifierName | String | Number) FunctionLiteral)
+ // )*[','] '}'
+
+ Expect(i::Token::LBRACE, CHECK_OK);
+ while (peek() != i::Token::RBRACE) {
+ i::Token::Value next = peek();
+ switch (next) {
+ case i::Token::IDENTIFIER: {
+ bool is_getter = false;
+ bool is_setter = false;
+ ParseIdentifierOrGetOrSet(&is_getter, &is_setter, CHECK_OK);
+ if ((is_getter || is_setter) && peek() != i::Token::COLON) {
+ i::Token::Value name = Next();
+ if (name != i::Token::IDENTIFIER &&
+ name != i::Token::NUMBER &&
+ name != i::Token::STRING &&
+ !i::Token::IsKeyword(name)) {
+ *ok = false;
+ return kUnknownExpression;
+ }
+ ParseFunctionLiteral(CHECK_OK);
+ if (peek() != i::Token::RBRACE) {
+ Expect(i::Token::COMMA, CHECK_OK);
+ }
+ continue; // restart the while
+ }
+ break;
+ }
+ case i::Token::STRING:
+ Consume(next);
+ GetStringSymbol();
+ break;
+ case i::Token::NUMBER:
+ Consume(next);
+ break;
+ default:
+ if (i::Token::IsKeyword(next)) {
+ Consume(next);
+ } else {
+ // Unexpected token.
+ *ok = false;
+ return kUnknownExpression;
+ }
+ }
+
+ Expect(i::Token::COLON, CHECK_OK);
+ ParseAssignmentExpression(true, CHECK_OK);
+
+ // TODO(1240767): Consider allowing trailing comma.
+ if (peek() != i::Token::RBRACE) Expect(i::Token::COMMA, CHECK_OK);
+ }
+ Expect(i::Token::RBRACE, CHECK_OK);
+
+ scope_->NextMaterializedLiteralIndex();
+ return kUnknownExpression;
+}
+
+
+template <typename Scanner, typename Log>
+Expression PreParser<Scanner, Log>::ParseRegExpLiteral(bool seen_equal,
+ bool* ok) {
+ if (!scanner_->ScanRegExpPattern(seen_equal)) {
+ Next();
+ typename Scanner::Location location = scanner_->location();
+ ReportMessageAt(location.beg_pos, location.end_pos,
+ "unterminated_regexp", NULL);
+ *ok = false;
+ return kUnknownExpression;
+ }
+
+ scope_->NextMaterializedLiteralIndex();
+
+ if (!scanner_->ScanRegExpFlags()) {
+ Next();
+ typename Scanner::Location location = scanner_->location();
+ ReportMessageAt(location.beg_pos, location.end_pos,
+ "invalid_regexp_flags", NULL);
+ *ok = false;
+ return kUnknownExpression;
+ }
+ Next();
+ return kUnknownExpression;
+}
+
+
+template <typename Scanner, typename Log>
+Arguments PreParser<Scanner, Log>::ParseArguments(bool* ok) {
+ // Arguments ::
+ // '(' (AssignmentExpression)*[','] ')'
+
+ Expect(i::Token::LPAREN, CHECK_OK);
+ bool done = (peek() == i::Token::RPAREN);
+ int argc = 0;
+ while (!done) {
+ ParseAssignmentExpression(true, CHECK_OK);
+ argc++;
+ done = (peek() == i::Token::RPAREN);
+ if (!done) Expect(i::Token::COMMA, CHECK_OK);
+ }
+ Expect(i::Token::RPAREN, CHECK_OK);
+ return argc;
+}
+
+
+template <typename Scanner, typename Log>
+Expression PreParser<Scanner, Log>::ParseFunctionLiteral(bool* ok) {
+ // Function ::
+ // '(' FormalParameterList? ')' '{' FunctionBody '}'
+
+ // Parse function body.
+ ScopeType outer_scope_type = scope_->type();
+ bool inside_with = scope_->IsInsideWith();
+ Scope function_scope(&scope_, kFunctionScope);
+
+ // FormalParameterList ::
+ // '(' (Identifier)*[','] ')'
+ Expect(i::Token::LPAREN, CHECK_OK);
+ bool done = (peek() == i::Token::RPAREN);
+ while (!done) {
+ ParseIdentifier(CHECK_OK);
+ done = (peek() == i::Token::RPAREN);
+ if (!done) {
+ Expect(i::Token::COMMA, CHECK_OK);
+ }
+ }
+ Expect(i::Token::RPAREN, CHECK_OK);
+
+ Expect(i::Token::LBRACE, CHECK_OK);
+ int function_block_pos = scanner_->location().beg_pos;
+
+ // Determine if the function will be lazily compiled.
+ // Currently only happens to top-level functions.
+ // Optimistically assume that all top-level functions are lazily compiled.
+ bool is_lazily_compiled =
+ (outer_scope_type == kTopLevelScope && !inside_with && allow_lazy_);
+
+ if (is_lazily_compiled) {
+ log_->PauseRecording();
+ ParseSourceElements(i::Token::RBRACE, ok);
+ log_->ResumeRecording();
+ if (!*ok) return kUnknownExpression;
+
+ Expect(i::Token::RBRACE, CHECK_OK);
+
+ int end_pos = scanner_->location().end_pos;
+ log_->LogFunction(function_block_pos, end_pos,
+ function_scope.materialized_literal_count(),
+ function_scope.expected_properties());
+ } else {
+ ParseSourceElements(i::Token::RBRACE, CHECK_OK);
+ Expect(i::Token::RBRACE, CHECK_OK);
+ }
+ return kUnknownExpression;
+}
+
+
+template <typename Scanner, typename Log>
+Expression PreParser<Scanner, Log>::ParseV8Intrinsic(bool* ok) {
+ // CallRuntime ::
+ // '%' Identifier Arguments
+
+ Expect(i::Token::MOD, CHECK_OK);
+ ParseIdentifier(CHECK_OK);
+ ParseArguments(CHECK_OK);
+
+ return kUnknownExpression;
+}
+
+
+template <typename Scanner, typename Log>
+void PreParser<Scanner, Log>::ExpectSemicolon(bool* ok) {
+ // Check for automatic semicolon insertion according to
+ // the rules given in ECMA-262, section 7.9, page 21.
+ i::Token::Value tok = peek();
+ if (tok == i::Token::SEMICOLON) {
+ Next();
+ return;
+ }
+ if (scanner_->has_line_terminator_before_next() ||
+ tok == i::Token::RBRACE ||
+ tok == i::Token::EOS) {
+ return;
+ }
+ Expect(i::Token::SEMICOLON, ok);
+}
+
+
+template <typename Scanner, typename Log>
+Identifier PreParser<Scanner, Log>::GetIdentifierSymbol() {
+ const char* literal_chars = scanner_->literal_string();
+ int literal_length = scanner_->literal_length();
+ int identifier_pos = scanner_->location().beg_pos;
+
+ log_->LogSymbol(identifier_pos, literal_chars, literal_length);
+
+ return kUnknownExpression;
+}
+
+
+template <typename Scanner, typename Log>
+Expression PreParser<Scanner, Log>::GetStringSymbol() {
+ const char* literal_chars = scanner_->literal_string();
+ int literal_length = scanner_->literal_length();
+
+ int literal_position = scanner_->location().beg_pos;
+ log_->LogSymbol(literal_position, literal_chars, literal_length);
+
+ return kUnknownExpression;
+}
+
+
+template <typename Scanner, typename Log>
+Identifier PreParser<Scanner, Log>::ParseIdentifier(bool* ok) {
+ Expect(i::Token::IDENTIFIER, ok);
+ return GetIdentifierSymbol();
+}
+
+
+template <typename Scanner, typename Log>
+Identifier PreParser<Scanner, Log>::ParseIdentifierName(bool* ok) {
+ i::Token::Value next = Next();
+ if (i::Token::IsKeyword(next)) {
+ int pos = scanner_->location().beg_pos;
+ const char* keyword = i::Token::String(next);
+ log_->LogSymbol(pos, keyword, strlen(keyword));
+ return kUnknownExpression;
+ }
+ if (next == i::Token::IDENTIFIER) {
+ return GetIdentifierSymbol();
+ }
+ *ok = false;
+ return kUnknownIdentifier;
+}
+
+
+// This function reads an identifier and determines whether or not it
+// is 'get' or 'set'. The reason for not using ParseIdentifier and
+// checking on the output is that this involves heap allocation which
+// we can't do during preparsing.
+template <typename Scanner, typename Log>
+Identifier PreParser<Scanner, Log>::ParseIdentifierOrGetOrSet(bool* is_get,
+ bool* is_set,
+ bool* ok) {
+ Expect(i::Token::IDENTIFIER, CHECK_OK);
+ if (scanner_->literal_length() == 3) {
+ const char* token = scanner_->literal_string();
+ *is_get = strncmp(token, "get", 3) == 0;
+ *is_set = !*is_get && strncmp(token, "set", 3) == 0;
+ }
+ return GetIdentifierSymbol();
+}
+
+#undef CHECK_OK
+} } // v8::preparser
+
+#endif // V8_PREPARSER_H
diff --git a/src/regexp.js b/src/regexp.js
index 51f4b094..9e708fd0 100644
--- a/src/regexp.js
+++ b/src/regexp.js
@@ -71,9 +71,6 @@ function DoConstructRegExp(object, pattern, flags, isConstructorCall) {
}
}
- if (!isConstructorCall) {
- regExpCache.type = 'none';
- }
%RegExpInitializeObject(object, pattern, global, ignoreCase, multiline);
// Call internal function to compile the pattern.
@@ -121,22 +118,6 @@ function DoRegExpExec(regexp, string, index) {
}
-function RegExpCache() {
- this.type = 'none';
- this.regExp = 0;
- this.subject = 0;
- this.replaceString = 0;
- this.answer = 0;
- // answerSaved marks whether the contents of answer is valid for a cache
- // hit in RegExpExec, StringMatch and StringSplit.
- this.answerSaved = false;
- this.splitLimit = 0; // Used only when type is "split".
-}
-
-
-var regExpCache = new RegExpCache();
-
-
function BuildResultFromMatchInfo(lastMatchInfo, s) {
var numResults = NUMBER_OF_CAPTURES(lastMatchInfo) >> 1;
var result = %_RegExpConstructResult(numResults, lastMatchInfo[CAPTURE0], s);
@@ -178,32 +159,6 @@ function RegExpExec(string) {
['RegExp.prototype.exec', this]);
}
- var cache = regExpCache;
- var saveAnswer = false;
-
- var lastIndex = this.lastIndex;
-
- // Since cache.subject is always a string, a matching input can not
- // cause visible side-effects when converted to a string, so we can omit
- // the conversion required by the specification.
- // Likewise, the regexp.lastIndex and regexp.global properties are value
- // properties that are not configurable, so reading them can also not cause
- // any side effects (converting lastIndex to a number can, though).
- if (%_ObjectEquals(cache.type, 'exec') &&
- %_ObjectEquals(0, lastIndex) &&
- %_IsRegExpEquivalent(cache.regExp, this) &&
- %_ObjectEquals(cache.subject, string)) {
- if (cache.answerSaved) {
- // The regexp.lastIndex value must be 0 for non-global RegExps, and for
- // global RegExps we only cache negative results, which gives a lastIndex
- // of zero as well.
- this.lastIndex = 0;
- return %_RegExpCloneResult(cache.answer);
- } else {
- saveAnswer = true;
- }
- }
-
if (%_ArgumentsLength() === 0) {
var regExpInput = LAST_INPUT(lastMatchInfo);
if (IS_UNDEFINED(regExpInput)) {
@@ -217,11 +172,13 @@ function RegExpExec(string) {
} else {
s = ToString(string);
}
- var global = this.global;
+ var lastIndex = this.lastIndex;
// Conversion is required by the ES5 specification (RegExp.prototype.exec
// algorithm, step 5) even if the value is discarded for non-global RegExps.
var i = TO_INTEGER(lastIndex);
+
+ var global = this.global;
if (global) {
if (i < 0 || i > s.length) {
this.lastIndex = 0;
@@ -237,16 +194,9 @@ function RegExpExec(string) {
if (matchIndices === null) {
if (global) {
- // Cache negative result only if initial lastIndex was zero.
this.lastIndex = 0;
- if (lastIndex !== 0) return matchIndices;
}
- cache.regExp = this;
- cache.subject = s; // Always a string.
- cache.answer = null;
- cache.answerSaved = true; // Safe since no cloning is needed.
- cache.type = 'exec';
- return matchIndices; // No match.
+ return null;
}
// Successful match.
@@ -254,17 +204,9 @@ function RegExpExec(string) {
var result = BuildResultFromMatchInfo(matchIndices, s);
if (global) {
- // Don't cache positive results for global regexps.
this.lastIndex = lastMatchInfo[CAPTURE1];
- } else {
- cache.regExp = this;
- cache.subject = s;
- if (saveAnswer) cache.answer = %_RegExpCloneResult(result);
- cache.answerSaved = saveAnswer;
- cache.type = 'exec';
}
return result;
-
}
@@ -289,33 +231,22 @@ function RegExpTest(string) {
string = regExpInput;
}
- var lastIndex = this.lastIndex;
-
- var cache = regExpCache;
- if (%_ObjectEquals(cache.type, 'test') &&
- %_IsRegExpEquivalent(cache.regExp, this) &&
- %_ObjectEquals(cache.subject, string) &&
- %_ObjectEquals(0, lastIndex)) {
- // The regexp.lastIndex value must be 0 for non-global RegExps, and for
- // global RegExps we only cache negative results, which gives a resulting
- // lastIndex of zero as well.
- if (global) this.lastIndex = 0;
- return cache.answer;
- }
-
var s;
if (IS_STRING(string)) {
s = string;
} else {
s = ToString(string);
}
- var length = s.length;
+
+ var lastIndex = this.lastIndex;
// Conversion is required by the ES5 specification (RegExp.prototype.exec
// algorithm, step 5) even if the value is discarded for non-global RegExps.
var i = TO_INTEGER(lastIndex);
+
+ var global = this.global;
if (global) {
- if (i < 0 || i > length) {
+ if (i < 0 || i > s.length) {
this.lastIndex = 0;
return false;
}
@@ -323,8 +254,6 @@ function RegExpTest(string) {
i = 0;
}
- var global = this.global;
-
// Remove irrelevant preceeding '.*' in a test regexp. The expression
// checks whether this.source starts with '.*' and that the third
// char is not a '?'
@@ -334,7 +263,7 @@ function RegExpTest(string) {
if (!%_ObjectEquals(regexp_key, this)) {
regexp_key = this;
regexp_val = new $RegExp(this.source.substring(2, this.source.length),
- (this.global ? 'g' : '')
+ (global ? 'g' : '')
+ (this.ignoreCase ? 'i' : '')
+ (this.multiline ? 'm' : ''));
}
@@ -345,24 +274,13 @@ function RegExpTest(string) {
// matchIndices is either null or the lastMatchInfo array.
var matchIndices = %_RegExpExec(this, s, i, lastMatchInfo);
- var result = (matchIndices !== null);
- if (result) {
- lastMatchInfoOverride = null;
- }
- if (global) {
- if (result) {
- this.lastIndex = lastMatchInfo[CAPTURE1];
- return true;
- } else {
- this.lastIndex = 0;
- if (lastIndex !== 0) return false;
- }
+ if (matchIndices === null) {
+ if (global) this.lastIndex = 0;
+ return false;
}
- cache.type = 'test';
- cache.regExp = this;
- cache.subject = s;
- cache.answer = result;
- return result;
+ lastMatchInfoOverride = null;
+ if (global) this.lastIndex = lastMatchInfo[CAPTURE1];
+ return true;
}
@@ -510,7 +428,6 @@ function SetupRegExp() {
return IS_UNDEFINED(regExpInput) ? "" : regExpInput;
}
function RegExpSetInput(string) {
- regExpCache.type = 'none';
LAST_INPUT(lastMatchInfo) = ToString(string);
};
diff --git a/src/runtime.cc b/src/runtime.cc
index f701c031..5534db55 100644
--- a/src/runtime.cc
+++ b/src/runtime.cc
@@ -1424,66 +1424,6 @@ static MaybeObject* Runtime_RegExpConstructResult(Arguments args) {
}
-static MaybeObject* Runtime_RegExpCloneResult(Arguments args) {
- ASSERT(args.length() == 1);
- Map* regexp_result_map;
- {
- AssertNoAllocation no_gc;
- HandleScope handles;
- regexp_result_map = Top::global_context()->regexp_result_map();
- }
- if (!args[0]->IsJSArray()) return args[0];
-
- JSArray* result = JSArray::cast(args[0]);
- // Arguments to RegExpCloneResult should always be fresh RegExp exec call
- // results (either a fresh JSRegExpResult or null).
- // If the argument is not a JSRegExpResult, or isn't unmodified, just return
- // the argument uncloned.
- if (result->map() != regexp_result_map) return result;
-
- // Having the original JSRegExpResult map guarantees that we have
- // fast elements and no properties except the two in-object properties.
- ASSERT(result->HasFastElements());
- ASSERT(result->properties() == Heap::empty_fixed_array());
- ASSERT_EQ(2, regexp_result_map->inobject_properties());
-
- Object* new_array_alloc;
- { MaybeObject* maybe_new_array_alloc =
- Heap::AllocateRaw(JSRegExpResult::kSize, NEW_SPACE, OLD_POINTER_SPACE);
- if (!maybe_new_array_alloc->ToObject(&new_array_alloc)) {
- return maybe_new_array_alloc;
- }
- }
-
- // Set HeapObject map to JSRegExpResult map.
- reinterpret_cast<HeapObject*>(new_array_alloc)->set_map(regexp_result_map);
-
- JSArray* new_array = JSArray::cast(new_array_alloc);
-
- // Copy JSObject properties.
- new_array->set_properties(result->properties()); // Empty FixedArray.
-
- // Copy JSObject elements as copy-on-write.
- FixedArray* elements = FixedArray::cast(result->elements());
- if (elements != Heap::empty_fixed_array()) {
- elements->set_map(Heap::fixed_cow_array_map());
- }
- new_array->set_elements(elements);
-
- // Copy JSArray length.
- new_array->set_length(result->length());
-
- // Copy JSRegExpResult in-object property fields input and index.
- new_array->FastPropertyAtPut(JSRegExpResult::kIndexIndex,
- result->FastPropertyAt(
- JSRegExpResult::kIndexIndex));
- new_array->FastPropertyAtPut(JSRegExpResult::kInputIndex,
- result->FastPropertyAt(
- JSRegExpResult::kInputIndex));
- return new_array;
-}
-
-
static MaybeObject* Runtime_RegExpInitializeObject(Arguments args) {
AssertNoAllocation no_alloc;
ASSERT(args.length() == 5);
@@ -7129,20 +7069,31 @@ static MaybeObject* Runtime_GlobalReceiver(Arguments args) {
}
+static MaybeObject* Runtime_ParseJson(Arguments args) {
+ HandleScope scope;
+ ASSERT_EQ(1, args.length());
+ CONVERT_ARG_CHECKED(String, source, 0);
+
+ Handle<Object> result = JsonParser::Parse(source);
+ if (result.is_null()) {
+ // Syntax error or stack overflow in scanner.
+ ASSERT(Top::has_pending_exception());
+ return Failure::Exception();
+ }
+ return *result;
+}
+
+
static MaybeObject* Runtime_CompileString(Arguments args) {
HandleScope scope;
- ASSERT_EQ(2, args.length());
+ ASSERT_EQ(1, args.length());
CONVERT_ARG_CHECKED(String, source, 0);
- CONVERT_ARG_CHECKED(Oddball, is_json, 1)
// Compile source string in the global context.
Handle<Context> context(Top::context()->global_context());
- Compiler::ValidationState validate = (is_json->IsTrue())
- ? Compiler::VALIDATE_JSON : Compiler::DONT_VALIDATE_JSON;
Handle<SharedFunctionInfo> shared = Compiler::CompileEval(source,
context,
- true,
- validate);
+ true);
if (shared.is_null()) return Failure::Exception();
Handle<JSFunction> fun =
Factory::NewFunctionFromSharedFunctionInfo(shared, context, NOT_TENURED);
@@ -7157,8 +7108,7 @@ static ObjectPair CompileGlobalEval(Handle<String> source,
Handle<SharedFunctionInfo> shared = Compiler::CompileEval(
source,
Handle<Context>(Top::context()),
- Top::context()->IsGlobalContext(),
- Compiler::DONT_VALIDATE_JSON);
+ Top::context()->IsGlobalContext());
if (shared.is_null()) return MakePair(Failure::Exception(), NULL);
Handle<JSFunction> compiled = Factory::NewFunctionFromSharedFunctionInfo(
shared,
@@ -8881,12 +8831,6 @@ static MaybeObject* Runtime_DebugPrintScopes(Arguments args) {
}
-static MaybeObject* Runtime_GetCFrames(Arguments args) {
- // See bug 906.
- return Heap::undefined_value();
-}
-
-
static MaybeObject* Runtime_GetThreadCount(Arguments args) {
HandleScope scope;
ASSERT(args.length() == 1);
@@ -9370,8 +9314,7 @@ static MaybeObject* Runtime_DebugEvaluate(Arguments args) {
Handle<SharedFunctionInfo> shared =
Compiler::CompileEval(function_source,
context,
- context->IsGlobalContext(),
- Compiler::DONT_VALIDATE_JSON);
+ context->IsGlobalContext());
if (shared.is_null()) return Failure::Exception();
Handle<JSFunction> compiled_function =
Factory::NewFunctionFromSharedFunctionInfo(shared, context);
@@ -9442,8 +9385,7 @@ static MaybeObject* Runtime_DebugEvaluateGlobal(Arguments args) {
Handle<SharedFunctionInfo> shared =
Compiler::CompileEval(source,
context,
- true,
- Compiler::DONT_VALIDATE_JSON);
+ true);
if (shared.is_null()) return Failure::Exception();
Handle<JSFunction> compiled_function =
Handle<JSFunction>(Factory::NewFunctionFromSharedFunctionInfo(shared,
diff --git a/src/runtime.h b/src/runtime.h
index 8057d8bd..756099b4 100644
--- a/src/runtime.h
+++ b/src/runtime.h
@@ -162,7 +162,9 @@ namespace internal {
F(RegExpExecMultiple, 4, 1) \
F(RegExpInitializeObject, 5, 1) \
F(RegExpConstructResult, 3, 1) \
- F(RegExpCloneResult, 1, 1) \
+ \
+ /* JSON */ \
+ F(ParseJson, 1, 1) \
\
/* Strings */ \
F(StringCharCodeAt, 2, 1) \
@@ -222,7 +224,7 @@ namespace internal {
/* Numbers */ \
\
/* Globals */ \
- F(CompileString, 2, 1) \
+ F(CompileString, 1, 1) \
F(GlobalPrint, 1, 1) \
\
/* Eval */ \
@@ -322,7 +324,6 @@ namespace internal {
F(GetScopeCount, 2, 1) \
F(GetScopeDetails, 3, 1) \
F(DebugPrintScopes, 0, 1) \
- F(GetCFrames, 1, 1) \
F(GetThreadCount, 1, 1) \
F(GetThreadDetails, 2, 1) \
F(SetDisableBreak, 1, 1) \
@@ -423,7 +424,7 @@ namespace internal {
// ----------------------------------------------------------------------------
// INLINE_AND_RUNTIME_FUNCTION_LIST defines all inlined functions accessed
// with a native call of the form %_name from within JS code that also have
- // a corresponding runtime function, that is called for slow cases.
+// a corresponding runtime function, that is called for slow cases.
// Entries have the form F(name, number of arguments, number of return values).
#define INLINE_RUNTIME_FUNCTION_LIST(F) \
F(IsConstructCall, 0, 1) \
@@ -435,7 +436,6 @@ namespace internal {
F(StringCompare, 2, 1) \
F(RegExpExec, 4, 1) \
F(RegExpConstructResult, 3, 1) \
- F(RegExpCloneResult, 1, 1) \
F(GetFromCache, 2, 1) \
F(NumberToString, 1, 1) \
F(SwapElements, 3, 1)
diff --git a/src/scanner-base.cc b/src/scanner-base.cc
new file mode 100644
index 00000000..6e9d40e0
--- /dev/null
+++ b/src/scanner-base.cc
@@ -0,0 +1,167 @@
+// Copyright 2010 the V8 project authors. All rights reserved.
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following
+// disclaimer in the documentation and/or other materials provided
+// with the distribution.
+// * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived
+// from this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+// Features shared by parsing and pre-parsing scanners.
+
+#include "scanner-base.h"
+
+namespace v8 {
+namespace internal {
+
+// ----------------------------------------------------------------------------
+// Keyword Matcher
+
+KeywordMatcher::FirstState KeywordMatcher::first_states_[] = {
+ { "break", KEYWORD_PREFIX, Token::BREAK },
+ { NULL, C, Token::ILLEGAL },
+ { NULL, D, Token::ILLEGAL },
+ { "else", KEYWORD_PREFIX, Token::ELSE },
+ { NULL, F, Token::ILLEGAL },
+ { NULL, UNMATCHABLE, Token::ILLEGAL },
+ { NULL, UNMATCHABLE, Token::ILLEGAL },
+ { NULL, I, Token::ILLEGAL },
+ { NULL, UNMATCHABLE, Token::ILLEGAL },
+ { NULL, UNMATCHABLE, Token::ILLEGAL },
+ { NULL, UNMATCHABLE, Token::ILLEGAL },
+ { NULL, UNMATCHABLE, Token::ILLEGAL },
+ { NULL, N, Token::ILLEGAL },
+ { NULL, UNMATCHABLE, Token::ILLEGAL },
+ { NULL, UNMATCHABLE, Token::ILLEGAL },
+ { NULL, UNMATCHABLE, Token::ILLEGAL },
+ { "return", KEYWORD_PREFIX, Token::RETURN },
+ { "switch", KEYWORD_PREFIX, Token::SWITCH },
+ { NULL, T, Token::ILLEGAL },
+ { NULL, UNMATCHABLE, Token::ILLEGAL },
+ { NULL, V, Token::ILLEGAL },
+ { NULL, W, Token::ILLEGAL }
+};
+
+
+void KeywordMatcher::Step(unibrow::uchar input) {
+ switch (state_) {
+ case INITIAL: {
+ // matching the first character is the only state with significant fanout.
+ // Match only lower-case letters in range 'b'..'w'.
+ unsigned int offset = input - kFirstCharRangeMin;
+ if (offset < kFirstCharRangeLength) {
+ state_ = first_states_[offset].state;
+ if (state_ == KEYWORD_PREFIX) {
+ keyword_ = first_states_[offset].keyword;
+ counter_ = 1;
+ keyword_token_ = first_states_[offset].token;
+ }
+ return;
+ }
+ break;
+ }
+ case KEYWORD_PREFIX:
+ if (static_cast<unibrow::uchar>(keyword_[counter_]) == input) {
+ counter_++;
+ if (keyword_[counter_] == '\0') {
+ state_ = KEYWORD_MATCHED;
+ token_ = keyword_token_;
+ }
+ return;
+ }
+ break;
+ case KEYWORD_MATCHED:
+ token_ = Token::IDENTIFIER;
+ break;
+ case C:
+ if (MatchState(input, 'a', CA)) return;
+ if (MatchState(input, 'o', CO)) return;
+ break;
+ case CA:
+ if (MatchKeywordStart(input, "case", 2, Token::CASE)) return;
+ if (MatchKeywordStart(input, "catch", 2, Token::CATCH)) return;
+ break;
+ case CO:
+ if (MatchState(input, 'n', CON)) return;
+ break;
+ case CON:
+ if (MatchKeywordStart(input, "const", 3, Token::CONST)) return;
+ if (MatchKeywordStart(input, "continue", 3, Token::CONTINUE)) return;
+ break;
+ case D:
+ if (MatchState(input, 'e', DE)) return;
+ if (MatchKeyword(input, 'o', KEYWORD_MATCHED, Token::DO)) return;
+ break;
+ case DE:
+ if (MatchKeywordStart(input, "debugger", 2, Token::DEBUGGER)) return;
+ if (MatchKeywordStart(input, "default", 2, Token::DEFAULT)) return;
+ if (MatchKeywordStart(input, "delete", 2, Token::DELETE)) return;
+ break;
+ case F:
+ if (MatchKeywordStart(input, "false", 1, Token::FALSE_LITERAL)) return;
+ if (MatchKeywordStart(input, "finally", 1, Token::FINALLY)) return;
+ if (MatchKeywordStart(input, "for", 1, Token::FOR)) return;
+ if (MatchKeywordStart(input, "function", 1, Token::FUNCTION)) return;
+ break;
+ case I:
+ if (MatchKeyword(input, 'f', KEYWORD_MATCHED, Token::IF)) return;
+ if (MatchKeyword(input, 'n', IN, Token::IN)) return;
+ break;
+ case IN:
+ token_ = Token::IDENTIFIER;
+ if (MatchKeywordStart(input, "instanceof", 2, Token::INSTANCEOF)) {
+ return;
+ }
+ break;
+ case N:
+ if (MatchKeywordStart(input, "native", 1, Token::NATIVE)) return;
+ if (MatchKeywordStart(input, "new", 1, Token::NEW)) return;
+ if (MatchKeywordStart(input, "null", 1, Token::NULL_LITERAL)) return;
+ break;
+ case T:
+ if (MatchState(input, 'h', TH)) return;
+ if (MatchState(input, 'r', TR)) return;
+ if (MatchKeywordStart(input, "typeof", 1, Token::TYPEOF)) return;
+ break;
+ case TH:
+ if (MatchKeywordStart(input, "this", 2, Token::THIS)) return;
+ if (MatchKeywordStart(input, "throw", 2, Token::THROW)) return;
+ break;
+ case TR:
+ if (MatchKeywordStart(input, "true", 2, Token::TRUE_LITERAL)) return;
+ if (MatchKeyword(input, 'y', KEYWORD_MATCHED, Token::TRY)) return;
+ break;
+ case V:
+ if (MatchKeywordStart(input, "var", 1, Token::VAR)) return;
+ if (MatchKeywordStart(input, "void", 1, Token::VOID)) return;
+ break;
+ case W:
+ if (MatchKeywordStart(input, "while", 1, Token::WHILE)) return;
+ if (MatchKeywordStart(input, "with", 1, Token::WITH)) return;
+ break;
+ case UNMATCHABLE:
+ break;
+ }
+ // On fallthrough, it's a failure.
+ state_ = UNMATCHABLE;
+}
+
+} } // namespace v8::internal
diff --git a/src/scanner-base.h b/src/scanner-base.h
new file mode 100644
index 00000000..500870b5
--- /dev/null
+++ b/src/scanner-base.h
@@ -0,0 +1,165 @@
+// Copyright 2010 the V8 project authors. All rights reserved.
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following
+// disclaimer in the documentation and/or other materials provided
+// with the distribution.
+// * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived
+// from this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+// Features shared by parsing and pre-parsing scanners.
+
+#ifndef V8_SCANNER_BASE_H_
+#define V8_SCANNER_BASE_H_
+
+#include "token.h"
+#include "unicode.h"
+
+namespace v8 {
+namespace internal {
+
+class KeywordMatcher {
+// Incrementally recognize keywords.
+//
+// Recognized keywords:
+// break case catch const* continue debugger* default delete do else
+// finally false for function if in instanceof native* new null
+// return switch this throw true try typeof var void while with
+//
+// *: Actually "future reserved keywords". These are the only ones we
+// recognized, the remaining are allowed as identifiers.
+ public:
+ KeywordMatcher()
+ : state_(INITIAL),
+ token_(Token::IDENTIFIER),
+ keyword_(NULL),
+ counter_(0),
+ keyword_token_(Token::ILLEGAL) {}
+
+ Token::Value token() { return token_; }
+
+ inline void AddChar(unibrow::uchar input) {
+ if (state_ != UNMATCHABLE) {
+ Step(input);
+ }
+ }
+
+ void Fail() {
+ token_ = Token::IDENTIFIER;
+ state_ = UNMATCHABLE;
+ }
+
+ private:
+ enum State {
+ UNMATCHABLE,
+ INITIAL,
+ KEYWORD_PREFIX,
+ KEYWORD_MATCHED,
+ C,
+ CA,
+ CO,
+ CON,
+ D,
+ DE,
+ F,
+ I,
+ IN,
+ N,
+ T,
+ TH,
+ TR,
+ V,
+ W
+ };
+
+ struct FirstState {
+ const char* keyword;
+ State state;
+ Token::Value token;
+ };
+
+ // Range of possible first characters of a keyword.
+ static const unsigned int kFirstCharRangeMin = 'b';
+ static const unsigned int kFirstCharRangeMax = 'w';
+ static const unsigned int kFirstCharRangeLength =
+ kFirstCharRangeMax - kFirstCharRangeMin + 1;
+ // State map for first keyword character range.
+ static FirstState first_states_[kFirstCharRangeLength];
+
+ // If input equals keyword's character at position, continue matching keyword
+ // from that position.
+ inline bool MatchKeywordStart(unibrow::uchar input,
+ const char* keyword,
+ int position,
+ Token::Value token_if_match) {
+ if (input == static_cast<unibrow::uchar>(keyword[position])) {
+ state_ = KEYWORD_PREFIX;
+ this->keyword_ = keyword;
+ this->counter_ = position + 1;
+ this->keyword_token_ = token_if_match;
+ return true;
+ }
+ return false;
+ }
+
+ // If input equals match character, transition to new state and return true.
+ inline bool MatchState(unibrow::uchar input, char match, State new_state) {
+ if (input == static_cast<unibrow::uchar>(match)) {
+ state_ = new_state;
+ return true;
+ }
+ return false;
+ }
+
+ inline bool MatchKeyword(unibrow::uchar input,
+ char match,
+ State new_state,
+ Token::Value keyword_token) {
+ if (input != static_cast<unibrow::uchar>(match)) {
+ return false;
+ }
+ state_ = new_state;
+ token_ = keyword_token;
+ return true;
+ }
+
+ void Step(unibrow::uchar input);
+
+ // Current state.
+ State state_;
+ // Token for currently added characters.
+ Token::Value token_;
+
+ // Matching a specific keyword string (there is only one possible valid
+ // keyword with the current prefix).
+ const char* keyword_;
+ int counter_;
+ Token::Value keyword_token_;
+};
+
+
+
+
+
+
+} } // namespace v8::internal
+
+#endif // V8_SCANNER_BASE_H_
diff --git a/src/scanner.cc b/src/scanner.cc
index 79d63f17..a24952ac 100755
--- a/src/scanner.cc
+++ b/src/scanner.cc
@@ -184,142 +184,6 @@ void ExternalStringUTF16Buffer<StringType, CharType>::SeekForward(int pos) {
pos_ = pos;
}
-
-// ----------------------------------------------------------------------------
-// Keyword Matcher
-
-KeywordMatcher::FirstState KeywordMatcher::first_states_[] = {
- { "break", KEYWORD_PREFIX, Token::BREAK },
- { NULL, C, Token::ILLEGAL },
- { NULL, D, Token::ILLEGAL },
- { "else", KEYWORD_PREFIX, Token::ELSE },
- { NULL, F, Token::ILLEGAL },
- { NULL, UNMATCHABLE, Token::ILLEGAL },
- { NULL, UNMATCHABLE, Token::ILLEGAL },
- { NULL, I, Token::ILLEGAL },
- { NULL, UNMATCHABLE, Token::ILLEGAL },
- { NULL, UNMATCHABLE, Token::ILLEGAL },
- { NULL, UNMATCHABLE, Token::ILLEGAL },
- { NULL, UNMATCHABLE, Token::ILLEGAL },
- { NULL, N, Token::ILLEGAL },
- { NULL, UNMATCHABLE, Token::ILLEGAL },
- { NULL, UNMATCHABLE, Token::ILLEGAL },
- { NULL, UNMATCHABLE, Token::ILLEGAL },
- { "return", KEYWORD_PREFIX, Token::RETURN },
- { "switch", KEYWORD_PREFIX, Token::SWITCH },
- { NULL, T, Token::ILLEGAL },
- { NULL, UNMATCHABLE, Token::ILLEGAL },
- { NULL, V, Token::ILLEGAL },
- { NULL, W, Token::ILLEGAL }
-};
-
-
-void KeywordMatcher::Step(uc32 input) {
- switch (state_) {
- case INITIAL: {
- // matching the first character is the only state with significant fanout.
- // Match only lower-case letters in range 'b'..'w'.
- unsigned int offset = input - kFirstCharRangeMin;
- if (offset < kFirstCharRangeLength) {
- state_ = first_states_[offset].state;
- if (state_ == KEYWORD_PREFIX) {
- keyword_ = first_states_[offset].keyword;
- counter_ = 1;
- keyword_token_ = first_states_[offset].token;
- }
- return;
- }
- break;
- }
- case KEYWORD_PREFIX:
- if (keyword_[counter_] == input) {
- ASSERT_NE(input, '\0');
- counter_++;
- if (keyword_[counter_] == '\0') {
- state_ = KEYWORD_MATCHED;
- token_ = keyword_token_;
- }
- return;
- }
- break;
- case KEYWORD_MATCHED:
- token_ = Token::IDENTIFIER;
- break;
- case C:
- if (MatchState(input, 'a', CA)) return;
- if (MatchState(input, 'o', CO)) return;
- break;
- case CA:
- if (MatchKeywordStart(input, "case", 2, Token::CASE)) return;
- if (MatchKeywordStart(input, "catch", 2, Token::CATCH)) return;
- break;
- case CO:
- if (MatchState(input, 'n', CON)) return;
- break;
- case CON:
- if (MatchKeywordStart(input, "const", 3, Token::CONST)) return;
- if (MatchKeywordStart(input, "continue", 3, Token::CONTINUE)) return;
- break;
- case D:
- if (MatchState(input, 'e', DE)) return;
- if (MatchKeyword(input, 'o', KEYWORD_MATCHED, Token::DO)) return;
- break;
- case DE:
- if (MatchKeywordStart(input, "debugger", 2, Token::DEBUGGER)) return;
- if (MatchKeywordStart(input, "default", 2, Token::DEFAULT)) return;
- if (MatchKeywordStart(input, "delete", 2, Token::DELETE)) return;
- break;
- case F:
- if (MatchKeywordStart(input, "false", 1, Token::FALSE_LITERAL)) return;
- if (MatchKeywordStart(input, "finally", 1, Token::FINALLY)) return;
- if (MatchKeywordStart(input, "for", 1, Token::FOR)) return;
- if (MatchKeywordStart(input, "function", 1, Token::FUNCTION)) return;
- break;
- case I:
- if (MatchKeyword(input, 'f', KEYWORD_MATCHED, Token::IF)) return;
- if (MatchKeyword(input, 'n', IN, Token::IN)) return;
- break;
- case IN:
- token_ = Token::IDENTIFIER;
- if (MatchKeywordStart(input, "instanceof", 2, Token::INSTANCEOF)) {
- return;
- }
- break;
- case N:
- if (MatchKeywordStart(input, "native", 1, Token::NATIVE)) return;
- if (MatchKeywordStart(input, "new", 1, Token::NEW)) return;
- if (MatchKeywordStart(input, "null", 1, Token::NULL_LITERAL)) return;
- break;
- case T:
- if (MatchState(input, 'h', TH)) return;
- if (MatchState(input, 'r', TR)) return;
- if (MatchKeywordStart(input, "typeof", 1, Token::TYPEOF)) return;
- break;
- case TH:
- if (MatchKeywordStart(input, "this", 2, Token::THIS)) return;
- if (MatchKeywordStart(input, "throw", 2, Token::THROW)) return;
- break;
- case TR:
- if (MatchKeywordStart(input, "true", 2, Token::TRUE_LITERAL)) return;
- if (MatchKeyword(input, 'y', KEYWORD_MATCHED, Token::TRY)) return;
- break;
- case V:
- if (MatchKeywordStart(input, "var", 1, Token::VAR)) return;
- if (MatchKeywordStart(input, "void", 1, Token::VOID)) return;
- break;
- case W:
- if (MatchKeywordStart(input, "while", 1, Token::WHILE)) return;
- if (MatchKeywordStart(input, "with", 1, Token::WITH)) return;
- break;
- default:
- UNREACHABLE();
- }
- // On fallthrough, it's a failure.
- state_ = UNMATCHABLE;
-}
-
-
-
// ----------------------------------------------------------------------------
// Scanner::LiteralScope
diff --git a/src/scanner.h b/src/scanner.h
index 6e5333bc..1f49fd0e 100644
--- a/src/scanner.h
+++ b/src/scanner.h
@@ -30,6 +30,7 @@
#include "token.h"
#include "char-predicates-inl.h"
+#include "scanner-base.h"
namespace v8 {
namespace internal {
@@ -142,127 +143,6 @@ class ExternalStringUTF16Buffer: public UTF16Buffer {
};
-class KeywordMatcher {
-// Incrementally recognize keywords.
-//
-// Recognized keywords:
-// break case catch const* continue debugger* default delete do else
-// finally false for function if in instanceof native* new null
-// return switch this throw true try typeof var void while with
-//
-// *: Actually "future reserved keywords". These are the only ones we
-// recognized, the remaining are allowed as identifiers.
- public:
- KeywordMatcher()
- : state_(INITIAL),
- token_(Token::IDENTIFIER),
- keyword_(NULL),
- counter_(0),
- keyword_token_(Token::ILLEGAL) {}
-
- Token::Value token() { return token_; }
-
- inline void AddChar(uc32 input) {
- if (state_ != UNMATCHABLE) {
- Step(input);
- }
- }
-
- void Fail() {
- token_ = Token::IDENTIFIER;
- state_ = UNMATCHABLE;
- }
-
- private:
- enum State {
- UNMATCHABLE,
- INITIAL,
- KEYWORD_PREFIX,
- KEYWORD_MATCHED,
- C,
- CA,
- CO,
- CON,
- D,
- DE,
- F,
- I,
- IN,
- N,
- T,
- TH,
- TR,
- V,
- W
- };
-
- struct FirstState {
- const char* keyword;
- State state;
- Token::Value token;
- };
-
- // Range of possible first characters of a keyword.
- static const unsigned int kFirstCharRangeMin = 'b';
- static const unsigned int kFirstCharRangeMax = 'w';
- static const unsigned int kFirstCharRangeLength =
- kFirstCharRangeMax - kFirstCharRangeMin + 1;
- // State map for first keyword character range.
- static FirstState first_states_[kFirstCharRangeLength];
-
- // If input equals keyword's character at position, continue matching keyword
- // from that position.
- inline bool MatchKeywordStart(uc32 input,
- const char* keyword,
- int position,
- Token::Value token_if_match) {
- if (input == keyword[position]) {
- state_ = KEYWORD_PREFIX;
- this->keyword_ = keyword;
- this->counter_ = position + 1;
- this->keyword_token_ = token_if_match;
- return true;
- }
- return false;
- }
-
- // If input equals match character, transition to new state and return true.
- inline bool MatchState(uc32 input, char match, State new_state) {
- if (input == match) {
- state_ = new_state;
- return true;
- }
- return false;
- }
-
- inline bool MatchKeyword(uc32 input,
- char match,
- State new_state,
- Token::Value keyword_token) {
- if (input != match) {
- return false;
- }
- state_ = new_state;
- token_ = keyword_token;
- return true;
- }
-
- void Step(uc32 input);
-
- // Current state.
- State state_;
- // Token for currently added characters.
- Token::Value token_;
-
- // Matching a specific keyword string (there is only one possible valid
- // keyword with the current prefix).
- const char* keyword_;
- int counter_;
- Token::Value keyword_token_;
-};
-
-
-enum ParserMode { PARSE, PREPARSE };
enum ParserLanguage { JAVASCRIPT, JSON };
@@ -296,6 +176,9 @@ class Scanner {
// Returns the next token.
Token::Value Next();
+ // Returns the current token again.
+ Token::Value current_token() { return current_.token; }
+
// One token look-ahead (past the token returned by Next()).
Token::Value peek() const { return next_.token; }
diff --git a/src/string.js b/src/string.js
index d97f632b..d82ce052 100644
--- a/src/string.js
+++ b/src/string.js
@@ -144,16 +144,6 @@ function StringLastIndexOf(searchString /* position */) { // length == 1
}
-function CloneDenseArray(array) {
- if (array === null) return null;
- var clone = new $Array(array.length);
- for (var i = 0; i < array.length; i++) {
- clone[i] = array[i];
- }
- return clone;
-}
-
-
// ECMA-262 section 15.5.4.9
//
// This function is implementation specific. For now, we do not
@@ -172,33 +162,12 @@ function StringMatch(regexp) {
var subject = TO_STRING_INLINE(this);
if (IS_REGEXP(regexp)) {
if (!regexp.global) return regexp.exec(subject);
-
- var cache = regExpCache;
- var saveAnswer = false;
-
- if (%_ObjectEquals(cache.type, 'match') &&
- %_IsRegExpEquivalent(cache.regExp, regexp) &&
- %_ObjectEquals(cache.subject, subject)) {
- if (cache.answerSaved) {
- return CloneDenseArray(cache.answer);
- } else {
- saveAnswer = true;
- }
- }
%_Log('regexp', 'regexp-match,%0S,%1r', [subject, regexp]);
// lastMatchInfo is defined in regexp.js.
- var result = %StringMatch(subject, regexp, lastMatchInfo);
- cache.type = 'match';
- cache.regExp = regexp;
- cache.subject = subject;
- if (saveAnswer) cache.answer = CloneDenseArray(result);
- cache.answerSaved = saveAnswer;
- return result;
+ return %StringMatch(subject, regexp, lastMatchInfo);
}
// Non-regexp argument.
regexp = new $RegExp(regexp);
- // Don't check regexp exec cache, since the regexp is new.
- // TODO(lrn): Change this if we start caching regexps here.
return RegExpExecNoTests(regexp, subject, 0);
}
@@ -231,7 +200,6 @@ function StringReplace(search, replace) {
if (IS_REGEXP(search)) {
%_Log('regexp', 'regexp-replace,%0r,%1S', [search, subject]);
if (IS_FUNCTION(replace)) {
- regExpCache.type = 'none';
if (search.global) {
return StringReplaceGlobalRegExpWithFunction(subject, search, replace);
} else {
@@ -273,24 +241,10 @@ function StringReplace(search, replace) {
// Helper function for regular expressions in String.prototype.replace.
function StringReplaceRegExp(subject, regexp, replace) {
- var cache = regExpCache;
- if (%_ObjectEquals(cache.type, 'replace') &&
- %_IsRegExpEquivalent(cache.regExp, regexp) &&
- %_ObjectEquals(cache.replaceString, replace) &&
- %_ObjectEquals(cache.subject, subject)) {
- return cache.answer;
- }
- replace = TO_STRING_INLINE(replace);
- var answer = %StringReplaceRegExpWithString(subject,
- regexp,
- replace,
- lastMatchInfo);
- cache.subject = subject;
- cache.regExp = regexp;
- cache.replaceString = replace;
- cache.answer = answer;
- cache.type = 'replace';
- return answer;
+ return %StringReplaceRegExpWithString(subject,
+ regexp,
+ TO_STRING_INLINE(replace),
+ lastMatchInfo);
}
@@ -605,34 +559,12 @@ function StringSplit(separator, limit) {
return result;
}
- var cache = regExpCache;
- var saveAnswer = false;
-
- if (%_ObjectEquals(cache.type, 'split') &&
- %_IsRegExpEquivalent(cache.regExp, separator) &&
- %_ObjectEquals(cache.subject, subject) &&
- %_ObjectEquals(cache.splitLimit, limit)) {
- if (cache.answerSaved) {
- return CloneDenseArray(cache.answer);
- } else {
- saveAnswer = true;
- }
- }
-
- cache.type = 'split';
- cache.regExp = separator;
- cache.subject = subject;
- cache.splitLimit = limit;
-
%_Log('regexp', 'regexp-split,%0S,%1r', [subject, separator]);
if (length === 0) {
- cache.answerSaved = true;
- if (splitMatch(separator, subject, 0, 0) != null) {
- cache.answer = [];
+ if (DoRegExpExec(separator, subject, 0, 0) != null) {
return [];
}
- cache.answer = [subject];
return [subject];
}
@@ -680,8 +612,6 @@ function StringSplit(separator, limit) {
startIndex = currentIndex = endIndex;
}
- if (saveAnswer) cache.answer = CloneDenseArray(result);
- cache.answerSaved = saveAnswer;
return result;
}
diff --git a/src/stub-cache.h b/src/stub-cache.h
index 07d21979..9d947e40 100644
--- a/src/stub-cache.h
+++ b/src/stub-cache.h
@@ -241,13 +241,15 @@ class StubCache : public AllStatic {
static void Clear();
// Generate code for probing the stub cache table.
- // If extra != no_reg it might be used as am extra scratch register.
+ // Arguments extra and extra2 may be used to pass additional scratch
+ // registers. Set to no_reg if not needed.
static void GenerateProbe(MacroAssembler* masm,
Code::Flags flags,
Register receiver,
Register name,
Register scratch,
- Register extra);
+ Register extra,
+ Register extra2 = no_reg);
enum Table {
kPrimary,
diff --git a/src/token.h b/src/token.h
index ebc7fea1..74d9539f 100644
--- a/src/token.h
+++ b/src/token.h
@@ -28,6 +28,8 @@
#ifndef V8_TOKEN_H_
#define V8_TOKEN_H_
+#include "checks.h"
+
namespace v8 {
namespace internal {
diff --git a/src/top.h b/src/top.h
index e97289f3..bc3a85e8 100644
--- a/src/top.h
+++ b/src/top.h
@@ -105,7 +105,11 @@ class ThreadLocalTop BASE_EMBEDDED {
Address handler_; // try-blocks are chained through the stack
#ifdef USE_SIMULATOR
+#ifdef V8_TARGET_ARCH_ARM
assembler::arm::Simulator* simulator_;
+#elif V8_TARGET_ARCH_MIPS
+ assembler::mips::Simulator* simulator_;
+#endif
#endif // USE_SIMULATOR
#ifdef ENABLE_LOGGING_AND_PROFILING
diff --git a/src/utils.cc b/src/utils.cc
index 45a4cd60..7096ba35 100644
--- a/src/utils.cc
+++ b/src/utils.cc
@@ -37,34 +37,6 @@ namespace v8 {
namespace internal {
-// Implementation is from "Hacker's Delight" by Henry S. Warren, Jr.,
-// figure 3-3, page 48, where the function is called clp2.
-uint32_t RoundUpToPowerOf2(uint32_t x) {
- ASSERT(x <= 0x80000000u);
- x = x - 1;
- x = x | (x >> 1);
- x = x | (x >> 2);
- x = x | (x >> 4);
- x = x | (x >> 8);
- x = x | (x >> 16);
- return x + 1;
-}
-
-
-// Thomas Wang, Integer Hash Functions.
-// http://www.concentric.net/~Ttwang/tech/inthash.htm
-uint32_t ComputeIntegerHash(uint32_t key) {
- uint32_t hash = key;
- hash = ~hash + (hash << 15); // hash = (hash << 15) - hash - 1;
- hash = hash ^ (hash >> 12);
- hash = hash + (hash << 2);
- hash = hash ^ (hash >> 4);
- hash = hash * 2057; // hash = (hash + (hash << 3)) + (hash << 11);
- hash = hash ^ (hash >> 16);
- return hash;
-}
-
-
void PrintF(const char* format, ...) {
va_list arguments;
va_start(arguments, format);
@@ -274,12 +246,4 @@ char* StringBuilder::Finalize() {
}
-int TenToThe(int exponent) {
- ASSERT(exponent <= 9);
- ASSERT(exponent >= 1);
- int answer = 10;
- for (int i = 1; i < exponent; i++) answer *= 10;
- return answer;
-}
-
} } // namespace v8::internal
diff --git a/src/utils.h b/src/utils.h
index ffdb639e..069be4f8 100644
--- a/src/utils.h
+++ b/src/utils.h
@@ -31,6 +31,8 @@
#include <stdlib.h>
#include <string.h>
+#include "checks.h"
+
namespace v8 {
namespace internal {
@@ -142,7 +144,19 @@ static int PointerValueCompare(const T* a, const T* b) {
// Returns the smallest power of two which is >= x. If you pass in a
// number that is already a power of two, it is returned as is.
-uint32_t RoundUpToPowerOf2(uint32_t x);
+// Implementation is from "Hacker's Delight" by Henry S. Warren, Jr.,
+// figure 3-3, page 48, where the function is called clp2.
+static inline uint32_t RoundUpToPowerOf2(uint32_t x) {
+ ASSERT(x <= 0x80000000u);
+ x = x - 1;
+ x = x | (x >> 1);
+ x = x | (x >> 2);
+ x = x | (x >> 4);
+ x = x | (x >> 8);
+ x = x | (x >> 16);
+ return x + 1;
+}
+
template <typename T>
@@ -216,65 +230,18 @@ class BitField {
// ----------------------------------------------------------------------------
// Hash function.
-uint32_t ComputeIntegerHash(uint32_t key);
-
-
-// ----------------------------------------------------------------------------
-// I/O support.
-
-#if __GNUC__ >= 4
-// On gcc we can ask the compiler to check the types of %d-style format
-// specifiers and their associated arguments. TODO(erikcorry) fix this
-// so it works on MacOSX.
-#if defined(__MACH__) && defined(__APPLE__)
-#define PRINTF_CHECKING
-#else // MacOsX.
-#define PRINTF_CHECKING __attribute__ ((format (printf, 1, 2)))
-#endif
-#else
-#define PRINTF_CHECKING
-#endif
-
-// Our version of printf().
-void PRINTF_CHECKING PrintF(const char* format, ...);
-
-// Our version of fflush.
-void Flush();
-
-
-// Read a line of characters after printing the prompt to stdout. The resulting
-// char* needs to be disposed off with DeleteArray by the caller.
-char* ReadLine(const char* prompt);
-
-
-// Read and return the raw bytes in a file. the size of the buffer is returned
-// in size.
-// The returned buffer must be freed by the caller.
-byte* ReadBytes(const char* filename, int* size, bool verbose = true);
-
-
-// Write size chars from str to the file given by filename.
-// The file is overwritten. Returns the number of chars written.
-int WriteChars(const char* filename,
- const char* str,
- int size,
- bool verbose = true);
-
-
-// Write size bytes to the file given by filename.
-// The file is overwritten. Returns the number of bytes written.
-int WriteBytes(const char* filename,
- const byte* bytes,
- int size,
- bool verbose = true);
-
-
-// Write the C code
-// const char* <varname> = "<str>";
-// const int <varname>_len = <len>;
-// to the file given by filename. Only the first len chars are written.
-int WriteAsCFile(const char* filename, const char* varname,
- const char* str, int size, bool verbose = true);
+// Thomas Wang, Integer Hash Functions.
+// http://www.concentric.net/~Ttwang/tech/inthash.htm
+static inline uint32_t ComputeIntegerHash(uint32_t key) {
+ uint32_t hash = key;
+ hash = ~hash + (hash << 15); // hash = (hash << 15) - hash - 1;
+ hash = hash ^ (hash >> 12);
+ hash = hash + (hash << 2);
+ hash = hash ^ (hash >> 4);
+ hash = hash * 2057; // hash = (hash + (hash << 3)) + (hash << 11);
+ hash = hash ^ (hash >> 16);
+ return hash;
+}
// ----------------------------------------------------------------------------
@@ -416,23 +383,6 @@ class Vector {
};
-// A temporary assignment sets a (non-local) variable to a value on
-// construction and resets it the value on destruction.
-template <typename T>
-class TempAssign {
- public:
- TempAssign(T* var, T value): var_(var), old_value_(*var) {
- *var = value;
- }
-
- ~TempAssign() { *var_ = old_value_; }
-
- private:
- T* var_;
- T old_value_;
-};
-
-
template <typename T, int kSize>
class EmbeddedVector : public Vector<T> {
public:
@@ -484,13 +434,6 @@ inline Vector<char> MutableCStrVector(char* data, int max) {
return Vector<char>(data, (length < max) ? length : max);
}
-template <typename T>
-inline Vector< Handle<Object> > HandleVector(v8::internal::Handle<T>* elms,
- int length) {
- return Vector< Handle<Object> >(
- reinterpret_cast<v8::internal::Handle<Object>*>(elms), length);
-}
-
/*
* A class that collects values into a backing store.
@@ -699,156 +642,6 @@ class SequenceCollector : public Collector<T, growth_factor, max_growth> {
};
-// Simple support to read a file into a 0-terminated C-string.
-// The returned buffer must be freed by the caller.
-// On return, *exits tells whether the file existed.
-Vector<const char> ReadFile(const char* filename,
- bool* exists,
- bool verbose = true);
-
-
-// Simple wrapper that allows an ExternalString to refer to a
-// Vector<const char>. Doesn't assume ownership of the data.
-class AsciiStringAdapter: public v8::String::ExternalAsciiStringResource {
- public:
- explicit AsciiStringAdapter(Vector<const char> data) : data_(data) {}
-
- virtual const char* data() const { return data_.start(); }
-
- virtual size_t length() const { return data_.length(); }
-
- private:
- Vector<const char> data_;
-};
-
-
-// Helper class for building result strings in a character buffer. The
-// purpose of the class is to use safe operations that checks the
-// buffer bounds on all operations in debug mode.
-class StringBuilder {
- public:
- // Create a string builder with a buffer of the given size. The
- // buffer is allocated through NewArray<char> and must be
- // deallocated by the caller of Finalize().
- explicit StringBuilder(int size);
-
- StringBuilder(char* buffer, int size)
- : buffer_(buffer, size), position_(0) { }
-
- ~StringBuilder() { if (!is_finalized()) Finalize(); }
-
- int size() const { return buffer_.length(); }
-
- // Get the current position in the builder.
- int position() const {
- ASSERT(!is_finalized());
- return position_;
- }
-
- // Reset the position.
- void Reset() { position_ = 0; }
-
- // Add a single character to the builder. It is not allowed to add
- // 0-characters; use the Finalize() method to terminate the string
- // instead.
- void AddCharacter(char c) {
- ASSERT(c != '\0');
- ASSERT(!is_finalized() && position_ < buffer_.length());
- buffer_[position_++] = c;
- }
-
- // Add an entire string to the builder. Uses strlen() internally to
- // compute the length of the input string.
- void AddString(const char* s);
-
- // Add the first 'n' characters of the given string 's' to the
- // builder. The input string must have enough characters.
- void AddSubstring(const char* s, int n);
-
- // Add formatted contents to the builder just like printf().
- void AddFormatted(const char* format, ...);
-
- // Add character padding to the builder. If count is non-positive,
- // nothing is added to the builder.
- void AddPadding(char c, int count);
-
- // Finalize the string by 0-terminating it and returning the buffer.
- char* Finalize();
-
- private:
- Vector<char> buffer_;
- int position_;
-
- bool is_finalized() const { return position_ < 0; }
-
- DISALLOW_IMPLICIT_CONSTRUCTORS(StringBuilder);
-};
-
-
-// Custom memcpy implementation for platforms where the standard version
-// may not be good enough.
-// TODO(lrn): Check whether some IA32 platforms should be excluded.
-#if defined(V8_TARGET_ARCH_IA32)
-
-// TODO(lrn): Extend to other platforms as needed.
-
-typedef void (*MemCopyFunction)(void* dest, const void* src, size_t size);
-
-// Implemented in codegen-<arch>.cc.
-MemCopyFunction CreateMemCopyFunction();
-
-// Copy memory area to disjoint memory area.
-static inline void MemCopy(void* dest, const void* src, size_t size) {
- static MemCopyFunction memcopy = CreateMemCopyFunction();
- (*memcopy)(dest, src, size);
-#ifdef DEBUG
- CHECK_EQ(0, memcmp(dest, src, size));
-#endif
-}
-
-
-// Limit below which the extra overhead of the MemCopy function is likely
-// to outweigh the benefits of faster copying.
-// TODO(lrn): Try to find a more precise value.
-static const int kMinComplexMemCopy = 64;
-
-#else // V8_TARGET_ARCH_IA32
-
-static inline void MemCopy(void* dest, const void* src, size_t size) {
- memcpy(dest, src, size);
-}
-
-static const int kMinComplexMemCopy = 256;
-
-#endif // V8_TARGET_ARCH_IA32
-
-
-// Copy from ASCII/16bit chars to ASCII/16bit chars.
-template <typename sourcechar, typename sinkchar>
-static inline void CopyChars(sinkchar* dest, const sourcechar* src, int chars) {
- sinkchar* limit = dest + chars;
-#ifdef V8_HOST_CAN_READ_UNALIGNED
- if (sizeof(*dest) == sizeof(*src)) {
- if (chars >= static_cast<int>(kMinComplexMemCopy / sizeof(*dest))) {
- MemCopy(dest, src, chars * sizeof(*dest));
- return;
- }
- // Number of characters in a uintptr_t.
- static const int kStepSize = sizeof(uintptr_t) / sizeof(*dest); // NOLINT
- while (dest <= limit - kStepSize) {
- *reinterpret_cast<uintptr_t*>(dest) =
- *reinterpret_cast<const uintptr_t*>(src);
- dest += kStepSize;
- src += kStepSize;
- }
- }
-#endif
- while (dest < limit) {
- *dest++ = static_cast<sinkchar>(*src++);
- }
-}
-
-
// Compare ASCII/16bit chars to ASCII/16bit chars.
template <typename lchar, typename rchar>
static inline int CompareChars(const lchar* lhs, const rchar* rhs, int chars) {
@@ -877,54 +670,14 @@ static inline int CompareChars(const lchar* lhs, const rchar* rhs, int chars) {
}
-template <typename T>
-static inline void MemsetPointer(T** dest, T* value, int counter) {
-#if defined(V8_HOST_ARCH_IA32)
-#define STOS "stosl"
-#elif defined(V8_HOST_ARCH_X64)
-#define STOS "stosq"
-#endif
-
-#if defined(__GNUC__) && defined(STOS)
- asm volatile(
- "cld;"
- "rep ; " STOS
- : "+&c" (counter), "+&D" (dest)
- : "a" (value)
- : "memory", "cc");
-#else
- for (int i = 0; i < counter; i++) {
- dest[i] = value;
- }
-#endif
-
-#undef STOS
-}
-
-
-// Copies data from |src| to |dst|. The data spans MUST not overlap.
-inline void CopyWords(Object** dst, Object** src, int num_words) {
- ASSERT(Min(dst, src) + num_words <= Max(dst, src));
- ASSERT(num_words > 0);
-
- // Use block copying memcpy if the segment we're copying is
- // enough to justify the extra call/setup overhead.
- static const int kBlockCopyLimit = 16;
-
- if (num_words >= kBlockCopyLimit) {
- memcpy(dst, src, num_words * kPointerSize);
- } else {
- int remaining = num_words;
- do {
- remaining--;
- *dst++ = *src++;
- } while (remaining > 0);
- }
-}
-
-
// Calculate 10^exponent.
-int TenToThe(int exponent);
+static inline int TenToThe(int exponent) {
+ ASSERT(exponent <= 9);
+ ASSERT(exponent >= 1);
+ int answer = 10;
+ for (int i = 1; i < exponent; i++) answer *= 10;
+ return answer;
+}
// The type-based aliasing rule allows the compiler to assume that pointers of
diff --git a/src/v8.h b/src/v8.h
index 9dbdf4c2..1cb8d2f1 100644
--- a/src/v8.h
+++ b/src/v8.h
@@ -56,7 +56,7 @@
#include "globals.h"
#include "checks.h"
#include "allocation.h"
-#include "utils.h"
+#include "v8utils.h"
#include "flags.h"
// Objects & heap
diff --git a/src/v8natives.js b/src/v8natives.js
index 88aea9c1..50a2774d 100644
--- a/src/v8natives.js
+++ b/src/v8natives.js
@@ -140,7 +140,7 @@ function GlobalEval(x) {
'be the global object from which eval originated');
}
- var f = %CompileString(x, false);
+ var f = %CompileString(x);
if (!IS_FUNCTION(f)) return f;
return f.call(this);
@@ -151,7 +151,7 @@ function GlobalEval(x) {
function GlobalExecScript(expr, lang) {
// NOTE: We don't care about the character casing.
if (!lang || /javascript/i.test(lang)) {
- var f = %CompileString(ToString(expr), false);
+ var f = %CompileString(ToString(expr));
f.call(%GlobalReceiver(global));
}
return null;
@@ -1177,7 +1177,7 @@ function NewFunction(arg1) { // length == 1
// The call to SetNewFunctionAttributes will ensure the prototype
// property of the resulting function is enumerable (ECMA262, 15.3.5.2).
- var f = %CompileString(source, false)();
+ var f = %CompileString(source)();
%FunctionSetName(f, "anonymous");
return %SetNewFunctionAttributes(f);
}
diff --git a/src/v8utils.h b/src/v8utils.h
new file mode 100644
index 00000000..a907c9f5
--- /dev/null
+++ b/src/v8utils.h
@@ -0,0 +1,301 @@
+// Copyright 2010 the V8 project authors. All rights reserved.
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following
+// disclaimer in the documentation and/or other materials provided
+// with the distribution.
+// * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived
+// from this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+#ifndef V8_V8UTILS_H_
+#define V8_V8UTILS_H_
+
+#include "utils.h"
+
+namespace v8 {
+namespace internal {
+
+// ----------------------------------------------------------------------------
+// I/O support.
+
+#if __GNUC__ >= 4
+// On gcc we can ask the compiler to check the types of %d-style format
+// specifiers and their associated arguments. TODO(erikcorry) fix this
+// so it works on MacOSX.
+#if defined(__MACH__) && defined(__APPLE__)
+#define PRINTF_CHECKING
+#else // MacOsX.
+#define PRINTF_CHECKING __attribute__ ((format (printf, 1, 2)))
+#endif
+#else
+#define PRINTF_CHECKING
+#endif
+
+// Our version of printf().
+void PRINTF_CHECKING PrintF(const char* format, ...);
+
+// Our version of fflush.
+void Flush();
+
+
+// Read a line of characters after printing the prompt to stdout. The resulting
+// char* needs to be disposed off with DeleteArray by the caller.
+char* ReadLine(const char* prompt);
+
+
+// Read and return the raw bytes in a file. the size of the buffer is returned
+// in size.
+// The returned buffer must be freed by the caller.
+byte* ReadBytes(const char* filename, int* size, bool verbose = true);
+
+
+// Write size chars from str to the file given by filename.
+// The file is overwritten. Returns the number of chars written.
+int WriteChars(const char* filename,
+ const char* str,
+ int size,
+ bool verbose = true);
+
+
+// Write size bytes to the file given by filename.
+// The file is overwritten. Returns the number of bytes written.
+int WriteBytes(const char* filename,
+ const byte* bytes,
+ int size,
+ bool verbose = true);
+
+
+// Write the C code
+// const char* <varname> = "<str>";
+// const int <varname>_len = <len>;
+// to the file given by filename. Only the first len chars are written.
+int WriteAsCFile(const char* filename, const char* varname,
+ const char* str, int size, bool verbose = true);
+
+
+// Data structures
+
+template <typename T>
+inline Vector< Handle<Object> > HandleVector(v8::internal::Handle<T>* elms,
+ int length) {
+ return Vector< Handle<Object> >(
+ reinterpret_cast<v8::internal::Handle<Object>*>(elms), length);
+}
+
+// Memory
+
+// Copies data from |src| to |dst|. The data spans MUST not overlap.
+inline void CopyWords(Object** dst, Object** src, int num_words) {
+ ASSERT(Min(dst, src) + num_words <= Max(dst, src));
+ ASSERT(num_words > 0);
+
+ // Use block copying memcpy if the segment we're copying is
+ // enough to justify the extra call/setup overhead.
+ static const int kBlockCopyLimit = 16;
+
+ if (num_words >= kBlockCopyLimit) {
+ memcpy(dst, src, num_words * kPointerSize);
+ } else {
+ int remaining = num_words;
+ do {
+ remaining--;
+ *dst++ = *src++;
+ } while (remaining > 0);
+ }
+}
+
+
+template <typename T>
+static inline void MemsetPointer(T** dest, T* value, int counter) {
+#if defined(V8_HOST_ARCH_IA32)
+#define STOS "stosl"
+#elif defined(V8_HOST_ARCH_X64)
+#define STOS "stosq"
+#endif
+
+#if defined(__GNUC__) && defined(STOS)
+ asm volatile(
+ "cld;"
+ "rep ; " STOS
+ : "+&c" (counter), "+&D" (dest)
+ : "a" (value)
+ : "memory", "cc");
+#else
+ for (int i = 0; i < counter; i++) {
+ dest[i] = value;
+ }
+#endif
+
+#undef STOS
+}
+
+
+// Simple wrapper that allows an ExternalString to refer to a
+// Vector<const char>. Doesn't assume ownership of the data.
+class AsciiStringAdapter: public v8::String::ExternalAsciiStringResource {
+ public:
+ explicit AsciiStringAdapter(Vector<const char> data) : data_(data) {}
+
+ virtual const char* data() const { return data_.start(); }
+
+ virtual size_t length() const { return data_.length(); }
+
+ private:
+ Vector<const char> data_;
+};
+
+
+// Simple support to read a file into a 0-terminated C-string.
+// The returned buffer must be freed by the caller.
+// On return, *exits tells whether the file existed.
+Vector<const char> ReadFile(const char* filename,
+ bool* exists,
+ bool verbose = true);
+
+
+// Helper class for building result strings in a character buffer. The
+// purpose of the class is to use safe operations that checks the
+// buffer bounds on all operations in debug mode.
+class StringBuilder {
+ public:
+ // Create a string builder with a buffer of the given size. The
+ // buffer is allocated through NewArray<char> and must be
+ // deallocated by the caller of Finalize().
+ explicit StringBuilder(int size);
+
+ StringBuilder(char* buffer, int size)
+ : buffer_(buffer, size), position_(0) { }
+
+ ~StringBuilder() { if (!is_finalized()) Finalize(); }
+
+ int size() const { return buffer_.length(); }
+
+ // Get the current position in the builder.
+ int position() const {
+ ASSERT(!is_finalized());
+ return position_;
+ }
+
+ // Reset the position.
+ void Reset() { position_ = 0; }
+
+ // Add a single character to the builder. It is not allowed to add
+ // 0-characters; use the Finalize() method to terminate the string
+ // instead.
+ void AddCharacter(char c) {
+ ASSERT(c != '\0');
+ ASSERT(!is_finalized() && position_ < buffer_.length());
+ buffer_[position_++] = c;
+ }
+
+ // Add an entire string to the builder. Uses strlen() internally to
+ // compute the length of the input string.
+ void AddString(const char* s);
+
+ // Add the first 'n' characters of the given string 's' to the
+ // builder. The input string must have enough characters.
+ void AddSubstring(const char* s, int n);
+
+ // Add formatted contents to the builder just like printf().
+ void AddFormatted(const char* format, ...);
+
+ // Add character padding to the builder. If count is non-positive,
+ // nothing is added to the builder.
+ void AddPadding(char c, int count);
+
+ // Finalize the string by 0-terminating it and returning the buffer.
+ char* Finalize();
+
+ private:
+ Vector<char> buffer_;
+ int position_;
+
+ bool is_finalized() const { return position_ < 0; }
+
+ DISALLOW_IMPLICIT_CONSTRUCTORS(StringBuilder);
+};
+
+
+// Custom memcpy implementation for platforms where the standard version
+// may not be good enough.
+#if defined(V8_TARGET_ARCH_IA32)
+
+// The default memcpy on ia32 architectures is generally not as efficient
+// as possible. (If any further ia32 platforms are introduced where the
+// memcpy function is efficient, exclude them from this branch).
+
+typedef void (*MemCopyFunction)(void* dest, const void* src, size_t size);
+
+// Implemented in codegen-<arch>.cc.
+MemCopyFunction CreateMemCopyFunction();
+
+// Copy memory area to disjoint memory area.
+static inline void MemCopy(void* dest, const void* src, size_t size) {
+ static MemCopyFunction memcopy = CreateMemCopyFunction();
+ (*memcopy)(dest, src, size);
+#ifdef DEBUG
+ CHECK_EQ(0, memcmp(dest, src, size));
+#endif
+}
+
+// Limit below which the extra overhead of the MemCopy function is likely
+// to outweigh the benefits of faster copying.
+static const int kMinComplexMemCopy = 64;
+
+#else // V8_TARGET_ARCH_IA32
+
+static inline void MemCopy(void* dest, const void* src, size_t size) {
+ memcpy(dest, src, size);
+}
+
+static const int kMinComplexMemCopy = 256;
+
+#endif // V8_TARGET_ARCH_IA32
+
+
+// Copy from ASCII/16bit chars to ASCII/16bit chars.
+template <typename sourcechar, typename sinkchar>
+static inline void CopyChars(sinkchar* dest, const sourcechar* src, int chars) {
+ sinkchar* limit = dest + chars;
+#ifdef V8_HOST_CAN_READ_UNALIGNED
+ if (sizeof(*dest) == sizeof(*src)) {
+ if (chars >= static_cast<int>(kMinComplexMemCopy / sizeof(*dest))) {
+ MemCopy(dest, src, chars * sizeof(*dest));
+ return;
+ }
+ // Number of characters in a uintptr_t.
+ static const int kStepSize = sizeof(uintptr_t) / sizeof(*dest); // NOLINT
+ while (dest <= limit - kStepSize) {
+ *reinterpret_cast<uintptr_t*>(dest) =
+ *reinterpret_cast<const uintptr_t*>(src);
+ dest += kStepSize;
+ src += kStepSize;
+ }
+ }
+#endif
+ while (dest < limit) {
+ *dest++ = static_cast<sinkchar>(*src++);
+ }
+}
+
+} } // namespace v8::internal
+
+#endif // V8_V8UTILS_H_
diff --git a/src/version.cc b/src/version.cc
index 4017ae5e..5ef2a651 100644
--- a/src/version.cc
+++ b/src/version.cc
@@ -34,7 +34,7 @@
// cannot be changed without changing the SCons build script.
#define MAJOR_VERSION 2
#define MINOR_VERSION 5
-#define BUILD_NUMBER 2
+#define BUILD_NUMBER 5
#define PATCH_LEVEL 0
#define CANDIDATE_VERSION false
diff --git a/src/x64/assembler-x64.cc b/src/x64/assembler-x64.cc
index bf5ee5bb..caed7c8a 100644
--- a/src/x64/assembler-x64.cc
+++ b/src/x64/assembler-x64.cc
@@ -296,7 +296,7 @@ static void InitCoverageLog();
byte* Assembler::spare_buffer_ = NULL;
Assembler::Assembler(void* buffer, int buffer_size)
- : code_targets_(100) {
+ : code_targets_(100), positions_recorder_(this) {
if (buffer == NULL) {
// Do our own buffer management.
if (buffer_size <= kMinimalBufferSize) {
@@ -337,10 +337,7 @@ Assembler::Assembler(void* buffer, int buffer_size)
reloc_info_writer.Reposition(buffer_ + buffer_size, pc_);
last_pc_ = NULL;
- current_statement_position_ = RelocInfo::kNoPosition;
- current_position_ = RelocInfo::kNoPosition;
- written_statement_position_ = current_statement_position_;
- written_position_ = current_position_;
+
#ifdef GENERATED_CODE_COVERAGE
InitCoverageLog();
#endif
@@ -845,7 +842,7 @@ void Assembler::call(Label* L) {
void Assembler::call(Handle<Code> target, RelocInfo::Mode rmode) {
- WriteRecordedPositions();
+ positions_recorder()->WriteRecordedPositions();
EnsureSpace ensure_space(this);
last_pc_ = pc_;
// 1110 1000 #32-bit disp.
@@ -2935,14 +2932,14 @@ void Assembler::RecordRelocInfo(RelocInfo::Mode rmode, intptr_t data) {
}
void Assembler::RecordJSReturn() {
- WriteRecordedPositions();
+ positions_recorder()->WriteRecordedPositions();
EnsureSpace ensure_space(this);
RecordRelocInfo(RelocInfo::JS_RETURN);
}
void Assembler::RecordDebugBreakSlot() {
- WriteRecordedPositions();
+ positions_recorder()->WriteRecordedPositions();
EnsureSpace ensure_space(this);
RecordRelocInfo(RelocInfo::DEBUG_BREAK_SLOT);
}
@@ -2956,47 +2953,6 @@ void Assembler::RecordComment(const char* msg) {
}
-void Assembler::RecordPosition(int pos) {
- ASSERT(pos != RelocInfo::kNoPosition);
- ASSERT(pos >= 0);
- current_position_ = pos;
-}
-
-
-void Assembler::RecordStatementPosition(int pos) {
- ASSERT(pos != RelocInfo::kNoPosition);
- ASSERT(pos >= 0);
- current_statement_position_ = pos;
-}
-
-
-bool Assembler::WriteRecordedPositions() {
- bool written = false;
-
- // Write the statement position if it is different from what was written last
- // time.
- if (current_statement_position_ != written_statement_position_) {
- EnsureSpace ensure_space(this);
- RecordRelocInfo(RelocInfo::STATEMENT_POSITION, current_statement_position_);
- written_statement_position_ = current_statement_position_;
- written = true;
- }
-
- // Write the position if it is different from what was written last time and
- // also different from the written statement position.
- if (current_position_ != written_position_ &&
- current_position_ != written_statement_position_) {
- EnsureSpace ensure_space(this);
- RecordRelocInfo(RelocInfo::POSITION, current_position_);
- written_position_ = current_position_;
- written = true;
- }
-
- // Return whether something was written.
- return written;
-}
-
-
const int RelocInfo::kApplyMask = RelocInfo::kCodeTargetMask |
1 << RelocInfo::INTERNAL_REFERENCE;
diff --git a/src/x64/assembler-x64.h b/src/x64/assembler-x64.h
index bbc10106..c7f76322 100644
--- a/src/x64/assembler-x64.h
+++ b/src/x64/assembler-x64.h
@@ -1174,13 +1174,9 @@ class Assembler : public Malloced {
// Use --debug_code to enable.
void RecordComment(const char* msg);
- void RecordPosition(int pos);
- void RecordStatementPosition(int pos);
- bool WriteRecordedPositions();
-
int pc_offset() const { return static_cast<int>(pc_ - buffer_); }
- int current_statement_position() const { return current_statement_position_; }
- int current_position() const { return current_position_; }
+
+ PositionsRecorder* positions_recorder() { return &positions_recorder_; }
// Check if there is less than kGap bytes available in the buffer.
// If this is the case, we need to grow the buffer before emitting
@@ -1404,11 +1400,8 @@ class Assembler : public Malloced {
// push-pop elimination
byte* last_pc_;
- // source position information
- int current_statement_position_;
- int current_position_;
- int written_statement_position_;
- int written_position_;
+ PositionsRecorder positions_recorder_;
+ friend class PositionsRecorder;
};
diff --git a/src/x64/codegen-x64.cc b/src/x64/codegen-x64.cc
index 9e6ef3b5..e0e40950 100644
--- a/src/x64/codegen-x64.cc
+++ b/src/x64/codegen-x64.cc
@@ -2956,7 +2956,7 @@ void CodeGenerator::VisitReturnStatement(ReturnStatement* node) {
CodeForStatementPosition(node);
Load(node->expression());
Result return_value = frame_->Pop();
- masm()->WriteRecordedPositions();
+ masm()->positions_recorder()->WriteRecordedPositions();
if (function_return_is_shadowed_) {
function_return_.Jump(&return_value);
} else {
@@ -4866,6 +4866,11 @@ void CodeGenerator::VisitObjectLiteral(ObjectLiteral* node) {
}
frame_->Push(&clone);
+ // Mark all computed expressions that are bound to a key that
+ // is shadowed by a later occurrence of the same key. For the
+ // marked expressions, no store code is emitted.
+ node->CalculateEmitStore();
+
for (int i = 0; i < node->properties()->length(); i++) {
ObjectLiteral::Property* property = node->properties()->at(i);
switch (property->kind()) {
@@ -4880,13 +4885,17 @@ void CodeGenerator::VisitObjectLiteral(ObjectLiteral* node) {
// Duplicate the object as the IC receiver.
frame_->Dup();
Load(property->value());
- Result ignored =
- frame_->CallStoreIC(Handle<String>::cast(key), false);
- // A test rax instruction following the store IC call would
- // indicate the presence of an inlined version of the
- // store. Add a nop to indicate that there is no such
- // inlined version.
- __ nop();
+ if (property->emit_store()) {
+ Result ignored =
+ frame_->CallStoreIC(Handle<String>::cast(key), false);
+ // A test rax instruction following the store IC call would
+ // indicate the presence of an inlined version of the
+ // store. Add a nop to indicate that there is no such
+ // inlined version.
+ __ nop();
+ } else {
+ frame_->Drop(2);
+ }
break;
}
// Fall through
@@ -4896,8 +4905,12 @@ void CodeGenerator::VisitObjectLiteral(ObjectLiteral* node) {
frame_->Dup();
Load(property->key());
Load(property->value());
- Result ignored = frame_->CallRuntime(Runtime::kSetProperty, 3);
- // Ignore the result.
+ if (property->emit_store()) {
+ // Ignore the result.
+ Result ignored = frame_->CallRuntime(Runtime::kSetProperty, 3);
+ } else {
+ frame_->Drop(3);
+ }
break;
}
case ObjectLiteral::Property::SETTER: {
@@ -6551,86 +6564,6 @@ void CodeGenerator::GenerateRegExpConstructResult(ZoneList<Expression*>* args) {
}
-void CodeGenerator::GenerateRegExpCloneResult(ZoneList<Expression*>* args) {
- ASSERT_EQ(1, args->length());
-
- Load(args->at(0));
- Result object_result = frame_->Pop();
- object_result.ToRegister(rax);
- object_result.Unuse();
- {
- VirtualFrame::SpilledScope spilled_scope;
-
- Label done;
- __ JumpIfSmi(rax, &done);
-
- // Load JSRegExpResult map into rdx.
- // Arguments to this function should be results of calling RegExp exec,
- // which is either an unmodified JSRegExpResult or null. Anything not having
- // the unmodified JSRegExpResult map is returned unmodified.
- // This also ensures that elements are fast.
-
- __ movq(rdx, ContextOperand(rsi, Context::GLOBAL_INDEX));
- __ movq(rdx, FieldOperand(rdx, GlobalObject::kGlobalContextOffset));
- __ movq(rdx, ContextOperand(rdx, Context::REGEXP_RESULT_MAP_INDEX));
- __ cmpq(rdx, FieldOperand(rax, HeapObject::kMapOffset));
- __ j(not_equal, &done);
-
- if (FLAG_debug_code) {
- // Check that object really has empty properties array, as the map
- // should guarantee.
- __ CompareRoot(FieldOperand(rax, JSObject::kPropertiesOffset),
- Heap::kEmptyFixedArrayRootIndex);
- __ Check(equal, "JSRegExpResult: default map but non-empty properties.");
- }
-
- DeferredAllocateInNewSpace* allocate_fallback =
- new DeferredAllocateInNewSpace(JSRegExpResult::kSize,
- rbx,
- rdx.bit() | rax.bit());
-
- // All set, copy the contents to a new object.
- __ AllocateInNewSpace(JSRegExpResult::kSize,
- rbx,
- no_reg,
- no_reg,
- allocate_fallback->entry_label(),
- TAG_OBJECT);
- __ bind(allocate_fallback->exit_label());
-
- STATIC_ASSERT(JSRegExpResult::kSize % (2 * kPointerSize) == 0);
- // There is an even number of fields, so unroll the loop once
- // for efficiency.
- for (int i = 0; i < JSRegExpResult::kSize; i += 2 * kPointerSize) {
- STATIC_ASSERT(JSObject::kMapOffset % (2 * kPointerSize) == 0);
- if (i != JSObject::kMapOffset) {
- // The map was already loaded into edx.
- __ movq(rdx, FieldOperand(rax, i));
- }
- __ movq(rcx, FieldOperand(rax, i + kPointerSize));
-
- STATIC_ASSERT(JSObject::kElementsOffset % (2 * kPointerSize) == 0);
- if (i == JSObject::kElementsOffset) {
- // If the elements array isn't empty, make it copy-on-write
- // before copying it.
- Label empty;
- __ CompareRoot(rdx, Heap::kEmptyFixedArrayRootIndex);
- __ j(equal, &empty);
- __ LoadRoot(kScratchRegister, Heap::kFixedCOWArrayMapRootIndex);
- __ movq(FieldOperand(rdx, HeapObject::kMapOffset), kScratchRegister);
- __ bind(&empty);
- }
- __ movq(FieldOperand(rbx, i), rdx);
- __ movq(FieldOperand(rbx, i + kPointerSize), rcx);
- }
- __ movq(rax, rbx);
-
- __ bind(&done);
- }
- frame_->Push(rax);
-}
-
-
class DeferredSearchCache: public DeferredCode {
public:
DeferredSearchCache(Register dst,
diff --git a/src/x64/codegen-x64.h b/src/x64/codegen-x64.h
index 79573245..1853c832 100644
--- a/src/x64/codegen-x64.h
+++ b/src/x64/codegen-x64.h
@@ -656,8 +656,6 @@ class CodeGenerator: public AstVisitor {
void GenerateRegExpConstructResult(ZoneList<Expression*>* args);
- void GenerateRegExpCloneResult(ZoneList<Expression*>* args);
-
// Support for fast native caches.
void GenerateGetFromCache(ZoneList<Expression*>* args);
diff --git a/src/x64/full-codegen-x64.cc b/src/x64/full-codegen-x64.cc
index 32d62426..00ea6845 100644
--- a/src/x64/full-codegen-x64.cc
+++ b/src/x64/full-codegen-x64.cc
@@ -1158,6 +1158,11 @@ void FullCodeGenerator::VisitObjectLiteral(ObjectLiteral* expr) {
// result_saved is false the result is in rax.
bool result_saved = false;
+ // Mark all computed expressions that are bound to a key that
+ // is shadowed by a later occurrence of the same key. For the
+ // marked expressions, no store code is emitted.
+ expr->CalculateEmitStore();
+
for (int i = 0; i < expr->properties()->length(); i++) {
ObjectLiteral::Property* property = expr->properties()->at(i);
if (property->IsCompileTimeValue()) continue;
@@ -1179,8 +1184,10 @@ void FullCodeGenerator::VisitObjectLiteral(ObjectLiteral* expr) {
VisitForAccumulatorValue(value);
__ Move(rcx, key->handle());
__ movq(rdx, Operand(rsp, 0));
- Handle<Code> ic(Builtins::builtin(Builtins::StoreIC_Initialize));
- EmitCallIC(ic, RelocInfo::CODE_TARGET);
+ if (property->emit_store()) {
+ Handle<Code> ic(Builtins::builtin(Builtins::StoreIC_Initialize));
+ EmitCallIC(ic, RelocInfo::CODE_TARGET);
+ }
break;
}
// Fall through.
@@ -1188,7 +1195,11 @@ void FullCodeGenerator::VisitObjectLiteral(ObjectLiteral* expr) {
__ push(Operand(rsp, 0)); // Duplicate receiver.
VisitForStackValue(key);
VisitForStackValue(value);
- __ CallRuntime(Runtime::kSetProperty, 3);
+ if (property->emit_store()) {
+ __ CallRuntime(Runtime::kSetProperty, 3);
+ } else {
+ __ Drop(3);
+ }
break;
case ObjectLiteral::Property::SETTER:
case ObjectLiteral::Property::GETTER:
@@ -1706,12 +1717,14 @@ void FullCodeGenerator::EmitCallWithIC(Call* expr,
// Code common for calls using the IC.
ZoneList<Expression*>* args = expr->arguments();
int arg_count = args->length();
- for (int i = 0; i < arg_count; i++) {
- VisitForStackValue(args->at(i));
+ { PreserveStatementPositionScope scope(masm()->positions_recorder());
+ for (int i = 0; i < arg_count; i++) {
+ VisitForStackValue(args->at(i));
+ }
+ __ Move(rcx, name);
}
- __ Move(rcx, name);
// Record source position for debugger.
- SetSourcePosition(expr->position());
+ SetSourcePosition(expr->position(), FORCED_POSITION);
// Call the IC initialization code.
InLoopFlag in_loop = (loop_depth() > 0) ? IN_LOOP : NOT_IN_LOOP;
Handle<Code> ic = CodeGenerator::ComputeCallInitialize(arg_count,
@@ -1729,13 +1742,15 @@ void FullCodeGenerator::EmitKeyedCallWithIC(Call* expr,
// Code common for calls using the IC.
ZoneList<Expression*>* args = expr->arguments();
int arg_count = args->length();
- for (int i = 0; i < arg_count; i++) {
- VisitForStackValue(args->at(i));
+ { PreserveStatementPositionScope scope(masm()->positions_recorder());
+ for (int i = 0; i < arg_count; i++) {
+ VisitForStackValue(args->at(i));
+ }
+ VisitForAccumulatorValue(key);
+ __ movq(rcx, rax);
}
- VisitForAccumulatorValue(key);
- __ movq(rcx, rax);
// Record source position for debugger.
- SetSourcePosition(expr->position());
+ SetSourcePosition(expr->position(), FORCED_POSITION);
// Call the IC initialization code.
InLoopFlag in_loop = (loop_depth() > 0) ? IN_LOOP : NOT_IN_LOOP;
Handle<Code> ic = CodeGenerator::ComputeKeyedCallInitialize(arg_count,
@@ -1751,11 +1766,13 @@ void FullCodeGenerator::EmitCallWithStub(Call* expr) {
// Code common for calls using the call stub.
ZoneList<Expression*>* args = expr->arguments();
int arg_count = args->length();
- for (int i = 0; i < arg_count; i++) {
- VisitForStackValue(args->at(i));
+ { PreserveStatementPositionScope scope(masm()->positions_recorder());
+ for (int i = 0; i < arg_count; i++) {
+ VisitForStackValue(args->at(i));
+ }
}
// Record source position for debugger.
- SetSourcePosition(expr->position());
+ SetSourcePosition(expr->position(), FORCED_POSITION);
InLoopFlag in_loop = (loop_depth() > 0) ? IN_LOOP : NOT_IN_LOOP;
CallFunctionStub stub(arg_count, in_loop, RECEIVER_MIGHT_BE_VALUE);
__ CallStub(&stub);
@@ -1776,37 +1793,38 @@ void FullCodeGenerator::VisitCall(Call* expr) {
// resolve the function we need to call and the receiver of the
// call. The we call the resolved function using the given
// arguments.
- VisitForStackValue(fun);
- __ PushRoot(Heap::kUndefinedValueRootIndex); // Reserved receiver slot.
-
- // Push the arguments.
ZoneList<Expression*>* args = expr->arguments();
int arg_count = args->length();
- for (int i = 0; i < arg_count; i++) {
- VisitForStackValue(args->at(i));
- }
+ { PreserveStatementPositionScope pos_scope(masm()->positions_recorder());
+ VisitForStackValue(fun);
+ __ PushRoot(Heap::kUndefinedValueRootIndex); // Reserved receiver slot.
- // Push copy of the function - found below the arguments.
- __ push(Operand(rsp, (arg_count + 1) * kPointerSize));
+ // Push the arguments.
+ for (int i = 0; i < arg_count; i++) {
+ VisitForStackValue(args->at(i));
+ }
- // Push copy of the first argument or undefined if it doesn't exist.
- if (arg_count > 0) {
- __ push(Operand(rsp, arg_count * kPointerSize));
- } else {
- __ PushRoot(Heap::kUndefinedValueRootIndex);
- }
+ // Push copy of the function - found below the arguments.
+ __ push(Operand(rsp, (arg_count + 1) * kPointerSize));
- // Push the receiver of the enclosing function and do runtime call.
- __ push(Operand(rbp, (2 + scope()->num_parameters()) * kPointerSize));
- __ CallRuntime(Runtime::kResolvePossiblyDirectEval, 3);
+ // Push copy of the first argument or undefined if it doesn't exist.
+ if (arg_count > 0) {
+ __ push(Operand(rsp, arg_count * kPointerSize));
+ } else {
+ __ PushRoot(Heap::kUndefinedValueRootIndex);
+ }
- // The runtime call returns a pair of values in rax (function) and
- // rdx (receiver). Touch up the stack with the right values.
- __ movq(Operand(rsp, (arg_count + 0) * kPointerSize), rdx);
- __ movq(Operand(rsp, (arg_count + 1) * kPointerSize), rax);
+ // Push the receiver of the enclosing function and do runtime call.
+ __ push(Operand(rbp, (2 + scope()->num_parameters()) * kPointerSize));
+ __ CallRuntime(Runtime::kResolvePossiblyDirectEval, 3);
+ // The runtime call returns a pair of values in rax (function) and
+ // rdx (receiver). Touch up the stack with the right values.
+ __ movq(Operand(rsp, (arg_count + 0) * kPointerSize), rdx);
+ __ movq(Operand(rsp, (arg_count + 1) * kPointerSize), rax);
+ }
// Record source position for debugger.
- SetSourcePosition(expr->position());
+ SetSourcePosition(expr->position(), FORCED_POSITION);
InLoopFlag in_loop = (loop_depth() > 0) ? IN_LOOP : NOT_IN_LOOP;
CallFunctionStub stub(arg_count, in_loop, RECEIVER_MIGHT_BE_VALUE);
__ CallStub(&stub);
@@ -1823,35 +1841,37 @@ void FullCodeGenerator::VisitCall(Call* expr) {
// Call to a lookup slot (dynamically introduced variable).
Label slow, done;
- // Generate code for loading from variables potentially shadowed
- // by eval-introduced variables.
- EmitDynamicLoadFromSlotFastCase(var->AsSlot(),
- NOT_INSIDE_TYPEOF,
- &slow,
- &done);
-
- __ bind(&slow);
- // Call the runtime to find the function to call (returned in rax)
- // and the object holding it (returned in rdx).
- __ push(context_register());
- __ Push(var->name());
- __ CallRuntime(Runtime::kLoadContextSlot, 2);
- __ push(rax); // Function.
- __ push(rdx); // Receiver.
-
- // If fast case code has been generated, emit code to push the
- // function and receiver and have the slow path jump around this
- // code.
- if (done.is_linked()) {
- NearLabel call;
- __ jmp(&call);
- __ bind(&done);
- // Push function.
- __ push(rax);
- // Push global receiver.
- __ movq(rbx, CodeGenerator::GlobalObject());
- __ push(FieldOperand(rbx, GlobalObject::kGlobalReceiverOffset));
- __ bind(&call);
+ { PreserveStatementPositionScope scope(masm()->positions_recorder());
+ // Generate code for loading from variables potentially shadowed
+ // by eval-introduced variables.
+ EmitDynamicLoadFromSlotFastCase(var->AsSlot(),
+ NOT_INSIDE_TYPEOF,
+ &slow,
+ &done);
+
+ __ bind(&slow);
+ // Call the runtime to find the function to call (returned in rax)
+ // and the object holding it (returned in rdx).
+ __ push(context_register());
+ __ Push(var->name());
+ __ CallRuntime(Runtime::kLoadContextSlot, 2);
+ __ push(rax); // Function.
+ __ push(rdx); // Receiver.
+
+ // If fast case code has been generated, emit code to push the
+ // function and receiver and have the slow path jump around this
+ // code.
+ if (done.is_linked()) {
+ NearLabel call;
+ __ jmp(&call);
+ __ bind(&done);
+ // Push function.
+ __ push(rax);
+ // Push global receiver.
+ __ movq(rbx, CodeGenerator::GlobalObject());
+ __ push(FieldOperand(rbx, GlobalObject::kGlobalReceiverOffset));
+ __ bind(&call);
+ }
}
EmitCallWithStub(expr);
@@ -1862,18 +1882,24 @@ void FullCodeGenerator::VisitCall(Call* expr) {
Literal* key = prop->key()->AsLiteral();
if (key != NULL && key->handle()->IsSymbol()) {
// Call to a named property, use call IC.
- VisitForStackValue(prop->obj());
+ { PreserveStatementPositionScope scope(masm()->positions_recorder());
+ VisitForStackValue(prop->obj());
+ }
EmitCallWithIC(expr, key->handle(), RelocInfo::CODE_TARGET);
} else {
// Call to a keyed property.
// For a synthetic property use keyed load IC followed by function call,
// for a regular property use KeyedCallIC.
- VisitForStackValue(prop->obj());
+ { PreserveStatementPositionScope scope(masm()->positions_recorder());
+ VisitForStackValue(prop->obj());
+ }
if (prop->is_synthetic()) {
- VisitForAccumulatorValue(prop->key());
- __ movq(rdx, Operand(rsp, 0));
+ { PreserveStatementPositionScope scope(masm()->positions_recorder());
+ VisitForAccumulatorValue(prop->key());
+ __ movq(rdx, Operand(rsp, 0));
+ }
// Record source code position for IC call.
- SetSourcePosition(prop->position());
+ SetSourcePosition(prop->position(), FORCED_POSITION);
Handle<Code> ic(Builtins::builtin(Builtins::KeyedLoadIC_Initialize));
EmitCallIC(ic, RelocInfo::CODE_TARGET);
// Pop receiver.
@@ -1898,7 +1924,9 @@ void FullCodeGenerator::VisitCall(Call* expr) {
loop_depth() == 0) {
lit->set_try_full_codegen(true);
}
- VisitForStackValue(fun);
+ { PreserveStatementPositionScope scope(masm()->positions_recorder());
+ VisitForStackValue(fun);
+ }
// Load global receiver object.
__ movq(rbx, CodeGenerator::GlobalObject());
__ push(FieldOperand(rbx, GlobalObject::kGlobalReceiverOffset));
diff --git a/src/x64/ic-x64.cc b/src/x64/ic-x64.cc
index 1d95b7f6..9ec78148 100644
--- a/src/x64/ic-x64.cc
+++ b/src/x64/ic-x64.cc
@@ -33,7 +33,6 @@
#include "ic-inl.h"
#include "runtime.h"
#include "stub-cache.h"
-#include "utils.h"
namespace v8 {
namespace internal {
diff --git a/src/x64/stub-cache-x64.cc b/src/x64/stub-cache-x64.cc
index 3891e1d5..24609bf6 100644
--- a/src/x64/stub-cache-x64.cc
+++ b/src/x64/stub-cache-x64.cc
@@ -273,9 +273,11 @@ void StubCache::GenerateProbe(MacroAssembler* masm,
Register receiver,
Register name,
Register scratch,
- Register extra) {
+ Register extra,
+ Register extra2) {
Label miss;
- USE(extra); // The register extra is not used on the X64 platform.
+ USE(extra); // The register extra is not used on the X64 platform.
+ USE(extra2); // The register extra2 is not used on the X64 platform.
// Make sure that code is valid. The shifting code relies on the
// entry size being 16.
ASSERT(sizeof(Entry) == 16);
@@ -287,6 +289,10 @@ void StubCache::GenerateProbe(MacroAssembler* masm,
ASSERT(!scratch.is(receiver));
ASSERT(!scratch.is(name));
+ // Check scratch register is valid, extra and extra2 are unused.
+ ASSERT(!scratch.is(no_reg));
+ ASSERT(extra2.is(no_reg));
+
// Check that the receiver isn't a smi.
__ JumpIfSmi(receiver, &miss);
diff --git a/test/cctest/test-debug.cc b/test/cctest/test-debug.cc
index d59e2f5a..748e3e8d 100644
--- a/test/cctest/test-debug.cc
+++ b/test/cctest/test-debug.cc
@@ -902,6 +902,7 @@ static void DebugEventBreak(v8::DebugEvent event,
// Debug event handler which re-issues a debug break until a limit has been
// reached.
int max_break_point_hit_count = 0;
+bool terminate_after_max_break_point_hit = false;
static void DebugEventBreakMax(v8::DebugEvent event,
v8::Handle<v8::Object> exec_state,
v8::Handle<v8::Object> event_data,
@@ -909,12 +910,17 @@ static void DebugEventBreakMax(v8::DebugEvent event,
// When hitting a debug event listener there must be a break set.
CHECK_NE(v8::internal::Debug::break_id(), 0);
- if (event == v8::Break && break_point_hit_count < max_break_point_hit_count) {
- // Count the number of breaks.
- break_point_hit_count++;
+ if (event == v8::Break) {
+ if (break_point_hit_count < max_break_point_hit_count) {
+ // Count the number of breaks.
+ break_point_hit_count++;
- // Set the break flag again to come back here as soon as possible.
- v8::Debug::DebugBreak();
+ // Set the break flag again to come back here as soon as possible.
+ v8::Debug::DebugBreak();
+ } else if (terminate_after_max_break_point_hit) {
+ // Terminate execution after the last break if requested.
+ v8::V8::TerminateExecution();
+ }
}
}
@@ -6892,4 +6898,33 @@ TEST(DebugEventBreakData) {
CheckDebuggerUnloaded();
}
+
+// Test that setting the terminate execution flag during debug break processing.
+TEST(DebugBreakLoop) {
+ v8::HandleScope scope;
+ DebugLocalContext env;
+
+ // Receive 100 breaks and terminate.
+ max_break_point_hit_count = 100;
+ terminate_after_max_break_point_hit = true;
+
+ // Register a debug event listener which sets the break flag and counts.
+ v8::Debug::SetDebugEventListener(DebugEventBreakMax);
+
+ // Function with infinite loop.
+ CompileRun("function f() { while (true) { } }");
+
+ // Set the debug break to enter the debugger as soon as possible.
+ v8::Debug::DebugBreak();
+
+ // Call function with infinite loop.
+ CompileRun("f();");
+ CHECK_EQ(100, break_point_hit_count);
+
+ // Get rid of the debug event listener.
+ v8::Debug::SetDebugEventListener(NULL);
+ CheckDebuggerUnloaded();
+}
+
+
#endif // ENABLE_DEBUGGER_SUPPORT
diff --git a/test/cctest/test-lock.cc b/test/cctest/test-lock.cc
index 5eecfcee..9039e022 100644
--- a/test/cctest/test-lock.cc
+++ b/test/cctest/test-lock.cc
@@ -60,4 +60,5 @@ TEST(SemaphoreTimeout) {
sem->Signal();
ok = sem->Wait(1000);
CHECK(ok);
+ delete sem;
}
diff --git a/test/cctest/test-parsing.cc b/test/cctest/test-parsing.cc
index 02503f22..7ae8dcfa 100755
--- a/test/cctest/test-parsing.cc
+++ b/test/cctest/test-parsing.cc
@@ -26,6 +26,7 @@
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#include <stdlib.h>
+#include <stdio.h>
#include "v8.h"
@@ -34,7 +35,8 @@
#include "parser.h"
#include "utils.h"
#include "execution.h"
-
+#include "scanner.h"
+#include "preparser.h"
#include "cctest.h"
namespace i = ::v8::internal;
@@ -239,3 +241,32 @@ TEST(Preparsing) {
i::Vector<const char*> args = pre_impl->BuildArgs();
CHECK_GT(strlen(message), 0);
}
+
+
+TEST(StandAlonePreParser) {
+ int marker;
+ i::StackGuard::SetStackLimit(
+ reinterpret_cast<uintptr_t>(&marker) - 128 * 1024);
+
+ const char* programs[] = {
+ "{label: 42}",
+ "var x = 42;",
+ "function foo(x, y) { return x + y; }",
+ "native function foo(); return %ArgleBargle(glop);",
+ "var x = new new Function('this.x = 42');",
+ NULL
+ };
+
+ for (int i = 0; programs[i]; i++) {
+ const char* program = programs[i];
+ unibrow::Utf8InputBuffer<256> stream(program, strlen(program));
+ i::CompleteParserRecorder log;
+ i::Scanner scanner;
+ scanner.Initialize(i::Handle<i::String>::null(), &stream, i::JAVASCRIPT);
+ v8::preparser::PreParser<i::Scanner, i::CompleteParserRecorder> preparser;
+ bool result = preparser.PreParseProgram(&scanner, &log, true);
+ CHECK(result);
+ i::ScriptDataImpl data(log.ExtractData());
+ CHECK(!data.has_error());
+ }
+}
diff --git a/test/cctest/test-regexp.cc b/test/cctest/test-regexp.cc
index 11a808e3..3e6709ae 100644
--- a/test/cctest/test-regexp.cc
+++ b/test/cctest/test-regexp.cc
@@ -64,7 +64,7 @@ static bool CheckParse(const char* input) {
ZoneScope zone_scope(DELETE_ON_EXIT);
FlatStringReader reader(CStrVector(input));
RegExpCompileData result;
- return v8::internal::Parser::ParseRegExp(&reader, false, &result);
+ return v8::internal::RegExpParser::ParseRegExp(&reader, false, &result);
}
@@ -74,7 +74,7 @@ static SmartPointer<const char> Parse(const char* input) {
ZoneScope zone_scope(DELETE_ON_EXIT);
FlatStringReader reader(CStrVector(input));
RegExpCompileData result;
- CHECK(v8::internal::Parser::ParseRegExp(&reader, false, &result));
+ CHECK(v8::internal::RegExpParser::ParseRegExp(&reader, false, &result));
CHECK(result.tree != NULL);
CHECK(result.error.is_null());
SmartPointer<const char> output = result.tree->ToString();
@@ -88,7 +88,7 @@ static bool CheckSimple(const char* input) {
ZoneScope zone_scope(DELETE_ON_EXIT);
FlatStringReader reader(CStrVector(input));
RegExpCompileData result;
- CHECK(v8::internal::Parser::ParseRegExp(&reader, false, &result));
+ CHECK(v8::internal::RegExpParser::ParseRegExp(&reader, false, &result));
CHECK(result.tree != NULL);
CHECK(result.error.is_null());
return result.simple;
@@ -106,7 +106,7 @@ static MinMaxPair CheckMinMaxMatch(const char* input) {
ZoneScope zone_scope(DELETE_ON_EXIT);
FlatStringReader reader(CStrVector(input));
RegExpCompileData result;
- CHECK(v8::internal::Parser::ParseRegExp(&reader, false, &result));
+ CHECK(v8::internal::RegExpParser::ParseRegExp(&reader, false, &result));
CHECK(result.tree != NULL);
CHECK(result.error.is_null());
int min_match = result.tree->min_match();
@@ -365,7 +365,7 @@ static void ExpectError(const char* input,
ZoneScope zone_scope(DELETE_ON_EXIT);
FlatStringReader reader(CStrVector(input));
RegExpCompileData result;
- CHECK_EQ(false, v8::internal::Parser::ParseRegExp(&reader, false, &result));
+ CHECK(!v8::internal::RegExpParser::ParseRegExp(&reader, false, &result));
CHECK(result.tree == NULL);
CHECK(!result.error.is_null());
SmartPointer<char> str = result.error->ToCString(ALLOW_NULLS);
@@ -473,7 +473,8 @@ static RegExpNode* Compile(const char* input, bool multiline, bool is_ascii) {
V8::Initialize(NULL);
FlatStringReader reader(CStrVector(input));
RegExpCompileData compile_data;
- if (!v8::internal::Parser::ParseRegExp(&reader, multiline, &compile_data))
+ if (!v8::internal::RegExpParser::ParseRegExp(&reader, multiline,
+ &compile_data))
return NULL;
Handle<String> pattern = Factory::NewStringFromUtf8(CStrVector(input));
RegExpEngine::Compile(&compile_data, false, multiline, pattern, is_ascii);
diff --git a/test/cctest/test-serialize.cc b/test/cctest/test-serialize.cc
index 6a513e5f..1cbaf2bf 100644
--- a/test/cctest/test-serialize.cc
+++ b/test/cctest/test-serialize.cc
@@ -216,6 +216,7 @@ void FileByteSink::WriteSpaceUsed(
Vector<char> name = Vector<char>::New(file_name_length + 1);
OS::SNPrintF(name, "%s.size", file_name_);
FILE* fp = OS::FOpen(name.start(), "w");
+ name.Dispose();
fprintf(fp, "new %d\n", new_space_used);
fprintf(fp, "pointer %d\n", pointer_space_used);
fprintf(fp, "data %d\n", data_space_used);
@@ -381,6 +382,7 @@ TEST(PartialSerialization) {
env.Dispose();
FileByteSink startup_sink(startup_name.start());
+ startup_name.Dispose();
StartupSerializer startup_serializer(&startup_sink);
startup_serializer.SerializeStrongReferences();
@@ -403,6 +405,7 @@ static void ReserveSpaceForPartialSnapshot(const char* file_name) {
Vector<char> name = Vector<char>::New(file_name_length + 1);
OS::SNPrintF(name, "%s.size", file_name);
FILE* fp = OS::FOpen(name.start(), "r");
+ name.Dispose();
int new_size, pointer_size, data_size, code_size, map_size, cell_size;
int large_size;
#ifdef _MSC_VER
@@ -438,6 +441,7 @@ DEPENDENT_TEST(PartialDeserialization, PartialSerialization) {
OS::SNPrintF(startup_name, "%s.startup", FLAG_testing_serialization_file);
CHECK(Snapshot::Initialize(startup_name.start()));
+ startup_name.Dispose();
const char* file_name = FLAG_testing_serialization_file;
ReserveSpaceForPartialSnapshot(file_name);
@@ -495,6 +499,7 @@ TEST(ContextSerialization) {
env.Dispose();
FileByteSink startup_sink(startup_name.start());
+ startup_name.Dispose();
StartupSerializer startup_serializer(&startup_sink);
startup_serializer.SerializeStrongReferences();
@@ -519,6 +524,7 @@ DEPENDENT_TEST(ContextDeserialization, ContextSerialization) {
OS::SNPrintF(startup_name, "%s.startup", FLAG_testing_serialization_file);
CHECK(Snapshot::Initialize(startup_name.start()));
+ startup_name.Dispose();
const char* file_name = FLAG_testing_serialization_file;
ReserveSpaceForPartialSnapshot(file_name);
diff --git a/test/mjsunit/debug-compile-event.js b/test/mjsunit/debug-compile-event.js
index e7ecf47e..b00a907a 100644
--- a/test/mjsunit/debug-compile-event.js
+++ b/test/mjsunit/debug-compile-event.js
@@ -36,7 +36,6 @@ var current_source = ''; // Current source being compiled.
var source_count = 0; // Total number of scources compiled.
var host_compilations = 0; // Number of scources compiled through the API.
var eval_compilations = 0; // Number of scources compiled through eval.
-var json_compilations = 0; // Number of scources compiled through JSON.parse.
function compileSource(source) {
@@ -62,9 +61,6 @@ function listener(event, exec_state, event_data, data) {
case Debug.ScriptCompilationType.Eval:
eval_compilations++;
break;
- case Debug.ScriptCompilationType.JSON:
- json_compilations++;
- break;
}
}
@@ -74,13 +70,6 @@ function listener(event, exec_state, event_data, data) {
// For source with 'eval' there will be compile events with substrings
// as well as with with the exact source.
assertTrue(current_source.indexOf(event_data.script().source()) >= 0);
- } else if (current_source.indexOf('JSON.parse') == 0) {
- // For JSON the JSON source will be in parentheses.
- var s = event_data.script().source();
- if (s[0] == '(') {
- s = s.substring(1, s.length - 2);
- }
- assertTrue(current_source.indexOf(s) >= 0);
} else {
// For source without 'eval' there will be a compile events with the
// exact source.
@@ -113,7 +102,7 @@ source_count++; // Using eval causes additional compilation event.
compileSource('eval("eval(\'(function(){return a;})\')")');
source_count += 2; // Using eval causes additional compilation event.
compileSource('JSON.parse(\'{"a":1,"b":2}\')');
-source_count++; // Using JSON.parse causes additional compilation event.
+// Using JSON.parse does not causes additional compilation events.
compileSource('x=1; //@ sourceURL=myscript.js');
// Make sure that the debug event listener was invoked.
@@ -123,10 +112,9 @@ assertFalse(exception, "exception in listener")
assertEquals(before_compile_count, after_compile_count);
// Check the actual number of events (no compilation through the API as all
-// source compiled through eval except for one JSON.parse call).
+// source compiled through eval).
assertEquals(source_count, after_compile_count);
assertEquals(0, host_compilations);
-assertEquals(source_count - 1, eval_compilations);
-assertEquals(1, json_compilations);
+assertEquals(source_count, eval_compilations);
Debug.setListener(null);
diff --git a/test/mjsunit/mirror-script.js b/test/mjsunit/mirror-script.js
index 8631028e..71561701 100644
--- a/test/mjsunit/mirror-script.js
+++ b/test/mjsunit/mirror-script.js
@@ -83,12 +83,10 @@ function testScriptMirror(f, file_name, file_lines, type, compilation_type,
// Test the script mirror for different functions.
-testScriptMirror(function(){}, 'mirror-script.js', 100, 2, 0);
+testScriptMirror(function(){}, 'mirror-script.js', 98, 2, 0);
testScriptMirror(Math.sin, 'native math.js', -1, 0, 0);
testScriptMirror(eval('(function(){})'), null, 1, 2, 1, '(function(){})', 87);
testScriptMirror(eval('(function(){\n })'), null, 2, 2, 1, '(function(){\n })', 88);
-testScriptMirror(%CompileString('{"a":1,"b":2}', true), null, 1, 2, 2, '{"a":1,"b":2}');
-testScriptMirror(%CompileString('{"a":1,\n "b":2}', true), null, 2, 2, 2, '{"a":1,\n "b":2}');
// Test taking slices of source.
var mirror = debug.MakeMirror(eval('(function(){\n 1;\n})')).script();
diff --git a/test/mjsunit/mjsunit.status b/test/mjsunit/mjsunit.status
index 3c8cbdbf..820dca7c 100644
--- a/test/mjsunit/mjsunit.status
+++ b/test/mjsunit/mjsunit.status
@@ -45,6 +45,10 @@ unicode-case-overoptimization: PASS, TIMEOUT if ($arch == arm)
# Skip long running test in debug and allow it to timeout in release mode.
regress/regress-524: (PASS || TIMEOUT), SKIP if $mode == debug
+# Stack manipulations in LiveEdit are buggy - see bug 915
+debug-liveedit-check-stack: SKIP
+debug-liveedit-patch-positions-replace: SKIP
+
[ $arch == arm ]
# Slow tests which times out in debug mode.
@@ -61,14 +65,9 @@ array-splice: PASS || TIMEOUT
# Skip long running test in debug mode on ARM.
string-indexof-2: PASS, SKIP if $mode == debug
-# Stack manipulations in LiveEdit is implemented for ia32 only.
-debug-liveedit-check-stack: SKIP
[ $arch == mips ]
-# Stack manipulations in LiveEdit is implemented for ia32 only.
-debug-liveedit-check-stack: SKIP
-
# Skip all tests on MIPS.
*: SKIP
diff --git a/test/mjsunit/object-literal-conversions.js b/test/mjsunit/object-literal-conversions.js
new file mode 100644
index 00000000..8540d930
--- /dev/null
+++ b/test/mjsunit/object-literal-conversions.js
@@ -0,0 +1,46 @@
+// Copyright 2010 the V8 project authors. All rights reserved.
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following
+// disclaimer in the documentation and/or other materials provided
+// with the distribution.
+// * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived
+// from this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+// Test that the various conversions between property names are correctly
+// used when overwriting initializers.
+
+var test1 = { 13: 6, "13": 7 };
+var test2 = { 13: 7, "13.0": 6 };
+var test3 = { "13": 6, 13.0000000000000000: 7 };
+var test4 = { 13.213000: 6, "13.213": 7 };
+
+assertEquals(7, test1[13]);
+assertEquals(7, test2[13]);
+assertEquals(7, test3[13]);
+assertEquals(7, test4[13.213]);
+
+var test5 = { 13: function() {}, "13": 7 };
+var test6 = { 17.31: function() {}, "17.31": 7 };
+
+assertEquals(7, test5[13]);
+assertEquals(7, test6[17.31]);
+ \ No newline at end of file
diff --git a/test/mjsunit/object-literal-overwrite.js b/test/mjsunit/object-literal-overwrite.js
new file mode 100644
index 00000000..5c58a2dd
--- /dev/null
+++ b/test/mjsunit/object-literal-overwrite.js
@@ -0,0 +1,118 @@
+// Copyright 2010 the V8 project authors. All rights reserved.
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following
+// disclaimer in the documentation and/or other materials provided
+// with the distribution.
+// * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived
+// from this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+// Check that constants and computed properties are overwriting each other
+// correctly, i.e., the last initializer for any name is stored in the object.
+
+
+// Tests for the full code generator (if active).
+
+var foo1 = {
+ bar: 6,
+ bar: 7
+};
+
+var foo2 = {
+ bar: function(a){},
+ bar: 7
+};
+
+var foo3 = {
+ bar: function(a){},
+ bar: function(b){},
+ bar: 7
+};
+
+var foo4 = {
+ bar: function(b){},
+ bar: 7,
+ bar: function(){return 7},
+};
+
+var foo5 = {
+ 13: function(a){},
+ 13: 7
+}
+
+var foo6 = {
+ 14.31: function(a){},
+ 14.31: 7
+}
+
+var foo7 = {
+ 15: 6,
+ 15: 7
+}
+
+assertEquals(7, foo1.bar);
+assertEquals(7, foo2.bar);
+assertEquals(7, foo3.bar);
+assertEquals(7, foo4.bar());
+assertEquals(7, foo5[13]);
+assertEquals(7, foo6[14.31]);
+assertEquals(7, foo7[15]);
+
+// Test for the classic code generator.
+
+function fun(x) {
+ var inner = { j: function(x) { return x; }, j: 7 };
+ return inner.j;
+}
+
+assertEquals(7, fun(7) );
+
+// Check that the initializers of computed properties are executed, even if
+// no store instructions are generated for the literals.
+
+var glob1 = 0;
+
+var bar1 = { x: glob1++, x: glob1++, x: glob1++, x: 7};
+
+assertEquals(3, glob1);
+
+
+var glob2 = 0;
+
+function fun2() {
+ var r = { y: glob2++, y: glob2++, y: glob2++, y: 7};
+ return r.y;
+}
+
+var y = fun2();
+assertEquals(7, y);
+assertEquals(3, glob2);
+
+var glob3 = 0;
+
+function fun3() {
+ var r = { 113: glob3++, 113: glob3++, 113: glob3++, 113: 7};
+ return r[113];
+}
+
+var y = fun3();
+assertEquals(7, y);
+assertEquals(3, glob3); \ No newline at end of file
diff --git a/test/mjsunit/regress/regress-conditional-position.js b/test/mjsunit/regress/regress-conditional-position.js
new file mode 100644
index 00000000..cd8f7bd7
--- /dev/null
+++ b/test/mjsunit/regress/regress-conditional-position.js
@@ -0,0 +1,95 @@
+// Copyright 2010 the V8 project authors. All rights reserved.
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following
+// disclaimer in the documentation and/or other materials provided
+// with the distribution.
+// * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived
+// from this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+// Flags: --always-full-compiler
+
+var functionToCatch;
+var lineNumber;
+
+function catchLineNumber () {
+ var x = {};
+
+ Error.prepareStackTrace = function (error, stackTrace) {
+ stackTrace.some(function (frame) {
+ if (frame.getFunction() == functionToCatch) {
+ lineNumber = frame.getLineNumber();
+ return true;
+ }
+ return false;
+ });
+ return lineNumber;
+ };
+
+ Error.captureStackTrace(x);
+ return x.stack;
+}
+
+function log() {
+ catchLineNumber();
+}
+
+function foo() {}
+
+function test1() {
+ log(foo() == foo()
+ ? 'a'
+ : 'b');
+}
+
+function test2() {
+ var o = { foo: function () {}}
+ log(o.foo() == o.foo()
+ ? 'a'
+ : 'b');
+}
+
+function test3() {
+ var o = { log: log, foo: function() { } };
+ o.log(o.foo() == o.foo()
+ ? 'a'
+ : 'b');
+
+}
+
+function test(f, expectedLineNumber) {
+ functionToCatch = f;
+ f();
+
+ assertEquals(expectedLineNumber, lineNumber);
+}
+
+test(test1, 58);
+test(test2, 65);
+test(test3, 72);
+
+eval(test1.toString() + "//@ sourceUrl=foo");
+eval(test2.toString() + "//@ sourceUrl=foo");
+eval(test3.toString() + "//@ sourceUrl=foo");
+
+test(test1, 2);
+test(test2, 3);
+test(test3, 3);
diff --git a/test/mjsunit/string-externalize.js b/test/mjsunit/string-externalize.js
index 5b1f9170..da897869 100644
--- a/test/mjsunit/string-externalize.js
+++ b/test/mjsunit/string-externalize.js
@@ -25,7 +25,7 @@
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
-// Flags: --expose-externalize-string
+// Flags: --expose-externalize-string --expose-gc
var size = 1024;
@@ -93,3 +93,7 @@ function test() {
for (var i = 0; i < 10; i++) {
test();
}
+
+// Clean up string to make Valgrind happy.
+gc();
+gc();
diff --git a/test/mjsunit/string-replace-with-empty.js b/test/mjsunit/string-replace-with-empty.js
index 0e1e70a1..aa97f27a 100644
--- a/test/mjsunit/string-replace-with-empty.js
+++ b/test/mjsunit/string-replace-with-empty.js
@@ -25,33 +25,45 @@
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
-// Flags: --expose-externalize-string
+// Flags: --expose-externalize-string --expose-gc
-assertEquals("0123", "aa0bb1cc2dd3".replace(/[a-z]/g, ""));
-assertEquals("0123", "\u1234a0bb1cc2dd3".replace(/[\u1234a-z]/g, ""));
+function test() {
+ assertEquals("0123", "aa0bb1cc2dd3".replace(/[a-z]/g, ""));
+ assertEquals("0123", "\u1234a0bb1cc2dd3".replace(/[\u1234a-z]/g, ""));
-var expected = "0123";
-var cons = "a0b1c2d3";
-for (var i = 0; i < 5; i++) {
- expected += expected;
- cons += cons;
-}
-assertEquals(expected, cons.replace(/[a-z]/g, ""));
-cons = "\u12340b1c2d3";
-for (var i = 0; i < 5; i++) {
- cons += cons;
-}
-assertEquals(expected, cons.replace(/[\u1234a-z]/g, ""));
+ var expected = "0123";
+ var cons = "a0b1c2d3";
+ for (var i = 0; i < 5; i++) {
+ expected += expected;
+ cons += cons;
+ }
+ assertEquals(expected, cons.replace(/[a-z]/g, ""));
+ cons = "\u12340b1c2d3";
+ for (var i = 0; i < 5; i++) {
+ cons += cons;
+ }
+ assertEquals(expected, cons.replace(/[\u1234a-z]/g, ""));
-cons = "a0b1c2d3";
-for (var i = 0; i < 5; i++) {
- cons += cons;
-}
-externalizeString(cons, true/* force two-byte */);
-assertEquals(expected, cons.replace(/[a-z]/g, ""));
-cons = "\u12340b1c2d3";
-for (var i = 0; i < 5; i++) {
- cons += cons;
+ cons = "a0b1c2d3";
+ for (var i = 0; i < 5; i++) {
+ cons += cons;
+ }
+ externalizeString(cons, true/* force two-byte */);
+ assertEquals(expected, cons.replace(/[a-z]/g, ""));
+ cons = "\u12340b1c2d3";
+ for (var i = 0; i < 5; i++) {
+ cons += cons;
+ }
+ externalizeString(cons);
+ assertEquals(expected, cons.replace(/[\u1234a-z]/g, ""));
}
-externalizeString(cons);
-assertEquals(expected, cons.replace(/[\u1234a-z]/g, ""));
+
+test();
+
+// Clear the regexp cache to allow the GC to work.
+"foo".replace(/foo/g, "");
+
+// GC in order to free up things on the C side so we don't get
+// a memory leak. This makes valgrind happy.
+gc();
+gc();
diff --git a/test/mjsunit/string-split.js b/test/mjsunit/string-split.js
index 59d3ad3e..c741f6a3 100644
--- a/test/mjsunit/string-split.js
+++ b/test/mjsunit/string-split.js
@@ -27,76 +27,45 @@
expected = ["A", undefined, "B", "bold", "/", "B", "and", undefined, "CODE", "coded", "/", "CODE", ""];
result = "A<B>bold</B>and<CODE>coded</CODE>".split(/<(\/)?([^<>]+)>/);
-assertArrayEquals(expected, result, 1);
+assertArrayEquals(expected, result);
-expected = ["a", "b"];
-result = "ab".split(/a*?/);
-assertArrayEquals(expected, result, 2);
-expected = ["", "b"];
-result = "ab".split(/a*/);
-assertArrayEquals(expected, result, 3);
+assertArrayEquals(["a", "b"], "ab".split(/a*?/));
-expected = ["a"];
-result = "ab".split(/a*?/, 1);
-assertArrayEquals(expected, result, 4);
+assertArrayEquals(["", "b"], "ab".split(/a*/));
-expected = [""];
-result = "ab".split(/a*/, 1);
-assertArrayEquals(expected, result, 5);
+assertArrayEquals(["a"], "ab".split(/a*?/, 1));
-expected = ["as","fas","fas","f"];
-result = "asdfasdfasdf".split("d");
-assertArrayEquals(expected, result, 6);
+assertArrayEquals([""], "ab".split(/a*/, 1));
-expected = ["as","fas","fas","f"];
-result = "asdfasdfasdf".split("d", -1);
-assertArrayEquals(expected, result, 7);
+assertArrayEquals(["as","fas","fas","f"], "asdfasdfasdf".split("d"));
-expected = ["as", "fas"];
-result = "asdfasdfasdf".split("d", 2);
-assertArrayEquals(expected, result, 8);
+assertArrayEquals(["as","fas","fas","f"], "asdfasdfasdf".split("d", -1));
-expected = [];
-result = "asdfasdfasdf".split("d", 0);
-assertArrayEquals(expected, result, 9);
+assertArrayEquals(["as", "fas"], "asdfasdfasdf".split("d", 2));
-expected = ["as","fas","fas",""];
-result = "asdfasdfasd".split("d");
-assertArrayEquals(expected, result, 10);
+assertArrayEquals([], "asdfasdfasdf".split("d", 0));
-expected = [];
-result = "".split("");
-assertArrayEquals(expected, result, 11);
+assertArrayEquals(["as","fas","fas",""], "asdfasdfasd".split("d"));
-expected = [""]
-result = "".split("a");
-assertArrayEquals(expected, result, 12);
+assertArrayEquals([], "".split(""));
-expected = ["a","b"]
-result = "axxb".split(/x*/);
-assertArrayEquals(expected, result, 13);
+assertArrayEquals([""], "".split("a"));
-expected = ["a","b"]
-result = "axxb".split(/x+/);
-assertArrayEquals(expected, result, 14);
+assertArrayEquals(["a","b"], "axxb".split(/x*/));
-expected = ["a","","b"]
-result = "axxb".split(/x/);
-assertArrayEquals(expected, result, 15);
+assertArrayEquals(["a","b"], "axxb".split(/x+/));
+
+assertArrayEquals(["a","","b"], "axxb".split(/x/));
// This was http://b/issue?id=1151354
-expected = ["div", "#id", ".class"]
-result = "div#id.class".split(/(?=[#.])/);
-assertArrayEquals(expected, result, 16);
+assertArrayEquals(["div", "#id", ".class"], "div#id.class".split(/(?=[#.])/));
+
-expected = ["div", "#i", "d", ".class"]
-result = "div#id.class".split(/(?=[d#.])/);
-assertArrayEquals(expected, result, 17);
+assertArrayEquals(["div", "#i", "d", ".class"], "div#id.class".split(/(?=[d#.])/));
+
+assertArrayEquals(["a", "b", "c"], "abc".split(/(?=.)/));
-expected = ["a", "b", "c"]
-result = "abc".split(/(?=.)/);
-assertArrayEquals(expected, result, 18);
/* "ab".split(/((?=.))/)
*
@@ -108,19 +77,23 @@ assertArrayEquals(expected, result, 18);
*
* Opera seems to have this right. The others make no sense.
*/
-expected = ["a", "", "b"]
-result = "ab".split(/((?=.))/);
-assertArrayEquals(expected, result, 19);
+assertArrayEquals(["a", "", "b"], "ab".split(/((?=.))/));
/* "ab".split(/(?=)/)
*
* KJS: a,b
* SM: ab
* IE: a,b
- * Opera: a,b
+ * Opera: a,bb
* V8: a,b
*/
-expected = ["a", "b"]
-result = "ab".split(/(?=)/);
-assertArrayEquals(expected, result, 20);
+assertArrayEquals(["a", "b"], "ab".split(/(?=)/));
+
+// For issue http://code.google.com/p/v8/issues/detail?id=924
+// Splitting the empty string is a special case.
+assertEquals([""], ''.split());
+assertEquals([""], ''.split(/./));
+assertEquals([], ''.split(/.?/));
+assertEquals([], ''.split(/.??/));
+assertEquals([], ''.split(/()()/));
diff --git a/tools/gyp/v8.gyp b/tools/gyp/v8.gyp
index 17d556f1..3e40fcc6 100644
--- a/tools/gyp/v8.gyp
+++ b/tools/gyp/v8.gyp
@@ -426,6 +426,8 @@
'../../src/rewriter.h',
'../../src/runtime.cc',
'../../src/runtime.h',
+ '../../src/scanner-base.cc',
+ '../../src/scanner-base.h',
'../../src/scanner.cc',
'../../src/scanner.h',
'../../src/scopeinfo.cc',
diff --git a/tools/ll_prof.py b/tools/ll_prof.py
index 563084dd..8390d4af 100755
--- a/tools/ll_prof.py
+++ b/tools/ll_prof.py
@@ -353,7 +353,7 @@ class CodeLogReader(object):
r"code-info,([^,]+),(\d+)")
_CODE_CREATE_RE = re.compile(
- r"code-creation,([^,]+),(0x[a-f0-9]+),(\d+),\"([^\"]*)\"(?:,(\d+))?")
+ r"code-creation,([^,]+),(0x[a-f0-9]+),(\d+),\"(.*)\"(?:,(\d+))?")
_CODE_MOVE_RE = re.compile(
r"code-move,(0x[a-f0-9]+),(0x[a-f0-9]+)")
@@ -910,7 +910,7 @@ if __name__ == "__main__":
start = time.time()
mmap_info = trace_reader.ReadMmap(header, offset)
if mmap_info.filename == V8_GC_FAKE_MMAP:
- log_reader.ReadUpToGC()
+ log_reader.ReadUpToGC(code_info)
else:
library_repo.Load(mmap_info, code_map, options)
mmap_time += time.time() - start
diff --git a/tools/presubmit.py b/tools/presubmit.py
index e69c9a85..ebf8bd89 100755
--- a/tools/presubmit.py
+++ b/tools/presubmit.py
@@ -195,7 +195,7 @@ class CppLintProcessor(SourceFileProcessor):
or (name in CppLintProcessor.IGNORE_LINT))
def GetPathsToSearch(self):
- return ['src', 'public', 'samples', join('test', 'cctest')]
+ return ['src', 'include', 'samples', join('test', 'cctest')]
def ProcessFiles(self, files, path):
good_files_cache = FileContentsCache('.cpplint-cache')
diff --git a/tools/v8.xcodeproj/project.pbxproj b/tools/v8.xcodeproj/project.pbxproj
index 08558cc5..5f93c78a 100644
--- a/tools/v8.xcodeproj/project.pbxproj
+++ b/tools/v8.xcodeproj/project.pbxproj
@@ -115,6 +115,7 @@
89A88E180E71A6960043BA31 /* property.cc in Sources */ = {isa = PBXBuildFile; fileRef = 897FF16D0E719B8F00D62E90 /* property.cc */; };
89A88E190E71A6970043BA31 /* rewriter.cc in Sources */ = {isa = PBXBuildFile; fileRef = 897FF16F0E719B8F00D62E90 /* rewriter.cc */; };
89A88E1A0E71A69B0043BA31 /* runtime.cc in Sources */ = {isa = PBXBuildFile; fileRef = 897FF1710E719B8F00D62E90 /* runtime.cc */; };
+ 89A88E1B0E71A69D0043BA31 /* scanner-base.cc in Sources */ = {isa = PBXBuildFile; fileRef = 897FF1730E719B8F00D62E90 /* scanner-base.cc */; };
89A88E1B0E71A69D0043BA31 /* scanner.cc in Sources */ = {isa = PBXBuildFile; fileRef = 897FF1730E719B8F00D62E90 /* scanner.cc */; };
89A88E1C0E71A69E0043BA31 /* scopeinfo.cc in Sources */ = {isa = PBXBuildFile; fileRef = 897FF1760E719B8F00D62E90 /* scopeinfo.cc */; };
89A88E1D0E71A6A00043BA31 /* scopes.cc in Sources */ = {isa = PBXBuildFile; fileRef = 897FF1780E719B8F00D62E90 /* scopes.cc */; };
@@ -177,6 +178,7 @@
89F23C6C0E78D5B2006B2466 /* property.cc in Sources */ = {isa = PBXBuildFile; fileRef = 897FF16D0E719B8F00D62E90 /* property.cc */; };
89F23C6D0E78D5B2006B2466 /* rewriter.cc in Sources */ = {isa = PBXBuildFile; fileRef = 897FF16F0E719B8F00D62E90 /* rewriter.cc */; };
89F23C6E0E78D5B2006B2466 /* runtime.cc in Sources */ = {isa = PBXBuildFile; fileRef = 897FF1710E719B8F00D62E90 /* runtime.cc */; };
+ 89F23C6F0E78D5B2006B2466 /* scanner-base.cc in Sources */ = {isa = PBXBuildFile; fileRef = 897FF1730E719B8F00D62E90 /* scanner-base.cc */; };
89F23C6F0E78D5B2006B2466 /* scanner.cc in Sources */ = {isa = PBXBuildFile; fileRef = 897FF1730E719B8F00D62E90 /* scanner.cc */; };
89F23C700E78D5B2006B2466 /* scopeinfo.cc in Sources */ = {isa = PBXBuildFile; fileRef = 897FF1760E719B8F00D62E90 /* scopeinfo.cc */; };
89F23C710E78D5B2006B2466 /* scopes.cc in Sources */ = {isa = PBXBuildFile; fileRef = 897FF1780E719B8F00D62E90 /* scopes.cc */; };
@@ -481,6 +483,8 @@
897FF1700E719B8F00D62E90 /* rewriter.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = rewriter.h; sourceTree = "<group>"; };
897FF1710E719B8F00D62E90 /* runtime.cc */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = runtime.cc; sourceTree = "<group>"; };
897FF1720E719B8F00D62E90 /* runtime.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = runtime.h; sourceTree = "<group>"; };
+ 897FF1730E719B8F00D62E90 /* scanner-base.cc */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = scanner-base.cc; sourceTree = "<group>"; };
+ 897FF1740E719B8F00D62E90 /* scanner-base.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = scanner-base.h; sourceTree = "<group>"; };
897FF1730E719B8F00D62E90 /* scanner.cc */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = scanner.cc; sourceTree = "<group>"; };
897FF1740E719B8F00D62E90 /* scanner.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = scanner.h; sourceTree = "<group>"; };
897FF1750E719B8F00D62E90 /* SConscript */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = SConscript; sourceTree = "<group>"; };
@@ -943,6 +947,8 @@
897FF1700E719B8F00D62E90 /* rewriter.h */,
897FF1710E719B8F00D62E90 /* runtime.cc */,
897FF1720E719B8F00D62E90 /* runtime.h */,
+ 897FF1730E719B8F00D62E90 /* scanner-base.cc */,
+ 897FF1740E719B8F00D62E90 /* scanner-base.h */,
897FF1730E719B8F00D62E90 /* scanner.cc */,
897FF1740E719B8F00D62E90 /* scanner.h */,
897FF1760E719B8F00D62E90 /* scopeinfo.cc */,
@@ -1348,6 +1354,7 @@
58950D630F5551AF00F3E8BA /* register-allocator.cc in Sources */,
89A88E190E71A6970043BA31 /* rewriter.cc in Sources */,
89A88E1A0E71A69B0043BA31 /* runtime.cc in Sources */,
+ 89A88E1B0E71A69D0043BA31 /* scanner-base.cc in Sources */,
89A88E1B0E71A69D0043BA31 /* scanner.cc in Sources */,
89A88E1C0E71A69E0043BA31 /* scopeinfo.cc in Sources */,
89A88E1D0E71A6A00043BA31 /* scopes.cc in Sources */,
@@ -1472,6 +1479,7 @@
58950D640F5551B500F3E8BA /* register-allocator.cc in Sources */,
89F23C6D0E78D5B2006B2466 /* rewriter.cc in Sources */,
89F23C6E0E78D5B2006B2466 /* runtime.cc in Sources */,
+ 89F23C6F0E78D5B2006B2466 /* scanner-base.cc in Sources */,
89F23C6F0E78D5B2006B2466 /* scanner.cc in Sources */,
89F23C700E78D5B2006B2466 /* scopeinfo.cc in Sources */,
89F23C710E78D5B2006B2466 /* scopes.cc in Sources */,
diff --git a/tools/visual_studio/v8_base.vcproj b/tools/visual_studio/v8_base.vcproj
index 62d45015..bddf38e2 100644
--- a/tools/visual_studio/v8_base.vcproj
+++ b/tools/visual_studio/v8_base.vcproj
@@ -882,6 +882,14 @@
>
</File>
<File
+ RelativePath="..\..\src\scanner-base.cc"
+ >
+ </File>
+ <File
+ RelativePath="..\..\src\scanner-base.h"
+ >
+ </File>
+ <File
RelativePath="..\..\src\scanner.cc"
>
</File>