Enabled new regular expression engine.
Made a number of changes to the debugger protocol.
Fixed a number of bugs in the preemption support.
Added -p option to the developer shell to run files in parallel using preemption.
Fixed a number of minor bugs (including issues 176, 187, 189, 192, 193, 198 and 201).
Fixed a number of bugs in the serialization/deserialization support for the ARM platform.
git-svn-id: http://v8.googlecode.com/svn/trunk@1172 ce2b1a6d-e550-0410-aec6-3dcde31c8c00
diff --git a/ChangeLog b/ChangeLog
index 943ef34..cea2d03 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,21 @@
+2009-01-27: Version 0.4.9
+
+ Enabled new regular expression engine.
+
+ Made a number of changes to the debugger protocol.
+
+ Fixed a number of bugs in the preemption support.
+
+ Added -p option to the developer shell to run files in parallel
+ using preemption.
+
+ Fixed a number of minor bugs (including issues 176, 187, 189, 192,
+ 193, 198 and 201).
+
+ Fixed a number of bugs in the serialization/deserialization
+ support for the ARM platform.
+
+
2009-01-19: Version 0.4.8.1
Minor patch to debugger support.
diff --git a/SConstruct b/SConstruct
index 0f73dac..ccf412d 100644
--- a/SConstruct
+++ b/SConstruct
@@ -52,7 +52,7 @@
'CPPDEFINES': ['ENABLE_DISASSEMBLER', 'DEBUG']
},
'mode:release': {
- 'CCFLAGS': ['-O3', '-fomit-frame-pointer']
+ 'CCFLAGS': ['-O3', '-fomit-frame-pointer', '-fdata-sections', '-ffunction-sections']
},
'os:freebsd': {
'LIBS': ['execinfo']
diff --git a/include/v8.h b/include/v8.h
index 1876a54..fc8b47a 100644
--- a/include/v8.h
+++ b/include/v8.h
@@ -396,19 +396,11 @@
*/
class EXPORT HandleScope {
public:
- HandleScope() : previous_(current_), is_closed_(false) {
- current_.extensions = 0;
- }
+ HandleScope();
- ~HandleScope() {
- // TODO(1245391): In a perfect world, there would be a way of not
- // having to check for explicitly closed scopes maybe through
- // subclassing HandleScope?
- if (!is_closed_) RestorePreviousState();
- }
+ ~HandleScope();
/**
- * TODO(1245391): Consider introducing a subclass for this.
* Closes the handle scope and returns the value as a handle in the
* previous scope, which is the new current scope after the call.
*/
@@ -432,6 +424,8 @@
void* operator new(size_t size);
void operator delete(void*, size_t);
+ // This Data class is accessible internally through a typedef in the
+ // ImplementationUtilities class.
class EXPORT Data {
public:
int extensions;
@@ -443,31 +437,13 @@
}
};
- static Data current_;
- const Data previous_;
+ Data previous_;
- /**
- * Re-establishes the previous scope state. Should be called only
- * once, and only for the current scope.
- */
- void RestorePreviousState() {
- if (current_.extensions > 0) DeleteExtensions();
- current_ = previous_;
-#ifdef DEBUG
- ZapRange(current_.next, current_.limit);
-#endif
- }
-
- // TODO(1245391): Consider creating a subclass for this.
+ // Allow for the active closing of HandleScopes which allows to pass a handle
+ // from the HandleScope being closed to the next top most HandleScope.
bool is_closed_;
void** RawClose(void** value);
- /** Deallocates any extensions used by the current scope.*/
- static void DeleteExtensions();
-
- // Zaps the handles in the half-open interval [start, end).
- static void ZapRange(void** start, void** end);
-
friend class ImplementationUtilities;
};
@@ -2284,10 +2260,17 @@
*/
static bool IsLocked();
+ /**
+ * Returns whether v8::Locker is being used by this V8 instance.
+ */
+ static bool IsActive() { return active_; }
+
private:
bool has_lock_;
bool top_level_;
+ static bool active_;
+
// Disallow copying and assigning.
Locker(const Locker&);
void operator=(const Locker&);
diff --git a/src/api.cc b/src/api.cc
index 4069740..f617876 100644
--- a/src/api.cc
+++ b/src/api.cc
@@ -36,6 +36,7 @@
#include "platform.h"
#include "serialize.h"
#include "snapshot.h"
+#include "v8threads.h"
namespace i = v8::internal;
@@ -73,6 +74,15 @@
} while (false)
+#define API_ENTRY_CHECK(msg) \
+ do { \
+ if (v8::Locker::IsActive()) { \
+ ApiCheck(i::ThreadManager::IsLockedByCurrentThread(), \
+ msg, \
+ "Entering the V8 API without proper locking in place"); \
+ } \
+ } while (false)
+
// --- D a t a t h a t i s s p e c i f i c t o a t h r e a d ---
@@ -190,6 +200,19 @@
}
+ImplementationUtilities::HandleScopeData*
+ ImplementationUtilities::CurrentHandleScope() {
+ return &i::HandleScope::current_;
+}
+
+
+#ifdef DEBUG
+void ImplementationUtilities::ZapHandleRange(void** begin, void** end) {
+ i::HandleScope::ZapRange(begin, end);
+}
+#endif
+
+
v8::Handle<v8::Primitive> ImplementationUtilities::Undefined() {
if (IsDeadCheck("v8::Undefined()")) return v8::Handle<v8::Primitive>();
EnsureInitialized("v8::Undefined()");
@@ -359,55 +382,26 @@
// --- H a n d l e s ---
-HandleScope::Data HandleScope::current_ = { -1, NULL, NULL };
+HandleScope::HandleScope() : is_closed_(false) {
+ API_ENTRY_CHECK("HandleScope::HandleScope");
+ i::HandleScope::Enter(&previous_);
+}
+
+
+HandleScope::~HandleScope() {
+ if (!is_closed_) {
+ i::HandleScope::Leave(&previous_);
+ }
+}
int HandleScope::NumberOfHandles() {
- int n = thread_local.Blocks()->length();
- if (n == 0) return 0;
- return ((n - 1) * i::kHandleBlockSize) +
- (current_.next - thread_local.Blocks()->last());
+ return i::HandleScope::NumberOfHandles();
}
void** v8::HandleScope::CreateHandle(void* value) {
- void** result = current_.next;
- if (result == current_.limit) {
- // Make sure there's at least one scope on the stack and that the
- // top of the scope stack isn't a barrier.
- if (!ApiCheck(current_.extensions >= 0,
- "v8::HandleScope::CreateHandle()",
- "Cannot create a handle without a HandleScope")) {
- return NULL;
- }
- // If there's more room in the last block, we use that. This is used
- // for fast creation of scopes after scope barriers.
- if (!thread_local.Blocks()->is_empty()) {
- void** limit = &thread_local.Blocks()->last()[i::kHandleBlockSize];
- if (current_.limit != limit) {
- current_.limit = limit;
- }
- }
-
- // If we still haven't found a slot for the handle, we extend the
- // current handle scope by allocating a new handle block.
- if (result == current_.limit) {
- // If there's a spare block, use it for growing the current scope.
- result = thread_local.GetSpareOrNewBlock();
- // Add the extension to the global list of blocks, but count the
- // extension as part of the current scope.
- thread_local.Blocks()->Add(result);
- current_.extensions++;
- current_.limit = &result[i::kHandleBlockSize];
- }
- }
-
- // Update the current next field, set the value in the created
- // handle, and return the result.
- ASSERT(result < current_.limit);
- current_.next = result + 1;
- *result = value;
- return result;
+ return i::HandleScope::CreateHandle(value);
}
@@ -436,20 +430,6 @@
}
-void v8::HandleScope::DeleteExtensions() {
- ASSERT(current_.extensions != 0);
- thread_local.DeleteExtensions(current_.extensions);
-}
-
-
-void HandleScope::ZapRange(void** start, void** end) {
- if (start == NULL) return;
- for (void** p = start; p < end; p++) {
- *p = reinterpret_cast<void*>(v8::internal::kHandleZapValue);
- }
-}
-
-
void** v8::HandleScope::RawClose(void** value) {
if (!ApiCheck(!is_closed_,
"v8::HandleScope::Close()",
@@ -461,7 +441,7 @@
// Read the result before popping the handle block.
i::Object* result = reinterpret_cast<i::Object*>(*value);
is_closed_ = true;
- RestorePreviousState();
+ i::HandleScope::Leave(&previous_);
// Allocate a new handle on the previous handle block.
i::Handle<i::Object> handle(result);
@@ -2037,7 +2017,7 @@
i::Handle<i::String> str = Utils::OpenHandle(this);
// Flatten the string for efficiency. This applies whether we are
// using StringInputBuffer or Get(i) to access the characters.
- str->TryFlatten(i::StringShape(*str));
+ str->TryFlattenIfNotFlat(i::StringShape(*str));
int end = length;
if ( (length == -1) || (length > str->length() - start) )
end = str->length() - start;
@@ -2062,7 +2042,7 @@
i::Handle<i::String> str = Utils::OpenHandle(this);
// Flatten the string for efficiency. This applies whether we are
// using StringInputBuffer or Get(i) to access the characters.
- str->TryFlatten(i::StringShape(*str));
+ str->TryFlattenIfNotFlat(i::StringShape(*str));
int end = length;
if ( (length == -1) || (length > str->length() - start) )
end = str->length() - start;
@@ -2204,7 +2184,7 @@
const char* v8::V8::GetVersion() {
- return "0.4.8.1";
+ return "0.4.9";
}
@@ -2951,8 +2931,8 @@
char* HandleScopeImplementer::ArchiveThreadHelper(char* storage) {
- ImplementationUtilities::HandleScopeData* current =
- ImplementationUtilities::CurrentHandleScope();
+ v8::ImplementationUtilities::HandleScopeData* current =
+ v8::ImplementationUtilities::CurrentHandleScope();
handle_scope_data_ = *current;
memcpy(storage, this, sizeof(*this));
@@ -2975,7 +2955,7 @@
char* HandleScopeImplementer::RestoreThreadHelper(char* storage) {
memcpy(this, storage, sizeof(*this));
- *ImplementationUtilities::CurrentHandleScope() = handle_scope_data_;
+ *v8::ImplementationUtilities::CurrentHandleScope() = handle_scope_data_;
return storage + ArchiveSpacePerThread();
}
@@ -2983,7 +2963,7 @@
void HandleScopeImplementer::Iterate(
ObjectVisitor* v,
List<void**>* blocks,
- ImplementationUtilities::HandleScopeData* handle_data) {
+ v8::ImplementationUtilities::HandleScopeData* handle_data) {
// Iterate over all handles in the blocks except for the last.
for (int i = blocks->length() - 2; i >= 0; --i) {
Object** block =
@@ -3000,8 +2980,8 @@
void HandleScopeImplementer::Iterate(ObjectVisitor* v) {
- ImplementationUtilities::HandleScopeData* current =
- ImplementationUtilities::CurrentHandleScope();
+ v8::ImplementationUtilities::HandleScopeData* current =
+ v8::ImplementationUtilities::CurrentHandleScope();
Iterate(v, thread_local.Blocks(), current);
}
@@ -3010,7 +2990,7 @@
HandleScopeImplementer* thread_local =
reinterpret_cast<HandleScopeImplementer*>(storage);
List<void**>* blocks_of_archived_thread = thread_local->Blocks();
- ImplementationUtilities::HandleScopeData* handle_data_of_archived_thread =
+ v8::ImplementationUtilities::HandleScopeData* handle_data_of_archived_thread =
&thread_local->handle_scope_data_;
Iterate(v, blocks_of_archived_thread, handle_data_of_archived_thread);
diff --git a/src/api.h b/src/api.h
index f4cea8b..85b13ec 100644
--- a/src/api.h
+++ b/src/api.h
@@ -28,6 +28,7 @@
#ifndef V8_API_H_
#define V8_API_H_
+#include "apiutils.h"
#include "factory.h"
namespace v8 {
@@ -159,45 +160,6 @@
};
-class ImplementationUtilities {
- public:
- static v8::Handle<v8::Primitive> Undefined();
- static v8::Handle<v8::Primitive> Null();
- static v8::Handle<v8::Boolean> True();
- static v8::Handle<v8::Boolean> False();
-
- static int GetNameCount(ExtensionConfiguration* that) {
- return that->name_count_;
- }
-
- static const char** GetNames(ExtensionConfiguration* that) {
- 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);
- }
-
- // Introduce an alias for the handle scope data to allow non-friends
- // to access the HandleScope data.
- typedef v8::HandleScope::Data HandleScopeData;
-
- static HandleScopeData* CurrentHandleScope() {
- return &v8::HandleScope::current_;
- }
-
-#ifdef DEBUG
- static void ZapHandleRange(void** begin, void** end) {
- v8::HandleScope::ZapRange(begin, end);
- }
-#endif
-};
-
-
class Utils {
public:
static bool ReportApiFailure(const char* location, const char* message);
@@ -275,7 +237,7 @@
template <class T>
v8::internal::Handle<T> v8::internal::Handle<T>::EscapeFrom(
- HandleScope* scope) {
+ v8::HandleScope* scope) {
return Utils::OpenHandle(*scope->Close(Utils::ToLocal(*this)));
}
@@ -408,11 +370,11 @@
List<Handle<Object> > saved_contexts_;
bool ignore_out_of_memory;
// This is only used for threading support.
- ImplementationUtilities::HandleScopeData handle_scope_data_;
+ v8::ImplementationUtilities::HandleScopeData handle_scope_data_;
static void Iterate(ObjectVisitor* v,
- List<void**>* blocks,
- ImplementationUtilities::HandleScopeData* handle_data);
+ List<void**>* blocks,
+ v8::ImplementationUtilities::HandleScopeData* handle_data);
char* RestoreThreadHelper(char* from);
char* ArchiveThreadHelper(char* to);
@@ -474,13 +436,14 @@
for (int i = extensions; i > 1; --i) {
void** block = blocks.RemoveLast();
#ifdef DEBUG
- ImplementationUtilities::ZapHandleRange(block, &block[kHandleBlockSize]);
+ v8::ImplementationUtilities::ZapHandleRange(block,
+ &block[kHandleBlockSize]);
#endif
DeleteArray(block);
}
spare = reinterpret_cast<Object**>(blocks.RemoveLast());
#ifdef DEBUG
- ImplementationUtilities::ZapHandleRange(
+ v8::ImplementationUtilities::ZapHandleRange(
reinterpret_cast<void**>(spare),
reinterpret_cast<void**>(&spare[kHandleBlockSize]));
#endif
diff --git a/src/apiutils.h b/src/apiutils.h
new file mode 100644
index 0000000..5745343
--- /dev/null
+++ b/src/apiutils.h
@@ -0,0 +1,69 @@
+// Copyright 2009 the V8 project authors. All rights reserved.
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following
+// disclaimer in the documentation and/or other materials provided
+// with the distribution.
+// * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived
+// from this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+#ifndef V8_APIUTILS_H_
+#define V8_APIUTILS_H_
+
+namespace v8 {
+
+class ImplementationUtilities {
+ public:
+ static v8::Handle<v8::Primitive> Undefined();
+ static v8::Handle<v8::Primitive> Null();
+ static v8::Handle<v8::Boolean> True();
+ static v8::Handle<v8::Boolean> False();
+
+ static int GetNameCount(ExtensionConfiguration* that) {
+ return that->name_count_;
+ }
+
+ static const char** GetNames(ExtensionConfiguration* that) {
+ 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);
+ }
+
+ // Introduce an alias for the handle scope data to allow non-friends
+ // to access the HandleScope data.
+ typedef v8::HandleScope::Data HandleScopeData;
+
+ static HandleScopeData* CurrentHandleScope();
+
+#ifdef DEBUG
+ static void ZapHandleRange(void** begin, void** end);
+#endif
+};
+
+} // namespace v8
+
+#endif // V8_APIUTILS_H_
diff --git a/src/assembler-ia32-inl.h b/src/assembler-ia32-inl.h
index 8b5fc64..7b65225 100644
--- a/src/assembler-ia32-inl.h
+++ b/src/assembler-ia32-inl.h
@@ -270,8 +270,7 @@
}
-void Operand::set_modrm(int mod, // reg == 0
- Register rm) {
+void Operand::set_modrm(int mod, Register rm) {
ASSERT((mod & -4) == 0);
buf_[0] = mod << 6 | rm.code();
len_ = 1;
diff --git a/src/assembler-ia32.cc b/src/assembler-ia32.cc
index 3f663df..a4c3c9d 100644
--- a/src/assembler-ia32.cc
+++ b/src/assembler-ia32.cc
@@ -255,12 +255,6 @@
}
-void Operand::set_reg(Register reg) const {
- ASSERT(len_ > 0);
- buf_[0] = (buf_[0] & ~0x38) | static_cast<byte>(reg.code() << 3);
-}
-
-
bool Operand::is_reg(Register reg) const {
return ((buf_[0] & 0xF8) == 0xC0) // addressing mode is register only.
&& ((buf_[0] & 0x07) == reg.code()); // register codes match.
@@ -2098,10 +2092,18 @@
void Assembler::emit_operand(Register reg, const Operand& adr) {
- adr.set_reg(reg);
- memmove(pc_, adr.buf_, adr.len_);
- pc_ += adr.len_;
- if (adr.len_ >= sizeof(int32_t) && adr.rmode_ != RelocInfo::NONE) {
+ const unsigned length = adr.len_;
+ ASSERT(length > 0);
+
+ // Emit updated ModRM byte containing the given register.
+ pc_[0] = (adr.buf_[0] & ~0x38) | (reg.code() << 3);
+
+ // Emit the rest of the encoded operand.
+ for (unsigned i = 1; i < length; i++) pc_[i] = adr.buf_[i];
+ pc_ += length;
+
+ // Emit relocation information if necessary.
+ if (length >= sizeof(int32_t) && adr.rmode_ != RelocInfo::NONE) {
pc_ -= sizeof(int32_t); // pc_ must be *at* disp32
RecordRelocInfo(adr.rmode_);
pc_ += sizeof(int32_t);
diff --git a/src/assembler-ia32.h b/src/assembler-ia32.h
index cb273c0..4219212 100644
--- a/src/assembler-ia32.h
+++ b/src/assembler-ia32.h
@@ -260,19 +260,19 @@
bool is_reg(Register reg) const;
private:
- // Mutable because reg in ModR/M byte is set by Assembler via set_reg().
- mutable byte buf_[6];
+ byte buf_[6];
// The number of bytes in buf_.
unsigned int len_;
// Only valid if len_ > 4.
RelocInfo::Mode rmode_;
- inline void set_modrm(int mod, // reg == 0
- Register rm);
+ // Set the ModRM byte without an encoded 'reg' register. The
+ // register is encoded later as part of the emit_operand operation.
+ inline void set_modrm(int mod, Register rm);
+
inline void set_sib(ScaleFactor scale, Register index, Register base);
inline void set_disp8(int8_t disp);
inline void set_dispr(int32_t disp, RelocInfo::Mode rmode);
- inline void set_reg(Register reg) const;
friend class Assembler;
};
diff --git a/src/ast.cc b/src/ast.cc
index 5034151..a5f4dc8 100644
--- a/src/ast.cc
+++ b/src/ast.cc
@@ -244,6 +244,36 @@
}
+bool RegExpAssertion::IsAnchored() {
+ return type() == RegExpAssertion::START_OF_INPUT;
+}
+
+
+bool RegExpAlternative::IsAnchored() {
+ return this->nodes()->at(0)->IsAnchored();
+}
+
+
+bool RegExpDisjunction::IsAnchored() {
+ ZoneList<RegExpTree*>* alternatives = this->alternatives();
+ for (int i = 0; i < alternatives->length(); i++) {
+ if (!alternatives->at(i)->IsAnchored())
+ return false;
+ }
+ return true;
+}
+
+
+bool RegExpLookahead::IsAnchored() {
+ return is_positive() && body()->IsAnchored();
+}
+
+
+bool RegExpCapture::IsAnchored() {
+ return body()->IsAnchored();
+}
+
+
// Convert regular expression trees to a simple sexp representation.
// This representation should be different from the input grammar
// in as many cases as possible, to make it more difficult for incorrect
@@ -417,6 +447,7 @@
RegExpDisjunction::RegExpDisjunction(ZoneList<RegExpTree*>* alternatives)
: alternatives_(alternatives) {
+ ASSERT(alternatives->length() > 1);
RegExpTree* first_alternative = alternatives->at(0);
min_match_ = first_alternative->min_match();
max_match_ = first_alternative->max_match();
@@ -430,6 +461,7 @@
RegExpAlternative::RegExpAlternative(ZoneList<RegExpTree*>* nodes)
: nodes_(nodes) {
+ ASSERT(nodes->length() > 1);
min_match_ = 0;
max_match_ = 0;
for (int i = 0; i < nodes->length(); i++) {
diff --git a/src/ast.h b/src/ast.h
index 360a054..ea4c947 100644
--- a/src/ast.h
+++ b/src/ast.h
@@ -1253,6 +1253,7 @@
virtual RegExpNode* ToNode(RegExpCompiler* compiler,
RegExpNode* on_success) = 0;
virtual bool IsTextElement() { return false; }
+ virtual bool IsAnchored() { return false; }
virtual int min_match() = 0;
virtual int max_match() = 0;
// Returns the interval of registers used for captures within this
@@ -1277,6 +1278,7 @@
virtual RegExpDisjunction* AsDisjunction();
virtual Interval CaptureRegisters();
virtual bool IsDisjunction();
+ virtual bool IsAnchored();
virtual int min_match() { return min_match_; }
virtual int max_match() { return max_match_; }
ZoneList<RegExpTree*>* alternatives() { return alternatives_; }
@@ -1296,6 +1298,7 @@
virtual RegExpAlternative* AsAlternative();
virtual Interval CaptureRegisters();
virtual bool IsAlternative();
+ virtual bool IsAnchored();
virtual int min_match() { return min_match_; }
virtual int max_match() { return max_match_; }
ZoneList<RegExpTree*>* nodes() { return nodes_; }
@@ -1322,6 +1325,7 @@
RegExpNode* on_success);
virtual RegExpAssertion* AsAssertion();
virtual bool IsAssertion();
+ virtual bool IsAnchored();
virtual int min_match() { return 0; }
virtual int max_match() { return 0; }
Type type() { return type_; }
@@ -1382,7 +1386,7 @@
// W : non-ASCII word character
// d : ASCII digit
// D : non-ASCII digit
- // . : non-unicode newline
+ // . : non-unicode non-newline
// * : All characters
uc16 standard_type() { return set_.standard_set_type(); }
ZoneList<CharacterRange>* ranges() { return set_.ranges(); }
@@ -1495,6 +1499,7 @@
RegExpCompiler* compiler,
RegExpNode* on_success);
virtual RegExpCapture* AsCapture();
+ virtual bool IsAnchored();
virtual Interval CaptureRegisters();
virtual bool IsCapture();
virtual int min_match() { return body_->min_match(); }
@@ -1516,22 +1521,33 @@
class RegExpLookahead: public RegExpTree {
public:
- RegExpLookahead(RegExpTree* body, bool is_positive)
+ RegExpLookahead(RegExpTree* body,
+ bool is_positive,
+ int capture_count,
+ int capture_from)
: body_(body),
- is_positive_(is_positive) { }
+ is_positive_(is_positive),
+ capture_count_(capture_count),
+ capture_from_(capture_from) { }
+
virtual void* Accept(RegExpVisitor* visitor, void* data);
virtual RegExpNode* ToNode(RegExpCompiler* compiler,
RegExpNode* on_success);
virtual RegExpLookahead* AsLookahead();
virtual Interval CaptureRegisters();
virtual bool IsLookahead();
+ virtual bool IsAnchored();
virtual int min_match() { return 0; }
virtual int max_match() { return 0; }
RegExpTree* body() { return body_; }
bool is_positive() { return is_positive_; }
+ int capture_count() { return capture_count_; }
+ int capture_from() { return capture_from_; }
private:
RegExpTree* body_;
bool is_positive_;
+ int capture_count_;
+ int capture_from_;
};
diff --git a/src/bootstrapper.cc b/src/bootstrapper.cc
index 970acd3..7928a5e 100644
--- a/src/bootstrapper.cc
+++ b/src/bootstrapper.cc
@@ -265,6 +265,11 @@
Genesis* previous() { return previous_; }
static Genesis* current() { return current_; }
+ // Support for thread preemption.
+ static int ArchiveSpacePerThread();
+ static char* ArchiveState(char* to);
+ static char* RestoreState(char* from);
+
private:
Handle<Context> global_context_;
@@ -1466,4 +1471,45 @@
result_ = global_context_;
}
+
+// Support for thread preemption.
+
+// Reserve space for statics needing saving and restoring.
+int Bootstrapper::ArchiveSpacePerThread() {
+ return Genesis::ArchiveSpacePerThread();
+}
+
+
+// Archive statics that are thread local.
+char* Bootstrapper::ArchiveState(char* to) {
+ return Genesis::ArchiveState(to);
+}
+
+
+// Restore statics that are thread local.
+char* Bootstrapper::RestoreState(char* from) {
+ return Genesis::RestoreState(from);
+}
+
+
+// Reserve space for statics needing saving and restoring.
+int Genesis::ArchiveSpacePerThread() {
+ return sizeof(current_);
+}
+
+
+// Archive statics that are thread local.
+char* Genesis::ArchiveState(char* to) {
+ *reinterpret_cast<Genesis**>(to) = current_;
+ current_ = NULL;
+ return to + sizeof(current_);
+}
+
+
+// Restore statics that are thread local.
+char* Genesis::RestoreState(char* from) {
+ current_ = *reinterpret_cast<Genesis**>(from);
+ return from + sizeof(current_);
+}
+
} } // namespace v8::internal
diff --git a/src/bootstrapper.h b/src/bootstrapper.h
index 1314bfd..e2883dc 100644
--- a/src/bootstrapper.h
+++ b/src/bootstrapper.h
@@ -68,6 +68,11 @@
class FixupFlagsIsPCRelative: public BitField<bool, 0, 1> {};
class FixupFlagsUseCodeObject: public BitField<bool, 1, 1> {};
class FixupFlagsArgumentsCount: public BitField<uint32_t, 2, 32-2> {};
+
+ // Support for thread preemption.
+ static int ArchiveSpacePerThread();
+ static char* ArchiveState(char* to);
+ static char* RestoreState(char* from);
};
}} // namespace v8::internal
diff --git a/src/bytecodes-irregexp.h b/src/bytecodes-irregexp.h
index 7ec8635..c7cb908 100644
--- a/src/bytecodes-irregexp.h
+++ b/src/bytecodes-irregexp.h
@@ -31,49 +31,59 @@
namespace v8 { namespace internal {
+
+static const int BYTECODE_MASK = 0xff;
+static const unsigned int MAX_FIRST_ARG = 0xffffffu;
+static const int BYTECODE_SHIFT = 8;
+
#define BYTECODE_ITERATOR(V) \
-V(BREAK, 0, 1) /* break */ \
-V(PUSH_CP, 1, 5) /* push_cp offset32 */ \
-V(PUSH_BT, 2, 5) /* push_bt addr32 */ \
-V(PUSH_REGISTER, 3, 2) /* push_register register_index */ \
-V(SET_REGISTER_TO_CP, 4, 6) /* set_register_to_cp register_index offset32 */ \
-V(SET_CP_TO_REGISTER, 5, 2) /* set_cp_to_registger register_index */ \
-V(SET_REGISTER_TO_SP, 6, 2) /* set_register_to_sp register_index */ \
-V(SET_SP_TO_REGISTER, 7, 2) /* set_sp_to_registger register_index */ \
-V(SET_REGISTER, 8, 6) /* set_register register_index value32 */ \
-V(ADVANCE_REGISTER, 9, 6) /* advance_register register_index value32 */ \
-V(POP_CP, 10, 1) /* pop_cp */ \
-V(POP_BT, 11, 1) /* pop_bt */ \
-V(POP_REGISTER, 12, 2) /* pop_register register_index */ \
-V(FAIL, 13, 1) /* fail */ \
-V(SUCCEED, 14, 1) /* succeed */ \
-V(ADVANCE_CP, 15, 5) /* advance_cp offset32 */ \
-V(GOTO, 16, 5) /* goto addr32 */ \
-V(LOAD_CURRENT_CHAR, 17, 9) /* load offset32 addr32 */ \
-V(LOAD_CURRENT_CHAR_UNCHECKED, 18, 5) /* load offset32 */ \
-V(LOAD_2_CURRENT_CHARS, 19, 9) /* load offset32 addr32 */ \
-V(LOAD_2_CURRENT_CHARS_UNCHECKED, 20, 5) /* load offset32 */ \
-V(LOAD_4_CURRENT_CHARS, 21, 9) /* load offset32 addr32 */ \
-V(LOAD_4_CURRENT_CHARS_UNCHECKED, 22, 5) /* load offset32 */ \
-V(CHECK_CHAR, 23, 9) /* check_char uint32 addr32 */ \
-V(CHECK_NOT_CHAR, 24, 9) /* check_not_char uint32 addr32 */ \
-V(AND_CHECK_CHAR, 25, 13) /* and_check_char uint32 uint32 addr32 */ \
-V(AND_CHECK_NOT_CHAR, 26, 13) /* and_check_not_char uint32 uint32 addr32 */ \
-V(MINUS_AND_CHECK_NOT_CHAR, 27, 11) /* minus_and_check_not_char uc16 uc16...*/ \
-V(CHECK_LT, 28, 7) /* check_lt uc16 addr32 */ \
-V(CHECK_GT, 29, 7) /* check_gr uc16 addr32 */ \
-V(CHECK_NOT_BACK_REF, 30, 6) /* check_not_back_ref capture_idx addr32 */ \
-V(CHECK_NOT_BACK_REF_NO_CASE, 31, 6) /* check_not_back_ref_no_case captu... */ \
-V(CHECK_NOT_REGS_EQUAL, 32, 7) /* check_not_regs_equal reg1 reg2 addr32 */ \
-V(LOOKUP_MAP1, 33, 11) /* l_map1 start16 bit_map_addr32 addr32 */ \
-V(LOOKUP_MAP2, 34, 99) /* l_map2 start16 half_nibble_map_addr32* */ \
-V(LOOKUP_MAP8, 35, 99) /* l_map8 start16 byte_map addr32* */ \
-V(LOOKUP_HI_MAP8, 36, 99) /* l_himap8 start8 byte_map_addr32 addr32* */ \
-V(CHECK_REGISTER_LT, 37, 8) /* check_reg_lt register_index value16 addr32 */ \
-V(CHECK_REGISTER_GE, 38, 8) /* check_reg_ge register_index value16 addr32 */ \
-V(CHECK_REGISTER_EQ_POS, 39, 6) /* check_register_eq_pos index addr32 */ \
-V(CHECK_NOT_AT_START, 40, 5) /* check_not_at_start addr32 */ \
-V(CHECK_GREEDY, 41, 5) /* check_greedy addr32 */
+V(BREAK, 0, 4) /* bc8 */ \
+V(PUSH_CP, 1, 4) /* bc8 pad24 */ \
+V(PUSH_BT, 2, 8) /* bc8 pad24 offset32 */ \
+V(PUSH_REGISTER, 3, 4) /* bc8 reg_idx24 */ \
+V(SET_REGISTER_TO_CP, 4, 8) /* bc8 reg_idx24 offset32 */ \
+V(SET_CP_TO_REGISTER, 5, 4) /* bc8 reg_idx24 */ \
+V(SET_REGISTER_TO_SP, 6, 4) /* bc8 reg_idx24 */ \
+V(SET_SP_TO_REGISTER, 7, 4) /* bc8 reg_idx24 */ \
+V(SET_REGISTER, 8, 8) /* bc8 reg_idx24 value32 */ \
+V(ADVANCE_REGISTER, 9, 8) /* bc8 reg_idx24 value32 */ \
+V(POP_CP, 10, 4) /* bc8 pad24 */ \
+V(POP_BT, 11, 4) /* bc8 pad24 */ \
+V(POP_REGISTER, 12, 4) /* bc8 reg_idx24 */ \
+V(FAIL, 13, 4) /* bc8 pad24 */ \
+V(SUCCEED, 14, 4) /* bc8 pad24 */ \
+V(ADVANCE_CP, 15, 4) /* bc8 offset24 */ \
+V(GOTO, 16, 8) /* bc8 pad24 addr32 */ \
+V(LOAD_CURRENT_CHAR, 17, 8) /* bc8 offset24 addr32 */ \
+V(LOAD_CURRENT_CHAR_UNCHECKED, 18, 4) /* bc8 offset24 */ \
+V(LOAD_2_CURRENT_CHARS, 19, 8) /* bc8 offset24 addr32 */ \
+V(LOAD_2_CURRENT_CHARS_UNCHECKED, 20, 4) /* bc8 offset24 */ \
+V(LOAD_4_CURRENT_CHARS, 21, 8) /* bc8 offset24 addr32 */ \
+V(LOAD_4_CURRENT_CHARS_UNCHECKED, 22, 4) /* bc8 offset24 */ \
+V(CHECK_4_CHARS, 23, 12) /* bc8 pad24 uint32 addr32 */ \
+V(CHECK_CHAR, 24, 8) /* bc8 pad8 uint16 addr32 */ \
+V(CHECK_NOT_4_CHARS, 25, 12) /* bc8 pad24 uint32 addr32 */ \
+V(CHECK_NOT_CHAR, 26, 8) /* bc8 pad8 uint16 addr32 */ \
+V(AND_CHECK_4_CHARS, 27, 16) /* bc8 pad24 uint32 uint32 addr32 */ \
+V(AND_CHECK_CHAR, 28, 12) /* bc8 pad8 uint16 uint32 addr32 */ \
+V(AND_CHECK_NOT_4_CHARS, 29, 16) /* bc8 pad24 uint32 uint32 addr32 */ \
+V(AND_CHECK_NOT_CHAR, 30, 12) /* bc8 pad8 uint16 uint32 addr32 */ \
+V(MINUS_AND_CHECK_NOT_CHAR, 31, 12) /* bc8 pad8 uc16 uc16 addr32 */ \
+V(CHECK_LT, 32, 8) /* bc8 pad8 uc16 addr32 */ \
+V(CHECK_GT, 33, 8) /* bc8 pad8 uc16 addr32 */ \
+V(CHECK_NOT_BACK_REF, 34, 8) /* bc8 reg_idx24 addr32 */ \
+V(CHECK_NOT_BACK_REF_NO_CASE, 35, 8) /* bc8 reg_idx24 addr32 */ \
+V(CHECK_NOT_REGS_EQUAL, 36, 12) /* bc8 regidx24 reg_idx32 addr32 */ \
+V(LOOKUP_MAP1, 37, 12) /* bc8 pad8 start16 bit_map_addr32 addr32 */ \
+V(LOOKUP_MAP2, 38, 96) /* bc8 pad8 start16 half_nibble_map_addr32* */ \
+V(LOOKUP_MAP8, 39, 96) /* bc8 pad8 start16 byte_map addr32* */ \
+V(LOOKUP_HI_MAP8, 40, 96) /* bc8 start24 byte_map_addr32 addr32* */ \
+V(CHECK_REGISTER_LT, 41, 12) /* bc8 reg_idx24 value32 addr32 */ \
+V(CHECK_REGISTER_GE, 42, 12) /* bc8 reg_idx24 value32 addr32 */ \
+V(CHECK_REGISTER_EQ_POS, 43, 8) /* bc8 reg_idx24 addr32 */ \
+V(CHECK_AT_START, 44, 8) /* bc8 pad24 addr32 */ \
+V(CHECK_NOT_AT_START, 45, 8) /* bc8 pad24 addr32 */ \
+V(CHECK_GREEDY, 46, 8) /* bc8 pad24 addr32 */
#define DECLARE_BYTECODES(name, code, length) \
static const int BC_##name = code;
diff --git a/src/checks.h b/src/checks.h
index 77c43bc..0ff79af 100644
--- a/src/checks.h
+++ b/src/checks.h
@@ -136,9 +136,9 @@
void* value) {
if (expected != value) {
V8_Fatal(file, line,
- "CHECK_EQ(%s, %s) failed\n# Expected: %i\n# Found: %i",
+ "CHECK_EQ(%s, %s) failed\n# Expected: %p\n# Found: %p",
expected_source, value_source,
- reinterpret_cast<int>(expected), reinterpret_cast<int>(value));
+ expected, value);
}
}
@@ -150,8 +150,8 @@
const char* value_source,
void* value) {
if (expected == value) {
- V8_Fatal(file, line, "CHECK_NE(%s, %s) failed\n# Value: %i",
- expected_source, value_source, reinterpret_cast<int>(value));
+ V8_Fatal(file, line, "CHECK_NE(%s, %s) failed\n# Value: %p",
+ expected_source, value_source, value);
}
}
diff --git a/src/codegen-ia32.cc b/src/codegen-ia32.cc
index a9cc800..287074c 100644
--- a/src/codegen-ia32.cc
+++ b/src/codegen-ia32.cc
@@ -1014,8 +1014,8 @@
// code size is increased by ~1% (measured on a combination of
// different benchmarks).
- // TODO(1217802): Optimize some special cases of operations
- // involving a smi literal (multiply by 2, shift by 0, etc.).
+ // TODO(199): Optimize some special cases of operations involving a
+ // smi literal (multiply by 2, shift by 0, etc.).
// Get the literal value.
int int_value = Smi::cast(*value)->value();
@@ -1127,11 +1127,13 @@
__ j(not_zero, deferred->enter(), not_taken);
__ sar(ebx, kSmiTagSize);
__ shl(ebx, shift_value);
- __ lea(ecx, Operand(ebx, 0x40000000));
- __ test(ecx, Immediate(0x80000000));
- __ j(not_zero, deferred->enter(), not_taken);
- // tag result and store it in TOS (eax)
- ASSERT(kSmiTagSize == times_2); // adjust code if not the case
+ // This is the Smi check for the shifted result.
+ // After signed subtraction of 0xc0000000, the valid
+ // Smis are positive.
+ __ cmp(ebx, 0xc0000000);
+ __ j(sign, deferred->enter(), not_taken);
+ // Tag the result and store it on top of the frame.
+ ASSERT(kSmiTagSize == times_2); // Adjust the code if not true.
__ lea(eax, Operand(ebx, ebx, times_1, kSmiTag));
__ bind(deferred->exit());
frame_->Push(eax);
@@ -4274,9 +4276,8 @@
case Token::SHL:
__ shl(eax);
// Check that the *signed* result fits in a smi.
- __ lea(ecx, Operand(eax, 0x40000000));
- __ test(ecx, Immediate(0x80000000));
- __ j(not_zero, slow, not_taken);
+ __ cmp(eax, 0xc0000000);
+ __ j(sign, slow, not_taken);
break;
default:
UNREACHABLE();
diff --git a/src/contexts.cc b/src/contexts.cc
index 0f3f608..8155999 100644
--- a/src/contexts.cc
+++ b/src/contexts.cc
@@ -94,7 +94,11 @@
// check extension/with object
if (context->has_extension()) {
Handle<JSObject> extension = Handle<JSObject>(context->extension());
- if ((flags & FOLLOW_PROTOTYPE_CHAIN) == 0) {
+ // Context extension objects needs to behave as if they have no
+ // prototype. So even if we want to follow prototype chains, we
+ // need to only do a local lookup for context extension objects.
+ if ((flags & FOLLOW_PROTOTYPE_CHAIN) == 0 ||
+ extension->IsJSContextExtensionObject()) {
*attributes = extension->GetLocalPropertyAttribute(*name);
} else {
*attributes = extension->GetPropertyAttribute(*name);
diff --git a/src/d8.cc b/src/d8.cc
index 24eb722..e026d28 100644
--- a/src/d8.cc
+++ b/src/d8.cc
@@ -33,6 +33,7 @@
#include "debug.h"
#include "api.h"
#include "natives.h"
+#include "platform.h"
namespace v8 {
@@ -383,10 +384,10 @@
}
-// Reads a file into a v8 string.
-Handle<String> Shell::ReadFile(const char* name) {
+static char* ReadChars(const char *name, int* size_out) {
+ v8::Unlocker unlocker; // Release the V8 lock while reading files.
FILE* file = i::OS::FOpen(name, "rb");
- if (file == NULL) return Handle<String>();
+ if (file == NULL) return NULL;
fseek(file, 0, SEEK_END);
int size = ftell(file);
@@ -399,7 +400,17 @@
i += read;
}
fclose(file);
- Handle<String> result = String::New(chars, size);
+ *size_out = size;
+ return chars;
+}
+
+
+// Reads a file into a v8 string.
+Handle<String> Shell::ReadFile(const char* name) {
+ int size = 0;
+ char* chars = ReadChars(name, &size);
+ if (chars == NULL) return Handle<String>();
+ Handle<String> result = String::New(chars);
delete[] chars;
return result;
}
@@ -410,7 +421,9 @@
printf("V8 version %s [console: %s]\n", V8::GetVersion(), editor->name());
editor->Open();
while (true) {
+ Locker locker;
HandleScope handle_scope;
+ Context::Scope context_scope(evaluation_context_);
i::SmartPointer<char> input = editor->Prompt(Shell::kPrompt);
if (input.is_empty())
break;
@@ -423,6 +436,53 @@
}
+class ShellThread : public i::Thread {
+ public:
+ ShellThread(int no, i::Vector<const char> files)
+ : no_(no), files_(files) { }
+ virtual void Run();
+ private:
+ int no_;
+ i::Vector<const char> files_;
+};
+
+
+void ShellThread::Run() {
+ // Prepare the context for this thread.
+ Locker locker;
+ HandleScope scope;
+ Handle<ObjectTemplate> global_template = ObjectTemplate::New();
+ global_template->Set(String::New("print"),
+ FunctionTemplate::New(Shell::Print));
+ global_template->Set(String::New("load"),
+ FunctionTemplate::New(Shell::Load));
+ global_template->Set(String::New("version"),
+ FunctionTemplate::New(Shell::Version));
+
+ Persistent<Context> thread_context = Context::New(NULL, global_template);
+ thread_context->SetSecurityToken(Undefined());
+
+ Context::Scope context_scope(thread_context);
+
+ char* ptr = const_cast<char*>(files_.start());
+ while ((ptr != NULL) && (*ptr != '\0')) {
+ // For each newline-separated line.
+ char *filename = ptr;
+ char* next = ::strchr(ptr, '\n');
+ if (next != NULL) {
+ *next = '\0';
+ ptr = (next + 1);
+ } else {
+ ptr = NULL;
+ }
+ Handle<String> str = Shell::ReadFile(filename);
+ Shell::ExecuteString(str, String::New(filename), false, false);
+ }
+
+ thread_context.Dispose();
+}
+
+
int Shell::Main(int argc, char* argv[]) {
i::FlagList::SetFlagsFromCommandLine(&argc, argv, true);
if (i::FLAG_help) {
@@ -430,42 +490,67 @@
}
Initialize();
bool run_shell = (argc == 1);
- Context::Scope context_scope(evaluation_context_);
- for (int i = 1; i < argc; i++) {
- char* str = argv[i];
- if (strcmp(str, "--shell") == 0) {
- run_shell = true;
- } else if (strcmp(str, "-f") == 0) {
- // Ignore any -f flags for compatibility with other stand-alone
- // JavaScript engines.
- continue;
- } else if (strncmp(str, "--", 2) == 0) {
- printf("Warning: unknown flag %s.\nTry --help for options\n", str);
- } else if (strcmp(str, "-e") == 0 && i + 1 < argc) {
- // Execute argument given to -e option directly.
- v8::HandleScope handle_scope;
- v8::Handle<v8::String> file_name = v8::String::New("unnamed");
- v8::Handle<v8::String> source = v8::String::New(argv[i + 1]);
- if (!ExecuteString(source, file_name, false, true))
- return 1;
- i++;
- } else {
- // Use all other arguments as names of files to load and run.
- HandleScope handle_scope;
- Handle<String> file_name = v8::String::New(str);
- Handle<String> source = ReadFile(str);
- if (source.IsEmpty()) {
- printf("Error reading '%s'\n", str);
- return 1;
+ i::List<i::Thread*> threads(1);
+
+ {
+ // Acquire the V8 lock once initialization has finished. Since the thread
+ // below may spawn new threads accessing V8 holding the V8 lock here is
+ // mandatory.
+ Locker locker;
+ Context::Scope context_scope(evaluation_context_);
+ for (int i = 1; i < argc; i++) {
+ char* str = argv[i];
+ if (strcmp(str, "--shell") == 0) {
+ run_shell = true;
+ } else if (strcmp(str, "-f") == 0) {
+ // Ignore any -f flags for compatibility with other stand-alone
+ // JavaScript engines.
+ continue;
+ } else if (strncmp(str, "--", 2) == 0) {
+ printf("Warning: unknown flag %s.\nTry --help for options\n", str);
+ } else if (strcmp(str, "-e") == 0 && i + 1 < argc) {
+ // Execute argument given to -e option directly.
+ v8::HandleScope handle_scope;
+ v8::Handle<v8::String> file_name = v8::String::New("unnamed");
+ v8::Handle<v8::String> source = v8::String::New(argv[i + 1]);
+ if (!ExecuteString(source, file_name, false, true))
+ return 1;
+ i++;
+ } else if (strcmp(str, "-p") == 0 && i + 1 < argc) {
+ // Use the lowest possible thread preemption interval to test as many
+ // edgecases as possible.
+ Locker::StartPreemption(1);
+ int size = 0;
+ const char *files = ReadChars(argv[++i], &size);
+ if (files == NULL) return 1;
+ ShellThread *thread =
+ new ShellThread(threads.length(),
+ i::Vector<const char>(files, size));
+ thread->Start();
+ threads.Add(thread);
+ } else {
+ // Use all other arguments as names of files to load and run.
+ HandleScope handle_scope;
+ Handle<String> file_name = v8::String::New(str);
+ Handle<String> source = ReadFile(str);
+ if (source.IsEmpty()) {
+ printf("Error reading '%s'\n", str);
+ return 1;
+ }
+ if (!ExecuteString(source, file_name, false, true))
+ return 1;
}
- if (!ExecuteString(source, file_name, false, true))
- return 1;
}
+ if (i::FLAG_debugger)
+ v8::Debug::AddDebugEventListener(HandleDebugEvent);
}
- if (i::FLAG_debugger)
- v8::Debug::AddDebugEventListener(HandleDebugEvent);
if (run_shell)
RunShell();
+ for (int i = 0; i < threads.length(); i++) {
+ i::Thread *thread = threads[i];
+ thread->Join();
+ delete thread;
+ }
OnExit();
return 0;
}
diff --git a/src/d8.js b/src/d8.js
index 61467b9..0314d51 100644
--- a/src/d8.js
+++ b/src/d8.js
@@ -262,6 +262,10 @@
this.request_ = this.printCommandToJSONRequest_(args);
break;
+ case 'dir':
+ this.request_ = this.dirCommandToJSONRequest_(args);
+ break;
+
case 'source':
this.request_ = this.sourceCommandToJSONRequest_(args);
break;
@@ -289,6 +293,8 @@
default:
throw new Error('Unknown command "' + cmd + '"');
}
+
+ last_cmd = cmd;
}
DebugRequest.prototype.JSONRequest = function() {
@@ -330,6 +336,27 @@
};
+// Create a JSON request for the evaluation command.
+DebugRequest.prototype.makeEvaluateJSONRequest_ = function(expression) {
+ // Check if the expression is a handle id in the form #<handle>#.
+ var handle_match = expression.match(/^#([0-9]*)#$/);
+ if (handle_match) {
+ // Build an evaluate request.
+ var request = this.createRequest('lookup');
+ request.arguments = {};
+ request.arguments.handle = parseInt(handle_match[1]);
+ return request.toJSONProtocol();
+ } else {
+ // Build an evaluate request.
+ var request = this.createRequest('evaluate');
+ request.arguments = {};
+ request.arguments.expression = expression;
+ return request.toJSONProtocol();
+ }
+};
+
+
+
// Create a JSON request for the continue command.
DebugRequest.prototype.continueCommandToJSONRequest_ = function(args) {
var request = this.createRequest('continue');
@@ -438,16 +465,21 @@
// Create a JSON request for the print command.
DebugRequest.prototype.printCommandToJSONRequest_ = function(args) {
- // Build a evaluate request from the text command.
- var request = this.createRequest('evaluate');
+ // Build an evaluate request from the text command.
if (args.length == 0) {
throw new Error('Missing expression.');
}
+ return this.makeEvaluateJSONRequest_(args);
+};
- request.arguments = {};
- request.arguments.expression = args;
- return request.toJSONProtocol();
+// Create a JSON request for the dir command.
+DebugRequest.prototype.dirCommandToJSONRequest_ = function(args) {
+ // Build an evaluate request from the text command.
+ if (args.length == 0) {
+ throw new Error('Missing expression.');
+ }
+ return this.makeEvaluateJSONRequest_(args);
};
@@ -585,39 +617,43 @@
}
+function formatHandleReference_(value) {
+ return '#' + value.handle() + '#';
+}
+
+
// Convert a JSON response to text for display in a text based debugger.
function DebugResponseDetails(json_response) {
details = {text:'', running:false}
try {
// Convert the JSON string to an object.
- response = eval('(' + json_response + ')');
+ var response = new ProtocolPackage(json_response);
- if (!response.success) {
- details.text = response.message;
+ if (!response.success()) {
+ details.text = response.message();
return details;
}
// Get the running state.
- details.running = response.running;
+ details.running = response.running();
- switch (response.command) {
+ var body = response.body();
+ var result = '';
+ switch (response.command()) {
case 'setbreakpoint':
- var body = response.body;
result = 'set breakpoint #';
result += body.breakpoint;
details.text = result;
break;
case 'clearbreakpoint':
- var body = response.body;
result = 'cleared breakpoint #';
result += body.breakpoint;
details.text = result;
break;
case 'backtrace':
- var body = response.body;
if (body.totalFrames == 0) {
result = '(empty stack)';
} else {
@@ -632,20 +668,67 @@
break;
case 'frame':
- details.text = SourceUnderline(response.body.sourceLineText,
- response.body.column);
- Debug.State.currentSourceLine = response.body.line;
- Debug.State.currentFrame = response.body.index;
+ details.text = SourceUnderline(body.sourceLineText,
+ body.column);
+ Debug.State.currentSourceLine = body.line;
+ Debug.State.currentFrame = body.index;
break;
case 'evaluate':
- details.text = response.body.text;
+ case 'lookup':
+ if (last_cmd == 'p' || last_cmd == 'print') {
+ details.text = body.text;
+ } else {
+ var value = response.bodyValue();
+ if (value.isObject()) {
+ result += formatHandleReference_(value);
+ result += ', type: object'
+ result += ', constructor ';
+ var ctor = value.constructorFunctionValue();
+ result += formatHandleReference_(ctor);
+ result += ', __proto__ ';
+ var proto = value.protoObjectValue();
+ result += formatHandleReference_(proto);
+ result += ', ';
+ result += value.propertyCount();
+ result += ' properties.\n';
+ for (var i = 0; i < value.propertyCount(); i++) {
+ result += ' ';
+ result += value.propertyName(i);
+ result += ': ';
+ var property_value = value.propertyValue(i);
+ if (property_value && property_value.type()) {
+ result += property_value.type();
+ } else {
+ result += '<no type>';
+ }
+ result += ' ';
+ result += formatHandleReference_(property_value);
+ result += '\n';
+ }
+ } else {
+ result += 'type: ';
+ result += value.type();
+ if (!value.isUndefined() && !value.isNull()) {
+ result += ', ';
+ if (value.isString()) {
+ result += '"';
+ }
+ result += value.value();
+ if (value.isString()) {
+ result += '"';
+ }
+ }
+ result += '\n';
+ }
+ }
+ details.text = result;
break;
case 'source':
// Get the source from the response.
- var source = response.body.source;
- var from_line = response.body.fromLine + 1;
+ var source = body.source;
+ var from_line = body.fromLine + 1;
var lines = source.split('\n');
var maxdigits = 1 + Math.floor(log10(from_line + lines.length));
if (maxdigits < 3) {
@@ -679,25 +762,25 @@
case 'scripts':
var result = '';
- for (i = 0; i < response.body.length; i++) {
+ for (i = 0; i < body.length; i++) {
if (i != 0) result += '\n';
- if (response.body[i].name) {
- result += response.body[i].name;
+ if (body[i].name) {
+ result += body[i].name;
} else {
result += '[unnamed] ';
- var sourceStart = response.body[i].sourceStart;
+ var sourceStart = body[i].sourceStart;
if (sourceStart.length > 40) {
sourceStart = sourceStart.substring(0, 37) + '...';
}
result += sourceStart;
}
result += ' (lines: ';
- result += response.body[i].sourceLines;
+ result += body[i].sourceLines;
result += ', length: ';
- result += response.body[i].sourceLength;
- if (response.body[i].type == Debug.ScriptType.Native) {
+ result += body[i].sourceLength;
+ if (body[i].type == Debug.ScriptType.Native) {
result += ', native';
- } else if (response.body[i].type == Debug.ScriptType.Extension) {
+ } else if (body[i].type == Debug.ScriptType.Extension) {
result += ', extension';
}
result += ')';
@@ -722,6 +805,270 @@
};
+/**
+ * Protocol packages send from the debugger.
+ * @param {string} json - raw protocol packet as JSON string.
+ * @constructor
+ */
+function ProtocolPackage(json) {
+ this.packet_ = eval('(' + json + ')');
+ this.refs_ = [];
+ if (this.packet_.refs) {
+ for (var i = 0; i < this.packet_.refs.length; i++) {
+ this.refs_[this.packet_.refs[i].handle] = this.packet_.refs[i];
+ }
+ }
+}
+
+
+/**
+ * Get the packet type.
+ * @return {String} the packet type
+ */
+ProtocolPackage.prototype.type = function() {
+ return this.packet_.type;
+}
+
+
+/**
+ * Get the packet event.
+ * @return {Object} the packet event
+ */
+ProtocolPackage.prototype.event = function() {
+ return this.packet_.event;
+}
+
+
+/**
+ * Get the packet request sequence.
+ * @return {number} the packet request sequence
+ */
+ProtocolPackage.prototype.requestSeq = function() {
+ return this.packet_.request_seq;
+}
+
+
+/**
+ * Get the packet request sequence.
+ * @return {number} the packet request sequence
+ */
+ProtocolPackage.prototype.running = function() {
+ return this.packet_.running ? true : false;
+}
+
+
+ProtocolPackage.prototype.success = function() {
+ return this.packet_.success ? true : false;
+}
+
+
+ProtocolPackage.prototype.message = function() {
+ return this.packet_.message;
+}
+
+
+ProtocolPackage.prototype.command = function() {
+ return this.packet_.command;
+}
+
+
+ProtocolPackage.prototype.body = function() {
+ return this.packet_.body;
+}
+
+
+ProtocolPackage.prototype.bodyValue = function() {
+ return new ProtocolValue(this.packet_.body, this);
+}
+
+
+ProtocolPackage.prototype.body = function() {
+ return this.packet_.body;
+}
+
+
+ProtocolPackage.prototype.lookup = function(handle) {
+ var value = this.refs_[handle];
+ if (value) {
+ return new ProtocolValue(value, this);
+ } else {
+ return new ProtocolReference(handle);
+ }
+}
+
+
+function ProtocolValue(value, packet) {
+ this.value_ = value;
+ this.packet_ = packet;
+}
+
+
+/**
+ * Get the value type.
+ * @return {String} the value type
+ */
+ProtocolValue.prototype.type = function() {
+ return this.value_.type;
+}
+
+
+/**
+ * Check is the value is a primitive value.
+ * @return {boolean} true if the value is primitive
+ */
+ProtocolValue.prototype.isPrimitive = function() {
+ return this.isUndefined() || this.isNull() || this.isBoolean() ||
+ this.isNumber() || this.isString();
+}
+
+
+/**
+ * Get the object handle.
+ * @return {number} the value handle
+ */
+ProtocolValue.prototype.handle = function() {
+ return this.value_.handle;
+}
+
+
+/**
+ * Check is the value is undefined.
+ * @return {boolean} true if the value is undefined
+ */
+ProtocolValue.prototype.isUndefined = function() {
+ return this.value_.type == 'undefined';
+}
+
+
+/**
+ * Check is the value is null.
+ * @return {boolean} true if the value is null
+ */
+ProtocolValue.prototype.isNull = function() {
+ return this.value_.type == 'null';
+}
+
+
+/**
+ * Check is the value is a boolean.
+ * @return {boolean} true if the value is a boolean
+ */
+ProtocolValue.prototype.isBoolean = function() {
+ return this.value_.type == 'boolean';
+}
+
+
+/**
+ * Check is the value is a number.
+ * @return {boolean} true if the value is a number
+ */
+ProtocolValue.prototype.isNumber = function() {
+ return this.value_.type == 'number';
+}
+
+
+/**
+ * Check is the value is a string.
+ * @return {boolean} true if the value is a string
+ */
+ProtocolValue.prototype.isString = function() {
+ return this.value_.type == 'string';
+}
+
+
+/**
+ * Check is the value is an object.
+ * @return {boolean} true if the value is an object
+ */
+ProtocolValue.prototype.isObject = function() {
+ return this.value_.type == 'object' || this.value_.type == 'function' ||
+ this.value_.type == 'error' || this.value_.type == 'regexp';
+}
+
+
+/**
+ * Get the constructor function
+ * @return {ProtocolValue} constructor function
+ */
+ProtocolValue.prototype.constructorFunctionValue = function() {
+ var ctor = this.value_.constructorFunction;
+ return this.packet_.lookup(ctor.ref);
+}
+
+
+/**
+ * Get the __proto__ value
+ * @return {ProtocolValue} __proto__ value
+ */
+ProtocolValue.prototype.protoObjectValue = function() {
+ var proto = this.value_.protoObject;
+ return this.packet_.lookup(proto.ref);
+}
+
+
+/**
+ * Get the number og properties.
+ * @return {number} the number of properties
+ */
+ProtocolValue.prototype.propertyCount = function() {
+ return this.value_.properties ? this.value_.properties.length : 0;
+}
+
+
+/**
+ * Get the specified property name.
+ * @return {string} property name
+ */
+ProtocolValue.prototype.propertyName = function(index) {
+ var property = this.value_.properties[index];
+ return property.name;
+}
+
+
+/**
+ * Return index for the property name.
+ * @param name The property name to look for
+ * @return {number} index for the property name
+ */
+ProtocolValue.prototype.propertyIndex = function(name) {
+ for (var i = 0; i < this.propertyCount(); i++) {
+ if (this.value_.properties[i].name == name) {
+ return i;
+ }
+ }
+ return null;
+}
+
+
+/**
+ * Get the specified property value.
+ * @return {ProtocolValue} property value
+ */
+ProtocolValue.prototype.propertyValue = function(index) {
+ var property = this.value_.properties[index];
+ return this.packet_.lookup(property.ref);
+}
+
+
+/**
+ * Check is the value is a string.
+ * @return {boolean} true if the value is a string
+ */
+ProtocolValue.prototype.value = function() {
+ return this.value_.value;
+}
+
+
+function ProtocolReference(handle) {
+ this.handle_ = handle;
+}
+
+
+ProtocolReference.prototype.handle = function() {
+ return this.handle_;
+}
+
+
function MakeJSONPair_(name, value) {
return '"' + name + '":' + value;
}
diff --git a/src/debug-delay.js b/src/debug-delay.js
index d388946..71ed37f 100644
--- a/src/debug-delay.js
+++ b/src/debug-delay.js
@@ -1071,6 +1071,8 @@
this.frameRequest_(request, response);
} else if (request.command == 'evaluate') {
this.evaluateRequest_(request, response);
+ } else if (request.command == 'lookup') {
+ this.lookupRequest_(request, response);
} else if (request.command == 'source') {
this.sourceRequest_(request, response);
} else if (request.command == 'scripts') {
@@ -1366,7 +1368,12 @@
}
// With no arguments just keep the selected frame.
- if (request.arguments && request.arguments.number >= 0) {
+ if (request.arguments) {
+ index = request.arguments.number;
+ if (index < 0 || this.exec_state_.frameCount() <= index) {
+ return response.failed('Invalid frame number');
+ }
+
this.exec_state_.setSelectedFrame(request.arguments.number);
}
response.body = this.exec_state_.frame();
@@ -1429,6 +1436,29 @@
};
+DebugCommandProcessor.prototype.lookupRequest_ = function(request, response) {
+ if (!request.arguments) {
+ return response.failed('Missing arguments');
+ }
+
+ // Pull out arguments.
+ var handle = request.arguments.handle;
+
+ // Check for legal arguments.
+ if (IS_UNDEFINED(handle)) {
+ return response.failed('Argument "handle" missing');
+ }
+
+ // Lookup handle.
+ var mirror = LookupMirror(handle);
+ if (mirror) {
+ response.body = mirror;
+ } else {
+ return response.failed('Object #' + handle + '# not found');
+ }
+};
+
+
DebugCommandProcessor.prototype.sourceRequest_ = function(request, response) {
// No frames no source.
if (this.exec_state_.frameCount() == 0) {
diff --git a/src/execution.cc b/src/execution.cc
index cb05314..f721cbd 100644
--- a/src/execution.cc
+++ b/src/execution.cc
@@ -191,12 +191,24 @@
StackGuard::StackGuard() {
+ // NOTE: Overall the StackGuard code assumes that the stack grows towards
+ // lower addresses.
ExecutionAccess access;
- if (thread_local_.nesting_++ == 0 &&
- thread_local_.jslimit_ != kInterruptLimit) {
- // NOTE: We assume that the stack grows towards lower addresses.
- ASSERT(thread_local_.jslimit_ == kIllegalLimit);
- ASSERT(thread_local_.climit_ == kIllegalLimit);
+ if (thread_local_.nesting_++ == 0) {
+ // Initial StackGuard is being set. We will set the stack limits based on
+ // the current stack pointer allowing the stack to grow kLimitSize from
+ // here.
+
+ // Ensure that either the stack limits are unset (kIllegalLimit) or that
+ // they indicate a pending interruption. The interrupt limit will be
+ // temporarily reset through the code below and reestablished if the
+ // interrupt flags indicate that an interrupt is pending.
+ ASSERT(thread_local_.jslimit_ == kIllegalLimit ||
+ (thread_local_.jslimit_ == kInterruptLimit &&
+ thread_local_.interrupt_flags_ != 0));
+ ASSERT(thread_local_.climit_ == kIllegalLimit ||
+ (thread_local_.climit_ == kInterruptLimit &&
+ thread_local_.interrupt_flags_ != 0));
thread_local_.initial_jslimit_ = thread_local_.jslimit_ =
GENERATED_CODE_STACK_LIMIT(kLimitSize);
@@ -210,9 +222,11 @@
set_limits(kInterruptLimit, access);
}
}
- // make sure we have proper limits setup
+ // Ensure that proper limits have been set.
ASSERT(thread_local_.jslimit_ != kIllegalLimit &&
thread_local_.climit_ != kIllegalLimit);
+ ASSERT(thread_local_.initial_jslimit_ != kIllegalLimit &&
+ thread_local_.initial_climit_ != kIllegalLimit);
}
diff --git a/src/execution.h b/src/execution.h
index bd37525..d5f2fb6 100644
--- a/src/execution.h
+++ b/src/execution.h
@@ -228,7 +228,12 @@
class StackLimitCheck BASE_EMBEDDED {
public:
bool HasOverflowed() const {
- return reinterpret_cast<uintptr_t>(this) < StackGuard::climit();
+ // Stack has overflowed in C++ code only if stack pointer exceeds the C++
+ // stack guard and the limits are not set to interrupt values.
+ // TODO(214): Stack overflows are ignored if a interrupt is pending. This
+ // code should probably always use the initial C++ limit.
+ return (reinterpret_cast<uintptr_t>(this) < StackGuard::climit()) &&
+ StackGuard::IsStackOverflow();
}
};
diff --git a/src/factory.cc b/src/factory.cc
index c49f58a..52d3bad 100644
--- a/src/factory.cc
+++ b/src/factory.cc
@@ -327,7 +327,7 @@
Handle<Object> Factory::NewError(const char* maker, const char* type,
Vector< Handle<Object> > args) {
- HandleScope scope;
+ v8::HandleScope scope; // Instantiate a closeable HandleScope for EscapeFrom.
Handle<FixedArray> array = Factory::NewFixedArray(args.length());
for (int i = 0; i < args.length(); i++) {
array->set(i, *args[i]);
diff --git a/src/flag-definitions.h b/src/flag-definitions.h
index 194340b..b97c44b 100644
--- a/src/flag-definitions.h
+++ b/src/flag-definitions.h
@@ -199,12 +199,10 @@
DEFINE_bool(preemption, false,
"activate a 100ms timer that switches between V8 threads")
-// irregexp
-DEFINE_bool(irregexp, false, "new regular expression code")
+// Irregexp
+DEFINE_bool(irregexp, true, "new regular expression code")
DEFINE_bool(trace_regexps, false, "trace Irregexp execution")
-DEFINE_bool(irregexp_native, false, "use native code Irregexp implementation (IA32 only)")
-DEFINE_bool(disable_jscre, false, "abort if JSCRE is used. Only useful with --irregexp")
-DEFINE_bool(attempt_multiline_irregexp, false, "attempt to use Irregexp for multiline regexps")
+DEFINE_bool(irregexp_native, true, "use native code Irregexp implementation (IA32 only)")
// Testing flags test/cctest/test-{flags,api,serialization}.cc
DEFINE_bool(testing_bool_flag, true, "testing_bool_flag")
diff --git a/src/handles-inl.h b/src/handles-inl.h
index 0f804d7..e5899e3 100644
--- a/src/handles-inl.h
+++ b/src/handles-inl.h
@@ -29,6 +29,7 @@
#ifndef V8_HANDLES_INL_H_
#define V8_HANDLES_INL_H_
+#include "apiutils.h"
#include "handles.h"
#include "api.h"
@@ -51,8 +52,8 @@
#ifdef DEBUG
inline NoHandleAllocation::NoHandleAllocation() {
- ImplementationUtilities::HandleScopeData* current =
- ImplementationUtilities::CurrentHandleScope();
+ v8::ImplementationUtilities::HandleScopeData* current =
+ v8::ImplementationUtilities::CurrentHandleScope();
extensions_ = current->extensions;
// Shrink the current handle scope to make it impossible to do
// handle allocations without an explicit handle scope.
@@ -64,7 +65,7 @@
inline NoHandleAllocation::~NoHandleAllocation() {
// Restore state in current handle scope to re-enable handle
// allocations.
- ImplementationUtilities::CurrentHandleScope()->extensions = extensions_;
+ v8::ImplementationUtilities::CurrentHandleScope()->extensions = extensions_;
}
#endif
diff --git a/src/handles.cc b/src/handles.cc
index 58ba472..3f198c8 100644
--- a/src/handles.cc
+++ b/src/handles.cc
@@ -40,6 +40,74 @@
namespace v8 { namespace internal {
+v8::ImplementationUtilities::HandleScopeData HandleScope::current_ =
+ { -1, NULL, NULL };
+
+
+int HandleScope::NumberOfHandles() {
+ int n = HandleScopeImplementer::instance()->Blocks()->length();
+ if (n == 0) return 0;
+ return ((n - 1) * kHandleBlockSize) +
+ (current_.next - HandleScopeImplementer::instance()->Blocks()->last());
+}
+
+
+void** HandleScope::CreateHandle(void* value) {
+ void** result = current_.next;
+ if (result == current_.limit) {
+ // Make sure there's at least one scope on the stack and that the
+ // top of the scope stack isn't a barrier.
+ if (current_.extensions < 0) {
+ Utils::ReportApiFailure("v8::HandleScope::CreateHandle()",
+ "Cannot create a handle without a HandleScope");
+ return NULL;
+ }
+ HandleScopeImplementer* impl = HandleScopeImplementer::instance();
+ // If there's more room in the last block, we use that. This is used
+ // for fast creation of scopes after scope barriers.
+ if (!impl->Blocks()->is_empty()) {
+ void** limit = &impl->Blocks()->last()[kHandleBlockSize];
+ if (current_.limit != limit) {
+ current_.limit = limit;
+ }
+ }
+
+ // If we still haven't found a slot for the handle, we extend the
+ // current handle scope by allocating a new handle block.
+ if (result == current_.limit) {
+ // If there's a spare block, use it for growing the current scope.
+ result = impl->GetSpareOrNewBlock();
+ // Add the extension to the global list of blocks, but count the
+ // extension as part of the current scope.
+ impl->Blocks()->Add(result);
+ current_.extensions++;
+ current_.limit = &result[kHandleBlockSize];
+ }
+ }
+
+ // Update the current next field, set the value in the created
+ // handle, and return the result.
+ ASSERT(result < current_.limit);
+ current_.next = result + 1;
+ *result = value;
+ return result;
+}
+
+
+void HandleScope::DeleteExtensions() {
+ ASSERT(current_.extensions != 0);
+ HandleScopeImplementer::instance()->DeleteExtensions(current_.extensions);
+}
+
+
+void HandleScope::ZapRange(void** start, void** end) {
+ if (start == NULL) return;
+ for (void** p = start; p < end; p++) {
+ *p = reinterpret_cast<void*>(v8::internal::kHandleZapValue);
+ }
+}
+
+
Handle<FixedArray> AddKeysFromJSArray(Handle<FixedArray> content,
Handle<JSArray> array) {
CALL_HEAP_FUNCTION(content->AddKeysFromJSArray(*array), FixedArray);
@@ -120,7 +188,7 @@
void FlattenString(Handle<String> string) {
StringShape shape(*string);
if (string->IsFlat(shape)) return;
- CALL_HEAP_FUNCTION_VOID(string->Flatten(shape));
+ CALL_HEAP_FUNCTION_VOID(string->TryFlatten(shape));
ASSERT(string->IsFlat(StringShape(*string)));
}
diff --git a/src/handles.h b/src/handles.h
index ec0614b..dd453a7 100644
--- a/src/handles.h
+++ b/src/handles.h
@@ -28,12 +28,14 @@
#ifndef V8_HANDLES_H_
#define V8_HANDLES_H_
+#include "apiutils.h"
+
namespace v8 { namespace internal {
// ----------------------------------------------------------------------------
// A Handle provides a reference to an object that survives relocation by
// the garbage collector.
-// Handles are only valid withing a HandleScope.
+// Handles are only valid within a HandleScope.
// When a handle is created for an object a cell is allocated in the heap.
template<class T>
@@ -83,13 +85,82 @@
// Closes the given scope, but lets this handle escape. See
// implementation in api.h.
- inline Handle<T> EscapeFrom(HandleScope* scope);
+ inline Handle<T> EscapeFrom(v8::HandleScope* scope);
private:
T** location_;
};
+// A stack-allocated class that governs a number of local handles.
+// After a handle scope has been created, all local handles will be
+// allocated within that handle scope until either the handle scope is
+// deleted or another handle scope is created. If there is already a
+// handle scope and a new one is created, all allocations will take
+// place in the new handle scope until it is deleted. After that,
+// new handles will again be allocated in the original handle scope.
+//
+// After the handle scope of a local handle has been deleted the
+// garbage collector will no longer track the object stored in the
+// handle and may deallocate it. The behavior of accessing a handle
+// for which the handle scope has been deleted is undefined.
+class HandleScope {
+ public:
+ HandleScope() : previous_(current_) {
+ current_.extensions = 0;
+ }
+
+ ~HandleScope() {
+ Leave(&previous_);
+ }
+
+ // Counts the number of allocated handles.
+ static int NumberOfHandles();
+
+ // Creates a new handle with the given value.
+ static void** CreateHandle(void* value);
+
+ private:
+ // Prevent heap allocation or illegal handle scopes.
+ HandleScope(const HandleScope&);
+ void operator=(const HandleScope&);
+ void* operator new(size_t size);
+ void operator delete(void* size_t);
+
+ static v8::ImplementationUtilities::HandleScopeData current_;
+ const v8::ImplementationUtilities::HandleScopeData previous_;
+
+ // Pushes a fresh handle scope to be used when allocating new handles.
+ static void Enter(
+ v8::ImplementationUtilities::HandleScopeData* previous) {
+ *previous = current_;
+ current_.extensions = 0;
+ }
+
+ // Re-establishes the previous scope state. Should be called only
+ // once, and only for the current scope.
+ static void Leave(
+ const v8::ImplementationUtilities::HandleScopeData* previous) {
+ if (current_.extensions > 0) {
+ DeleteExtensions();
+ }
+ current_ = *previous;
+#ifdef DEBUG
+ ZapRange(current_.next, current_.limit);
+#endif
+ }
+
+ // Deallocates any extensions used by the current scope.
+ static void DeleteExtensions();
+
+ // Zaps the handles in the half-open interval [start, end).
+ static void ZapRange(void** start, void** end);
+
+ friend class v8::HandleScope;
+ friend class v8::ImplementationUtilities;
+};
+
+
// ----------------------------------------------------------------------------
// Handle operations.
// They might invoke garbage collection. The result is an handle to
diff --git a/src/heap.cc b/src/heap.cc
index f8caa49..b0a2db4 100644
--- a/src/heap.cc
+++ b/src/heap.cc
@@ -1724,9 +1724,12 @@
Object* Heap::AllocateFunctionPrototype(JSFunction* function) {
- // Allocate the prototype.
- Object* prototype =
- AllocateJSObject(Top::context()->global_context()->object_function());
+ // Allocate the prototype. Make sure to use the object function
+ // from the function's context, since the function can be from a
+ // different context.
+ JSFunction* object_function =
+ function->context()->global_context()->object_function();
+ Object* prototype = AllocateJSObject(object_function);
if (prototype->IsFailure()) return prototype;
// When creating the prototype for the function we must set its
// constructor to the function.
diff --git a/src/interpreter-irregexp.cc b/src/interpreter-irregexp.cc
index 8ffc040..c6ccda1 100644
--- a/src/interpreter-irregexp.cc
+++ b/src/interpreter-irregexp.cc
@@ -97,7 +97,7 @@
current_char,
printable ? current_char : '.',
bytecode_name);
- for (int i = 1; i < bytecode_length; i++) {
+ for (int i = 0; i < bytecode_length; i++) {
printf(", %02x", pc[i]);
}
printf(" ");
@@ -129,6 +129,17 @@
#endif
+static int32_t Load32Aligned(const byte* pc) {
+ ASSERT((reinterpret_cast<int>(pc) & 3) == 0);
+ return *reinterpret_cast<const int32_t *>(pc);
+}
+
+
+static int32_t Load16Aligned(const byte* pc) {
+ ASSERT((reinterpret_cast<int>(pc) & 1) == 0);
+ return *reinterpret_cast<const uint16_t *>(pc);
+}
+
template <typename Char>
static bool RawMatch(const byte* code_base,
@@ -147,7 +158,8 @@
}
#endif
while (true) {
- switch (*pc) {
+ int32_t insn = Load32Aligned(pc);
+ switch (insn & BYTECODE_MASK) {
BYTECODE(BREAK)
UNREACHABLE();
return false;
@@ -155,45 +167,45 @@
if (--backtrack_stack_space < 0) {
return false; // No match on backtrack stack overflow.
}
- *backtrack_sp++ = current + Load32(pc + 1);
+ *backtrack_sp++ = current;
pc += BC_PUSH_CP_LENGTH;
break;
BYTECODE(PUSH_BT)
if (--backtrack_stack_space < 0) {
return false; // No match on backtrack stack overflow.
}
- *backtrack_sp++ = Load32(pc + 1);
+ *backtrack_sp++ = Load32Aligned(pc + 4);
pc += BC_PUSH_BT_LENGTH;
break;
BYTECODE(PUSH_REGISTER)
if (--backtrack_stack_space < 0) {
return false; // No match on backtrack stack overflow.
}
- *backtrack_sp++ = registers[pc[1]];
+ *backtrack_sp++ = registers[insn >> BYTECODE_SHIFT];
pc += BC_PUSH_REGISTER_LENGTH;
break;
BYTECODE(SET_REGISTER)
- registers[pc[1]] = Load32(pc + 2);
+ registers[insn >> BYTECODE_SHIFT] = Load32Aligned(pc + 4);
pc += BC_SET_REGISTER_LENGTH;
break;
BYTECODE(ADVANCE_REGISTER)
- registers[pc[1]] += Load32(pc + 2);
+ registers[insn >> BYTECODE_SHIFT] += Load32Aligned(pc + 4);
pc += BC_ADVANCE_REGISTER_LENGTH;
break;
BYTECODE(SET_REGISTER_TO_CP)
- registers[pc[1]] = current + Load32(pc + 2);
+ registers[insn >> BYTECODE_SHIFT] = current + Load32Aligned(pc + 4);
pc += BC_SET_REGISTER_TO_CP_LENGTH;
break;
BYTECODE(SET_CP_TO_REGISTER)
- current = registers[pc[1]];
+ current = registers[insn >> BYTECODE_SHIFT];
pc += BC_SET_CP_TO_REGISTER_LENGTH;
break;
BYTECODE(SET_REGISTER_TO_SP)
- registers[pc[1]] = backtrack_sp - backtrack_stack;
+ registers[insn >> BYTECODE_SHIFT] = backtrack_sp - backtrack_stack;
pc += BC_SET_REGISTER_TO_SP_LENGTH;
break;
BYTECODE(SET_SP_TO_REGISTER)
- backtrack_sp = backtrack_stack + registers[pc[1]];
+ backtrack_sp = backtrack_stack + registers[insn >> BYTECODE_SHIFT];
backtrack_stack_space = kBacktrackStackSize -
(backtrack_sp - backtrack_stack);
pc += BC_SET_SP_TO_REGISTER_LENGTH;
@@ -212,7 +224,7 @@
BYTECODE(POP_REGISTER)
backtrack_stack_space++;
--backtrack_sp;
- registers[pc[1]] = *backtrack_sp;
+ registers[insn >> BYTECODE_SHIFT] = *backtrack_sp;
pc += BC_POP_REGISTER_LENGTH;
break;
BYTECODE(FAIL)
@@ -220,25 +232,25 @@
BYTECODE(SUCCEED)
return true;
BYTECODE(ADVANCE_CP)
- current += Load32(pc + 1);
+ current += insn >> BYTECODE_SHIFT;
pc += BC_ADVANCE_CP_LENGTH;
break;
BYTECODE(GOTO)
- pc = code_base + Load32(pc + 1);
+ pc = code_base + Load32Aligned(pc + 4);
break;
BYTECODE(CHECK_GREEDY)
if (current == backtrack_sp[-1]) {
backtrack_sp--;
backtrack_stack_space++;
- pc = code_base + Load32(pc + 1);
+ pc = code_base + Load32Aligned(pc + 4);
} else {
pc += BC_CHECK_GREEDY_LENGTH;
}
break;
BYTECODE(LOAD_CURRENT_CHAR) {
- int pos = current + Load32(pc + 1);
+ int pos = current + (insn >> BYTECODE_SHIFT);
if (pos >= subject.length()) {
- pc = code_base + Load32(pc + 5);
+ pc = code_base + Load32Aligned(pc + 4);
} else {
current_char = subject[pos];
pc += BC_LOAD_CURRENT_CHAR_LENGTH;
@@ -246,15 +258,15 @@
break;
}
BYTECODE(LOAD_CURRENT_CHAR_UNCHECKED) {
- int pos = current + Load32(pc + 1);
+ int pos = current + (insn >> BYTECODE_SHIFT);
current_char = subject[pos];
pc += BC_LOAD_CURRENT_CHAR_UNCHECKED_LENGTH;
break;
}
BYTECODE(LOAD_2_CURRENT_CHARS) {
- int pos = current + Load32(pc + 1);
+ int pos = current + (insn >> BYTECODE_SHIFT);
if (pos + 2 > subject.length()) {
- pc = code_base + Load32(pc + 5);
+ pc = code_base + Load32Aligned(pc + 4);
} else {
Char next = subject[pos + 1];
current_char =
@@ -264,7 +276,7 @@
break;
}
BYTECODE(LOAD_2_CURRENT_CHARS_UNCHECKED) {
- int pos = current + Load32(pc + 1);
+ int pos = current + (insn >> BYTECODE_SHIFT);
Char next = subject[pos + 1];
current_char = (subject[pos] | (next << (kBitsPerByte * sizeof(Char))));
pc += BC_LOAD_2_CURRENT_CHARS_UNCHECKED_LENGTH;
@@ -272,9 +284,9 @@
}
BYTECODE(LOAD_4_CURRENT_CHARS) {
ASSERT(sizeof(Char) == 1);
- int pos = current + Load32(pc + 1);
+ int pos = current + (insn >> BYTECODE_SHIFT);
if (pos + 4 > subject.length()) {
- pc = code_base + Load32(pc + 5);
+ pc = code_base + Load32Aligned(pc + 4);
} else {
Char next1 = subject[pos + 1];
Char next2 = subject[pos + 2];
@@ -289,7 +301,7 @@
}
BYTECODE(LOAD_4_CURRENT_CHARS_UNCHECKED) {
ASSERT(sizeof(Char) == 1);
- int pos = current + Load32(pc + 1);
+ int pos = current + (insn >> BYTECODE_SHIFT);
Char next1 = subject[pos + 1];
Char next2 = subject[pos + 2];
Char next3 = subject[pos + 3];
@@ -300,100 +312,136 @@
pc += BC_LOAD_4_CURRENT_CHARS_UNCHECKED_LENGTH;
break;
}
- BYTECODE(CHECK_CHAR) {
- uint32_t c = Load32(pc + 1);
+ BYTECODE(CHECK_4_CHARS) {
+ uint32_t c = Load32Aligned(pc + 4);
if (c == current_char) {
- pc = code_base + Load32(pc + 5);
+ pc = code_base + Load32Aligned(pc + 8);
+ } else {
+ pc += BC_CHECK_4_CHARS_LENGTH;
+ }
+ break;
+ }
+ BYTECODE(CHECK_CHAR) {
+ uint32_t c = (insn >> BYTECODE_SHIFT);
+ if (c == current_char) {
+ pc = code_base + Load32Aligned(pc + 4);
} else {
pc += BC_CHECK_CHAR_LENGTH;
}
break;
}
- BYTECODE(CHECK_NOT_CHAR) {
- uint32_t c = Load32(pc + 1);
+ BYTECODE(CHECK_NOT_4_CHARS) {
+ uint32_t c = Load32Aligned(pc + 4);
if (c != current_char) {
- pc = code_base + Load32(pc + 5);
+ pc = code_base + Load32Aligned(pc + 8);
+ } else {
+ pc += BC_CHECK_NOT_4_CHARS_LENGTH;
+ }
+ break;
+ }
+ BYTECODE(CHECK_NOT_CHAR) {
+ uint32_t c = (insn >> BYTECODE_SHIFT);
+ if (c != current_char) {
+ pc = code_base + Load32Aligned(pc + 4);
} else {
pc += BC_CHECK_NOT_CHAR_LENGTH;
}
break;
}
+ BYTECODE(AND_CHECK_4_CHARS) {
+ uint32_t c = Load32Aligned(pc + 4);
+ if (c == (current_char & Load32Aligned(pc + 8))) {
+ pc = code_base + Load32Aligned(pc + 12);
+ } else {
+ pc += BC_AND_CHECK_4_CHARS_LENGTH;
+ }
+ break;
+ }
BYTECODE(AND_CHECK_CHAR) {
- uint32_t c = Load32(pc + 1);
- if (c == (current_char & Load32(pc + 5))) {
- pc = code_base + Load32(pc + 9);
+ uint32_t c = (insn >> BYTECODE_SHIFT);
+ if (c == (current_char & Load32Aligned(pc + 4))) {
+ pc = code_base + Load32Aligned(pc + 8);
} else {
pc += BC_AND_CHECK_CHAR_LENGTH;
}
break;
}
+ BYTECODE(AND_CHECK_NOT_4_CHARS) {
+ uint32_t c = Load32Aligned(pc + 4);
+ if (c != (current_char & Load32Aligned(pc + 8))) {
+ pc = code_base + Load32Aligned(pc + 12);
+ } else {
+ pc += BC_AND_CHECK_NOT_4_CHARS_LENGTH;
+ }
+ break;
+ }
BYTECODE(AND_CHECK_NOT_CHAR) {
- uint32_t c = Load32(pc + 1);
- if (c != (current_char & Load32(pc + 5))) {
- pc = code_base + Load32(pc + 9);
+ uint32_t c = (insn >> BYTECODE_SHIFT);
+ if (c != (current_char & Load32Aligned(pc + 4))) {
+ pc = code_base + Load32Aligned(pc + 8);
} else {
pc += BC_AND_CHECK_NOT_CHAR_LENGTH;
}
break;
}
BYTECODE(MINUS_AND_CHECK_NOT_CHAR) {
- uint32_t c = Load16(pc + 1);
- uint32_t minus = Load16(pc + 3);
- uint32_t mask = Load16(pc + 5);
+ uint32_t c = (insn >> BYTECODE_SHIFT);
+ uint32_t minus = Load16Aligned(pc + 4);
+ uint32_t mask = Load16Aligned(pc + 6);
if (c != ((current_char - minus) & mask)) {
- pc = code_base + Load32(pc + 7);
+ pc = code_base + Load32Aligned(pc + 8);
} else {
pc += BC_MINUS_AND_CHECK_NOT_CHAR_LENGTH;
}
break;
}
BYTECODE(CHECK_LT) {
- uint32_t limit = Load16(pc + 1);
+ uint32_t limit = (insn >> BYTECODE_SHIFT);
if (current_char < limit) {
- pc = code_base + Load32(pc + 3);
+ pc = code_base + Load32Aligned(pc + 4);
} else {
pc += BC_CHECK_LT_LENGTH;
}
break;
}
BYTECODE(CHECK_GT) {
- uint32_t limit = Load16(pc + 1);
+ uint32_t limit = (insn >> BYTECODE_SHIFT);
if (current_char > limit) {
- pc = code_base + Load32(pc + 3);
+ pc = code_base + Load32Aligned(pc + 4);
} else {
pc += BC_CHECK_GT_LENGTH;
}
break;
}
BYTECODE(CHECK_REGISTER_LT)
- if (registers[pc[1]] < Load16(pc + 2)) {
- pc = code_base + Load32(pc + 4);
+ if (registers[insn >> BYTECODE_SHIFT] < Load32Aligned(pc + 4)) {
+ pc = code_base + Load32Aligned(pc + 8);
} else {
pc += BC_CHECK_REGISTER_LT_LENGTH;
}
break;
BYTECODE(CHECK_REGISTER_GE)
- if (registers[pc[1]] >= Load16(pc + 2)) {
- pc = code_base + Load32(pc + 4);
+ if (registers[insn >> BYTECODE_SHIFT] >= Load32Aligned(pc + 4)) {
+ pc = code_base + Load32Aligned(pc + 8);
} else {
pc += BC_CHECK_REGISTER_GE_LENGTH;
}
break;
BYTECODE(CHECK_REGISTER_EQ_POS)
- if (registers[pc[1]] == current) {
- pc = code_base + Load32(pc + 2);
+ if (registers[insn >> BYTECODE_SHIFT] == current) {
+ pc = code_base + Load32Aligned(pc + 4);
} else {
pc += BC_CHECK_REGISTER_EQ_POS_LENGTH;
}
break;
BYTECODE(LOOKUP_MAP1) {
// Look up character in a bitmap. If we find a 0, then jump to the
- // location at pc + 7. Otherwise fall through!
- int index = current_char - Load16(pc + 1);
- byte map = code_base[Load32(pc + 3) + (index >> 3)];
+ // location at pc + 8. Otherwise fall through!
+ int index = current_char - (insn >> BYTECODE_SHIFT);
+ byte map = code_base[Load32Aligned(pc + 4) + (index >> 3)];
map = ((map >> (index & 7)) & 1);
if (map == 0) {
- pc = code_base + Load32(pc + 7);
+ pc = code_base + Load32Aligned(pc + 8);
} else {
pc += BC_LOOKUP_MAP1_LENGTH;
}
@@ -401,22 +449,22 @@
}
BYTECODE(LOOKUP_MAP2) {
// Look up character in a half-nibble map. If we find 00, then jump to
- // the location at pc + 7. If we find 01 then jump to location at
+ // the location at pc + 8. If we find 01 then jump to location at
// pc + 11, etc.
- int index = (current_char - Load16(pc + 1)) << 1;
- byte map = code_base[Load32(pc + 3) + (index >> 3)];
+ int index = (current_char - (insn >> BYTECODE_SHIFT)) << 1;
+ byte map = code_base[Load32Aligned(pc + 3) + (index >> 3)];
map = ((map >> (index & 7)) & 3);
if (map < 2) {
if (map == 0) {
- pc = code_base + Load32(pc + 7);
+ pc = code_base + Load32Aligned(pc + 8);
} else {
- pc = code_base + Load32(pc + 11);
+ pc = code_base + Load32Aligned(pc + 12);
}
} else {
if (map == 2) {
- pc = code_base + Load32(pc + 15);
+ pc = code_base + Load32Aligned(pc + 16);
} else {
- pc = code_base + Load32(pc + 19);
+ pc = code_base + Load32Aligned(pc + 20);
}
}
break;
@@ -424,43 +472,44 @@
BYTECODE(LOOKUP_MAP8) {
// Look up character in a byte map. Use the byte as an index into a
// table that follows this instruction immediately.
- int index = current_char - Load16(pc + 1);
- byte map = code_base[Load32(pc + 3) + index];
- const byte* new_pc = code_base + Load32(pc + 7) + (map << 2);
- pc = code_base + Load32(new_pc);
+ int index = current_char - (insn >> BYTECODE_SHIFT);
+ byte map = code_base[Load32Aligned(pc + 4) + index];
+ const byte* new_pc = code_base + Load32Aligned(pc + 8) + (map << 2);
+ pc = code_base + Load32Aligned(new_pc);
break;
}
BYTECODE(LOOKUP_HI_MAP8) {
// Look up high byte of this character in a byte map. Use the byte as
// an index into a table that follows this instruction immediately.
- int index = (current_char >> 8) - pc[1];
- byte map = code_base[Load32(pc + 2) + index];
- const byte* new_pc = code_base + Load32(pc + 6) + (map << 2);
- pc = code_base + Load32(new_pc);
+ int index = (current_char >> 8) - (insn >> BYTECODE_SHIFT);
+ byte map = code_base[Load32Aligned(pc + 4) + index];
+ const byte* new_pc = code_base + Load32Aligned(pc + 8) + (map << 2);
+ pc = code_base + Load32Aligned(new_pc);
break;
}
BYTECODE(CHECK_NOT_REGS_EQUAL)
- if (registers[pc[1]] == registers[pc[2]]) {
+ if (registers[insn >> BYTECODE_SHIFT] ==
+ registers[Load32Aligned(pc + 4)]) {
pc += BC_CHECK_NOT_REGS_EQUAL_LENGTH;
} else {
- pc = code_base + Load32(pc + 3);
+ pc = code_base + Load32Aligned(pc + 8);
}
break;
BYTECODE(CHECK_NOT_BACK_REF) {
- int from = registers[pc[1]];
- int len = registers[pc[1] + 1] - from;
+ int from = registers[insn >> BYTECODE_SHIFT];
+ int len = registers[(insn >> BYTECODE_SHIFT) + 1] - from;
if (from < 0 || len <= 0) {
pc += BC_CHECK_NOT_BACK_REF_LENGTH;
break;
}
if (current + len > subject.length()) {
- pc = code_base + Load32(pc + 2);
+ pc = code_base + Load32Aligned(pc + 4);
break;
} else {
int i;
for (i = 0; i < len; i++) {
if (subject[from + i] != subject[current + i]) {
- pc = code_base + Load32(pc + 2);
+ pc = code_base + Load32Aligned(pc + 4);
break;
}
}
@@ -471,30 +520,37 @@
break;
}
BYTECODE(CHECK_NOT_BACK_REF_NO_CASE) {
- int from = registers[pc[1]];
- int len = registers[pc[1] + 1] - from;
+ int from = registers[insn >> BYTECODE_SHIFT];
+ int len = registers[(insn >> BYTECODE_SHIFT) + 1] - from;
if (from < 0 || len <= 0) {
pc += BC_CHECK_NOT_BACK_REF_NO_CASE_LENGTH;
break;
}
if (current + len > subject.length()) {
- pc = code_base + Load32(pc + 2);
+ pc = code_base + Load32Aligned(pc + 4);
break;
} else {
if (BackRefMatchesNoCase(from, current, len, subject)) {
current += len;
pc += BC_CHECK_NOT_BACK_REF_NO_CASE_LENGTH;
} else {
- pc = code_base + Load32(pc + 2);
+ pc = code_base + Load32Aligned(pc + 4);
}
}
break;
}
+ BYTECODE(CHECK_AT_START)
+ if (current == 0) {
+ pc = code_base + Load32Aligned(pc + 4);
+ } else {
+ pc += BC_CHECK_AT_START_LENGTH;
+ }
+ break;
BYTECODE(CHECK_NOT_AT_START)
if (current == 0) {
pc += BC_CHECK_NOT_AT_START_LENGTH;
} else {
- pc = code_base + Load32(pc + 1);
+ pc = code_base + Load32Aligned(pc + 4);
}
break;
default:
diff --git a/src/jsregexp.cc b/src/jsregexp.cc
index aa11a69..fef0b0d 100644
--- a/src/jsregexp.cc
+++ b/src/jsregexp.cc
@@ -298,21 +298,10 @@
return AtomExec(regexp, subject, index);
case JSRegExp::IRREGEXP: {
Handle<Object> result = IrregexpExec(regexp, subject, index);
- if (!result.is_null() || Top::has_pending_exception()) {
- return result;
- }
- // We couldn't handle the regexp using Irregexp, so fall back
- // on JSCRE.
- // Reset the JSRegExp to use JSCRE.
- JscrePrepare(regexp,
- Handle<String>(regexp->Pattern()),
- regexp->GetFlags());
- // Fall-through to JSCRE.
+ if (result.is_null()) ASSERT(Top::has_pending_exception());
+ return result;
}
case JSRegExp::JSCRE:
- if (FLAG_disable_jscre) {
- UNIMPLEMENTED();
- }
return JscreExec(regexp, subject, index);
default:
UNREACHABLE();
@@ -328,22 +317,10 @@
return AtomExecGlobal(regexp, subject);
case JSRegExp::IRREGEXP: {
Handle<Object> result = IrregexpExecGlobal(regexp, subject);
- if (!result.is_null() || Top::has_pending_exception()) {
- return result;
- }
- // Empty handle as result but no exception thrown means that
- // the regexp contains features not yet handled by the irregexp
- // compiler.
- // We have to fall back on JSCRE. Reset the JSRegExp to use JSCRE.
- JscrePrepare(regexp,
- Handle<String>(regexp->Pattern()),
- regexp->GetFlags());
- // Fall-through to JSCRE.
+ if (result.is_null()) ASSERT(Top::has_pending_exception());
+ return result;
}
case JSRegExp::JSCRE:
- if (FLAG_disable_jscre) {
- UNIMPLEMENTED();
- }
return JscreExecGlobal(regexp, subject);
default:
UNREACHABLE();
@@ -460,7 +437,7 @@
&JSREMalloc,
&JSREFree);
if (*code == NULL && (malloc_failure->IsRetryAfterGC() ||
- malloc_failure->IsOutOfMemoryFailure())) {
+ malloc_failure->IsOutOfMemoryFailure())) {
return malloc_failure;
} else {
// It doesn't matter which object we return here, we just need to return
@@ -697,7 +674,7 @@
Handle<String> pattern(re->Pattern());
StringShape shape(*pattern);
if (!pattern->IsFlat(shape)) {
- pattern->Flatten(shape);
+ FlattenString(pattern);
}
RegExpCompileData compile_data;
@@ -824,7 +801,7 @@
Handle<Object> matches;
if (!subject->IsFlat(shape)) {
- subject->Flatten(shape);
+ FlattenString(subject);
}
while (true) {
@@ -920,6 +897,7 @@
offsets_vector,
previous_index == 0);
} else { // Sequential string
+ ASSERT(StringShape(*subject).IsSequential());
Address char_address =
is_ascii ? SeqAsciiString::cast(*subject)->GetCharsAddress()
: SeqTwoByteString::cast(*subject)->GetCharsAddress();
@@ -1197,7 +1175,13 @@
public:
RegExpCompiler(int capture_count, bool ignore_case, bool is_ascii);
- int AllocateRegister() { return next_register_++; }
+ int AllocateRegister() {
+ if (next_register_ >= RegExpMacroAssembler::kMaxRegister) {
+ reg_exp_too_big_ = true;
+ return next_register_;
+ }
+ return next_register_++;
+ }
Handle<FixedArray> Assemble(RegExpMacroAssembler* assembler,
RegExpNode* start,
@@ -1218,6 +1202,8 @@
inline void IncrementRecursionDepth() { recursion_depth_++; }
inline void DecrementRecursionDepth() { recursion_depth_--; }
+ void SetRegExpTooBig() { reg_exp_too_big_ = true; }
+
inline bool ignore_case() { return ignore_case_; }
inline bool ascii() { return ascii_; }
@@ -1230,6 +1216,7 @@
RegExpMacroAssembler* macro_assembler_;
bool ignore_case_;
bool ascii_;
+ bool reg_exp_too_big_;
};
@@ -1244,6 +1231,18 @@
};
+static Handle<FixedArray> IrregexpRegExpTooBig(Handle<String> pattern) {
+ Handle<JSArray> array = Factory::NewJSArray(2);
+ SetElement(array, 0, pattern);
+ const char* message = "RegExp too big";
+ SetElement(array, 1, Factory::NewStringFromUtf8(CStrVector(message)));
+ Handle<Object> regexp_err =
+ Factory::NewSyntaxError("malformed_regexp", array);
+ Top::Throw(*regexp_err);
+ return Handle<FixedArray>();
+}
+
+
// Attempts to compile the regexp using an Irregexp code generator. Returns
// a fixed array or a null handle depending on whether it succeeded.
RegExpCompiler::RegExpCompiler(int capture_count, bool ignore_case, bool ascii)
@@ -1251,8 +1250,10 @@
work_list_(NULL),
recursion_depth_(0),
ignore_case_(ignore_case),
- ascii_(ascii) {
+ ascii_(ascii),
+ reg_exp_too_big_(false) {
accept_ = new EndNode(EndNode::ACCEPT);
+ ASSERT(next_register_ - 1 <= RegExpMacroAssembler::kMaxRegister);
}
@@ -1272,17 +1273,13 @@
Label fail;
macro_assembler->PushBacktrack(&fail);
Trace new_trace;
- if (!start->Emit(this, &new_trace)) {
- fail.Unuse();
- return Handle<FixedArray>::null();
- }
+ start->Emit(this, &new_trace);
macro_assembler_->Bind(&fail);
macro_assembler_->Fail();
while (!work_list.is_empty()) {
- if (!work_list.RemoveLast()->Emit(this, &new_trace)) {
- return Handle<FixedArray>::null();
- }
+ work_list.RemoveLast()->Emit(this, &new_trace);
}
+ if (reg_exp_too_big_) return IrregexpRegExpTooBig(pattern);
Handle<FixedArray> array =
Factory::NewFixedArray(RegExpImpl::kIrregexpDataLength);
array->set(RegExpImpl::kIrregexpImplementationIndex,
@@ -1302,6 +1299,7 @@
return array;
}
+
bool Trace::DeferredAction::Mentions(int that) {
if (type() == ActionNode::CLEAR_CAPTURES) {
Interval range = static_cast<DeferredClearCaptures*>(this)->range();
@@ -1360,41 +1358,44 @@
}
-void Trace::PushAffectedRegisters(RegExpMacroAssembler* assembler,
- int max_register,
- OutSet& affected_registers) {
- // Stay safe and check every half times the limit.
- // (Round up in case the limit is 1).
- int push_limit = (assembler->stack_limit_slack() + 1) / 2;
- for (int reg = 0, pushes = 0; reg <= max_register; reg++) {
- if (affected_registers.Get(reg)) {
- pushes++;
- RegExpMacroAssembler::StackCheckFlag check_stack_limit =
- (pushes % push_limit) == 0 ?
- RegExpMacroAssembler::kCheckStackLimit :
- RegExpMacroAssembler::kNoStackLimitCheck;
- assembler->PushRegister(reg, check_stack_limit);
- }
- }
-}
-
-
void Trace::RestoreAffectedRegisters(RegExpMacroAssembler* assembler,
int max_register,
- OutSet& affected_registers) {
+ OutSet& registers_to_pop,
+ OutSet& registers_to_clear) {
for (int reg = max_register; reg >= 0; reg--) {
- if (affected_registers.Get(reg)) assembler->PopRegister(reg);
+ if (registers_to_pop.Get(reg)) assembler->PopRegister(reg);
+ else if (registers_to_clear.Get(reg)) {
+ int clear_to = reg;
+ while (reg > 0 && registers_to_clear.Get(reg - 1)) {
+ reg--;
+ }
+ assembler->ClearRegisters(reg, clear_to);
+ }
}
}
void Trace::PerformDeferredActions(RegExpMacroAssembler* assembler,
int max_register,
- OutSet& affected_registers) {
+ OutSet& affected_registers,
+ OutSet* registers_to_pop,
+ OutSet* registers_to_clear) {
+ // The "+1" is to avoid a push_limit of zero if stack_limit_slack() is 1.
+ const int push_limit = (assembler->stack_limit_slack() + 1) / 2;
+
for (int reg = 0; reg <= max_register; reg++) {
if (!affected_registers.Get(reg)) {
continue;
}
+ // Count pushes performed to force a stack limit check occasionally.
+ int pushes = 0;
+
+ // The chronologically first deferred action in the trace
+ // is used to infer the action needed to restore a register
+ // to its previous state (or not, if it's safe to ignore it).
+ enum DeferredActionUndoType { IGNORE, RESTORE, CLEAR };
+ DeferredActionUndoType undo_action = IGNORE;
+
int value = 0;
bool absolute = false;
bool clear = false;
@@ -1409,8 +1410,16 @@
case ActionNode::SET_REGISTER: {
Trace::DeferredSetRegister* psr =
static_cast<Trace::DeferredSetRegister*>(action);
- value += psr->value();
- absolute = true;
+ if (!absolute) {
+ value += psr->value();
+ absolute = true;
+ }
+ // SET_REGISTER is currently only used for newly introduced loop
+ // counters. They can have a significant previous value if they
+ // occour in a loop. TODO(lrn): Propagate this information, so
+ // we can set undo_action to IGNORE if we know there is no value to
+ // restore.
+ undo_action = RESTORE;
ASSERT_EQ(store_position, -1);
ASSERT(!clear);
break;
@@ -1421,6 +1430,7 @@
}
ASSERT_EQ(store_position, -1);
ASSERT(!clear);
+ undo_action = RESTORE;
break;
case ActionNode::STORE_POSITION: {
Trace::DeferredCapture* pc =
@@ -1428,6 +1438,19 @@
if (!clear && store_position == -1) {
store_position = pc->cp_offset();
}
+
+ // For captures we know that stores and clears alternate.
+ // Other register, are never cleared, and if the occur
+ // inside a loop, they might be assigned more than once.
+ if (reg <= 1) {
+ // Registers zero and one, aka "capture zero", is
+ // always set correctly if we succeed. There is no
+ // need to undo a setting on backtrack, because we
+ // will set it again or fail.
+ undo_action = IGNORE;
+ } else {
+ undo_action = pc->is_capture() ? CLEAR : RESTORE;
+ }
ASSERT(!absolute);
ASSERT_EQ(value, 0);
break;
@@ -1436,8 +1459,10 @@
// Since we're scanning in reverse order, if we've already
// set the position we have to ignore historically earlier
// clearing operations.
- if (store_position == -1)
+ if (store_position == -1) {
clear = true;
+ }
+ undo_action = RESTORE;
ASSERT(!absolute);
ASSERT_EQ(value, 0);
break;
@@ -1448,10 +1473,27 @@
}
}
}
+ // Prepare for the undo-action (e.g., push if it's going to be popped).
+ if (undo_action == RESTORE) {
+ pushes++;
+ RegExpMacroAssembler::StackCheckFlag stack_check =
+ RegExpMacroAssembler::kNoStackLimitCheck;
+ if (pushes == push_limit) {
+ stack_check = RegExpMacroAssembler::kCheckStackLimit;
+ pushes = 0;
+ }
+
+ assembler->PushRegister(reg, stack_check);
+ registers_to_pop->Set(reg);
+ } else if (undo_action == CLEAR) {
+ registers_to_clear->Set(reg);
+ }
+ // Perform the chronologically last action (or accumulated increment)
+ // for the register.
if (store_position != -1) {
assembler->WriteCurrentPositionToRegister(reg, store_position);
} else if (clear) {
- assembler->ClearRegister(reg);
+ assembler->ClearRegisters(reg, reg);
} else if (absolute) {
assembler->SetRegister(reg, value);
} else if (value != 0) {
@@ -1464,7 +1506,7 @@
// This is called as we come into a loop choice node and some other tricky
// nodes. It normalizes the state of the code generator to ensure we can
// generate generic code.
-bool Trace::Flush(RegExpCompiler* compiler, RegExpNode* successor) {
+void Trace::Flush(RegExpCompiler* compiler, RegExpNode* successor) {
RegExpMacroAssembler* assembler = compiler->macro_assembler();
ASSERT(actions_ != NULL ||
@@ -1481,14 +1523,21 @@
if (cp_offset_ != 0) assembler->AdvanceCurrentPosition(cp_offset_);
// Create a new trivial state and generate the node with that.
Trace new_state;
- return successor->Emit(compiler, &new_state);
+ successor->Emit(compiler, &new_state);
+ return;
}
// Generate deferred actions here along with code to undo them again.
OutSet affected_registers;
+
int max_register = FindAffectedRegisters(&affected_registers);
- PushAffectedRegisters(assembler, max_register, affected_registers);
- PerformDeferredActions(assembler, max_register, affected_registers);
+ OutSet registers_to_pop;
+ OutSet registers_to_clear;
+ PerformDeferredActions(assembler,
+ max_register,
+ affected_registers,
+ ®isters_to_pop,
+ ®isters_to_clear);
if (backtrack() != NULL) {
// Here we have a concrete backtrack location. These are set up by choice
// nodes and so they indicate that we have a deferred save of the current
@@ -1503,58 +1552,56 @@
Label undo;
assembler->PushBacktrack(&undo);
Trace new_state;
- bool ok = successor->Emit(compiler, &new_state);
+ successor->Emit(compiler, &new_state);
// On backtrack we need to restore state.
assembler->Bind(&undo);
- if (!ok) return false;
if (backtrack() != NULL) {
assembler->PopCurrentPosition();
}
- RestoreAffectedRegisters(assembler, max_register, affected_registers);
+ RestoreAffectedRegisters(assembler,
+ max_register,
+ registers_to_pop,
+ registers_to_clear);
if (backtrack() == NULL) {
assembler->Backtrack();
} else {
assembler->GoTo(backtrack());
}
-
- return true;
}
-void EndNode::EmitInfoChecks(RegExpMacroAssembler* assembler, Trace* trace) {
- if (info()->at_end) {
- Label succeed;
- // LoadCurrentCharacter will go to the label if we are at the end of the
- // input string.
- assembler->LoadCurrentCharacter(0, &succeed);
- assembler->GoTo(trace->backtrack());
- assembler->Bind(&succeed);
- }
-}
-
-
-bool NegativeSubmatchSuccess::Emit(RegExpCompiler* compiler, Trace* trace) {
- if (!trace->is_trivial()) {
- return trace->Flush(compiler, this);
- }
+void NegativeSubmatchSuccess::Emit(RegExpCompiler* compiler, Trace* trace) {
RegExpMacroAssembler* assembler = compiler->macro_assembler();
+
+ // Omit flushing the trace. We discard the entire stack frame anyway.
+
if (!label()->is_bound()) {
+ // We are completely independent of the trace, since we ignore it,
+ // so this code can be used as the generic version.
assembler->Bind(label());
}
- EmitInfoChecks(assembler, trace);
+
+ // Throw away everything on the backtrack stack since the start
+ // of the negative submatch and restore the character position.
assembler->ReadCurrentPositionFromRegister(current_position_register_);
assembler->ReadStackPointerFromRegister(stack_pointer_register_);
+ if (clear_capture_count_ > 0) {
+ // Clear any captures that might have been performed during the success
+ // of the body of the negative look-ahead.
+ int clear_capture_end = clear_capture_start_ + clear_capture_count_ - 1;
+ assembler->ClearRegisters(clear_capture_start_, clear_capture_end);
+ }
// Now that we have unwound the stack we find at the top of the stack the
// backtrack that the BeginSubmatch node got.
assembler->Backtrack();
- return true;
}
-bool EndNode::Emit(RegExpCompiler* compiler, Trace* trace) {
+void EndNode::Emit(RegExpCompiler* compiler, Trace* trace) {
if (!trace->is_trivial()) {
- return trace->Flush(compiler, this);
+ trace->Flush(compiler, this);
+ return;
}
RegExpMacroAssembler* assembler = compiler->macro_assembler();
if (!label()->is_bound()) {
@@ -1562,19 +1609,16 @@
}
switch (action_) {
case ACCEPT:
- EmitInfoChecks(assembler, trace);
assembler->Succeed();
- return true;
+ return;
case BACKTRACK:
- ASSERT(!info()->at_end);
assembler->GoTo(trace->backtrack());
- return true;
+ return;
case NEGATIVE_SUBMATCH_SUCCESS:
// This case is handled in a different virtual method.
UNREACHABLE();
}
UNIMPLEMENTED();
- return false;
}
@@ -1602,9 +1646,12 @@
}
-ActionNode* ActionNode::StorePosition(int reg, RegExpNode* on_success) {
+ActionNode* ActionNode::StorePosition(int reg,
+ bool is_capture,
+ RegExpNode* on_success) {
ActionNode* result = new ActionNode(STORE_POSITION, on_success);
result->data_.u_position_register.reg = reg;
+ result->data_.u_position_register.is_capture = is_capture;
return result;
}
@@ -1630,10 +1677,14 @@
ActionNode* ActionNode::PositiveSubmatchSuccess(int stack_reg,
int position_reg,
+ int clear_register_count,
+ int clear_register_from,
RegExpNode* on_success) {
ActionNode* result = new ActionNode(POSITIVE_SUBMATCH_SUCCESS, on_success);
result->data_.u_submatch.stack_pointer_register = stack_reg;
result->data_.u_submatch.current_position_register = position_reg;
+ result->data_.u_submatch.clear_register_count = clear_register_count;
+ result->data_.u_submatch.clear_register_from = clear_register_from;
return result;
}
@@ -1849,6 +1900,9 @@
// ASCII optimizations for us.
macro_assembler->GoTo(on_failure);
}
+ if (check_offset) {
+ macro_assembler->CheckPosition(cp_offset, on_failure);
+ }
return;
}
@@ -1856,10 +1910,8 @@
!cc->is_negated() &&
ranges->at(0).IsEverything(max_char)) {
// This is a common case hit by non-anchored expressions.
- // TODO(erikcorry): We should have a macro assembler instruction that just
- // checks for end of string without loading the character.
if (check_offset) {
- macro_assembler->LoadCurrentCharacter(cp_offset, on_failure);
+ macro_assembler->CheckPosition(cp_offset, on_failure);
}
return;
}
@@ -1935,13 +1987,6 @@
RegExpNode::LimitResult RegExpNode::LimitVersions(RegExpCompiler* compiler,
Trace* trace) {
- // TODO(erikcorry): Implement support.
- if (info_.follows_word_interest ||
- info_.follows_newline_interest ||
- info_.follows_start_interest) {
- return FAIL;
- }
-
// If we are generating a greedy loop then don't stop and don't reuse code.
if (trace->stop_node() != NULL) {
return CONTINUE;
@@ -1978,27 +2023,62 @@
// If we get here code has been generated for this node too many times or
// recursion is too deep. Time to switch to a generic version. The code for
// generic versions above can handle deep recursion properly.
- bool ok = trace->Flush(compiler, this);
- return ok ? DONE : FAIL;
+ trace->Flush(compiler, this);
+ return DONE;
}
-int ActionNode::EatsAtLeast(int recursion_depth) {
+int ActionNode::EatsAtLeast(int still_to_find, int recursion_depth) {
if (recursion_depth > RegExpCompiler::kMaxRecursion) return 0;
if (type_ == POSITIVE_SUBMATCH_SUCCESS) return 0; // Rewinds input!
- return on_success()->EatsAtLeast(recursion_depth + 1);
+ return on_success()->EatsAtLeast(still_to_find, recursion_depth + 1);
}
-int TextNode::EatsAtLeast(int recursion_depth) {
+int AssertionNode::EatsAtLeast(int still_to_find, int recursion_depth) {
+ if (recursion_depth > RegExpCompiler::kMaxRecursion) return 0;
+ return on_success()->EatsAtLeast(still_to_find, recursion_depth + 1);
+}
+
+
+int BackReferenceNode::EatsAtLeast(int still_to_find, int recursion_depth) {
+ if (recursion_depth > RegExpCompiler::kMaxRecursion) return 0;
+ return on_success()->EatsAtLeast(still_to_find, recursion_depth + 1);
+}
+
+
+int TextNode::EatsAtLeast(int still_to_find, int recursion_depth) {
int answer = Length();
- if (answer >= 4) return answer;
+ if (answer >= still_to_find) return answer;
if (recursion_depth > RegExpCompiler::kMaxRecursion) return answer;
- return answer + on_success()->EatsAtLeast(recursion_depth + 1);
+ return answer + on_success()->EatsAtLeast(still_to_find - answer,
+ recursion_depth + 1);
}
-int ChoiceNode::EatsAtLeastHelper(int recursion_depth,
+int NegativeLookaheadChoiceNode:: EatsAtLeast(int still_to_find,
+ int recursion_depth) {
+ if (recursion_depth > RegExpCompiler::kMaxRecursion) return 0;
+ // Alternative 0 is the negative lookahead, alternative 1 is what comes
+ // afterwards.
+ RegExpNode* node = alternatives_->at(1).node();
+ return node->EatsAtLeast(still_to_find, recursion_depth + 1);
+}
+
+
+void NegativeLookaheadChoiceNode::GetQuickCheckDetails(
+ QuickCheckDetails* details,
+ RegExpCompiler* compiler,
+ int filled_in) {
+ // Alternative 0 is the negative lookahead, alternative 1 is what comes
+ // afterwards.
+ RegExpNode* node = alternatives_->at(1).node();
+ return node->GetQuickCheckDetails(details, compiler, filled_in);
+}
+
+
+int ChoiceNode::EatsAtLeastHelper(int still_to_find,
+ int recursion_depth,
RegExpNode* ignore_this_node) {
if (recursion_depth > RegExpCompiler::kMaxRecursion) return 0;
int min = 100;
@@ -2006,20 +2086,21 @@
for (int i = 0; i < choice_count; i++) {
RegExpNode* node = alternatives_->at(i).node();
if (node == ignore_this_node) continue;
- int node_eats_at_least = node->EatsAtLeast(recursion_depth + 1);
+ int node_eats_at_least = node->EatsAtLeast(still_to_find,
+ recursion_depth + 1);
if (node_eats_at_least < min) min = node_eats_at_least;
}
return min;
}
-int LoopChoiceNode::EatsAtLeast(int recursion_depth) {
- return EatsAtLeastHelper(recursion_depth, loop_node_);
+int LoopChoiceNode::EatsAtLeast(int still_to_find, int recursion_depth) {
+ return EatsAtLeastHelper(still_to_find, recursion_depth, loop_node_);
}
-int ChoiceNode::EatsAtLeast(int recursion_depth) {
- return EatsAtLeastHelper(recursion_depth, NULL);
+int ChoiceNode::EatsAtLeast(int still_to_find, int recursion_depth) {
+ return EatsAtLeastHelper(still_to_find, recursion_depth, NULL);
}
@@ -2257,7 +2338,7 @@
void QuickCheckDetails::Advance(int by, bool ascii) {
- ASSERT(by > 0);
+ ASSERT(by >= 0);
if (by >= characters_) {
Clear();
return;
@@ -2342,6 +2423,150 @@
}
+// Check for [0-9A-Z_a-z].
+static void EmitWordCheck(RegExpMacroAssembler* assembler,
+ Label* word,
+ Label* non_word,
+ bool fall_through_on_word) {
+ assembler->CheckCharacterGT('z', non_word);
+ assembler->CheckCharacterLT('0', non_word);
+ assembler->CheckCharacterGT('a' - 1, word);
+ assembler->CheckCharacterLT('9' + 1, word);
+ assembler->CheckCharacterLT('A', non_word);
+ assembler->CheckCharacterLT('Z' + 1, word);
+ if (fall_through_on_word) {
+ assembler->CheckNotCharacter('_', non_word);
+ } else {
+ assembler->CheckCharacter('_', word);
+ }
+}
+
+
+// Emit the code to check for a ^ in multiline mode (1-character lookbehind
+// that matches newline or the start of input).
+static void EmitHat(RegExpCompiler* compiler,
+ RegExpNode* on_success,
+ Trace* trace) {
+ RegExpMacroAssembler* assembler = compiler->macro_assembler();
+ // We will be loading the previous character into the current character
+ // register.
+ Trace new_trace(*trace);
+ new_trace.InvalidateCurrentCharacter();
+
+ Label ok;
+ if (new_trace.cp_offset() == 0) {
+ // The start of input counts as a newline in this context, so skip to
+ // ok if we are at the start.
+ assembler->CheckAtStart(&ok);
+ }
+ // We already checked that we are not at the start of input so it must be
+ // OK to load the previous character.
+ assembler->LoadCurrentCharacter(new_trace.cp_offset() -1,
+ new_trace.backtrack(),
+ false);
+ // Newline means \n, \r, 0x2028 or 0x2029.
+ if (!compiler->ascii()) {
+ assembler->CheckCharacterAfterAnd(0x2028, 0xfffe, &ok);
+ }
+ assembler->CheckCharacter('\n', &ok);
+ assembler->CheckNotCharacter('\r', new_trace.backtrack());
+ assembler->Bind(&ok);
+ on_success->Emit(compiler, &new_trace);
+}
+
+
+// Emit the code to handle \b and \B (word-boundary or non-word-boundary).
+static void EmitBoundaryCheck(AssertionNode::AssertionNodeType type,
+ RegExpCompiler* compiler,
+ RegExpNode* on_success,
+ Trace* trace) {
+ RegExpMacroAssembler* assembler = compiler->macro_assembler();
+ Label before_non_word;
+ Label before_word;
+ if (trace->characters_preloaded() != 1) {
+ assembler->LoadCurrentCharacter(trace->cp_offset(), &before_non_word);
+ }
+ // Fall through on non-word.
+ EmitWordCheck(assembler, &before_word, &before_non_word, false);
+
+ // We will be loading the previous character into the current character
+ // register.
+ Trace new_trace(*trace);
+ new_trace.InvalidateCurrentCharacter();
+
+ Label ok;
+ Label* boundary;
+ Label* not_boundary;
+ if (type == AssertionNode::AT_BOUNDARY) {
+ boundary = &ok;
+ not_boundary = new_trace.backtrack();
+ } else {
+ not_boundary = &ok;
+ boundary = new_trace.backtrack();
+ }
+
+ // Next character is not a word character.
+ assembler->Bind(&before_non_word);
+ if (new_trace.cp_offset() == 0) {
+ // The start of input counts as a non-word character, so the question is
+ // decided if we are at the start.
+ assembler->CheckAtStart(not_boundary);
+ }
+ // We already checked that we are not at the start of input so it must be
+ // OK to load the previous character.
+ assembler->LoadCurrentCharacter(new_trace.cp_offset() - 1,
+ &ok, // Unused dummy label in this call.
+ false);
+ // Fall through on non-word.
+ EmitWordCheck(assembler, boundary, not_boundary, false);
+ assembler->GoTo(not_boundary);
+
+ // Next character is a word character.
+ assembler->Bind(&before_word);
+ if (new_trace.cp_offset() == 0) {
+ // The start of input counts as a non-word character, so the question is
+ // decided if we are at the start.
+ assembler->CheckAtStart(boundary);
+ }
+ // We already checked that we are not at the start of input so it must be
+ // OK to load the previous character.
+ assembler->LoadCurrentCharacter(new_trace.cp_offset() - 1,
+ &ok, // Unused dummy label in this call.
+ false);
+ bool fall_through_on_word = (type == AssertionNode::AT_NON_BOUNDARY);
+ EmitWordCheck(assembler, not_boundary, boundary, fall_through_on_word);
+
+ assembler->Bind(&ok);
+
+ on_success->Emit(compiler, &new_trace);
+}
+
+
+void AssertionNode::Emit(RegExpCompiler* compiler, Trace* trace) {
+ RegExpMacroAssembler* assembler = compiler->macro_assembler();
+ switch (type_) {
+ case AT_END: {
+ Label ok;
+ assembler->CheckPosition(trace->cp_offset(), &ok);
+ assembler->GoTo(trace->backtrack());
+ assembler->Bind(&ok);
+ break;
+ }
+ case AT_START:
+ assembler->CheckNotAtStart(trace->backtrack());
+ break;
+ case AFTER_NEWLINE:
+ EmitHat(compiler, on_success(), trace);
+ return;
+ case AT_NON_BOUNDARY:
+ case AT_BOUNDARY:
+ EmitBoundaryCheck(type_, compiler, on_success(), trace);
+ return;
+ }
+ on_success()->Emit(compiler, trace);
+}
+
+
// We call this repeatedly to generate code for each pass over the text node.
// The passes are in increasing order of difficulty because we hope one
// of the first passes will fail in which case we are saved the work of the
@@ -2481,21 +2706,14 @@
// pass from left to right. Instead we pass over the text node several times,
// emitting code for some character positions every time. See the comment on
// TextEmitPass for details.
-bool TextNode::Emit(RegExpCompiler* compiler, Trace* trace) {
+void TextNode::Emit(RegExpCompiler* compiler, Trace* trace) {
LimitResult limit_result = LimitVersions(compiler, trace);
- if (limit_result == FAIL) return false;
- if (limit_result == DONE) return true;
+ if (limit_result == DONE) return;
ASSERT(limit_result == CONTINUE);
- if (info()->follows_word_interest ||
- info()->follows_newline_interest ||
- info()->follows_start_interest) {
- return false;
- }
-
- if (info()->at_end) {
- compiler->macro_assembler()->GoTo(trace->backtrack());
- return true;
+ if (trace->cp_offset() + Length() > RegExpMacroAssembler::kMaxCPOffset) {
+ compiler->SetRegExpTooBig();
+ return;
}
if (compiler->ascii()) {
@@ -2555,13 +2773,18 @@
&bound_checked_to);
Trace successor_trace(*trace);
- successor_trace.AdvanceCurrentPositionInTrace(Length(), compiler->ascii());
+ successor_trace.AdvanceCurrentPositionInTrace(Length(), compiler);
RecursionCheck rc(compiler);
- return on_success()->Emit(compiler, &successor_trace);
+ on_success()->Emit(compiler, &successor_trace);
}
-void Trace::AdvanceCurrentPositionInTrace(int by, bool ascii) {
+void Trace::InvalidateCurrentCharacter() {
+ characters_preloaded_ = 0;
+}
+
+
+void Trace::AdvanceCurrentPositionInTrace(int by, RegExpCompiler* compiler) {
ASSERT(by > 0);
// We don't have an instruction for shifting the current character register
// down or for using a shifted value for anything so lets just forget that
@@ -2570,8 +2793,12 @@
// Adjust the offsets of the quick check performed information. This
// information is used to find out what we already determined about the
// characters by means of mask and compare.
- quick_check_performed_.Advance(by, ascii);
+ quick_check_performed_.Advance(by, compiler->ascii());
cp_offset_ += by;
+ if (cp_offset_ > RegExpMacroAssembler::kMaxCPOffset) {
+ compiler->SetRegExpTooBig();
+ cp_offset_ = 0;
+ }
bound_checked_up_to_ = Max(0, bound_checked_up_to_ - by);
}
@@ -2616,12 +2843,6 @@
if (recursion_depth++ > RegExpCompiler::kMaxRecursion) {
return kNodeIsTooComplexForGreedyLoops;
}
- NodeInfo* info = node->info();
- if (info->follows_word_interest ||
- info->follows_newline_interest ||
- info->follows_start_interest) {
- return kNodeIsTooComplexForGreedyLoops;
- }
int node_length = node->GreedyLoopTextLength();
if (node_length == kNodeIsTooComplexForGreedyLoops) {
return kNodeIsTooComplexForGreedyLoops;
@@ -2648,7 +2869,7 @@
}
-bool LoopChoiceNode::Emit(RegExpCompiler* compiler, Trace* trace) {
+void LoopChoiceNode::Emit(RegExpCompiler* compiler, Trace* trace) {
RegExpMacroAssembler* macro_assembler = compiler->macro_assembler();
if (trace->stop_node() == this) {
int text_length = GreedyLoopTextLength(&(alternatives_->at(0)));
@@ -2658,18 +2879,19 @@
ASSERT(trace->cp_offset() == text_length);
macro_assembler->AdvanceCurrentPosition(text_length);
macro_assembler->GoTo(trace->loop_label());
- return true;
+ return;
}
ASSERT(trace->stop_node() == NULL);
if (!trace->is_trivial()) {
- return trace->Flush(compiler, this);
+ trace->Flush(compiler, this);
+ return;
}
- return ChoiceNode::Emit(compiler, trace);
+ ChoiceNode::Emit(compiler, trace);
}
int ChoiceNode::CalculatePreloadCharacters(RegExpCompiler* compiler) {
- int preload_characters = EatsAtLeast(0);
+ int preload_characters = EatsAtLeast(4, 0);
#ifdef CAN_READ_UNALIGNED
bool ascii = compiler->ascii();
if (ascii) {
@@ -2817,7 +3039,7 @@
*/
-bool ChoiceNode::Emit(RegExpCompiler* compiler, Trace* trace) {
+void ChoiceNode::Emit(RegExpCompiler* compiler, Trace* trace) {
RegExpMacroAssembler* macro_assembler = compiler->macro_assembler();
int choice_count = alternatives_->length();
#ifdef DEBUG
@@ -2832,8 +3054,7 @@
#endif
LimitResult limit_result = LimitVersions(compiler, trace);
- if (limit_result == DONE) return true;
- if (limit_result == FAIL) return false;
+ if (limit_result == DONE) return;
ASSERT(limit_result == CONTINUE);
RecursionCheck rc(compiler);
@@ -2864,13 +3085,8 @@
macro_assembler->Bind(&loop_label);
greedy_match_trace.set_stop_node(this);
greedy_match_trace.set_loop_label(&loop_label);
- bool ok = alternatives_->at(0).node()->Emit(compiler,
- &greedy_match_trace);
+ alternatives_->at(0).node()->Emit(compiler, &greedy_match_trace);
macro_assembler->Bind(&greedy_match_failed);
- if (!ok) {
- greedy_loop_label.Unuse();
- return false;
- }
}
Label second_choice; // For use in greedy matches.
@@ -2903,7 +3119,8 @@
new_trace.quick_check_performed()->Clear();
alt_gen->expects_preload = preload_is_current;
bool generate_full_check_inline = false;
- if (alternative.node()->EmitQuickCheck(compiler,
+ if (try_to_emit_quick_check_for_alternative(i) &&
+ alternative.node()->EmitQuickCheck(compiler,
&new_trace,
preload_has_checked_bounds,
&alt_gen->possible_success,
@@ -2941,10 +3158,7 @@
for (int j = 0; j < guard_count; j++) {
GenerateGuard(macro_assembler, guards->at(j), &new_trace);
}
- if (!alternative.node()->Emit(compiler, &new_trace)) {
- greedy_loop_label.Unuse();
- return false;
- }
+ alternative.node()->Emit(compiler, &new_trace);
preload_is_current = false;
}
macro_assembler->Bind(&alt_gen->after);
@@ -2962,26 +3176,23 @@
// label was bound.
for (int i = first_normal_choice; i < choice_count - 1; i++) {
AlternativeGeneration* alt_gen = alt_gens.at(i);
- if (!EmitOutOfLineContinuation(compiler,
- current_trace,
- alternatives_->at(i),
- alt_gen,
- preload_characters,
- alt_gens.at(i + 1)->expects_preload)) {
- return false;
- }
+ EmitOutOfLineContinuation(compiler,
+ current_trace,
+ alternatives_->at(i),
+ alt_gen,
+ preload_characters,
+ alt_gens.at(i + 1)->expects_preload);
}
- return true;
}
-bool ChoiceNode::EmitOutOfLineContinuation(RegExpCompiler* compiler,
+void ChoiceNode::EmitOutOfLineContinuation(RegExpCompiler* compiler,
Trace* trace,
GuardedAlternative alternative,
AlternativeGeneration* alt_gen,
int preload_characters,
bool next_expects_preload) {
- if (!alt_gen->possible_success.is_linked()) return true;
+ if (!alt_gen->possible_success.is_linked()) return;
RegExpMacroAssembler* macro_assembler = compiler->macro_assembler();
macro_assembler->Bind(&alt_gen->possible_success);
@@ -2996,7 +3207,7 @@
for (int j = 0; j < guard_count; j++) {
GenerateGuard(macro_assembler, guards->at(j), &out_of_line_trace);
}
- bool ok = alternative.node()->Emit(compiler, &out_of_line_trace);
+ alternative.node()->Emit(compiler, &out_of_line_trace);
macro_assembler->Bind(&reload_current_char);
// Reload the current character, since the next quick check expects that.
// We don't need to check bounds here because we only get into this
@@ -3006,22 +3217,20 @@
false,
preload_characters);
macro_assembler->GoTo(&(alt_gen->after));
- return ok;
} else {
out_of_line_trace.set_backtrack(&(alt_gen->after));
for (int j = 0; j < guard_count; j++) {
GenerateGuard(macro_assembler, guards->at(j), &out_of_line_trace);
}
- return alternative.node()->Emit(compiler, &out_of_line_trace);
+ alternative.node()->Emit(compiler, &out_of_line_trace);
}
}
-bool ActionNode::Emit(RegExpCompiler* compiler, Trace* trace) {
+void ActionNode::Emit(RegExpCompiler* compiler, Trace* trace) {
RegExpMacroAssembler* assembler = compiler->macro_assembler();
LimitResult limit_result = LimitVersions(compiler, trace);
- if (limit_result == DONE) return true;
- if (limit_result == FAIL) return false;
+ if (limit_result == DONE) return;
ASSERT(limit_result == CONTINUE);
RecursionCheck rc(compiler);
@@ -3029,24 +3238,29 @@
switch (type_) {
case STORE_POSITION: {
Trace::DeferredCapture
- new_capture(data_.u_position_register.reg, trace);
+ new_capture(data_.u_position_register.reg,
+ data_.u_position_register.is_capture,
+ trace);
Trace new_trace = *trace;
new_trace.add_action(&new_capture);
- return on_success()->Emit(compiler, &new_trace);
+ on_success()->Emit(compiler, &new_trace);
+ break;
}
case INCREMENT_REGISTER: {
Trace::DeferredIncrementRegister
new_increment(data_.u_increment_register.reg);
Trace new_trace = *trace;
new_trace.add_action(&new_increment);
- return on_success()->Emit(compiler, &new_trace);
+ on_success()->Emit(compiler, &new_trace);
+ break;
}
case SET_REGISTER: {
Trace::DeferredSetRegister
new_set(data_.u_store_register.reg, data_.u_store_register.value);
Trace new_trace = *trace;
new_trace.add_action(&new_set);
- return on_success()->Emit(compiler, &new_trace);
+ on_success()->Emit(compiler, &new_trace);
+ break;
}
case CLEAR_CAPTURES: {
Trace::DeferredClearCaptures
@@ -3054,15 +3268,20 @@
data_.u_clear_captures.range_to));
Trace new_trace = *trace;
new_trace.add_action(&new_capture);
- return on_success()->Emit(compiler, &new_trace);
+ on_success()->Emit(compiler, &new_trace);
+ break;
}
case BEGIN_SUBMATCH:
- if (!trace->is_trivial()) return trace->Flush(compiler, this);
- assembler->WriteCurrentPositionToRegister(
- data_.u_submatch.current_position_register, 0);
- assembler->WriteStackPointerToRegister(
- data_.u_submatch.stack_pointer_register);
- return on_success()->Emit(compiler, trace);
+ if (!trace->is_trivial()) {
+ trace->Flush(compiler, this);
+ } else {
+ assembler->WriteCurrentPositionToRegister(
+ data_.u_submatch.current_position_register, 0);
+ assembler->WriteStackPointerToRegister(
+ data_.u_submatch.stack_pointer_register);
+ on_success()->Emit(compiler, trace);
+ }
+ break;
case EMPTY_MATCH_CHECK: {
int start_pos_reg = data_.u_empty_match_check.start_register;
int stored_pos = 0;
@@ -3073,84 +3292,84 @@
// If we know we haven't advanced and there is no minimum we
// can just backtrack immediately.
assembler->GoTo(trace->backtrack());
- return true;
} else if (know_dist && stored_pos < trace->cp_offset()) {
// If we know we've advanced we can generate the continuation
// immediately.
- return on_success()->Emit(compiler, trace);
+ on_success()->Emit(compiler, trace);
+ } else if (!trace->is_trivial()) {
+ trace->Flush(compiler, this);
+ } else {
+ Label skip_empty_check;
+ // If we have a minimum number of repetitions we check the current
+ // number first and skip the empty check if it's not enough.
+ if (has_minimum) {
+ int limit = data_.u_empty_match_check.repetition_limit;
+ assembler->IfRegisterLT(rep_reg, limit, &skip_empty_check);
+ }
+ // If the match is empty we bail out, otherwise we fall through
+ // to the on-success continuation.
+ assembler->IfRegisterEqPos(data_.u_empty_match_check.start_register,
+ trace->backtrack());
+ assembler->Bind(&skip_empty_check);
+ on_success()->Emit(compiler, trace);
}
- if (!trace->is_trivial()) return trace->Flush(compiler, this);
- Label skip_empty_check;
- // If we have a minimum number of repetitions we check the current
- // number first and skip the empty check if it's not enough.
- if (has_minimum) {
- int limit = data_.u_empty_match_check.repetition_limit;
- assembler->IfRegisterLT(rep_reg, limit, &skip_empty_check);
- }
- // If the match is empty we bail out, otherwise we fall through
- // to the on-success continuation.
- assembler->IfRegisterEqPos(data_.u_empty_match_check.start_register,
- trace->backtrack());
- assembler->Bind(&skip_empty_check);
- return on_success()->Emit(compiler, trace);
+ break;
}
- case POSITIVE_SUBMATCH_SUCCESS:
- if (!trace->is_trivial()) return trace->Flush(compiler, this);
- // TODO(erikcorry): Implement support.
- if (info()->follows_word_interest ||
- info()->follows_newline_interest ||
- info()->follows_start_interest) {
- return false;
- }
- if (info()->at_end) {
- Label at_end;
- // Load current character jumps to the label if we are beyond the string
- // end.
- assembler->LoadCurrentCharacter(0, &at_end);
- assembler->GoTo(trace->backtrack());
- assembler->Bind(&at_end);
+ case POSITIVE_SUBMATCH_SUCCESS: {
+ if (!trace->is_trivial()) {
+ trace->Flush(compiler, this);
+ return;
}
assembler->ReadCurrentPositionFromRegister(
data_.u_submatch.current_position_register);
assembler->ReadStackPointerFromRegister(
data_.u_submatch.stack_pointer_register);
- return on_success()->Emit(compiler, trace);
+ int clear_register_count = data_.u_submatch.clear_register_count;
+ if (clear_register_count == 0) {
+ on_success()->Emit(compiler, trace);
+ return;
+ }
+ int clear_registers_from = data_.u_submatch.clear_register_from;
+ Label clear_registers_backtrack;
+ Trace new_trace = *trace;
+ new_trace.set_backtrack(&clear_registers_backtrack);
+ on_success()->Emit(compiler, &new_trace);
+
+ assembler->Bind(&clear_registers_backtrack);
+ int clear_registers_to = clear_registers_from + clear_register_count - 1;
+ assembler->ClearRegisters(clear_registers_from, clear_registers_to);
+
+ ASSERT(trace->backtrack() == NULL);
+ assembler->Backtrack();
+ return;
+ }
default:
UNREACHABLE();
- return false;
}
}
-bool BackReferenceNode::Emit(RegExpCompiler* compiler, Trace* trace) {
+void BackReferenceNode::Emit(RegExpCompiler* compiler, Trace* trace) {
RegExpMacroAssembler* assembler = compiler->macro_assembler();
if (!trace->is_trivial()) {
- return trace->Flush(compiler, this);
+ trace->Flush(compiler, this);
+ return;
}
LimitResult limit_result = LimitVersions(compiler, trace);
- if (limit_result == DONE) return true;
- if (limit_result == FAIL) return false;
+ if (limit_result == DONE) return;
ASSERT(limit_result == CONTINUE);
RecursionCheck rc(compiler);
ASSERT_EQ(start_reg_ + 1, end_reg_);
- if (info()->at_end) {
- // If we are constrained to match at the end of the input then succeed
- // iff the back reference is empty.
- assembler->CheckNotRegistersEqual(start_reg_,
- end_reg_,
- trace->backtrack());
+ if (compiler->ignore_case()) {
+ assembler->CheckNotBackReferenceIgnoreCase(start_reg_,
+ trace->backtrack());
} else {
- if (compiler->ignore_case()) {
- assembler->CheckNotBackReferenceIgnoreCase(start_reg_,
- trace->backtrack());
- } else {
- assembler->CheckNotBackReference(start_reg_, trace->backtrack());
- }
+ assembler->CheckNotBackReference(start_reg_, trace->backtrack());
}
- return on_success()->Emit(compiler, trace);
+ on_success()->Emit(compiler, trace);
}
@@ -3389,6 +3608,33 @@
}
+void DotPrinter::VisitAssertion(AssertionNode* that) {
+ stream()->Add(" n%p [", that);
+ switch (that->type()) {
+ case AssertionNode::AT_END:
+ stream()->Add("label=\"$\", shape=septagon");
+ break;
+ case AssertionNode::AT_START:
+ stream()->Add("label=\"^\", shape=septagon");
+ break;
+ case AssertionNode::AT_BOUNDARY:
+ stream()->Add("label=\"\\b\", shape=septagon");
+ break;
+ case AssertionNode::AT_NON_BOUNDARY:
+ stream()->Add("label=\"\\B\", shape=septagon");
+ break;
+ case AssertionNode::AFTER_NEWLINE:
+ stream()->Add("label=\"(?<=\\n)\", shape=septagon");
+ break;
+ }
+ stream()->Add("];\n");
+ PrintAttributes(that);
+ RegExpNode* successor = that->on_success();
+ stream()->Add(" n%p -> n%p;\n", that, successor);
+ Visit(successor);
+}
+
+
void DotPrinter::VisitAction(ActionNode* that) {
stream()->Add(" n%p [", that);
switch (that->type_) {
@@ -3713,7 +3959,7 @@
if (body_can_be_empty) {
// If the body can be empty we need to store the start position
// so we can bail out if it was empty.
- body_node = ActionNode::StorePosition(body_start_reg, body_node);
+ body_node = ActionNode::StorePosition(body_start_reg, false, body_node);
}
if (needs_capture_clearing) {
// Before entering the body of this loop we need to clear captures.
@@ -3749,22 +3995,51 @@
NodeInfo info;
switch (type()) {
case START_OF_LINE:
- info.follows_newline_interest = true;
- break;
+ return AssertionNode::AfterNewline(on_success);
case START_OF_INPUT:
- info.follows_start_interest = true;
- break;
- case BOUNDARY: case NON_BOUNDARY:
- info.follows_word_interest = true;
- break;
+ return AssertionNode::AtStart(on_success);
+ case BOUNDARY:
+ return AssertionNode::AtBoundary(on_success);
+ case NON_BOUNDARY:
+ return AssertionNode::AtNonBoundary(on_success);
case END_OF_INPUT:
- info.at_end = true;
- break;
- case END_OF_LINE:
- // This is wrong but has the effect of making the compiler abort.
- info.at_end = true;
+ return AssertionNode::AtEnd(on_success);
+ case END_OF_LINE: {
+ // Compile $ in multiline regexps as an alternation with a positive
+ // lookahead in one side and an end-of-input on the other side.
+ // We need two registers for the lookahead.
+ int stack_pointer_register = compiler->AllocateRegister();
+ int position_register = compiler->AllocateRegister();
+ // The ChoiceNode to distinguish between a newline and end-of-input.
+ ChoiceNode* result = new ChoiceNode(2);
+ // Create a newline atom.
+ ZoneList<CharacterRange>* newline_ranges =
+ new ZoneList<CharacterRange>(3);
+ CharacterRange::AddClassEscape('n', newline_ranges);
+ RegExpCharacterClass* newline_atom = new RegExpCharacterClass('n');
+ TextNode* newline_matcher = new TextNode(
+ newline_atom,
+ ActionNode::PositiveSubmatchSuccess(stack_pointer_register,
+ position_register,
+ 0, // No captures inside.
+ -1, // Ignored if no captures.
+ on_success));
+ // Create an end-of-input matcher.
+ RegExpNode* end_of_line = ActionNode::BeginSubmatch(
+ stack_pointer_register,
+ position_register,
+ newline_matcher);
+ // Add the two alternatives to the ChoiceNode.
+ GuardedAlternative eol_alternative(end_of_line);
+ result->AddAlternative(eol_alternative);
+ GuardedAlternative end_alternative(AssertionNode::AtEnd(on_success));
+ result->AddAlternative(end_alternative);
+ return result;
+ }
+ default:
+ UNREACHABLE();
}
- return on_success->PropagateForward(&info);
+ return on_success;
}
@@ -3786,16 +4061,26 @@
RegExpNode* on_success) {
int stack_pointer_register = compiler->AllocateRegister();
int position_register = compiler->AllocateRegister();
+
+ const int registers_per_capture = 2;
+ const int register_of_first_capture = 2;
+ int register_count = capture_count_ * registers_per_capture;
+ int register_start =
+ register_of_first_capture + capture_from_ * registers_per_capture;
+
RegExpNode* success;
if (is_positive()) {
- return ActionNode::BeginSubmatch(
+ RegExpNode* node = ActionNode::BeginSubmatch(
stack_pointer_register,
position_register,
body()->ToNode(
compiler,
ActionNode::PositiveSubmatchSuccess(stack_pointer_register,
position_register,
+ register_count,
+ register_start,
on_success)));
+ return node;
} else {
// We use a ChoiceNode for a negative lookahead because it has most of
// the characteristics we need. It has the body of the lookahead as its
@@ -3804,19 +4089,19 @@
// NegativeSubmatchSuccess will unwind the stack including everything the
// choice node set up and backtrack. If the first alternative fails then
// the second alternative is tried, which is exactly the desired result
- // for a negative lookahead. In the case where the dispatch table
- // determines that the first alternative cannot match we will save time
- // by not trying it. Things are not quite so well-optimized if the
- // dispatch table determines that the second alternative cannot match.
- // In this case we could optimize by immediately backtracking.
- ChoiceNode* choice_node = new ChoiceNode(2);
+ // for a negative lookahead. The NegativeLookaheadChoiceNode is a special
+ // ChoiceNode that knows to ignore the first exit when calculating quick
+ // checks.
GuardedAlternative body_alt(
body()->ToNode(
compiler,
success = new NegativeSubmatchSuccess(stack_pointer_register,
- position_register)));
- choice_node->AddAlternative(body_alt);
- choice_node->AddAlternative(GuardedAlternative(on_success));
+ position_register,
+ register_count,
+ register_start)));
+ ChoiceNode* choice_node =
+ new NegativeLookaheadChoiceNode(body_alt,
+ GuardedAlternative(on_success));
return ActionNode::BeginSubmatch(stack_pointer_register,
position_register,
choice_node);
@@ -3836,9 +4121,9 @@
RegExpNode* on_success) {
int start_reg = RegExpCapture::StartRegister(index);
int end_reg = RegExpCapture::EndRegister(index);
- RegExpNode* store_end = ActionNode::StorePosition(end_reg, on_success);
+ RegExpNode* store_end = ActionNode::StorePosition(end_reg, true, on_success);
RegExpNode* body_node = body->ToNode(compiler, store_end);
- return ActionNode::StorePosition(start_reg, body_node);
+ return ActionNode::StorePosition(start_reg, true, body_node);
}
@@ -3911,6 +4196,13 @@
case '*':
ranges->Add(CharacterRange::Everything());
break;
+ // This is the set of characters matched by the $ and ^ symbols
+ // in multiline mode.
+ case 'n':
+ AddClass(kLineTerminatorRanges,
+ kLineTerminatorRangeCount,
+ ranges);
+ break;
default:
UNREACHABLE();
}
@@ -4096,62 +4388,6 @@
}
-RegExpNode* ActionNode::PropagateForward(NodeInfo* info) {
- NodeInfo full_info(*this->info());
- full_info.AddFromPreceding(info);
- bool cloned = false;
- ActionNode* action = EnsureSibling(this, &full_info, &cloned);
- action->set_on_success(action->on_success()->PropagateForward(info));
- return action;
-}
-
-
-RegExpNode* ChoiceNode::PropagateForward(NodeInfo* info) {
- NodeInfo full_info(*this->info());
- full_info.AddFromPreceding(info);
- bool cloned = false;
- ChoiceNode* choice = EnsureSibling(this, &full_info, &cloned);
- if (cloned) {
- ZoneList<GuardedAlternative>* old_alternatives = alternatives();
- int count = old_alternatives->length();
- choice->alternatives_ = new ZoneList<GuardedAlternative>(count);
- for (int i = 0; i < count; i++) {
- GuardedAlternative alternative = old_alternatives->at(i);
- alternative.set_node(alternative.node()->PropagateForward(info));
- choice->alternatives()->Add(alternative);
- }
- }
- return choice;
-}
-
-
-RegExpNode* EndNode::PropagateForward(NodeInfo* info) {
- return PropagateToEndpoint(this, info);
-}
-
-
-RegExpNode* BackReferenceNode::PropagateForward(NodeInfo* info) {
- NodeInfo full_info(*this->info());
- full_info.AddFromPreceding(info);
- bool cloned = false;
- BackReferenceNode* back_ref = EnsureSibling(this, &full_info, &cloned);
- if (cloned) {
- // TODO(erikcorry): A back reference has to have two successors (by default
- // the same node). The first is used if the back reference matches a non-
- // empty back reference, the second if it matches an empty one. This
- // doesn't matter for at_end, which is the only one implemented right now,
- // but it will matter for other pieces of info.
- back_ref->set_on_success(back_ref->on_success()->PropagateForward(info));
- }
- return back_ref;
-}
-
-
-RegExpNode* TextNode::PropagateForward(NodeInfo* info) {
- return PropagateToEndpoint(this, info);
-}
-
-
// -------------------------------------------------------------------
// Splay tree
@@ -4389,6 +4625,11 @@
}
+void Analysis::VisitAssertion(AssertionNode* that) {
+ EnsureAnalyzed(that->on_success());
+}
+
+
// -------------------------------------------------------------------
// Dispatch table construction
@@ -4441,6 +4682,12 @@
}
+void DispatchTableConstructor::VisitAssertion(AssertionNode* that) {
+ RegExpNode* target = that->on_success();
+ target->Accept(this);
+}
+
+
static int CompareRangeByFrom(const CharacterRange* a,
const CharacterRange* b) {
@@ -4504,33 +4751,32 @@
bool is_multiline,
Handle<String> pattern,
bool is_ascii) {
+ if ((data->capture_count + 1) * 2 - 1 > RegExpMacroAssembler::kMaxRegister) {
+ return IrregexpRegExpTooBig(pattern);
+ }
RegExpCompiler compiler(data->capture_count, ignore_case, is_ascii);
// Wrap the body of the regexp in capture #0.
RegExpNode* captured_body = RegExpCapture::ToNode(data->tree,
0,
&compiler,
compiler.accept());
- // Add a .*? at the beginning, outside the body capture.
- // Note: We could choose to not add this if the regexp is anchored at
- // the start of the input but I'm not sure how best to do that and
- // since we don't even handle ^ yet I'm saving that optimization for
- // later.
- RegExpNode* node = RegExpQuantifier::ToNode(0,
- RegExpTree::kInfinity,
- false,
- new RegExpCharacterClass('*'),
- &compiler,
- captured_body);
+ RegExpNode* node = captured_body;
+ if (!data->tree->IsAnchored()) {
+ // Add a .*? at the beginning, outside the body capture, unless
+ // this expression is anchored at the beginning.
+ node = RegExpQuantifier::ToNode(0,
+ RegExpTree::kInfinity,
+ false,
+ new RegExpCharacterClass('*'),
+ &compiler,
+ captured_body);
+ }
data->node = node;
Analysis analysis(ignore_case);
analysis.EnsureAnalyzed(node);
NodeInfo info = *node->info();
- if (is_multiline && !FLAG_attempt_multiline_irregexp) {
- return Handle<FixedArray>::null();
- }
-
if (FLAG_irregexp_native) {
#ifdef ARM
// Unimplemented, fall-through to bytecode implementation.
diff --git a/src/jsregexp.h b/src/jsregexp.h
index bf3bdb7..0783727 100644
--- a/src/jsregexp.h
+++ b/src/jsregexp.h
@@ -410,6 +410,7 @@
VISIT(Action) \
VISIT(Choice) \
VISIT(BackReference) \
+ VISIT(Assertion) \
VISIT(Text)
@@ -587,12 +588,12 @@
virtual ~RegExpNode();
virtual void Accept(NodeVisitor* visitor) = 0;
// Generates a goto to this node or actually generates the code at this point.
- // Until the implementation is complete we will return true for success and
- // false for failure.
- virtual bool Emit(RegExpCompiler* compiler, Trace* trace) = 0;
+ virtual void Emit(RegExpCompiler* compiler, Trace* trace) = 0;
// How many characters must this node consume at a minimum in order to
- // succeed.
- virtual int EatsAtLeast(int recursion_depth) = 0;
+ // succeed. If we have found at least 'still_to_find' characters that
+ // must be consumed there is no need to ask any following nodes whether
+ // they are sure to eat any more characters.
+ virtual int EatsAtLeast(int still_to_find, int recursion_depth) = 0;
// Emits some quick code that checks whether the preloaded characters match.
// Falls through on certain failure, jumps to the label on possible success.
// If the node cannot make a quick check it does nothing and returns false.
@@ -619,12 +620,6 @@
// the deferred actions in the current trace and generating a goto.
static const int kMaxCopiesCodeGenerated = 10;
- // Propagates the given interest information forward. When seeing
- // \bfoo for instance, the \b is implemented by propagating forward
- // to the 'foo' string that it should only succeed if its first
- // character is a letter xor the previous character was a letter.
- virtual RegExpNode* PropagateForward(NodeInfo* info) = 0;
-
NodeInfo* info() { return &info_; }
void AddSibling(RegExpNode* node) { siblings_.Add(node); }
@@ -640,7 +635,7 @@
void set_siblings(SiblingList* other) { siblings_ = *other; }
protected:
- enum LimitResult { DONE, FAIL, CONTINUE };
+ enum LimitResult { DONE, CONTINUE };
LimitResult LimitVersions(RegExpCompiler* compiler, Trace* trace);
// Returns a sibling of this node whose interests and assumptions
@@ -724,27 +719,30 @@
};
static ActionNode* SetRegister(int reg, int val, RegExpNode* on_success);
static ActionNode* IncrementRegister(int reg, RegExpNode* on_success);
- static ActionNode* StorePosition(int reg, RegExpNode* on_success);
+ static ActionNode* StorePosition(int reg,
+ bool is_capture,
+ RegExpNode* on_success);
static ActionNode* ClearCaptures(Interval range, RegExpNode* on_success);
static ActionNode* BeginSubmatch(int stack_pointer_reg,
int position_reg,
RegExpNode* on_success);
static ActionNode* PositiveSubmatchSuccess(int stack_pointer_reg,
int restore_reg,
+ int clear_capture_count,
+ int clear_capture_from,
RegExpNode* on_success);
static ActionNode* EmptyMatchCheck(int start_register,
int repetition_register,
int repetition_limit,
RegExpNode* on_success);
virtual void Accept(NodeVisitor* visitor);
- virtual bool Emit(RegExpCompiler* compiler, Trace* trace);
- virtual int EatsAtLeast(int recursion_depth);
+ virtual void Emit(RegExpCompiler* compiler, Trace* trace);
+ virtual int EatsAtLeast(int still_to_find, int recursion_depth);
virtual void GetQuickCheckDetails(QuickCheckDetails* details,
RegExpCompiler* compiler,
int filled_in) {
return on_success()->GetQuickCheckDetails(details, compiler, filled_in);
}
- virtual RegExpNode* PropagateForward(NodeInfo* info);
Type type() { return type_; }
// TODO(erikcorry): We should allow some action nodes in greedy loops.
virtual int GreedyLoopTextLength() { return kNodeIsTooComplexForGreedyLoops; }
@@ -761,10 +759,13 @@
} u_increment_register;
struct {
int reg;
+ bool is_capture;
} u_position_register;
struct {
int stack_pointer_register;
int current_position_register;
+ int clear_register_count;
+ int clear_register_from;
} u_submatch;
struct {
int start_register;
@@ -797,9 +798,8 @@
elms_->Add(TextElement::CharClass(that));
}
virtual void Accept(NodeVisitor* visitor);
- virtual RegExpNode* PropagateForward(NodeInfo* info);
- virtual bool Emit(RegExpCompiler* compiler, Trace* trace);
- virtual int EatsAtLeast(int recursion_depth);
+ virtual void Emit(RegExpCompiler* compiler, Trace* trace);
+ virtual int EatsAtLeast(int still_to_find, int recursion_depth);
virtual void GetQuickCheckDetails(QuickCheckDetails* details,
RegExpCompiler* compiler,
int characters_filled_in);
@@ -831,6 +831,47 @@
};
+class AssertionNode: public SeqRegExpNode {
+ public:
+ enum AssertionNodeType {
+ AT_END,
+ AT_START,
+ AT_BOUNDARY,
+ AT_NON_BOUNDARY,
+ AFTER_NEWLINE
+ };
+ static AssertionNode* AtEnd(RegExpNode* on_success) {
+ return new AssertionNode(AT_END, on_success);
+ }
+ static AssertionNode* AtStart(RegExpNode* on_success) {
+ return new AssertionNode(AT_START, on_success);
+ }
+ static AssertionNode* AtBoundary(RegExpNode* on_success) {
+ return new AssertionNode(AT_BOUNDARY, on_success);
+ }
+ static AssertionNode* AtNonBoundary(RegExpNode* on_success) {
+ return new AssertionNode(AT_NON_BOUNDARY, on_success);
+ }
+ static AssertionNode* AfterNewline(RegExpNode* on_success) {
+ return new AssertionNode(AFTER_NEWLINE, on_success);
+ }
+ virtual void Accept(NodeVisitor* visitor);
+ virtual void Emit(RegExpCompiler* compiler, Trace* trace);
+ virtual int EatsAtLeast(int still_to_find, int recursion_depth);
+ virtual void GetQuickCheckDetails(QuickCheckDetails* details,
+ RegExpCompiler* compiler,
+ int filled_in) {
+ return on_success()->GetQuickCheckDetails(details, compiler, filled_in);
+ }
+ virtual AssertionNode* Clone() { return new AssertionNode(*this); }
+ AssertionNodeType type() { return type_; }
+ private:
+ AssertionNode(AssertionNodeType t, RegExpNode* on_success)
+ : SeqRegExpNode(on_success), type_(t) { }
+ AssertionNodeType type_;
+};
+
+
class BackReferenceNode: public SeqRegExpNode {
public:
BackReferenceNode(int start_reg,
@@ -842,14 +883,13 @@
virtual void Accept(NodeVisitor* visitor);
int start_register() { return start_reg_; }
int end_register() { return end_reg_; }
- virtual bool Emit(RegExpCompiler* compiler, Trace* trace);
- virtual int EatsAtLeast(int recursion_depth) { return 0; }
+ virtual void Emit(RegExpCompiler* compiler, Trace* trace);
+ virtual int EatsAtLeast(int still_to_find, int recursion_depth);
virtual void GetQuickCheckDetails(QuickCheckDetails* details,
RegExpCompiler* compiler,
int characters_filled_in) {
return;
}
- virtual RegExpNode* PropagateForward(NodeInfo* info);
virtual BackReferenceNode* Clone() { return new BackReferenceNode(*this); }
private:
@@ -863,20 +903,16 @@
enum Action { ACCEPT, BACKTRACK, NEGATIVE_SUBMATCH_SUCCESS };
explicit EndNode(Action action) : action_(action) { }
virtual void Accept(NodeVisitor* visitor);
- virtual bool Emit(RegExpCompiler* compiler, Trace* trace);
- virtual int EatsAtLeast(int recursion_depth) { return 0; }
+ virtual void Emit(RegExpCompiler* compiler, Trace* trace);
+ virtual int EatsAtLeast(int still_to_find, int recursion_depth) { return 0; }
virtual void GetQuickCheckDetails(QuickCheckDetails* details,
RegExpCompiler* compiler,
int characters_filled_in) {
// Returning 0 from EatsAtLeast should ensure we never get here.
UNREACHABLE();
}
- virtual RegExpNode* PropagateForward(NodeInfo* info);
virtual EndNode* Clone() { return new EndNode(*this); }
- protected:
- void EmitInfoChecks(RegExpMacroAssembler* macro, Trace* trace);
-
private:
Action action_;
};
@@ -884,15 +920,22 @@
class NegativeSubmatchSuccess: public EndNode {
public:
- NegativeSubmatchSuccess(int stack_pointer_reg, int position_reg)
+ NegativeSubmatchSuccess(int stack_pointer_reg,
+ int position_reg,
+ int clear_capture_count,
+ int clear_capture_start)
: EndNode(NEGATIVE_SUBMATCH_SUCCESS),
stack_pointer_register_(stack_pointer_reg),
- current_position_register_(position_reg) { }
- virtual bool Emit(RegExpCompiler* compiler, Trace* trace);
+ current_position_register_(position_reg),
+ clear_capture_count_(clear_capture_count),
+ clear_capture_start_(clear_capture_start) { }
+ virtual void Emit(RegExpCompiler* compiler, Trace* trace);
private:
int stack_pointer_register_;
int current_position_register_;
+ int clear_capture_count_;
+ int clear_capture_start_;
};
@@ -941,17 +984,19 @@
void AddAlternative(GuardedAlternative node) { alternatives()->Add(node); }
ZoneList<GuardedAlternative>* alternatives() { return alternatives_; }
DispatchTable* GetTable(bool ignore_case);
- virtual bool Emit(RegExpCompiler* compiler, Trace* trace);
- virtual int EatsAtLeast(int recursion_depth);
- int EatsAtLeastHelper(int recursion_depth, RegExpNode* ignore_this_node);
+ virtual void Emit(RegExpCompiler* compiler, Trace* trace);
+ virtual int EatsAtLeast(int still_to_find, int recursion_depth);
+ int EatsAtLeastHelper(int still_to_find,
+ int recursion_depth,
+ RegExpNode* ignore_this_node);
virtual void GetQuickCheckDetails(QuickCheckDetails* details,
RegExpCompiler* compiler,
int characters_filled_in);
- virtual RegExpNode* PropagateForward(NodeInfo* info);
virtual ChoiceNode* Clone() { return new ChoiceNode(*this); }
bool being_calculated() { return being_calculated_; }
void set_being_calculated(bool b) { being_calculated_ = b; }
+ virtual bool try_to_emit_quick_check_for_alternative(int i) { return true; }
protected:
int GreedyLoopTextLength(GuardedAlternative *alternative);
@@ -964,7 +1009,7 @@
Guard *guard,
Trace* trace);
int CalculatePreloadCharacters(RegExpCompiler* compiler);
- bool EmitOutOfLineContinuation(RegExpCompiler* compiler,
+ void EmitOutOfLineContinuation(RegExpCompiler* compiler,
Trace* trace,
GuardedAlternative alternative,
AlternativeGeneration* alt_gen,
@@ -975,6 +1020,27 @@
};
+class NegativeLookaheadChoiceNode: public ChoiceNode {
+ public:
+ explicit NegativeLookaheadChoiceNode(GuardedAlternative this_must_fail,
+ GuardedAlternative then_do_this)
+ : ChoiceNode(2) {
+ AddAlternative(this_must_fail);
+ AddAlternative(then_do_this);
+ }
+ virtual int EatsAtLeast(int still_to_find, int recursion_depth);
+ virtual void GetQuickCheckDetails(QuickCheckDetails* details,
+ RegExpCompiler* compiler,
+ int characters_filled_in);
+ // For a negative lookahead we don't emit the quick check for the
+ // alternative that is expected to fail. This is because quick check code
+ // starts by loading enough characters for the alternative that takes fewest
+ // characters, but on a negative lookahead the negative branch did not take
+ // part in that calculation (EatsAtLeast) so the assumptions don't hold.
+ virtual bool try_to_emit_quick_check_for_alternative(int i) { return i != 0; }
+};
+
+
class LoopChoiceNode: public ChoiceNode {
public:
explicit LoopChoiceNode(bool body_can_be_zero_length)
@@ -984,8 +1050,8 @@
body_can_be_zero_length_(body_can_be_zero_length) { }
void AddLoopAlternative(GuardedAlternative alt);
void AddContinueAlternative(GuardedAlternative alt);
- virtual bool Emit(RegExpCompiler* compiler, Trace* trace);
- virtual int EatsAtLeast(int recursion_depth); // Returns 0.
+ virtual void Emit(RegExpCompiler* compiler, Trace* trace);
+ virtual int EatsAtLeast(int still_to_find, int recursion_depth);
virtual void GetQuickCheckDetails(QuickCheckDetails* details,
RegExpCompiler* compiler,
int characters_filled_in);
@@ -1037,18 +1103,21 @@
friend class Trace;
};
- class DeferredCapture: public DeferredAction {
+ class DeferredCapture : public DeferredAction {
public:
- DeferredCapture(int reg, Trace* trace)
+ DeferredCapture(int reg, bool is_capture, Trace* trace)
: DeferredAction(ActionNode::STORE_POSITION, reg),
- cp_offset_(trace->cp_offset()) { }
+ cp_offset_(trace->cp_offset()),
+ is_capture_(is_capture) { }
int cp_offset() { return cp_offset_; }
+ bool is_capture() { return is_capture_; }
private:
int cp_offset_;
+ bool is_capture_;
void set_cp_offset(int cp_offset) { cp_offset_ = cp_offset; }
};
- class DeferredSetRegister :public DeferredAction {
+ class DeferredSetRegister : public DeferredAction {
public:
DeferredSetRegister(int reg, int value)
: DeferredAction(ActionNode::SET_REGISTER, reg),
@@ -1068,7 +1137,7 @@
Interval range_;
};
- class DeferredIncrementRegister: public DeferredAction {
+ class DeferredIncrementRegister : public DeferredAction {
public:
explicit DeferredIncrementRegister(int reg)
: DeferredAction(ActionNode::INCREMENT_REGISTER, reg) { }
@@ -1086,7 +1155,7 @@
// and pushing a backtrack location onto the backtrack stack. Once this is
// done we can start a new trace or go to one that has already been
// generated.
- bool Flush(RegExpCompiler* compiler, RegExpNode* successor);
+ void Flush(RegExpCompiler* compiler, RegExpNode* successor);
int cp_offset() { return cp_offset_; }
DeferredAction* actions() { return actions_; }
// A trivial trace is one that has no deferred actions or other state that
@@ -1133,20 +1202,19 @@
void set_quick_check_performed(QuickCheckDetails* d) {
quick_check_performed_ = *d;
}
- void clear_quick_check_performed() {
- }
- void AdvanceCurrentPositionInTrace(int by, bool ascii);
+ void InvalidateCurrentCharacter();
+ void AdvanceCurrentPositionInTrace(int by, RegExpCompiler* compiler);
private:
int FindAffectedRegisters(OutSet* affected_registers);
void PerformDeferredActions(RegExpMacroAssembler* macro,
int max_register,
- OutSet& affected_registers);
+ OutSet& affected_registers,
+ OutSet* registers_to_pop,
+ OutSet* registers_to_clear);
void RestoreAffectedRegisters(RegExpMacroAssembler* macro,
int max_register,
- OutSet& affected_registers);
- void PushAffectedRegisters(RegExpMacroAssembler* macro,
- int max_register,
- OutSet& affected_registers);
+ OutSet& registers_to_pop,
+ OutSet& registers_to_clear);
int cp_offset_;
DeferredAction* actions_;
Label* backtrack_;
diff --git a/src/mark-compact.cc b/src/mark-compact.cc
index ede5741..1d93f84 100644
--- a/src/mark-compact.cc
+++ b/src/mark-compact.cc
@@ -35,16 +35,7 @@
namespace v8 { namespace internal {
-#ifdef DEBUG
-// The verification code used between phases of the m-c collector does not
-// currently work.
-//
-// TODO(1240833): Fix the heap verification code and turn this into a real
-// flag.
-static const bool FLAG_verify_global_gc = false;
-#endif // DEBUG
-
-// ----------------------------------------------------------------------------
+// -------------------------------------------------------------------------
// MarkCompactCollector
bool MarkCompactCollector::compacting_collection_ = false;
@@ -177,7 +168,7 @@
}
-// ----------------------------------------------------------------------------
+// -------------------------------------------------------------------------
// Phase 1: tracing and marking live objects.
// before: all objects are in normal state.
// after: a live object's map pointer is marked as '00'.
@@ -729,10 +720,6 @@
symbol_table->IterateElements(&v);
symbol_table->ElementsRemoved(v.PointersRemoved());
-#ifdef DEBUG
- if (FLAG_verify_global_gc) VerifyHeapAfterMarkingPhase();
-#endif
-
// Remove object groups after marking phase.
GlobalHandles::RemoveObjectGroups();
}
@@ -765,46 +752,6 @@
UNREACHABLE();
}
}
-
-
-void MarkCompactCollector::VerifyHeapAfterMarkingPhase() {
- Heap::new_space()->Verify();
- Heap::old_pointer_space()->Verify();
- Heap::old_data_space()->Verify();
- Heap::code_space()->Verify();
- Heap::map_space()->Verify();
-
- int live_objects;
-
-#define CHECK_LIVE_OBJECTS(it, expected) \
- live_objects = 0; \
- while (it.has_next()) { \
- HeapObject* obj = HeapObject::cast(it.next()); \
- if (obj->IsMarked()) live_objects++; \
- } \
- ASSERT(live_objects == expected);
-
- SemiSpaceIterator new_it(Heap::new_space(), &CountMarkedCallback);
- CHECK_LIVE_OBJECTS(new_it, live_young_objects_);
-
- HeapObjectIterator old_pointer_it(Heap::old_pointer_space(),
- &CountMarkedCallback);
- CHECK_LIVE_OBJECTS(old_pointer_it, live_old_pointer_objects_);
-
- HeapObjectIterator old_data_it(Heap::old_data_space(), &CountMarkedCallback);
- CHECK_LIVE_OBJECTS(old_data_it, live_old_data_objects_);
-
- HeapObjectIterator code_it(Heap::code_space(), &CountMarkedCallback);
- CHECK_LIVE_OBJECTS(code_it, live_code_objects_);
-
- HeapObjectIterator map_it(Heap::map_space(), &CountMarkedCallback);
- CHECK_LIVE_OBJECTS(map_it, live_map_objects_);
-
- LargeObjectIterator lo_it(Heap::lo_space(), &CountMarkedCallback);
- CHECK_LIVE_OBJECTS(lo_it, live_lo_objects_);
-
-#undef CHECK_LIVE_OBJECTS
-}
#endif // DEBUG
@@ -1325,54 +1272,7 @@
}
-#ifdef DEBUG
-static int VerifyMapObject(HeapObject* obj) {
- InstanceType type = reinterpret_cast<Map*>(obj)->instance_type();
- ASSERT(FIRST_TYPE <= type && type <= LAST_TYPE);
- return Map::kSize;
-}
-
-
-void MarkCompactCollector::VerifyHeapAfterEncodingForwardingAddresses() {
- AllSpaces spaces;
- while (Space* space = spaces.next()) space->Verify();
-
- ASSERT(state_ == ENCODE_FORWARDING_ADDRESSES);
- int live_maps = IterateLiveObjects(Heap::map_space(), &VerifyMapObject);
- ASSERT(live_maps == live_map_objects_);
-
- // Verify page headers in paged spaces.
- PagedSpaces paged_spaces;
- while (PagedSpace* space = paged_spaces.next()) VerifyPageHeaders(space);
-}
-
-
-void MarkCompactCollector::VerifyPageHeaders(PagedSpace* space) {
- PageIterator mc_it(space, PageIterator::PAGES_USED_BY_MC);
- while (mc_it.has_next()) {
- Page* p = mc_it.next();
- Address mc_alloc_top = p->mc_relocation_top;
- ASSERT(p->ObjectAreaStart() <= mc_alloc_top &&
- mc_alloc_top <= p->ObjectAreaEnd());
- }
-
- int page_count = 0;
- PageIterator it(space, PageIterator::PAGES_IN_USE);
- while (it.has_next()) {
- Page* p = it.next();
- ASSERT(p->mc_page_index == page_count);
- page_count++;
-
- // first_forwarded could be 'deadbeed' if no live objects in this page
- Address first_forwarded = p->mc_first_forwarded;
- ASSERT(first_forwarded == kZapValue ||
- space->Contains(first_forwarded));
- }
-}
-#endif
-
-
-// ----------------------------------------------------------------------------
+// -------------------------------------------------------------------------
// Phase 3: Update pointers
// Helper class for updating pointers in HeapObjects.
@@ -1494,8 +1394,6 @@
ASSERT(live_pointer_olds == live_old_pointer_objects_);
ASSERT(live_codes == live_code_objects_);
ASSERT(live_news == live_young_objects_);
-
- if (FLAG_verify_global_gc) VerifyHeapAfterUpdatingPointers();
#endif
}
@@ -1601,19 +1499,7 @@
}
-#ifdef DEBUG
-void MarkCompactCollector::VerifyHeapAfterUpdatingPointers() {
- ASSERT(state_ == UPDATE_POINTERS);
-
- AllSpaces spaces;
- while (Space* space = spaces.next()) space->Verify();
- PagedSpaces paged_spaces;
- while (PagedSpace* space = paged_spaces.next()) VerifyPageHeaders(space);
-}
-#endif
-
-
-// ----------------------------------------------------------------------------
+// -------------------------------------------------------------------------
// Phase 4: Relocate objects
void MarkCompactCollector::RelocateObjects() {
@@ -1664,10 +1550,6 @@
#endif
PagedSpaces spaces;
while (PagedSpace* space = spaces.next()) space->MCCommitRelocationInfo();
-
-#ifdef DEBUG
- if (FLAG_verify_global_gc) VerifyHeapAfterRelocatingObjects();
-#endif
}
@@ -1804,18 +1686,6 @@
}
-#ifdef DEBUG
-class VerifyCopyingVisitor: public ObjectVisitor {
- public:
- void VisitPointers(Object** start, Object** end) {
- for (Object** p = start; p < end; p++) {
- MarkCompactCollector::VerifyCopyingObjects(p);
- }
- }
-};
-
-#endif
-
int MarkCompactCollector::RelocateNewObject(HeapObject* obj) {
int obj_size = obj->Size();
@@ -1845,44 +1715,13 @@
if (FLAG_gc_verbose) {
PrintF("relocate %p -> %p\n", old_addr, new_addr);
}
- if (FLAG_verify_global_gc) {
- VerifyCopyingVisitor v;
- HeapObject* copied_to = HeapObject::FromAddress(new_addr);
- copied_to->Iterate(&v);
- }
#endif
return obj_size;
}
-#ifdef DEBUG
-void MarkCompactCollector::VerifyHeapAfterRelocatingObjects() {
- ASSERT(state_ == RELOCATE_OBJECTS);
-
- Heap::new_space()->Verify();
- PagedSpaces spaces;
- while (PagedSpace* space = spaces.next()) {
- space->Verify();
- PageIterator it(space, PageIterator::PAGES_IN_USE);
- while (it.has_next()) {
- Page* p = it.next();
- ASSERT_PAGE_OFFSET(p->Offset(p->AllocationTop()));
- }
- }
-}
-#endif
-
-
-#ifdef DEBUG
-void MarkCompactCollector::VerifyCopyingObjects(Object** p) {
- if (!(*p)->IsHeapObject()) return;
- ASSERT(!Heap::InToSpace(*p));
-}
-#endif // DEBUG
-
-
-// -----------------------------------------------------------------------------
+// -------------------------------------------------------------------------
// Phase 5: rebuild remembered sets
void MarkCompactCollector::RebuildRSets() {
diff --git a/src/mark-compact.h b/src/mark-compact.h
index 22dd890..746aead 100644
--- a/src/mark-compact.h
+++ b/src/mark-compact.h
@@ -44,12 +44,12 @@
class MarkingVisitor;
-// ----------------------------------------------------------------------------
+// -------------------------------------------------------------------------
// Mark-Compact collector
//
// All methods are static.
-class MarkCompactCollector : public AllStatic {
+class MarkCompactCollector: public AllStatic {
public:
// Type of functions to compute forwarding addresses of objects in
// compacted spaces. Given an object and its size, return a (non-failure)
@@ -127,21 +127,18 @@
// choosing spaces to compact.
static void Prepare();
- // Finishes GC, performs heap verification.
+ // Finishes GC, performs heap verification if enabled.
static void Finish();
- // --------------------------------------------------------------------------
- // Phase 1: functions related to marking phase.
- // before: Heap is in normal state, collector is 'IDLE'.
+ // -----------------------------------------------------------------------
+ // Phase 1: Marking live objects.
//
- // The first word of a page in old spaces has the end of
- // allocation address of the page.
+ // Before: The heap has been prepared for garbage collection by
+ // MarkCompactCollector::Prepare() and is otherwise in its
+ // normal state.
//
- // The word at Chunk::high_ address has the address of the
- // first page in the next chunk. (The address is tagged to
- // distinguish it from end-of-allocation address).
- //
- // after: live objects are marked.
+ // After: Live objects are marked and non-live objects are unmarked.
+
friend class RootMarkingVisitor;
friend class MarkingVisitor;
@@ -206,7 +203,6 @@
#ifdef DEBUG
static void UpdateLiveObjectCount(HeapObject* obj);
- static void VerifyHeapAfterMarkingPhase();
#endif
// We sweep the large object space in the same way whether we are
@@ -216,30 +212,50 @@
// Test whether a (possibly marked) object is a Map.
static inline bool SafeIsMap(HeapObject* object);
- // Map transitions from a live map to a dead map must be killed.
+ // Map transitions from a live map to a dead map must be killed.
// We replace them with a null descriptor, with the same key.
static void ClearNonLiveTransitions();
- // --------------------------------------------------------------------------
- // Phase 2: functions related to computing and encoding forwarding pointers
- // before: live objects' map pointers are marked as '00'
- // after: Map pointers of live old and map objects have encoded
- // forwarding pointers and map pointers
+ // -----------------------------------------------------------------------
+ // Phase 2: Sweeping to clear mark bits and free non-live objects for
+ // a non-compacting collection, or else computing and encoding
+ // forwarding addresses for a compacting collection.
//
- // The 3rd word of a page has the page top offset after compaction.
+ // Before: Live objects are marked and non-live objects are unmarked.
//
- // The 4th word of a page in the map space has the map index
- // of this page in the map table. This word is not used in
- // the old space.
+ // After: (Non-compacting collection.) Live objects are unmarked,
+ // non-live regions have been added to their space's free
+ // list.
//
- // The 5th and 6th words of a page have the start and end
- // addresses of the first free region in the page.
+ // After: (Compacting collection.) The forwarding address of live
+ // objects in the paged spaces is encoded in their map word
+ // along with their (non-forwarded) map pointer.
//
- // The 7th word of a page in old spaces has the forwarding address
- // of the first live object in the page.
+ // The forwarding address of live objects in the new space is
+ // written to their map word's offset in the inactive
+ // semispace.
//
- // Live young objects have their forwarding pointers in
- // the from space at the same offset to the beginning of the space.
+ // Bookkeeping data is written to the remembered-set are of
+ // eached paged-space page that contains live objects after
+ // compaction:
+ //
+ // The 3rd word of the page (first word of the remembered
+ // set) contains the relocation top address, the address of
+ // the first word after the end of the last live object in
+ // the page after compaction.
+ //
+ // The 4th word contains the zero-based index of the page in
+ // its space. This word is only used for map space pages, in
+ // order to encode the map addresses in 21 bits to free 11
+ // bits per map word for the forwarding address.
+ //
+ // The 5th word contains the (nonencoded) forwarding address
+ // of the first live object in the page.
+ //
+ // In both the new space and the paged spaces, a linked list
+ // of live regions is constructructed (linked through
+ // pointers in the non-live region immediately following each
+ // live region) to speed further passes of the collector.
// Encodes forwarding addresses of objects in compactable parts of the
// heap.
@@ -272,19 +288,21 @@
static void DeallocateCodeBlock(Address start, int size_in_bytes);
static void DeallocateMapBlock(Address start, int size_in_bytes);
- // Phase 2: If we are not compacting the heap, we simply sweep the spaces
- // except for the large object space, clearing mark bits and adding
- // unmarked regions to each space's free list.
+ // If we are not compacting the heap, we simply sweep the spaces except
+ // for the large object space, clearing mark bits and adding unmarked
+ // regions to each space's free list.
static void SweepSpaces();
-#ifdef DEBUG
- static void VerifyHeapAfterEncodingForwardingAddresses();
-#endif
-
- // --------------------------------------------------------------------------
- // Phase 3: function related to updating pointers and decode map pointers
- // before: see after phase 2
- // after: all pointers are updated to forwarding addresses.
+ // -----------------------------------------------------------------------
+ // Phase 3: Updating pointers in live objects.
+ //
+ // Before: Same as after phase 2 (compacting collection).
+ //
+ // After: All pointers in live objects, including encoded map
+ // pointers, are updated to point to their target's new
+ // location. The remembered set area of each paged-space
+ // page containing live objects still contains bookkeeping
+ // information.
friend class UpdatingVisitor; // helper for updating visited objects
@@ -302,14 +320,17 @@
// Calculates the forwarding address of an object in an old space.
static Address GetForwardingAddressInOldSpace(HeapObject* obj);
-#ifdef DEBUG
- static void VerifyHeapAfterUpdatingPointers();
-#endif
-
- // --------------------------------------------------------------------------
- // Phase 4: functions related to relocating objects
- // before: see after phase 3
- // after: heap is in a normal state, except remembered set is not built
+ // -----------------------------------------------------------------------
+ // Phase 4: Relocating objects.
+ //
+ // Before: Pointers to live objects are updated to point to their
+ // target's new location. The remembered set area of each
+ // paged-space page containing live objects still contains
+ // bookkeeping information.
+ //
+ // After: Objects have been moved to their new addresses. The
+ // remembered set area of each paged-space page containing
+ // live objects still contains bookkeeping information.
// Relocates objects in all spaces.
static void RelocateObjects();
@@ -334,18 +355,19 @@
// Copy a new object.
static int RelocateNewObject(HeapObject* obj);
-#ifdef DEBUG
- static void VerifyHeapAfterRelocatingObjects();
-#endif
-
- // ---------------------------------------------------------------------------
- // Phase 5: functions related to rebuilding remembered sets
+ // -----------------------------------------------------------------------
+ // Phase 5: Rebuilding remembered sets.
+ //
+ // Before: The heap is in a normal state except that remembered sets
+ // in the paged spaces are not correct.
+ //
+ // After: The heap is in a normal state.
// Rebuild remembered set in old and map spaces.
static void RebuildRSets();
#ifdef DEBUG
- // ---------------------------------------------------------------------------
+ // -----------------------------------------------------------------------
// Debugging variables, functions and classes
// Counters used for debugging the marking phase of mark-compact or
// mark-sweep collection.
@@ -371,12 +393,6 @@
// Number of live bytes in this collection.
static int live_bytes_;
- static void VerifyPageHeaders(PagedSpace* space);
-
- // Verification functions when relocating objects.
- friend class VerifyCopyingVisitor;
- static void VerifyCopyingObjects(Object** p);
-
friend class MarkObjectVisitor;
static void VisitObject(HeapObject* obj);
diff --git a/src/messages.cc b/src/messages.cc
index e6a5084..ca0ce2a 100644
--- a/src/messages.cc
+++ b/src/messages.cc
@@ -66,7 +66,7 @@
Vector< Handle<Object> > args,
Handle<String> stack_trace) {
// Build error message object
- HandleScope scope;
+ v8::HandleScope scope; // Instantiate a closeable HandleScope for EscapeFrom.
Handle<Object> type_str = Factory::LookupAsciiSymbol(type);
Handle<Object> array = Factory::NewJSArray(args.length());
for (int i = 0; i < args.length(); i++)
diff --git a/src/mirror-delay.js b/src/mirror-delay.js
index fe6fb9c..2482029 100644
--- a/src/mirror-delay.js
+++ b/src/mirror-delay.js
@@ -58,6 +58,11 @@
if (mirror.value() === value) {
return mirror;
}
+ // Special check for NaN as NaN == NaN is false.
+ if (mirror.isNumber() && isNaN(mirror.value()) &&
+ typeof value == 'number' && isNaN(value)) {
+ return mirror;
+ }
}
if (IS_UNDEFINED(value)) {
@@ -90,6 +95,18 @@
/**
+ * Returns the mirror for a specified mirror handle.
+ *
+ * @param {number} handle the handle to find the mirror for
+ * @returns {Mirror or undefiend} the mirror with the requested handle or
+ * undefined if no mirror with the requested handle was found
+ */
+function LookupMirror(handle) {
+ return mirror_cache_[handle];
+}
+
+
+/**
* Returns the mirror for the undefined value.
*
* @returns {Mirror} the mirror reflects the undefined value
@@ -342,6 +359,14 @@
}
+/**
+ * Allocate a handle id for this object.
+ */
+Mirror.prototype.allocateHandle_ = function() {
+ this.handle_ = next_handle_++;
+}
+
+
Mirror.prototype.toText = function() {
// Simpel to text which is used when on specialization in subclass.
return "#<" + builtins.GetInstanceName(this.constructor.name) + ">";
@@ -357,8 +382,8 @@
*/
function ValueMirror(type, value) {
Mirror.call(this, type);
- this.handle_ = next_handle_++;
this.value_ = value;
+ this.allocateHandle_();
}
inherits(ValueMirror, Mirror);
@@ -512,7 +537,7 @@
ObjectMirror.prototype.protoObject = function() {
- return MakeMirror(%GetPrototype(this.value_));
+ return MakeMirror(%DebugGetPrototype(this.value_));
};
@@ -1516,6 +1541,7 @@
function ScriptMirror(script) {
Mirror.call(this, SCRIPT_TYPE);
this.script_ = script;
+ this.allocateHandle_();
}
inherits(ScriptMirror, Mirror);
@@ -1656,9 +1682,10 @@
JSONProtocolSerializer.prototype.serialize_ = function(mirror, reference,
details) {
- // If serializing a reference to a value just return the reference and add the
- // mirror to the referenced mirrors.
- if (reference && mirror.isValue()) {
+ // If serializing a reference to a mirror just return the reference and add
+ // the mirror to the referenced mirrors.
+ if (reference &&
+ (mirror.isValue() || mirror.isScript())) {
this.add_(mirror);
return '{"ref":' + mirror.handle() + '}';
}
@@ -1785,7 +1812,7 @@
content.push(MakeJSONPair_('source', StringToJSON_(mirror.source())));
}
if (mirror.script()) {
- content.push(MakeJSONPair_('script', this.serializeValue(mirror.script())));
+ content.push(MakeJSONPair_('script', this.serializeReference(mirror.script())));
}
}
diff --git a/src/mksnapshot.cc b/src/mksnapshot.cc
index 441ae18..a1c9850 100644
--- a/src/mksnapshot.cc
+++ b/src/mksnapshot.cc
@@ -114,7 +114,7 @@
// Write C++ code that defines Snapshot::snapshot_ to contain the snapshot
// to the file given by filename. Only the first size chars are written.
static int WriteInternalSnapshotToFile(const char* filename,
- const char* str,
+ const v8::internal::byte* bytes,
int size) {
FILE* f = i::OS::FOpen(filename, "wb");
if (f == NULL) {
@@ -126,11 +126,11 @@
fprintf(f, "#include \"platform.h\"\n\n");
fprintf(f, "#include \"snapshot.h\"\n\n");
fprintf(f, "namespace v8 {\nnamespace internal {\n\n");
- fprintf(f, "const char Snapshot::data_[] = {");
+ fprintf(f, "const byte Snapshot::data_[] = {");
int written = 0;
- written += fprintf(f, "%i", str[0]);
+ written += fprintf(f, "0x%x", bytes[0]);
for (int i = 1; i < size; ++i) {
- written += fprintf(f, ",%i", str[i]);
+ written += fprintf(f, ",0x%x", bytes[i]);
// The following is needed to keep the line length low on Visual C++:
if (i % 512 == 0) fprintf(f, "\n");
}
@@ -174,13 +174,13 @@
i::Heap::CollectAllGarbage();
i::Serializer ser;
ser.Serialize();
- char* str;
+ v8::internal::byte* bytes;
int len;
- ser.Finalize(&str, &len);
+ ser.Finalize(&bytes, &len);
- WriteInternalSnapshotToFile(argv[1], str, len);
+ WriteInternalSnapshotToFile(argv[1], bytes, len);
- i::DeleteArray(str);
+ i::DeleteArray(bytes);
return 0;
}
diff --git a/src/objects-debug.cc b/src/objects-debug.cc
index 6b58f25..d3ac805 100644
--- a/src/objects-debug.cc
+++ b/src/objects-debug.cc
@@ -281,6 +281,10 @@
PrintF(" (callback)\n");
} else if (r.type() == MAP_TRANSITION) {
PrintF(" (map transition)\n");
+ } else if (r.type() == CONSTANT_TRANSITION) {
+ PrintF(" (constant transition)\n");
+ } else if (r.type() == NULL_DESCRIPTOR) {
+ PrintF(" (null descriptor)\n");
} else {
UNREACHABLE();
}
@@ -410,6 +414,24 @@
PrintF(" - type: %s\n", TypeToString(instance_type()));
PrintF(" - instance size: %d\n", instance_size());
PrintF(" - unused property fields: %d\n", unused_property_fields());
+ if (is_hidden_prototype()) {
+ PrintF(" - hidden_prototype\n");
+ }
+ if (has_named_interceptor()) {
+ PrintF(" - named_interceptor\n");
+ }
+ if (has_indexed_interceptor()) {
+ PrintF(" - indexed_interceptor\n");
+ }
+ if (is_undetectable()) {
+ PrintF(" - undetectable\n");
+ }
+ if (has_instance_call_handler()) {
+ PrintF(" - instance_call_handler\n");
+ }
+ if (is_access_check_needed()) {
+ PrintF(" - access_check_needed\n");
+ }
PrintF(" - instance descriptors: ");
instance_descriptors()->ShortPrint();
PrintF("\n - prototype: ");
diff --git a/src/objects-inl.h b/src/objects-inl.h
index 43f1a04..22d126a 100644
--- a/src/objects-inl.h
+++ b/src/objects-inl.h
@@ -1431,13 +1431,14 @@
}
-void String::TryFlatten(StringShape shape) {
+Object* String::TryFlattenIfNotFlat(StringShape shape) {
ASSERT(shape.type() == StringShape(this).type());
// We don't need to flatten strings that are already flat. Since this code
// is inlined, it can be helpful in the flat case to not call out to Flatten.
if (!IsFlat(shape)) {
- Flatten(shape);
+ return TryFlatten(shape);
}
+ return this;
}
diff --git a/src/objects.cc b/src/objects.cc
index 352f5bd..1aaf736 100644
--- a/src/objects.cc
+++ b/src/objects.cc
@@ -587,7 +587,7 @@
}
-Object* String::Flatten(StringShape shape) {
+Object* String::TryFlatten(StringShape shape) {
#ifdef DEBUG
// Do not attempt to flatten in debug mode when allocation is not
// allowed. This is to avoid an assertion failure when allocating.
@@ -604,11 +604,11 @@
// SlicedStrings.
String* buf = ss->buffer();
ASSERT(!buf->IsSlicedString());
- Object* ok = buf->Flatten(StringShape(buf));
+ Object* ok = buf->TryFlatten(StringShape(buf));
if (ok->IsFailure()) return ok;
- // Under certain circumstances (TryFlatten fails in String::Slice)
- // we can have a cons string under a slice. In this case we need
- // to get the flat string out of the cons!
+ // Under certain circumstances (TryFlattenIfNotFlat fails in
+ // String::Slice) we can have a cons string under a slice.
+ // In this case we need to get the flat string out of the cons!
if (StringShape(String::cast(ok)).IsCons()) {
ss->set_buffer(ConsString::cast(ok)->first());
}
@@ -2413,8 +2413,8 @@
return Heap::undefined_value();
}
- // TryFlatten before operating on the string.
- name->TryFlatten(StringShape(name));
+ // Try to flatten before operating on the string.
+ name->TryFlattenIfNotFlat(StringShape(name));
// Make sure name is not an index.
uint32_t index;
@@ -3065,9 +3065,7 @@
// doesn't make Utf8Length faster, but it is very likely that
// the string will be accessed later (for example by WriteUtf8)
// so it's still a good idea.
- if (!IsFlat(shape)) {
- TryFlatten(shape); // shape is now no longer valid.
- }
+ TryFlattenIfNotFlat(shape); // shape is now no longer valid.
Access<StringInputBuffer> buffer(&string_input_buffer);
buffer->Reset(0, this);
int result = 0;
@@ -5160,6 +5158,7 @@
uint32_t new_length = 0;
if (IsJSArray()) {
CHECK(Array::IndexFromObject(JSArray::cast(this)->length(), &new_length));
+ JSArray::cast(this)->set_length(Smi::FromInt(new_length));
} else {
new_length = Dictionary::cast(elements())->max_number_key() + 1;
}
@@ -5695,10 +5694,10 @@
// Fill in the names of local properties into the supplied storage. The main
// purpose of this function is to provide reflection information for the object
// mirrors.
-void JSObject::GetLocalPropertyNames(FixedArray* storage) {
- ASSERT(storage->length() ==
- NumberOfLocalProperties(static_cast<PropertyAttributes>(NONE)));
- int index = 0;
+void JSObject::GetLocalPropertyNames(FixedArray* storage, int index) {
+ ASSERT(storage->length() >=
+ NumberOfLocalProperties(static_cast<PropertyAttributes>(NONE)) -
+ index);
if (HasFastProperties()) {
for (DescriptorReader r(map()->instance_descriptors());
!r.eos();
@@ -5707,7 +5706,7 @@
storage->set(index++, r.GetKey());
}
}
- ASSERT(storage->length() == index);
+ ASSERT(storage->length() >= index);
} else {
property_dictionary()->CopyKeysTo(storage);
}
diff --git a/src/objects.h b/src/objects.h
index b77c0b2..7a40b70 100644
--- a/src/objects.h
+++ b/src/objects.h
@@ -1298,8 +1298,9 @@
int NumberOfLocalProperties(PropertyAttributes filter);
// Returns the number of enumerable properties (ignoring interceptors).
int NumberOfEnumProperties();
- // Fill in details for properties into storage.
- void GetLocalPropertyNames(FixedArray* storage);
+ // Fill in details for properties into storage starting at the specified
+ // index.
+ void GetLocalPropertyNames(FixedArray* storage, int index);
// Returns the number of properties on this object filtering out properties
// with the specified attributes (ignoring interceptors).
@@ -2964,7 +2965,6 @@
// Dispatched behavior.
#ifdef DEBUG
- void JSRegExpPrint();
void JSRegExpVerify();
#endif
@@ -3130,15 +3130,17 @@
// to this method are not efficient unless the string is flat.
inline uint16_t Get(StringShape shape, int index);
- // Flatten the top level ConsString that is hiding behind this
+ // Try to flatten the top level ConsString that is hiding behind this
// string. This is a no-op unless the string is a ConsString or a
// SlicedString. Flatten mutates the ConsString and might return a
// failure.
- Object* Flatten(StringShape shape);
- // Try to flatten the string. Do not allow handling of allocation
- // failures. After calling TryFlatten, the string could still be a
- // ConsString.
- inline void TryFlatten(StringShape shape);
+ Object* TryFlatten(StringShape shape);
+
+ // Try to flatten the string. Checks first inline to see if it is necessary.
+ // Do not handle allocation failures. After calling TryFlattenIfNotFlat, the
+ // string could still be a ConsString, in which case a failure is returned.
+ // Use FlattenString from Handles.cc to be sure to flatten.
+ inline Object* TryFlattenIfNotFlat(StringShape shape);
Vector<const char> ToAsciiVector();
Vector<const uc16> ToUC16Vector();
diff --git a/src/parser.cc b/src/parser.cc
index 7f1bff2..333f1c7 100644
--- a/src/parser.cc
+++ b/src/parser.cc
@@ -1092,7 +1092,7 @@
Counters::total_parse_size.Increment(source->length(shape));
// Initialize parser state.
- source->TryFlatten(shape);
+ source->TryFlattenIfNotFlat(shape);
scanner_.Init(source, stream, 0);
ASSERT(target_stack_ == NULL);
@@ -1119,7 +1119,7 @@
temp_scope.materialized_literal_count(),
temp_scope.contains_array_literal(),
temp_scope.expected_property_count(),
- 0, 0, source->length(shape), false));
+ 0, 0, source->length(), false));
} else if (scanner().stack_overflow()) {
Top::StackOverflow();
}
@@ -1141,7 +1141,7 @@
bool is_expression) {
ZoneScope zone_scope(DONT_DELETE_ON_EXIT);
StatsRateScope timer(&Counters::parse_lazy);
- source->TryFlatten(StringShape(*source));
+ source->TryFlattenIfNotFlat(StringShape(*source));
StringShape shape(*source);
Counters::total_parse_size.Increment(source->length(shape));
SafeStringInputBuffer buffer(source.location());
@@ -4052,6 +4052,7 @@
Advance();
return '\v';
case 'c':
+ Advance();
return ParseControlLetterEscape();
case '0': case '1': case '2': case '3': case '4': case '5':
case '6': case '7':
@@ -4148,7 +4149,10 @@
} else {
ASSERT(type == '=' || type == '!');
bool is_positive = (type == '=');
- return new RegExpLookahead(body, is_positive);
+ return new RegExpLookahead(body,
+ is_positive,
+ end_capture_index - capture_index,
+ capture_index);
}
}
diff --git a/src/platform-freebsd.cc b/src/platform-freebsd.cc
index d7b3223..7b4febf 100644
--- a/src/platform-freebsd.cc
+++ b/src/platform-freebsd.cc
@@ -195,7 +195,19 @@
char* OS::StrNDup(const char* str, size_t n) {
- return strndup(str, n);
+ // Stupid implementation of strndup since freebsd isn't born with
+ // one.
+ size_t len = strlen(str);
+ if (len <= n) {
+ return StrDup(str);
+ }
+ char* result = new char[n+1];
+ size_t i;
+ for (i = 0; i <= n; i++) {
+ result[i] = str[i];
+ }
+ result[i] = '\0';
+ return result;
}
diff --git a/src/platform-macos.cc b/src/platform-macos.cc
index f625174..7fc71a5 100644
--- a/src/platform-macos.cc
+++ b/src/platform-macos.cc
@@ -199,12 +199,14 @@
// Stupid implementation of strndup since macos isn't born with
// one.
size_t len = strlen(str);
- if (len <= n)
+ if (len <= n) {
return StrDup(str);
+ }
char* result = new char[n+1];
size_t i;
- for (i = 0; i <= n; i++)
+ for (i = 0; i <= n; i++) {
result[i] = str[i];
+ }
result[i] = '\0';
return result;
}
diff --git a/src/platform-win32.cc b/src/platform-win32.cc
index 92fb536..71ba8ee 100644
--- a/src/platform-win32.cc
+++ b/src/platform-win32.cc
@@ -704,12 +704,14 @@
// Stupid implementation of strndup since windows isn't born with
// one.
size_t len = strlen(str);
- if (len <= n)
+ if (len <= n) {
return StrDup(str);
+ }
char* result = new char[n+1];
size_t i;
- for (i = 0; i <= n; i++)
+ for (i = 0; i <= n; i++) {
result[i] = str[i];
+ }
result[i] = '\0';
return result;
}
diff --git a/src/regexp-macro-assembler-ia32.cc b/src/regexp-macro-assembler-ia32.cc
index 824a297..e58177c 100644
--- a/src/regexp-macro-assembler-ia32.cc
+++ b/src/regexp-macro-assembler-ia32.cc
@@ -174,6 +174,20 @@
}
+void RegExpMacroAssemblerIA32::CheckAtStart(Label* on_at_start) {
+ Label ok;
+ // Did we start the match at the start of the string at all?
+ __ cmp(Operand(ebp, kAtStart), Immediate(0));
+ BranchOrBacktrack(equal, &ok);
+ // If we did, are we still at the start of the input?
+ __ mov(eax, Operand(ebp, kInputEndOffset));
+ __ add(eax, Operand(edi));
+ __ cmp(eax, Operand(ebp, kInputStartOffset));
+ BranchOrBacktrack(equal, on_at_start);
+ __ bind(&ok);
+}
+
+
void RegExpMacroAssemblerIA32::CheckNotAtStart(Label* on_not_at_start) {
// Did we start the match at the start of the string at all?
__ cmp(Operand(ebp, kAtStart), Immediate(0));
@@ -317,16 +331,30 @@
__ push(backtrack_stackpointer());
__ push(ebx);
const int four_arguments = 4;
- FrameAlign(four_arguments);
- // Put arguments into allocated stack area.
+ FrameAlign(four_arguments, ecx);
+ // Put arguments into allocated stack area, last argument highest on stack.
+ // Parameters are
+ // UC16** buffer - really the String** of the input string
+ // int byte_offset1 - byte offset from *buffer of start of capture
+ // int byte_offset2 - byte offset from *buffer of current position
+ // size_t byte_length - length of capture in bytes(!)
+
+ // Set byte_length.
__ mov(Operand(esp, 3 * kPointerSize), ebx);
+ // Set byte_offset2.
+ // Found by adding negative string-end offset of current position (edi)
+ // to String** offset of end of string.
__ mov(ecx, Operand(ebp, kInputEndOffset));
__ add(edi, Operand(ecx));
__ mov(Operand(esp, 2 * kPointerSize), edi);
+ // Set byte_offset1.
+ // Start of capture, where eax already holds string-end negative offset.
__ add(eax, Operand(ecx));
__ mov(Operand(esp, 1 * kPointerSize), eax);
+ // Set buffer. Original String** parameter to regexp code.
__ mov(eax, Operand(ebp, kInputBuffer));
__ mov(Operand(esp, 0 * kPointerSize), eax);
+
Address function_address = FUNCTION_ADDR(&CaseInsensitiveCompareUC16);
CallCFunction(function_address, four_arguments);
// Pop original values before reacting on result value.
@@ -632,7 +660,7 @@
__ bind(&stack_limit_hit);
int num_arguments = 2;
- FrameAlign(num_arguments);
+ FrameAlign(num_arguments, ebx);
__ mov(Operand(esp, 1 * kPointerSize), Immediate(self_));
__ lea(eax, Operand(esp, -kPointerSize));
__ mov(Operand(esp, 0 * kPointerSize), eax);
@@ -755,7 +783,7 @@
__ bind(&retry);
int num_arguments = 2;
- FrameAlign(num_arguments);
+ FrameAlign(num_arguments, ebx);
__ mov(Operand(esp, 1 * kPointerSize), Immediate(self_));
__ lea(eax, Operand(esp, -kPointerSize));
__ mov(Operand(esp, 0 * kPointerSize), eax);
@@ -791,7 +819,7 @@
// Call GrowStack(backtrack_stackpointer())
int num_arguments = 1;
- FrameAlign(num_arguments);
+ FrameAlign(num_arguments, ebx);
__ mov(Operand(esp, 0), backtrack_stackpointer());
CallCFunction(FUNCTION_ADDR(&GrowStack), num_arguments);
// If return NULL, we have failed to grow the stack, and
@@ -863,7 +891,7 @@
Label* on_end_of_input,
bool check_bounds,
int characters) {
- ASSERT(cp_offset >= 0);
+ ASSERT(cp_offset >= -1); // ^ and \b can look behind one character.
ASSERT(cp_offset < (1<<30)); // Be sane! (And ensure negation works)
CheckPosition(cp_offset + characters - 1, on_end_of_input);
LoadCurrentCharacterUnchecked(cp_offset, characters);
@@ -932,9 +960,12 @@
}
-void RegExpMacroAssemblerIA32::ClearRegister(int reg) {
+void RegExpMacroAssemblerIA32::ClearRegisters(int reg_from, int reg_to) {
+ ASSERT(reg_from <= reg_to);
__ mov(eax, Operand(ebp, kInputStartMinusOne));
- __ mov(register_location(reg), eax);
+ for (int reg = reg_from; reg <= reg_to; reg++) {
+ __ mov(register_location(reg), eax);
+ }
}
@@ -973,8 +1004,8 @@
stack_top);
if (result < 0 && !Top::has_pending_exception()) {
- // We detected a stack overflow in RegExp code, but haven't created
- // the exception yet.
+ // We detected a stack overflow (on the backtrack stack) in RegExp code,
+ // but haven't created the exception yet.
Top::StackOverflow();
}
return (result < 0) ? EXCEPTION : (result ? SUCCESS : FAILURE);
@@ -1155,16 +1186,19 @@
}
-void RegExpMacroAssemblerIA32::FrameAlign(int num_arguments) {
+void RegExpMacroAssemblerIA32::FrameAlign(int num_arguments, Register scratch) {
+ // TODO(lrn): Since we no longer use the system stack arbitrarily, we
+ // know the current stack alignment - esp points to the last regexp register.
+ // We can do this simpler then.
int frameAlignment = OS::ActivationFrameAlignment();
if (frameAlignment != 0) {
// Make stack end at alignment and make room for num_arguments words
// and the original value of esp.
- __ mov(ebx, esp);
+ __ mov(scratch, esp);
__ sub(Operand(esp), Immediate((num_arguments + 1) * kPointerSize));
ASSERT(IsPowerOf2(frameAlignment));
__ and_(esp, -frameAlignment);
- __ mov(Operand(esp, num_arguments * kPointerSize), ebx);
+ __ mov(Operand(esp, num_arguments * kPointerSize), scratch);
} else {
__ sub(Operand(esp), Immediate(num_arguments * kPointerSize));
}
diff --git a/src/regexp-macro-assembler-ia32.h b/src/regexp-macro-assembler-ia32.h
index 8d28beb..75ab693 100644
--- a/src/regexp-macro-assembler-ia32.h
+++ b/src/regexp-macro-assembler-ia32.h
@@ -43,6 +43,7 @@
virtual void AdvanceRegister(int reg, int by);
virtual void Backtrack();
virtual void Bind(Label* label);
+ virtual void CheckAtStart(Label* on_at_start);
virtual void CheckBitmap(uc16 start, Label* bitmap, Label* on_zero);
virtual void CheckCharacter(uint32_t c, Label* on_equal);
virtual void CheckCharacterAfterAnd(uint32_t c,
@@ -70,6 +71,9 @@
uc16 minus,
uc16 mask,
Label* on_not_equal);
+ // Checks whether the given offset from the current position is before
+ // the end of the string.
+ virtual void CheckPosition(int cp_offset, Label* on_outside_input);
virtual bool CheckSpecialCharacterClass(uc16 type,
int cp_offset,
bool check_offset,
@@ -106,7 +110,7 @@
virtual void SetRegister(int register_index, int to);
virtual void Succeed();
virtual void WriteCurrentPositionToRegister(int reg, int cp_offset);
- virtual void ClearRegister(int reg);
+ virtual void ClearRegisters(int reg_from, int reg_to);
virtual void WriteStackPointerToRegister(int reg);
static Result Execute(Code* code,
@@ -170,10 +174,6 @@
// This function must not trigger a garbage collection.
static Address GrowStack(Address stack_top);
- // Checks whether the given offset from the current position is before
- // the end of the string.
- void CheckPosition(int cp_offset, Label* on_outside_input);
-
// The ebp-relative location of a regexp register.
Operand register_location(int register_index);
@@ -218,7 +218,9 @@
// etc., not pushed. The argument count assumes all arguments are word sized.
// Some compilers/platforms require the stack to be aligned when calling
// C++ code.
- inline void FrameAlign(int num_arguments);
+ // Needs a scratch register to do some arithmetic. This register will be
+ // trashed.
+ inline void FrameAlign(int num_arguments, Register scratch);
// Calls a C function and cleans up the space for arguments allocated
// by FrameAlign. The called function is not allowed to trigger a garbage
diff --git a/src/regexp-macro-assembler-irregexp-inl.h b/src/regexp-macro-assembler-irregexp-inl.h
index faf12da..24f32c5 100644
--- a/src/regexp-macro-assembler-irregexp-inl.h
+++ b/src/regexp-macro-assembler-irregexp-inl.h
@@ -36,12 +36,15 @@
namespace v8 { namespace internal {
-void RegExpMacroAssemblerIrregexp::Emit(uint32_t byte) {
+void RegExpMacroAssemblerIrregexp::Emit(uint32_t byte,
+ uint32_t twenty_four_bits) {
+ uint32_t word = ((twenty_four_bits << BYTECODE_SHIFT) | byte);
ASSERT(pc_ <= buffer_.length());
- if (pc_ == buffer_.length()) {
+ if (pc_ + 3 >= buffer_.length()) {
Expand();
}
- buffer_[pc_++] = byte;
+ *reinterpret_cast<uint32_t*>(buffer_.start() + pc_) = word;
+ pc_ += 4;
}
@@ -50,7 +53,7 @@
if (pc_ + 1 >= buffer_.length()) {
Expand();
}
- Store16(buffer_.start() + pc_, word);
+ *reinterpret_cast<uint16_t*>(buffer_.start() + pc_) = word;
pc_ += 2;
}
@@ -60,7 +63,7 @@
if (pc_ + 3 >= buffer_.length()) {
Expand();
}
- Store32(buffer_.start() + pc_, word);
+ *reinterpret_cast<uint32_t*>(buffer_.start() + pc_) = word;
pc_ += 4;
}
diff --git a/src/regexp-macro-assembler-irregexp.cc b/src/regexp-macro-assembler-irregexp.cc
index a36f0a7..155b197 100644
--- a/src/regexp-macro-assembler-irregexp.cc
+++ b/src/regexp-macro-assembler-irregexp.cc
@@ -60,8 +60,8 @@
int pos = l->pos();
while (pos != 0) {
int fixup = pos;
- pos = Load32(buffer_.start() + fixup);
- Store32(buffer_.start() + fixup, pc_);
+ pos = *reinterpret_cast<int32_t*>(buffer_.start() + fixup);
+ *reinterpret_cast<uint32_t*>(buffer_.start() + fixup) = pc_;
}
}
l->bind_to(pc_);
@@ -84,8 +84,9 @@
void RegExpMacroAssemblerIrregexp::PopRegister(int register_index) {
- Emit(BC_POP_REGISTER);
- Emit(register_index);
+ ASSERT(register_index >= 0);
+ ASSERT(register_index <= kMaxRegister);
+ Emit(BC_POP_REGISTER, register_index);
}
@@ -93,112 +94,115 @@
int register_index,
StackCheckFlag check_stack_limit) {
ASSERT(register_index >= 0);
- Emit(BC_PUSH_REGISTER);
- Emit(register_index);
+ ASSERT(register_index <= kMaxRegister);
+ Emit(BC_PUSH_REGISTER, register_index);
}
void RegExpMacroAssemblerIrregexp::WriteCurrentPositionToRegister(
int register_index, int cp_offset) {
ASSERT(register_index >= 0);
- Emit(BC_SET_REGISTER_TO_CP);
- Emit(register_index);
+ ASSERT(register_index <= kMaxRegister);
+ Emit(BC_SET_REGISTER_TO_CP, register_index);
Emit32(cp_offset); // Current position offset.
}
-void RegExpMacroAssemblerIrregexp::ClearRegister(int reg) {
- SetRegister(reg, -1);
+void RegExpMacroAssemblerIrregexp::ClearRegisters(int reg_from, int reg_to) {
+ ASSERT(reg_from <= reg_to);
+ for (int reg = reg_from; reg <= reg_to; reg++) {
+ SetRegister(reg, -1);
+ }
}
void RegExpMacroAssemblerIrregexp::ReadCurrentPositionFromRegister(
int register_index) {
ASSERT(register_index >= 0);
- Emit(BC_SET_CP_TO_REGISTER);
- Emit(register_index);
+ ASSERT(register_index <= kMaxRegister);
+ Emit(BC_SET_CP_TO_REGISTER, register_index);
}
void RegExpMacroAssemblerIrregexp::WriteStackPointerToRegister(
int register_index) {
ASSERT(register_index >= 0);
- Emit(BC_SET_REGISTER_TO_SP);
- Emit(register_index);
+ ASSERT(register_index <= kMaxRegister);
+ Emit(BC_SET_REGISTER_TO_SP, register_index);
}
void RegExpMacroAssemblerIrregexp::ReadStackPointerFromRegister(
int register_index) {
ASSERT(register_index >= 0);
- Emit(BC_SET_SP_TO_REGISTER);
- Emit(register_index);
+ ASSERT(register_index <= kMaxRegister);
+ Emit(BC_SET_SP_TO_REGISTER, register_index);
}
void RegExpMacroAssemblerIrregexp::SetRegister(int register_index, int to) {
ASSERT(register_index >= 0);
- Emit(BC_SET_REGISTER);
- Emit(register_index);
+ ASSERT(register_index <= kMaxRegister);
+ Emit(BC_SET_REGISTER, register_index);
Emit32(to);
}
void RegExpMacroAssemblerIrregexp::AdvanceRegister(int register_index, int by) {
ASSERT(register_index >= 0);
- Emit(BC_ADVANCE_REGISTER);
- Emit(register_index);
+ ASSERT(register_index <= kMaxRegister);
+ Emit(BC_ADVANCE_REGISTER, register_index);
Emit32(by);
}
void RegExpMacroAssemblerIrregexp::PopCurrentPosition() {
- Emit(BC_POP_CP);
+ Emit(BC_POP_CP, 0);
}
void RegExpMacroAssemblerIrregexp::PushCurrentPosition() {
- Emit(BC_PUSH_CP);
- Emit32(0); // Current position offset.
+ Emit(BC_PUSH_CP, 0);
}
void RegExpMacroAssemblerIrregexp::Backtrack() {
- Emit(BC_POP_BT);
+ Emit(BC_POP_BT, 0);
}
void RegExpMacroAssemblerIrregexp::GoTo(Label* l) {
- Emit(BC_GOTO);
+ Emit(BC_GOTO, 0);
EmitOrLink(l);
}
void RegExpMacroAssemblerIrregexp::PushBacktrack(Label* l) {
- Emit(BC_PUSH_BT);
+ Emit(BC_PUSH_BT, 0);
EmitOrLink(l);
}
void RegExpMacroAssemblerIrregexp::Succeed() {
- Emit(BC_SUCCEED);
+ Emit(BC_SUCCEED, 0);
}
void RegExpMacroAssemblerIrregexp::Fail() {
- Emit(BC_FAIL);
+ Emit(BC_FAIL, 0);
}
void RegExpMacroAssemblerIrregexp::AdvanceCurrentPosition(int by) {
- Emit(BC_ADVANCE_CP);
- Emit32(by);
+ ASSERT(by >= kMinCPOffset);
+ ASSERT(by <= kMaxCPOffset);
+ Emit(BC_ADVANCE_CP, by);
}
void RegExpMacroAssemblerIrregexp::CheckGreedyLoop(
Label* on_tos_equals_current_position) {
- Emit(BC_CHECK_GREEDY);
+ Emit(BC_CHECK_GREEDY, 0);
EmitOrLink(on_tos_equals_current_position);
}
@@ -207,6 +211,8 @@
Label* on_failure,
bool check_bounds,
int characters) {
+ ASSERT(cp_offset >= kMinCPOffset);
+ ASSERT(cp_offset <= kMaxCPOffset);
int bytecode;
if (check_bounds) {
if (characters == 4) {
@@ -227,45 +233,56 @@
bytecode = BC_LOAD_CURRENT_CHAR_UNCHECKED;
}
}
- Emit(bytecode);
- Emit32(cp_offset);
+ Emit(bytecode, cp_offset);
if (check_bounds) EmitOrLink(on_failure);
}
void RegExpMacroAssemblerIrregexp::CheckCharacterLT(uc16 limit,
Label* on_less) {
- Emit(BC_CHECK_LT);
- Emit16(limit);
+ Emit(BC_CHECK_LT, limit);
EmitOrLink(on_less);
}
void RegExpMacroAssemblerIrregexp::CheckCharacterGT(uc16 limit,
Label* on_greater) {
- Emit(BC_CHECK_GT);
- Emit16(limit);
+ Emit(BC_CHECK_GT, limit);
EmitOrLink(on_greater);
}
void RegExpMacroAssemblerIrregexp::CheckCharacter(uint32_t c, Label* on_equal) {
- Emit(BC_CHECK_CHAR);
- Emit32(c);
+ if (c > MAX_FIRST_ARG) {
+ Emit(BC_CHECK_4_CHARS, 0);
+ Emit32(c);
+ } else {
+ Emit(BC_CHECK_CHAR, c);
+ }
EmitOrLink(on_equal);
}
+void RegExpMacroAssemblerIrregexp::CheckAtStart(Label* on_at_start) {
+ Emit(BC_CHECK_AT_START, 0);
+ EmitOrLink(on_at_start);
+}
+
+
void RegExpMacroAssemblerIrregexp::CheckNotAtStart(Label* on_not_at_start) {
- Emit(BC_CHECK_NOT_AT_START);
+ Emit(BC_CHECK_NOT_AT_START, 0);
EmitOrLink(on_not_at_start);
}
void RegExpMacroAssemblerIrregexp::CheckNotCharacter(uint32_t c,
Label* on_not_equal) {
- Emit(BC_CHECK_NOT_CHAR);
- Emit32(c);
+ if (c > MAX_FIRST_ARG) {
+ Emit(BC_CHECK_NOT_4_CHARS, 0);
+ Emit32(c);
+ } else {
+ Emit(BC_CHECK_NOT_CHAR, c);
+ }
EmitOrLink(on_not_equal);
}
@@ -274,8 +291,12 @@
uint32_t c,
uint32_t mask,
Label* on_equal) {
- Emit(BC_AND_CHECK_CHAR);
- Emit32(c);
+ if (c > MAX_FIRST_ARG) {
+ Emit(BC_AND_CHECK_4_CHARS, 0);
+ Emit32(c);
+ } else {
+ Emit(BC_AND_CHECK_CHAR, c);
+ }
Emit32(mask);
EmitOrLink(on_equal);
}
@@ -285,8 +306,12 @@
uint32_t c,
uint32_t mask,
Label* on_not_equal) {
- Emit(BC_AND_CHECK_NOT_CHAR);
- Emit32(c);
+ if (c > MAX_FIRST_ARG) {
+ Emit(BC_AND_CHECK_NOT_4_CHARS, 0);
+ Emit32(c);
+ } else {
+ Emit(BC_AND_CHECK_NOT_CHAR, c);
+ }
Emit32(mask);
EmitOrLink(on_not_equal);
}
@@ -297,8 +322,7 @@
uc16 minus,
uc16 mask,
Label* on_not_equal) {
- Emit(BC_MINUS_AND_CHECK_NOT_CHAR);
- Emit16(c);
+ Emit(BC_MINUS_AND_CHECK_NOT_CHAR, c);
Emit16(minus);
Emit16(mask);
EmitOrLink(on_not_equal);
@@ -307,8 +331,9 @@
void RegExpMacroAssemblerIrregexp::CheckNotBackReference(int start_reg,
Label* on_not_equal) {
- Emit(BC_CHECK_NOT_BACK_REF);
- Emit(start_reg);
+ ASSERT(start_reg >= 0);
+ ASSERT(start_reg <= kMaxRegister);
+ Emit(BC_CHECK_NOT_BACK_REF, start_reg);
EmitOrLink(on_not_equal);
}
@@ -316,8 +341,9 @@
void RegExpMacroAssemblerIrregexp::CheckNotBackReferenceIgnoreCase(
int start_reg,
Label* on_not_equal) {
- Emit(BC_CHECK_NOT_BACK_REF_NO_CASE);
- Emit(start_reg);
+ ASSERT(start_reg >= 0);
+ ASSERT(start_reg <= kMaxRegister);
+ Emit(BC_CHECK_NOT_BACK_REF_NO_CASE, start_reg);
EmitOrLink(on_not_equal);
}
@@ -325,9 +351,10 @@
void RegExpMacroAssemblerIrregexp::CheckNotRegistersEqual(int reg1,
int reg2,
Label* on_not_equal) {
- Emit(BC_CHECK_NOT_REGS_EQUAL);
- Emit(reg1);
- Emit(reg2);
+ ASSERT(reg1 >= 0);
+ ASSERT(reg1 <= kMaxRegister);
+ Emit(BC_CHECK_NOT_REGS_EQUAL, reg1);
+ Emit32(reg2);
EmitOrLink(on_not_equal);
}
@@ -368,19 +395,18 @@
int cp_offset,
Label* on_failure,
bool check_end_of_string) {
+ ASSERT(cp_offset >= kMinCPOffset);
+ ASSERT(cp_offset + str.length() - 1 <= kMaxCPOffset);
// It is vital that this loop is backwards due to the unchecked character
// load below.
for (int i = str.length() - 1; i >= 0; i--) {
if (check_end_of_string && i == str.length() - 1) {
- Emit(BC_LOAD_CURRENT_CHAR);
- Emit32(cp_offset + i);
+ Emit(BC_LOAD_CURRENT_CHAR, cp_offset + i);
EmitOrLink(on_failure);
} else {
- Emit(BC_LOAD_CURRENT_CHAR_UNCHECKED);
- Emit32(cp_offset + i);
+ Emit(BC_LOAD_CURRENT_CHAR_UNCHECKED, cp_offset + i);
}
- Emit(BC_CHECK_NOT_CHAR);
- Emit32(str[i]);
+ Emit(BC_CHECK_NOT_CHAR, str[i]);
EmitOrLink(on_failure);
}
}
@@ -389,10 +415,10 @@
void RegExpMacroAssemblerIrregexp::IfRegisterLT(int register_index,
int comparand,
Label* on_less_than) {
- ASSERT(comparand >= 0 && comparand <= 65535);
- Emit(BC_CHECK_REGISTER_LT);
- Emit(register_index);
- Emit16(comparand);
+ ASSERT(register_index >= 0);
+ ASSERT(register_index <= kMaxRegister);
+ Emit(BC_CHECK_REGISTER_LT, register_index);
+ Emit32(comparand);
EmitOrLink(on_less_than);
}
@@ -400,25 +426,26 @@
void RegExpMacroAssemblerIrregexp::IfRegisterGE(int register_index,
int comparand,
Label* on_greater_or_equal) {
- ASSERT(comparand >= 0 && comparand <= 65535);
- Emit(BC_CHECK_REGISTER_GE);
- Emit(register_index);
- Emit16(comparand);
+ ASSERT(register_index >= 0);
+ ASSERT(register_index <= kMaxRegister);
+ Emit(BC_CHECK_REGISTER_GE, register_index);
+ Emit32(comparand);
EmitOrLink(on_greater_or_equal);
}
void RegExpMacroAssemblerIrregexp::IfRegisterEqPos(int register_index,
Label* on_eq) {
- Emit(BC_CHECK_REGISTER_EQ_POS);
- Emit(register_index);
+ ASSERT(register_index >= 0);
+ ASSERT(register_index <= kMaxRegister);
+ Emit(BC_CHECK_REGISTER_EQ_POS, register_index);
EmitOrLink(on_eq);
}
Handle<Object> RegExpMacroAssemblerIrregexp::GetCode(Handle<String> source) {
Bind(&backtrack_);
- Emit(BC_POP_BT);
+ Emit(BC_POP_BT, 0);
Handle<ByteArray> array = Factory::NewByteArray(length());
Copy(array->GetDataStartAddress());
return array;
diff --git a/src/regexp-macro-assembler-irregexp.h b/src/regexp-macro-assembler-irregexp.h
index 95b903c..0d5999f 100644
--- a/src/regexp-macro-assembler-irregexp.h
+++ b/src/regexp-macro-assembler-irregexp.h
@@ -66,7 +66,7 @@
virtual void AdvanceRegister(int reg, int by); // r[reg] += by.
virtual void SetRegister(int register_index, int to);
virtual void WriteCurrentPositionToRegister(int reg, int cp_offset);
- virtual void ClearRegister(int reg);
+ virtual void ClearRegisters(int reg_from, int reg_to);
virtual void ReadCurrentPositionFromRegister(int reg);
virtual void WriteStackPointerToRegister(int reg);
virtual void ReadStackPointerFromRegister(int reg);
@@ -81,6 +81,7 @@
virtual void CheckCharacterGT(uc16 limit, Label* on_greater);
virtual void CheckCharacterLT(uc16 limit, Label* on_less);
virtual void CheckGreedyLoop(Label* on_tos_equals_current_position);
+ virtual void CheckAtStart(Label* on_at_start);
virtual void CheckNotAtStart(Label* on_not_at_start);
virtual void CheckNotCharacter(uint32_t c, Label* on_not_equal);
virtual void CheckNotCharacterAfterAnd(uint32_t c,
@@ -119,7 +120,7 @@
// Code and bitmap emission.
inline void Emit32(uint32_t x);
inline void Emit16(uint32_t x);
- inline void Emit(uint32_t x);
+ inline void Emit(uint32_t bc, uint32_t arg);
// Bytecode buffer.
int length();
void Copy(Address a);
diff --git a/src/regexp-macro-assembler-tracer.cc b/src/regexp-macro-assembler-tracer.cc
index 5caf032..74345d8 100644
--- a/src/regexp-macro-assembler-tracer.cc
+++ b/src/regexp-macro-assembler-tracer.cc
@@ -150,9 +150,9 @@
}
-void RegExpMacroAssemblerTracer::ClearRegister(int reg) {
- PrintF(" ClearRegister(register=%d);\n", reg);
- assembler_->ClearRegister(reg);
+void RegExpMacroAssemblerTracer::ClearRegisters(int reg_from, int reg_to) {
+ PrintF(" ClearRegister(from=%d, to=%d);\n", reg_from, reg_to);
+ assembler_->ClearRegisters(reg_from, reg_to);
}
@@ -210,6 +210,12 @@
}
+void RegExpMacroAssemblerTracer::CheckAtStart(Label* on_at_start) {
+ PrintF(" CheckAtStart(label[%08x]);\n", on_at_start);
+ assembler_->CheckAtStart(on_at_start);
+}
+
+
void RegExpMacroAssemblerTracer::CheckNotAtStart(Label* on_not_at_start) {
PrintF(" CheckNotAtStart(label[%08x]);\n", on_not_at_start);
assembler_->CheckNotAtStart(on_not_at_start);
diff --git a/src/regexp-macro-assembler-tracer.h b/src/regexp-macro-assembler-tracer.h
index c8088a9..d3aeff7 100644
--- a/src/regexp-macro-assembler-tracer.h
+++ b/src/regexp-macro-assembler-tracer.h
@@ -41,6 +41,7 @@
virtual void AdvanceRegister(int reg, int by); // r[reg] += by.
virtual void Backtrack();
virtual void Bind(Label* label);
+ virtual void CheckAtStart(Label* on_at_start);
virtual void CheckBitmap(uc16 start, Label* bitmap, Label* on_zero);
virtual void CheckCharacter(uint32_t c, Label* on_equal);
virtual void CheckCharacterAfterAnd(uint32_t c,
@@ -106,7 +107,7 @@
virtual void SetRegister(int register_index, int to);
virtual void Succeed();
virtual void WriteCurrentPositionToRegister(int reg, int cp_offset);
- virtual void ClearRegister(int reg);
+ virtual void ClearRegisters(int reg_from, int reg_to);
virtual void WriteStackPointerToRegister(int reg);
private:
RegExpMacroAssembler* assembler_;
diff --git a/src/regexp-macro-assembler.h b/src/regexp-macro-assembler.h
index e9f7731..a3f398d 100644
--- a/src/regexp-macro-assembler.h
+++ b/src/regexp-macro-assembler.h
@@ -38,6 +38,10 @@
class RegExpMacroAssembler {
public:
+ // The implementation must be able to handle at least:
+ static const int kMaxRegister = (1 << 16) - 1;
+ static const int kMaxCPOffset = (1 << 15) - 1;
+ static const int kMinCPOffset = -(1 << 15);
enum IrregexpImplementation {
kIA32Implementation,
kARMImplementation,
@@ -61,6 +65,7 @@
// stack by an earlier PushBacktrack(Label*).
virtual void Backtrack() = 0;
virtual void Bind(Label* label) = 0;
+ virtual void CheckAtStart(Label* on_at_start) = 0;
// Check the current character against a bitmap. The range of the current
// character must be from start to start + length_of_bitmap_in_bits.
virtual void CheckBitmap(
@@ -110,6 +115,12 @@
virtual void CheckNotRegistersEqual(int reg1,
int reg2,
Label* on_not_equal) = 0;
+
+ // Checks whether the given offset from the current position is before
+ // the end of the string. May overwrite the current character.
+ virtual void CheckPosition(int cp_offset, Label* on_outside_input) {
+ LoadCurrentCharacter(cp_offset, on_outside_input, true);
+ }
// Check whether a standard/default character class matches the current
// character. Returns false if the type of special character class does
// not have custom support.
@@ -167,7 +178,7 @@
virtual void SetRegister(int register_index, int to) = 0;
virtual void Succeed() = 0;
virtual void WriteCurrentPositionToRegister(int reg, int cp_offset) = 0;
- virtual void ClearRegister(int reg) = 0;
+ virtual void ClearRegisters(int reg_from, int reg_to) = 0;
virtual void WriteStackPointerToRegister(int reg) = 0;
private:
diff --git a/src/runtime.cc b/src/runtime.cc
index fd36724..18d6c03 100644
--- a/src/runtime.cc
+++ b/src/runtime.cc
@@ -161,15 +161,16 @@
for (int index = 0; index < length; index +=2) {
Handle<Object> key(constant_properties->get(index+0));
Handle<Object> value(constant_properties->get(index+1));
+ Handle<Object> result;
uint32_t element_index = 0;
if (key->IsSymbol()) {
// If key is a symbol it is not an array element.
Handle<String> name(String::cast(*key));
ASSERT(!name->AsArrayIndex(&element_index));
- SetProperty(boilerplate, name, value, NONE);
+ result = SetProperty(boilerplate, name, value, NONE);
} else if (Array::IndexFromObject(*key, &element_index)) {
// Array index (uint32).
- SetElement(boilerplate, element_index, value);
+ result = SetElement(boilerplate, element_index, value);
} else {
// Non-uint32 number.
ASSERT(key->IsNumber());
@@ -178,8 +179,13 @@
Vector<char> buffer(arr, ARRAY_SIZE(arr));
const char* str = DoubleToCString(num, buffer);
Handle<String> name = Factory::NewStringFromAscii(CStrVector(str));
- SetProperty(boilerplate, name, value, NONE);
+ result = SetProperty(boilerplate, name, value, NONE);
}
+ // If setting the property on the boilerplate throws an
+ // exception, the exception is converted to an empty handle in
+ // the handle based operations. In that case, we need to
+ // convert back to an exception.
+ if (result.is_null()) return Failure::Exception();
}
}
@@ -758,56 +764,94 @@
int index;
PropertyAttributes attributes;
- ContextLookupFlags flags = DONT_FOLLOW_CHAINS;
+ ContextLookupFlags flags = FOLLOW_CHAINS;
Handle<Object> holder =
context->Lookup(name, flags, &index, &attributes);
- // The property should always be present. It is always declared
- // before being initialized through DeclareContextSlot.
- ASSERT(attributes != ABSENT && (attributes & READ_ONLY) != 0);
-
- // If the slot is in the context, we set it but only if it hasn't
- // been set before.
+ // In most situations, the property introduced by the const
+ // declaration should be present in the context extension object.
+ // However, because declaration and initialization are separate, the
+ // property might have been deleted (if it was introduced by eval)
+ // before we reach the initialization point.
+ //
+ // Example:
+ //
+ // function f() { eval("delete x; const x;"); }
+ //
+ // In that case, the initialization behaves like a normal assignment
+ // to property 'x'.
if (index >= 0) {
- // The constant context slot should always be in the function
- // context; not in any outer context nor in the arguments object.
- ASSERT(holder.is_identical_to(context));
- if (context->get(index)->IsTheHole()) {
- context->set(index, *value);
+ // Property was found in a context.
+ if (holder->IsContext()) {
+ // The holder cannot be the function context. If it is, there
+ // should have been a const redeclaration error when declaring
+ // the const property.
+ ASSERT(!holder.is_identical_to(context));
+ if ((attributes & READ_ONLY) == 0) {
+ Handle<Context>::cast(holder)->set(index, *value);
+ }
+ } else {
+ // The holder is an arguments object.
+ ASSERT((attributes & READ_ONLY) == 0);
+ Handle<JSObject>::cast(holder)->SetElement(index, *value);
}
return *value;
}
- // Otherwise, the slot must be in a JS object extension.
- Handle<JSObject> context_ext(JSObject::cast(*holder));
+ // The property could not be found, we introduce it in the global
+ // context.
+ if (attributes == ABSENT) {
+ Handle<JSObject> global = Handle<JSObject>(Top::context()->global());
+ SetProperty(global, name, value, NONE);
+ return *value;
+ }
- // We must initialize the value only if it wasn't initialized
- // before, e.g. for const declarations in a loop. The property has
- // the hole value if it wasn't initialized yet. NOTE: We cannot use
- // GetProperty() to get the current value as it 'unholes' the value.
- LookupResult lookup;
- context_ext->LocalLookupRealNamedProperty(*name, &lookup);
- ASSERT(lookup.IsProperty()); // the property was declared
- ASSERT(lookup.IsReadOnly()); // and it was declared as read-only
+ // The property was present in a context extension object.
+ Handle<JSObject> context_ext = Handle<JSObject>::cast(holder);
- PropertyType type = lookup.type();
- if (type == FIELD) {
- FixedArray* properties = context_ext->properties();
- int index = lookup.GetFieldIndex();
- if (properties->get(index)->IsTheHole()) {
- properties->set(index, *value);
- }
- } else if (type == NORMAL) {
- Dictionary* dictionary = context_ext->property_dictionary();
- int entry = lookup.GetDictionaryEntry();
- if (dictionary->ValueAt(entry)->IsTheHole()) {
- dictionary->ValueAtPut(entry, *value);
+ if (*context_ext == context->extension()) {
+ // This is the property that was introduced by the const
+ // declaration. Set it if it hasn't been set before. NOTE: We
+ // cannot use GetProperty() to get the current value as it
+ // 'unholes' the value.
+ LookupResult lookup;
+ context_ext->LocalLookupRealNamedProperty(*name, &lookup);
+ ASSERT(lookup.IsProperty()); // the property was declared
+ ASSERT(lookup.IsReadOnly()); // and it was declared as read-only
+
+ PropertyType type = lookup.type();
+ if (type == FIELD) {
+ FixedArray* properties = context_ext->properties();
+ int index = lookup.GetFieldIndex();
+ if (properties->get(index)->IsTheHole()) {
+ properties->set(index, *value);
+ }
+ } else if (type == NORMAL) {
+ Dictionary* dictionary = context_ext->property_dictionary();
+ int entry = lookup.GetDictionaryEntry();
+ if (dictionary->ValueAt(entry)->IsTheHole()) {
+ dictionary->ValueAtPut(entry, *value);
+ }
+ } else {
+ // We should not reach here. Any real, named property should be
+ // either a field or a dictionary slot.
+ UNREACHABLE();
}
} else {
- // We should not reach here. Any real, named property should be
- // either a field or a dictionary slot.
- UNREACHABLE();
+ // The property was found in a different context extension object.
+ // Set it if it is not a read-only property.
+ if ((attributes & READ_ONLY) == 0) {
+ Handle<Object> set = SetProperty(context_ext, name, value, attributes);
+ // Setting a property might throw an exception. Exceptions
+ // are converted to empty handles in handle operations. We
+ // need to convert back to exceptions here.
+ if (set.is_null()) {
+ ASSERT(Top::has_pending_exception());
+ return Failure::Exception();
+ }
+ }
}
+
return *value;
}
@@ -1022,7 +1066,7 @@
// Flatten the string. If someone wants to get a char at an index
// in a cons string, it is likely that more indices will be
// accessed.
- subject->TryFlatten(StringShape(subject));
+ subject->TryFlattenIfNotFlat(StringShape(subject));
StringShape shape(subject);
if (i >= static_cast<uint32_t>(subject->length(shape))) {
return Heap::nan_value();
@@ -1493,8 +1537,8 @@
CONVERT_CHECKED(String, pat, args[1]);
Object* index = args[2];
- sub->TryFlatten(StringShape(sub));
- pat->TryFlatten(StringShape(pat));
+ sub->TryFlattenIfNotFlat(StringShape(sub));
+ pat->TryFlattenIfNotFlat(StringShape(pat));
StringShape sub_shape(sub);
StringShape pat_shape(pat);
@@ -1553,8 +1597,8 @@
int d = str1->Get(shape1, 0) - str2->Get(shape2, 0);
if (d != 0) return Smi::FromInt(d);
- str1->TryFlatten(shape1); // Shapes are no longer valid now!
- str2->TryFlatten(shape2);
+ str1->TryFlattenIfNotFlat(shape1); // Shapes are no longer valid now!
+ str2->TryFlattenIfNotFlat(shape2);
static StringInputBuffer buf1;
static StringInputBuffer buf2;
@@ -1691,7 +1735,7 @@
static Handle<Object> GetCharAt(Handle<String> string, uint32_t index) {
StringShape shape(*string);
if (index < static_cast<uint32_t>(string->length(shape))) {
- string->TryFlatten(shape); // Invalidates shape!
+ string->TryFlattenIfNotFlat(shape); // Invalidates shape!
return LookupSingleCharacterStringFromCode(
string->Get(StringShape(*string), index));
}
@@ -1884,7 +1928,7 @@
result = SetElement(js_object, index, value);
} else {
Handle<String> key_string = Handle<String>::cast(key);
- key_string->TryFlatten(StringShape(*key_string));
+ key_string->TryFlattenIfNotFlat(StringShape(*key_string));
result = SetProperty(js_object, key_string, value, attr);
}
if (result.is_null()) return Failure::Exception();
@@ -2156,7 +2200,7 @@
NoHandleAllocation ha;
ASSERT(args.length() == 1);
CONVERT_CHECKED(String, subject, args[0]);
- subject->TryFlatten(StringShape(subject));
+ subject->TryFlattenIfNotFlat(StringShape(subject));
return Heap::NumberFromDouble(StringToDouble(subject, ALLOW_HEX));
}
@@ -2239,7 +2283,7 @@
ASSERT(args.length() == 1);
CONVERT_CHECKED(String, source, args[0]);
- source->TryFlatten(StringShape(source));
+ source->TryFlattenIfNotFlat(StringShape(source));
int escaped_length = 0;
int length = source->length();
@@ -2353,7 +2397,7 @@
ASSERT(args.length() == 1);
CONVERT_CHECKED(String, source, args[0]);
- source->TryFlatten(StringShape(source));
+ source->TryFlattenIfNotFlat(StringShape(source));
StringShape source_shape(source);
bool ascii = true;
@@ -2402,7 +2446,7 @@
CONVERT_DOUBLE_CHECKED(n, args[1]);
int radix = FastD2I(n);
- s->TryFlatten(StringShape(s));
+ s->TryFlattenIfNotFlat(StringShape(s));
StringShape shape(s);
@@ -2475,7 +2519,7 @@
NoHandleAllocation ha;
CONVERT_CHECKED(String, s, args[0]);
- s->TryFlatten(StringShape(s));
+ s->TryFlattenIfNotFlat(StringShape(s));
StringShape shape(s);
int raw_string_length = s->length(shape);
@@ -3079,8 +3123,8 @@
if (d < 0) return Smi::FromInt(LESS);
else if (d > 0) return Smi::FromInt(GREATER);
- x->TryFlatten(x_shape); // Shapes are no longer valid!
- y->TryFlatten(y_shape);
+ x->TryFlattenIfNotFlat(x_shape); // Shapes are no longer valid!
+ y->TryFlattenIfNotFlat(y_shape);
static StringInputBuffer bufx;
static StringInputBuffer bufy;
@@ -3734,7 +3778,9 @@
ASSERT(args.length() == 1);
// First check if this is a real stack overflow.
- if (StackGuard::IsStackOverflow()) return Runtime_StackOverflow(args);
+ if (StackGuard::IsStackOverflow()) {
+ return Runtime_StackOverflow(args);
+ }
return Execution::HandleStackGuardInterrupt();
}
@@ -4536,6 +4582,21 @@
}
+// Find the length of the prototype chain that is to to handled as one. If a
+// prototype object is hidden it is to be viewed as part of the the object it
+// is prototype for.
+static int LocalPrototypeChainLength(JSObject* obj) {
+ int count = 1;
+ Object* proto = obj->GetPrototype();
+ while (proto->IsJSObject() &&
+ JSObject::cast(proto)->map()->is_hidden_prototype()) {
+ count++;
+ proto = JSObject::cast(proto)->GetPrototype();
+ }
+ return count;
+}
+
+
static Object* DebugLookupResultValue(Object* receiver, LookupResult* result,
bool* caught_exception) {
Object* value;
@@ -4611,6 +4672,13 @@
CONVERT_ARG_CHECKED(JSObject, obj, 0);
CONVERT_ARG_CHECKED(String, name, 1);
+ // Skip the global proxy as it has no properties and always delegates to the
+ // real global object.
+ if (obj->IsJSGlobalProxy()) {
+ obj = Handle<JSObject>(JSObject::cast(obj->GetPrototype()));
+ }
+
+
// Check if the name is trivially convertible to an index and get the element
// if so.
uint32_t index;
@@ -4621,9 +4689,22 @@
return *Factory::NewJSArrayWithElements(details);
}
- // Perform standard local lookup on the object.
+ // Find the number of objects making up this.
+ int length = LocalPrototypeChainLength(*obj);
+
+ // Try local lookup on each of the objects.
LookupResult result;
- obj->LocalLookup(*name, &result);
+ Handle<JSObject> jsproto = obj;
+ for (int i = 0; i < length; i++) {
+ jsproto->LocalLookup(*name, &result);
+ if (result.IsProperty()) {
+ break;
+ }
+ if (i < length - 1) {
+ jsproto = Handle<JSObject>(JSObject::cast(jsproto->GetPrototype()));
+ }
+ }
+
if (result.IsProperty()) {
bool caught_exception = false;
Handle<Object> value(DebugLookupResultValue(*obj, &result,
@@ -4676,9 +4757,43 @@
}
CONVERT_ARG_CHECKED(JSObject, obj, 0);
- int n = obj->NumberOfLocalProperties(static_cast<PropertyAttributes>(NONE));
- Handle<FixedArray> names = Factory::NewFixedArray(n);
- obj->GetLocalPropertyNames(*names);
+ // Skip the global proxy as it has no properties and always delegates to the
+ // real global object.
+ if (obj->IsJSGlobalProxy()) {
+ obj = Handle<JSObject>(JSObject::cast(obj->GetPrototype()));
+ }
+
+ // Find the number of objects making up this.
+ int length = LocalPrototypeChainLength(*obj);
+
+ // Find the number of local properties for each of the objects.
+ int* local_property_count = NewArray<int>(length);
+ int total_property_count = 0;
+ Handle<JSObject> jsproto = obj;
+ for (int i = 0; i < length; i++) {
+ int n;
+ n = jsproto->NumberOfLocalProperties(static_cast<PropertyAttributes>(NONE));
+ local_property_count[i] = n;
+ total_property_count += n;
+ if (i < length - 1) {
+ jsproto = Handle<JSObject>(JSObject::cast(jsproto->GetPrototype()));
+ }
+ }
+
+ // Allocate an array with storage for all the property names.
+ Handle<FixedArray> names = Factory::NewFixedArray(total_property_count);
+
+ // Get the property names.
+ jsproto = obj;
+ for (int i = 0; i < length; i++) {
+ jsproto->GetLocalPropertyNames(*names,
+ i == 0 ? 0 : local_property_count[i - 1]);
+ if (i < length - 1) {
+ jsproto = Handle<JSObject>(JSObject::cast(jsproto->GetPrototype()));
+ }
+ }
+
+ DeleteArray(local_property_count);
return *Factory::NewJSArrayWithElements(names);
}
@@ -5806,12 +5921,15 @@
}
-static Object* Runtime_GetPrototype(Arguments args) {
+// Find the effective prototype object as returned by __proto__.
+// args[0]: the object to find the prototype for.
+static Object* Runtime_DebugGetPrototype(Arguments args) {
ASSERT(args.length() == 1);
CONVERT_CHECKED(JSObject, obj, args[0]);
- return obj->GetPrototype();
+ // Use the __proto__ accessor.
+ return Accessors::ObjectPrototype.getter(obj, NULL);
}
diff --git a/src/runtime.h b/src/runtime.h
index ebe70a4..1924317 100644
--- a/src/runtime.h
+++ b/src/runtime.h
@@ -244,7 +244,7 @@
F(DebugGetLoadedScripts, 0) \
F(DebugReferencedBy, 3) \
F(DebugConstructedBy, 2) \
- F(GetPrototype, 1) \
+ F(DebugGetPrototype, 1) \
F(SystemBreak, 0) \
\
/* Literals */ \
diff --git a/src/serialize.cc b/src/serialize.cc
index 4e1529c..be85ea6 100644
--- a/src/serialize.cc
+++ b/src/serialize.cc
@@ -686,15 +686,15 @@
SnapshotWriter() {
len_ = 0;
max_ = 8 << 10; // 8K initial size
- str_ = NewArray<char>(max_);
+ str_ = NewArray<byte>(max_);
}
~SnapshotWriter() {
DeleteArray(str_);
}
- void GetString(char** str, int* len) {
- *str = NewArray<char>(len_);
+ void GetBytes(byte** str, int* len) {
+ *str = NewArray<byte>(len_);
memcpy(*str, str_, len_);
*len = len_;
}
@@ -742,7 +742,7 @@
Address position() { return reinterpret_cast<Address>(&str_[len_]); }
private:
- char* str_; // the snapshot
+ byte* str_; // the snapshot
int len_; // the current length of str_
int max_; // the allocated size of str_
};
@@ -752,14 +752,14 @@
CHECK(0 <= pos && pos <= len_);
while (len_ + bytes >= max_) {
max_ *= 2;
- char* old = str_;
- str_ = NewArray<char>(max_);
+ byte* old = str_;
+ str_ = NewArray<byte>(max_);
memcpy(str_, old, len_);
DeleteArray(old);
}
if (pos < len_) {
- char* old = str_;
- str_ = NewArray<char>(max_);
+ byte* old = str_;
+ str_ = NewArray<byte>(max_);
memcpy(str_, old, pos);
memcpy(str_ + pos + bytes, old + pos, len_ - pos);
DeleteArray(old);
@@ -814,8 +814,7 @@
void Update(Address start_address) {
for (int i = 0; i < offsets_.length(); i++) {
- Address* p = reinterpret_cast<Address*>(start_address + offsets_[i]);
- *p = addresses_[i];
+ memcpy(start_address + offsets_[i], &addresses_[i], sizeof(Address));
}
}
@@ -929,8 +928,8 @@
}
-void Serializer::Finalize(char** str, int* len) {
- writer_->GetString(str, len);
+void Serializer::Finalize(byte** str, int* len) {
+ writer_->GetBytes(str, len);
}
@@ -1180,7 +1179,7 @@
static const int kInitArraySize = 32;
-Deserializer::Deserializer(const char* str, int len)
+Deserializer::Deserializer(const byte* str, int len)
: reader_(str, len),
map_pages_(kInitArraySize),
old_pointer_pages_(kInitArraySize),
diff --git a/src/serialize.h b/src/serialize.h
index 0679ecc..18d36f3 100644
--- a/src/serialize.h
+++ b/src/serialize.h
@@ -135,7 +135,7 @@
// Returns the serialized buffer. Ownership is transferred to the
// caller. Only the destructor and getters may be called after this call.
- void Finalize(char** str, int* len);
+ void Finalize(byte** str, int* len);
int roots() { return roots_; }
int objects() { return objects_; }
@@ -211,7 +211,7 @@
class SnapshotReader {
public:
- SnapshotReader(const char* str, int len): str_(str), end_(str + len) {}
+ SnapshotReader(const byte* str, int len): str_(str), end_(str + len) {}
void ExpectC(char expected) {
int c = GetC();
@@ -225,8 +225,8 @@
}
int GetInt() {
- int result = *reinterpret_cast<const int*>(str_);
- str_ += sizeof(result);
+ int result;
+ GetBytes(reinterpret_cast<Address>(&result), sizeof(result));
return result;
}
@@ -247,8 +247,8 @@
}
private:
- const char* str_;
- const char* end_;
+ const byte* str_;
+ const byte* end_;
};
@@ -257,7 +257,7 @@
class Deserializer: public ObjectVisitor {
public:
// Create a deserializer. The snapshot is held in str and has size len.
- Deserializer(const char* str, int len);
+ Deserializer(const byte* str, int len);
virtual ~Deserializer();
diff --git a/src/snapshot-common.cc b/src/snapshot-common.cc
index b0e9323..3aa1cae 100644
--- a/src/snapshot-common.cc
+++ b/src/snapshot-common.cc
@@ -35,7 +35,7 @@
namespace v8 { namespace internal {
-bool Snapshot::Deserialize(const char* content, int len) {
+bool Snapshot::Deserialize(const byte* content, int len) {
Deserializer des(content, len);
des.GetFlags();
return V8::Initialize(&des);
@@ -45,7 +45,7 @@
bool Snapshot::Initialize(const char* snapshot_file) {
if (snapshot_file) {
int len;
- char* str = ReadChars(snapshot_file, &len);
+ byte* str = ReadBytes(snapshot_file, &len);
if (!str) return false;
bool result = Deserialize(str, len);
DeleteArray(str);
@@ -60,11 +60,11 @@
bool Snapshot::WriteToFile(const char* snapshot_file) {
Serializer ser;
ser.Serialize();
- char* str;
+ byte* str;
int len;
ser.Finalize(&str, &len);
- int written = WriteChars(snapshot_file, str, len);
+ int written = WriteBytes(snapshot_file, str, len);
DeleteArray(str);
return written == len;
diff --git a/src/snapshot-empty.cc b/src/snapshot-empty.cc
index 1c8ec5f..d4cda19 100644
--- a/src/snapshot-empty.cc
+++ b/src/snapshot-empty.cc
@@ -33,7 +33,7 @@
namespace v8 { namespace internal {
-const char Snapshot::data_[] = { 0 };
+const byte Snapshot::data_[] = { 0 };
int Snapshot::size_ = 0;
} } // namespace v8::internal
diff --git a/src/snapshot.h b/src/snapshot.h
index 2d8b9ac..b3f23d3 100644
--- a/src/snapshot.h
+++ b/src/snapshot.h
@@ -45,10 +45,10 @@
static bool WriteToFile(const char* snapshot_file);
private:
- static const char data_[];
+ static const byte data_[];
static int size_;
- static bool Deserialize(const char* content, int len);
+ static bool Deserialize(const byte* content, int len);
DISALLOW_IMPLICIT_CONSTRUCTORS(Snapshot);
};
diff --git a/src/string.js b/src/string.js
index 614d541..c90c0b4 100644
--- a/src/string.js
+++ b/src/string.js
@@ -572,6 +572,9 @@
if (ovector == null) return null;
var nof_results = ovector.length >> 1;
var result = new $Array(nof_results + 1);
+ // Section 15.5.4.14 paragraph two says that we do not allow zero length
+ // matches at the end of the string.
+ if (ovector[0] === subject.length) return null;
result[0] = ovector[1];
result[1] = subject.slice(current_index, ovector[0]);
for (var i = 1; i < nof_results; i++) {
diff --git a/src/top.cc b/src/top.cc
index d339591..76c608e 100644
--- a/src/top.cc
+++ b/src/top.cc
@@ -37,7 +37,7 @@
namespace v8 { namespace internal {
ThreadLocalTop Top::thread_local_;
-Mutex* Top::break_access_;
+Mutex* Top::break_access_ = OS::CreateMutex();
StackFrame::Id Top::break_frame_id_;
int Top::break_count_;
int Top::break_id_;
@@ -225,7 +225,6 @@
InitializeThreadLocal();
- break_access_ = OS::CreateMutex();
break_frame_id_ = StackFrame::NO_ID;
break_count_ = 0;
break_id_ = 0;
diff --git a/src/utils.cc b/src/utils.cc
index 9310301..3920320 100644
--- a/src/utils.cc
+++ b/src/utils.cc
@@ -182,8 +182,9 @@
}
-char* ReadChars(const char* filename, int* size, bool verbose) {
- return ReadCharsFromFile(filename, size, 0, verbose);
+byte* ReadBytes(const char* filename, int* size, bool verbose) {
+ char* chars = ReadCharsFromFile(filename, size, 0, verbose);
+ return reinterpret_cast<byte*>(chars);
}
@@ -233,6 +234,15 @@
}
+int WriteBytes(const char* filename,
+ const byte* bytes,
+ int size,
+ bool verbose) {
+ const char* str = reinterpret_cast<const char*>(bytes);
+ return WriteChars(filename, str, size, verbose);
+}
+
+
StringBuilder::StringBuilder(int size) {
buffer_ = Vector<char>::New(size);
position_ = 0;
diff --git a/src/utils.h b/src/utils.h
index 9861246..f62b47a 100644
--- a/src/utils.h
+++ b/src/utils.h
@@ -224,10 +224,10 @@
char* ReadLine(const char* prompt);
-// Read and return the raw chars in a file. the size of the buffer is returned
+// Read and return the raw bytes in a file. the size of the buffer is returned
// in size.
-// The returned buffer is not 0-terminated. It must be freed by the caller.
-char* ReadChars(const char* filename, int* size, bool verbose = true);
+// 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.
@@ -238,6 +238,14 @@
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>;
@@ -527,55 +535,6 @@
}
-static inline int Load16(const byte* ptr) {
-#ifdef CAN_READ_UNALIGNED
- return *reinterpret_cast<const uint16_t*>(ptr);
-#else
- uint32_t word;
- word = ptr[1];
- word |= ptr[0] << 8;
- return word;
-#endif
-}
-
-
-static inline int Load32(const byte* ptr) {
-#ifdef CAN_READ_UNALIGNED
- return *reinterpret_cast<const uint32_t*>(ptr);
-#else
- uint32_t word;
- word = ptr[3];
- word |= ptr[2] << 8;
- word |= ptr[1] << 16;
- word |= ptr[0] << 24;
- return word;
-#endif
-}
-
-
-static inline void Store16(byte* ptr, uint16_t value) {
-#ifdef CAN_READ_UNALIGNED
- *reinterpret_cast<uint16_t*>(ptr) = value;
-#else
- // Cast to avoid warning C4244 when compiling with Microsoft Visual C++.
- ptr[1] = static_cast<byte>(value);
- ptr[0] = value >> 8;
-#endif
-}
-
-
-static inline void Store32(byte* ptr, uint32_t value) {
-#ifdef CAN_READ_UNALIGNED
- *reinterpret_cast<uint32_t*>(ptr) = value;
-#else
- ptr[3] = value;
- ptr[2] = value >> 8;
- ptr[1] = value >> 16;
- ptr[0] = value >> 24;
-#endif
-}
-
-
} } // namespace v8::internal
#endif // V8_UTILS_H_
diff --git a/src/v8threads.cc b/src/v8threads.cc
index 0a6c7c1..df0095d 100644
--- a/src/v8threads.cc
+++ b/src/v8threads.cc
@@ -28,6 +28,7 @@
#include "v8.h"
#include "api.h"
+#include "bootstrapper.h"
#include "debug.h"
#include "execution.h"
#include "v8threads.h"
@@ -38,9 +39,17 @@
static internal::Thread::LocalStorageKey thread_state_key =
internal::Thread::CreateThreadLocalKey();
+
+// Track whether this V8 instance has ever called v8::Locker. This allows the
+// API code to verify that the lock is always held when V8 is being entered.
+bool Locker::active_ = false;
+
+
// Constructor for the Locker object. Once the Locker is constructed the
// current thread will be guaranteed to have the big V8 lock.
Locker::Locker() : has_lock_(false), top_level_(true) {
+ // Record that the Locker has been used at least once.
+ active_ = true;
// Get the big lock if necessary.
if (!internal::ThreadManager::IsLockedByCurrentThread()) {
internal::ThreadManager::Lock();
@@ -111,6 +120,11 @@
Thread::SetThreadLocal(thread_state_key, NULL);
return true;
}
+
+ // Make sure that the preemption thread cannot modify the thread state while
+ // it is being archived or restored.
+ ExecutionAccess access;
+
// If there is another thread that was lazily archived then we have to really
// archive it now.
if (lazily_archived_thread_.IsValid()) {
@@ -127,6 +141,7 @@
from = Debug::RestoreDebug(from);
from = StackGuard::RestoreStackGuard(from);
from = RegExpStack::RestoreStack(from);
+ from = Bootstrapper::RestoreState(from);
Thread::SetThreadLocal(thread_state_key, NULL);
state->Unlink();
state->LinkInto(ThreadState::FREE_LIST);
@@ -152,7 +167,8 @@
Top::ArchiveSpacePerThread() +
Debug::ArchiveSpacePerThread() +
StackGuard::ArchiveSpacePerThread() +
- RegExpStack::ArchiveSpacePerThread();
+ RegExpStack::ArchiveSpacePerThread() +
+ Bootstrapper::ArchiveSpacePerThread();
}
@@ -234,6 +250,7 @@
to = Debug::ArchiveDebug(to);
to = StackGuard::ArchiveStackGuard(to);
to = RegExpStack::ArchiveStack(to);
+ to = Bootstrapper::ArchiveState(to);
lazily_archived_thread_.Initialize(ThreadHandle::INVALID);
lazily_archived_thread_state_ = NULL;
}
diff --git a/test/cctest/cctest.status b/test/cctest/cctest.status
index 9a528d3..2be0fd0 100644
--- a/test/cctest/cctest.status
+++ b/test/cctest/cctest.status
@@ -33,7 +33,6 @@
[ $arch == arm ]
test-debug: SKIP
-test-serialize: SKIP
# BUG(113): Test seems flaky on ARM.
test-spaces/LargeObjectSpace: PASS || FAIL
diff --git a/test/cctest/test-api.cc b/test/cctest/test-api.cc
index 19f702a..942ae05 100644
--- a/test/cctest/test-api.cc
+++ b/test/cctest/test-api.cc
@@ -5607,3 +5607,36 @@
CompileRun("for (var j = 0; j < 10; j++) RegExp('')");
}
}
+
+
+// Test that cross-context new calls use the context of the callee to
+// create the new JavaScript object.
+THREADED_TEST(CrossContextNew) {
+ v8::HandleScope scope;
+ v8::Persistent<Context> context0 = Context::New();
+ v8::Persistent<Context> context1 = Context::New();
+
+ // Allow cross-domain access.
+ Local<String> token = v8_str("<security token>");
+ context0->SetSecurityToken(token);
+ context1->SetSecurityToken(token);
+
+ // Set an 'x' property on the Object prototype and define a
+ // constructor function in context0.
+ context0->Enter();
+ CompileRun("Object.prototype.x = 42; function C() {};");
+ context0->Exit();
+
+ // Call the constructor function from context0 and check that the
+ // result has the 'x' property.
+ context1->Enter();
+ context1->Global()->Set(v8_str("other"), context0->Global());
+ Local<Value> value = CompileRun("var instance = new other.C(); instance.x");
+ CHECK(value->IsInt32());
+ CHECK_EQ(42, value->Int32Value());
+ context1->Exit();
+
+ // Dispose the contexts to allow them to be garbage collected.
+ context0.Dispose();
+ context1.Dispose();
+}
diff --git a/test/cctest/test-debug.cc b/test/cctest/test-debug.cc
index cd85b8f..15ebf16 100644
--- a/test/cctest/test-debug.cc
+++ b/test/cctest/test-debug.cc
@@ -2749,6 +2749,101 @@
}
+TEST(HiddenPrototypePropertyMirror) {
+ // Create a V8 environment with debug access.
+ v8::HandleScope scope;
+ DebugLocalContext env;
+ env.ExposeDebug();
+
+ v8::Handle<v8::FunctionTemplate> t0 = v8::FunctionTemplate::New();
+ t0->InstanceTemplate()->Set(v8::String::New("x"), v8::Number::New(0));
+ v8::Handle<v8::FunctionTemplate> t1 = v8::FunctionTemplate::New();
+ t1->SetHiddenPrototype(true);
+ t1->InstanceTemplate()->Set(v8::String::New("y"), v8::Number::New(1));
+ v8::Handle<v8::FunctionTemplate> t2 = v8::FunctionTemplate::New();
+ t2->SetHiddenPrototype(true);
+ t2->InstanceTemplate()->Set(v8::String::New("z"), v8::Number::New(2));
+ v8::Handle<v8::FunctionTemplate> t3 = v8::FunctionTemplate::New();
+ t3->InstanceTemplate()->Set(v8::String::New("u"), v8::Number::New(3));
+
+ // Create object and set them on the global object.
+ v8::Handle<v8::Object> o0 = t0->GetFunction()->NewInstance();
+ env->Global()->Set(v8::String::New("o0"), o0);
+ v8::Handle<v8::Object> o1 = t1->GetFunction()->NewInstance();
+ env->Global()->Set(v8::String::New("o1"), o1);
+ v8::Handle<v8::Object> o2 = t2->GetFunction()->NewInstance();
+ env->Global()->Set(v8::String::New("o2"), o2);
+ v8::Handle<v8::Object> o3 = t3->GetFunction()->NewInstance();
+ env->Global()->Set(v8::String::New("o3"), o3);
+
+ // Get mirrors for the four objects.
+ CompileRun(
+ "o0_mirror = debug.MakeMirror(o0);"
+ "o1_mirror = debug.MakeMirror(o1);"
+ "o2_mirror = debug.MakeMirror(o2);"
+ "o3_mirror = debug.MakeMirror(o3)");
+ CHECK(CompileRun("o0_mirror instanceof debug.ObjectMirror")->BooleanValue());
+ CHECK(CompileRun("o1_mirror instanceof debug.ObjectMirror")->BooleanValue());
+ CHECK(CompileRun("o2_mirror instanceof debug.ObjectMirror")->BooleanValue());
+ CHECK(CompileRun("o3_mirror instanceof debug.ObjectMirror")->BooleanValue());
+
+ // Check that each object has one property.
+ CHECK_EQ(1, CompileRun(
+ "o0_mirror.propertyNames().length")->Int32Value());
+ CHECK_EQ(1, CompileRun(
+ "o1_mirror.propertyNames().length")->Int32Value());
+ CHECK_EQ(1, CompileRun(
+ "o2_mirror.propertyNames().length")->Int32Value());
+ CHECK_EQ(1, CompileRun(
+ "o3_mirror.propertyNames().length")->Int32Value());
+
+ // Set o1 as prototype for o0. o1 has the hidden prototype flag so all
+ // properties on o1 should be seen on o0.
+ o0->Set(v8::String::New("__proto__"), o1);
+ CHECK_EQ(2, CompileRun(
+ "o0_mirror.propertyNames().length")->Int32Value());
+ CHECK_EQ(0, CompileRun(
+ "o0_mirror.property('x').value().value()")->Int32Value());
+ CHECK_EQ(1, CompileRun(
+ "o0_mirror.property('y').value().value()")->Int32Value());
+
+ // Set o2 as prototype for o0 (it will end up after o1 as o1 has the hidden
+ // prototype flag. o2 also has the hidden prototype flag so all properties
+ // on o2 should be seen on o0 as well as properties on o1.
+ o0->Set(v8::String::New("__proto__"), o2);
+ CHECK_EQ(3, CompileRun(
+ "o0_mirror.propertyNames().length")->Int32Value());
+ CHECK_EQ(0, CompileRun(
+ "o0_mirror.property('x').value().value()")->Int32Value());
+ CHECK_EQ(1, CompileRun(
+ "o0_mirror.property('y').value().value()")->Int32Value());
+ CHECK_EQ(2, CompileRun(
+ "o0_mirror.property('z').value().value()")->Int32Value());
+
+ // Set o3 as prototype for o0 (it will end up after o1 and o2 as both o1 and
+ // o2 has the hidden prototype flag. o3 does not have the hidden prototype
+ // flag so properties on o3 should not be seen on o0 whereas the properties
+ // from o1 and o2 should still be seen on o0.
+ // Final prototype chain: o0 -> o1 -> o2 -> o3
+ // Hidden prototypes: ^^ ^^
+ o0->Set(v8::String::New("__proto__"), o3);
+ CHECK_EQ(3, CompileRun(
+ "o0_mirror.propertyNames().length")->Int32Value());
+ CHECK_EQ(1, CompileRun(
+ "o3_mirror.propertyNames().length")->Int32Value());
+ CHECK_EQ(0, CompileRun(
+ "o0_mirror.property('x').value().value()")->Int32Value());
+ CHECK_EQ(1, CompileRun(
+ "o0_mirror.property('y').value().value()")->Int32Value());
+ CHECK_EQ(2, CompileRun(
+ "o0_mirror.property('z').value().value()")->Int32Value());
+ CHECK(CompileRun("o0_mirror.property('u').isUndefined()")->BooleanValue());
+
+ // The prototype (__proto__) for o0 should be o3 as o1 and o2 are hidden.
+ CHECK(CompileRun("o0_mirror.protoObject() == o3_mirror")->BooleanValue());
+}
+
+
// Multithreaded tests of JSON debugger protocol
// Support classes
diff --git a/test/mjsunit/ascii-regexp-subject.js b/test/mjsunit/ascii-regexp-subject.js
index 01d0697..e0c2f84 100644
--- a/test/mjsunit/ascii-regexp-subject.js
+++ b/test/mjsunit/ascii-regexp-subject.js
@@ -37,9 +37,13 @@
s = s + s;
}
-var re = /^bar/;
-
-for (i = 0; i < 1000; i++) {
- re.test(s);
- re = new RegExp("^bar");
+function repeatRegexp(re) {
+ for (i = 0; i < 1000; i++) {
+ re.test(s);
+ }
}
+
+repeatRegexp(/^bar/);
+repeatRegexp(/^foo|^bar|^baz/);
+repeatRegexp(/(^bar)/);
+repeatRegexp(/(?=^bar)\w+/);
diff --git a/test/mjsunit/const-eval-init.js b/test/mjsunit/const-eval-init.js
new file mode 100644
index 0000000..d3636de
--- /dev/null
+++ b/test/mjsunit/const-eval-init.js
@@ -0,0 +1,111 @@
+// Copyright 2009 the V8 project authors. All rights reserved.
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following
+// disclaimer in the documentation and/or other materials provided
+// with the distribution.
+// * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived
+// from this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+// Test the handling of initialization of deleted const variables.
+// This only makes sense in local scopes since the declaration and
+// initialization of consts in the global scope happen at the same
+// time.
+
+function testIntroduceGlobal() {
+ var source =
+ // Deleting 'x' removes the local const property.
+ "delete x;" +
+ // Initialization turns into assignment to global 'x'.
+ "const x = 3; assertEquals(3, x);" +
+ // No constness of the global 'x'.
+ "x = 4; assertEquals(4, x);";
+ eval(source);
+}
+
+testIntroduceGlobal();
+assertEquals(4, x);
+
+function testAssignExistingGlobal() {
+ var source =
+ // Delete 'x' to remove the local const property.
+ "delete x;" +
+ // Initialization turns into assignment to global 'x'.
+ "const x = 5; assertEquals(5, x);" +
+ // No constness of the global 'x'.
+ "x = 6; assertEquals(6, x);";
+ eval(source);
+}
+
+testAssignExistingGlobal();
+assertEquals(6, x);
+
+function testAssignmentArgument(x) {
+ function local() {
+ var source = "delete x; const x = 7; assertEquals(7, x)";
+ eval(source);
+ }
+ local();
+ assertEquals(7, x);
+}
+
+testAssignmentArgument();
+assertEquals(6, x);
+
+__defineSetter__('x', function() { throw 42; });
+function testAssignGlobalThrows() {
+ // Initialization turns into assignment to global 'x' which
+ // throws an exception.
+ var source = "delete x; const x = 8";
+ eval(source);
+}
+
+assertThrows("testAssignGlobalThrows()");
+
+function testInitFastCaseExtension() {
+ var source = "const x = 9; assertEquals(9, x); x = 10; assertEquals(9, x)";
+ eval(source);
+}
+
+testInitFastCaseExtension();
+
+function testInitSlowCaseExtension() {
+ var source = "";
+ // Introduce 100 properties on the context extension object to force
+ // it in slow case.
+ for (var i = 0; i < 100; i++) source += ("var a" + i + " = i;");
+ source += "const x = 10; assertEquals(10, x); x = 11; assertEquals(10, x)";
+ eval(source);
+}
+
+testInitSlowCaseExtension();
+
+function testAssignSurroundingContextSlot() {
+ var x = 12;
+ function local() {
+ var source = "delete x; const x = 13; assertEquals(13, x)";
+ eval(source);
+ }
+ local();
+ assertEquals(13, x);
+}
+
+testAssignSurroundingContextSlot();
diff --git a/test/mjsunit/const.js b/test/mjsunit/const.js
index 5ad0c9c..a48e82d 100644
--- a/test/mjsunit/const.js
+++ b/test/mjsunit/const.js
@@ -52,17 +52,19 @@
function g() {
const o = { valueOf: function() { valueOfCount++; return 42; } }
assertEquals(42, o);
- assertEquals(0, valueOfCount);
+ assertEquals(1, valueOfCount);
o++;
assertEquals(42, o);
- assertEquals(1, valueOfCount);
+ assertEquals(3, valueOfCount);
++o;
assertEquals(42, o);
- assertEquals(2, valueOfCount);
+ assertEquals(5, valueOfCount);
o--;
assertEquals(42, o);
- assertEquals(3, valueOfCount);
+ assertEquals(7, valueOfCount);
--o;
assertEquals(42, o);
- assertEquals(4, valueOfCount);
+ assertEquals(9, valueOfCount);
}
+
+g();
diff --git a/test/mjsunit/debug-handle.js b/test/mjsunit/debug-handle.js
new file mode 100644
index 0000000..7732bfd
--- /dev/null
+++ b/test/mjsunit/debug-handle.js
@@ -0,0 +1,199 @@
+// Copyright 2009 the V8 project authors. All rights reserved.
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following
+// disclaimer in the documentation and/or other materials provided
+// with the distribution.
+// * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived
+// from this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+// Flags: --expose-debug-as debug
+// Get the Debug object exposed from the debug context global object.
+Debug = debug.Debug
+
+listenerComplete = false;
+exception = false;
+
+function safeEval(code) {
+ try {
+ return eval('(' + code + ')');
+ } catch (e) {
+ assertEquals(void 0, e);
+ return undefined;
+ }
+}
+
+
+// Send an evaluation request and return the handle of the result.
+function evaluateRequest(dcp, arguments) {
+ // The base part of all evaluate requests.
+ var base_request = '"seq":0,"type":"request","command":"evaluate"'
+
+ // Generate request with the supplied arguments.
+ var request;
+ if (arguments) {
+ request = '{' + base_request + ',"arguments":' + arguments + '}';
+ } else {
+ request = '{' + base_request + '}'
+ }
+
+ var response = safeEval(dcp.processDebugJSONRequest(request));
+ assertTrue(response.success, request + ' -> ' + response.message);
+
+ return response.body.handle;
+}
+
+
+// Send a lookup request and return the evaluated JSON response.
+function lookupRequest(dcp, arguments, success) {
+ // The base part of all lookup requests.
+ var base_request = '"seq":0,"type":"request","command":"lookup"'
+
+ // Generate request with the supplied arguments.
+ var request;
+ if (arguments) {
+ request = '{' + base_request + ',"arguments":' + arguments + '}';
+ } else {
+ request = '{' + base_request + '}'
+ }
+
+ var response = safeEval(dcp.processDebugJSONRequest(request));
+ if (success) {
+ assertTrue(response.success, request + ' -> ' + response.message);
+ } else {
+ assertFalse(response.success, request + ' -> ' + response.message);
+ }
+ assertFalse(response.running, request + ' -> expected not running');
+
+ return response;
+}
+
+
+function listener(event, exec_state, event_data, data) {
+ try {
+ if (event == Debug.DebugEvent.Break) {
+ // Get the debug command processor.
+ var dcp = exec_state.debugCommandProcessor();
+
+ // Test some illegal lookup requests.
+ lookupRequest(dcp, void 0, false);
+ lookupRequest(dcp, '{"handle":"a"}', false);
+ lookupRequest(dcp, '{"handle":-1}', false);
+
+ // Evaluate and get some handles.
+ var handle_o = evaluateRequest(dcp, '{"expression":"o"}');
+ var handle_p = evaluateRequest(dcp, '{"expression":"p"}');
+ var handle_b = evaluateRequest(dcp, '{"expression":"a"}');
+ var handle_a = evaluateRequest(dcp, '{"expression":"b","frame":1}');
+ assertEquals(handle_o, handle_a);
+ assertEquals(handle_a, handle_b);
+ assertFalse(handle_o == handle_p, "o and p have he same handle");
+
+ var response;
+ var count;
+ response = lookupRequest(dcp, '{"handle":' + handle_o + '}', true);
+ assertEquals(handle_o, response.body.handle);
+ count = 0;
+ for (i in response.body.properties) {
+ switch (response.body.properties[i].name) {
+ case 'o':
+ response.body.properties[i].ref = handle_o;
+ count++;
+ break;
+ case 'p':
+ response.body.properties[i].ref = handle_p;
+ count++;
+ break;
+ }
+ }
+ assertEquals(2, count, 'Either "o" or "p" not found');
+ response = lookupRequest(dcp, '{"handle":' + handle_p + '}', true);
+ assertEquals(handle_p, response.body.handle);
+
+ // Check handles for functions on the stack.
+ var handle_f = evaluateRequest(dcp, '{"expression":"f"}');
+ var handle_g = evaluateRequest(dcp, '{"expression":"g"}');
+ var handle_caller = evaluateRequest(dcp, '{"expression":"f.caller"}');
+
+ assertFalse(handle_f == handle_g, "f and g have he same handle");
+ assertEquals(handle_g, handle_caller, "caller for f should be g");
+
+ response = lookupRequest(dcp, '{"handle":' + handle_f + '}', true);
+ assertEquals(handle_f, response.body.handle);
+ count = 0;
+ for (i in response.body.properties) {
+ var arguments = '{"handle":' + response.body.properties[i].ref + '}'
+ switch (response.body.properties[i].name) {
+ case 'name':
+ var response_name;
+ response_name = lookupRequest(dcp, arguments, true);
+ assertEquals('string', response_name.body.type);
+ assertEquals("f", response_name.body.value);
+ count++;
+ break;
+ case 'length':
+ var response_length;
+ response_length = lookupRequest(dcp, arguments, true);
+ assertEquals('number', response_length.body.type);
+ assertEquals(1, response_length.body.value);
+ count++;
+ break;
+ case 'caller':
+ assertEquals(handle_g, response.body.properties[i].ref);
+ count++;
+ break;
+ }
+ }
+ assertEquals(3, count, 'Either "name", "length" or "caller" not found');
+
+
+ // Indicate that all was processed.
+ listenerComplete = true;
+ }
+ } catch (e) {
+ exception = e
+ };
+};
+
+// Add the debug event listener.
+Debug.addListener(listener);
+
+function f(a) {
+ debugger;
+};
+
+function g(b) {
+ f(b);
+};
+
+// Set a break point at return in f and invoke g to hit the breakpoint.
+Debug.setBreakPoint(f, 2, 0);
+o = {};
+p = {}
+o.o = o;
+o.p = p;
+p.o = o;
+p.p = p;
+g(o);
+
+// Make sure that the debug event listener vas invoked.
+assertTrue(listenerComplete, "listener did not run to completion");
+assertFalse(exception, "exception in listener")
diff --git a/test/mjsunit/mirror-number.js b/test/mjsunit/mirror-number.js
index 33488ef..68eb0d7 100644
--- a/test/mjsunit/mirror-number.js
+++ b/test/mjsunit/mirror-number.js
@@ -53,7 +53,15 @@
if (!isNaN(n)) {
assertEquals(n, fromJSON.value);
} else {
- assertTrue(isNaN(fromJSON.value));
+ // NaN values are encoded as strings.
+ assertTrue(typeof fromJSON.value == 'string');
+ if (n === Infinity) {
+ assertEquals('Infinity', fromJSON.value);
+ } else if (n === -Infinity) {
+ assertEquals('-Infinity', fromJSON.value);
+ } else {
+ assertEquals('NaN', fromJSON.value);
+ }
}
}
diff --git a/test/mjsunit/mirror-object.js b/test/mjsunit/mirror-object.js
index ec5afa3..e829a0e 100644
--- a/test/mjsunit/mirror-object.js
+++ b/test/mjsunit/mirror-object.js
@@ -135,7 +135,12 @@
assertEquals(properties[i].value().type(), o.type, 'Unexpected serialized property type for ' + name);
if (properties[i].value().isPrimitive()) {
- assertEquals(properties[i].value().value(), o.value, 'Unexpected serialized property value for ' + name);
+ // Special check for NaN as NaN == NaN is false.
+ if (properties[i].value().isNumber() && isNaN(properties[i].value().value())) {
+ assertEquals('NaN', o.value, 'Unexpected serialized property value for ' + name);
+ } else {
+ assertEquals(properties[i].value().value(), o.value, 'Unexpected serialized property value for ' + name);
+ }
} else if (properties[i].value().isFunction()) {
assertEquals(properties[i].value().source(), o.source, 'Unexpected serialized property value for ' + name);
}
@@ -159,6 +164,7 @@
testObjectMirror({'1':void 0,'2':null,'f':function pow(x,y){return Math.pow(x,y);}}, 'Object', 'Object');
testObjectMirror(new Point(-1.2,2.003), 'Object', 'Point');
testObjectMirror(this, 'global', '', true); // Global object has special properties
+testObjectMirror(this.__proto__, 'Object', '');
testObjectMirror([], 'Array', 'Array');
testObjectMirror([1,2], 'Array', 'Array');
diff --git a/test/mjsunit/mjsunit.status b/test/mjsunit/mjsunit.status
index c354300..27dfc14 100644
--- a/test/mjsunit/mjsunit.status
+++ b/test/mjsunit/mjsunit.status
@@ -63,6 +63,7 @@
debug-stepin-constructor: CRASH, FAIL
debug-step: SKIP
debug-breakpoints: PASS || FAIL
+debug-handle: CRASH, FAIL if $mode == debug
# Bug number 130 http://code.google.com/p/v8/issues/detail?id=130
# Fails on real ARM hardware but not on the simulator.
diff --git a/test/mjsunit/regexp-capture.js b/test/mjsunit/regexp-capture.js
new file mode 100755
index 0000000..7227186
--- /dev/null
+++ b/test/mjsunit/regexp-capture.js
@@ -0,0 +1,52 @@
+// Copyright 2009 the V8 project authors. All rights reserved.
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following
+// disclaimer in the documentation and/or other materials provided
+// with the distribution.
+// * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived
+// from this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+// Tests from http://blog.stevenlevithan.com/archives/npcg-javascript
+
+assertEquals(true, /(x)?\1y/.test("y"));
+assertEquals(["y", undefined], /(x)?\1y/.exec("y"));
+assertEquals(["y", undefined], /(x)?y/.exec("y"));
+assertEquals(["y", undefined], "y".match(/(x)?\1y/));
+assertEquals(["y", undefined], "y".match(/(x)?y/));
+assertEquals(["y"], "y".match(/(x)?\1y/g));
+assertEquals(["", undefined, ""], "y".split(/(x)?\1y/));
+assertEquals(["", undefined, ""], "y".split(/(x)?y/));
+assertEquals(0, "y".search(/(x)?\1y/));
+assertEquals("z", "y".replace(/(x)?\1y/, "z"));
+assertEquals("", "y".replace(/(x)?y/, "$1"));
+assertEquals("undefined", "y".replace(/(x)?\1y/,
+ function($0, $1){
+ return String($1);
+ }));
+assertEquals("undefined", "y".replace(/(x)?y/,
+ function($0, $1){
+ return String($1);
+ }));
+assertEquals("undefined", "y".replace(/(x)?y/,
+ function($0, $1){
+ return $1;
+ }));
diff --git a/test/mjsunit/regexp-lookahead.js b/test/mjsunit/regexp-lookahead.js
new file mode 100644
index 0000000..1188b56
--- /dev/null
+++ b/test/mjsunit/regexp-lookahead.js
@@ -0,0 +1,166 @@
+// Copyright 2009 the V8 project authors. All rights reserved.
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following
+// disclaimer in the documentation and/or other materials provided
+// with the distribution.
+// * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived
+// from this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+// Tests captures in positive and negative look-ahead in regular expressions.
+
+function stringEscape(string) {
+ // Converts string to source literal.
+ return '"' + string.replace(/["\\]/g, "\\$1") + '"';
+}
+
+function testRE(re, input, expected_result) {
+ var testName = re + ".test(" + stringEscape(input) +")";
+ if (expected_result) {
+ assertTrue(re.test(input), testName);
+ } else {
+ assertFalse(re.test(input), testName);
+ }
+}
+
+function execRE(re, input, expected_result) {
+ var testName = re + ".exec('" + stringEscape(input) +"')";
+ assertEquals(expected_result, re.exec(input), testName);
+}
+
+// Test of simple positive lookahead.
+
+var re = /^(?=a)/;
+testRE(re, "a", true);
+testRE(re, "b", false);
+execRE(re, "a", [""]);
+
+re = /^(?=\woo)f\w/;
+testRE(re, "foo", true);
+testRE(re, "boo", false);
+testRE(re, "fao", false);
+testRE(re, "foa", false);
+execRE(re, "foo", ["fo"]);
+
+re = /(?=\w).(?=\W)/;
+testRE(re, ".a! ", true);
+testRE(re, ".! ", false);
+testRE(re, ".ab! ", true);
+execRE(re, ".ab! ", ["b"]);
+
+re = /(?=f(?=[^f]o))../;
+testRE(re, ", foo!", true);
+testRE(re, ", fo!", false);
+testRE(re, ", ffo", false);
+execRE(re, ", foo!", ["fo"]);
+
+// Positive lookahead with captures.
+re = /^[^\'\"]*(?=([\'\"])).*\1(\w+)\1/;
+testRE(re, " 'foo' ", true);
+testRE(re, ' "foo" ', true);
+testRE(re, " \" 'foo' ", false);
+testRE(re, " ' \"foo\" ", false);
+testRE(re, " 'foo\" ", false);
+testRE(re, " \"foo' ", false);
+execRE(re, " 'foo' ", [" 'foo'", "'", "foo"]);
+execRE(re, ' "foo" ', [' "foo"', '"', 'foo']);
+
+// Captures are cleared on backtrack past the look-ahead.
+re = /^(?:(?=(.))a|b)\1$/;
+testRE(re, "aa", true);
+testRE(re, "b", true);
+testRE(re, "bb", false);
+testRE(re, "a", false);
+execRE(re, "aa", ["aa", "a"]);
+execRE(re, "b", ["b", undefined]);
+
+re = /^(?=(.)(?=(.)\1\2)\2\1)\1\2/;
+testRE(re, "abab", true);
+testRE(re, "ababxxxxxxxx", true);
+testRE(re, "aba", false);
+execRE(re, "abab", ["ab", "a", "b"]);
+
+re = /^(?:(?=(.))a|b|c)$/;
+testRE(re, "a", true);
+testRE(re, "b", true);
+testRE(re, "c", true);
+testRE(re, "d", false);
+execRE(re, "a", ["a", "a"]);
+execRE(re, "b", ["b", undefined]);
+execRE(re, "c", ["c", undefined]);
+
+execRE(/^(?=(b))b/, "b", ["b", "b"]);
+execRE(/^(?:(?=(b))|a)b/, "ab", ["ab", undefined]);
+execRE(/^(?:(?=(b)(?:(?=(c))|d))|)bd/, "bd", ["bd", "b", undefined]);
+
+
+
+// Test of Negative Look-Ahead.
+
+re = /(?!x)./;
+testRE(re, "y", true);
+testRE(re, "x", false);
+execRE(re, "y", ["y"]);
+
+re = /(?!(\d))|\d/;
+testRE(re, "4", true);
+execRE(re, "4", ["4", undefined]);
+execRE(re, "x", ["", undefined]);
+
+
+// Test mixed nested look-ahead with captures.
+
+re = /^(?=(x)(?=(y)))/;
+testRE(re, "xy", true);
+testRE(re, "xz", false);
+execRE(re, "xy", ["", "x", "y"]);
+
+re = /^(?!(x)(?!(y)))/;
+testRE(re, "xy", true);
+testRE(re, "xz", false);
+execRE(re, "xy", ["", undefined, undefined]);
+
+re = /^(?=(x)(?!(y)))/;
+testRE(re, "xz", true);
+testRE(re, "xy", false)
+execRE(re, "xz", ["", "x", undefined]);
+
+re = /^(?!(x)(?=(y)))/;
+testRE(re, "xz", true);
+testRE(re, "xy", false);
+execRE(re, "xz", ["", undefined, undefined]);
+
+re = /^(?=(x)(?!(y)(?=(z))))/;
+testRE(re, "xaz", true);
+testRE(re, "xya", true);
+testRE(re, "xyz", false);
+testRE(re, "a", false);
+execRE(re, "xaz", ["", "x", undefined, undefined]);
+execRE(re, "xya", ["", "x", undefined, undefined]);
+
+re = /^(?!(x)(?=(y)(?!(z))))/;
+testRE(re, "a", true);
+testRE(re, "xa", true);
+testRE(re, "xyz", true);
+testRE(re, "xya", false);
+execRE(re, "a", ["", undefined, undefined, undefined]);
+execRE(re, "xa", ["", undefined, undefined, undefined]);
+execRE(re, "xyz", ["", undefined, undefined, undefined]);
diff --git a/test/mjsunit/regexp.js b/test/mjsunit/regexp.js
index 4422211..705754b 100644
--- a/test/mjsunit/regexp.js
+++ b/test/mjsunit/regexp.js
@@ -94,6 +94,22 @@
//assertTrue(/\c[a/]/.test( "\x1ba/]" ));
+// Test \c in character class
+re = /^[\cM]$/;
+assertTrue(re.test("\r"));
+assertFalse(re.test("M"));
+assertFalse(re.test("c"));
+assertFalse(re.test("\\"));
+assertFalse(re.test("\x03")); // I.e., read as \cc
+
+re = /^[\c]]$/;
+assertTrue(re.test("c]"));
+assertFalse(re.test("\\]"));
+assertFalse(re.test("\x1d")); // ']' & 0x1f
+assertFalse(re.test("\\]"));
+assertFalse(re.test("\x03]")); // I.e., read as \cc
+
+
// Test that we handle \s and \S correctly inside some bizarre
// character classes.
re = /[\s-:]/;
@@ -316,3 +332,7 @@
assertFalse(/()x\1y([0-7]%%%x|[0-6]%%%y)/.test('xy7%%%y'), 'qt6');
assertFalse(/xy([0-7]%%%x|[0-6]%%%y)/.test('xy7%%%y'), 'qt7');
assertFalse(/x([0-7]%%%x|[0-6]%%%y)/.test('x7%%%y'), 'qt8');
+
+
+// Don't hang on this one.
+/[^\xfe-\xff]*/.test("");
diff --git a/test/mjsunit/bugs/bug-187.js b/test/mjsunit/regress/regress-187.js
similarity index 100%
rename from test/mjsunit/bugs/bug-187.js
rename to test/mjsunit/regress/regress-187.js
diff --git a/test/mjsunit/regress/regress-189.js b/test/mjsunit/regress/regress-189.js
new file mode 100644
index 0000000..a84b620
--- /dev/null
+++ b/test/mjsunit/regress/regress-189.js
@@ -0,0 +1,36 @@
+// Copyright 2009 the V8 project authors. All rights reserved.
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following
+// disclaimer in the documentation and/or other materials provided
+// with the distribution.
+// * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived
+// from this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+// Test that we can handle initialization of a deleted const variable.
+
+// See http://code.google.com/p/v8/issues/detail?id=189.
+
+function f() {
+ eval("delete x; const x = 32");
+}
+
+f();
diff --git a/test/mjsunit/regress/regress-192.js b/test/mjsunit/regress/regress-192.js
new file mode 100644
index 0000000..8f0978f
--- /dev/null
+++ b/test/mjsunit/regress/regress-192.js
@@ -0,0 +1,38 @@
+// Copyright 2009 the V8 project authors. All rights reserved.
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following
+// disclaimer in the documentation and/or other materials provided
+// with the distribution.
+// * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived
+// from this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+// Test that exceptions are correctly propagated when creating object
+// literals.
+
+// See http://code.google.com/p/v8/issues/detail?id=192
+
+Object.prototype.__defineGetter__("x", function() {});
+
+// Creating this object literal will throw an exception because we are
+// assigning to a property that has only a getter.
+assertThrows("({ x: 42 })");
+
diff --git a/test/mjsunit/regress/regress-193.js b/test/mjsunit/regress/regress-193.js
new file mode 100644
index 0000000..f803483
--- /dev/null
+++ b/test/mjsunit/regress/regress-193.js
@@ -0,0 +1,44 @@
+// Copyright 2009 the V8 project authors. All rights reserved.
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following
+// disclaimer in the documentation and/or other materials provided
+// with the distribution.
+// * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived
+// from this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+// Test that context extension objects do not have a constructor
+// property.
+
+// See http://code.google.com/p/v8/issues/detail?id=193.
+
+function f() {
+ return eval("var x; constructor");
+}
+
+// It should be ok to call the constructor function returned by f.
+f()();
+
+// The call to f should get the constructor of the receiver which is
+// the constructor of the global object.
+assertEquals(constructor, f());
+
+
diff --git a/test/mjsunit/regress/regress-201.js b/test/mjsunit/regress/regress-201.js
new file mode 100644
index 0000000..8847fc0
--- /dev/null
+++ b/test/mjsunit/regress/regress-201.js
@@ -0,0 +1,37 @@
+// Copyright 2009 the V8 project authors. All rights reserved.
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following
+// disclaimer in the documentation and/or other materials provided
+// with the distribution.
+// * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived
+// from this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+// See http://code.google.com/p/v8/issues/detail?id=201.
+
+function testsort(n) {
+ n=1*n;
+ var numbers=new Array(n);
+ for (var i=0;i<n;i++) numbers[i]=i;
+ numbers.sort();
+}
+
+testsort("5001")
diff --git a/test/mozilla/mozilla.status b/test/mozilla/mozilla.status
index 2e5a823..97182f3 100644
--- a/test/mozilla/mozilla.status
+++ b/test/mozilla/mozilla.status
@@ -240,11 +240,8 @@
# 'minimum repeat count' is reached, the empty string must not match.
# In this case, we are similar but not identical to JSC. Hard to
# support the JS behavior with PCRE, so maybe emulate JSC?
-#
-# Note: We do not support toSource currently so we cannot run this
-# test. We should make an isolated test case for the regexp issue.
-ecma_3/RegExp/regress-209919: FAIL_OK
-js1_5/extensions/regress-459606: FAIL_OK
+ecma_3/RegExp/regress-209919: PASS || FAIL_OK
+js1_5/extensions/regress-459606: PASS || FAIL_OK
# PCRE's match limit is reached. SpiderMonkey hangs on the first one,
@@ -265,11 +262,6 @@
js1_5/Regress/regress-230216-2: FAIL_OK
-# According to ECMA-262, \b is a 'word' boundary, where words are only
-# ASCII characters. PCRE supports non-ASCII word characters.
-js1_5/Regress/regress-247179: FAIL_OK
-
-
# Regexp too long for PCRE.
js1_5/Regress/regress-280769: PASS || FAIL
js1_5/Regress/regress-280769-1: PASS || FAIL
@@ -471,7 +463,7 @@
# A non-breaking space doesn't match \s in a regular expression. This behaviour
# matches JSC. All the VMs have different behaviours in which characters match
# \s so we do the same as JSC until they change.
-ecma_3/Unicode/uc-002: FAIL_OK
+ecma_3/Unicode/uc-002: PASS || FAIL_OK
# String.prototype.split on empty strings always returns an array
@@ -521,10 +513,12 @@
# Regular expression test failures due to PCRE. We match JSC (ie, perl)
# behavior and not the ECMA spec.
-ecma_3/RegExp/15.10.2-1: FAIL_OK
-ecma_3/RegExp/perlstress-001: FAIL_OK
+ecma_3/RegExp/perlstress-001: PASS || FAIL_OK
ecma_3/RegExp/regress-334158: PASS || FAIL
+# This test fails due to http://code.google.com/p/v8/issues/detail?id=187
+# Failure to clear captures when a lookahead is unwound.
+ecma_3/RegExp/15.10.2-1: PASS || FAIL_OK
# This test requires a failure if we try to compile a function with more
# than 65536 arguments. This seems to be a Mozilla restriction.
diff --git a/tools/v8.xcodeproj/project.pbxproj b/tools/v8.xcodeproj/project.pbxproj
index 8adfd0e..aa1790d 100644
--- a/tools/v8.xcodeproj/project.pbxproj
+++ b/tools/v8.xcodeproj/project.pbxproj
@@ -16,6 +16,7 @@
7BF8919B0E7309AD000BAF8A /* PBXTargetDependency */,
7BF891970E73099F000BAF8A /* PBXTargetDependency */,
7BF891990E73099F000BAF8A /* PBXTargetDependency */,
+ 893988100F2A3647007D5254 /* PBXTargetDependency */,
896FD03E0E78D731003DFB6A /* PBXTargetDependency */,
896FD0400E78D735003DFB6A /* PBXTargetDependency */,
);
@@ -31,6 +32,11 @@
890A14020EE9C4B400E49346 /* regexp-macro-assembler-irregexp.cc in Sources */ = {isa = PBXBuildFile; fileRef = 89A15C750EE466D000B48DEB /* regexp-macro-assembler-irregexp.cc */; };
890A14030EE9C4B500E49346 /* regexp-macro-assembler-tracer.cc in Sources */ = {isa = PBXBuildFile; fileRef = 89A15C770EE466D000B48DEB /* regexp-macro-assembler-tracer.cc */; };
890A14040EE9C4B700E49346 /* regexp-macro-assembler.cc in Sources */ = {isa = PBXBuildFile; fileRef = 89A15C790EE466D000B48DEB /* regexp-macro-assembler.cc */; };
+ 893988060F2A35FA007D5254 /* libjscre.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 897FF1BF0E719CB600D62E90 /* libjscre.a */; };
+ 893988070F2A35FA007D5254 /* libv8.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 8970F2F00E719FB2006AE7B5 /* libv8.a */; };
+ 8939880D0F2A362A007D5254 /* d8.cc in Sources */ = {isa = PBXBuildFile; fileRef = 89A15C920EE46A1700B48DEB /* d8.cc */; };
+ 893988160F2A3688007D5254 /* d8-debug.cc in Sources */ = {isa = PBXBuildFile; fileRef = 893988150F2A3686007D5254 /* d8-debug.cc */; };
+ 893988330F2A3B8F007D5254 /* d8-js.cc in Sources */ = {isa = PBXBuildFile; fileRef = 893988320F2A3B8B007D5254 /* d8-js.cc */; };
893CCE640E71D83700357A03 /* code-stubs.cc in Sources */ = {isa = PBXBuildFile; fileRef = 897FF1110E719B8F00D62E90 /* code-stubs.cc */; };
8944AD100F1D4D500028D560 /* regexp-stack.cc in Sources */ = {isa = PBXBuildFile; fileRef = 8944AD0E0F1D4D3A0028D560 /* regexp-stack.cc */; };
8944AD110F1D4D570028D560 /* regexp-stack.cc in Sources */ = {isa = PBXBuildFile; fileRef = 8944AD0E0F1D4D3A0028D560 /* regexp-stack.cc */; };
@@ -213,6 +219,27 @@
remoteGlobalIDString = 897FF1BE0E719CB600D62E90;
remoteInfo = jscre;
};
+ 893988000F2A35FA007D5254 /* PBXContainerItemProxy */ = {
+ isa = PBXContainerItemProxy;
+ containerPortal = 8915B8680E719336009C4E19 /* Project object */;
+ proxyType = 1;
+ remoteGlobalIDString = 897FF1BE0E719CB600D62E90;
+ remoteInfo = jscre;
+ };
+ 893988020F2A35FA007D5254 /* PBXContainerItemProxy */ = {
+ isa = PBXContainerItemProxy;
+ containerPortal = 8915B8680E719336009C4E19 /* Project object */;
+ proxyType = 1;
+ remoteGlobalIDString = 8970F2EF0E719FB2006AE7B5;
+ remoteInfo = v8;
+ };
+ 8939880F0F2A3647007D5254 /* PBXContainerItemProxy */ = {
+ isa = PBXContainerItemProxy;
+ containerPortal = 8915B8680E719336009C4E19 /* Project object */;
+ proxyType = 1;
+ remoteGlobalIDString = 893987FE0F2A35FA007D5254;
+ remoteInfo = d8_shell;
+ };
896FD03B0E78D71F003DFB6A /* PBXContainerItemProxy */ = {
isa = PBXContainerItemProxy;
containerPortal = 8915B8680E719336009C4E19 /* Project object */;
@@ -259,6 +286,10 @@
/* Begin PBXFileReference section */
8900116B0E71CA2300F91F35 /* libraries.cc */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = libraries.cc; sourceTree = "<group>"; };
+ 893986D40F29020C007D5254 /* apiutils.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = apiutils.h; sourceTree = "<group>"; };
+ 8939880B0F2A35FA007D5254 /* v8_shell */ = {isa = PBXFileReference; explicitFileType = "compiled.mach-o.executable"; includeInIndex = 0; path = v8_shell; sourceTree = BUILT_PRODUCTS_DIR; };
+ 893988150F2A3686007D5254 /* d8-debug.cc */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = "d8-debug.cc"; path = "../src/d8-debug.cc"; sourceTree = "<group>"; };
+ 893988320F2A3B8B007D5254 /* d8-js.cc */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = "d8-js.cc"; sourceTree = "<group>"; };
8944AD0E0F1D4D3A0028D560 /* regexp-stack.cc */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = "regexp-stack.cc"; sourceTree = "<group>"; };
8944AD0F0F1D4D3A0028D560 /* regexp-stack.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "regexp-stack.h"; sourceTree = "<group>"; };
89471C7F0EB23EE400B6874B /* flag-definitions.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "flag-definitions.h"; sourceTree = "<group>"; };
@@ -504,6 +535,15 @@
/* End PBXFileReference section */
/* Begin PBXFrameworksBuildPhase section */
+ 893988050F2A35FA007D5254 /* Frameworks */ = {
+ isa = PBXFrameworksBuildPhase;
+ buildActionMask = 2147483647;
+ files = (
+ 893988060F2A35FA007D5254 /* libjscre.a in Frameworks */,
+ 893988070F2A35FA007D5254 /* libv8.a in Frameworks */,
+ );
+ runOnlyForDeploymentPostprocessing = 0;
+ };
8970F2EE0E719FB2006AE7B5 /* Frameworks */ = {
isa = PBXFrameworksBuildPhase;
buildActionMask = 2147483647;
@@ -595,6 +635,7 @@
897FF0F90E719B8F00D62E90 /* allocation.h */,
897FF0FA0E719B8F00D62E90 /* api.cc */,
897FF0FB0E719B8F00D62E90 /* api.h */,
+ 893986D40F29020C007D5254 /* apiutils.h */,
897FF0FC0E719B8F00D62E90 /* arguments.h */,
897FF0FD0E719B8F00D62E90 /* assembler-arm-inl.h */,
897FF0FE0E719B8F00D62E90 /* assembler-arm.cc */,
@@ -854,6 +895,7 @@
isa = PBXGroup;
children = (
89A15C910EE46A1700B48DEB /* d8-readline.cc */,
+ 893988150F2A3686007D5254 /* d8-debug.cc */,
89A15C920EE46A1700B48DEB /* d8.cc */,
89A15C930EE46A1700B48DEB /* d8.h */,
89A15C940EE46A1700B48DEB /* d8.js */,
@@ -880,6 +922,7 @@
897F767A0E71B4CC007ACF34 /* v8_shell */,
89F23C870E78D5B2006B2466 /* libv8-arm.a */,
89F23C950E78D5B6006B2466 /* v8_shell-arm */,
+ 8939880B0F2A35FA007D5254 /* v8_shell */,
);
name = Products;
sourceTree = "<group>";
@@ -887,6 +930,7 @@
89A9C1630E71C8E300BE6CCA /* generated */ = {
isa = PBXGroup;
children = (
+ 893988320F2A3B8B007D5254 /* d8-js.cc */,
8900116B0E71CA2300F91F35 /* libraries.cc */,
);
path = generated;
@@ -895,6 +939,25 @@
/* End PBXGroup section */
/* Begin PBXNativeTarget section */
+ 893987FE0F2A35FA007D5254 /* d8_shell */ = {
+ isa = PBXNativeTarget;
+ buildConfigurationList = 893988080F2A35FA007D5254 /* Build configuration list for PBXNativeTarget "d8_shell" */;
+ buildPhases = (
+ 893988220F2A376C007D5254 /* ShellScript */,
+ 893988030F2A35FA007D5254 /* Sources */,
+ 893988050F2A35FA007D5254 /* Frameworks */,
+ );
+ buildRules = (
+ );
+ dependencies = (
+ 893987FF0F2A35FA007D5254 /* PBXTargetDependency */,
+ 893988010F2A35FA007D5254 /* PBXTargetDependency */,
+ );
+ name = d8_shell;
+ productName = v8_shell;
+ productReference = 8939880B0F2A35FA007D5254 /* v8_shell */;
+ productType = "com.apple.product-type.tool";
+ };
8970F2EF0E719FB2006AE7B5 /* v8 */ = {
isa = PBXNativeTarget;
buildConfigurationList = 8970F2F70E719FC1006AE7B5 /* Build configuration list for PBXNativeTarget "v8" */;
@@ -998,6 +1061,7 @@
897FF1BE0E719CB600D62E90 /* jscre */,
8970F2EF0E719FB2006AE7B5 /* v8 */,
897F76790E71B4CC007ACF34 /* v8_shell */,
+ 893987FE0F2A35FA007D5254 /* d8_shell */,
89F23C3C0E78D5B2006B2466 /* v8-arm */,
89F23C880E78D5B6006B2466 /* v8_shell-arm */,
);
@@ -1005,6 +1069,19 @@
/* End PBXProject section */
/* Begin PBXShellScriptBuildPhase section */
+ 893988220F2A376C007D5254 /* ShellScript */ = {
+ isa = PBXShellScriptBuildPhase;
+ buildActionMask = 2147483647;
+ files = (
+ );
+ inputPaths = (
+ );
+ outputPaths = (
+ );
+ runOnlyForDeploymentPostprocessing = 0;
+ shellPath = /bin/sh;
+ shellScript = "set -ex\nJS_FILES=\"d8.js\"\\\n\" macros.py\"\n\nV8ROOT=\"${SRCROOT}/..\"\n\nSRC_DIR=\"${V8ROOT}/src\"\n\nNATIVE_JS_FILES=\"\"\n\nfor i in ${JS_FILES} ; do\n NATIVE_JS_FILES+=\"${SRC_DIR}/${i} \"\ndone\n\nV8_GENERATED_SOURCES_DIR=\"${CONFIGURATION_TEMP_DIR}/generated\"\nmkdir -p \"${V8_GENERATED_SOURCES_DIR}\"\n\nD8_CC=\"${V8_GENERATED_SOURCES_DIR}/d8-js.cc\"\nD8_EMPTY_CC=\"${V8_GENERATED_SOURCES_DIR}/d8-js-empty.cc\"\n\npython \"${V8ROOT}/tools/js2c.py\" \\\n \"${D8_CC}.new\" \\\n \"${D8_EMPTY_CC}.new\" \\\n \"D8\" \\\n ${NATIVE_JS_FILES}\n\n# Only use the new files if they're different from the existing files (if any),\n# preserving the existing files' timestamps when there are no changes. This\n# minimizes unnecessary build activity for a no-change build.\n\nif ! diff -q \"${D8_CC}.new\" \"${D8_CC}\" >& /dev/null ; then\n mv \"${D8_CC}.new\" \"${D8_CC}\"\nelse\n rm \"${D8_CC}.new\"\nfi\n\nif ! diff -q \"${D8_EMPTY_CC}.new\" \"${D8_EMPTY_CC}\" >& /dev/null ; then\n mv \"${D8_EMPTY_CC}.new\" \"${D8_EMPTY_CC}\"\nelse\n rm \"${D8_EMPTY_CC}.new\"\nfi\n";
+ };
89EA6FB50E71AA1F00F59E1B /* ShellScript */ = {
isa = PBXShellScriptBuildPhase;
buildActionMask = 2147483647;
@@ -1034,6 +1111,16 @@
/* End PBXShellScriptBuildPhase section */
/* Begin PBXSourcesBuildPhase section */
+ 893988030F2A35FA007D5254 /* Sources */ = {
+ isa = PBXSourcesBuildPhase;
+ buildActionMask = 2147483647;
+ files = (
+ 8939880D0F2A362A007D5254 /* d8.cc in Sources */,
+ 893988160F2A3688007D5254 /* d8-debug.cc in Sources */,
+ 893988330F2A3B8F007D5254 /* d8-js.cc in Sources */,
+ );
+ runOnlyForDeploymentPostprocessing = 0;
+ };
8970F2ED0E719FB2006AE7B5 /* Sources */ = {
isa = PBXSourcesBuildPhase;
buildActionMask = 2147483647;
@@ -1247,6 +1334,21 @@
target = 897FF1BE0E719CB600D62E90 /* jscre */;
targetProxy = 7BF8919A0E7309AD000BAF8A /* PBXContainerItemProxy */;
};
+ 893987FF0F2A35FA007D5254 /* PBXTargetDependency */ = {
+ isa = PBXTargetDependency;
+ target = 897FF1BE0E719CB600D62E90 /* jscre */;
+ targetProxy = 893988000F2A35FA007D5254 /* PBXContainerItemProxy */;
+ };
+ 893988010F2A35FA007D5254 /* PBXTargetDependency */ = {
+ isa = PBXTargetDependency;
+ target = 8970F2EF0E719FB2006AE7B5 /* v8 */;
+ targetProxy = 893988020F2A35FA007D5254 /* PBXContainerItemProxy */;
+ };
+ 893988100F2A3647007D5254 /* PBXTargetDependency */ = {
+ isa = PBXTargetDependency;
+ target = 893987FE0F2A35FA007D5254 /* d8_shell */;
+ targetProxy = 8939880F0F2A3647007D5254 /* PBXContainerItemProxy */;
+ };
896FD03C0E78D71F003DFB6A /* PBXTargetDependency */ = {
isa = PBXTargetDependency;
target = 89F23C3C0E78D5B2006B2466 /* v8-arm */;
@@ -1366,6 +1468,22 @@
};
name = Release;
};
+ 893988090F2A35FA007D5254 /* Debug */ = {
+ isa = XCBuildConfiguration;
+ buildSettings = {
+ HEADER_SEARCH_PATHS = ../src;
+ PRODUCT_NAME = v8_shell;
+ };
+ name = Debug;
+ };
+ 8939880A0F2A35FA007D5254 /* Release */ = {
+ isa = XCBuildConfiguration;
+ buildSettings = {
+ HEADER_SEARCH_PATHS = ../src;
+ PRODUCT_NAME = v8_shell;
+ };
+ name = Release;
+ };
8970F2F10E719FB2006AE7B5 /* Debug */ = {
isa = XCBuildConfiguration;
buildSettings = {
@@ -1504,6 +1622,15 @@
defaultConfigurationIsVisible = 0;
defaultConfigurationName = Release;
};
+ 893988080F2A35FA007D5254 /* Build configuration list for PBXNativeTarget "d8_shell" */ = {
+ isa = XCConfigurationList;
+ buildConfigurations = (
+ 893988090F2A35FA007D5254 /* Debug */,
+ 8939880A0F2A35FA007D5254 /* Release */,
+ );
+ defaultConfigurationIsVisible = 0;
+ defaultConfigurationName = Release;
+ };
8970F2F70E719FC1006AE7B5 /* Build configuration list for PBXNativeTarget "v8" */ = {
isa = XCConfigurationList;
buildConfigurations = (