Fixed string length bug on ARM (issue 171).
Made most methods in the API const.
Optimized object literals by improving data locality.
Fixed bug that caused incomplete functions to be cached in case of stack overflow exceptions.
Fixed bugs that caused catch variables and variables introduced by eval to behave incorrectly when using accessors (issues 186, 190 and 191).
git-svn-id: http://v8.googlecode.com/svn/trunk@1095 ce2b1a6d-e550-0410-aec6-3dcde31c8c00
diff --git a/ChangeLog b/ChangeLog
index 17fce33..25ace2e 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,16 +1,32 @@
+2009-01-16: Version 0.4.8
+
+ Fixed string length bug on ARM (issue 171).
+
+ Made most methods in the API const.
+
+ Optimized object literals by improving data locality.
+
+ Fixed bug that caused incomplete functions to be cached in case of
+ stack overflow exceptions.
+
+ Fixed bugs that caused catch variables and variables introduced by
+ eval to behave incorrectly when using accessors (issues 186, 190
+ and 191).
+
+
2009-01-06: Version 0.4.7
- Minor bugfixes and optimizations.
+ Minor bugfixes and optimizations.
- Added command line debugger to D8 shell.
+ Added command line debugger to D8 shell.
- Fixed subtle bug that caused the wrong 'this' to be used when
- calling a caught function in a catch clause.
+ Fixed subtle bug that caused the wrong 'this' to be used when
+ calling a caught function in a catch clause.
- Inline array loads within loops directly in the code instead of
- always calling a stub.
+ Inline array loads within loops directly in the code instead of
+ always calling a stub.
-
+
2008-12-11: Version 0.4.6
Fixed exception reporting bug where certain exceptions were
@@ -32,7 +48,7 @@
collection. Entire object groups are now passed to V8 instead of
individual members of the groups.
-
+
2008-12-03: Version 0.4.5
Added experimental API support for allocating V8 symbols as
@@ -51,7 +67,7 @@
Added support for building V8 on Windows with either the shared or
static version of MSVCRT
-
+
Added the v8::jscre namespace around the jscre functions to avoid
link errors (duplicate symbols) when building Google Chrome.
@@ -94,8 +110,8 @@
context of the associated global object.
Treat byte-order marks as whitespace characters.
-
-
+
+
2008-11-04: Version 0.4.3
Added support for API accessors that prohibit overwriting by
@@ -111,7 +127,7 @@
Fixed bug in fast-case code for switch statements that only have
integer labels.
-
+
2008-10-30: Version 0.4.2
@@ -160,7 +176,7 @@
Fixed issues with message reporting for exceptions in try-finally
blocks (issues 73 and 75).
- Optimized flattening of strings and string equality checking.
+ Optimized flattening of strings and string equality checking.
Improved Boyer-Moore implementation for faster indexOf operations.
@@ -200,7 +216,7 @@
to print [native code] instead of the actual source. Some web
applications do not like constructors with complex toString
results.
-
+
2008-10-06: Version 0.3.4
@@ -249,7 +265,7 @@
literal maps. This will allow JSON objects with the same set of
properties to share the same map making inline caching work better
for JSON objects.
-
+
2008-09-17: Version 0.3.2
@@ -322,8 +338,8 @@
Fixed bug in error reporting when attempting to convert null to an
object.
-
-
+
+
2008-09-04: Version 0.3.0
Added support for running tests on the ARM simulator.
@@ -389,7 +405,7 @@
Fixed bug in the runtime system where the prototype chain was not
always searched for a setter when setting a property that does not
exist locally.
-
+
2008-08-14: Version 0.2.3
diff --git a/benchmarks/base.js b/benchmarks/base.js
index 5afc110..6e1ff8c 100644
--- a/benchmarks/base.js
+++ b/benchmarks/base.js
@@ -120,7 +120,8 @@
}
if (runner.NotifyScore) {
var score = BenchmarkSuite.GeometricMean(BenchmarkSuite.scores);
- runner.NotifyScore(100 * score);
+ var formatted = BenchmarkSuite.FormatScore(100 * score);
+ runner.NotifyScore(formatted);
}
}
RunStep();
@@ -149,6 +150,16 @@
}
+// Converts a score value to a string with at least three significant
+// digits.
+BenchmarkSuite.FormatScore = function(value) {
+ if (value > 100) {
+ return value.toFixed(0);
+ } else {
+ return value.toPrecision(3);
+ }
+}
+
// Notifies the runner that we're done running a single benchmark in
// the benchmark suite. This can be useful to report progress.
BenchmarkSuite.prototype.NotifyStep = function(result) {
@@ -164,7 +175,8 @@
var score = this.reference / mean;
BenchmarkSuite.scores.push(score);
if (this.runner.NotifyResult) {
- this.runner.NotifyResult(this.name, 100 * score);
+ var formatted = BenchmarkSuite.FormatScore(100 * score);
+ this.runner.NotifyResult(this.name, formatted);
}
}
@@ -219,14 +231,3 @@
}
return RunNext();
}
-
-
-// Converts a score value to a string with at least three significant
-// digits.
-function formatScore(value) {
- if (value > 100) {
- return value.toFixed(0);
- } else {
- return value.toPrecision(3);
- }
-}
diff --git a/benchmarks/run.html b/benchmarks/run.html
index d521e0b..01cadc0 100644
--- a/benchmarks/run.html
+++ b/benchmarks/run.html
@@ -21,7 +21,7 @@
function AddResult(name, result) {
- var text = name + ': ' + formatScore(result);
+ var text = name + ': ' + result;
var results = document.getElementById("results");
results.innerHTML += (text + "<br/>");
}
@@ -36,7 +36,7 @@
function AddScore(score) {
var status = document.getElementById("status");
if (success) {
- status.innerHTML = "Score: " + formatScore(score);
+ status.innerHTML = "Score: " + score;
}
}
diff --git a/benchmarks/run.js b/benchmarks/run.js
index ecfee90..da2373b 100644
--- a/benchmarks/run.js
+++ b/benchmarks/run.js
@@ -36,7 +36,7 @@
var success = true;
function PrintResult(name, result) {
- print(name + ': ' + formatScore(result));
+ print(name + ': ' + result);
}
@@ -49,8 +49,7 @@
function PrintScore(score) {
if (success) {
print('----');
- print('Score (version ' + BenchmarkSuite.version + '): '
- + formatScore(score));
+ print('Score (version ' + BenchmarkSuite.version + '): ' + score);
}
}
diff --git a/include/v8.h b/include/v8.h
index 2a8f86d..1876a54 100644
--- a/include/v8.h
+++ b/include/v8.h
@@ -200,11 +200,11 @@
/**
* Returns true if the handle is empty.
*/
- bool IsEmpty() { return val_ == 0; }
+ bool IsEmpty() const { return val_ == 0; }
- T* operator->();
+ T* operator->() const;
- T* operator*();
+ T* operator*() const;
/**
* Sets the handle to be empty. IsEmpty() will then return true.
@@ -217,7 +217,7 @@
* to which they refer are identical.
* The handles' references are not checked.
*/
- template <class S> bool operator==(Handle<S> that) {
+ template <class S> bool operator==(Handle<S> that) const {
void** a = reinterpret_cast<void**>(**this);
void** b = reinterpret_cast<void**>(*that);
if (a == 0) return b == 0;
@@ -231,7 +231,7 @@
* the objects to which they refer are different.
* The handles' references are not checked.
*/
- template <class S> bool operator!=(Handle<S> that) {
+ template <class S> bool operator!=(Handle<S> that) const {
return !operator==(that);
}
@@ -367,12 +367,12 @@
/**
*Checks if the handle holds the only reference to an object.
*/
- bool IsNearDeath();
+ bool IsNearDeath() const;
/**
* Returns true if the handle's reference is weak.
*/
- bool IsWeak();
+ bool IsWeak() const;
private:
friend class ImplementationUtilities;
@@ -556,39 +556,39 @@
*/
class EXPORT Message {
public:
- Local<String> Get();
- Local<String> GetSourceLine();
+ Local<String> Get() const;
+ Local<String> GetSourceLine() const;
- Handle<Value> GetScriptResourceName();
+ Handle<Value> GetScriptResourceName() const;
/**
* Returns the number, 1-based, of the line where the error occurred.
*/
- int GetLineNumber();
+ int GetLineNumber() const;
/**
* Returns the index within the script of the first character where
* the error occurred.
*/
- int GetStartPosition();
+ int GetStartPosition() const;
/**
* Returns the index within the script of the last character where
* the error occurred.
*/
- int GetEndPosition();
+ int GetEndPosition() const;
/**
* Returns the index within the line of the first character where
* the error occurred.
*/
- int GetStartColumn();
+ int GetStartColumn() const;
/**
* Returns the index within the line of the last character where
* the error occurred.
*/
- int GetEndColumn();
+ int GetEndColumn() const;
// TODO(1245381): Print to a string instead of on a FILE.
static void PrintCurrentStackTrace(FILE* out);
@@ -608,94 +608,94 @@
* Returns true if this value is the undefined value. See ECMA-262
* 4.3.10.
*/
- bool IsUndefined();
+ bool IsUndefined() const;
/**
* Returns true if this value is the null value. See ECMA-262
* 4.3.11.
*/
- bool IsNull();
+ bool IsNull() const;
/**
* Returns true if this value is true.
*/
- bool IsTrue();
+ bool IsTrue() const;
/**
* Returns true if this value is false.
*/
- bool IsFalse();
+ bool IsFalse() const;
/**
* Returns true if this value is an instance of the String type.
* See ECMA-262 8.4.
*/
- bool IsString();
+ bool IsString() const;
/**
* Returns true if this value is a function.
*/
- bool IsFunction();
+ bool IsFunction() const;
/**
* Returns true if this value is an array.
*/
- bool IsArray();
+ bool IsArray() const;
/**
* Returns true if this value is an object.
*/
- bool IsObject();
+ bool IsObject() const;
/**
* Returns true if this value is boolean.
*/
- bool IsBoolean();
+ bool IsBoolean() const;
/**
* Returns true if this value is a number.
*/
- bool IsNumber();
+ bool IsNumber() const;
/**
* Returns true if this value is external.
*/
- bool IsExternal();
+ bool IsExternal() const;
/**
* Returns true if this value is a 32-bit signed integer.
*/
- bool IsInt32();
+ bool IsInt32() const;
/**
* Returns true if this value is a Date.
*/
- bool IsDate();
+ bool IsDate() const;
- Local<Boolean> ToBoolean();
- Local<Number> ToNumber();
- Local<String> ToString();
- Local<String> ToDetailString();
- Local<Object> ToObject();
- Local<Integer> ToInteger();
- Local<Uint32> ToUint32();
- Local<Int32> ToInt32();
+ Local<Boolean> ToBoolean() const;
+ Local<Number> ToNumber() const;
+ Local<String> ToString() const;
+ Local<String> ToDetailString() const;
+ Local<Object> ToObject() const;
+ Local<Integer> ToInteger() const;
+ Local<Uint32> ToUint32() const;
+ Local<Int32> ToInt32() const;
/**
* Attempts to convert a string to an array index.
* Returns an empty handle if the conversion fails.
*/
- Local<Uint32> ToArrayIndex();
+ Local<Uint32> ToArrayIndex() const;
- bool BooleanValue();
- double NumberValue();
- int64_t IntegerValue();
- uint32_t Uint32Value();
- int32_t Int32Value();
+ bool BooleanValue() const;
+ double NumberValue() const;
+ int64_t IntegerValue() const;
+ uint32_t Uint32Value() const;
+ int32_t Int32Value() const;
/** JS == */
- bool Equals(Handle<Value> that);
- bool StrictEquals(Handle<Value> that);
+ bool Equals(Handle<Value> that) const;
+ bool StrictEquals(Handle<Value> that) const;
};
@@ -711,7 +711,7 @@
*/
class EXPORT Boolean : public Primitive {
public:
- bool Value();
+ bool Value() const;
static inline Handle<Boolean> New(bool value);
};
@@ -725,13 +725,13 @@
/**
* Returns the number of characters in this string.
*/
- int Length();
+ int Length() const;
/**
* Returns the number of bytes in the UTF-8 encoded
* representation of this string.
*/
- int Utf8Length();
+ int Utf8Length() const;
/**
* Write the contents of the string to an external buffer.
@@ -750,19 +750,19 @@
* \return The number of characters copied to the buffer
* excluding the NULL terminator.
*/
- int Write(uint16_t* buffer, int start = 0, int length = -1); // UTF-16
- int WriteAscii(char* buffer, int start = 0, int length = -1); // ASCII
- int WriteUtf8(char* buffer, int length = -1); // UTF-8
+ int Write(uint16_t* buffer, int start = 0, int length = -1) const; // UTF-16
+ int WriteAscii(char* buffer, int start = 0, int length = -1) const; // ASCII
+ int WriteUtf8(char* buffer, int length = -1) const; // UTF-8
/**
* Returns true if the string is external
*/
- bool IsExternal();
+ bool IsExternal() const;
/**
* Returns true if the string is both external and ascii
*/
- bool IsExternalAscii();
+ bool IsExternalAscii() const;
/**
* An ExternalStringResource is a wrapper around a two-byte string
* buffer that resides outside V8's heap. Implement an
@@ -822,13 +822,13 @@
* Get the ExternalStringResource for an external string. Only
* valid if IsExternal() returns true.
*/
- ExternalStringResource* GetExternalStringResource();
+ ExternalStringResource* GetExternalStringResource() const;
/**
* Get the ExternalAsciiStringResource for an external ascii string.
* Only valid if IsExternalAscii() returns true.
*/
- ExternalAsciiStringResource* GetExternalAsciiStringResource();
+ ExternalAsciiStringResource* GetExternalAsciiStringResource() const;
static String* Cast(v8::Value* obj);
@@ -938,7 +938,7 @@
*/
class EXPORT Number : public Primitive {
public:
- double Value();
+ double Value() const;
static Local<Number> New(double value);
static Number* Cast(v8::Value* obj);
private:
@@ -952,7 +952,7 @@
class EXPORT Integer : public Number {
public:
static Local<Integer> New(int32_t value);
- int64_t Value();
+ int64_t Value() const;
static Integer* Cast(v8::Value* obj);
private:
Integer();
@@ -964,7 +964,7 @@
*/
class EXPORT Int32 : public Integer {
public:
- int32_t Value();
+ int32_t Value() const;
private:
Int32();
};
@@ -975,7 +975,7 @@
*/
class EXPORT Uint32 : public Integer {
public:
- uint32_t Value();
+ uint32_t Value() const;
private:
Uint32();
};
@@ -992,7 +992,7 @@
* A specialization of Value::NumberValue that is more efficient
* because we know the structure of this object.
*/
- double NumberValue();
+ double NumberValue() const;
static Date* Cast(v8::Value* obj);
};
@@ -1087,7 +1087,7 @@
*/
class EXPORT Array : public Object {
public:
- uint32_t Length();
+ uint32_t Length() const;
static Local<Array> New(int length = 0);
static Array* Cast(Value* obj);
@@ -1101,11 +1101,11 @@
*/
class EXPORT Function : public Object {
public:
- Local<Object> NewInstance();
- Local<Object> NewInstance(int argc, Handle<Value> argv[]);
+ Local<Object> NewInstance() const;
+ Local<Object> NewInstance(int argc, Handle<Value> argv[]) const;
Local<Value> Call(Handle<Object> recv, int argc, Handle<Value> argv[]);
void SetName(Handle<String> name);
- Handle<Value> GetName();
+ Handle<Value> GetName() const;
static Function* Cast(Value* obj);
private:
Function();
@@ -1121,7 +1121,7 @@
public:
static Local<External> New(void* value);
static External* Cast(Value* obj);
- void* Value();
+ void* Value() const;
private:
External();
};
@@ -2322,14 +2322,14 @@
template <class T>
-bool Persistent<T>::IsNearDeath() {
+bool Persistent<T>::IsNearDeath() const {
if (this->IsEmpty()) return false;
return V8::IsGlobalNearDeath(reinterpret_cast<void**>(**this));
}
template <class T>
-bool Persistent<T>::IsWeak() {
+bool Persistent<T>::IsWeak() const {
if (this->IsEmpty()) return false;
return V8::IsGlobalWeak(reinterpret_cast<void**>(**this));
}
@@ -2356,13 +2356,13 @@
}
template <class T>
-T* Handle<T>::operator->() {
+T* Handle<T>::operator->() const {
return val_;
}
template <class T>
-T* Handle<T>::operator*() {
+T* Handle<T>::operator*() const {
return val_;
}
diff --git a/src/SConscript b/src/SConscript
index c1bd12b..092fe8d 100644
--- a/src/SConscript
+++ b/src/SConscript
@@ -44,11 +44,11 @@
'interpreter-irregexp.cc', 'jsregexp.cc', 'log.cc', 'mark-compact.cc',
'messages.cc', 'objects.cc', 'parser.cc', 'property.cc',
'regexp-macro-assembler.cc', 'regexp-macro-assembler-irregexp.cc',
- 'rewriter.cc', 'runtime.cc', 'scanner.cc', 'scopeinfo.cc', 'scopes.cc',
- 'serialize.cc', 'snapshot-common.cc', 'spaces.cc', 'string-stream.cc',
- 'stub-cache.cc', 'token.cc', 'top.cc', 'unicode.cc', 'usage-analyzer.cc',
- 'utils.cc', 'v8-counters.cc', 'v8.cc', 'v8threads.cc', 'variables.cc',
- 'zone.cc'
+ 'regexp-stack.cc', 'rewriter.cc', 'runtime.cc', 'scanner.cc',
+ 'scopeinfo.cc', 'scopes.cc', 'serialize.cc', 'snapshot-common.cc',
+ 'spaces.cc', 'string-stream.cc', 'stub-cache.cc', 'token.cc', 'top.cc',
+ 'unicode.cc', 'usage-analyzer.cc', 'utils.cc', 'v8-counters.cc', 'v8.cc',
+ 'v8threads.cc', 'variables.cc', 'zone.cc'
],
'arch:arm': ['assembler-arm.cc', 'builtins-arm.cc', 'codegen-arm.cc',
'cpu-arm.cc', 'debug-arm.cc', 'disasm-arm.cc', 'frames-arm.cc',
diff --git a/src/api.cc b/src/api.cc
index e227824..c7e8409 100644
--- a/src/api.cc
+++ b/src/api.cc
@@ -140,7 +140,7 @@
static bool ReportV8Dead(const char* location) {
FatalErrorCallback callback = GetFatalErrorHandler();
- callback(location, "V8 is no longer useable");
+ callback(location, "V8 is no longer usable");
return true;
}
@@ -153,7 +153,7 @@
/**
- * IsDeadCheck checks that the vm is useable. If, for instance, the vm has been
+ * IsDeadCheck checks that the vm is usable. If, for instance, the vm has been
* out of memory at some point this check will fail. It should be called on
* entry to all methods that touch anything in the heap, except destructors
* which you sometimes can't avoid calling after the vm has crashed. Functions
@@ -174,7 +174,7 @@
}
-static inline bool EmptyCheck(const char* location, v8::Data* obj) {
+static inline bool EmptyCheck(const char* location, const v8::Data* obj) {
return (obj == 0) ? ReportEmptyHandle(location) : false;
}
@@ -1137,7 +1137,7 @@
// --- M e s s a g e ---
-Local<String> Message::Get() {
+Local<String> Message::Get() const {
ON_BAILOUT("v8::Message::Get()", return Local<String>());
HandleScope scope;
i::Handle<i::Object> obj = Utils::OpenHandle(this);
@@ -1147,7 +1147,7 @@
}
-v8::Handle<Value> Message::GetScriptResourceName() {
+v8::Handle<Value> Message::GetScriptResourceName() const {
if (IsDeadCheck("v8::Message::GetScriptResourceName()")) {
return Local<String>();
}
@@ -1189,7 +1189,7 @@
}
-int Message::GetLineNumber() {
+int Message::GetLineNumber() const {
ON_BAILOUT("v8::Message::GetLineNumber()", return -1);
HandleScope scope;
EXCEPTION_PREAMBLE();
@@ -1201,7 +1201,7 @@
}
-int Message::GetStartPosition() {
+int Message::GetStartPosition() const {
if (IsDeadCheck("v8::Message::GetStartPosition()")) return 0;
HandleScope scope;
@@ -1210,7 +1210,7 @@
}
-int Message::GetEndPosition() {
+int Message::GetEndPosition() const {
if (IsDeadCheck("v8::Message::GetEndPosition()")) return 0;
HandleScope scope;
i::Handle<i::JSObject> data_obj = Utils::OpenHandle(this);
@@ -1218,7 +1218,7 @@
}
-int Message::GetStartColumn() {
+int Message::GetStartColumn() const {
if (IsDeadCheck("v8::Message::GetStartColumn()")) return 0;
HandleScope scope;
i::Handle<i::JSObject> data_obj = Utils::OpenHandle(this);
@@ -1232,7 +1232,7 @@
}
-int Message::GetEndColumn() {
+int Message::GetEndColumn() const {
if (IsDeadCheck("v8::Message::GetEndColumn()")) return 0;
HandleScope scope;
i::Handle<i::JSObject> data_obj = Utils::OpenHandle(this);
@@ -1248,7 +1248,7 @@
}
-Local<String> Message::GetSourceLine() {
+Local<String> Message::GetSourceLine() const {
ON_BAILOUT("v8::Message::GetSourceLine()", return Local<String>());
HandleScope scope;
EXCEPTION_PREAMBLE();
@@ -1272,73 +1272,73 @@
// --- D a t a ---
-bool Value::IsUndefined() {
+bool Value::IsUndefined() const {
if (IsDeadCheck("v8::Value::IsUndefined()")) return false;
return Utils::OpenHandle(this)->IsUndefined();
}
-bool Value::IsNull() {
+bool Value::IsNull() const {
if (IsDeadCheck("v8::Value::IsNull()")) return false;
return Utils::OpenHandle(this)->IsNull();
}
-bool Value::IsTrue() {
+bool Value::IsTrue() const {
if (IsDeadCheck("v8::Value::IsTrue()")) return false;
return Utils::OpenHandle(this)->IsTrue();
}
-bool Value::IsFalse() {
+bool Value::IsFalse() const {
if (IsDeadCheck("v8::Value::IsFalse()")) return false;
return Utils::OpenHandle(this)->IsFalse();
}
-bool Value::IsFunction() {
+bool Value::IsFunction() const {
if (IsDeadCheck("v8::Value::IsFunction()")) return false;
return Utils::OpenHandle(this)->IsJSFunction();
}
-bool Value::IsString() {
+bool Value::IsString() const {
if (IsDeadCheck("v8::Value::IsString()")) return false;
return Utils::OpenHandle(this)->IsString();
}
-bool Value::IsArray() {
+bool Value::IsArray() const {
if (IsDeadCheck("v8::Value::IsArray()")) return false;
return Utils::OpenHandle(this)->IsJSArray();
}
-bool Value::IsObject() {
+bool Value::IsObject() const {
if (IsDeadCheck("v8::Value::IsObject()")) return false;
return Utils::OpenHandle(this)->IsJSObject();
}
-bool Value::IsNumber() {
+bool Value::IsNumber() const {
if (IsDeadCheck("v8::Value::IsNumber()")) return false;
return Utils::OpenHandle(this)->IsNumber();
}
-bool Value::IsBoolean() {
+bool Value::IsBoolean() const {
if (IsDeadCheck("v8::Value::IsBoolean()")) return false;
return Utils::OpenHandle(this)->IsBoolean();
}
-bool Value::IsExternal() {
+bool Value::IsExternal() const {
if (IsDeadCheck("v8::Value::IsExternal()")) return false;
return Utils::OpenHandle(this)->IsProxy();
}
-bool Value::IsInt32() {
+bool Value::IsInt32() const {
if (IsDeadCheck("v8::Value::IsInt32()")) return false;
i::Handle<i::Object> obj = Utils::OpenHandle(this);
if (obj->IsSmi()) return true;
@@ -1350,14 +1350,14 @@
}
-bool Value::IsDate() {
+bool Value::IsDate() const {
if (IsDeadCheck("v8::Value::IsDate()")) return false;
i::Handle<i::Object> obj = Utils::OpenHandle(this);
return obj->HasSpecificClassOf(i::Heap::Date_symbol());
}
-Local<String> Value::ToString() {
+Local<String> Value::ToString() const {
if (IsDeadCheck("v8::Value::ToString()")) return Local<String>();
LOG_API("ToString");
i::Handle<i::Object> obj = Utils::OpenHandle(this);
@@ -1373,7 +1373,7 @@
}
-Local<String> Value::ToDetailString() {
+Local<String> Value::ToDetailString() const {
if (IsDeadCheck("v8::Value::ToDetailString()")) return Local<String>();
LOG_API("ToDetailString");
i::Handle<i::Object> obj = Utils::OpenHandle(this);
@@ -1389,7 +1389,7 @@
}
-Local<v8::Object> Value::ToObject() {
+Local<v8::Object> Value::ToObject() const {
if (IsDeadCheck("v8::Value::ToObject()")) return Local<v8::Object>();
LOG_API("ToObject");
i::Handle<i::Object> obj = Utils::OpenHandle(this);
@@ -1405,7 +1405,7 @@
}
-Local<Boolean> Value::ToBoolean() {
+Local<Boolean> Value::ToBoolean() const {
if (IsDeadCheck("v8::Value::ToBoolean()")) return Local<Boolean>();
LOG_API("ToBoolean");
i::Handle<i::Object> obj = Utils::OpenHandle(this);
@@ -1415,7 +1415,7 @@
}
-Local<Number> Value::ToNumber() {
+Local<Number> Value::ToNumber() const {
if (IsDeadCheck("v8::Value::ToNumber()")) return Local<Number>();
LOG_API("ToNumber");
i::Handle<i::Object> obj = Utils::OpenHandle(this);
@@ -1431,7 +1431,7 @@
}
-Local<Integer> Value::ToInteger() {
+Local<Integer> Value::ToInteger() const {
if (IsDeadCheck("v8::Value::ToInteger()")) return Local<Integer>();
LOG_API("ToInteger");
i::Handle<i::Object> obj = Utils::OpenHandle(this);
@@ -1527,7 +1527,7 @@
}
-bool Value::BooleanValue() {
+bool Value::BooleanValue() const {
if (IsDeadCheck("v8::Value::BooleanValue()")) return false;
LOG_API("BooleanValue");
i::Handle<i::Object> obj = Utils::OpenHandle(this);
@@ -1537,7 +1537,7 @@
}
-double Value::NumberValue() {
+double Value::NumberValue() const {
if (IsDeadCheck("v8::Value::NumberValue()")) return i::OS::nan_value();
LOG_API("NumberValue");
i::Handle<i::Object> obj = Utils::OpenHandle(this);
@@ -1553,7 +1553,7 @@
}
-int64_t Value::IntegerValue() {
+int64_t Value::IntegerValue() const {
if (IsDeadCheck("v8::Value::IntegerValue()")) return 0;
LOG_API("IntegerValue");
i::Handle<i::Object> obj = Utils::OpenHandle(this);
@@ -1573,7 +1573,7 @@
}
-Local<Int32> Value::ToInt32() {
+Local<Int32> Value::ToInt32() const {
if (IsDeadCheck("v8::Value::ToInt32()")) return Local<Int32>();
LOG_API("ToInt32");
i::Handle<i::Object> obj = Utils::OpenHandle(this);
@@ -1589,7 +1589,7 @@
}
-Local<Uint32> Value::ToUint32() {
+Local<Uint32> Value::ToUint32() const {
if (IsDeadCheck("v8::Value::ToUint32()")) return Local<Uint32>();
LOG_API("ToUInt32");
i::Handle<i::Object> obj = Utils::OpenHandle(this);
@@ -1605,7 +1605,7 @@
}
-Local<Uint32> Value::ToArrayIndex() {
+Local<Uint32> Value::ToArrayIndex() const {
if (IsDeadCheck("v8::Value::ToArrayIndex()")) return Local<Uint32>();
LOG_API("ToArrayIndex");
i::Handle<i::Object> obj = Utils::OpenHandle(this);
@@ -1632,7 +1632,7 @@
}
-int32_t Value::Int32Value() {
+int32_t Value::Int32Value() const {
if (IsDeadCheck("v8::Value::Int32Value()")) return 0;
LOG_API("Int32Value");
i::Handle<i::Object> obj = Utils::OpenHandle(this);
@@ -1653,7 +1653,7 @@
}
-bool Value::Equals(Handle<Value> that) {
+bool Value::Equals(Handle<Value> that) const {
if (IsDeadCheck("v8::Value::Equals()")
|| EmptyCheck("v8::Value::Equals()", this)
|| EmptyCheck("v8::Value::Equals()", that))
@@ -1670,7 +1670,7 @@
}
-bool Value::StrictEquals(Handle<Value> that) {
+bool Value::StrictEquals(Handle<Value> that) const {
if (IsDeadCheck("v8::Value::StrictEquals()")
|| EmptyCheck("v8::Value::StrictEquals()", this)
|| EmptyCheck("v8::Value::StrictEquals()", that))
@@ -1700,7 +1700,7 @@
}
-uint32_t Value::Uint32Value() {
+uint32_t Value::Uint32Value() const {
if (IsDeadCheck("v8::Value::Uint32Value()")) return 0;
LOG_API("Uint32Value");
i::Handle<i::Object> obj = Utils::OpenHandle(this);
@@ -1922,13 +1922,13 @@
}
-Local<v8::Object> Function::NewInstance() {
+Local<v8::Object> Function::NewInstance() const {
return NewInstance(0, NULL);
}
Local<v8::Object> Function::NewInstance(int argc,
- v8::Handle<v8::Value> argv[]) {
+ v8::Handle<v8::Value> argv[]) const {
ON_BAILOUT("v8::Function::NewInstance()", return Local<v8::Object>());
LOG_API("Function::NewInstance");
HandleScope scope;
@@ -1971,25 +1971,25 @@
}
-Handle<Value> Function::GetName() {
+Handle<Value> Function::GetName() const {
i::Handle<i::JSFunction> func = Utils::OpenHandle(this);
return Utils::ToLocal(i::Handle<i::Object>(func->shared()->name()));
}
-int String::Length() {
+int String::Length() const {
if (IsDeadCheck("v8::String::Length()")) return 0;
return Utils::OpenHandle(this)->length();
}
-int String::Utf8Length() {
+int String::Utf8Length() const {
if (IsDeadCheck("v8::String::Utf8Length()")) return 0;
return Utils::OpenHandle(this)->Utf8Length();
}
-int String::WriteUtf8(char* buffer, int capacity) {
+int String::WriteUtf8(char* buffer, int capacity) const {
if (IsDeadCheck("v8::String::WriteUtf8()")) return 0;
LOG_API("String::WriteUtf8");
i::Handle<i::String> str = Utils::OpenHandle(this);
@@ -2030,7 +2030,7 @@
}
-int String::WriteAscii(char* buffer, int start, int length) {
+int String::WriteAscii(char* buffer, int start, int length) const {
if (IsDeadCheck("v8::String::WriteAscii()")) return 0;
LOG_API("String::WriteAscii");
ASSERT(start >= 0 && length >= -1);
@@ -2055,7 +2055,7 @@
}
-int String::Write(uint16_t* buffer, int start, int length) {
+int String::Write(uint16_t* buffer, int start, int length) const {
if (IsDeadCheck("v8::String::Write()")) return 0;
LOG_API("String::Write");
ASSERT(start >= 0 && length >= -1);
@@ -2077,7 +2077,7 @@
}
-bool v8::String::IsExternal() {
+bool v8::String::IsExternal() const {
EnsureInitialized("v8::String::IsExternal()");
i::Handle<i::String> str = Utils::OpenHandle(this);
i::StringShape shape(*str);
@@ -2085,7 +2085,7 @@
}
-bool v8::String::IsExternalAscii() {
+bool v8::String::IsExternalAscii() const {
EnsureInitialized("v8::String::IsExternalAscii()");
i::Handle<i::String> str = Utils::OpenHandle(this);
i::StringShape shape(*str);
@@ -2093,7 +2093,8 @@
}
-v8::String::ExternalStringResource* v8::String::GetExternalStringResource() {
+v8::String::ExternalStringResource*
+v8::String::GetExternalStringResource() const {
EnsureInitialized("v8::String::GetExternalStringResource()");
i::Handle<i::String> str = Utils::OpenHandle(this);
ASSERT(str->IsExternalTwoByteString());
@@ -2103,7 +2104,7 @@
v8::String::ExternalAsciiStringResource*
- v8::String::GetExternalAsciiStringResource() {
+ v8::String::GetExternalAsciiStringResource() const {
EnsureInitialized("v8::String::GetExternalAsciiStringResource()");
i::Handle<i::String> str = Utils::OpenHandle(this);
ASSERT(str->IsExternalAsciiString());
@@ -2112,21 +2113,21 @@
}
-double Number::Value() {
+double Number::Value() const {
if (IsDeadCheck("v8::Number::Value()")) return 0;
i::Handle<i::Object> obj = Utils::OpenHandle(this);
return obj->Number();
}
-bool Boolean::Value() {
+bool Boolean::Value() const {
if (IsDeadCheck("v8::Boolean::Value()")) return false;
i::Handle<i::Object> obj = Utils::OpenHandle(this);
return obj->IsTrue();
}
-int64_t Integer::Value() {
+int64_t Integer::Value() const {
if (IsDeadCheck("v8::Integer::Value()")) return 0;
i::Handle<i::Object> obj = Utils::OpenHandle(this);
if (obj->IsSmi()) {
@@ -2137,7 +2138,7 @@
}
-int32_t Int32::Value() {
+int32_t Int32::Value() const {
if (IsDeadCheck("v8::Int32::Value()")) return 0;
i::Handle<i::Object> obj = Utils::OpenHandle(this);
if (obj->IsSmi()) {
@@ -2148,7 +2149,7 @@
}
-void* External::Value() {
+void* External::Value() const {
if (IsDeadCheck("v8::External::Value()")) return 0;
i::Handle<i::Object> obj = Utils::OpenHandle(this);
return reinterpret_cast<void*>(i::Proxy::cast(*obj)->proxy());
@@ -2203,7 +2204,7 @@
const char* v8::V8::GetVersion() {
- return "0.4.7";
+ return "0.4.8";
}
@@ -2529,7 +2530,7 @@
}
-double v8::Date::NumberValue() {
+double v8::Date::NumberValue() const {
if (IsDeadCheck("v8::Date::NumberValue()")) return 0;
LOG_API("Date::NumberValue");
i::Handle<i::Object> obj = Utils::OpenHandle(this);
@@ -2546,7 +2547,7 @@
}
-uint32_t v8::Array::Length() {
+uint32_t v8::Array::Length() const {
if (IsDeadCheck("v8::Array::Length()")) return 0;
i::Handle<i::JSArray> obj = Utils::OpenHandle(this);
i::Object* length = obj->length();
diff --git a/src/api.h b/src/api.h
index 4843926..f4cea8b 100644
--- a/src/api.h
+++ b/src/api.h
@@ -237,33 +237,33 @@
v8::internal::Handle<v8::internal::TypeSwitchInfo> obj);
static inline v8::internal::Handle<v8::internal::TemplateInfo>
- OpenHandle(Template* that);
+ OpenHandle(const Template* that);
static inline v8::internal::Handle<v8::internal::FunctionTemplateInfo>
- OpenHandle(FunctionTemplate* that);
+ OpenHandle(const FunctionTemplate* that);
static inline v8::internal::Handle<v8::internal::ObjectTemplateInfo>
- OpenHandle(ObjectTemplate* that);
+ OpenHandle(const ObjectTemplate* that);
static inline v8::internal::Handle<v8::internal::Object>
- OpenHandle(Data* data);
+ OpenHandle(const Data* data);
static inline v8::internal::Handle<v8::internal::JSObject>
- OpenHandle(v8::Object* data);
+ OpenHandle(const v8::Object* data);
static inline v8::internal::Handle<v8::internal::JSArray>
- OpenHandle(v8::Array* data);
+ OpenHandle(const v8::Array* data);
static inline v8::internal::Handle<v8::internal::String>
- OpenHandle(String* data);
+ OpenHandle(const String* data);
static inline v8::internal::Handle<v8::internal::JSFunction>
- OpenHandle(Script* data);
+ OpenHandle(const Script* data);
static inline v8::internal::Handle<v8::internal::JSFunction>
- OpenHandle(Function* data);
+ OpenHandle(const Function* data);
static inline v8::internal::Handle<v8::internal::JSObject>
- OpenHandle(Message* message);
+ OpenHandle(const Message* message);
static inline v8::internal::Handle<v8::internal::Context>
- OpenHandle(v8::Context* context);
+ OpenHandle(const v8::Context* context);
static inline v8::internal::Handle<v8::internal::SignatureInfo>
- OpenHandle(v8::Signature* sig);
+ OpenHandle(const v8::Signature* sig);
static inline v8::internal::Handle<v8::internal::TypeSwitchInfo>
- OpenHandle(v8::TypeSwitch* that);
+ OpenHandle(const v8::TypeSwitch* that);
static inline v8::internal::Handle<v8::internal::Proxy>
- OpenHandle(v8::External* that);
+ OpenHandle(const v8::External* that);
};
@@ -309,9 +309,10 @@
// Implementations of OpenHandle
#define MAKE_OPEN_HANDLE(From, To) \
- v8::internal::Handle<v8::internal::To> Utils::OpenHandle(v8::From* that) { \
+ v8::internal::Handle<v8::internal::To> Utils::OpenHandle(\
+ const v8::From* that) { \
return v8::internal::Handle<v8::internal::To>( \
- reinterpret_cast<v8::internal::To**>(that)); \
+ reinterpret_cast<v8::internal::To**>(const_cast<v8::From*>(that))); \
}
MAKE_OPEN_HANDLE(Template, TemplateInfo)
diff --git a/src/apinatives.js b/src/apinatives.js
index 8ae80e7..2981eec 100644
--- a/src/apinatives.js
+++ b/src/apinatives.js
@@ -59,23 +59,33 @@
function InstantiateFunction(data, name) {
+ // We need a reference to kApiFunctionCache in the stack frame
+ // if we need to bail out from a stack overflow.
+ var cache = kApiFunctionCache;
var serialNumber = %GetTemplateField(data, kApiSerialNumberOffset);
- if (!(serialNumber in kApiFunctionCache)) {
- kApiFunctionCache[serialNumber] = null;
- var fun = %CreateApiFunction(data);
- if (name) %FunctionSetName(fun, name);
- kApiFunctionCache[serialNumber] = fun;
- var prototype = %GetTemplateField(data, kApiPrototypeTemplateOffset);
- fun.prototype = prototype ? Instantiate(prototype) : {};
- %SetProperty(fun.prototype, "constructor", fun, DONT_ENUM);
- var parent = %GetTemplateField(data, kApiParentTemplateOffset);
- if (parent) {
- var parent_fun = Instantiate(parent);
- fun.prototype.__proto__ = parent_fun.prototype;
+ var isFunctionCached =
+ (serialNumber in cache) && (cache[serialNumber] != kUninitialized);
+ if (!isFunctionCached) {
+ try {
+ cache[serialNumber] = null;
+ var fun = %CreateApiFunction(data);
+ if (name) %FunctionSetName(fun, name);
+ cache[serialNumber] = fun;
+ var prototype = %GetTemplateField(data, kApiPrototypeTemplateOffset);
+ fun.prototype = prototype ? Instantiate(prototype) : {};
+ %SetProperty(fun.prototype, "constructor", fun, DONT_ENUM);
+ var parent = %GetTemplateField(data, kApiParentTemplateOffset);
+ if (parent) {
+ var parent_fun = Instantiate(parent);
+ fun.prototype.__proto__ = parent_fun.prototype;
+ }
+ ConfigureTemplateInstance(fun, data);
+ } catch (e) {
+ cache[serialNumber] = kUninitialized;
+ throw e;
}
- ConfigureTemplateInstance(fun, data);
}
- return kApiFunctionCache[serialNumber];
+ return cache[serialNumber];
}
diff --git a/src/assembler-arm-inl.h b/src/assembler-arm-inl.h
index fbe37d7..5a64d01 100644
--- a/src/assembler-arm-inl.h
+++ b/src/assembler-arm-inl.h
@@ -61,13 +61,19 @@
Address RelocInfo::target_address() {
- ASSERT(IsCodeTarget(rmode_));
+ ASSERT(IsCodeTarget(rmode_) || rmode_ == RUNTIME_ENTRY);
return Assembler::target_address_at(pc_);
}
+Address RelocInfo::target_address_address() {
+ ASSERT(IsCodeTarget(rmode_) || rmode_ == RUNTIME_ENTRY);
+ return reinterpret_cast<Address>(Assembler::target_address_address_at(pc_));
+}
+
+
void RelocInfo::set_target_address(Address target) {
- ASSERT(IsCodeTarget(rmode_));
+ ASSERT(IsCodeTarget(rmode_) || rmode_ == RUNTIME_ENTRY);
Assembler::set_target_address_at(pc_, target);
}
@@ -92,7 +98,7 @@
Address* RelocInfo::target_reference_address() {
ASSERT(rmode_ == EXTERNAL_REFERENCE);
- return reinterpret_cast<Address*>(pc_);
+ return reinterpret_cast<Address*>(Assembler::target_address_address_at(pc_));
}
diff --git a/src/assembler-arm.cc b/src/assembler-arm.cc
index 4e3ae1a..f7725f6 100644
--- a/src/assembler-arm.cc
+++ b/src/assembler-arm.cc
@@ -1331,7 +1331,7 @@
}
// Write the position if it is different from what was written last time and
- // also diferent from the written statement position.
+ // also different from the written statement position.
if (current_position_ != written_position_ &&
current_position_ != written_statement_position_) {
CheckBuffer();
diff --git a/src/assembler-arm.h b/src/assembler-arm.h
index faf402a..d8aa068 100644
--- a/src/assembler-arm.h
+++ b/src/assembler-arm.h
@@ -389,7 +389,7 @@
// GetCode emits any pending (non-emitted) code and fills the descriptor
// desc. GetCode() is idempotent; it returns the same result if no other
- // Assembler functions are invoked inbetween GetCode() calls.
+ // Assembler functions are invoked in between GetCode() calls.
void GetCode(CodeDesc* desc);
// Label operations & relative jumps (PPUM Appendix D)
diff --git a/src/assembler-ia32-inl.h b/src/assembler-ia32-inl.h
index aee847d..8b5fc64 100644
--- a/src/assembler-ia32-inl.h
+++ b/src/assembler-ia32-inl.h
@@ -70,6 +70,12 @@
}
+Address RelocInfo::target_address_address() {
+ ASSERT(IsCodeTarget(rmode_) || rmode_ == RUNTIME_ENTRY);
+ return reinterpret_cast<Address>(pc_);
+}
+
+
void RelocInfo::set_target_address(Address target) {
ASSERT(IsCodeTarget(rmode_) || rmode_ == RUNTIME_ENTRY);
Assembler::set_target_address_at(pc_, target);
diff --git a/src/assembler-ia32.cc b/src/assembler-ia32.cc
index f487c41..3f663df 100644
--- a/src/assembler-ia32.cc
+++ b/src/assembler-ia32.cc
@@ -1990,7 +1990,7 @@
}
// Write the position if it is different from what was written last time and
- // also diferent from the written statement position.
+ // also different from the written statement position.
if (current_position_ != written_position_ &&
current_position_ != written_statement_position_) {
EnsureSpace ensure_space(this);
diff --git a/src/assembler-ia32.h b/src/assembler-ia32.h
index 7db218e..cb273c0 100644
--- a/src/assembler-ia32.h
+++ b/src/assembler-ia32.h
@@ -405,7 +405,7 @@
// GetCode emits any pending (non-emitted) code and fills the descriptor
// desc. GetCode() is idempotent; it returns the same result if no other
- // Assembler functions are invoked inbetween GetCode() calls.
+ // Assembler functions are invoked in between GetCode() calls.
void GetCode(CodeDesc* desc);
// Read/Modify the code target in the branch/call instruction at pc.
@@ -430,10 +430,10 @@
// in the sense that some operations (e.g. mov()) can be called in more
// the one way to generate the same instruction: The Register argument
// can in some cases be replaced with an Operand(Register) argument.
- // This should be cleaned up and made more othogonal. The questions
+ // This should be cleaned up and made more orthogonal. The questions
// is: should we always use Operands instead of Registers where an
// Operand is possible, or should we have a Register (overloaded) form
- // instead? We must be carefull to make sure that the selected instruction
+ // instead? We must be careful to make sure that the selected instruction
// is obvious from the parameters to avoid hard-to-find code generation
// bugs.
diff --git a/src/assembler.cc b/src/assembler.cc
index c56eb2f..ad4b24c 100644
--- a/src/assembler.cc
+++ b/src/assembler.cc
@@ -41,6 +41,7 @@
#include "runtime.h"
#include "serialize.h"
#include "stub-cache.h"
+#include "regexp-stack.h"
namespace v8 { namespace internal {
@@ -551,6 +552,11 @@
}
+ExternalReference ExternalReference::address_of_regexp_stack_limit() {
+ return ExternalReference(RegExpStack::limit_address());
+}
+
+
ExternalReference ExternalReference::debug_break() {
return ExternalReference(FUNCTION_ADDR(Debug::Break));
}
diff --git a/src/assembler.h b/src/assembler.h
index 7bd117c..62ef9dc 100644
--- a/src/assembler.h
+++ b/src/assembler.h
@@ -230,14 +230,19 @@
// Apply a relocation by delta bytes
INLINE(void apply(int delta));
- // Read/modify the code target in the branch/call instruction this relocation
- // applies to; can only be called if IsCodeTarget(rmode_)
+ // Read/modify the code target in the branch/call instruction
+ // this relocation applies to;
+ // can only be called if IsCodeTarget(rmode_) || rmode_ == RUNTIME_ENTRY
INLINE(Address target_address());
INLINE(void set_target_address(Address target));
INLINE(Object* target_object());
INLINE(Object** target_object_address());
INLINE(void set_target_object(Object* target));
+ // Read the address of the word containing the target_address. Can only
+ // be called if IsCodeTarget(rmode_) || rmode_ == RUNTIME_ENTRY.
+ INLINE(Address target_address_address());
+
// Read/modify the reference in the instruction this relocation
// applies to; can only be called if rmode_ is external_reference
INLINE(Address* target_reference_address());
@@ -442,6 +447,9 @@
// Static variable StackGuard::address_of_limit()
static ExternalReference address_of_stack_guard_limit();
+ // Static variable RegExpStack::limit_address()
+ static ExternalReference address_of_regexp_stack_limit();
+
// Function Debug::Break()
static ExternalReference debug_break();
diff --git a/src/ast.cc b/src/ast.cc
index 1433fe7..5034151 100644
--- a/src/ast.cc
+++ b/src/ast.cc
@@ -210,6 +210,40 @@
RegExpEmpty RegExpEmpty::kInstance;
+static Interval ListCaptureRegisters(ZoneList<RegExpTree*>* children) {
+ Interval result = Interval::Empty();
+ for (int i = 0; i < children->length(); i++)
+ result = result.Union(children->at(i)->CaptureRegisters());
+ return result;
+}
+
+
+Interval RegExpAlternative::CaptureRegisters() {
+ return ListCaptureRegisters(nodes());
+}
+
+
+Interval RegExpDisjunction::CaptureRegisters() {
+ return ListCaptureRegisters(alternatives());
+}
+
+
+Interval RegExpLookahead::CaptureRegisters() {
+ return body()->CaptureRegisters();
+}
+
+
+Interval RegExpCapture::CaptureRegisters() {
+ Interval self(StartRegister(index()), EndRegister(index()));
+ return self.Union(body()->CaptureRegisters());
+}
+
+
+Interval RegExpQuantifier::CaptureRegisters() {
+ return body()->CaptureRegisters();
+}
+
+
// 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
diff --git a/src/ast.h b/src/ast.h
index 150506b..360a054 100644
--- a/src/ast.h
+++ b/src/ast.h
@@ -77,6 +77,7 @@
V(RegExpLiteral) \
V(ObjectLiteral) \
V(ArrayLiteral) \
+ V(CatchExtensionObject) \
V(Assignment) \
V(Throw) \
V(Property) \
@@ -722,6 +723,26 @@
};
+// Node for constructing a context extension object for a catch block.
+// The catch context extension object has one property, the catch
+// variable, which should be DontDelete.
+class CatchExtensionObject: public Expression {
+ public:
+ CatchExtensionObject(Literal* key, VariableProxy* value)
+ : key_(key), value_(value) {
+ }
+
+ virtual void Accept(AstVisitor* v);
+
+ Literal* key() const { return key_; }
+ VariableProxy* value() const { return value_; }
+
+ private:
+ Literal* key_;
+ VariableProxy* value_;
+};
+
+
class VariableProxy: public Expression {
public:
virtual void Accept(AstVisitor* v);
@@ -1214,6 +1235,16 @@
// Regular expressions
+class RegExpVisitor BASE_EMBEDDED {
+ public:
+ virtual ~RegExpVisitor() { }
+#define MAKE_CASE(Name) \
+ virtual void* Visit##Name(RegExp##Name*, void* data) = 0;
+ FOR_EACH_REG_EXP_TREE_TYPE(MAKE_CASE)
+#undef MAKE_CASE
+};
+
+
class RegExpTree: public ZoneObject {
public:
static const int kInfinity = kMaxInt;
@@ -1224,6 +1255,9 @@
virtual bool IsTextElement() { return false; }
virtual int min_match() = 0;
virtual int max_match() = 0;
+ // Returns the interval of registers used for captures within this
+ // expression.
+ virtual Interval CaptureRegisters() { return Interval::Empty(); }
virtual void AppendToText(RegExpText* text);
SmartPointer<const char> ToString();
#define MAKE_ASTYPE(Name) \
@@ -1241,6 +1275,7 @@
virtual RegExpNode* ToNode(RegExpCompiler* compiler,
RegExpNode* on_success);
virtual RegExpDisjunction* AsDisjunction();
+ virtual Interval CaptureRegisters();
virtual bool IsDisjunction();
virtual int min_match() { return min_match_; }
virtual int max_match() { return max_match_; }
@@ -1259,6 +1294,7 @@
virtual RegExpNode* ToNode(RegExpCompiler* compiler,
RegExpNode* on_success);
virtual RegExpAlternative* AsAlternative();
+ virtual Interval CaptureRegisters();
virtual bool IsAlternative();
virtual int min_match() { return min_match_; }
virtual int max_match() { return max_match_; }
@@ -1423,6 +1459,7 @@
RegExpCompiler* compiler,
RegExpNode* on_success);
virtual RegExpQuantifier* AsQuantifier();
+ virtual Interval CaptureRegisters();
virtual bool IsQuantifier();
virtual int min_match() { return min_match_; }
virtual int max_match() { return max_match_; }
@@ -1458,6 +1495,7 @@
RegExpCompiler* compiler,
RegExpNode* on_success);
virtual RegExpCapture* AsCapture();
+ virtual Interval CaptureRegisters();
virtual bool IsCapture();
virtual int min_match() { return body_->min_match(); }
virtual int max_match() { return body_->max_match(); }
@@ -1485,6 +1523,7 @@
virtual RegExpNode* ToNode(RegExpCompiler* compiler,
RegExpNode* on_success);
virtual RegExpLookahead* AsLookahead();
+ virtual Interval CaptureRegisters();
virtual bool IsLookahead();
virtual int min_match() { return 0; }
virtual int max_match() { return 0; }
@@ -1505,7 +1544,7 @@
RegExpNode* on_success);
virtual RegExpBackReference* AsBackReference();
virtual bool IsBackReference();
- virtual int min_match() { return capture_->min_match(); }
+ virtual int min_match() { return 0; }
virtual int max_match() { return capture_->max_match(); }
int index() { return capture_->index(); }
RegExpCapture* capture() { return capture_; }
@@ -1530,16 +1569,6 @@
};
-class RegExpVisitor BASE_EMBEDDED {
- public:
- virtual ~RegExpVisitor() { }
-#define MAKE_CASE(Name) \
- virtual void* Visit##Name(RegExp##Name*, void* data) = 0;
- FOR_EACH_REG_EXP_TREE_TYPE(MAKE_CASE)
-#undef MAKE_CASE
-};
-
-
// ----------------------------------------------------------------------------
// Basic visitor
// - leaf node visitors are abstract.
diff --git a/src/bootstrapper.cc b/src/bootstrapper.cc
index ce25d00..970acd3 100644
--- a/src/bootstrapper.cc
+++ b/src/bootstrapper.cc
@@ -794,8 +794,11 @@
// Create a function for the context extension objects.
Handle<Code> code = Handle<Code>(Builtins::builtin(Builtins::Illegal));
Handle<JSFunction> context_extension_fun =
- Factory::NewFunction(Factory::empty_symbol(), JS_OBJECT_TYPE,
- JSObject::kHeaderSize, code, true);
+ Factory::NewFunction(Factory::empty_symbol(),
+ JS_CONTEXT_EXTENSION_OBJECT_TYPE,
+ JSObject::kHeaderSize,
+ code,
+ true);
Handle<String> name = Factory::LookupAsciiSymbol("context_extension");
context_extension_fun->shared()->set_instance_class_name(*name);
@@ -1080,7 +1083,7 @@
call->shared()->DontAdaptArguments();
ASSERT(call->is_compiled());
- // Set the expected paramters for apply to 2; required by builtin.
+ // Set the expected parameters for apply to 2; required by builtin.
apply->shared()->set_formal_parameter_count(2);
// Set the lengths for the functions to satisfy ECMA-262.
@@ -1434,7 +1437,7 @@
v8::Handle<v8::ObjectTemplate> global_template,
v8::ExtensionConfiguration* extensions) {
// Link this genesis object into the stacked genesis chain. This
- // must be done before any early exits because the deconstructor
+ // must be done before any early exits because the destructor
// will always do unlinking.
previous_ = current_;
current_ = this;
diff --git a/src/bootstrapper.h b/src/bootstrapper.h
index 908217d..1314bfd 100644
--- a/src/bootstrapper.h
+++ b/src/bootstrapper.h
@@ -49,7 +49,7 @@
// Detach the environment from its outer global object.
static void DetachGlobal(Handle<Context> env);
- // Traverses the pointers for memory manangment.
+ // Traverses the pointers for memory management.
static void Iterate(ObjectVisitor* v);
// Accessors for the native scripts cache. Used in lazy loading.
@@ -61,7 +61,7 @@
// Append code that needs fixup at the end of boot strapping.
static void AddFixup(Code* code, MacroAssembler* masm);
- // Tells whether boostrapping is active.
+ // Tells whether bootstrapping is active.
static bool IsActive();
// Encoding/decoding support for fixup flags.
diff --git a/src/builtins-arm.cc b/src/builtins-arm.cc
index caddfb9..2e6f255 100644
--- a/src/builtins-arm.cc
+++ b/src/builtins-arm.cc
@@ -565,7 +565,7 @@
__ cmp(r2, Operand(SharedFunctionInfo::kDontAdaptArgumentsSentinel));
__ b(eq, &dont_adapt_arguments);
- { // Enough parameters: actual >= excpected
+ { // Enough parameters: actual >= expected
__ bind(&enough);
EnterArgumentsAdaptorFrame(masm);
diff --git a/src/bytecodes-irregexp.h b/src/bytecodes-irregexp.h
index 5ffc4b3..7ec8635 100644
--- a/src/bytecodes-irregexp.h
+++ b/src/bytecodes-irregexp.h
@@ -71,8 +71,9 @@
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_NOT_AT_START, 39, 5) /* check_not_at_start addr32 */ \
-V(CHECK_GREEDY, 40, 5) /* check_greedy 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 */
#define DECLARE_BYTECODES(name, code, length) \
static const int BC_##name = code;
diff --git a/src/codegen-arm.cc b/src/codegen-arm.cc
index 47da62d..c06e848 100644
--- a/src/codegen-arm.cc
+++ b/src/codegen-arm.cc
@@ -2235,6 +2235,17 @@
}
+void CodeGenerator::VisitCatchExtensionObject(CatchExtensionObject* node) {
+ // Call runtime routine to allocate the catch extension object and
+ // assign the exception value to the catch variable.
+ Comment cmnt(masm_, "[CatchExtensionObject ");
+ Load(node->key());
+ Load(node->value());
+ __ CallRuntime(Runtime::kCreateCatchExtensionObject, 2);
+ frame_->Push(r0);
+}
+
+
void CodeGenerator::VisitAssignment(Assignment* node) {
Comment cmnt(masm_, "[ Assignment");
CodeForStatement(node);
@@ -4028,9 +4039,9 @@
__ b(ne, &continue_exception);
// Retrieve the pending exception and clear the variable.
- __ mov(ip, Operand(Factory::the_hole_value().location()));
+ __ mov(ip, Operand(ExternalReference::the_hole_value_location()));
__ ldr(r3, MemOperand(ip));
- __ mov(ip, Operand(Top::pending_exception_address()));
+ __ mov(ip, Operand(ExternalReference(Top::k_pending_exception_address)));
__ ldr(r0, MemOperand(ip));
__ str(r3, MemOperand(ip));
@@ -4160,7 +4171,7 @@
// exception field in the JSEnv and return a failure sentinel.
// Coming in here the fp will be invalid because the PushTryHandler below
// sets it to 0 to signal the existence of the JSEntry frame.
- __ mov(ip, Operand(Top::pending_exception_address()));
+ __ mov(ip, Operand(ExternalReference(Top::k_pending_exception_address)));
__ str(r0, MemOperand(ip));
__ mov(r0, Operand(reinterpret_cast<int32_t>(Failure::Exception())));
__ b(&exit);
@@ -4177,7 +4188,7 @@
// Clear any pending exceptions.
__ mov(ip, Operand(ExternalReference::the_hole_value_location()));
__ ldr(r5, MemOperand(ip));
- __ mov(ip, Operand(Top::pending_exception_address()));
+ __ mov(ip, Operand(ExternalReference(Top::k_pending_exception_address)));
__ str(r5, MemOperand(ip));
// Invoke the function by calling through JS entry trampoline builtin.
diff --git a/src/codegen-ia32.cc b/src/codegen-ia32.cc
index 119e41f..a9cc800 100644
--- a/src/codegen-ia32.cc
+++ b/src/codegen-ia32.cc
@@ -2505,7 +2505,7 @@
for (int i = 0; i < node->properties()->length(); i++) {
- ObjectLiteral::Property* property = node->properties()->at(i);
+ ObjectLiteral::Property* property = node->properties()->at(i);
switch (property->kind()) {
case ObjectLiteral::Property::CONSTANT: break;
case ObjectLiteral::Property::COMPUTED: {
@@ -2607,6 +2607,17 @@
}
+void CodeGenerator::VisitCatchExtensionObject(CatchExtensionObject* node) {
+ // Call runtime routine to allocate the catch extension object and
+ // assign the exception value to the catch variable.
+ Comment cmnt(masm_, "[CatchExtensionObject ");
+ Load(node->key());
+ Load(node->value());
+ __ CallRuntime(Runtime::kCreateCatchExtensionObject, 2);
+ frame_->Push(eax);
+}
+
+
bool CodeGenerator::IsInlineSmi(Literal* literal) {
if (literal == NULL || !literal->handle()->IsSmi()) return false;
int int_value = Smi::cast(*literal->handle())->value();
diff --git a/src/d8-debug.cc b/src/d8-debug.cc
new file mode 100644
index 0000000..552b93e
--- /dev/null
+++ b/src/d8-debug.cc
@@ -0,0 +1,124 @@
+// Copyright 2008 the V8 project authors. All rights reserved.
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following
+// disclaimer in the documentation and/or other materials provided
+// with the distribution.
+// * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived
+// from this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+
+#include "d8.h"
+#include "d8-debug.h"
+
+
+namespace v8 {
+
+
+void HandleDebugEvent(DebugEvent event,
+ Handle<Object> exec_state,
+ Handle<Object> event_data,
+ Handle<Value> data) {
+ HandleScope scope;
+
+ // Currently only handles break and exception events.
+ if (event != Break && event != Exception) return;
+
+ TryCatch try_catch;
+
+ // Print the event details.
+ Handle<String> details = Shell::DebugEventToText(event_data);
+ String::Utf8Value str(details);
+ printf("%s\n", *str);
+
+ // Get the debug command processor.
+ Local<String> fun_name = String::New("debugCommandProcessor");
+ Local<Function> fun = Function::Cast(*exec_state->Get(fun_name));
+ Local<Object> cmd_processor =
+ Object::Cast(*fun->Call(exec_state, 0, NULL));
+ if (try_catch.HasCaught()) {
+ Shell::ReportException(&try_catch);
+ return;
+ }
+
+ static const int kBufferSize = 256;
+ bool running = false;
+ while (!running) {
+ char command[kBufferSize];
+ printf("dbg> ");
+ char* str = fgets(command, kBufferSize, stdin);
+ if (str == NULL) break;
+
+ // Ignore empty commands.
+ if (strlen(command) == 0) continue;
+
+ TryCatch try_catch;
+
+ // Convert the debugger command to a JSON debugger request.
+ Handle<Value> request =
+ Shell::DebugCommandToJSONRequest(String::New(command));
+ if (try_catch.HasCaught()) {
+ Shell::ReportException(&try_catch);
+ continue;
+ }
+
+ // If undefined is returned the command was handled internally and there is
+ // no JSON to send.
+ if (request->IsUndefined()) {
+ continue;
+ }
+
+ Handle<String> fun_name;
+ Handle<Function> fun;
+ // All the functions used below take one argument.
+ static const int kArgc = 1;
+ Handle<Value> args[kArgc];
+
+ // Invoke the JavaScript to convert the debug command line to a JSON
+ // request, invoke the JSON request and convert the JSON respose to a text
+ // representation.
+ fun_name = String::New("processDebugRequest");
+ fun = Handle<Function>::Cast(cmd_processor->Get(fun_name));
+ args[0] = request;
+ Handle<Value> response_val = fun->Call(cmd_processor, kArgc, args);
+ if (try_catch.HasCaught()) {
+ Shell::ReportException(&try_catch);
+ continue;
+ }
+ Handle<String> response = Handle<String>::Cast(response_val);
+
+ // Convert the debugger response into text details and the running state.
+ Handle<Object> response_details = Shell::DebugResponseDetails(response);
+ if (try_catch.HasCaught()) {
+ Shell::ReportException(&try_catch);
+ continue;
+ }
+ String::Utf8Value text_str(response_details->Get(String::New("text")));
+ if (text_str.length() > 0) {
+ printf("%s\n", *text_str);
+ }
+ running =
+ response_details->Get(String::New("running"))->ToBoolean()->Value();
+ }
+}
+
+
+} // namespace v8
diff --git a/src/d8-debug.h b/src/d8-debug.h
new file mode 100644
index 0000000..e6d66be
--- /dev/null
+++ b/src/d8-debug.h
@@ -0,0 +1,48 @@
+// Copyright 2008 the V8 project authors. All rights reserved.
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following
+// disclaimer in the documentation and/or other materials provided
+// with the distribution.
+// * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived
+// from this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+#ifndef V8_D8_DEBUG_H_
+#define V8_D8_DEBUG_H_
+
+
+#include "d8.h"
+#include "debug.h"
+
+
+namespace v8 {
+
+
+void HandleDebugEvent(DebugEvent event,
+ Handle<Object> exec_state,
+ Handle<Object> event_data,
+ Handle<Value> data);
+
+
+} // namespace v8
+
+
+#endif // V8_D8_DEBUG_H_
diff --git a/src/d8-readline.cc b/src/d8-readline.cc
index b81a616..b86648d 100644
--- a/src/d8-readline.cc
+++ b/src/d8-readline.cc
@@ -35,7 +35,7 @@
// There are incompatibilities between different versions and different
-// implementations of readline. This smoothes out one known incompatibility.
+// implementations of readline. This smooths out one known incompatibility.
#if RL_READLINE_VERSION >= 0x0500
#define completion_matches rl_completion_matches
#endif
diff --git a/src/debug-delay.js b/src/debug-delay.js
index 32a7151..d388946 100644
--- a/src/debug-delay.js
+++ b/src/debug-delay.js
@@ -958,40 +958,6 @@
}
-function RequestPacket(command) {
- this.seq = 0;
- this.type = 'request';
- this.command = command;
-}
-
-
-RequestPacket.prototype.toJSONProtocol = function() {
- // Encode the protocol header.
- var json = '{';
- json += '"seq":' + this.seq;
- json += ',"type":"' + this.type + '"';
- if (this.command) {
- json += ',"command":' + StringToJSON_(this.command);
- }
- if (this.arguments) {
- json += ',"arguments":';
- // Encode the arguments part.
- if (this.arguments.toJSONProtocol) {
- json += this.arguments.toJSONProtocol()
- } else {
- json += SimpleObjectToJSON_(this.arguments);
- }
- }
- json += '}';
- return json;
-}
-
-
-DebugCommandProcessor.prototype.createRequest = function(command) {
- return new RequestPacket(command);
-};
-
-
function ResponsePacket(request) {
// Build the initial response from the request.
this.seq = next_response_seq++;
@@ -1028,22 +994,25 @@
if (this.body) {
json += ',"body":';
// Encode the body part.
- if (this.body.toJSONProtocol) {
- json += this.body.toJSONProtocol(true);
+ var serializer = MakeMirrorSerializer(true);
+ if (this.body instanceof Mirror) {
+ json += serializer.serializeValue(this.body);
} else if (this.body instanceof Array) {
json += '[';
for (var i = 0; i < this.body.length; i++) {
if (i != 0) json += ',';
- if (this.body[i].toJSONProtocol) {
- json += this.body[i].toJSONProtocol(true)
+ if (this.body[i] instanceof Mirror) {
+ json += serializer.serializeValue(this.body[i]);
} else {
- json += SimpleObjectToJSON_(this.body[i]);
+ json += SimpleObjectToJSON_(this.body[i], serializer);
}
}
json += ']';
} else {
- json += SimpleObjectToJSON_(this.body);
+ json += SimpleObjectToJSON_(this.body, serializer);
}
+ json += ',"refs":';
+ json += serializer.serializeReferencedObjects();
}
if (this.message) {
json += ',"message":' + StringToJSON_(this.message) ;
@@ -1602,9 +1571,11 @@
* a general implementation but sufficient for the debugger. Note that circular
* structures will cause infinite recursion.
* @param {Object} object The object to format as JSON
+ * @param {MirrorSerializer} mirror_serializer The serializer to use if any
+ * mirror objects are encountered.
* @return {string} JSON formatted object value
*/
-function SimpleObjectToJSON_(object) {
+function SimpleObjectToJSON_(object, mirror_serializer) {
var content = [];
for (var key in object) {
// Only consider string keys.
@@ -1618,9 +1589,9 @@
if (typeof property_value.toJSONProtocol == 'function') {
property_value_json = property_value.toJSONProtocol(true)
} else if (IS_ARRAY(property_value)){
- property_value_json = SimpleArrayToJSON_(property_value);
+ property_value_json = SimpleArrayToJSON_(property_value, mirror_serializer);
} else {
- property_value_json = SimpleObjectToJSON_(property_value);
+ property_value_json = SimpleObjectToJSON_(property_value, mirror_serializer);
}
break;
@@ -1654,10 +1625,12 @@
/**
* Convert an array to its JSON representation. This is a VERY simple
* implementation just to support what is needed for the debugger.
- * @param {Array} arrya The array to format as JSON
+ * @param {Array} array The array to format as JSON
+ * @param {MirrorSerializer} mirror_serializer The serializer to use if any
+ * mirror objects are encountered.
* @return {string} JSON formatted array value
*/
-function SimpleArrayToJSON_(array) {
+function SimpleArrayToJSON_(array, mirror_serializer) {
// Make JSON array representation.
var json = '[';
for (var i = 0; i < array.length; i++) {
@@ -1665,8 +1638,8 @@
json += ',';
}
var elem = array[i];
- if (elem.toJSONProtocol) {
- json += elem.toJSONProtocol(true)
+ if (elem instanceof Mirror) {
+ json += mirror_serializer.serializeValue(elem);
} else if (IS_OBJECT(elem)) {
json += SimpleObjectToJSON_(elem);
} else if (IS_BOOLEAN(elem)) {
diff --git a/src/debug.cc b/src/debug.cc
index 368a975..ef059ad 100644
--- a/src/debug.cc
+++ b/src/debug.cc
@@ -1303,6 +1303,22 @@
}
+void Debug::ClearMirrorCache() {
+ ASSERT(Top::context() == *Debug::debug_context());
+
+ // Clear the mirror cache.
+ Handle<String> function_name =
+ Factory::LookupSymbol(CStrVector("ClearMirrorCache"));
+ Handle<Object> fun(Top::global()->GetProperty(*function_name));
+ ASSERT(fun->IsJSFunction());
+ bool caught_exception;
+ Handle<Object> js_object = Execution::TryCall(
+ Handle<JSFunction>::cast(fun),
+ Handle<JSObject>(Debug::debug_context()->global()),
+ 0, NULL, &caught_exception);
+}
+
+
bool Debugger::debugger_active_ = false;
bool Debugger::compiling_natives_ = false;
bool Debugger::is_loading_debugger_ = false;
@@ -1628,6 +1644,9 @@
}
}
}
+
+ // Clear the mirror cache.
+ Debug::ClearMirrorCache();
}
diff --git a/src/debug.h b/src/debug.h
index 56b8026..6520f0c 100644
--- a/src/debug.h
+++ b/src/debug.h
@@ -179,7 +179,7 @@
static Handle<DebugInfo> GetDebugInfo(Handle<SharedFunctionInfo> shared);
static bool HasDebugInfo(Handle<SharedFunctionInfo> shared);
- // Returns whether the operation succedded.
+ // Returns whether the operation succeeded.
static bool EnsureDebugInfo(Handle<SharedFunctionInfo> shared);
static bool IsDebugBreak(Address addr);
@@ -260,6 +260,9 @@
static char* RestoreDebug(char* from);
static int ArchiveSpacePerThread();
+ // Mirror cache handling.
+ static void ClearMirrorCache();
+
// Code generation assumptions.
static const int kIa32CallInstructionLength = 5;
static const int kIa32JSReturnSequenceLength = 6;
@@ -283,7 +286,7 @@
static void ActivateStepIn(StackFrame* frame);
static void ClearStepIn();
static void ClearStepNext();
- // Returns whether the compile succedded.
+ // Returns whether the compile succeeded.
static bool EnsureCompiled(Handle<SharedFunctionInfo> shared);
static void RemoveDebugInfo(Handle<DebugInfo> debug_info);
static void SetAfterBreakTarget(JavaScriptFrame* frame);
diff --git a/src/execution.cc b/src/execution.cc
index 7ccef5e..cb05314 100644
--- a/src/execution.cc
+++ b/src/execution.cc
@@ -439,7 +439,7 @@
int serial_number = Smi::cast(data->serial_number())->value();
Object* elm =
Top::global_context()->function_cache()->GetElement(serial_number);
- if (!elm->IsUndefined()) return Handle<JSFunction>(JSFunction::cast(elm));
+ if (elm->IsJSFunction()) return Handle<JSFunction>(JSFunction::cast(elm));
// The function has not yet been instantiated in this context; do it.
Object** args[1] = { Handle<Object>::cast(data).location() };
Handle<Object> result =
diff --git a/src/factory.cc b/src/factory.cc
index 9f6025b..c49f58a 100644
--- a/src/factory.cc
+++ b/src/factory.cc
@@ -195,6 +195,29 @@
}
+Handle<Map> Factory::CopyMap(Handle<Map> src,
+ int extra_inobject_properties) {
+ Handle<Map> copy = CopyMap(src);
+ // Check that we do not overflow the instance size when adding the
+ // extra inobject properties.
+ int instance_size_delta = extra_inobject_properties * kPointerSize;
+ int max_instance_size_delta =
+ JSObject::kMaxInstanceSize - copy->instance_size();
+ if (instance_size_delta > max_instance_size_delta) {
+ // If the instance size overflows, we allocate as many properties
+ // as we can as inobject properties.
+ instance_size_delta = max_instance_size_delta;
+ extra_inobject_properties = max_instance_size_delta >> kPointerSizeLog2;
+ }
+ // Adjust the map with the extra inobject properties.
+ int inobject_properties =
+ copy->inobject_properties() + extra_inobject_properties;
+ copy->set_inobject_properties(inobject_properties);
+ copy->set_unused_property_fields(inobject_properties);
+ copy->set_instance_size(copy->instance_size() + instance_size_delta);
+ return copy;
+}
+
Handle<Map> Factory::CopyMapDropTransitions(Handle<Map> src) {
CALL_HEAP_FUNCTION(src->CopyDropTransitions(), Map);
}
@@ -577,16 +600,6 @@
}
-Handle<JSObject> Factory::NewObjectLiteral(int expected_number_of_properties) {
- Handle<Map> map = Handle<Map>(Top::object_function()->initial_map());
- map = Factory::CopyMap(map);
- map->set_instance_descriptors(Heap::empty_descriptor_array());
- map->set_unused_property_fields(expected_number_of_properties);
- CALL_HEAP_FUNCTION(Heap::AllocateJSObjectFromMap(*map, TENURED),
- JSObject);
-}
-
-
Handle<JSArray> Factory::NewArrayLiteral(int length) {
return NewJSArrayWithElements(NewFixedArray(length), TENURED);
}
@@ -809,14 +822,15 @@
Handle<MapCache> new_cache = NewMapCache(24);
context->set_map_cache(*new_cache);
}
- // Check to see whether there is a maching element in the cache.
+ // Check to see whether there is a matching element in the cache.
Handle<MapCache> cache =
Handle<MapCache>(MapCache::cast(context->map_cache()));
Handle<Object> result = Handle<Object>(cache->Lookup(*keys));
if (result->IsMap()) return Handle<Map>::cast(result);
// Create a new map and add it to the cache.
Handle<Map> map =
- CopyMap(Handle<Map>(context->object_function()->initial_map()));
+ CopyMap(Handle<Map>(context->object_function()->initial_map()),
+ keys->length());
AddToMapCache(context, keys, map);
return Handle<Map>(map);
}
diff --git a/src/factory.h b/src/factory.h
index 3cbefd8..f34997d 100644
--- a/src/factory.h
+++ b/src/factory.h
@@ -129,7 +129,7 @@
Handle<JSObject> extension,
bool is_catch_context);
- // Return the Symbol maching the passed in string.
+ // Return the Symbol matching the passed in string.
static Handle<String> SymbolFromString(Handle<String> value);
// Allocate a new struct. The struct is pretenured (allocated directly in
@@ -157,6 +157,10 @@
static Handle<Map> CopyMap(Handle<Map> map);
+ // Copy the map adding more inobject properties if possible without
+ // overflowing the instance size.
+ static Handle<Map> CopyMap(Handle<Map> map, int extra_inobject_props);
+
static Handle<Map> CopyMapDropTransitions(Handle<Map> map);
static Handle<FixedArray> CopyFixedArray(Handle<FixedArray> array);
@@ -182,10 +186,6 @@
// runtime.
static Handle<JSObject> NewJSObjectFromMap(Handle<Map> map);
- // Allocate a JS object representing an object literal. The object is
- // pretenured (allocated directly in the old generation).
- static Handle<JSObject> NewObjectLiteral(int expected_number_of_properties);
-
// Allocate a JS array representing an array literal. The array is
// pretenured (allocated directly in the old generation).
static Handle<JSArray> NewArrayLiteral(int length);
@@ -293,7 +293,7 @@
// Installs interceptors on the instance. 'desc' is a function template,
// and instance is an object instance created by the function of this
- // function tempalte.
+ // function template.
static void ConfigureInstance(Handle<FunctionTemplateInfo> desc,
Handle<JSObject> instance,
bool* pending_exception);
diff --git a/src/frames.h b/src/frames.h
index 662d237..ea82cc6 100644
--- a/src/frames.h
+++ b/src/frames.h
@@ -261,7 +261,7 @@
virtual Code* FindCode() const;
- // Garbage colletion support.
+ // Garbage collection support.
virtual void Iterate(ObjectVisitor* v) const;
static ExitFrame* cast(StackFrame* frame) {
@@ -390,7 +390,7 @@
// frame below it on the stack.
inline bool has_adapted_arguments() const;
- // Garbage colletion support.
+ // Garbage collection support.
virtual void Iterate(ObjectVisitor* v) const;
// Printing support.
@@ -459,7 +459,7 @@
public:
virtual Type type() const { return INTERNAL; }
- // Garbage colletion support.
+ // Garbage collection support.
virtual void Iterate(ObjectVisitor* v) const;
// Determine the code for the frame.
diff --git a/src/handles.cc b/src/handles.cc
index a33cbfc..58ba472 100644
--- a/src/handles.cc
+++ b/src/handles.cc
@@ -99,8 +99,9 @@
}
-void NormalizeProperties(Handle<JSObject> object) {
- CALL_HEAP_FUNCTION_VOID(object->NormalizeProperties());
+void NormalizeProperties(Handle<JSObject> object,
+ PropertyNormalizationMode mode) {
+ CALL_HEAP_FUNCTION_VOID(object->NormalizeProperties(mode));
}
@@ -454,7 +455,7 @@
// Normalize the properties of object to avoid n^2 behavior
// when extending the object multiple properties.
unused_property_fields_ = object->map()->unused_property_fields();
- NormalizeProperties(object_);
+ NormalizeProperties(object_, KEEP_INOBJECT_PROPERTIES);
has_been_transformed_ = true;
} else {
diff --git a/src/handles.h b/src/handles.h
index 6d75c77..ec0614b 100644
--- a/src/handles.h
+++ b/src/handles.h
@@ -94,9 +94,10 @@
// Handle operations.
// They might invoke garbage collection. The result is an handle to
// an object of expected type, or the handle is an error if running out
-// of space or encounting an internal error.
+// of space or encountering an internal error.
-void NormalizeProperties(Handle<JSObject> object);
+void NormalizeProperties(Handle<JSObject> object,
+ PropertyNormalizationMode mode);
void NormalizeElements(Handle<JSObject> object);
void TransformToFastProperties(Handle<JSObject> object,
int unused_property_fields);
diff --git a/src/hashmap.h b/src/hashmap.h
index 7826311..fabf3dc 100644
--- a/src/hashmap.h
+++ b/src/hashmap.h
@@ -59,7 +59,7 @@
~HashMap();
- // HashMap entries are (key, value, hash) tripplets.
+ // HashMap entries are (key, value, hash) triplets.
// Some clients may not need to use the value slot
// (e.g. implementers of sets, where the key is the value).
struct Entry {
diff --git a/src/heap.cc b/src/heap.cc
index c4a860b..f8caa49 100644
--- a/src/heap.cc
+++ b/src/heap.cc
@@ -1904,7 +1904,7 @@
if (clone->IsFailure()) return clone;
ASSERT(Heap::InNewSpace(clone));
// Since we know the clone is allocated in new space, we can copy
- // the contents without worring about updating the write barrier.
+ // the contents without worrying about updating the write barrier.
CopyBlock(reinterpret_cast<Object**>(HeapObject::cast(clone)->address()),
reinterpret_cast<Object**>(source->address()),
object_size);
@@ -2293,6 +2293,7 @@
Object* Heap::AllocateFixedArray(int length) {
+ if (length == 0) return empty_fixed_array();
Object* result = AllocateRawFixedArray(length);
if (!result->IsFailure()) {
// Initialize header.
diff --git a/src/heap.h b/src/heap.h
index 3754c0b..46c6b0e 100644
--- a/src/heap.h
+++ b/src/heap.h
@@ -734,7 +734,7 @@
static void TracePathToGlobal();
#endif
- // Callback function pased to Heap::Iterate etc. Copies an object if
+ // Callback function passed to Heap::Iterate etc. Copies an object if
// necessary, the object might be promoted to an old space. The caller must
// ensure the precondition that the object is (a) a heap object and (b) in
// the heap's from space.
diff --git a/src/ic-arm.cc b/src/ic-arm.cc
index 4ccefa1..4db3980 100644
--- a/src/ic-arm.cc
+++ b/src/ic-arm.cc
@@ -699,7 +699,7 @@
// Check the key against the length in the array, compute the
// address to store into and fall through to fast case.
- __ ldr(r1, MemOperand(sp)); // resotre key
+ __ ldr(r1, MemOperand(sp)); // restore key
// r0 == value, r1 == key, r2 == elements, r3 == object.
__ ldr(ip, FieldMemOperand(r3, JSArray::kLengthOffset));
__ cmp(r1, Operand(ip));
diff --git a/src/ic.cc b/src/ic.cc
index b28d6f6..d7bd764 100644
--- a/src/ic.cc
+++ b/src/ic.cc
@@ -121,7 +121,7 @@
// normally would be.
Address addr = pc() - Assembler::kTargetAddrToReturnAddrDist;
// Return the address in the original code. This is the place where
- // the call which has been overwriten by the DebugBreakXXX resides
+ // the call which has been overwritten by the DebugBreakXXX resides
// and the place where the inline cache system should look.
int delta = original_code->instruction_start() - code->instruction_start();
return addr + delta;
@@ -1162,7 +1162,7 @@
FixedArray* new_storage = FixedArray::cast(result);
new_storage->set(old_storage->length(), value);
- // Set the new property value and do the map tranistion.
+ // Set the new property value and do the map transition.
object->set_properties(new_storage);
object->set_map(transition);
diff --git a/src/interpreter-irregexp.cc b/src/interpreter-irregexp.cc
index 13ba619..8ffc040 100644
--- a/src/interpreter-irregexp.cc
+++ b/src/interpreter-irregexp.cc
@@ -379,6 +379,13 @@
pc += BC_CHECK_REGISTER_GE_LENGTH;
}
break;
+ BYTECODE(CHECK_REGISTER_EQ_POS)
+ if (registers[pc[1]] == current) {
+ pc = code_base + Load32(pc + 2);
+ } 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!
diff --git a/src/jsregexp.cc b/src/jsregexp.cc
index 6cca7fc..aa11a69 100644
--- a/src/jsregexp.cc
+++ b/src/jsregexp.cc
@@ -40,6 +40,7 @@
#include "regexp-macro-assembler.h"
#include "regexp-macro-assembler-tracer.h"
#include "regexp-macro-assembler-irregexp.h"
+#include "regexp-stack.h"
#ifdef ARM
#include "regexp-macro-assembler-arm.h"
@@ -913,7 +914,7 @@
}
res = RegExpMacroAssemblerIA32::Execute(
*code,
- &address,
+ const_cast<Address*>(&address),
start_offset << char_size_shift,
end_offset << char_size_shift,
offsets_vector,
@@ -925,7 +926,7 @@
int byte_offset = char_address - reinterpret_cast<Address>(*subject);
res = RegExpMacroAssemblerIA32::Execute(
*code,
- subject.location(),
+ reinterpret_cast<Address*>(subject.location()),
byte_offset + (start_offset << char_size_shift),
byte_offset + (end_offset << char_size_shift),
offsets_vector,
@@ -1109,11 +1110,10 @@
// Execution state virtualization.
//
// Instead of emitting code, nodes that manipulate the state can record their
-// manipulation in an object called the GenerationVariant. The
-// GenerationVariant object can record a current position offset, an
-// optional backtrack code location on the top of the virtualized backtrack
-// stack and some register changes. When a node is to be emitted it can flush
-// the GenerationVariant or update it. Flushing the GenerationVariant
+// manipulation in an object called the Trace. The Trace object can record a
+// current position offset, an optional backtrack code location on the top of
+// the virtualized backtrack stack and some register changes. When a node is
+// to be emitted it can flush the Trace or update it. Flushing the Trace
// will emit code to bring the actual state into line with the virtual state.
// Avoiding flushing the state can postpone some work (eg updates of capture
// registers). Postponing work can save time when executing the regular
@@ -1122,20 +1122,19 @@
// known backtrack code location than it is to pop an unknown backtrack
// location from the stack and jump there.
//
-// The virtual state found in the GenerationVariant affects code generation.
-// For example the virtual state contains the difference between the actual
-// current position and the virtual current position, and matching code needs
-// to use this offset to attempt a match in the correct location of the input
-// string. Therefore code generated for a non-trivial GenerationVariant is
-// specialized to that GenerationVariant. The code generator therefore
-// has the ability to generate code for each node several times. In order to
-// limit the size of the generated code there is an arbitrary limit on how
-// many specialized sets of code may be generated for a given node. If the
-// limit is reached, the GenerationVariant is flushed and a generic version of
-// the code for a node is emitted. This is subsequently used for that node.
-// The code emitted for non-generic GenerationVariants is not recorded in the
-// node and so it cannot currently be reused in the event that code generation
-// is requested for an identical GenerationVariant.
+// The virtual state found in the Trace affects code generation. For example
+// the virtual state contains the difference between the actual current
+// position and the virtual current position, and matching code needs to use
+// this offset to attempt a match in the correct location of the input
+// string. Therefore code generated for a non-trivial trace is specialized
+// to that trace. The code generator therefore has the ability to generate
+// code for each node several times. In order to limit the size of the
+// generated code there is an arbitrary limit on how many specialized sets of
+// code may be generated for a given node. If the limit is reached, the
+// trace is flushed and a generic version of the code for a node is emitted.
+// This is subsequently used for that node. The code emitted for non-generic
+// trace is not recorded in the node and so it cannot currently be reused in
+// the event that code generation is requested for an identical trace.
void RegExpTree::AppendToText(RegExpText* text) {
@@ -1222,6 +1221,7 @@
inline bool ignore_case() { return ignore_case_; }
inline bool ascii() { return ascii_; }
+ static const int kNoRegister = -1;
private:
EndNode* accept_;
int next_register_;
@@ -1271,15 +1271,15 @@
work_list_ = &work_list;
Label fail;
macro_assembler->PushBacktrack(&fail);
- GenerationVariant generic_variant;
- if (!start->Emit(this, &generic_variant)) {
+ Trace new_trace;
+ if (!start->Emit(this, &new_trace)) {
fail.Unuse();
return Handle<FixedArray>::null();
}
macro_assembler_->Bind(&fail);
macro_assembler_->Fail();
while (!work_list.is_empty()) {
- if (!work_list.RemoveLast()->Emit(this, &generic_variant)) {
+ if (!work_list.RemoveLast()->Emit(this, &new_trace)) {
return Handle<FixedArray>::null();
}
}
@@ -1302,71 +1302,117 @@
return array;
}
+bool Trace::DeferredAction::Mentions(int that) {
+ if (type() == ActionNode::CLEAR_CAPTURES) {
+ Interval range = static_cast<DeferredClearCaptures*>(this)->range();
+ return range.Contains(that);
+ } else {
+ return reg() == that;
+ }
+}
-bool GenerationVariant::mentions_reg(int reg) {
+
+bool Trace::mentions_reg(int reg) {
for (DeferredAction* action = actions_;
action != NULL;
action = action->next()) {
- if (reg == action->reg()) return true;
+ if (action->Mentions(reg))
+ return true;
}
return false;
}
-int GenerationVariant::FindAffectedRegisters(OutSet* affected_registers) {
- int max_register = -1;
+bool Trace::GetStoredPosition(int reg, int* cp_offset) {
+ ASSERT_EQ(0, *cp_offset);
for (DeferredAction* action = actions_;
action != NULL;
action = action->next()) {
- affected_registers->Set(action->reg());
- if (action->reg() > max_register) max_register = action->reg();
+ if (action->Mentions(reg)) {
+ if (action->type() == ActionNode::STORE_POSITION) {
+ *cp_offset = static_cast<DeferredCapture*>(action)->cp_offset();
+ return true;
+ } else {
+ return false;
+ }
+ }
+ }
+ return false;
+}
+
+
+int Trace::FindAffectedRegisters(OutSet* affected_registers) {
+ int max_register = RegExpCompiler::kNoRegister;
+ for (DeferredAction* action = actions_;
+ action != NULL;
+ action = action->next()) {
+ if (action->type() == ActionNode::CLEAR_CAPTURES) {
+ Interval range = static_cast<DeferredClearCaptures*>(action)->range();
+ for (int i = range.from(); i <= range.to(); i++)
+ affected_registers->Set(i);
+ if (range.to() > max_register) max_register = range.to();
+ } else {
+ affected_registers->Set(action->reg());
+ if (action->reg() > max_register) max_register = action->reg();
+ }
}
return max_register;
}
-void GenerationVariant::PushAffectedRegisters(RegExpMacroAssembler* assembler,
- int max_register,
- OutSet& affected_registers) {
- for (int reg = 0; reg <= max_register; reg++) {
- if (affected_registers.Get(reg)) assembler->PushRegister(reg);
+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 GenerationVariant::RestoreAffectedRegisters(
- RegExpMacroAssembler* assembler,
- int max_register,
- OutSet& affected_registers) {
+void Trace::RestoreAffectedRegisters(RegExpMacroAssembler* assembler,
+ int max_register,
+ OutSet& affected_registers) {
for (int reg = max_register; reg >= 0; reg--) {
if (affected_registers.Get(reg)) assembler->PopRegister(reg);
}
}
-void GenerationVariant::PerformDeferredActions(RegExpMacroAssembler* assembler,
- int max_register,
- OutSet& affected_registers) {
+void Trace::PerformDeferredActions(RegExpMacroAssembler* assembler,
+ int max_register,
+ OutSet& affected_registers) {
for (int reg = 0; reg <= max_register; reg++) {
if (!affected_registers.Get(reg)) {
continue;
}
int value = 0;
bool absolute = false;
+ bool clear = false;
int store_position = -1;
// This is a little tricky because we are scanning the actions in reverse
// historical order (newest first).
for (DeferredAction* action = actions_;
action != NULL;
action = action->next()) {
- if (action->reg() == reg) {
+ if (action->Mentions(reg)) {
switch (action->type()) {
case ActionNode::SET_REGISTER: {
- GenerationVariant::DeferredSetRegister* psr =
- static_cast<GenerationVariant::DeferredSetRegister*>(action);
+ Trace::DeferredSetRegister* psr =
+ static_cast<Trace::DeferredSetRegister*>(action);
value += psr->value();
absolute = true;
ASSERT_EQ(store_position, -1);
+ ASSERT(!clear);
break;
}
case ActionNode::INCREMENT_REGISTER:
@@ -1374,17 +1420,28 @@
value++;
}
ASSERT_EQ(store_position, -1);
+ ASSERT(!clear);
break;
case ActionNode::STORE_POSITION: {
- GenerationVariant::DeferredCapture* pc =
- static_cast<GenerationVariant::DeferredCapture*>(action);
- if (store_position == -1) {
+ Trace::DeferredCapture* pc =
+ static_cast<Trace::DeferredCapture*>(action);
+ if (!clear && store_position == -1) {
store_position = pc->cp_offset();
}
ASSERT(!absolute);
ASSERT_EQ(value, 0);
break;
}
+ case ActionNode::CLEAR_CAPTURES: {
+ // 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)
+ clear = true;
+ ASSERT(!absolute);
+ ASSERT_EQ(value, 0);
+ break;
+ }
default:
UNREACHABLE();
break;
@@ -1393,23 +1450,21 @@
}
if (store_position != -1) {
assembler->WriteCurrentPositionToRegister(reg, store_position);
- } else {
- if (absolute) {
- assembler->SetRegister(reg, value);
- } else {
- if (value != 0) {
- assembler->AdvanceRegister(reg, value);
- }
- }
+ } else if (clear) {
+ assembler->ClearRegister(reg);
+ } else if (absolute) {
+ assembler->SetRegister(reg, value);
+ } else if (value != 0) {
+ assembler->AdvanceRegister(reg, value);
}
}
}
// This is called as we come into a loop choice node and some other tricky
-// nodes. It normalises the state of the code generator to ensure we can
+// nodes. It normalizes the state of the code generator to ensure we can
// generate generic code.
-bool GenerationVariant::Flush(RegExpCompiler* compiler, RegExpNode* successor) {
+bool Trace::Flush(RegExpCompiler* compiler, RegExpNode* successor) {
RegExpMacroAssembler* assembler = compiler->macro_assembler();
ASSERT(actions_ != NULL ||
@@ -1425,7 +1480,7 @@
// through a quick check that was already performed.
if (cp_offset_ != 0) assembler->AdvanceCurrentPosition(cp_offset_);
// Create a new trivial state and generate the node with that.
- GenerationVariant new_state;
+ Trace new_state;
return successor->Emit(compiler, &new_state);
}
@@ -1447,7 +1502,7 @@
// Create a new trivial state and generate the node with that.
Label undo;
assembler->PushBacktrack(&undo);
- GenerationVariant new_state;
+ Trace new_state;
bool ok = successor->Emit(compiler, &new_state);
// On backtrack we need to restore state.
@@ -1467,29 +1522,27 @@
}
-void EndNode::EmitInfoChecks(RegExpMacroAssembler* assembler,
- GenerationVariant* variant) {
+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(variant->backtrack());
+ assembler->GoTo(trace->backtrack());
assembler->Bind(&succeed);
}
}
-bool NegativeSubmatchSuccess::Emit(RegExpCompiler* compiler,
- GenerationVariant* variant) {
- if (!variant->is_trivial()) {
- return variant->Flush(compiler, this);
+bool NegativeSubmatchSuccess::Emit(RegExpCompiler* compiler, Trace* trace) {
+ if (!trace->is_trivial()) {
+ return trace->Flush(compiler, this);
}
RegExpMacroAssembler* assembler = compiler->macro_assembler();
if (!label()->is_bound()) {
assembler->Bind(label());
}
- EmitInfoChecks(assembler, variant);
+ EmitInfoChecks(assembler, trace);
assembler->ReadCurrentPositionFromRegister(current_position_register_);
assembler->ReadStackPointerFromRegister(stack_pointer_register_);
// Now that we have unwound the stack we find at the top of the stack the
@@ -1499,9 +1552,9 @@
}
-bool EndNode::Emit(RegExpCompiler* compiler, GenerationVariant* variant) {
- if (!variant->is_trivial()) {
- return variant->Flush(compiler, this);
+bool EndNode::Emit(RegExpCompiler* compiler, Trace* trace) {
+ if (!trace->is_trivial()) {
+ return trace->Flush(compiler, this);
}
RegExpMacroAssembler* assembler = compiler->macro_assembler();
if (!label()->is_bound()) {
@@ -1509,12 +1562,12 @@
}
switch (action_) {
case ACCEPT:
- EmitInfoChecks(assembler, variant);
+ EmitInfoChecks(assembler, trace);
assembler->Succeed();
return true;
case BACKTRACK:
ASSERT(!info()->at_end);
- assembler->GoTo(variant->backtrack());
+ assembler->GoTo(trace->backtrack());
return true;
case NEGATIVE_SUBMATCH_SUCCESS:
// This case is handled in a different virtual method.
@@ -1556,6 +1609,15 @@
}
+ActionNode* ActionNode::ClearCaptures(Interval range,
+ RegExpNode* on_success) {
+ ActionNode* result = new ActionNode(CLEAR_CAPTURES, on_success);
+ result->data_.u_clear_captures.range_from = range.from();
+ result->data_.u_clear_captures.range_to = range.to();
+ return result;
+}
+
+
ActionNode* ActionNode::BeginSubmatch(int stack_reg,
int position_reg,
RegExpNode* on_success) {
@@ -1576,6 +1638,18 @@
}
+ActionNode* ActionNode::EmptyMatchCheck(int start_register,
+ int repetition_register,
+ int repetition_limit,
+ RegExpNode* on_success) {
+ ActionNode* result = new ActionNode(EMPTY_MATCH_CHECK, on_success);
+ result->data_.u_empty_match_check.start_register = start_register;
+ result->data_.u_empty_match_check.repetition_register = repetition_register;
+ result->data_.u_empty_match_check.repetition_limit = repetition_limit;
+ return result;
+}
+
+
#define DEFINE_ACCEPT(Type) \
void Type##Node::Accept(NodeVisitor* visitor) { \
visitor->Visit##Type(this); \
@@ -1595,19 +1669,19 @@
void ChoiceNode::GenerateGuard(RegExpMacroAssembler* macro_assembler,
Guard* guard,
- GenerationVariant* variant) {
+ Trace* trace) {
switch (guard->op()) {
case Guard::LT:
- ASSERT(!variant->mentions_reg(guard->reg()));
+ ASSERT(!trace->mentions_reg(guard->reg()));
macro_assembler->IfRegisterGE(guard->reg(),
guard->value(),
- variant->backtrack());
+ trace->backtrack());
break;
case Guard::GEQ:
- ASSERT(!variant->mentions_reg(guard->reg()));
+ ASSERT(!trace->mentions_reg(guard->reg()));
macro_assembler->IfRegisterLT(guard->reg(),
guard->value(),
- variant->backtrack());
+ trace->backtrack());
break;
}
}
@@ -1860,7 +1934,7 @@
RegExpNode::LimitResult RegExpNode::LimitVersions(RegExpCompiler* compiler,
- GenerationVariant* variant) {
+ Trace* trace) {
// TODO(erikcorry): Implement support.
if (info_.follows_word_interest ||
info_.follows_newline_interest ||
@@ -1869,12 +1943,12 @@
}
// If we are generating a greedy loop then don't stop and don't reuse code.
- if (variant->stop_node() != NULL) {
+ if (trace->stop_node() != NULL) {
return CONTINUE;
}
RegExpMacroAssembler* macro_assembler = compiler->macro_assembler();
- if (variant->is_trivial()) {
+ if (trace->is_trivial()) {
if (label_.is_bound()) {
// We are being asked to generate a generic version, but that's already
// been done so just go to it.
@@ -1895,16 +1969,16 @@
// We are being asked to make a non-generic version. Keep track of how many
// non-generic versions we generate so as not to overdo it.
- variants_generated_++;
- if (variants_generated_ < kMaxVariantsGenerated &&
+ trace_count_++;
+ if (trace_count_ < kMaxCopiesCodeGenerated &&
compiler->recursion_depth() <= RegExpCompiler::kMaxRecursion) {
return CONTINUE;
}
- // If we get here there have been too many variants generated or recursion
- // is too deep. Time to switch to a generic version. The code for
+ // 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 = variant->Flush(compiler, this);
+ bool ok = trace->Flush(compiler, this);
return ok ? DONE : FAIL;
}
@@ -1985,7 +2059,7 @@
bool RegExpNode::EmitQuickCheck(RegExpCompiler* compiler,
- GenerationVariant* variant,
+ Trace* trace,
bool preload_has_checked_bounds,
Label* on_possible_success,
QuickCheckDetails* details,
@@ -1998,9 +2072,9 @@
RegExpMacroAssembler* assembler = compiler->macro_assembler();
- if (variant->characters_preloaded() != details->characters()) {
- assembler->LoadCurrentCharacter(variant->cp_offset(),
- variant->backtrack(),
+ if (trace->characters_preloaded() != details->characters()) {
+ assembler->LoadCurrentCharacter(trace->cp_offset(),
+ trace->backtrack(),
!preload_has_checked_bounds,
details->characters());
}
@@ -2037,9 +2111,9 @@
}
} else {
if (need_mask) {
- assembler->CheckNotCharacterAfterAnd(value, mask, variant->backtrack());
+ assembler->CheckNotCharacterAfterAnd(value, mask, trace->backtrack());
} else {
- assembler->CheckNotCharacter(value, variant->backtrack());
+ assembler->CheckNotCharacter(value, trace->backtrack());
}
}
return true;
@@ -2225,10 +2299,25 @@
}
+class VisitMarker {
+ public:
+ explicit VisitMarker(NodeInfo* info) : info_(info) {
+ ASSERT(!info->visited);
+ info->visited = true;
+ }
+ ~VisitMarker() {
+ info_->visited = false;
+ }
+ private:
+ NodeInfo* info_;
+};
+
+
void LoopChoiceNode::GetQuickCheckDetails(QuickCheckDetails* details,
RegExpCompiler* compiler,
int characters_filled_in) {
- if (body_can_be_zero_length_) return;
+ if (body_can_be_zero_length_ || info()->visited) return;
+ VisitMarker marker(info());
return ChoiceNode::GetQuickCheckDetails(details,
compiler,
characters_filled_in);
@@ -2274,7 +2363,7 @@
// first_element_checked to indicate that that character does not need to be
// checked again.
//
-// In addition to all this we are passed a GenerationVariant, which can
+// In addition to all this we are passed a Trace, which can
// contain an AlternativeGeneration object. In this AlternativeGeneration
// object we can see details of any quick check that was already passed in
// order to get to the code we are now generating. The quick check can involve
@@ -2285,17 +2374,17 @@
void TextNode::TextEmitPass(RegExpCompiler* compiler,
TextEmitPassType pass,
bool preloaded,
- GenerationVariant* variant,
+ Trace* trace,
bool first_element_checked,
int* checked_up_to) {
RegExpMacroAssembler* assembler = compiler->macro_assembler();
bool ascii = compiler->ascii();
- Label* backtrack = variant->backtrack();
- QuickCheckDetails* quick_check = variant->quick_check_performed();
+ Label* backtrack = trace->backtrack();
+ QuickCheckDetails* quick_check = trace->quick_check_performed();
int element_count = elms_->length();
for (int i = preloaded ? 0 : element_count - 1; i >= 0; i--) {
TextElement elm = elms_->at(i);
- int cp_offset = variant->cp_offset() + elm.cp_offset;
+ int cp_offset = trace->cp_offset() + elm.cp_offset;
if (elm.type == TextElement::ATOM) {
if (pass == NON_ASCII_MATCH ||
pass == CHARACTER_MATCH ||
@@ -2392,8 +2481,8 @@
// 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, GenerationVariant* variant) {
- LimitResult limit_result = LimitVersions(compiler, variant);
+bool TextNode::Emit(RegExpCompiler* compiler, Trace* trace) {
+ LimitResult limit_result = LimitVersions(compiler, trace);
if (limit_result == FAIL) return false;
if (limit_result == DONE) return true;
ASSERT(limit_result == CONTINUE);
@@ -2405,40 +2494,40 @@
}
if (info()->at_end) {
- compiler->macro_assembler()->GoTo(variant->backtrack());
+ compiler->macro_assembler()->GoTo(trace->backtrack());
return true;
}
if (compiler->ascii()) {
int dummy = 0;
- TextEmitPass(compiler, NON_ASCII_MATCH, false, variant, false, &dummy);
+ TextEmitPass(compiler, NON_ASCII_MATCH, false, trace, false, &dummy);
}
bool first_elt_done = false;
- int bound_checked_to = variant->cp_offset() - 1;
- bound_checked_to += variant->bound_checked_up_to();
+ int bound_checked_to = trace->cp_offset() - 1;
+ bound_checked_to += trace->bound_checked_up_to();
// If a character is preloaded into the current character register then
// check that now.
- if (variant->characters_preloaded() == 1) {
+ if (trace->characters_preloaded() == 1) {
TextEmitPass(compiler,
CHARACTER_MATCH,
true,
- variant,
+ trace,
false,
&bound_checked_to);
if (compiler->ignore_case()) {
TextEmitPass(compiler,
CASE_CHARACTER_MATCH,
true,
- variant,
+ trace,
false,
&bound_checked_to);
}
TextEmitPass(compiler,
CHARACTER_CLASS_MATCH,
true,
- variant,
+ trace,
false,
&bound_checked_to);
first_elt_done = true;
@@ -2447,32 +2536,32 @@
TextEmitPass(compiler,
CHARACTER_MATCH,
false,
- variant,
+ trace,
first_elt_done,
&bound_checked_to);
if (compiler->ignore_case()) {
TextEmitPass(compiler,
CASE_CHARACTER_MATCH,
false,
- variant,
+ trace,
first_elt_done,
&bound_checked_to);
}
TextEmitPass(compiler,
CHARACTER_CLASS_MATCH,
false,
- variant,
+ trace,
first_elt_done,
&bound_checked_to);
- GenerationVariant successor_variant(*variant);
- successor_variant.AdvanceVariant(Length(), compiler->ascii());
+ Trace successor_trace(*trace);
+ successor_trace.AdvanceCurrentPositionInTrace(Length(), compiler->ascii());
RecursionCheck rc(compiler);
- return on_success()->Emit(compiler, &successor_variant);
+ return on_success()->Emit(compiler, &successor_trace);
}
-void GenerationVariant::AdvanceVariant(int by, bool ascii) {
+void Trace::AdvanceCurrentPositionInTrace(int by, bool ascii) {
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
@@ -2559,24 +2648,23 @@
}
-bool LoopChoiceNode::Emit(RegExpCompiler* compiler,
- GenerationVariant* variant) {
+bool LoopChoiceNode::Emit(RegExpCompiler* compiler, Trace* trace) {
RegExpMacroAssembler* macro_assembler = compiler->macro_assembler();
- if (variant->stop_node() == this) {
+ if (trace->stop_node() == this) {
int text_length = GreedyLoopTextLength(&(alternatives_->at(0)));
ASSERT(text_length != kNodeIsTooComplexForGreedyLoops);
// Update the counter-based backtracking info on the stack. This is an
// optimization for greedy loops (see below).
- ASSERT(variant->cp_offset() == text_length);
+ ASSERT(trace->cp_offset() == text_length);
macro_assembler->AdvanceCurrentPosition(text_length);
- macro_assembler->GoTo(variant->loop_label());
+ macro_assembler->GoTo(trace->loop_label());
return true;
}
- ASSERT(variant->stop_node() == NULL);
- if (!variant->is_trivial()) {
- return variant->Flush(compiler, this);
+ ASSERT(trace->stop_node() == NULL);
+ if (!trace->is_trivial()) {
+ return trace->Flush(compiler, this);
}
- return ChoiceNode::Emit(compiler, variant);
+ return ChoiceNode::Emit(compiler, trace);
}
@@ -2729,7 +2817,7 @@
*/
-bool ChoiceNode::Emit(RegExpCompiler* compiler, GenerationVariant* variant) {
+bool ChoiceNode::Emit(RegExpCompiler* compiler, Trace* trace) {
RegExpMacroAssembler* macro_assembler = compiler->macro_assembler();
int choice_count = alternatives_->length();
#ifdef DEBUG
@@ -2738,25 +2826,25 @@
ZoneList<Guard*>* guards = alternative.guards();
int guard_count = (guards == NULL) ? 0 : guards->length();
for (int j = 0; j < guard_count; j++) {
- ASSERT(!variant->mentions_reg(guards->at(j)->reg()));
+ ASSERT(!trace->mentions_reg(guards->at(j)->reg()));
}
}
#endif
- LimitResult limit_result = LimitVersions(compiler, variant);
+ LimitResult limit_result = LimitVersions(compiler, trace);
if (limit_result == DONE) return true;
if (limit_result == FAIL) return false;
ASSERT(limit_result == CONTINUE);
RecursionCheck rc(compiler);
- GenerationVariant* current_variant = variant;
+ Trace* current_trace = trace;
int text_length = GreedyLoopTextLength(&(alternatives_->at(0)));
bool greedy_loop = false;
Label greedy_loop_label;
- GenerationVariant counter_backtrack_variant;
- counter_backtrack_variant.set_backtrack(&greedy_loop_label);
+ Trace counter_backtrack_trace;
+ counter_backtrack_trace.set_backtrack(&greedy_loop_label);
if (choice_count > 1 && text_length != kNodeIsTooComplexForGreedyLoops) {
// Here we have special handling for greedy loops containing only text nodes
// and other simple nodes. These are handled by pushing the current
@@ -2766,18 +2854,18 @@
// information for each iteration of the loop, which could take up a lot of
// space.
greedy_loop = true;
- ASSERT(variant->stop_node() == NULL);
+ ASSERT(trace->stop_node() == NULL);
macro_assembler->PushCurrentPosition();
- current_variant = &counter_backtrack_variant;
+ current_trace = &counter_backtrack_trace;
Label greedy_match_failed;
- GenerationVariant greedy_match_variant;
- greedy_match_variant.set_backtrack(&greedy_match_failed);
+ Trace greedy_match_trace;
+ greedy_match_trace.set_backtrack(&greedy_match_failed);
Label loop_label;
macro_assembler->Bind(&loop_label);
- greedy_match_variant.set_stop_node(this);
- greedy_match_variant.set_loop_label(&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_variant);
+ &greedy_match_trace);
macro_assembler->Bind(&greedy_match_failed);
if (!ok) {
greedy_loop_label.Unuse();
@@ -2792,7 +2880,7 @@
int preload_characters = CalculatePreloadCharacters(compiler);
bool preload_is_current =
- (current_variant->characters_preloaded() == preload_characters);
+ (current_trace->characters_preloaded() == preload_characters);
bool preload_has_checked_bounds = preload_is_current;
AlternativeGenerationList alt_gens(choice_count);
@@ -2801,22 +2889,22 @@
// is to use the Dispatch table to try only the relevant ones.
for (int i = first_normal_choice; i < choice_count; i++) {
GuardedAlternative alternative = alternatives_->at(i);
- AlternativeGeneration* alt_gen(alt_gens.at(i));
+ AlternativeGeneration* alt_gen = alt_gens.at(i);
alt_gen->quick_check_details.set_characters(preload_characters);
ZoneList<Guard*>* guards = alternative.guards();
int guard_count = (guards == NULL) ? 0 : guards->length();
- GenerationVariant new_variant(*current_variant);
- new_variant.set_characters_preloaded(preload_is_current ?
+ Trace new_trace(*current_trace);
+ new_trace.set_characters_preloaded(preload_is_current ?
preload_characters :
0);
if (preload_has_checked_bounds) {
- new_variant.set_bound_checked_up_to(preload_characters);
+ new_trace.set_bound_checked_up_to(preload_characters);
}
- new_variant.quick_check_performed()->Clear();
+ new_trace.quick_check_performed()->Clear();
alt_gen->expects_preload = preload_is_current;
bool generate_full_check_inline = false;
if (alternative.node()->EmitQuickCheck(compiler,
- &new_variant,
+ &new_trace,
preload_has_checked_bounds,
&alt_gen->possible_success,
&alt_gen->quick_check_details,
@@ -2829,9 +2917,9 @@
// generate the full check inline.
if (i == choice_count - 1) {
macro_assembler->Bind(&alt_gen->possible_success);
- new_variant.set_quick_check_performed(&alt_gen->quick_check_details);
- new_variant.set_characters_preloaded(preload_characters);
- new_variant.set_bound_checked_up_to(preload_characters);
+ new_trace.set_quick_check_performed(&alt_gen->quick_check_details);
+ new_trace.set_characters_preloaded(preload_characters);
+ new_trace.set_bound_checked_up_to(preload_characters);
generate_full_check_inline = true;
}
} else {
@@ -2842,18 +2930,18 @@
// to generate probably can't use it.
if (i != first_normal_choice) {
alt_gen->expects_preload = false;
- new_variant.set_characters_preloaded(0);
+ new_trace.set_characters_preloaded(0);
}
if (i < choice_count - 1) {
- new_variant.set_backtrack(&alt_gen->after);
+ new_trace.set_backtrack(&alt_gen->after);
}
generate_full_check_inline = true;
}
if (generate_full_check_inline) {
for (int j = 0; j < guard_count; j++) {
- GenerateGuard(macro_assembler, guards->at(j), &new_variant);
+ GenerateGuard(macro_assembler, guards->at(j), &new_trace);
}
- if (!alternative.node()->Emit(compiler, &new_variant)) {
+ if (!alternative.node()->Emit(compiler, &new_trace)) {
greedy_loop_label.Unuse();
return false;
}
@@ -2864,7 +2952,7 @@
if (greedy_loop) {
macro_assembler->Bind(&greedy_loop_label);
// If we have unwound to the bottom then backtrack.
- macro_assembler->CheckGreedyLoop(variant->backtrack());
+ macro_assembler->CheckGreedyLoop(trace->backtrack());
// Otherwise try the second priority at an earlier position.
macro_assembler->AdvanceCurrentPosition(-text_length);
macro_assembler->GoTo(&second_choice);
@@ -2875,7 +2963,7 @@
for (int i = first_normal_choice; i < choice_count - 1; i++) {
AlternativeGeneration* alt_gen = alt_gens.at(i);
if (!EmitOutOfLineContinuation(compiler,
- current_variant,
+ current_trace,
alternatives_->at(i),
alt_gen,
preload_characters,
@@ -2888,7 +2976,7 @@
bool ChoiceNode::EmitOutOfLineContinuation(RegExpCompiler* compiler,
- GenerationVariant* variant,
+ Trace* trace,
GuardedAlternative alternative,
AlternativeGeneration* alt_gen,
int preload_characters,
@@ -2897,41 +2985,41 @@
RegExpMacroAssembler* macro_assembler = compiler->macro_assembler();
macro_assembler->Bind(&alt_gen->possible_success);
- GenerationVariant out_of_line_variant(*variant);
- out_of_line_variant.set_characters_preloaded(preload_characters);
- out_of_line_variant.set_quick_check_performed(&alt_gen->quick_check_details);
+ Trace out_of_line_trace(*trace);
+ out_of_line_trace.set_characters_preloaded(preload_characters);
+ out_of_line_trace.set_quick_check_performed(&alt_gen->quick_check_details);
ZoneList<Guard*>* guards = alternative.guards();
int guard_count = (guards == NULL) ? 0 : guards->length();
if (next_expects_preload) {
Label reload_current_char;
- out_of_line_variant.set_backtrack(&reload_current_char);
+ out_of_line_trace.set_backtrack(&reload_current_char);
for (int j = 0; j < guard_count; j++) {
- GenerateGuard(macro_assembler, guards->at(j), &out_of_line_variant);
+ GenerateGuard(macro_assembler, guards->at(j), &out_of_line_trace);
}
- bool ok = alternative.node()->Emit(compiler, &out_of_line_variant);
+ bool ok = 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
// code through a quick check which already did the checked load.
- macro_assembler->LoadCurrentCharacter(variant->cp_offset(),
+ macro_assembler->LoadCurrentCharacter(trace->cp_offset(),
NULL,
false,
preload_characters);
macro_assembler->GoTo(&(alt_gen->after));
return ok;
} else {
- out_of_line_variant.set_backtrack(&(alt_gen->after));
+ 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_variant);
+ GenerateGuard(macro_assembler, guards->at(j), &out_of_line_trace);
}
- return alternative.node()->Emit(compiler, &out_of_line_variant);
+ return alternative.node()->Emit(compiler, &out_of_line_trace);
}
}
-bool ActionNode::Emit(RegExpCompiler* compiler, GenerationVariant* variant) {
+bool ActionNode::Emit(RegExpCompiler* compiler, Trace* trace) {
RegExpMacroAssembler* assembler = compiler->macro_assembler();
- LimitResult limit_result = LimitVersions(compiler, variant);
+ LimitResult limit_result = LimitVersions(compiler, trace);
if (limit_result == DONE) return true;
if (limit_result == FAIL) return false;
ASSERT(limit_result == CONTINUE);
@@ -2940,35 +3028,74 @@
switch (type_) {
case STORE_POSITION: {
- GenerationVariant::DeferredCapture
- new_capture(data_.u_position_register.reg, variant);
- GenerationVariant new_variant = *variant;
- new_variant.add_action(&new_capture);
- return on_success()->Emit(compiler, &new_variant);
+ Trace::DeferredCapture
+ new_capture(data_.u_position_register.reg, trace);
+ Trace new_trace = *trace;
+ new_trace.add_action(&new_capture);
+ return on_success()->Emit(compiler, &new_trace);
}
case INCREMENT_REGISTER: {
- GenerationVariant::DeferredIncrementRegister
+ Trace::DeferredIncrementRegister
new_increment(data_.u_increment_register.reg);
- GenerationVariant new_variant = *variant;
- new_variant.add_action(&new_increment);
- return on_success()->Emit(compiler, &new_variant);
+ Trace new_trace = *trace;
+ new_trace.add_action(&new_increment);
+ return on_success()->Emit(compiler, &new_trace);
}
case SET_REGISTER: {
- GenerationVariant::DeferredSetRegister
+ Trace::DeferredSetRegister
new_set(data_.u_store_register.reg, data_.u_store_register.value);
- GenerationVariant new_variant = *variant;
- new_variant.add_action(&new_set);
- return on_success()->Emit(compiler, &new_variant);
+ Trace new_trace = *trace;
+ new_trace.add_action(&new_set);
+ return on_success()->Emit(compiler, &new_trace);
+ }
+ case CLEAR_CAPTURES: {
+ Trace::DeferredClearCaptures
+ new_capture(Interval(data_.u_clear_captures.range_from,
+ data_.u_clear_captures.range_to));
+ Trace new_trace = *trace;
+ new_trace.add_action(&new_capture);
+ return on_success()->Emit(compiler, &new_trace);
}
case BEGIN_SUBMATCH:
- if (!variant->is_trivial()) return variant->Flush(compiler, this);
+ 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, variant);
+ return on_success()->Emit(compiler, trace);
+ case EMPTY_MATCH_CHECK: {
+ int start_pos_reg = data_.u_empty_match_check.start_register;
+ int stored_pos = 0;
+ int rep_reg = data_.u_empty_match_check.repetition_register;
+ bool has_minimum = (rep_reg != RegExpCompiler::kNoRegister);
+ bool know_dist = trace->GetStoredPosition(start_pos_reg, &stored_pos);
+ if (know_dist && !has_minimum && stored_pos == trace->cp_offset()) {
+ // 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);
+ }
+ 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);
+ }
case POSITIVE_SUBMATCH_SUCCESS:
- if (!variant->is_trivial()) return variant->Flush(compiler, this);
+ if (!trace->is_trivial()) return trace->Flush(compiler, this);
// TODO(erikcorry): Implement support.
if (info()->follows_word_interest ||
info()->follows_newline_interest ||
@@ -2980,14 +3107,14 @@
// Load current character jumps to the label if we are beyond the string
// end.
assembler->LoadCurrentCharacter(0, &at_end);
- assembler->GoTo(variant->backtrack());
+ assembler->GoTo(trace->backtrack());
assembler->Bind(&at_end);
}
assembler->ReadCurrentPositionFromRegister(
data_.u_submatch.current_position_register);
assembler->ReadStackPointerFromRegister(
data_.u_submatch.stack_pointer_register);
- return on_success()->Emit(compiler, variant);
+ return on_success()->Emit(compiler, trace);
default:
UNREACHABLE();
return false;
@@ -2995,14 +3122,13 @@
}
-bool BackReferenceNode::Emit(RegExpCompiler* compiler,
- GenerationVariant* variant) {
+bool BackReferenceNode::Emit(RegExpCompiler* compiler, Trace* trace) {
RegExpMacroAssembler* assembler = compiler->macro_assembler();
- if (!variant->is_trivial()) {
- return variant->Flush(compiler, this);
+ if (!trace->is_trivial()) {
+ return trace->Flush(compiler, this);
}
- LimitResult limit_result = LimitVersions(compiler, variant);
+ LimitResult limit_result = LimitVersions(compiler, trace);
if (limit_result == DONE) return true;
if (limit_result == FAIL) return false;
ASSERT(limit_result == CONTINUE);
@@ -3015,16 +3141,16 @@
// iff the back reference is empty.
assembler->CheckNotRegistersEqual(start_reg_,
end_reg_,
- variant->backtrack());
+ trace->backtrack());
} else {
if (compiler->ignore_case()) {
assembler->CheckNotBackReferenceIgnoreCase(start_reg_,
- variant->backtrack());
+ trace->backtrack());
} else {
- assembler->CheckNotBackReference(start_reg_, variant->backtrack());
+ assembler->CheckNotBackReference(start_reg_, trace->backtrack());
}
}
- return on_success()->Emit(compiler, variant);
+ return on_success()->Emit(compiler, trace);
}
@@ -3286,6 +3412,18 @@
case ActionNode::POSITIVE_SUBMATCH_SUCCESS:
stream()->Add("label=\"escape\", shape=septagon");
break;
+ case ActionNode::EMPTY_MATCH_CHECK:
+ stream()->Add("label=\"$%i=$pos?,$%i<%i?\", shape=septagon",
+ that->data_.u_empty_match_check.start_register,
+ that->data_.u_empty_match_check.repetition_register,
+ that->data_.u_empty_match_check.repetition_limit);
+ break;
+ case ActionNode::CLEAR_CAPTURES: {
+ stream()->Add("label=\"clear $%i to $%i\", shape=septagon",
+ that->data_.u_clear_captures.range_from,
+ that->data_.u_clear_captures.range_to);
+ break;
+ }
}
stream()->Add("];\n");
PrintAttributes(that);
@@ -3511,7 +3649,15 @@
static const int kMaxUnrolledMinMatches = 3; // Unroll (foo)+ and (foo){3,}
static const int kMaxUnrolledMaxMatches = 3; // Unroll (foo)? and (foo){x,3}
if (max == 0) return on_success; // This can happen due to recursion.
- if (body->min_match() > 0) {
+ bool body_can_be_empty = (body->min_match() == 0);
+ int body_start_reg = RegExpCompiler::kNoRegister;
+ Interval capture_registers = body->CaptureRegisters();
+ bool needs_capture_clearing = !capture_registers.is_empty();
+ if (body_can_be_empty) {
+ body_start_reg = compiler->AllocateRegister();
+ } else if (!needs_capture_clearing) {
+ // Only unroll if there are no captures and the body can't be
+ // empty.
if (min > 0 && min <= kMaxUnrolledMinMatches) {
int new_max = (max == kInfinity) ? max : max - min;
// Recurse once to get the loop or optional matches after the fixed ones.
@@ -3548,12 +3694,31 @@
bool has_min = min > 0;
bool has_max = max < RegExpTree::kInfinity;
bool needs_counter = has_min || has_max;
- int reg_ctr = needs_counter ? compiler->AllocateRegister() : -1;
+ int reg_ctr = needs_counter
+ ? compiler->AllocateRegister()
+ : RegExpCompiler::kNoRegister;
LoopChoiceNode* center = new LoopChoiceNode(body->min_match() == 0);
RegExpNode* loop_return = needs_counter
? static_cast<RegExpNode*>(ActionNode::IncrementRegister(reg_ctr, center))
: static_cast<RegExpNode*>(center);
+ if (body_can_be_empty) {
+ // If the body can be empty we need to check if it was and then
+ // backtrack.
+ loop_return = ActionNode::EmptyMatchCheck(body_start_reg,
+ reg_ctr,
+ min,
+ loop_return);
+ }
RegExpNode* body_node = body->ToNode(compiler, loop_return);
+ 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);
+ }
+ if (needs_capture_clearing) {
+ // Before entering the body of this loop we need to clear captures.
+ body_node = ActionNode::ClearCaptures(capture_registers, body_node);
+ }
GuardedAlternative body_alt(body_node);
if (has_max) {
Guard* body_guard = new Guard(reg_ctr, Guard::LT, max);
diff --git a/src/jsregexp.h b/src/jsregexp.h
index a2e1647..bf3bdb7 100644
--- a/src/jsregexp.h
+++ b/src/jsregexp.h
@@ -349,7 +349,7 @@
uint32_t first_;
ZoneList<unsigned>* remaining_;
ZoneList<OutSet*>* successors_;
- friend class GenerationVariant;
+ friend class Trace;
};
@@ -449,7 +449,7 @@
};
-class GenerationVariant;
+class Trace;
struct NodeInfo {
@@ -583,13 +583,13 @@
class RegExpNode: public ZoneObject {
public:
- RegExpNode() : variants_generated_(0) { }
+ RegExpNode() : trace_count_(0) { }
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, GenerationVariant* variant) = 0;
+ virtual bool 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;
@@ -597,7 +597,7 @@
// 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.
bool EmitQuickCheck(RegExpCompiler* compiler,
- GenerationVariant* variant,
+ Trace* trace,
bool preload_has_checked_bounds,
Label* on_possible_success,
QuickCheckDetails* details_return,
@@ -612,7 +612,12 @@
static const int kNodeIsTooComplexForGreedyLoops = -1;
virtual int GreedyLoopTextLength() { return kNodeIsTooComplexForGreedyLoops; }
Label* label() { return &label_; }
- static const int kMaxVariantsGenerated = 10;
+ // If non-generic code is generated for a node (ie the node is not at the
+ // start of the trace) then it cannot be reused. This variable sets a limit
+ // on how often we allow that to happen before we insist on starting a new
+ // trace and generating generic code for a node that can be reused by flushing
+ // 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
@@ -636,8 +641,7 @@
protected:
enum LimitResult { DONE, FAIL, CONTINUE };
- LimitResult LimitVersions(RegExpCompiler* compiler,
- GenerationVariant* variant);
+ LimitResult LimitVersions(RegExpCompiler* compiler, Trace* trace);
// Returns a sibling of this node whose interests and assumptions
// match the ones in the given node info. If no sibling exists NULL
@@ -653,14 +657,46 @@
// Returns a clone of this node initialized using the copy constructor
// of its concrete class. Note that the node may have to be pre-
- // processed before it is on a useable state.
+ // processed before it is on a usable state.
virtual RegExpNode* Clone() = 0;
private:
Label label_;
NodeInfo info_;
SiblingList siblings_;
- int variants_generated_;
+ // This variable keeps track of how many times code has been generated for
+ // this node (in different traces). We don't keep track of where the
+ // generated code is located unless the code is generated at the start of
+ // a trace, in which case it is generic and can be reused by flushing the
+ // deferred operations in the current trace and generating a goto.
+ int trace_count_;
+};
+
+
+// A simple closed interval.
+class Interval {
+ public:
+ Interval() : from_(kNone), to_(kNone) { }
+ Interval(int from, int to) : from_(from), to_(to) { }
+ Interval Union(Interval that) {
+ if (that.from_ == kNone)
+ return *this;
+ else if (from_ == kNone)
+ return that;
+ else
+ return Interval(Min(from_, that.from_), Max(to_, that.to_));
+ }
+ bool Contains(int value) {
+ return (from_ <= value) && (value <= to_);
+ }
+ bool is_empty() { return from_ == kNone; }
+ int from() { return from_; }
+ int to() { return to_; }
+ static Interval Empty() { return Interval(); }
+ static const int kNone = -1;
+ private:
+ int from_;
+ int to_;
};
@@ -682,21 +718,26 @@
INCREMENT_REGISTER,
STORE_POSITION,
BEGIN_SUBMATCH,
- POSITIVE_SUBMATCH_SUCCESS
+ POSITIVE_SUBMATCH_SUCCESS,
+ EMPTY_MATCH_CHECK,
+ CLEAR_CAPTURES
};
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* BeginSubmatch(
- int stack_pointer_reg,
- int position_reg,
- RegExpNode* on_success);
- static ActionNode* PositiveSubmatchSuccess(
- int stack_pointer_reg,
- int restore_reg,
- 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,
+ 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, GenerationVariant* variant);
+ virtual bool Emit(RegExpCompiler* compiler, Trace* trace);
virtual int EatsAtLeast(int recursion_depth);
virtual void GetQuickCheckDetails(QuickCheckDetails* details,
RegExpCompiler* compiler,
@@ -725,6 +766,15 @@
int stack_pointer_register;
int current_position_register;
} u_submatch;
+ struct {
+ int start_register;
+ int repetition_register;
+ int repetition_limit;
+ } u_empty_match_check;
+ struct {
+ int range_from;
+ int range_to;
+ } u_clear_captures;
} data_;
ActionNode(Type type, RegExpNode* on_success)
: SeqRegExpNode(on_success),
@@ -748,7 +798,7 @@
}
virtual void Accept(NodeVisitor* visitor);
virtual RegExpNode* PropagateForward(NodeInfo* info);
- virtual bool Emit(RegExpCompiler* compiler, GenerationVariant* variant);
+ virtual bool Emit(RegExpCompiler* compiler, Trace* trace);
virtual int EatsAtLeast(int recursion_depth);
virtual void GetQuickCheckDetails(QuickCheckDetails* details,
RegExpCompiler* compiler,
@@ -773,7 +823,7 @@
void TextEmitPass(RegExpCompiler* compiler,
TextEmitPassType pass,
bool preloaded,
- GenerationVariant* variant,
+ Trace* trace,
bool first_element_checked,
int* checked_up_to);
int Length();
@@ -792,7 +842,7 @@
virtual void Accept(NodeVisitor* visitor);
int start_register() { return start_reg_; }
int end_register() { return end_reg_; }
- virtual bool Emit(RegExpCompiler* compiler, GenerationVariant* variant);
+ virtual bool Emit(RegExpCompiler* compiler, Trace* trace);
virtual int EatsAtLeast(int recursion_depth) { return 0; }
virtual void GetQuickCheckDetails(QuickCheckDetails* details,
RegExpCompiler* compiler,
@@ -813,7 +863,7 @@
enum Action { ACCEPT, BACKTRACK, NEGATIVE_SUBMATCH_SUCCESS };
explicit EndNode(Action action) : action_(action) { }
virtual void Accept(NodeVisitor* visitor);
- virtual bool Emit(RegExpCompiler* compiler, GenerationVariant* variant);
+ virtual bool Emit(RegExpCompiler* compiler, Trace* trace);
virtual int EatsAtLeast(int recursion_depth) { return 0; }
virtual void GetQuickCheckDetails(QuickCheckDetails* details,
RegExpCompiler* compiler,
@@ -825,7 +875,7 @@
virtual EndNode* Clone() { return new EndNode(*this); }
protected:
- void EmitInfoChecks(RegExpMacroAssembler* macro, GenerationVariant* variant);
+ void EmitInfoChecks(RegExpMacroAssembler* macro, Trace* trace);
private:
Action action_;
@@ -838,7 +888,7 @@
: EndNode(NEGATIVE_SUBMATCH_SUCCESS),
stack_pointer_register_(stack_pointer_reg),
current_position_register_(position_reg) { }
- virtual bool Emit(RegExpCompiler* compiler, GenerationVariant* variant);
+ virtual bool Emit(RegExpCompiler* compiler, Trace* trace);
private:
int stack_pointer_register_;
@@ -891,7 +941,7 @@
void AddAlternative(GuardedAlternative node) { alternatives()->Add(node); }
ZoneList<GuardedAlternative>* alternatives() { return alternatives_; }
DispatchTable* GetTable(bool ignore_case);
- virtual bool Emit(RegExpCompiler* compiler, GenerationVariant* variant);
+ virtual bool Emit(RegExpCompiler* compiler, Trace* trace);
virtual int EatsAtLeast(int recursion_depth);
int EatsAtLeastHelper(int recursion_depth, RegExpNode* ignore_this_node);
virtual void GetQuickCheckDetails(QuickCheckDetails* details,
@@ -912,10 +962,10 @@
friend class Analysis;
void GenerateGuard(RegExpMacroAssembler* macro_assembler,
Guard *guard,
- GenerationVariant* variant);
+ Trace* trace);
int CalculatePreloadCharacters(RegExpCompiler* compiler);
bool EmitOutOfLineContinuation(RegExpCompiler* compiler,
- GenerationVariant* variant,
+ Trace* trace,
GuardedAlternative alternative,
AlternativeGeneration* alt_gen,
int preload_characters,
@@ -934,7 +984,7 @@
body_can_be_zero_length_(body_can_be_zero_length) { }
void AddLoopAlternative(GuardedAlternative alt);
void AddContinueAlternative(GuardedAlternative alt);
- virtual bool Emit(RegExpCompiler* compiler, GenerationVariant* variant);
+ virtual bool Emit(RegExpCompiler* compiler, Trace* trace);
virtual int EatsAtLeast(int recursion_depth); // Returns 0.
virtual void GetQuickCheckDetails(QuickCheckDetails* details,
RegExpCompiler* compiler,
@@ -961,28 +1011,37 @@
// There are many ways to generate code for a node. This class encapsulates
// the current way we should be generating. In other words it encapsulates
-// the current state of the code generator.
-class GenerationVariant {
+// the current state of the code generator. The effect of this is that we
+// generate code for paths that the matcher can take through the regular
+// expression. A given node in the regexp can be code-generated several times
+// as it can be part of several traces. For example for the regexp:
+// /foo(bar|ip)baz/ the code to match baz will be generated twice, once as part
+// of the foo-bar-baz trace and once as part of the foo-ip-baz trace. The code
+// to match foo is generated only once (the traces have a common prefix). The
+// code to store the capture is deferred and generated (twice) after the places
+// where baz has been matched.
+class Trace {
public:
class DeferredAction {
public:
DeferredAction(ActionNode::Type type, int reg)
: type_(type), reg_(reg), next_(NULL) { }
DeferredAction* next() { return next_; }
+ bool Mentions(int reg);
int reg() { return reg_; }
ActionNode::Type type() { return type_; }
private:
ActionNode::Type type_;
int reg_;
DeferredAction* next_;
- friend class GenerationVariant;
+ friend class Trace;
};
class DeferredCapture: public DeferredAction {
public:
- DeferredCapture(int reg, GenerationVariant* variant)
+ DeferredCapture(int reg, Trace* trace)
: DeferredAction(ActionNode::STORE_POSITION, reg),
- cp_offset_(variant->cp_offset()) { }
+ cp_offset_(trace->cp_offset()) { }
int cp_offset() { return cp_offset_; }
private:
int cp_offset_;
@@ -999,13 +1058,23 @@
int value_;
};
+ class DeferredClearCaptures : public DeferredAction {
+ public:
+ explicit DeferredClearCaptures(Interval range)
+ : DeferredAction(ActionNode::CLEAR_CAPTURES, -1),
+ range_(range) { }
+ Interval range() { return range_; }
+ private:
+ Interval range_;
+ };
+
class DeferredIncrementRegister: public DeferredAction {
public:
explicit DeferredIncrementRegister(int reg)
: DeferredAction(ActionNode::INCREMENT_REGISTER, reg) { }
};
- GenerationVariant()
+ Trace()
: cp_offset_(0),
actions_(NULL),
backtrack_(NULL),
@@ -1013,9 +1082,23 @@
loop_label_(NULL),
characters_preloaded_(0),
bound_checked_up_to_(0) { }
+ // End the trace. This involves flushing the deferred actions in the trace
+ // 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);
int cp_offset() { return cp_offset_; }
DeferredAction* actions() { return actions_; }
+ // A trivial trace is one that has no deferred actions or other state that
+ // affects the assumptions used when generating code. There is no recorded
+ // backtrack location in a trivial trace, so with a trivial trace we will
+ // generate code that, on a failure to match, gets the backtrack location
+ // from the backtrack stack rather than using a direct jump instruction. We
+ // always start code generation with a trivial trace and non-trivial traces
+ // are created as we emit code for nodes or add to the list of deferred
+ // actions in the trace. The location of the code generated for a node using
+ // a trivial trace is recorded in a label in the node so that gotos can be
+ // generated to that code.
bool is_trivial() {
return backtrack_ == NULL &&
actions_ == NULL &&
@@ -1031,9 +1114,12 @@
int bound_checked_up_to() { return bound_checked_up_to_; }
QuickCheckDetails* quick_check_performed() { return &quick_check_performed_; }
bool mentions_reg(int reg);
- // These set methods and AdvanceVariant should be used only on new
- // GenerationVariants - the intention is that GenerationVariants are
- // immutable after creation.
+ // Returns true if a deferred position store exists to the specified
+ // register and stores the offset in the out-parameter. Otherwise
+ // returns false.
+ bool GetStoredPosition(int reg, int* cp_offset);
+ // These set methods and AdvanceCurrentPositionInTrace should be used only on
+ // new traces - the intention is that traces are immutable after creation.
void add_action(DeferredAction* new_action) {
ASSERT(new_action->next_ == NULL);
new_action->next_ = actions_;
@@ -1049,7 +1135,7 @@
}
void clear_quick_check_performed() {
}
- void AdvanceVariant(int by, bool ascii);
+ void AdvanceCurrentPositionInTrace(int by, bool ascii);
private:
int FindAffectedRegisters(OutSet* affected_registers);
void PerformDeferredActions(RegExpMacroAssembler* macro,
diff --git a/src/macro-assembler-arm.cc b/src/macro-assembler-arm.cc
index 5cbeae9..dc6d40f 100644
--- a/src/macro-assembler-arm.cc
+++ b/src/macro-assembler-arm.cc
@@ -913,7 +913,7 @@
// We want to pass the msg string like a smi to avoid GC
// problems, however msg is not guaranteed to be aligned
// properly. Instead, we pass an aligned pointer that is
- // a proper v8 smi, but also pass the aligment difference
+ // a proper v8 smi, but also pass the alignment difference
// from the real pointer as a smi.
intptr_t p1 = reinterpret_cast<intptr_t>(msg);
intptr_t p0 = (p1 & ~kSmiTagMask) + kSmiTag;
diff --git a/src/macro-assembler-ia32.cc b/src/macro-assembler-ia32.cc
index fd7e4d9..80e4d3e 100644
--- a/src/macro-assembler-ia32.cc
+++ b/src/macro-assembler-ia32.cc
@@ -975,7 +975,7 @@
// We want to pass the msg string like a smi to avoid GC
// problems, however msg is not guaranteed to be aligned
// properly. Instead, we pass an aligned pointer that is
- // a proper v8 smi, but also pass the aligment difference
+ // a proper v8 smi, but also pass the alignment difference
// from the real pointer as a smi.
intptr_t p1 = reinterpret_cast<intptr_t>(msg);
intptr_t p0 = (p1 & ~kSmiTagMask) + kSmiTag;
@@ -996,7 +996,7 @@
CodePatcher::CodePatcher(byte* address, int size)
: address_(address), size_(size), masm_(address, size + Assembler::kGap) {
- // Create a new macro assembler pointing to the assress of the code to patch.
+ // Create a new macro assembler pointing to the address of the code to patch.
// The size is adjusted with kGap on order for the assembler to generate size
// bytes of instructions without failing with buffer size constraints.
ASSERT(masm_.reloc_info_writer.pos() == address_ + size_ + Assembler::kGap);
diff --git a/src/macros.py b/src/macros.py
index 5a7606a..b036c63 100644
--- a/src/macros.py
+++ b/src/macros.py
@@ -61,6 +61,9 @@
const msPerHour = 3600000;
const msPerDay = 86400000;
+# For apinatives.js
+const kUninitialized = -1;
+
# Note: kDayZeroInJulianDay = ToJulianDay(1970, 0, 1).
const kInvalidDate = 'Invalid Date';
const kDayZeroInJulianDay = 2440588;
diff --git a/src/mirror-delay.js b/src/mirror-delay.js
index e7ff910..f087f68 100644
--- a/src/mirror-delay.js
+++ b/src/mirror-delay.js
@@ -33,6 +33,18 @@
Date;
+var next_handle_ = 0;
+var mirror_cache_ = [];
+
+/**
+ * Clear the mirror handle cache.
+ */
+function ClearMirrorCache() {
+ next_handle_ = 0;
+ mirror_cache_ = [];
+}
+
+
/**
* Returns the mirror for a specified value or object.
*
@@ -40,17 +52,50 @@
* @returns {Mirror} the mirror reflects the passed value or object
*/
function MakeMirror(value) {
- if (IS_UNDEFINED(value)) return new UndefinedMirror();
- if (IS_NULL(value)) return new NullMirror();
- if (IS_BOOLEAN(value)) return new BooleanMirror(value);
- if (IS_NUMBER(value)) return new NumberMirror(value);
- if (IS_STRING(value)) return new StringMirror(value);
- if (IS_ARRAY(value)) return new ArrayMirror(value);
- if (IS_DATE(value)) return new DateMirror(value);
- if (IS_FUNCTION(value)) return new FunctionMirror(value);
- if (IS_REGEXP(value)) return new RegExpMirror(value);
- if (IS_ERROR(value)) return new ErrorMirror(value);
- return new ObjectMirror(value);
+ var mirror;
+ for (id in mirror_cache_) {
+ mirror = mirror_cache_[id];
+ if (mirror.value() === value) {
+ return mirror;
+ }
+ }
+
+ if (IS_UNDEFINED(value)) {
+ mirror = new UndefinedMirror();
+ } else if (IS_NULL(value)) {
+ mirror = new NullMirror();
+ } else if (IS_BOOLEAN(value)) {
+ mirror = new BooleanMirror(value);
+ } else if (IS_NUMBER(value)) {
+ mirror = new NumberMirror(value);
+ } else if (IS_STRING(value)) {
+ mirror = new StringMirror(value);
+ } else if (IS_ARRAY(value)) {
+ mirror = new ArrayMirror(value);
+ } else if (IS_DATE(value)) {
+ mirror = new DateMirror(value);
+ } else if (IS_FUNCTION(value)) {
+ mirror = new FunctionMirror(value);
+ } else if (IS_REGEXP(value)) {
+ mirror = new RegExpMirror(value);
+ } else if (IS_ERROR(value)) {
+ mirror = new ErrorMirror(value);
+ } else {
+ mirror = new ObjectMirror(value);
+ }
+
+ mirror_cache_[mirror.handle()] = mirror;
+ return mirror;
+}
+
+
+/**
+ * Returns the mirror for the undefined value.
+ *
+ * @returns {Mirror} the mirror reflects the undefined value
+ */
+function GetUndefinedMirror() {
+ return MakeMirror(void 0);
}
@@ -87,7 +132,6 @@
const REGEXP_TYPE = 'regexp';
const ERROR_TYPE = 'error';
const PROPERTY_TYPE = 'property';
-const ACCESSOR_TYPE = 'accessor';
const FRAME_TYPE = 'frame';
const SCRIPT_TYPE = 'script';
@@ -135,8 +179,6 @@
// - RegExpMirror
// - ErrorMirror
// - PropertyMirror
-// - InterceptorPropertyMirror
-// - AccessorMirror
// - FrameMirror
// - ScriptMirror
@@ -157,6 +199,15 @@
/**
+ * Check whether the mirror reflects a value.
+ * @returns {boolean} True if the mirror reflects a value.
+ */
+Mirror.prototype.isValue = function() {
+ return this instanceof ValueMirror;
+}
+
+
+/**
* Check whether the mirror reflects the undefined value.
* @returns {boolean} True if the mirror reflects the undefined value.
*/
@@ -274,25 +325,6 @@
/**
- * Check whether the mirror reflects a property from an interceptor.
- * @returns {boolean} True if the mirror reflects a property from an
- * interceptor
- */
-Mirror.prototype.isInterceptorProperty = function() {
- return this instanceof InterceptorPropertyMirror;
-}
-
-
-/**
- * Check whether the mirror reflects an accessor.
- * @returns {boolean} True if the mirror reflects an accessor
- */
-Mirror.prototype.isAccessor = function() {
- return this instanceof AccessorMirror;
-}
-
-
-/**
* Check whether the mirror reflects a stack frame.
* @returns {boolean} True if the mirror reflects a stack frame
*/
@@ -310,18 +342,6 @@
}
-/**
- * Serialize object in JSON format. The actual serialization is handled by the
- * JSONProtocolSerializer.
- * @param {boolean} details Indicate level of details to include
- * @return {string} JSON serialization
- */
-Mirror.prototype.toJSONProtocol = function(details) {
- var serializer = new JSONProtocolSerializer(details)
- return serializer.serialize(this)
-}
-
-
Mirror.prototype.toText = function() {
// Simpel to text which is used when on specialization in subclass.
return "#<" + builtins.GetInstanceName(this.constructor.name) + ">";
@@ -337,11 +357,17 @@
*/
function ValueMirror(type, value) {
Mirror.call(this, type);
+ this.handle_ = next_handle_++;
this.value_ = value;
}
inherits(ValueMirror, Mirror);
+Mirror.prototype.handle = function() {
+ return this.handle_;
+};
+
+
/**
* Check whether this is a primitive value.
* @return {boolean} True if the mirror reflects a primitive value
@@ -356,7 +382,7 @@
};
- /**
+/**
* Get the actual value reflected by this mirror.
* @return {value} The value reflected by this mirror
*/
@@ -519,13 +545,39 @@
var propertyNames;
var elementNames;
var total = 0;
+
+ // Find all the named properties.
if (kind & PropertyKind.Named) {
+ // Get the local property names.
propertyNames = %DebugLocalPropertyNames(this.value_);
total += propertyNames.length;
+
+ // Get names for named interceptor properties if any.
+ if (this.hasNamedInterceptor() && (kind & PropertyKind.Named)) {
+ var namedInterceptorNames =
+ %DebugNamedInterceptorPropertyNames(this.value_);
+ if (namedInterceptorNames) {
+ propertyNames = propertyNames.concat(namedInterceptorNames);
+ total += namedInterceptorNames.length;
+ }
+ }
}
+
+ // Find all the indexed properties.
if (kind & PropertyKind.Indexed) {
- elementNames = %DebugLocalElementNames(this.value_)
+ // Get the local element names.
+ elementNames = %DebugLocalElementNames(this.value_);
total += elementNames.length;
+
+ // Get names for indexed interceptor properties.
+ if (this.hasIndexedInterceptor() && (kind & PropertyKind.Indexed)) {
+ var indexedInterceptorNames =
+ %DebugIndexedInterceptorElementNames(this.value_);
+ if (indexedInterceptorNames) {
+ elementNames = elementNames.concat(indexedInterceptorNames);
+ total += indexedInterceptorNames.length;
+ }
+ }
}
limit = Math.min(limit || total, total);
@@ -569,99 +621,14 @@
};
-/**
- * Return the interceptor property names for this object.
- * @param {number} kind Indicate whether named, indexed or both kinds of
- * interceptor properties are requested
- * @param {number} limit Limit the number of names returend to the specified
- value
- * @return {Array} interceptor property names for this object
- */
-ObjectMirror.prototype.interceptorPropertyNames = function(kind, limit) {
- // Find kind.
- kind = kind || PropertyKind.Named | PropertyKind.Indexed;
- var namedInterceptorNames;
- var indexedInterceptorNames;
-
- // Get names for named interceptor properties.
- if (this.hasNamedInterceptor() && kind & PropertyKind.Named) {
- namedInterceptorNames = %DebugNamedInterceptorPropertyNames(this.value_);
- }
-
- // Get names for indexed interceptor properties.
- if (this.hasIndexedInterceptor() && kind & PropertyKind.Indexed) {
- indexedInterceptorNames = %DebugIndexedInterceptorElementNames(this.value_);
- }
-
- // Return either retult or both concattenated.
- if (namedInterceptorNames && indexedInterceptorNames) {
- return namedInterceptorNames.concat(indexedInterceptorNames);
- } else if (namedInterceptorNames) {
- return namedInterceptorNames;
- } else if (indexedInterceptorNames) {
- return indexedInterceptorNames;
- } else {
- return new Array(0);
- }
-};
-
-
-/**
- * Return interceptor properties this object.
- * @param {number} opt_kind Indicate whether named, indexed or both kinds of
- * interceptor properties are requested
- * @param {Array} opt_names Limit the number of properties returned to the
- specified value
- * @return {Array} properties this object as an array of PropertyMirror objects
- */
-ObjectMirror.prototype.interceptorProperties = function(opt_kind, opt_names) {
- // Find kind.
- var kind = opt_kind || PropertyKind.Named | PropertyKind.Indexed;
- var namedInterceptorProperties;
- var indexedInterceptorProperties;
-
- // Get values for named interceptor properties.
- if (kind & PropertyKind.Named) {
- var names = opt_names || this.interceptorPropertyNames(PropertyKind.Named);
- namedInterceptorProperties = new Array(names.length);
- for (i = 0; i < names.length; i++) {
- var value = %DebugNamedInterceptorPropertyValue(this.value_, names[i]);
- namedInterceptorProperties[i] = new InterceptorPropertyMirror(this, names[i], value);
- }
- }
-
- // Get values for indexed interceptor properties.
- if (kind & PropertyKind.Indexed) {
- var names = opt_names || this.interceptorPropertyNames(PropertyKind.Indexed);
- indexedInterceptorProperties = new Array(names.length);
- for (i = 0; i < names.length; i++) {
- // Don't try to get the value if the name is not a number.
- if (IS_NUMBER(names[i])) {
- var value = %DebugIndexedInterceptorElementValue(this.value_, names[i]);
- indexedInterceptorProperties[i] = new InterceptorPropertyMirror(this, names[i], value);
- }
- }
- }
-
- // Return either result or both concattenated.
- if (namedInterceptorProperties && indexedInterceptorProperties) {
- return namedInterceptorProperties.concat(indexedInterceptorProperties);
- } else if (namedInterceptorProperties) {
- return namedInterceptorProperties;
- } else {
- return indexedInterceptorProperties;
- }
-};
-
-
ObjectMirror.prototype.property = function(name) {
var details = %DebugGetPropertyDetails(this.value_, %ToString(name));
if (details) {
- return new PropertyMirror(this, name, details[0], details[1]);
+ return new PropertyMirror(this, name, details);
}
// Nothing found.
- return new UndefinedMirror();
+ return GetUndefinedMirror();
};
@@ -688,7 +655,7 @@
}
// Nothing found.
- return new UndefinedMirror();
+ return GetUndefinedMirror();
};
@@ -767,8 +734,6 @@
// Return source if function is resolved. Otherwise just fall through to
// return undefined.
if (this.resolved()) {
- // This builtins function is context independant (only uses runtime
- // calls and typeof.
return builtins.FunctionSourceString(this.value_);
}
};
@@ -843,17 +808,17 @@
UnresolvedFunctionMirror.prototype.constructorFunction = function() {
- return new UndefinedMirror();
+ return GetUndefinedMirror();
};
UnresolvedFunctionMirror.prototype.prototypeObject = function() {
- return new UndefinedMirror();
+ return GetUndefinedMirror();
};
UnresolvedFunctionMirror.prototype.protoObject = function() {
- return new UndefinedMirror();
+ return GetUndefinedMirror();
};
@@ -893,9 +858,9 @@
var details = %DebugGetPropertyDetails(this.value_, %ToString(i));
var value;
if (details) {
- value = new PropertyMirror(this, i, details[0], details[1]);
+ value = new PropertyMirror(this, i, details);
} else {
- value = new UndefinedMirror();
+ value = GetUndefinedMirror();
}
values[i - from_index] = value;
}
@@ -1011,16 +976,21 @@
* Base mirror object for properties.
* @param {ObjectMirror} mirror The mirror object having this property
* @param {string} name The name of the property
- * @param {Object} value The value of the property
+ * @param {Array} details Details about the property
* @constructor
* @extends Mirror
*/
-function PropertyMirror(mirror, name, value, details) {
+function PropertyMirror(mirror, name, details) {
Mirror.call(this, PROPERTY_TYPE);
this.mirror_ = mirror;
this.name_ = name;
- this.value_ = value;
- this.details_ = details;
+ this.value_ = details[0];
+ this.details_ = details[1];
+ if (details.length > 2) {
+ this.exception_ = details[2]
+ this.getter_ = details[3];
+ this.setter_ = details[4];
+ }
}
inherits(PropertyMirror, Mirror);
@@ -1056,14 +1026,16 @@
PropertyMirror.prototype.value = function() {
- if (this.propertyType() == PropertyType.Callbacks) {
- // TODO(1242933): AccessorMirror should have getter/setter values.
- return new AccessorMirror();
- } else if (this.type() == PropertyType.Interceptor) {
- return new UndefinedMirror();
- } else {
- return MakeMirror(this.value_);
- }
+ return MakeMirror(this.value_);
+}
+
+
+/**
+ * Returns whether this property value is an exception.
+ * @return {booolean} True if this property value is an exception
+ */
+PropertyMirror.prototype.isException = function() {
+ return this.exception_ ? true : false;
}
@@ -1083,62 +1055,61 @@
/**
- * Mirror object for interceptor named properties.
- * @param {ObjectMirror} mirror The mirror object having this property
- * @param {String} name The name of the property
- * @param {value} value The value of the property
- * @constructor
- * @extends PropertyMirror
+ * Returns whether this property has a getter defined through __defineGetter__.
+ * @return {booolean} True if this property has a getter
*/
-function InterceptorPropertyMirror(mirror, name, value) {
- PropertyMirror.call(this, mirror, name, value, PropertyType.Interceptor);
-}
-inherits(InterceptorPropertyMirror, PropertyMirror);
-
-
-/**
- * Mirror object for property accessors.
- * @param {Function} getter The getter function for this accessor
- * @param {Function} setter The setter function for this accessor
- * @constructor
- * @extends Mirror
- */
-function AccessorMirror(getter, setter) {
- Mirror.call(this, ACCESSOR_TYPE);
- this.getter_ = getter;
- this.setter_ = setter;
-}
-inherits(AccessorMirror, Mirror);
-
-
-/**
- * Returns whether this accessor is native or not. A native accessor is either
- * a VM buildin or provided through the API. A non native accessor is defined
- * in JavaScript using the __defineGetter__ and/or __defineGetter__ functions.
- * @return {boolean} True is the accessor is native
- */
-AccessorMirror.prototype.isNative = function() {
- return IS_UNDEFINED(this.getter_) && IS_UNDEFINED(this.setter_);
+PropertyMirror.prototype.hasGetter = function() {
+ return this.getter_ ? true : false;
}
/**
- * Returns a mirror for the function of a non native getter.
- * @return {FunctionMirror} Function mirror for the getter set using
- * __defineGetter__.
+ * Returns whether this property has a setter defined through __defineSetter__.
+ * @return {booolean} True if this property has a setter
*/
-AccessorMirror.prototype.getter = function(details) {
- return MakeMirror(this.getter_);
+PropertyMirror.prototype.hasSetter = function() {
+ return this.setter_ ? true : false;
}
/**
- * Returns a mirror for the function of a non native setter.
- * @return {FunctionMirror} Function mirror for the getter set using
- * __defineSetter__.
+ * Returns the getter for this property defined through __defineGetter__.
+ * @return {Mirror} FunctionMirror reflecting the getter function or
+ * UndefinedMirror if there is no getter for this property
*/
-AccessorMirror.prototype.setter = function(details) {
- return MakeMirror(this.setter_);
+PropertyMirror.prototype.getter = function() {
+ if (this.hasGetter()) {
+ return MakeMirror(this.getter_);
+ } else {
+ return new UndefinedMirror();
+ }
+}
+
+
+/**
+ * Returns the setter for this property defined through __defineSetter__.
+ * @return {Mirror} FunctionMirror reflecting the setter function or
+ * UndefinedMirror if there is no setter for this property
+ */
+PropertyMirror.prototype.setter = function() {
+ if (this.hasSetter()) {
+ return MakeMirror(this.setter_);
+ } else {
+ return new UndefinedMirror();
+ }
+}
+
+
+/**
+ * Returns whether this property is natively implemented by the host or a set
+ * through JavaScript code.
+ * @return {boolean} True if the property is
+ * UndefinedMirror if there is no setter for this property
+ */
+PropertyMirror.prototype.isNative = function() {
+ return (this.propertyType() == PropertyType.Interceptor) ||
+ ((this.propertyType() == PropertyType.Callbacks) &&
+ !this.hasGetter() && !this.hasSetter());
}
@@ -1299,7 +1270,7 @@
// value returned from the VM might be a string if the function for the
// frame is unresolved.
if (IS_FUNCTION(f)) {
- return new FunctionMirror(f);
+ return MakeMirror(f);
} else {
return new UnresolvedFunctionMirror(f);
}
@@ -1419,7 +1390,7 @@
}
// Try to find the function as a property in the receiver. Include the
// prototype chain in the lookup.
- var property = new UndefinedMirror();
+ var property = GetUndefinedMirror();
if (!receiver.isUndefined()) {
for (var r = receiver; !r.isNull() && property.isUndefined(); r = r.protoObject()) {
property = r.lookupProperty(func);
@@ -1600,16 +1571,107 @@
}
-function JSONProtocolSerializer(details) {
- this.details_ = details;
+/**
+ * Returns a mirror serializer
+ *
+ * @param {boolean} details Set to true to include details
+ * @returns {MirrorSerializer} mirror serializer
+ */
+function MakeMirrorSerializer(details) {
+ return new JSONProtocolSerializer(details);
}
-JSONProtocolSerializer.prototype.serialize = function(mirror) {
- // Collect the JSON property/value pairs in a array.
+/**
+ * Object for serializing a mirror objects and its direct references.
+ * @param {boolean} details Indicates whether to include details for the mirror
+ * serialized
+ * @constructor
+ */
+function JSONProtocolSerializer(details) {
+ this.details_ = details;
+ this.mirrors_ = [ ];
+}
+
+
+/**
+ * Returns a serialization of an object reference. The referenced object are
+ * added to the serialization state.
+ *
+ * @param {Mirror} mirror The mirror to serialize
+ * @returns {String} JSON serialization
+ */
+JSONProtocolSerializer.prototype.serializeReference = function(mirror) {
+ return this.serialize_(mirror, true, true);
+}
+
+
+/**
+ * Returns a serialization of an object value. The referenced objects are
+ * added to the serialization state.
+ *
+ * @param {Mirror} mirror The mirror to serialize
+ * @returns {String} JSON serialization
+ */
+JSONProtocolSerializer.prototype.serializeValue = function(mirror) {
+ var json = this.serialize_(mirror, false, true);
+ return json;
+}
+
+
+/**
+ * Returns a serialization of all the objects referenced.
+ *
+ * @param {Mirror} mirror The mirror to serialize
+ * @returns {String} JSON serialization
+ */
+JSONProtocolSerializer.prototype.serializeReferencedObjects = function() {
+ // Collect the JSON serialization of the referenced objects in an array.
+ var content = new Array();
+
+ // Get the number of referenced objects.
+ var count = this.mirrors_.length;
+
+ for (var i = 0; i < count; i++) {
+ content.push(this.serialize_(this.mirrors_[i], false, false));
+ }
+
+ var json = ArrayToJSONArray_(content);
+ return json;
+}
+
+
+JSONProtocolSerializer.prototype.add_ = function(mirror) {
+ // If this mirror is already in the list just return.
+ for (var i = 0; i < this.mirrors_.length; i++) {
+ if (this.mirrors_[i] === mirror) {
+ return;
+ }
+ }
+
+ // Add the mirror to the list of mirrors to be serialized.
+ this.mirrors_.push(mirror);
+}
+
+
+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()) {
+ this.add_(mirror);
+ return '{ref:' + mirror.handle() + '}';
+ }
+
+ // Collect the JSON property/value pairs in an array.
var content = new Array();
- // Always add the type
+ // Add the handle for value mirrors.
+ if (mirror.isValue()) {
+ content.push(MakeJSONPair_('handle', NumberToJSON_(mirror.handle())));
+ }
+
+ // Always add the type.
content.push(MakeJSONPair_('type', StringToJSON_(mirror.type())));
switch (mirror.type()) {
@@ -1647,35 +1709,11 @@
case ERROR_TYPE:
case REGEXP_TYPE:
// Add object representation.
- this.serializeObject_(mirror, content);
+ this.serializeObject_(mirror, content, details);
break;
case PROPERTY_TYPE:
- // Properties are represented by name, value, attributes and type.
- content.push(MakeJSONPair_('name',
- StringToJSON_(mirror.name())));
- content.push(MakeJSONPair_('value',
- mirror.value().toJSONProtocol(this.details_)));
- if (mirror.attributes() != PropertyAttribute.None) {
- content.push(MakeJSONPair_('attributes',
- NumberToJSON_(mirror.attributes())));
- }
- if (mirror.propertyType() != PropertyType.Normal) {
- content.push(MakeJSONPair_('propertyType',
- NumberToJSON_(mirror.propertyType())));
- }
- break;
-
- case ACCESSOR_TYPE:
- // An accessor can either be native or defined through JavaScript.
- if (mirror.isNative()) {
- content.push(MakeJSONPair_('native', BooleanToJSON_(true)));
- } else {
- content.push(MakeJSONPair_('getter',
- mirror.getter().toJSONProtocol(false)));
- content.push(MakeJSONPair_('setter',
- mirror.setter().toJSONProtocol(false)));
- }
+ throw new Error('PropertyMirror cannot be serialized independeltly')
break;
case FRAME_TYPE:
@@ -1697,7 +1735,6 @@
content.push(MakeJSONPair_('scriptType',
NumberToJSON_(mirror.scriptType())));
break;
-
}
// Always add the text representation.
@@ -1708,94 +1745,121 @@
}
-JSONProtocolSerializer.prototype.serializeObject_ = function(mirror, content) {
+/**
+ * Serialize object information to the following JSON format.
+ *
+ * {"className":"<class name>",
+ * "constructorFunction":{"ref":<number>},
+ * "protoObject":{"ref":<number>},
+ * "prototypeObject":{"ref":<number>},
+ * "namedInterceptor":<boolean>,
+ * "indexedInterceptor":<boolean>,
+ * "properties":[<properties>]}
+ */
+JSONProtocolSerializer.prototype.serializeObject_ = function(mirror, content,
+ details) {
+ // Add general object properties.
content.push(MakeJSONPair_('className',
StringToJSON_(mirror.className())));
+ content.push(MakeJSONPair_('constructorFunction',
+ this.serializeReference(mirror.constructorFunction())));
+ content.push(MakeJSONPair_('protoObject',
+ this.serializeReference(mirror.protoObject())));
+ content.push(MakeJSONPair_('prototypeObject',
+ this.serializeReference(mirror.prototypeObject())));
- if (this.details_) {
- content.push(MakeJSONPair_('constructorFunction',
- mirror.constructorFunction().toJSONProtocol(false)));
- content.push(MakeJSONPair_('protoObject',
- mirror.protoObject().toJSONProtocol(false)));
- content.push(MakeJSONPair_('prototypeObject',
- mirror.prototypeObject().toJSONProtocol(false)));
-
- // Add properties. For arrays don't include indexed proeprties.
- var kind = PropertyKind.Named;
- if (!mirror.isArray()) {
- kind |= PropertyKind.Indexed
- }
- var propertyNames = mirror.propertyNames(kind);
- var x = new Array(propertyNames.length);
- for (var i = 0; i < propertyNames.length; i++) {
- x[i] = mirror.property(propertyNames[i]).toJSONProtocol(false);
- }
- content.push(MakeJSONPair_('properties', ArrayToJSONArray_(x)));
-
- // Add interceptor properties.
- propertyNames = mirror.interceptorPropertyNames();
- var x = new Array(propertyNames.length);
- for (var i = 0; i < propertyNames.length; i++) {
- x[i] = properties[i].toJSONProtocol(details);
- }
- content.push(MakeJSONPair_('interceptorProperties', ArrayToJSONArray_(x)));
-
- // For arrays the indexed properties are added separately and the length is
- // added as well.
- if (mirror.isArray()) {
- var propertyNames = mirror.propertyNames(PropertyKind.Indexed);
- var x = new Array(propertyNames.length);
- for (var i = 0; i < propertyNames.length; i++) {
- x[i] = mirror.property(propertyNames[i]).toJSONProtocol(false);
- }
- content.push(MakeJSONPair_('indexedProperties', ArrayToJSONArray_(x)));
-
- // Add the array length.
- content.push(MakeJSONPair_('length', NumberToJSON_(mirror.length())));
- }
- }
-
+ // Add flags to indicate whether there are interceptors.
if (mirror.hasNamedInterceptor()) {
content.push(MakeJSONPair_('namedInterceptor', BooleanToJSON_(true)));
}
-
if (mirror.hasIndexedInterceptor()) {
content.push(MakeJSONPair_('indexedInterceptor', BooleanToJSON_(true)));
}
+ // Add function specific properties.
if (mirror.isFunction()) {
// Add function specific properties.
content.push(MakeJSONPair_('name', StringToJSON_(mirror.name())));
content.push(MakeJSONPair_('resolved', BooleanToJSON_(mirror.resolved())));
- if (this.details_ && mirror.resolved()) {
+ if (mirror.resolved()) {
content.push(MakeJSONPair_('source', StringToJSON_(mirror.source())));
}
if (mirror.script()) {
- content.push(MakeJSONPair_('script', mirror.script().toJSONProtocol()));
+ content.push(MakeJSONPair_('script', this.serializeValue(mirror.script())));
}
- } else if (mirror.isDate()) {
+ }
+
+ // Add date specific properties.
+ if (mirror.isDate()) {
// Add date specific properties.
content.push(MakeJSONPair_('value', DateToJSON_(mirror.value())));
- } else if (mirror.isRegExp()) {
- // Add regexp specific properties.
- content.push(MakeJSONPair_('source', StringToJSON_(mirror.source())));
- content.push(MakeJSONPair_('global', BooleanToJSON_(mirror.global())));
- content.push(MakeJSONPair_('ignoreCase',
- BooleanToJSON_(mirror.ignoreCase())));
- content.push(MakeJSONPair_('multiline',
- BooleanToJSON_(mirror.multiline())));
- } else if (mirror.isError()) {
- // Add error specific properties.
- content.push(MakeJSONPair_('message', StringToJSON_(mirror.message())));
}
+
+ // Add actual properties - named properties followed by indexed properties.
+ var propertyNames = mirror.propertyNames(PropertyKind.Named);
+ var propertyIndexes = mirror.propertyNames(PropertyKind.Indexed);
+ var p = new Array(propertyNames.length + propertyIndexes.length);
+ for (var i = 0; i < propertyNames.length; i++) {
+ var property_mirror = mirror.property(propertyNames[i]);
+ p[i] = this.serializeProperty_(property_mirror);
+ if (details) {
+ this.add_(property_mirror.value());
+ }
+ }
+ for (var i = 0; i < propertyIndexes.length; i++) {
+ var property_mirror = mirror.property(propertyIndexes[i]);
+ p[propertyNames.length + i] = this.serializeProperty_(property_mirror);
+ if (details) {
+ this.add_(property_mirror.value());
+ }
+ }
+ content.push(MakeJSONPair_('properties', ArrayToJSONArray_(p)));
+}
+
+
+/**
+ * Serialize property information to the following JSON format for building the
+ * array of properties.
+ *
+ * {"name":"<property name>",
+ * "attributes":<number>,
+ * "propertyType":<number>,
+ * "ref":<number>}
+ *
+ * If the attribute for the property is PropertyAttribute.None it is not added.
+ * If the propertyType for the property is PropertyType.Normal it is not added.
+ * Here are a couple of examples.
+ *
+ * {"name":"hello","ref":1}
+ * {"name":"length","attributes":7,"propertyType":3,"ref":2}
+ *
+ * @param {PropertyMirror} property_mirror The property to serialize
+ * @returns {String} JSON serialization
+ */
+JSONProtocolSerializer.prototype.serializeProperty_ = function(property_mirror) {
+ var builder = new builtins.StringBuilder();
+ builder.add('{"name":');
+ builder.add(StringToJSON_(property_mirror.name()));
+ if (property_mirror.attributes() != PropertyAttribute.None) {
+ builder.add(',"attributes":');
+ builder.add(NumberToJSON_(property_mirror.attributes()));
+ }
+ if (property_mirror.propertyType() != PropertyType.Normal) {
+ builder.add(',"propertyType":');
+ builder.add(NumberToJSON_(property_mirror.propertyType()));
+ }
+ builder.add(',"ref":');
+ builder.add(NumberToJSON_(property_mirror.value().handle()));
+ builder.add('}');
+ return builder.generate();
}
JSONProtocolSerializer.prototype.serializeFrame_ = function(mirror, content) {
content.push(MakeJSONPair_('index', NumberToJSON_(mirror.index())));
content.push(MakeJSONPair_('receiver',
- mirror.receiver().toJSONProtocol(false)));
- content.push(MakeJSONPair_('func', mirror.func().toJSONProtocol(false)));
+ this.serializeReference(mirror.receiver())));
+ content.push(MakeJSONPair_('func', this.serializeReference(mirror.func())));
content.push(MakeJSONPair_('constructCall',
BooleanToJSON_(mirror.isConstructCall())));
content.push(MakeJSONPair_('debuggerFrame',
@@ -1808,7 +1872,7 @@
arg.push(MakeJSONPair_('name', StringToJSON_(argument_name)));
}
arg.push(MakeJSONPair_('value',
- mirror.argumentValue(i).toJSONProtocol(false)));
+ this.serializeReference(mirror.argumentValue(i))));
x[i] = ArrayToJSONObject_(arg);
}
content.push(MakeJSONPair_('arguments', ArrayToJSONArray_(x)));
@@ -1816,7 +1880,7 @@
for (var i = 0; i < mirror.localCount(); i++) {
var name = MakeJSONPair_('name', StringToJSON_(mirror.localName(i)));
var value = MakeJSONPair_('value',
- mirror.localValue(i).toJSONProtocol(false));
+ this.serializeReference(mirror.localValue(i)));
x[i] = '{' + name + ',' + value + '}';
}
content.push(MakeJSONPair_('locals', ArrayToJSONArray_(x)));
diff --git a/src/objects-debug.cc b/src/objects-debug.cc
index 1c1ffed..6b58f25 100644
--- a/src/objects-debug.cc
+++ b/src/objects-debug.cc
@@ -117,6 +117,7 @@
PrintF("filler");
break;
case JS_OBJECT_TYPE: // fall through
+ case JS_CONTEXT_EXTENSION_OBJECT_TYPE:
case JS_ARRAY_TYPE:
case JS_REGEXP_TYPE:
JSObject::cast(this)->JSObjectPrint();
@@ -193,6 +194,7 @@
Oddball::cast(this)->OddballVerify();
break;
case JS_OBJECT_TYPE:
+ case JS_CONTEXT_EXTENSION_OBJECT_TYPE:
JSObject::cast(this)->JSObjectVerify();
break;
case JS_VALUE_TYPE:
@@ -382,6 +384,7 @@
case BYTE_ARRAY_TYPE: return "BYTE_ARRAY";
case FILLER_TYPE: return "FILLER";
case JS_OBJECT_TYPE: return "JS_OBJECT";
+ case JS_CONTEXT_EXTENSION_OBJECT_TYPE: return "JS_CONTEXT_EXTENSION_OBJECT";
case ODDBALL_TYPE: return "ODDBALL";
case SHARED_FUNCTION_INFO_TYPE: return "SHARED_FUNCTION_INFO";
case JS_FUNCTION_TYPE: return "JS_FUNCTION";
diff --git a/src/objects-inl.h b/src/objects-inl.h
index 0bad5a1..43f1a04 100644
--- a/src/objects-inl.h
+++ b/src/objects-inl.h
@@ -27,7 +27,7 @@
//
// Review notes:
//
-// - The use of macros in these inline fuctions may seem superfluous
+// - The use of macros in these inline functions may seem superfluous
// but it is absolutely needed to make sure gcc generates optimal
// code. gcc is not happy when attempting to inline too deep.
//
@@ -328,6 +328,13 @@
}
+bool Object::IsJSContextExtensionObject() {
+ return IsHeapObject()
+ && (HeapObject::cast(this)->map()->instance_type() ==
+ JS_CONTEXT_EXTENSION_OBJECT_TYPE);
+}
+
+
bool Object::IsMap() {
return Object::IsHeapObject()
&& HeapObject::cast(this)->map()->instance_type() == MAP_TYPE;
@@ -1018,6 +1025,7 @@
case JS_REGEXP_TYPE:
return JSValue::kSize;
case JS_OBJECT_TYPE:
+ case JS_CONTEXT_EXTENSION_OBJECT_TYPE:
return JSObject::kHeaderSize;
default:
UNREACHABLE();
diff --git a/src/objects.cc b/src/objects.cc
index edc5a0d..352f5bd 100644
--- a/src/objects.cc
+++ b/src/objects.cc
@@ -43,6 +43,22 @@
namespace v8 { namespace internal {
+#define FIELD_ADDR(p, offset) \
+ (reinterpret_cast<byte*>(p) + offset - kHeapObjectTag)
+
+
+#define WRITE_FIELD(p, offset, value) \
+ (*reinterpret_cast<Object**>(FIELD_ADDR(p, offset)) = value)
+
+
+#define WRITE_INT_FIELD(p, offset, value) \
+ (*reinterpret_cast<int*>(FIELD_ADDR(p, offset)) = value)
+
+
+#define WRITE_BARRIER(object, offset) \
+ Heap::RecordWrite(object->address(), offset);
+
+
// Getters and setters are stored in a fixed array property. These are
// constants for their indices.
const int kGetterIndex = 0;
@@ -924,6 +940,7 @@
reinterpret_cast<FixedArray*>(this)->FixedArrayIterateBody(v);
break;
case JS_OBJECT_TYPE:
+ case JS_CONTEXT_EXTENSION_OBJECT_TYPE:
case JS_VALUE_TYPE:
case JS_ARRAY_TYPE:
case JS_REGEXP_TYPE:
@@ -1040,7 +1057,7 @@
// Normalize the object if the name is not a real identifier.
StringInputBuffer buffer(name);
if (!Scanner::IsIdentifier(&buffer)) {
- Object* obj = NormalizeProperties();
+ Object* obj = NormalizeProperties(CLEAR_INOBJECT_PROPERTIES);
if (obj->IsFailure()) return obj;
return AddSlowProperty(name, value, attributes);
}
@@ -1078,7 +1095,7 @@
if (map()->unused_property_fields() == 0) {
if (properties()->length() > kMaxFastProperties) {
- Object* obj = NormalizeProperties();
+ Object* obj = NormalizeProperties(CLEAR_INOBJECT_PROPERTIES);
if (obj->IsFailure()) return obj;
return AddSlowProperty(name, value, attributes);
}
@@ -1180,7 +1197,7 @@
} else {
// Normalize the object to prevent very large instance descriptors.
// This eliminates unwanted N^2 allocation and lookup behavior.
- Object* obj = NormalizeProperties();
+ Object* obj = NormalizeProperties(CLEAR_INOBJECT_PROPERTIES);
if (obj->IsFailure()) return obj;
}
}
@@ -1253,7 +1270,7 @@
PropertyAttributes attributes) {
if (map()->unused_property_fields() == 0 &&
properties()->length() > kMaxFastProperties) {
- Object* obj = NormalizeProperties();
+ Object* obj = NormalizeProperties(CLEAR_INOBJECT_PROPERTIES);
if (obj->IsFailure()) return obj;
return ReplaceSlowProperty(name, new_value, attributes);
}
@@ -1582,7 +1599,7 @@
return JSObject::cast(proto)->SetProperty(result, name, value, attributes);
}
- if (result->IsNotFound() || !result->IsProperty()) {
+ if (!result->IsProperty() && !IsJSContextExtensionObject()) {
// We could not find a local property so let's check whether there is an
// accessor that wants to handle the property.
LookupResult accessor_result;
@@ -1848,7 +1865,7 @@
}
-Object* JSObject::NormalizeProperties() {
+Object* JSObject::NormalizeProperties(PropertyNormalizationMode mode) {
if (!HasFastProperties()) return this;
// Allocate new content
@@ -1908,13 +1925,33 @@
// Allocate new map.
obj = map()->Copy();
if (obj->IsFailure()) return obj;
+ Map* new_map = Map::cast(obj);
- // We have now sucessfully allocated all the necessary objects.
+ // Clear inobject properties if needed by adjusting the instance
+ // size and putting in a filler or byte array instead of the
+ // inobject properties.
+ if (mode == CLEAR_INOBJECT_PROPERTIES && map()->inobject_properties() > 0) {
+ int instance_size_delta = map()->inobject_properties() * kPointerSize;
+ int new_instance_size = map()->instance_size() - instance_size_delta;
+ new_map->set_inobject_properties(0);
+ new_map->set_instance_size(new_instance_size);
+ if (instance_size_delta == kPointerSize) {
+ WRITE_FIELD(this, new_instance_size, Heap::one_word_filler_map());
+ } else {
+ int byte_array_length = ByteArray::LengthFor(instance_size_delta);
+ int byte_array_length_offset = new_instance_size + kPointerSize;
+ WRITE_FIELD(this, new_instance_size, Heap::byte_array_map());
+ WRITE_INT_FIELD(this, byte_array_length_offset, byte_array_length);
+ }
+ WRITE_BARRIER(this, new_instance_size);
+ }
+ new_map->set_unused_property_fields(0);
+
+ // We have now successfully allocated all the necessary objects.
// Changes can now be made with the guarantee that all of them take effect.
- set_map(Map::cast(obj));
+ set_map(new_map);
map()->set_instance_descriptors(Heap::empty_descriptor_array());
- map()->set_unused_property_fields(0);
set_properties(dictionary);
Counters::props_to_dictionary.Increment();
@@ -1982,7 +2019,7 @@
if (!result.IsValid()) return Heap::true_value();
// Normalize object if needed.
- Object* obj = NormalizeProperties();
+ Object* obj = NormalizeProperties(CLEAR_INOBJECT_PROPERTIES);
if (obj->IsFailure()) return obj;
ASSERT(!HasFastProperties());
@@ -2145,7 +2182,7 @@
return JSObject::cast(this)->DeleteLazyProperty(&result, name);
}
// Normalize object if needed.
- Object* obj = NormalizeProperties();
+ Object* obj = NormalizeProperties(CLEAR_INOBJECT_PROPERTIES);
if (obj->IsFailure()) return obj;
// Make sure the properties are normalized before removing the entry.
Dictionary* dictionary = property_dictionary();
@@ -2324,7 +2361,7 @@
}
// Check __proto__ before interceptor.
- if (name->Equals(Heap::Proto_symbol())) {
+ if (name->Equals(Heap::Proto_symbol()) && !IsJSContextExtensionObject()) {
result->ConstantResult(this);
return;
}
@@ -2387,7 +2424,7 @@
// callback overwriting in this object or it's prototype chain.
// This mechanism is needed for instance in a browser setting, where
// certain accessors such as window.location should not be allowed
- // to be overwriten because allowing overwriting could potentially
+ // to be overwritten because allowing overwriting could potentially
// cause security problems.
LookupResult callback_result;
LookupCallback(name, &callback_result);
@@ -2411,7 +2448,7 @@
}
// Normalize object to make this operation simple.
- Object* ok = NormalizeProperties();
+ Object* ok = NormalizeProperties(CLEAR_INOBJECT_PROPERTIES);
if (ok->IsFailure()) return ok;
// Allocate the fixed array to hold getter and setter.
@@ -6664,7 +6701,9 @@
if (descriptors_unchecked->IsFailure()) return descriptors_unchecked;
DescriptorArray* descriptors = DescriptorArray::cast(descriptors_unchecked);
- int number_of_allocated_fields = number_of_fields + unused_property_fields;
+ int inobject_props = obj->map()->inobject_properties();
+ int number_of_allocated_fields =
+ number_of_fields + unused_property_fields - inobject_props;
// Allocate the fixed array for the fields.
Object* fields = Heap::AllocateFixedArray(number_of_allocated_fields);
@@ -6682,6 +6721,7 @@
if (key->IsFailure()) return key;
PropertyDetails details = DetailsAt(i);
PropertyType type = details.type();
+
if (value->IsJSFunction()) {
ConstantFunctionDescriptor d(String::cast(key),
JSFunction::cast(value),
@@ -6689,7 +6729,14 @@
details.index());
w.Write(&d);
} else if (type == NORMAL) {
- FixedArray::cast(fields)->set(current_offset, value);
+ if (current_offset < inobject_props) {
+ obj->InObjectPropertyAtPut(current_offset,
+ value,
+ UPDATE_WRITE_BARRIER);
+ } else {
+ int offset = current_offset - inobject_props;
+ FixedArray::cast(fields)->set(offset, value);
+ }
FieldDescriptor d(String::cast(key),
current_offset++,
details.attributes(),
@@ -6722,8 +6769,9 @@
ASSERT(obj->IsJSObject());
descriptors->SetNextEnumerationIndex(NextEnumerationIndex());
- // Check it really works.
+ // Check that it really works.
ASSERT(obj->HasFastProperties());
+
return obj;
}
diff --git a/src/objects.h b/src/objects.h
index c79838a..b77c0b2 100644
--- a/src/objects.h
+++ b/src/objects.h
@@ -166,9 +166,19 @@
uint32_t value_;
};
+
// Setter that skips the write barrier if mode is SKIP_WRITE_BARRIER.
enum WriteBarrierMode { SKIP_WRITE_BARRIER, UPDATE_WRITE_BARRIER };
+
+// PropertyNormalizationMode is used to specify whether to keep
+// inobject properties when normalizing properties of a JSObject.
+enum PropertyNormalizationMode {
+ CLEAR_INOBJECT_PROPERTIES,
+ KEEP_INOBJECT_PROPERTIES
+};
+
+
// All Maps have a field instance_type containing a InstanceType.
// It describes the type of the instances.
//
@@ -268,6 +278,7 @@
\
V(JS_VALUE_TYPE) \
V(JS_OBJECT_TYPE) \
+ V(JS_CONTEXT_EXTENSION_OBJECT_TYPE) \
V(JS_GLOBAL_OBJECT_TYPE) \
V(JS_BUILTINS_OBJECT_TYPE) \
V(JS_GLOBAL_PROXY_TYPE) \
@@ -525,6 +536,7 @@
JS_VALUE_TYPE,
JS_OBJECT_TYPE,
+ JS_CONTEXT_EXTENSION_OBJECT_TYPE,
JS_GLOBAL_OBJECT_TYPE,
JS_BUILTINS_OBJECT_TYPE,
JS_GLOBAL_PROXY_TYPE,
@@ -560,9 +572,9 @@
inline void set_##name(bool value); \
-#define DECL_ACCESSORS(name, type) \
- inline type* name(); \
- inline void set_##name(type* value, \
+#define DECL_ACCESSORS(name, type) \
+ inline type* name(); \
+ inline void set_##name(type* value, \
WriteBarrierMode mode = UPDATE_WRITE_BARRIER); \
@@ -612,6 +624,7 @@
inline bool IsOutOfMemoryFailure();
inline bool IsException();
inline bool IsJSObject();
+ inline bool IsJSContextExtensionObject();
inline bool IsMap();
inline bool IsFixedArray();
inline bool IsDescriptorArray();
@@ -1357,7 +1370,7 @@
// Convert the object to use the canonical dictionary
// representation.
- Object* NormalizeProperties();
+ Object* NormalizeProperties(PropertyNormalizationMode mode);
Object* NormalizeElements();
// Transform slow named properties to fast variants.
@@ -1643,7 +1656,7 @@
int BinarySearch(String* name, int low, int high);
// Perform a linear search in the instance descriptors represented
- // by this fixed array. len is the number of descriptor indeces that are
+ // by this fixed array. len is the number of descriptor indices that are
// valid. Does not require the descriptors to be sorted.
int LinearSearch(String* name, int len);
@@ -1780,7 +1793,7 @@
// Returns the key at entry.
Object* KeyAt(int entry) { return get(EntryToIndex(entry)); }
- // Tells wheter k is a real key. Null and undefined are not allowed
+ // Tells whether k is a real key. Null and undefined are not allowed
// as keys and can be used to indicate missing or deleted elements.
bool IsKey(Object* k) {
return !k->IsNull() && !k->IsUndefined();
@@ -2047,7 +2060,7 @@
void UpdateMaxNumberKey(uint32_t key);
- // Generate new enumneration indices to avoid enumeration insdex overflow.
+ // Generate new enumeration indices to avoid enumeration index overflow.
Object* GenerateNewEnumerationIndices();
static const int kMaxNumberKeyIndex = kPrefixStartIndex;
@@ -2220,7 +2233,7 @@
// Returns true if pc is inside this object's instructions.
inline bool contains(byte* pc);
- // Returns the adddress of the scope information.
+ // Returns the address of the scope information.
inline byte* sinfo_start();
// Convert inline cache target from address to code object before GC.
@@ -2293,7 +2306,7 @@
// - How to iterate over an object (for garbage collection)
class Map: public HeapObject {
public:
- // instance size.
+ // Instance size.
inline int instance_size();
inline void set_instance_size(int value);
@@ -2301,16 +2314,16 @@
inline int inobject_properties();
inline void set_inobject_properties(int value);
- // instance type.
+ // Instance type.
inline InstanceType instance_type();
inline void set_instance_type(InstanceType value);
- // tells how many unused property fields are available in the instance.
- // (only used for JSObject in fast mode).
+ // Tells how many unused property fields are available in the
+ // instance (only used for JSObject in fast mode).
inline int unused_property_fields();
inline void set_unused_property_fields(int value);
- // bit field.
+ // Bit field.
inline byte bit_field();
inline void set_bit_field(byte value);
@@ -2463,7 +2476,6 @@
static const int kInObjectPropertiesOffset = kInstanceSizesOffset + 1;
// The bytes at positions 2 and 3 are not in use at the moment.
-
// Byte offsets within kInstanceAttributesOffset attributes.
static const int kInstanceTypeOffset = kInstanceAttributesOffset + 0;
static const int kUnusedPropertyFieldsOffset = kInstanceAttributesOffset + 1;
@@ -3762,7 +3774,7 @@
};
-// An accesor must have a getter, but can have no setter.
+// An accessor must have a getter, but can have no setter.
//
// When setting a property, V8 searches accessors in prototypes.
// If an accessor was found and it does not have a setter,
@@ -4025,11 +4037,11 @@
};
-// The DebugInfo class holds additional information for a function beeing
+// The DebugInfo class holds additional information for a function being
// debugged.
class DebugInfo: public Struct {
public:
- // The shared function info for the source beeing debugged.
+ // The shared function info for the source being debugged.
DECL_ACCESSORS(shared, SharedFunctionInfo)
// Code object for the original code.
DECL_ACCESSORS(original_code, Code)
diff --git a/src/parser.cc b/src/parser.cc
index 488389b..7f1bff2 100644
--- a/src/parser.cc
+++ b/src/parser.cc
@@ -640,7 +640,7 @@
// Allocation macro that should be used to allocate objects that must
// only be allocated in real parsing mode. Note that in preparse mode
// not only is the syntax tree not created but the constructor
-// arguments are not evaulated.
+// arguments are not evaluated.
#define NEW(expr) (is_pre_parsing_ ? NULL : new expr)
@@ -1254,7 +1254,7 @@
// statements, which themselves are only valid within blocks,
// iterations or 'switch' statements (i.e., BreakableStatements),
// labels can be simply ignored in all other cases; except for
- // trivial labelled break statements 'label: break label' which is
+ // trivial labeled break statements 'label: break label' which is
// parsed into an empty statement.
// Keep the source position of the statement
@@ -1869,7 +1869,7 @@
tok != Token::SEMICOLON && tok != Token::RBRACE && tok != Token::EOS) {
label = ParseIdentifier(CHECK_OK);
}
- // Parse labelled break statements that target themselves into
+ // Parse labeled break statements that target themselves into
// empty statements, e.g. 'l1: l2: l3: break l2;'
if (!label.is_null() && ContainsLabel(labels, label)) {
return factory()->EmptyStatement();
@@ -2064,32 +2064,6 @@
}
-Expression* Parser::MakeCatchContext(Handle<String> id, VariableProxy* value) {
- ZoneListWrapper<ObjectLiteral::Property> properties =
- factory()->NewList<ObjectLiteral::Property>(1);
- Literal* key = NEW(Literal(id));
- ObjectLiteral::Property* property = NEW(ObjectLiteral::Property(key, value));
- properties.Add(property);
-
- // This must be called always, even during pre-parsing!
- // (Computation of literal index must happen before pre-parse bailout.)
- int literal_index = temp_scope_->NextMaterializedLiteralIndex();
- if (is_pre_parsing_) {
- return NULL;
- }
-
- // Construct the expression for calling Runtime::CreateObjectLiteral
- // with the literal array as argument.
- Handle<FixedArray> constant_properties = Factory::empty_fixed_array();
- ZoneList<Expression*>* arguments = new ZoneList<Expression*>(1);
- arguments->Add(new Literal(constant_properties));
-
- return new ObjectLiteral(constant_properties,
- properties.elements(),
- literal_index);
-}
-
-
TryStatement* Parser::ParseTryStatement(bool* ok) {
// TryStatement ::
// 'try' Block Catch
@@ -2141,7 +2115,8 @@
// Allocate a temporary for holding the finally state while
// executing the finally block.
catch_var = top_scope_->NewTemporary(Factory::catch_var_symbol());
- Expression* obj = MakeCatchContext(name, catch_var);
+ Literal* name_literal = NEW(Literal(name));
+ Expression* obj = NEW(CatchExtensionObject(name_literal, catch_var));
{ Target target(this, &catch_collector);
catch_block = WithHelper(obj, NULL, true, CHECK_OK);
}
@@ -3103,10 +3078,6 @@
constant_properties->set(position++, *literal->handle());
}
- // Construct the expression for calling Runtime::CreateObjectLiteral
- // with the literal array as argument.
- ZoneList<Expression*>* arguments = new ZoneList<Expression*>(1);
- arguments->Add(new Literal(constant_properties));
return new ObjectLiteral(constant_properties,
properties.elements(),
literal_index);
diff --git a/src/parser.h b/src/parser.h
index 6e86f09..ee303b4 100644
--- a/src/parser.h
+++ b/src/parser.h
@@ -153,7 +153,7 @@
// Support for doing lazy compilation. The script is the script containing full
// source of the script where the function is declared. The start_position and
// end_position specifies the part of the script source which has the source
-// for the function decleration in the form:
+// for the function declaration in the form:
//
// (<formal parameters>) { <function body> }
//
diff --git a/src/platform-macos.cc b/src/platform-macos.cc
index 122e7fa..f625174 100644
--- a/src/platform-macos.cc
+++ b/src/platform-macos.cc
@@ -259,8 +259,8 @@
}
-void OS::Sleep(int miliseconds) {
- usleep(1000 * miliseconds);
+void OS::Sleep(int milliseconds) {
+ usleep(1000 * milliseconds);
}
diff --git a/src/platform-win32.cc b/src/platform-win32.cc
index ada090a..92fb536 100644
--- a/src/platform-win32.cc
+++ b/src/platform-win32.cc
@@ -61,7 +61,7 @@
#include <dbghelp.h> // For SymLoadModule64 and al.
#include <tlhelp32.h> // For Module32First and al.
-// These aditional WIN32 includes have to be right here as the #undef's below
+// These additional WIN32 includes have to be right here as the #undef's below
// makes it impossible to have them elsewhere.
#include <winsock2.h>
#include <process.h> // for _beginthreadex()
@@ -490,7 +490,7 @@
}
-// Return the dalight savings time offset for this time.
+// Return the daylight savings time offset for this time.
int64_t Time::DaylightSavingsOffset() {
return InDST() ? 60 * kMsPerMinute : 0;
}
@@ -859,7 +859,7 @@
// The following code loads functions defined in DbhHelp.h and TlHelp32.h
-// dynamically. This is to avoid beeing depending on dbghelp.dll and
+// dynamically. This is to avoid being depending on dbghelp.dll and
// tlhelp32.dll when running (the functions in tlhelp32.dll have been moved to
// kernel32.dll at some point so loading functions defines in TlHelp32.h
// dynamically might not be necessary any more - for some versions of Windows?).
@@ -1013,7 +1013,7 @@
dbghelp_loaded = result;
return result;
- // NOTE: The modules are never unloaded and will stay arround until the
+ // NOTE: The modules are never unloaded and will stay around until the
// application is closed.
}
diff --git a/src/platform.h b/src/platform.h
index 8d62efd..0e42ba6 100644
--- a/src/platform.h
+++ b/src/platform.h
@@ -33,7 +33,7 @@
// implementation for a particular platform is put in platform_<os>.cc.
// The build system then uses the implementation for the target platform.
//
-// This design has been choosen because it is simple and fast. Alternatively,
+// This design has been chosen because it is simple and fast. Alternatively,
// the platform dependent classes could have been implemented using abstract
// superclasses with virtual methods and having specializations for each
// platform. This design was rejected because it was more complicated and
@@ -145,7 +145,7 @@
static void PrintError(const char* format, ...);
static void VPrintError(const char* format, va_list args);
- // Allocate/Free memory used by JS heap. Pages are readable/writeable, but
+ // Allocate/Free memory used by JS heap. Pages are readable/writable, but
// they are not guaranteed to be executable unless 'executable' is true.
// Returns the address of allocated memory, or NULL if failed.
static void* Allocate(const size_t requested,
@@ -162,8 +162,8 @@
// heap corruption.
static bool IsOutsideAllocatedSpace(void* pointer);
- // Sleep for a number of miliseconds.
- static void Sleep(const int miliseconds);
+ // Sleep for a number of milliseconds.
+ static void Sleep(const int milliseconds);
// Abort the current process.
static void Abort();
diff --git a/src/prettyprinter.cc b/src/prettyprinter.cc
index a914383..c3bf531 100644
--- a/src/prettyprinter.cc
+++ b/src/prettyprinter.cc
@@ -285,6 +285,15 @@
}
+void PrettyPrinter::VisitCatchExtensionObject(CatchExtensionObject* node) {
+ Print("{ ");
+ Visit(node->key());
+ Print(": ");
+ Visit(node->value());
+ Print(" }");
+}
+
+
void PrettyPrinter::VisitSlot(Slot* node) {
switch (node->type()) {
case Slot::PARAMETER:
@@ -955,6 +964,13 @@
}
+void AstPrinter::VisitCatchExtensionObject(CatchExtensionObject* node) {
+ IndentedScope indent("CatchExtensionObject");
+ PrintIndentedVisit("KEY", node->key());
+ PrintIndentedVisit("VALUE", node->value());
+}
+
+
void AstPrinter::VisitSlot(Slot* node) {
PrintIndented("SLOT ");
switch (node->type()) {
diff --git a/src/property.h b/src/property.h
index 9af6cbb..914b8dd 100644
--- a/src/property.h
+++ b/src/property.h
@@ -324,7 +324,7 @@
protected:
DescriptorArray* descriptors_;
int pos_; // Current position.
- int limit_; // Limit for posistion.
+ int limit_; // Limit for position.
};
diff --git a/src/regexp-macro-assembler-ia32.cc b/src/regexp-macro-assembler-ia32.cc
index 89b416c..824a297 100644
--- a/src/regexp-macro-assembler-ia32.cc
+++ b/src/regexp-macro-assembler-ia32.cc
@@ -30,6 +30,7 @@
#include "unicode.h"
#include "log.h"
#include "ast.h"
+#include "regexp-stack.h"
#include "macro-assembler.h"
#include "regexp-macro-assembler.h"
#include "macro-assembler-ia32.h"
@@ -46,12 +47,15 @@
* - esi : end of input (points to byte after last character in input).
* - ebp : points to the location above the registers on the stack,
* as if by the "enter <register_count>" opcode.
- * - esp : points to tip of backtracking stack.
+ * - esp : points to tip of C stack.
+ * - ecx : points to tip of backtrack stack
*
* The registers eax, ebx and ecx are free to use for computations.
*
* Each call to a public method should retain this convention.
* The stack will have the following structure:
+ * - stack_area_top (High end of the memory area to use as
+ * backtracking stack)
* - at_start (if 1, start at start of string, if 0, don't)
* - int* capture_array (int[num_saved_registers_], for output).
* - end of input (index of end of string, relative to *string_base)
@@ -59,10 +63,10 @@
* to *string_base)
* - void** string_base (location of a handle containing the string)
* - return address
- * - backup of esi
- * - backup of edi
- * - backup of ebx
* ebp-> - old ebp
+ * - backup of caller esi
+ * - backup of caller edi
+ * - backup of caller ebx
* - register 0 ebp[-4] (Only positions must be stored in the first
* - register 1 ebp[-8] num_saved_registers_ registers)
* - ...
@@ -77,7 +81,8 @@
* int start_offset,
* int end_offset,
* int* capture_output_array,
- * bool at_start)
+ * bool at_start,
+ * byte* stack_area_top)
*/
#define __ masm_->
@@ -110,6 +115,12 @@
backtrack_label_.Unuse();
exit_label_.Unuse();
check_preempt_label_.Unuse();
+ stack_overflow_label_.Unuse();
+}
+
+
+int RegExpMacroAssemblerIA32::stack_limit_slack() {
+ return RegExpStack::kStackLimitSlack;
}
@@ -124,12 +135,18 @@
void RegExpMacroAssemblerIA32::AdvanceRegister(int reg, int by) {
ASSERT(reg >= 0);
ASSERT(reg < num_registers_);
- __ add(register_location(reg), Immediate(by));
+ if (by != 0) {
+ __ add(register_location(reg), Immediate(by));
+ }
}
void RegExpMacroAssemblerIA32::Backtrack() {
- SafeReturn();
+ CheckPreemption();
+ // Pop Code* offset from backtrack stack, add Code* and jump to location.
+ Pop(ebx);
+ __ add(Operand(ebx), Immediate(self_));
+ __ jmp(Operand(ebx));
}
@@ -137,22 +154,11 @@
__ bind(label);
}
+
void RegExpMacroAssemblerIA32::CheckBitmap(uc16 start,
Label* bitmap,
Label* on_zero) {
UNIMPLEMENTED();
- __ mov(eax, current_character());
- __ sub(Operand(eax), Immediate(start));
- __ cmp(eax, 64); // FIXME: 64 = length_of_bitmap_in_bits.
- BranchOrBacktrack(greater_equal, on_zero);
- __ mov(ebx, eax);
- __ shr(ebx, 3);
- // TODO(lrn): Where is the bitmap stored? Pass the bitmap as argument instead.
- // __ mov(ecx, position_of_bitmap);
- __ movzx_b(ebx, Operand(ecx, ebx, times_1, 0));
- __ and_(eax, (1<<3)-1);
- __ bt(Operand(ebx), eax);
- BranchOrBacktrack(carry, on_zero);
}
@@ -169,8 +175,10 @@
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));
BranchOrBacktrack(equal, on_not_at_start);
+ // 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));
@@ -191,6 +199,7 @@
int byte_length = str.length() * char_size();
int byte_offset = cp_offset * char_size();
if (check_end_of_string) {
+ // Check that there are at least str.length() characters left in the input.
__ cmp(Operand(edi), Immediate(-(byte_offset + byte_length)));
BranchOrBacktrack(greater, on_failure);
}
@@ -222,9 +231,9 @@
void RegExpMacroAssemblerIA32::CheckGreedyLoop(Label* on_equal) {
Label fallthrough;
- __ cmp(edi, Operand(esp, 0));
+ __ cmp(edi, Operand(backtrack_stackpointer(), 0));
__ j(not_equal, &fallthrough);
- __ add(Operand(esp), Immediate(4)); // Pop.
+ __ add(Operand(backtrack_stackpointer()), Immediate(kPointerSize)); // Pop.
BranchOrBacktrack(no_condition, on_equal);
__ bind(&fallthrough);
}
@@ -234,20 +243,31 @@
int start_reg,
Label* on_no_match) {
Label fallthrough;
- __ mov(edx, register_location(start_reg));
- __ mov(ecx, register_location(start_reg + 1));
- __ sub(ecx, Operand(edx)); // Length to check.
- BranchOrBacktrack(less, on_no_match);
+ __ mov(edx, register_location(start_reg)); // Index of start of capture
+ __ mov(ebx, register_location(start_reg + 1)); // Index of end of capture
+ __ sub(ebx, Operand(edx)); // Length of capture.
+
+ // The length of a capture should not be negative. This can only happen
+ // if the end of the capture is unrecorded, or at a point earlier than
+ // the start of the capture.
+ BranchOrBacktrack(less, on_no_match, not_taken);
+
+ // If length is zero, either the capture is empty or it is completely
+ // uncaptured. In either case succeed immediately.
__ j(equal, &fallthrough);
if (mode_ == ASCII) {
Label success;
Label fail;
Label loop_increment;
+ // Save register contents to make the registers available below.
__ push(edi);
- __ add(edx, Operand(esi));
- __ add(edi, Operand(esi));
- __ add(ecx, Operand(edi));
+ __ push(backtrack_stackpointer());
+ // After this, the eax, ebx, ecx, edx and edi registers are available.
+
+ __ add(edx, Operand(esi)); // Start of capture
+ __ add(edi, Operand(esi)); // Start of text to match against capture.
+ __ add(ebx, Operand(edi)); // End of text to match against capture.
Label loop;
__ bind(&loop);
@@ -255,55 +275,71 @@
__ cmpb_al(Operand(edx, 0));
__ j(equal, &loop_increment);
- // Compare lower-case if letters.
- __ or_(eax, 0x20); // To lower-case.
- __ lea(ebx, Operand(eax, -'a'));
- __ cmp(ebx, static_cast<int32_t>('z' - 'a'));
+ // Mismatch, try case-insensitive match (converting letters to lower-case).
+ __ or_(eax, 0x20); // Convert match character to lower-case.
+ __ lea(ecx, Operand(eax, -'a'));
+ __ cmp(ecx, static_cast<int32_t>('z' - 'a')); // Is eax a lowercase letter?
__ j(above, &fail);
- __ movzx_b(ebx, Operand(edx, 0));
- __ or_(ebx, 0x20); // To-lower-case
- __ cmp(eax, Operand(ebx));
+ // Also convert capture character.
+ __ movzx_b(ecx, Operand(edx, 0));
+ __ or_(ecx, 0x20);
+
+ __ cmp(eax, Operand(ecx));
__ j(not_equal, &fail);
__ bind(&loop_increment);
+ // Increment pointers into match and capture strings.
__ add(Operand(edx), Immediate(1));
__ add(Operand(edi), Immediate(1));
- __ cmp(edi, Operand(ecx));
+ // Compare to end of match, and loop if not done.
+ __ cmp(edi, Operand(ebx));
__ j(below, &loop, taken);
__ jmp(&success);
__ bind(&fail);
+ // Restore original values before failing.
+ __ pop(backtrack_stackpointer());
__ pop(edi);
BranchOrBacktrack(no_condition, on_no_match);
__ bind(&success);
- __ pop(eax); // discard original value of edi
+ // Restore original value before continuing.
+ __ pop(backtrack_stackpointer());
+ // Drop original value of character position.
+ __ add(Operand(esp), Immediate(kPointerSize));
+ // Compute new value of character position after the matched part.
__ sub(edi, Operand(esi));
} else {
ASSERT(mode_ == UC16);
+ // Save registers before calling C function.
__ push(esi);
__ push(edi);
- __ push(ecx);
+ __ push(backtrack_stackpointer());
+ __ push(ebx);
const int four_arguments = 4;
FrameAlign(four_arguments);
- // Put arguments on stack.
- __ mov(Operand(esp, 3 * kPointerSize), ecx);
- __ mov(ebx, Operand(ebp, kInputEndOffset));
- __ add(edi, Operand(ebx));
+ // Put arguments into allocated stack area.
+ __ mov(Operand(esp, 3 * kPointerSize), ebx);
+ __ mov(ecx, Operand(ebp, kInputEndOffset));
+ __ add(edi, Operand(ecx));
__ mov(Operand(esp, 2 * kPointerSize), edi);
- __ add(eax, Operand(ebx));
+ __ add(eax, Operand(ecx));
__ mov(Operand(esp, 1 * kPointerSize), eax);
__ mov(eax, Operand(ebp, kInputBuffer));
__ mov(Operand(esp, 0 * kPointerSize), eax);
Address function_address = FUNCTION_ADDR(&CaseInsensitiveCompareUC16);
CallCFunction(function_address, four_arguments);
- __ pop(ecx);
+ // Pop original values before reacting on result value.
+ __ pop(ebx);
+ __ pop(backtrack_stackpointer());
__ pop(edi);
__ pop(esi);
+ // Check if function returned non-zero for success or zero for failure.
__ or_(eax, Operand(eax));
BranchOrBacktrack(zero, on_no_match);
- __ add(edi, Operand(ecx));
+ // On success, increment position by length of capture.
+ __ add(edi, Operand(ebx));
}
__ bind(&fallthrough);
}
@@ -315,45 +351,60 @@
Label fallthrough;
Label success;
Label fail;
- __ mov(edx, register_location(start_reg));
- __ mov(ecx, register_location(start_reg + 1));
- __ sub(ecx, Operand(edx)); // Length to check.
- BranchOrBacktrack(less, on_no_match);
- __ j(equal, &fallthrough);
- // Check that there are sufficient characters left in the input.
+ // Find length of back-referenced capture.
+ __ mov(edx, register_location(start_reg));
+ __ mov(eax, register_location(start_reg + 1));
+ __ sub(eax, Operand(edx)); // Length to check.
+ // Fail on partial or illegal capture (start of capture after end of capture).
+ BranchOrBacktrack(less, on_no_match);
+ // Succeed on empty capture (including no capture)
+ __ j(equal, &fallthrough);
+
+ // Check that there are sufficient characters left in the input.
__ mov(ebx, edi);
- __ add(ebx, Operand(ecx));
+ __ add(ebx, Operand(eax));
BranchOrBacktrack(greater, on_no_match);
- __ mov(ebx, edi);
- __ add(edi, Operand(esi));
- __ add(edx, Operand(esi));
- __ add(ecx, Operand(edi));
+ // Save register to make it available below.
+ __ push(backtrack_stackpointer());
+
+ // Compute pointers to match string and capture string
+ __ lea(ebx, Operand(esi, edi, times_1, 0)); // Start of match.
+ __ add(edx, Operand(esi)); // Start of capture.
+ __ lea(ecx, Operand(eax, ebx, times_1, 0)); // End of match
Label loop;
__ bind(&loop);
if (mode_ == ASCII) {
__ movzx_b(eax, Operand(edx, 0));
- __ cmpb_al(Operand(edi, 0));
+ __ cmpb_al(Operand(ebx, 0));
} else {
ASSERT(mode_ == UC16);
__ movzx_w(eax, Operand(edx, 0));
- __ cmpw_ax(Operand(edi, 0));
+ __ cmpw_ax(Operand(ebx, 0));
}
__ j(not_equal, &fail);
+ // Increment pointers into capture and match string.
__ add(Operand(edx), Immediate(char_size()));
- __ add(Operand(edi), Immediate(char_size()));
- __ cmp(edi, Operand(ecx));
+ __ add(Operand(ebx), Immediate(char_size()));
+ // Check if we have reached end of match area.
+ __ cmp(ebx, Operand(ecx));
__ j(below, &loop);
__ jmp(&success);
__ bind(&fail);
- __ mov(edi, ebx);
+ // Restore backtrack stackpointer.
+ __ pop(backtrack_stackpointer());
BranchOrBacktrack(no_condition, on_no_match);
__ bind(&success);
- __ sub(edi, Operand(esi));
+ // Move current character position to position after match.
+ __ mov(edi, ecx);
+ __ sub(Operand(edi), esi);
+ // Restore backtrack stackpointer.
+ __ pop(backtrack_stackpointer());
+
__ bind(&fallthrough);
}
@@ -406,6 +457,7 @@
BranchOrBacktrack(not_equal, on_not_equal);
}
+
bool RegExpMacroAssemblerIA32::CheckSpecialCharacterClass(uc16 type,
int cp_offset,
bool check_offset,
@@ -428,7 +480,7 @@
// Check range 0x09..0x0d
__ sub(Operand(current_character()), Immediate('\t'));
__ cmp(current_character(), '\r' - '\t');
- BranchOrBacktrack(above_equal, on_no_match);
+ BranchOrBacktrack(above, on_no_match);
__ bind(&success);
return true;
}
@@ -446,7 +498,7 @@
BranchOrBacktrack(equal, on_no_match);
__ sub(Operand(current_character()), Immediate('\t'));
__ cmp(current_character(), '\r' - '\t');
- BranchOrBacktrack(below, on_no_match);
+ BranchOrBacktrack(below_equal, on_no_match);
return true;
}
return false;
@@ -459,7 +511,7 @@
}
__ sub(Operand(current_character()), Immediate('0'));
__ cmp(current_character(), '9' - '0');
- BranchOrBacktrack(greater_equal, on_no_match);
+ BranchOrBacktrack(above, on_no_match);
return true;
case 'D':
// Match non ASCII-digits
@@ -470,7 +522,7 @@
}
__ sub(Operand(current_character()), Immediate('0'));
__ cmp(current_character(), '9' - '0');
- BranchOrBacktrack(below, on_no_match);
+ BranchOrBacktrack(below_equal, on_no_match);
return true;
case '.': {
// Match non-newlines (not 0x0a('\n'), 0x0d('\r'), 0x2028 and 0x2029)
@@ -479,18 +531,18 @@
} else {
LoadCurrentCharacterUnchecked(cp_offset, 1);
}
- // Compute hash value so exactly 0x0a and 0x0d become zero.
- __ sub(Operand(current_character()), Immediate('\n'));
- __ mov(eax, current_character());
- __ and_(current_character(), 0x01);
- __ shr(eax, 1);
- __ xor_(current_character(), Operand(eax));
- BranchOrBacktrack(equal, on_no_match);
+ __ xor_(Operand(current_character()), Immediate(0x01));
+ // See if current character is '\n'^1 or '\r'^1, i.e., 0x0b or 0x0c
+ __ sub(Operand(current_character()), Immediate(0x0b));
+ __ cmp(current_character(), 0x0c - 0x0b);
+ BranchOrBacktrack(below_equal, on_no_match);
if (mode_ == UC16) {
// Compare original value to 0x2028 and 0x2029, using the already
- // computed ((current_char - '\n') >> 1) in eax.
- __ cmp(eax, (0x2028 - '\n') >> 1);
- BranchOrBacktrack(equal, on_no_match);
+ // computed (current_char ^ 0x01 - 0x0b). I.e., check for
+ // 0x201d (0x2028 - 0x0b) or 0x201e.
+ __ sub(Operand(current_character()), Immediate(0x2028 - 0x0b));
+ __ cmp(current_character(), 1);
+ BranchOrBacktrack(below_equal, on_no_match);
}
return true;
}
@@ -511,35 +563,6 @@
Label* half_nibble_map,
const Vector<Label*>& destinations) {
UNIMPLEMENTED();
- __ mov(eax, current_character());
- __ sub(Operand(eax), Immediate(start));
-
- __ mov(ecx, eax);
- __ shr(eax, 2);
- // FIXME: ecx must hold address of map
- __ movzx_b(eax, Operand(ecx, eax, times_1, 0));
- __ and_(ecx, 0x03);
- __ add(ecx, Operand(ecx));
- __ shr(eax); // Shift right cl times
-
- Label second_bit_set, case_3, case_1;
- __ test(eax, Immediate(0x02));
- __ j(not_zero, &second_bit_set);
- __ test(eax, Immediate(0x01));
- __ j(not_zero, &case_1);
- // Case 0:
- __ jmp(destinations[0]);
- __ bind(&case_1);
- // Case 1:
- __ jmp(destinations[1]);
- __ bind(&second_bit_set);
- __ test(eax, Immediate(0x01));
- __ j(not_zero, &case_3);
- // Case 2
- __ jmp(destinations[2]);
- __ bind(&case_3);
- // Case 3:
- __ jmp(destinations[3]);
}
@@ -548,17 +571,6 @@
Label* byte_map,
const Vector<Label*>& destinations) {
UNIMPLEMENTED();
-
- Label fallthrough;
- __ mov(eax, current_character());
- __ sub(Operand(eax), Immediate(start));
- __ cmp(eax, 64); // FIXME: 64 = size of map. Found somehow??
- __ j(greater_equal, &fallthrough);
- // TODO(lrn): ecx must hold address of map
- __ movzx_b(eax, Operand(ecx, eax, times_1, 0));
- // jump table: jump to destinations[eax];
-
- __ bind(&fallthrough);
}
@@ -567,16 +579,6 @@
Label* byte_map,
const Vector<Label*>& destinations) {
UNIMPLEMENTED();
-
- Label fallthrough;
- __ mov(eax, current_character());
- __ shr(eax, 8);
- __ sub(Operand(eax), Immediate(start));
- __ cmp(eax, destinations.length() - start);
- __ j(greater_equal, &fallthrough);
-
- // TODO(lrn) jumptable: jump to destinations[eax]
- __ bind(&fallthrough);
}
@@ -586,6 +588,7 @@
void RegExpMacroAssemblerIA32::Fail() {
+ ASSERT(FAILURE == 0); // Return value for failure is zero.
__ xor_(eax, Operand(eax)); // zero eax.
__ jmp(&exit_label_);
}
@@ -597,12 +600,56 @@
// Entry code:
__ bind(&entry_label_);
+ // Start new stack frame.
+ __ push(ebp);
+ __ mov(ebp, esp);
// Save callee-save registers. Order here should correspond to order of
// kBackup_ebx etc.
__ push(esi);
__ push(edi);
__ push(ebx); // Callee-save on MacOS.
- __ enter(Immediate(num_registers_ * kPointerSize));
+ __ push(Immediate(0)); // Make room for input start minus one
+
+ // Check if we have space on the stack for registers.
+ Label retry_stack_check;
+ Label stack_limit_hit;
+ Label stack_ok;
+
+ __ bind(&retry_stack_check);
+ ExternalReference stack_guard_limit =
+ ExternalReference::address_of_stack_guard_limit();
+ __ mov(ecx, esp);
+ __ sub(ecx, Operand::StaticVariable(stack_guard_limit));
+ // Handle it if the stack pointer is already below the stack limit.
+ __ j(below_equal, &stack_limit_hit, not_taken);
+ // Check if there is room for the variable number of registers above
+ // the stack limit.
+ __ cmp(ecx, num_registers_ * kPointerSize);
+ __ j(above_equal, &stack_ok, taken);
+ // Exit with exception.
+ __ mov(eax, EXCEPTION);
+ __ jmp(&exit_label_);
+
+ __ bind(&stack_limit_hit);
+ int num_arguments = 2;
+ FrameAlign(num_arguments);
+ __ mov(Operand(esp, 1 * kPointerSize), Immediate(self_));
+ __ lea(eax, Operand(esp, -kPointerSize));
+ __ mov(Operand(esp, 0 * kPointerSize), eax);
+ CallCFunction(FUNCTION_ADDR(&CheckStackGuardState), num_arguments);
+ __ or_(eax, Operand(eax));
+ // If returned value is non-zero, the stack guard reports the actual
+ // stack limit being hit and an exception has already been raised.
+ // Otherwise it was a preemption and we just check the limit again.
+ __ j(equal, &retry_stack_check);
+ // Return value was non-zero. Exit with exception.
+ __ mov(eax, EXCEPTION);
+ __ jmp(&exit_label_);
+
+ __ bind(&stack_ok);
+
+ // Allocate space on stack for registers.
+ __ sub(Operand(esp), Immediate(num_registers_ * kPointerSize));
// Load string length.
__ mov(esi, Operand(ebp, kInputEndOffset));
// Load input position.
@@ -617,15 +664,35 @@
__ add(esi, Operand(edx));
if (num_saved_registers_ > 0) {
// Fill saved registers with initial value = start offset - 1
- __ mov(ecx, -num_saved_registers_);
- __ mov(eax, Operand(edi));
- __ sub(Operand(eax), Immediate(char_size()));
+ // Fill in stack push order, to avoid accessing across an unwritten
+ // page (a problem on Windows).
+ __ mov(ecx, kRegisterZero);
+ // Set eax to address of char before start of input
+ // (effectively string position -1).
+ __ lea(eax, Operand(edi, -char_size()));
+ // Store this value in a local variable, for use when clearing
+ // position registers.
+ __ mov(Operand(ebp, kInputStartMinusOne), eax);
Label init_loop;
__ bind(&init_loop);
- __ mov(Operand(ebp, ecx, times_4, +0), eax);
- __ inc(ecx);
- __ j(not_equal, &init_loop);
+ __ mov(Operand(ebp, ecx, times_1, +0), eax);
+ __ sub(Operand(ecx), Immediate(kPointerSize));
+ __ cmp(ecx, kRegisterZero - num_saved_registers_ * kPointerSize);
+ __ j(greater, &init_loop);
}
+ // Ensure that we have written to each stack page, in order. Skipping a page
+ // on Windows can cause segmentation faults. Assuming page size is 4k.
+ const int kPageSize = 4096;
+ const int kRegistersPerPage = kPageSize / kPointerSize;
+ for (int i = num_saved_registers_ + kRegistersPerPage - 1;
+ i < num_registers_;
+ i += kRegistersPerPage) {
+ __ mov(register_location(i), eax); // One write every page.
+ }
+
+
+ // Initialize backtrack stack pointer.
+ __ mov(backtrack_stackpointer(), Operand(ebp, kStackHighEnd));
// Load previous char as initial value of current-character.
Label at_start;
__ cmp(Operand(ebp, kAtStart), Immediate(0));
@@ -639,7 +706,7 @@
// Exit code:
if (success_label_.is_linked()) {
- // Success
+ // Save captures when successful.
__ bind(&success_label_);
if (num_saved_registers_ > 0) {
// copy captures to output
@@ -655,14 +722,18 @@
__ mov(Operand(ebx, i * kPointerSize), eax);
}
}
- __ mov(eax, Immediate(1));
+ __ mov(eax, Immediate(SUCCESS));
}
// Exit and return eax
__ bind(&exit_label_);
- __ leave();
+ // Skip esp past regexp registers.
+ __ lea(esp, Operand(ebp, kBackup_ebx));
+ // Restore callee-save registers.
__ pop(ebx);
__ pop(edi);
__ pop(esi);
+ // Exit function frame, restore previous one.
+ __ pop(ebp);
__ ret(0);
// Backtrack code (branch target for conditional backtracks).
@@ -671,16 +742,16 @@
Backtrack();
}
+ Label exit_with_exception;
+
// Preempt-code
if (check_preempt_label_.is_linked()) {
__ bind(&check_preempt_label_);
- // TODO(lrn): call C function to check the stack guard and return current
- // stack state (0 = ok, positive = out of stack, negative = preempt).
- // Then dispatch to an action depending on state, and loop.
+
+ __ push(backtrack_stackpointer());
__ push(edi);
Label retry;
- Label stack_overflow;
__ bind(&retry);
int num_arguments = 2;
@@ -689,26 +760,57 @@
__ lea(eax, Operand(esp, -kPointerSize));
__ mov(Operand(esp, 0 * kPointerSize), eax);
CallCFunction(FUNCTION_ADDR(&CheckStackGuardState), num_arguments);
+ // Return value must be zero. We cannot have a stack overflow at
+ // this point, since we checked the stack on entry and haven't
+ // pushed anything since, that we haven't also popped again.
ExternalReference stack_guard_limit =
ExternalReference::address_of_stack_guard_limit();
-
- __ or_(eax, Operand(eax));
- __ j(not_equal, &stack_overflow);
-
+ // Check if we are still preempted.
__ cmp(esp, Operand::StaticVariable(stack_guard_limit));
__ j(below_equal, &retry);
__ pop(edi);
+ __ pop(backtrack_stackpointer());
// String might have moved: Recompute esi from scratch.
- __ mov(esi, Operand(esp, kInputBuffer));
+ __ mov(esi, Operand(ebp, kInputBuffer));
__ mov(esi, Operand(esi, 0));
- __ add(esi, Operand(esp, kInputEndOffset));
+ __ add(esi, Operand(ebp, kInputEndOffset));
SafeReturn();
+ }
- __ bind(&stack_overflow);
- // Exit with result -1 to signal thrown exception.
- __ mov(eax, -1);
+ // Backtrack stack overflow code.
+ if (stack_overflow_label_.is_linked()) {
+ __ bind(&stack_overflow_label_);
+ // Reached if the backtrack-stack limit has been hit.
+
+ Label grow_failed;
+ // Save registers before calling C function
+ __ push(esi);
+ __ push(edi);
+
+ // Call GrowStack(backtrack_stackpointer())
+ int num_arguments = 1;
+ FrameAlign(num_arguments);
+ __ mov(Operand(esp, 0), backtrack_stackpointer());
+ CallCFunction(FUNCTION_ADDR(&GrowStack), num_arguments);
+ // If return NULL, we have failed to grow the stack, and
+ // must exit with a stack-overflow exception.
+ __ or_(eax, Operand(eax));
+ __ j(equal, &exit_with_exception);
+ // Otherwise use return value as new stack pointer.
+ __ mov(backtrack_stackpointer(), eax);
+ // Restore saved registers and continue.
+ __ pop(edi);
+ __ pop(esi);
+ SafeReturn();
+ }
+
+ if (exit_with_exception.is_linked()) {
+ // If any of the code above needed to exit with an exception.
+ __ bind(&exit_with_exception);
+ // Exit with Result EXCEPTION(-1) to signal thrown exception.
+ __ mov(eax, EXCEPTION);
__ jmp(&exit_label_);
}
@@ -744,6 +846,13 @@
}
+void RegExpMacroAssemblerIA32::IfRegisterEqPos(int reg,
+ Label* if_eq) {
+ __ cmp(edi, register_location(reg));
+ BranchOrBacktrack(equal, if_eq);
+}
+
+
RegExpMacroAssembler::IrregexpImplementation
RegExpMacroAssemblerIA32::Implementation() {
return kIA32Implementation;
@@ -762,28 +871,32 @@
void RegExpMacroAssemblerIA32::PopCurrentPosition() {
- __ pop(edi);
+ Pop(edi);
}
void RegExpMacroAssemblerIA32::PopRegister(int register_index) {
- __ pop(register_location(register_index));
+ Pop(eax);
+ __ mov(register_location(register_index), eax);
}
void RegExpMacroAssemblerIA32::PushBacktrack(Label* label) {
- __ push(Immediate::CodeRelativeOffset(label));
+ Push(Immediate::CodeRelativeOffset(label));
CheckStackLimit();
}
void RegExpMacroAssemblerIA32::PushCurrentPosition() {
- __ push(edi);
+ Push(edi);
}
-void RegExpMacroAssemblerIA32::PushRegister(int register_index) {
- __ push(register_location(register_index));
+void RegExpMacroAssemblerIA32::PushRegister(int register_index,
+ StackCheckFlag check_stack_limit) {
+ __ mov(eax, register_location(register_index));
+ Push(eax);
+ if (check_stack_limit) CheckStackLimit();
}
@@ -793,7 +906,7 @@
void RegExpMacroAssemblerIA32::ReadStackPointerFromRegister(int reg) {
- __ mov(esp, register_location(reg));
+ __ mov(backtrack_stackpointer(), register_location(reg));
}
@@ -819,8 +932,14 @@
}
+void RegExpMacroAssemblerIA32::ClearRegister(int reg) {
+ __ mov(eax, Operand(ebp, kInputStartMinusOne));
+ __ mov(register_location(reg), eax);
+}
+
+
void RegExpMacroAssemblerIA32::WriteStackPointerToRegister(int reg) {
- __ mov(register_location(reg), esp);
+ __ mov(register_location(reg), backtrack_stackpointer());
}
@@ -830,6 +949,38 @@
static unibrow::Mapping<unibrow::Ecma262Canonicalize> canonicalize;
+RegExpMacroAssemblerIA32::Result RegExpMacroAssemblerIA32::Execute(
+ Code* code,
+ Address* input,
+ int start_offset,
+ int end_offset,
+ int* output,
+ bool at_start) {
+ typedef int (*matcher)(Address*, int, int, int*, int, void*);
+ matcher matcher_func = FUNCTION_CAST<matcher>(code->entry());
+
+ int at_start_val = at_start ? 1 : 0;
+
+ // Ensure that the minimum stack has been allocated.
+ RegExpStack stack;
+ void* stack_top = RegExpStack::stack_top();
+
+ int result = matcher_func(input,
+ start_offset,
+ end_offset,
+ output,
+ at_start_val,
+ stack_top);
+
+ if (result < 0 && !Top::has_pending_exception()) {
+ // We detected a stack overflow in RegExp code, but haven't created
+ // the exception yet.
+ Top::StackOverflow();
+ }
+ return (result < 0) ? EXCEPTION : (result ? SUCCESS : FAILURE);
+}
+
+
int RegExpMacroAssemblerIA32::CaseInsensitiveCompareUC16(uc16** buffer,
int byte_offset1,
int byte_offset2,
@@ -872,11 +1023,10 @@
// Prepare for possible GC.
Handle<Code> code_handle(re_code);
-#ifdef DEBUG
- CHECK(re_code->instruction_start() <= return_address);
- CHECK(return_address <=
+
+ ASSERT(re_code->instruction_start() <= return_address);
+ ASSERT(return_address <=
re_code->instruction_start() + re_code->instruction_size());
-#endif
Object* result = Execution::HandleStackGuardInterrupt();
@@ -892,22 +1042,23 @@
}
+Address RegExpMacroAssemblerIA32::GrowStack(Address stack_top) {
+ size_t size = RegExpStack::stack_capacity();
+ Address old_stack_end = RegExpStack::stack_top();
+ Address new_stack_end = RegExpStack::EnsureCapacity(size * 2);
+ if (new_stack_end == NULL) {
+ return NULL;
+ }
+ return stack_top + (new_stack_end - old_stack_end);
+}
+
+
Operand RegExpMacroAssemblerIA32::register_location(int register_index) {
ASSERT(register_index < (1<<30));
if (num_registers_ <= register_index) {
num_registers_ = register_index + 1;
}
- return Operand(ebp, -(register_index + 1) * kPointerSize);
-}
-
-
-Register RegExpMacroAssemblerIA32::current_character() {
- return edx;
-}
-
-
-size_t RegExpMacroAssemblerIA32::char_size() {
- return static_cast<size_t>(mode_);
+ return Operand(ebp, kRegisterZero - register_index * kPointerSize);
}
@@ -919,7 +1070,8 @@
void RegExpMacroAssemblerIA32::BranchOrBacktrack(Condition condition,
- Label* to) {
+ Label* to,
+ Hint hint) {
if (condition < 0) { // No condition
if (to == NULL) {
Backtrack();
@@ -929,10 +1081,10 @@
return;
}
if (to == NULL) {
- __ j(condition, &backtrack_label_);
+ __ j(condition, &backtrack_label_, hint);
return;
}
- __ j(condition, to);
+ __ j(condition, to, hint);
}
@@ -945,25 +1097,60 @@
void RegExpMacroAssemblerIA32::SafeReturn() {
- __ pop(ecx);
- __ add(Operand(ecx), Immediate(self_));
- __ jmp(Operand(ecx));
+ __ pop(ebx);
+ __ add(Operand(ebx), Immediate(self_));
+ __ jmp(Operand(ebx));
+}
+
+
+void RegExpMacroAssemblerIA32::Push(Register source) {
+ ASSERT(!source.is(backtrack_stackpointer()));
+ // Notice: This updates flags, unlike normal Push.
+ __ sub(Operand(backtrack_stackpointer()), Immediate(kPointerSize));
+ __ mov(Operand(backtrack_stackpointer(), 0), source);
+}
+
+
+void RegExpMacroAssemblerIA32::Push(Immediate value) {
+ // Notice: This updates flags, unlike normal Push.
+ __ sub(Operand(backtrack_stackpointer()), Immediate(kPointerSize));
+ __ mov(Operand(backtrack_stackpointer(), 0), value);
+}
+
+
+void RegExpMacroAssemblerIA32::Pop(Register target) {
+ ASSERT(!target.is(backtrack_stackpointer()));
+ __ mov(target, Operand(backtrack_stackpointer(), 0));
+ // Notice: This updates flags, unlike normal Pop.
+ __ add(Operand(backtrack_stackpointer()), Immediate(kPointerSize));
+}
+
+
+void RegExpMacroAssemblerIA32::CheckPreemption() {
+ // Check for preemption.
+ Label no_preempt;
+ ExternalReference stack_guard_limit =
+ ExternalReference::address_of_stack_guard_limit();
+ __ cmp(esp, Operand::StaticVariable(stack_guard_limit));
+ __ j(above, &no_preempt, taken);
+
+ SafeCall(&check_preempt_label_);
+
+ __ bind(&no_preempt);
}
void RegExpMacroAssemblerIA32::CheckStackLimit() {
if (FLAG_check_stack) {
- // Check for preemption first.
- Label no_preempt;
- // Check for preemption.
- ExternalReference stack_guard_limit =
- ExternalReference::address_of_stack_guard_limit();
- __ cmp(esp, Operand::StaticVariable(stack_guard_limit));
- __ j(above, &no_preempt, taken);
+ Label no_stack_overflow;
+ ExternalReference stack_limit =
+ ExternalReference::address_of_regexp_stack_limit();
+ __ cmp(backtrack_stackpointer(), Operand::StaticVariable(stack_limit));
+ __ j(above, &no_stack_overflow);
- SafeCall(&check_preempt_label_);
+ SafeCall(&stack_overflow_label_);
- __ bind(&no_preempt);
+ __ bind(&no_stack_overflow);
}
}
diff --git a/src/regexp-macro-assembler-ia32.h b/src/regexp-macro-assembler-ia32.h
index ecf0326..8d28beb 100644
--- a/src/regexp-macro-assembler-ia32.h
+++ b/src/regexp-macro-assembler-ia32.h
@@ -38,6 +38,7 @@
RegExpMacroAssemblerIA32(Mode mode, int registers_to_save);
virtual ~RegExpMacroAssemblerIA32();
+ virtual int stack_limit_slack();
virtual void AdvanceCurrentPosition(int by);
virtual void AdvanceRegister(int reg, int by);
virtual void Backtrack();
@@ -53,6 +54,8 @@
int cp_offset,
Label* on_failure,
bool check_end_of_string);
+ // A "greedy loop" is a loop that is both greedy and with a simple
+ // body. It has a particularly simple implementation.
virtual void CheckGreedyLoop(Label* on_tos_equals_current_position);
virtual void CheckNotAtStart(Label* on_not_at_start);
virtual void CheckNotBackReference(int start_reg, Label* on_no_match);
@@ -86,6 +89,7 @@
virtual void GoTo(Label* label);
virtual void IfRegisterGE(int reg, int comparand, Label* if_ge);
virtual void IfRegisterLT(int reg, int comparand, Label* if_lt);
+ virtual void IfRegisterEqPos(int reg, Label* if_eq);
virtual IrregexpImplementation Implementation();
virtual void LoadCurrentCharacter(int cp_offset,
Label* on_end_of_input,
@@ -95,61 +99,64 @@
virtual void PopRegister(int register_index);
virtual void PushBacktrack(Label* label);
virtual void PushCurrentPosition();
- virtual void PushRegister(int register_index);
+ virtual void PushRegister(int register_index,
+ StackCheckFlag check_stack_limit);
virtual void ReadCurrentPositionFromRegister(int reg);
virtual void ReadStackPointerFromRegister(int reg);
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 WriteStackPointerToRegister(int reg);
- template <typename T>
- static inline Result Execute(Code* code,
- T** input,
- int start_offset,
- int end_offset,
- int* output,
- bool at_start) {
- typedef int (*matcher)(T**, int, int, int*, int);
- matcher matcher_func = FUNCTION_CAST<matcher>(code->entry());
- int at_start_val = at_start ? 1 : 0;
- int result = matcher_func(input,
- start_offset,
- end_offset,
- output,
- at_start_val);
- return (result < 0) ? EXCEPTION : (result ? SUCCESS : FAILURE);
- }
+ static Result Execute(Code* code,
+ Address* input,
+ int start_offset,
+ int end_offset,
+ int* output,
+ bool at_start);
private:
- // Offsets from ebp of arguments to function.
- static const int kBackup_ebx = sizeof(uint32_t);
- static const int kBackup_edi = kBackup_ebx + sizeof(uint32_t);
- static const int kBackup_esi = kBackup_edi + sizeof(uint32_t);
- static const int kReturn_eip = kBackup_esi + sizeof(uint32_t);
- static const int kInputBuffer = kReturn_eip + sizeof(uint32_t);
- static const int kInputStartOffset = kInputBuffer + sizeof(uint32_t);
- static const int kInputEndOffset = kInputStartOffset + sizeof(uint32_t);
- static const int kRegisterOutput = kInputEndOffset + sizeof(uint32_t);
- static const int kAtStart = kRegisterOutput + sizeof(uint32_t);
+ // Offsets from ebp of function parameters and stored registers.
+ static const int kFramePointer = 0;
+ // Above the frame pointer - function parameters and return address.
+ static const int kReturn_eip = kFramePointer + kPointerSize;
+ static const int kInputBuffer = kReturn_eip + kPointerSize;
+ static const int kInputStartOffset = kInputBuffer + kPointerSize;
+ static const int kInputEndOffset = kInputStartOffset + kPointerSize;
+ static const int kRegisterOutput = kInputEndOffset + kPointerSize;
+ static const int kAtStart = kRegisterOutput + kPointerSize;
+ static const int kStackHighEnd = kAtStart + kPointerSize;
+ // Below the frame pointer - local stack variables.
+ // When adding local variables remember to push space for them in
+ // the frame in GetCode.
+ static const int kBackup_esi = kFramePointer - kPointerSize;
+ static const int kBackup_edi = kBackup_esi - kPointerSize;
+ static const int kBackup_ebx = kBackup_edi - kPointerSize;
+ static const int kInputStartMinusOne = kBackup_ebx - kPointerSize;
+ // First register address. Following registers are below it on the stack.
+ static const int kRegisterZero = kInputStartMinusOne - kPointerSize;
// Initial size of code buffer.
static const size_t kRegExpCodeSize = 1024;
// Initial size of constant buffers allocated during compilation.
static const int kRegExpConstantsSize = 256;
- // Only unroll loops up to this length. TODO(lrn): Actually use this.
- static const int kMaxInlineStringTests = 32;
// Compares two-byte strings case insensitively.
+ // Called from generated RegExp code.
static int CaseInsensitiveCompareUC16(uc16** buffer,
int byte_offset1,
int byte_offset2,
size_t byte_length);
- void LoadCurrentCharacterUnchecked(int cp_offset, int characters);
+ // Load a number of characters at the given offset from the
+ // current position, into the current-character register.
+ void LoadCurrentCharacterUnchecked(int cp_offset, int character_count);
- // Adds code that checks whether preemption has been requested
- // (and checks if we have hit the stack limit too).
+ // Check whether preemption has been requested.
+ void CheckPreemption();
+
+ // Check whether we are exceeding the stack limit on the backtrack stack.
void CheckStackLimit();
// Called from RegExp if the stack-guard is triggered.
@@ -157,6 +164,12 @@
// returning.
static int CheckStackGuardState(Address return_address, Code* re_code);
+ // Called from RegExp if the backtrack stack limit is hit.
+ // Tries to expand the stack. Returns the new stack-top pointer if
+ // successful, or 0 if unable to grow the stack.
+ // 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);
@@ -165,14 +178,18 @@
Operand register_location(int register_index);
// The register containing the current character after LoadCurrentCharacter.
- Register current_character();
+ inline Register current_character() { return edx; }
+
+ // The register containing the backtrack stack top. Provides a meaningful
+ // name to the register.
+ inline Register backtrack_stackpointer() { return ecx; }
// Byte size of chars in the string to match (decided by the Mode argument)
- size_t char_size();
+ inline int char_size() { return static_cast<int>(mode_); }
// Equivalent to a conditional branch to the label, unless the label
// is NULL, in which case it is a conditional Backtrack.
- void BranchOrBacktrack(Condition condition, Label* to);
+ void BranchOrBacktrack(Condition condition, Label* to, Hint hint = no_hint);
// Load the address of a "constant buffer" (a slice of a byte array)
// into a register. The address is computed from the ByteArray* address
@@ -181,30 +198,50 @@
// Call and return internally in the generated code in a way that
// is GC-safe (i.e., doesn't leave absolute code addresses on the stack)
- void SafeCall(Label* to);
- void SafeReturn();
+ inline void SafeCall(Label* to);
+ inline void SafeReturn();
+
+ // Pushes the value of a register on the backtrack stack. Decrements the
+ // stack pointer (ecx) by a word size and stores the register's value there.
+ inline void Push(Register source);
+
+ // Pushes a value on the backtrack stack. Decrements the stack pointer (ecx)
+ // by a word size and stores the value there.
+ inline void Push(Immediate value);
+
+ // Pops a value from the backtrack stack. Reads the word at the stack pointer
+ // (ecx) and increments it by a word size.
+ inline void Pop(Register target);
// Before calling a C-function from generated code, align arguments on stack.
// After aligning the frame, arguments must be stored in esp[0], esp[4],
// etc., not pushed. The argument count assumes all arguments are word sized.
- void FrameAlign(int num_arguments);
+ // Some compilers/platforms require the stack to be aligned when calling
+ // C++ code.
+ inline void FrameAlign(int num_arguments);
+
// Calls a C function and cleans up the space for arguments allocated
// by FrameAlign. The called function is not allowed to trigger a garbage
// collection, since that might move the code and invalidate the return
- // address
- void CallCFunction(Address function_address, int num_arguments);
+ // address (unless this is somehow accounted for).
+ inline void CallCFunction(Address function_address, int num_arguments);
MacroAssembler* masm_;
+
// Constant buffer provider. Allocates external storage for storing
// constants.
ByteArrayProvider constants_;
- // Which mode to generate code for (ASCII or UTF16).
+
+ // Which mode to generate code for (ASCII or UC16).
Mode mode_;
+
// One greater than maximal register index actually used.
int num_registers_;
+
// Number of registers to output at the end (the saved registers
// are always 0..num_saved_registers_-1)
int num_saved_registers_;
+
// Labels used internally.
Label entry_label_;
Label start_label_;
@@ -212,6 +249,8 @@
Label backtrack_label_;
Label exit_label_;
Label check_preempt_label_;
+ Label stack_overflow_label_;
+
// Handle used to represent the generated code object itself.
Handle<Object> self_;
};
diff --git a/src/regexp-macro-assembler-irregexp.cc b/src/regexp-macro-assembler-irregexp.cc
index cfcae32..a36f0a7 100644
--- a/src/regexp-macro-assembler-irregexp.cc
+++ b/src/regexp-macro-assembler-irregexp.cc
@@ -89,7 +89,9 @@
}
-void RegExpMacroAssemblerIrregexp::PushRegister(int register_index) {
+void RegExpMacroAssemblerIrregexp::PushRegister(
+ int register_index,
+ StackCheckFlag check_stack_limit) {
ASSERT(register_index >= 0);
Emit(BC_PUSH_REGISTER);
Emit(register_index);
@@ -105,6 +107,11 @@
}
+void RegExpMacroAssemblerIrregexp::ClearRegister(int reg) {
+ SetRegister(reg, -1);
+}
+
+
void RegExpMacroAssemblerIrregexp::ReadCurrentPositionFromRegister(
int register_index) {
ASSERT(register_index >= 0);
@@ -401,6 +408,14 @@
}
+void RegExpMacroAssemblerIrregexp::IfRegisterEqPos(int register_index,
+ Label* on_eq) {
+ Emit(BC_CHECK_REGISTER_EQ_POS);
+ Emit(register_index);
+ EmitOrLink(on_eq);
+}
+
+
Handle<Object> RegExpMacroAssemblerIrregexp::GetCode(Handle<String> source) {
Bind(&backtrack_);
Emit(BC_POP_BT);
diff --git a/src/regexp-macro-assembler-irregexp.h b/src/regexp-macro-assembler-irregexp.h
index 871e1e5..95b903c 100644
--- a/src/regexp-macro-assembler-irregexp.h
+++ b/src/regexp-macro-assembler-irregexp.h
@@ -48,6 +48,8 @@
// upon destruction of the assembler.
explicit RegExpMacroAssemblerIrregexp(Vector<byte>);
virtual ~RegExpMacroAssemblerIrregexp();
+ // The byte-code interpreter checks on each push anyway.
+ virtual int stack_limit_slack() { return 1; }
virtual void Bind(Label* label);
virtual void EmitOrLink(Label* label);
virtual void AdvanceCurrentPosition(int by); // Signed cp change.
@@ -59,10 +61,12 @@
virtual void Succeed();
virtual void Fail();
virtual void PopRegister(int register_index);
- virtual void PushRegister(int register_index);
+ virtual void PushRegister(int register_index,
+ StackCheckFlag check_stack_limit);
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 ReadCurrentPositionFromRegister(int reg);
virtual void WriteStackPointerToRegister(int reg);
virtual void ReadStackPointerFromRegister(int reg);
@@ -106,6 +110,7 @@
const Vector<Label*>& destinations);
virtual void IfRegisterLT(int register_index, int comparand, Label* if_lt);
virtual void IfRegisterGE(int register_index, int comparand, Label* if_ge);
+ virtual void IfRegisterEqPos(int register_index, Label* if_eq);
virtual IrregexpImplementation Implementation();
virtual Handle<Object> GetCode(Handle<String> source);
diff --git a/src/regexp-macro-assembler-tracer.cc b/src/regexp-macro-assembler-tracer.cc
index 94ede51..5caf032 100644
--- a/src/regexp-macro-assembler-tracer.cc
+++ b/src/regexp-macro-assembler-tracer.cc
@@ -95,7 +95,8 @@
void RegExpMacroAssemblerTracer::PushBacktrack(Label* label) {
- PrintF(" PushBacktrack(label[%08x]);\n", label);
+ PrintF(" PushBacktrack(label[%08x]);\n",
+ label);
assembler_->PushBacktrack(label);
}
@@ -118,9 +119,13 @@
}
-void RegExpMacroAssemblerTracer::PushRegister(int register_index) {
- PrintF(" PushRegister(register=%d);\n", register_index);
- assembler_->PushRegister(register_index);
+void RegExpMacroAssemblerTracer::PushRegister(
+ int register_index,
+ StackCheckFlag check_stack_limit) {
+ PrintF(" PushRegister(register=%d, %s);\n",
+ register_index,
+ check_stack_limit ? "check stack limit" : "");
+ assembler_->PushRegister(register_index, check_stack_limit);
}
@@ -145,6 +150,12 @@
}
+void RegExpMacroAssemblerTracer::ClearRegister(int reg) {
+ PrintF(" ClearRegister(register=%d);\n", reg);
+ assembler_->ClearRegister(reg);
+}
+
+
void RegExpMacroAssemblerTracer::ReadCurrentPositionFromRegister(int reg) {
PrintF(" ReadCurrentPositionFromRegister(register=%d);\n", reg);
assembler_->ReadCurrentPositionFromRegister(reg);
@@ -373,6 +384,14 @@
}
+void RegExpMacroAssemblerTracer::IfRegisterEqPos(int register_index,
+ Label* if_eq) {
+ PrintF(" IfRegisterEqPos(register=%d, label[%08x]);\n",
+ register_index, if_eq);
+ assembler_->IfRegisterEqPos(register_index, if_eq);
+}
+
+
void RegExpMacroAssemblerTracer::IfRegisterGE(int register_index,
int comparand, Label* if_ge) {
PrintF(" IfRegisterGE(register=%d, number=%d, label[%08x]);\n",
diff --git a/src/regexp-macro-assembler-tracer.h b/src/regexp-macro-assembler-tracer.h
index 0f3eb3f..c8088a9 100644
--- a/src/regexp-macro-assembler-tracer.h
+++ b/src/regexp-macro-assembler-tracer.h
@@ -35,6 +35,7 @@
public:
explicit RegExpMacroAssemblerTracer(RegExpMacroAssembler* assembler);
virtual ~RegExpMacroAssemblerTracer();
+ virtual int stack_limit_slack() { return assembler_->stack_limit_slack(); }
virtual void AdvanceCurrentPosition(int by); // Signed cp change.
virtual void AdvanceRegister(int reg, int by); // r[reg] += by.
@@ -88,6 +89,7 @@
virtual void GoTo(Label* label);
virtual void IfRegisterGE(int reg, int comparand, Label* if_ge);
virtual void IfRegisterLT(int reg, int comparand, Label* if_lt);
+ virtual void IfRegisterEqPos(int reg, Label* if_eq);
virtual IrregexpImplementation Implementation();
virtual void LoadCurrentCharacter(int cp_offset,
Label* on_end_of_input,
@@ -97,12 +99,14 @@
virtual void PopRegister(int register_index);
virtual void PushBacktrack(Label* label);
virtual void PushCurrentPosition();
- virtual void PushRegister(int register_index);
+ virtual void PushRegister(int register_index,
+ StackCheckFlag check_stack_limit);
virtual void ReadCurrentPositionFromRegister(int reg);
virtual void ReadStackPointerFromRegister(int reg);
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 WriteStackPointerToRegister(int reg);
private:
RegExpMacroAssembler* assembler_;
diff --git a/src/regexp-macro-assembler.cc b/src/regexp-macro-assembler.cc
index 34925f5..913ac64 100644
--- a/src/regexp-macro-assembler.cc
+++ b/src/regexp-macro-assembler.cc
@@ -68,10 +68,11 @@
return ArraySlice(current_byte_array_, free_offset);
}
+
template <typename T>
ArraySlice ByteArrayProvider::GetBuffer(Vector<T> values) {
ArraySlice slice = GetBuffer(values.length(), sizeof(T));
memcpy(slice.location(), values.start(), values.length() * sizeof(T));
return slice;
}
-} }
+} } // namespace v8::internal
diff --git a/src/regexp-macro-assembler.h b/src/regexp-macro-assembler.h
index 9d3ce5f..e9f7731 100644
--- a/src/regexp-macro-assembler.h
+++ b/src/regexp-macro-assembler.h
@@ -30,7 +30,6 @@
namespace v8 { namespace internal {
-
struct DisjunctDecisionRow {
RegExpCharacterClass cc;
Label* on_match;
@@ -42,12 +41,24 @@
enum IrregexpImplementation {
kIA32Implementation,
kARMImplementation,
- kBytecodeImplementation};
+ kBytecodeImplementation
+ };
+
+ enum StackCheckFlag {
+ kNoStackLimitCheck = false,
+ kCheckStackLimit = true
+ };
RegExpMacroAssembler();
virtual ~RegExpMacroAssembler();
+ // The maximal number of pushes between stack checks. Users must supply
+ // kCheckStackLimit flag to push operations (instead of kNoStackLimitCheck)
+ // at least once for every stack_limit() pushes that are executed.
+ virtual int stack_limit_slack() = 0;
virtual void AdvanceCurrentPosition(int by) = 0; // Signed cp change.
virtual void AdvanceRegister(int reg, int by) = 0; // r[reg] += by.
+ // Continues execution from the position pushed on the top of the backtrack
+ // stack by an earlier PushBacktrack(Label*).
virtual void Backtrack() = 0;
virtual void Bind(Label* label) = 0;
// Check the current character against a bitmap. The range of the current
@@ -135,6 +146,9 @@
// Check whether a register is < a given constant and go to a label if it is.
// Backtracks instead if the label is NULL.
virtual void IfRegisterLT(int reg, int comparand, Label* if_lt) = 0;
+ // Check whether a register is == to the current position and go to a
+ // label if it is.
+ virtual void IfRegisterEqPos(int reg, Label* if_eq) = 0;
virtual IrregexpImplementation Implementation() = 0;
virtual void LoadCurrentCharacter(int cp_offset,
Label* on_end_of_input,
@@ -142,14 +156,18 @@
int characters = 1) = 0;
virtual void PopCurrentPosition() = 0;
virtual void PopRegister(int register_index) = 0;
+ // Pushes the label on the backtrack stack, so that a following Backtrack
+ // will go to this label. Always checks the backtrack stack limit.
virtual void PushBacktrack(Label* label) = 0;
virtual void PushCurrentPosition() = 0;
- virtual void PushRegister(int register_index) = 0;
+ virtual void PushRegister(int register_index,
+ StackCheckFlag check_stack_limit) = 0;
virtual void ReadCurrentPositionFromRegister(int reg) = 0;
virtual void ReadStackPointerFromRegister(int reg) = 0;
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 WriteStackPointerToRegister(int reg) = 0;
private:
diff --git a/src/regexp-stack.cc b/src/regexp-stack.cc
new file mode 100644
index 0000000..05daa58
--- /dev/null
+++ b/src/regexp-stack.cc
@@ -0,0 +1,94 @@
+// 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.
+
+#include "v8.h"
+#include "top.h"
+#include "regexp-stack.h"
+
+namespace v8 { namespace internal {
+
+RegExpStack::RegExpStack() {
+ // Initialize, if not already initialized.
+ RegExpStack::EnsureCapacity(0);
+}
+
+
+RegExpStack::~RegExpStack() {
+ // Reset the buffer if it has grown.
+ RegExpStack::Reset();
+}
+
+
+char* RegExpStack::ArchiveStack(char* to) {
+ size_t size = sizeof(thread_local_);
+ memcpy(reinterpret_cast<void*>(to),
+ &thread_local_,
+ size);
+ thread_local_ = ThreadLocal();
+ return to + size;
+}
+
+
+char* RegExpStack::RestoreStack(char* from) {
+ size_t size = sizeof(thread_local_);
+ memcpy(&thread_local_, reinterpret_cast<void*>(from), size);
+ return from + size;
+}
+
+
+void RegExpStack::Reset() {
+ if (thread_local_.memory_size_ > kMinimumStackSize) {
+ DeleteArray(thread_local_.memory_);
+ thread_local_ = ThreadLocal();
+ }
+}
+
+
+Address RegExpStack::EnsureCapacity(size_t size) {
+ if (size > kMaximumStackSize) return NULL;
+ if (size < kMinimumStackSize) size = kMinimumStackSize;
+ if (thread_local_.memory_size_ < size) {
+ Address new_memory = NewArray<byte>(size);
+ if (thread_local_.memory_size_ > 0) {
+ // Copy original memory into top of new memory.
+ memcpy(reinterpret_cast<void*>(
+ new_memory + size - thread_local_.memory_size_),
+ reinterpret_cast<void*>(thread_local_.memory_),
+ thread_local_.memory_size_);
+ DeleteArray(thread_local_.memory_);
+ }
+ thread_local_.memory_ = new_memory;
+ thread_local_.memory_size_ = size;
+ thread_local_.limit_ = new_memory + kStackLimitSlack * kPointerSize;
+ }
+ return thread_local_.memory_ + thread_local_.memory_size_;
+}
+
+
+RegExpStack::ThreadLocal RegExpStack::thread_local_;
+
+}} // namespace v8::internal
diff --git a/src/regexp-stack.h b/src/regexp-stack.h
new file mode 100644
index 0000000..43f8f3d
--- /dev/null
+++ b/src/regexp-stack.h
@@ -0,0 +1,106 @@
+// 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 REGEXP_STACK_H_
+#define REGEXP_STACK_H_
+
+namespace v8 { namespace internal {
+
+// Maintains a per-v8thread stack area that can be used by irregexp
+// implementation for its backtracking stack.
+// Since there is only one stack area, the Irregexp implementation is not
+// re-entrant. I.e., no regular expressions may be executed in the same thread
+// during a preempted Irregexp execution.
+class RegExpStack {
+ public:
+ // Number of allocated locations on the stack below the limit.
+ // No sequence of pushes must be longer that this without doing a stack-limit
+ // check.
+ static const int kStackLimitSlack = 32;
+
+ // Create and delete an instance to control the life-time of a growing stack.
+ RegExpStack(); // Initializes the stack memory area if necessary.
+ ~RegExpStack(); // Releases the stack if it has grown.
+
+ // Gives the top of the memory used as stack.
+ static Address stack_top() {
+ ASSERT(thread_local_.memory_size_ != 0);
+ return thread_local_.memory_ + thread_local_.memory_size_;
+ }
+
+ // The total size of the memory allocated for the stack.
+ static size_t stack_capacity() { return thread_local_.memory_size_; }
+
+ // If the stack pointer gets below the limit, we should react and
+ // either grow the stack or report an out-of-stack exception.
+ // There is only a limited number of locations below the stack limit,
+ // so users of the stack should check the stack limit during any
+ // sequence of pushes longer that this.
+ static Address* limit_address() { return &(thread_local_.limit_); }
+
+ // Ensures that there is a memory area with at least the specified size.
+ // If passing zero, the default/minimum size buffer is allocated.
+ static Address EnsureCapacity(size_t size);
+
+ // Thread local archiving.
+ static size_t ArchiveSpacePerThread() { return sizeof(thread_local_); }
+ static char* ArchiveStack(char* to);
+ static char* RestoreStack(char* from);
+
+ private:
+ // Artificial limit used when no memory has been allocated.
+ static const uint32_t kMemoryTop = 0xffffffff;
+
+ // Minimal size of allocated stack area.
+ static const size_t kMinimumStackSize = 1 * KB;
+
+ // Maximal size of allocated stack area.
+ static const size_t kMaximumStackSize = 256 * KB;
+
+ // Structure holding the allocated memory, size and limit.
+ struct ThreadLocal {
+ ThreadLocal()
+ : memory_(NULL),
+ memory_size_(0),
+ limit_(reinterpret_cast<Address>(kMemoryTop)) {}
+ // If memory_size_ > 0 then memory_ must be non-NULL.
+ Address memory_;
+ size_t memory_size_;
+ Address limit_;
+ };
+
+ // Resets the buffer if it has grown beyond the default/minimum size.
+ // After this, the buffer is either the default size, or it is empty, so
+ // you have to call EnsureCapacity before using it again.
+ static void Reset();
+
+ static ThreadLocal thread_local_;
+};
+
+}} // namespace v8::internal
+
+#endif /* REGEXP_STACK_H_ */
diff --git a/src/rewriter.cc b/src/rewriter.cc
index f0d53b8..4468066 100644
--- a/src/rewriter.cc
+++ b/src/rewriter.cc
@@ -243,6 +243,12 @@
}
+void AstOptimizer::VisitCatchExtensionObject(CatchExtensionObject* node) {
+ Visit(node->key());
+ Visit(node->value());
+}
+
+
void AstOptimizer::VisitAssignment(Assignment* node) {
switch (node->op()) {
case Token::INIT_VAR:
@@ -282,7 +288,7 @@
case Token::INIT_VAR:
case Token::INIT_CONST:
case Token::ASSIGN:
- // Pure assigment copies the type from the value.
+ // Pure assignment copies the type from the value.
node->type()->CopyFrom(node->value()->type());
break;
case Token::ASSIGN_BIT_OR:
@@ -420,7 +426,7 @@
node->type()->SetAsLikelySmi();
}
if (node->type()->IsLikelySmi()) {
- // The type of this node changed to LIKELY_SMI. Propagate this knowlege
+ // The type of this node changed to LIKELY_SMI. Propagate this knowledge
// down through the nodes.
if (node->left()->type()->IsUnknown()) {
node->left()->type()->SetAsLikelySmi();
@@ -455,7 +461,7 @@
node->type()->SetAsLikelySmi();
}
if (node->type()->IsLikelySmi()) {
- // The type of this node changed to LIKELY_SMI. Propagate this knowlege
+ // The type of this node changed to LIKELY_SMI. Propagate this knowledge
// down through the nodes.
if (node->left()->type()->IsUnknown()) {
node->left()->type()->SetAsLikelySmi();
@@ -684,6 +690,12 @@
}
+void Processor::VisitCatchExtensionObject(CatchExtensionObject* node) {
+ USE(node);
+ UNREACHABLE();
+}
+
+
void Processor::VisitAssignment(Assignment* node) {
USE(node);
UNREACHABLE();
diff --git a/src/runtime.cc b/src/runtime.cc
index 931c2a0..36fae9d 100644
--- a/src/runtime.cc
+++ b/src/runtime.cc
@@ -102,9 +102,9 @@
Handle<Context> context,
Handle<FixedArray> constant_properties,
bool* is_result_from_cache) {
+ int number_of_properties = constant_properties->length() / 2;
if (FLAG_canonicalize_object_literal_maps) {
// First find prefix of consecutive symbol keys.
- int number_of_properties = constant_properties->length()/2;
int number_of_symbol_keys = 0;
while ((number_of_symbol_keys < number_of_properties) &&
(constant_properties->get(number_of_symbol_keys*2)->IsSymbol())) {
@@ -125,7 +125,9 @@
}
}
*is_result_from_cache = false;
- return Handle<Map>(context->object_function()->initial_map());
+ return Factory::CopyMap(
+ Handle<Map>(context->object_function()->initial_map()),
+ number_of_properties);
}
@@ -152,7 +154,7 @@
&is_result_from_cache);
Handle<JSObject> boilerplate = Factory::NewJSObjectFromMap(map);
- { // Add the constant propeties to the boilerplate.
+ { // Add the constant properties to the boilerplate.
int length = constant_properties->length();
OptimizedObjectForAddingMultipleProperties opt(boilerplate,
!is_result_from_cache);
@@ -213,6 +215,23 @@
}
+static Object* Runtime_CreateCatchExtensionObject(Arguments args) {
+ ASSERT(args.length() == 2);
+ CONVERT_CHECKED(String, key, args[0]);
+ Object* value = args[1];
+ // Create a catch context extension object.
+ JSFunction* constructor =
+ Top::context()->global_context()->context_extension_function();
+ Object* object = Heap::AllocateJSObject(constructor);
+ if (object->IsFailure()) return object;
+ // Assign the exception value to the catch variable and make sure
+ // that the catch variable is DontDelete.
+ value = JSObject::cast(object)->SetProperty(key, value, DONT_DELETE);
+ if (value->IsFailure()) return value;
+ return object;
+}
+
+
static Object* Runtime_ClassOf(Arguments args) {
NoHandleAllocation ha;
ASSERT(args.length() == 1);
@@ -341,8 +360,16 @@
static Object* Runtime_DisableAccessChecks(Arguments args) {
ASSERT(args.length() == 1);
CONVERT_CHECKED(HeapObject, object, args[0]);
- bool needs_access_checks = object->map()->is_access_check_needed();
- object->map()->set_is_access_check_needed(false);
+ Map* old_map = object->map();
+ bool needs_access_checks = old_map->is_access_check_needed();
+ if (needs_access_checks) {
+ // Copy map so it won't interfere constructor's initial map.
+ Object* new_map = old_map->CopyDropTransitions();
+ if (new_map->IsFailure()) return new_map;
+
+ Map::cast(new_map)->set_is_access_check_needed(false);
+ object->set_map(Map::cast(new_map));
+ }
return needs_access_checks ? Heap::true_value() : Heap::false_value();
}
@@ -350,7 +377,15 @@
static Object* Runtime_EnableAccessChecks(Arguments args) {
ASSERT(args.length() == 1);
CONVERT_CHECKED(HeapObject, object, args[0]);
- object->map()->set_is_access_check_needed(true);
+ Map* old_map = object->map();
+ if (!old_map->is_access_check_needed()) {
+ // Copy map so it won't interfere constructor's initial map.
+ Object* new_map = old_map->CopyDropTransitions();
+ if (new_map->IsFailure()) return new_map;
+
+ Map::cast(new_map)->set_is_access_check_needed(true);
+ object->set_map(Map::cast(new_map));
+ }
return Heap::undefined_value();
}
@@ -1717,7 +1752,7 @@
name = Handle<String>::cast(converted);
}
- // Check if the name is trivially convertable to an index and get
+ // Check if the name is trivially convertible to an index and get
// the element if so.
if (name->AsArrayIndex(&index)) {
return GetElementOrCharAt(object, index);
@@ -1749,7 +1784,7 @@
// itself.
//
// The global proxy objects has to be excluded since LocalLookup on
- // the global proxy object can return a valid result eventhough the
+ // the global proxy object can return a valid result even though the
// global proxy object never has properties. This is the case
// because the global proxy object forwards everything to its hidden
// prototype including local lookups.
@@ -2985,7 +3020,7 @@
// same as the lexicographic order of the string representations.
if (x_value == 0 || y_value == 0) return Smi::FromInt(x_value - y_value);
- // If only one of the intergers is negative the negative number is
+ // If only one of the integers is negative the negative number is
// smallest because the char code of '-' is less than the char code
// of any digit. Otherwise, we make both values positive.
if (x_value < 0 || y_value < 0) {
@@ -3323,7 +3358,7 @@
if (constructor->IsJSFunction()) {
JSFunction* function = JSFunction::cast(constructor);
- // Handle steping into constructors if step into is active.
+ // Handle stepping into constructors if step into is active.
if (Debug::StepInActive()) {
HandleScope scope;
Debug::HandleStepIn(Handle<JSFunction>(function), 0, true);
@@ -4501,30 +4536,32 @@
}
-static Object* DebugLookupResultValue(LookupResult* result) {
- Object* value;
+static Object* DebugLookupResultValue(Object* obj, String* name,
+ LookupResult* result,
+ bool* caught_exception) {
switch (result->type()) {
- case NORMAL: {
- Dictionary* dict =
- JSObject::cast(result->holder())->property_dictionary();
- value = dict->ValueAt(result->GetDictionaryEntry());
- if (value->IsTheHole()) {
- return Heap::undefined_value();
+ case NORMAL:
+ case FIELD:
+ case CONSTANT_FUNCTION:
+ return obj->GetProperty(name);
+ case CALLBACKS: {
+ // Get the property value. If there is an exception it must be thrown from
+ // a JavaScript getter.
+ Object* value;
+ value = obj->GetProperty(name);
+ if (value->IsException()) {
+ if (caught_exception != NULL) {
+ *caught_exception = true;
+ }
+ value = Top::pending_exception();
+ Top::optional_reschedule_exception(true);
}
+ ASSERT(!Top::has_pending_exception());
+ ASSERT(!Top::external_caught_exception());
return value;
}
- case FIELD:
- value =
- JSObject::cast(
- result->holder())->FastPropertyAt(result->GetFieldIndex());
- if (value->IsTheHole()) {
- return Heap::undefined_value();
- }
- return value;
- case CONSTANT_FUNCTION:
- return result->GetConstantFunction();
- case CALLBACKS:
case INTERCEPTOR:
+ return obj->GetProperty(name);
case MAP_TRANSITION:
case CONSTANT_TRANSITION:
case NULL_DESCRIPTOR:
@@ -4537,6 +4574,18 @@
}
+// Get debugger related details for an object property.
+// args[0]: object holding property
+// args[1]: name of the property
+//
+// The array returned contains the following information:
+// 0: Property value
+// 1: Property details
+// 2: Property value is exception
+// 3: Getter function if defined
+// 4: Setter function if defined
+// Items 2-4 are only filled if the property has either a getter or a setter
+// defined through __defineGetter__ and/or __defineSetter__.
static Object* Runtime_DebugGetPropertyDetails(Arguments args) {
HandleScope scope;
@@ -4557,12 +4606,26 @@
// Perform standard local lookup on the object.
LookupResult result;
- obj->Lookup(*name, &result);
+ obj->LocalLookup(*name, &result);
if (result.IsProperty()) {
- Handle<Object> value(DebugLookupResultValue(&result));
- Handle<FixedArray> details = Factory::NewFixedArray(2);
+ bool caught_exception = false;
+ Handle<Object> value(DebugLookupResultValue(*obj, *name, &result,
+ &caught_exception));
+ // If the callback object is a fixed array then it contains JavaScript
+ // getter and/or setter.
+ bool hasJavaScriptAccessors = result.type() == CALLBACKS &&
+ result.GetCallbackObject()->IsFixedArray();
+ Handle<FixedArray> details =
+ Factory::NewFixedArray(hasJavaScriptAccessors ? 5 : 2);
details->set(0, *value);
details->set(1, result.GetPropertyDetails().AsSmi());
+ if (hasJavaScriptAccessors) {
+ details->set(2,
+ caught_exception ? Heap::true_value() : Heap::false_value());
+ details->set(3, FixedArray::cast(result.GetCallbackObject())->get(0));
+ details->set(4, FixedArray::cast(result.GetCallbackObject())->get(1));
+ }
+
return *Factory::NewJSArrayWithElements(details);
}
return Heap::undefined_value();
@@ -4580,7 +4643,7 @@
LookupResult result;
obj->Lookup(*name, &result);
if (result.IsProperty()) {
- return DebugLookupResultValue(&result);
+ return DebugLookupResultValue(*obj, *name, &result, NULL);
}
return Heap::undefined_value();
}
@@ -4674,10 +4737,11 @@
HandleScope scope;
ASSERT(args.length() == 1);
CONVERT_ARG_CHECKED(JSObject, obj, 0);
- RUNTIME_ASSERT(obj->HasNamedInterceptor());
- v8::Handle<v8::Array> result = GetKeysForNamedInterceptor(obj, obj);
- if (!result.IsEmpty()) return *v8::Utils::OpenHandle(*result);
+ if (obj->HasNamedInterceptor()) {
+ v8::Handle<v8::Array> result = GetKeysForNamedInterceptor(obj, obj);
+ if (!result.IsEmpty()) return *v8::Utils::OpenHandle(*result);
+ }
return Heap::undefined_value();
}
@@ -4688,10 +4752,11 @@
HandleScope scope;
ASSERT(args.length() == 1);
CONVERT_ARG_CHECKED(JSObject, obj, 0);
- RUNTIME_ASSERT(obj->HasIndexedInterceptor());
- v8::Handle<v8::Array> result = GetKeysForIndexedInterceptor(obj, obj);
- if (!result.IsEmpty()) return *v8::Utils::OpenHandle(*result);
+ if (obj->HasIndexedInterceptor()) {
+ v8::Handle<v8::Array> result = GetKeysForIndexedInterceptor(obj, obj);
+ if (!result.IsEmpty()) return *v8::Utils::OpenHandle(*result);
+ }
return Heap::undefined_value();
}
@@ -5059,7 +5124,7 @@
}
if (start_position <= position &&
position <= shared->end_position()) {
- // If there is no candidate or this function is within the currrent
+ // If there is no candidate or this function is within the current
// candidate this is the new candidate.
if (target.is_null()) {
target_start_position = start_position;
@@ -5277,7 +5342,7 @@
// Evaluate a piece of JavaScript in the context of a stack frame for
-// debugging. This is acomplished by creating a new context which in its
+// debugging. This is accomplished by creating a new context which in its
// extension part has all the parameters and locals of the function on the
// stack frame. A function which calls eval with the code to evaluate is then
// compiled in this context and called in this context. As this context
@@ -5388,7 +5453,7 @@
// Wrap the evaluation statement in a new function compiled in the newly
// created context. The function has one parameter which has to be called
// 'arguments'. This it to have access to what would have been 'arguments' in
- // the function beeing debugged.
+ // the function being debugged.
// function(arguments,__source__) {return eval(__source__);}
static const char* source_str =
"function(arguments,__source__){return eval(__source__);}";
@@ -5503,7 +5568,7 @@
ASSERT(args.length() == 0);
// Perform two GCs to get rid of all unreferenced scripts. The first GC gets
- // rid of all the cached script wrappes and the second gets rid of the
+ // rid of all the cached script wrappers and the second gets rid of the
// scripts which is no longer referenced.
Heap::CollectAllGarbage();
Heap::CollectAllGarbage();
diff --git a/src/runtime.h b/src/runtime.h
index cb8d40b..ebe70a4 100644
--- a/src/runtime.h
+++ b/src/runtime.h
@@ -253,6 +253,9 @@
F(CreateObjectLiteralBoilerplate, 3) \
F(CloneObjectLiteralBoilerplate, 1) \
\
+ /* Catch context extension objects */ \
+ F(CreateCatchExtensionObject, 2) \
+ \
/* Statements */ \
F(NewClosure, 2) \
F(NewObject, 1) \
diff --git a/src/scanner.cc b/src/scanner.cc
index 3ae6936..8585e38 100644
--- a/src/scanner.cc
+++ b/src/scanner.cc
@@ -777,7 +777,7 @@
bool Scanner::IsIdentifier(unibrow::CharacterStream* buffer) {
- // Checks whether the buffer contains an identifier (no escapse).
+ // Checks whether the buffer contains an identifier (no escape).
if (!buffer->has_more()) return false;
if (!kIsIdentifierStart.get(buffer->GetNext())) return false;
while (buffer->has_more()) {
diff --git a/src/scopes.cc b/src/scopes.cc
index fcdc577..b198d96 100644
--- a/src/scopes.cc
+++ b/src/scopes.cc
@@ -542,7 +542,7 @@
// This lookup corresponds to a lookup in the "intermediate" scope sitting
// between this scope and the outer scope. (ECMA-262, 3rd., requires that
// the name of named function literal is kept in an intermediate scope
- // inbetween this scope and the next outer scope.)
+ // in between this scope and the next outer scope.)
if (function_ != NULL && function_->name().is_identical_to(name)) {
var = function_;
diff --git a/src/serialize.cc b/src/serialize.cc
index 4ecb2a5..4e1529c 100644
--- a/src/serialize.cc
+++ b/src/serialize.cc
@@ -586,29 +586,33 @@
UNCLASSIFIED,
3,
"StackGuard::address_of_limit()");
- Add(ExternalReference::debug_break().address(),
+ Add(ExternalReference::address_of_regexp_stack_limit().address(),
UNCLASSIFIED,
4,
+ "RegExpStack::limit_address()");
+ Add(ExternalReference::debug_break().address(),
+ UNCLASSIFIED,
+ 5,
"Debug::Break()");
Add(ExternalReference::new_space_start().address(),
UNCLASSIFIED,
- 5,
+ 6,
"Heap::NewSpaceStart()");
Add(ExternalReference::heap_always_allocate_scope_depth().address(),
UNCLASSIFIED,
- 6,
+ 7,
"Heap::always_allocate_scope_depth()");
Add(ExternalReference::new_space_allocation_limit_address().address(),
UNCLASSIFIED,
- 7,
+ 8,
"Heap::NewSpaceAllocationLimitAddress()");
Add(ExternalReference::new_space_allocation_top_address().address(),
UNCLASSIFIED,
- 8,
+ 9,
"Heap::NewSpaceAllocationTopAddress()");
Add(ExternalReference::debug_step_in_fp_address().address(),
UNCLASSIFIED,
- 9,
+ 10,
"Debug::step_in_fp_addr()");
}
@@ -739,7 +743,7 @@
private:
char* str_; // the snapshot
- int len_; // the curent length of str_
+ int len_; // the current length of str_
int max_; // the allocated size of str_
};
@@ -804,7 +808,7 @@
Address target = rinfo->target_address();
uint32_t encoding = reference_encoder_->Encode(target);
CHECK(target == NULL ? encoding == 0 : encoding != 0);
- offsets_.Add(reinterpret_cast<Address>(rinfo->pc()) - obj_address_);
+ offsets_.Add(rinfo->target_address_address() - obj_address_);
addresses_.Add(reinterpret_cast<Address>(encoding));
}
@@ -1269,7 +1273,7 @@
void Deserializer::VisitRuntimeEntry(RelocInfo* rinfo) {
- uint32_t* pc = reinterpret_cast<uint32_t*>(rinfo->pc());
+ uint32_t* pc = reinterpret_cast<uint32_t*>(rinfo->target_address_address());
uint32_t encoding = *pc;
Address target = reference_decoder_->Decode(encoding);
rinfo->set_target_address(target);
diff --git a/src/serialize.h b/src/serialize.h
index e17ee15..0679ecc 100644
--- a/src/serialize.h
+++ b/src/serialize.h
@@ -262,7 +262,7 @@
virtual ~Deserializer();
// Read the flags from the header of the file, and set those that
- // should be inhereted from the snapshot.
+ // should be inherited from the snapshot.
void GetFlags();
// Read saved profiling information from the file and log it if required.
diff --git a/src/simulator-arm.cc b/src/simulator-arm.cc
index 4203ad3..7047e9c 100644
--- a/src/simulator-arm.cc
+++ b/src/simulator-arm.cc
@@ -45,7 +45,7 @@
using ::v8::internal::DeleteArray;
// This macro provides a platform independent use of sscanf. The reason for
-// SScanF not beeing implemented in a platform independent was through
+// SScanF not being implemented in a platform independent was through
// ::v8::internal::OS in the same way as SNPrintF is that the Windows C Run-Time
// Library does not provide vsscanf.
#define SScanF sscanf // NOLINT
diff --git a/src/simulator-arm.h b/src/simulator-arm.h
index ccb3e80..4f3c53d 100644
--- a/src/simulator-arm.h
+++ b/src/simulator-arm.h
@@ -106,7 +106,7 @@
void Execute();
// V8 generally calls into generated code with 5 parameters. This is a
- // convenience funtion, which sets up the simulator state and grabs the
+ // convenience function, which sets up the simulator state and grabs the
// result on return.
v8::internal::Object* Call(int32_t entry, int32_t p0, int32_t p1,
int32_t p2, int32_t p3, int32_t p4);
diff --git a/src/spaces.h b/src/spaces.h
index 718f888..3d13219 100644
--- a/src/spaces.h
+++ b/src/spaces.h
@@ -800,7 +800,7 @@
// Expands the space by allocating a fixed number of pages. Returns false if
// it cannot allocate requested number of pages from OS. Newly allocated
- // pages are appened to the last_page;
+ // pages are append to the last_page;
bool Expand(Page* last_page);
// Generic fast case allocation function that tries linear allocation in
@@ -896,7 +896,7 @@
// Double the size of the semispace by committing extra virtual memory.
// Assumes that the caller has checked that the semispace has not reached
- // its maxmimum capacity (and thus there is space available in the reserved
+ // its maximum capacity (and thus there is space available in the reserved
// address range to grow).
bool Double();
@@ -922,7 +922,7 @@
return (reinterpret_cast<uint32_t>(o) & object_mask_) == object_expected_;
}
- // The offset of an address from the begining of the space.
+ // The offset of an address from the beginning of the space.
int SpaceOffsetForAddress(Address addr) { return addr - low(); }
// If we don't have this here then SemiSpace will be abstract. However
@@ -1526,11 +1526,11 @@
// Returns the object in this chunk.
inline HeapObject* GetObject();
- // Given a requested size (including any extra remembereed set words),
+ // Given a requested size (including any extra remembered set words),
// returns the physical size of a chunk to be allocated.
static int ChunkSizeFor(int size_in_bytes);
- // Given a chunk size, returns the object size it can accomodate (not
+ // Given a chunk size, returns the object size it can accommodate (not
// including any extra remembered set words). Used by
// LargeObjectSpace::Available. Note that this can overestimate the size
// of object that will fit in a chunk---if the object requires extra
diff --git a/src/stub-cache-arm.cc b/src/stub-cache-arm.cc
index 3584a4e..732d292 100644
--- a/src/stub-cache-arm.cc
+++ b/src/stub-cache-arm.cc
@@ -312,20 +312,24 @@
}
+// Generate code to load the length from a string object and return the length.
+// If the receiver object is not a string or a wrapped string object the
+// execution continues at the miss label. The register containing the
+// receiver is potentially clobbered.
void StubCompiler::GenerateLoadStringLength2(MacroAssembler* masm,
Register receiver,
Register scratch1,
Register scratch2,
Label* miss) {
- Label load_length, check_wrapper;
+ Label check_string, check_wrapper;
+ __ bind(&check_string);
// Check if the object is a string leaving the instance type in the
// scratch1 register.
GenerateStringCheck(masm, receiver, scratch1, scratch2,
miss, &check_wrapper);
// Load length directly from the string.
- __ bind(&load_length);
__ and_(scratch1, scratch1, Operand(kStringSizeMask));
__ add(scratch1, scratch1, Operand(String::kHashShift));
__ ldr(r0, FieldMemOperand(receiver, String::kLengthOffset));
@@ -338,11 +342,9 @@
__ cmp(scratch1, Operand(JS_VALUE_TYPE));
__ b(ne, miss);
- // Check if the wrapped value is a string and load the length
- // directly if it is.
- __ ldr(r0, FieldMemOperand(receiver, JSValue::kValueOffset));
- GenerateStringCheck(masm, receiver, scratch1, scratch1, miss, miss);
- __ b(&load_length);
+ // Unwrap the value in place and check if the wrapped value is a string.
+ __ ldr(receiver, FieldMemOperand(receiver, JSValue::kValueOffset));
+ __ b(&check_string);
}
@@ -382,7 +384,7 @@
// Perform map transition for the receiver if necessary.
if ((transition != NULL) && (object->map()->unused_property_fields() == 0)) {
// The properties must be extended before we can store the value.
- // We jump to a runtime call that extends the propeties array.
+ // We jump to a runtime call that extends the properties array.
__ mov(r2, Operand(Handle<Map>(transition)));
// Please note, if we implement keyed store for arm we need
// to call the Builtins::KeyedStoreIC_ExtendStorage.
diff --git a/src/stub-cache-ia32.cc b/src/stub-cache-ia32.cc
index f35877f..5acc1df 100644
--- a/src/stub-cache-ia32.cc
+++ b/src/stub-cache-ia32.cc
@@ -394,7 +394,7 @@
// Perform map transition for the receiver if necessary.
if ((transition != NULL) && (object->map()->unused_property_fields() == 0)) {
// The properties must be extended before we can store the value.
- // We jump to a runtime call that extends the propeties array.
+ // We jump to a runtime call that extends the properties array.
__ mov(ecx, Immediate(Handle<Map>(transition)));
Handle<Code> ic(Builtins::builtin(storage_extend));
__ jmp(ic, RelocInfo::CODE_TARGET);
diff --git a/src/top.cc b/src/top.cc
index be8c39b..d339591 100644
--- a/src/top.cc
+++ b/src/top.cc
@@ -644,7 +644,7 @@
Failure* Top::ReThrow(Object* exception, MessageLocation* location) {
- // Set the exception beeing re-thrown.
+ // Set the exception being re-thrown.
set_pending_exception(exception);
return Failure::Exception();
}
@@ -705,7 +705,7 @@
Handle<Object> is_top_level = it.done()
? Factory::true_value()
: Factory::false_value();
- // Generate and print strack trace line.
+ // Generate and print stack trace line.
Handle<String> line =
Execution::GetStackTraceLine(recv, fun, pos_obj, is_top_level);
if (line->length() > 0) {
diff --git a/src/top.h b/src/top.h
index 7fe735c..0a3bec3 100644
--- a/src/top.h
+++ b/src/top.h
@@ -38,7 +38,7 @@
// Top has static variables used for JavaScript execution.
-class SaveContext; // Forward decleration.
+class SaveContext; // Forward declaration.
class ThreadLocalTop BASE_EMBEDDED {
public:
@@ -318,7 +318,7 @@
// If the GCC version is 4.1.x or 4.2.x an additional field is added to the
-// class as a workarround for a bug in the generated code found with these
+// class as a work around for a bug in the generated code found with these
// versions of GCC. See V8 issue 122 for details.
class SaveContext BASE_EMBEDDED {
public:
diff --git a/src/usage-analyzer.cc b/src/usage-analyzer.cc
index ad26cac..13176f7 100644
--- a/src/usage-analyzer.cc
+++ b/src/usage-analyzer.cc
@@ -69,6 +69,7 @@
void VisitRegExpLiteral(RegExpLiteral* node);
void VisitObjectLiteral(ObjectLiteral* node);
void VisitArrayLiteral(ArrayLiteral* node);
+ void VisitCatchExtensionObject(CatchExtensionObject* node);
void VisitAssignment(Assignment* node);
void VisitThrow(Throw* node);
void VisitProperty(Property* node);
@@ -288,6 +289,11 @@
}
+void UsageComputer::VisitCatchExtensionObject(CatchExtensionObject* node) {
+ Read(node->value());
+}
+
+
void UsageComputer::VisitAssignment(Assignment* node) {
if (node->op() != Token::ASSIGN)
Read(node->target());
diff --git a/src/utils.h b/src/utils.h
index c8a4386..9861246 100644
--- a/src/utils.h
+++ b/src/utils.h
@@ -421,7 +421,7 @@
// Simple support to read a file into a 0-terminated C-string.
// The returned buffer must be freed by the caller.
-// On return, *exits tells whether the file exisited.
+// On return, *exits tells whether the file existed.
Vector<const char> ReadFile(const char* filename,
bool* exists,
bool verbose = true);
diff --git a/src/v8threads.cc b/src/v8threads.cc
index 6b7533b..0a6c7c1 100644
--- a/src/v8threads.cc
+++ b/src/v8threads.cc
@@ -31,6 +31,7 @@
#include "debug.h"
#include "execution.h"
#include "v8threads.h"
+#include "regexp-stack.h"
namespace v8 {
@@ -125,6 +126,7 @@
from = Top::RestoreThread(from);
from = Debug::RestoreDebug(from);
from = StackGuard::RestoreStackGuard(from);
+ from = RegExpStack::RestoreStack(from);
Thread::SetThreadLocal(thread_state_key, NULL);
state->Unlink();
state->LinkInto(ThreadState::FREE_LIST);
@@ -149,7 +151,8 @@
return HandleScopeImplementer::ArchiveSpacePerThread() +
Top::ArchiveSpacePerThread() +
Debug::ArchiveSpacePerThread() +
- StackGuard::ArchiveSpacePerThread();
+ StackGuard::ArchiveSpacePerThread() +
+ RegExpStack::ArchiveSpacePerThread();
}
@@ -230,6 +233,7 @@
to = Top::ArchiveThread(to);
to = Debug::ArchiveDebug(to);
to = StackGuard::ArchiveStackGuard(to);
+ to = RegExpStack::ArchiveStack(to);
lazily_archived_thread_.Initialize(ThreadHandle::INVALID);
lazily_archived_thread_state_ = NULL;
}
diff --git a/src/v8threads.h b/src/v8threads.h
index 557a8f1..b86df73 100644
--- a/src/v8threads.h
+++ b/src/v8threads.h
@@ -99,7 +99,7 @@
// Stop sending preemption requests to threads.
static void StopPreemption();
- // Preempted thread needs to call back to the ContextSwitcher to acknowlege
+ // Preempted thread needs to call back to the ContextSwitcher to acknowledge
// the handling of a preemption request.
static void PreemptionReceived();
diff --git a/src/zone.h b/src/zone.h
index 5a8b0fe..45c52a2 100644
--- a/src/zone.h
+++ b/src/zone.h
@@ -117,7 +117,7 @@
void* operator new(size_t size) { return Zone::New(size); }
// Ideally, the delete operator should be private instead of
- // public, but unfortuately the compiler sometimes synthesizes
+ // public, but unfortunately the compiler sometimes synthesizes
// (unused) destructors for classes derived from ZoneObject, which
// require the operator to be visible. MSVC requires the delete
// operator to be public.
diff --git a/test/cctest/test-api.cc b/test/cctest/test-api.cc
index 926272e..19f702a 100644
--- a/test/cctest/test-api.cc
+++ b/test/cctest/test-api.cc
@@ -5448,6 +5448,58 @@
CHECK(value->BooleanValue());
}
+static bool NamedGetAccessBlocker(Local<v8::Object> obj,
+ Local<Value> name,
+ v8::AccessType type,
+ Local<Value> data) {
+ return false;
+}
+
+
+static bool IndexedGetAccessBlocker(Local<v8::Object> obj,
+ uint32_t key,
+ v8::AccessType type,
+ Local<Value> data) {
+ return false;
+}
+
+
+
+THREADED_TEST(AccessChecksReenabledCorrectly) {
+ v8::HandleScope scope;
+ LocalContext context;
+ Local<ObjectTemplate> templ = ObjectTemplate::New();
+ templ->SetAccessCheckCallbacks(NamedGetAccessBlocker,
+ IndexedGetAccessBlocker);
+ templ->Set(v8_str("a"), v8_str("a"));
+ // Add more than 8 (see kMaxFastProperties) properties
+ // so that the constructor will force copying map.
+ // Cannot sprintf, gcc complains unsafety.
+ char buf[5];
+ for (char i = '0'; i <= '9' ; i++) {
+ buf[1] = i;
+ for (char j = '0'; j <= '9'; j++) {
+ buf[2] = j;
+ for (char k = '0'; k <= '9'; k++) {
+ buf[3] = k;
+ buf[4] = 0;
+ templ->Set(v8_str(buf), v8::Number::New(k));
+ }
+ }
+ }
+
+ Local<v8::Object> instance_1 = templ->NewInstance();
+ context->Global()->Set(v8_str("obj_1"), instance_1);
+
+ Local<Value> value_1 = CompileRun("obj_1.a");
+ CHECK(value_1->IsUndefined());
+
+ Local<v8::Object> instance_2 = templ->NewInstance();
+ context->Global()->Set(v8_str("obj_2"), instance_2);
+
+ Local<Value> value_2 = CompileRun("obj_2.a");
+ CHECK(value_2->IsUndefined());
+}
// This tests that access check information remains on the global
// object template when creating contexts.
diff --git a/test/cctest/test-debug.cc b/test/cctest/test-debug.cc
index 924eeec..816972b 100644
--- a/test/cctest/test-debug.cc
+++ b/test/cctest/test-debug.cc
@@ -2454,7 +2454,7 @@
ChangeBreakOnException(false, true);
step_action = StepIn;
break_point_hit_count = 0;
- expected_step_sequence = "ddedd";
+ expected_step_sequence = "dded";
d->Call(env->Global(), 0, NULL);
CHECK_EQ(strlen(expected_step_sequence), break_point_hit_count);
@@ -2462,7 +2462,7 @@
ChangeBreakOnException(true, true);
step_action = StepIn;
break_point_hit_count = 0;
- expected_step_sequence = "ddeedd";
+ expected_step_sequence = "ddeed";
d->Call(env->Global(), 0, NULL);
CHECK_EQ(strlen(expected_step_sequence), break_point_hit_count);
@@ -2472,7 +2472,7 @@
ChangeBreakOnException(false, true);
step_action = StepIn;
break_point_hit_count = 0;
- expected_step_sequence = "ffghff";
+ expected_step_sequence = "ffghf";
f->Call(env->Global(), 0, NULL);
CHECK_EQ(strlen(expected_step_sequence), break_point_hit_count);
@@ -2480,7 +2480,7 @@
ChangeBreakOnException(true, true);
step_action = StepIn;
break_point_hit_count = 0;
- expected_step_sequence = "ffghhff";
+ expected_step_sequence = "ffghhf";
f->Call(env->Global(), 0, NULL);
CHECK_EQ(strlen(expected_step_sequence), break_point_hit_count);
@@ -2660,58 +2660,76 @@
// Get the property names from the interceptors
CompileRun(
- "named_names = named_mirror.interceptorPropertyNames();"
- "indexed_names = indexed_mirror.interceptorPropertyNames();"
- "both_names = both_mirror.interceptorPropertyNames()");
+ "named_names = named_mirror.propertyNames();"
+ "indexed_names = indexed_mirror.propertyNames();"
+ "both_names = both_mirror.propertyNames()");
CHECK_EQ(3, CompileRun("named_names.length")->Int32Value());
CHECK_EQ(2, CompileRun("indexed_names.length")->Int32Value());
CHECK_EQ(5, CompileRun("both_names.length")->Int32Value());
// Check the expected number of properties.
const char* source;
- source = "named_mirror.interceptorProperties().length";
+ source = "named_mirror.properties().length";
CHECK_EQ(3, CompileRun(source)->Int32Value());
- source = "indexed_mirror.interceptorProperties().length";
+ source = "indexed_mirror.properties().length";
CHECK_EQ(2, CompileRun(source)->Int32Value());
- source = "both_mirror.interceptorProperties().length";
+ source = "both_mirror.properties().length";
CHECK_EQ(5, CompileRun(source)->Int32Value());
- source = "both_mirror.interceptorProperties(1).length";
+ // 1 is PropertyKind.Named;
+ source = "both_mirror.properties(1).length";
CHECK_EQ(3, CompileRun(source)->Int32Value());
- source = "both_mirror.interceptorProperties(2).length";
+ // 2 is PropertyKind.Indexed;
+ source = "both_mirror.properties(2).length";
CHECK_EQ(2, CompileRun(source)->Int32Value());
- source = "both_mirror.interceptorProperties(3).length";
+ // 3 is PropertyKind.Named | PropertyKind.Indexed;
+ source = "both_mirror.properties(3).length";
CHECK_EQ(5, CompileRun(source)->Int32Value());
+ // Get the interceptor properties for the object with only named interceptor.
+ CompileRun("named_values = named_mirror.properties()");
+
+ // Check that the properties are interceptor properties.
+ for (int i = 0; i < 3; i++) {
+ EmbeddedVector<char, SMALL_STRING_BUFFER_SIZE> buffer;
+ OS::SNPrintF(buffer,
+ "named_values[%d] instanceof debug.PropertyMirror", i);
+ CHECK(CompileRun(buffer.start())->BooleanValue());
+
+ // 4 is PropertyType.Interceptor
+ OS::SNPrintF(buffer, "named_values[%d].propertyType()", i);
+ CHECK_EQ(4, CompileRun(buffer.start())->Int32Value());
+
+ OS::SNPrintF(buffer, "named_values[%d].isNative()", i);
+ CHECK(CompileRun(buffer.start())->BooleanValue());
+ }
+
+ // Get the interceptor properties for the object with only indexed
+ // interceptor.
+ CompileRun("indexed_values = indexed_mirror.properties()");
+
+ // Check that the properties are interceptor properties.
+ for (int i = 0; i < 2; i++) {
+ EmbeddedVector<char, SMALL_STRING_BUFFER_SIZE> buffer;
+ OS::SNPrintF(buffer,
+ "indexed_values[%d] instanceof debug.PropertyMirror", i);
+ CHECK(CompileRun(buffer.start())->BooleanValue());
+ }
+
// Get the interceptor properties for the object with both types of
// interceptors.
- CompileRun("both_values = both_mirror.interceptorProperties()");
+ CompileRun("both_values = both_mirror.properties()");
- // Check the mirror hierachy
- source = "both_values[0] instanceof debug.PropertyMirror";
- CHECK(CompileRun(source)->BooleanValue());
-
- source = "both_values[0] instanceof debug.InterceptorPropertyMirror";
- CHECK(CompileRun(source)->BooleanValue());
-
- source = "both_values[1] instanceof debug.InterceptorPropertyMirror";
- CHECK(CompileRun(source)->BooleanValue());
-
- source = "both_values[2] instanceof debug.InterceptorPropertyMirror";
- CHECK(CompileRun(source)->BooleanValue());
-
- source = "both_values[3] instanceof debug.PropertyMirror";
- CHECK(CompileRun(source)->BooleanValue());
-
- source = "both_values[3] instanceof debug.InterceptorPropertyMirror";
- CHECK(CompileRun(source)->BooleanValue());
-
- source = "both_values[4] instanceof debug.InterceptorPropertyMirror";
- CHECK(CompileRun(source)->BooleanValue());
+ // Check that the properties are interceptor properties.
+ for (int i = 0; i < 5; i++) {
+ EmbeddedVector<char, SMALL_STRING_BUFFER_SIZE> buffer;
+ OS::SNPrintF(buffer, "both_values[%d] instanceof debug.PropertyMirror", i);
+ CHECK(CompileRun(buffer.start())->BooleanValue());
+ }
// Check the property names.
source = "both_values[0].name() == 'a'";
diff --git a/test/cctest/test-regexp.cc b/test/cctest/test-regexp.cc
index 19ced16..4383209 100644
--- a/test/cctest/test-regexp.cc
+++ b/test/cctest/test-regexp.cc
@@ -291,8 +291,8 @@
CHECK_MIN_MAX("(?:ab)|cde", 2, 3);
CHECK_MIN_MAX("(ab)", 2, 2);
CHECK_MIN_MAX("(ab|cde)", 2, 3);
- CHECK_MIN_MAX("(ab)\\1", 4, 4);
- CHECK_MIN_MAX("(ab|cde)\\1", 4, 6);
+ CHECK_MIN_MAX("(ab)\\1", 2, 4);
+ CHECK_MIN_MAX("(ab|cde)\\1", 2, 6);
CHECK_MIN_MAX("(?:ab)?", 0, 2);
CHECK_MIN_MAX("(?:ab)*", 0, RegExpTree::kInfinity);
CHECK_MIN_MAX("(?:ab)+", 2, RegExpTree::kInfinity);
@@ -598,7 +598,7 @@
foo_chars[2] = 'o';
Vector<const uc16> foo(foo_chars, 3);
m.SetRegister(4, 42);
- m.PushRegister(4);
+ m.PushRegister(4, RegExpMacroAssembler::kNoStackLimitCheck);
m.AdvanceRegister(4, 42);
m.GoTo(&start);
m.Fail();
@@ -666,6 +666,40 @@
v8::internal::StackGuard stack_guard_;
};
+// Helper functions for calling the Execute method.
+template <typename T>
+static RegExpMacroAssemblerIA32::Result ExecuteIA32(Code* code,
+ const T** input,
+ int start_offset,
+ int end_offset,
+ int* captures,
+ bool at_start) {
+ return RegExpMacroAssemblerIA32::Execute(
+ code,
+ reinterpret_cast<Address*>(
+ reinterpret_cast<void*>(const_cast<T**>(input))),
+ start_offset,
+ end_offset,
+ captures,
+ at_start);
+}
+
+template <typename T>
+static RegExpMacroAssemblerIA32::Result ExecuteIA32(Code* code,
+ T** input,
+ int start_offset,
+ int end_offset,
+ int* captures,
+ bool at_start) {
+ return RegExpMacroAssemblerIA32::Execute(
+ code,
+ reinterpret_cast<Address*>(reinterpret_cast<void*>(input)),
+ start_offset,
+ end_offset,
+ captures,
+ at_start);
+}
+
TEST(MacroAssemblerIA32Success) {
v8::V8::Initialize();
@@ -687,12 +721,12 @@
int end_offset = start_offset + seq_input->length();
RegExpMacroAssemblerIA32::Result result =
- RegExpMacroAssemblerIA32::Execute(*code,
- seq_input.location(),
- start_offset,
- end_offset,
- captures,
- true);
+ ExecuteIA32(*code,
+ seq_input.location(),
+ start_offset,
+ end_offset,
+ captures,
+ true);
CHECK_EQ(RegExpMacroAssemblerIA32::SUCCESS, result);
CHECK_EQ(-1, captures[0]);
@@ -732,12 +766,12 @@
int end_offset = start_offset + seq_input->length();
RegExpMacroAssemblerIA32::Result result =
- RegExpMacroAssemblerIA32::Execute(*code,
- seq_input.location(),
- start_offset,
- end_offset,
- captures,
- true);
+ ExecuteIA32(*code,
+ seq_input.location(),
+ start_offset,
+ end_offset,
+ captures,
+ true);
CHECK_EQ(RegExpMacroAssemblerIA32::SUCCESS, result);
CHECK_EQ(0, captures[0]);
@@ -751,12 +785,12 @@
start_offset = start_adr - reinterpret_cast<Address>(*seq_input);
end_offset = start_offset + seq_input->length();
- result = RegExpMacroAssemblerIA32::Execute(*code,
- seq_input.location(),
- start_offset,
- end_offset,
- captures,
- true);
+ result = ExecuteIA32(*code,
+ seq_input.location(),
+ start_offset,
+ end_offset,
+ captures,
+ true);
CHECK_EQ(RegExpMacroAssemblerIA32::FAILURE, result);
}
@@ -794,12 +828,12 @@
int end_offset = start_offset + seq_input->length() * sizeof(uc16);
RegExpMacroAssemblerIA32::Result result =
- RegExpMacroAssemblerIA32::Execute(*code,
- seq_input.location(),
- start_offset,
- end_offset,
- captures,
- true);
+ ExecuteIA32(*code,
+ seq_input.location(),
+ start_offset,
+ end_offset,
+ captures,
+ true);
CHECK_EQ(RegExpMacroAssemblerIA32::SUCCESS, result);
CHECK_EQ(0, captures[0]);
@@ -814,12 +848,12 @@
start_offset = start_adr - reinterpret_cast<Address>(*seq_input);
end_offset = start_offset + seq_input->length() * sizeof(uc16);
- result = RegExpMacroAssemblerIA32::Execute(*code,
- seq_input.location(),
- start_offset,
- end_offset,
- captures,
- true);
+ result = ExecuteIA32(*code,
+ seq_input.location(),
+ start_offset,
+ end_offset,
+ captures,
+ true);
CHECK_EQ(RegExpMacroAssemblerIA32::FAILURE, result);
}
@@ -853,12 +887,12 @@
int end_offset = start_offset + seq_input->length();
RegExpMacroAssemblerIA32::Result result =
- RegExpMacroAssemblerIA32::Execute(*code,
- seq_input.location(),
- start_offset,
- end_offset,
- NULL,
- true);
+ ExecuteIA32(*code,
+ seq_input.location(),
+ start_offset,
+ end_offset,
+ NULL,
+ true);
CHECK_EQ(RegExpMacroAssemblerIA32::FAILURE, result);
}
@@ -897,12 +931,12 @@
int output[3];
RegExpMacroAssemblerIA32::Result result =
- RegExpMacroAssemblerIA32::Execute(*code,
- seq_input.location(),
- start_offset,
- end_offset,
- output,
- true);
+ ExecuteIA32(*code,
+ seq_input.location(),
+ start_offset,
+ end_offset,
+ output,
+ true);
CHECK_EQ(RegExpMacroAssemblerIA32::SUCCESS, result);
CHECK_EQ(0, output[0]);
@@ -947,12 +981,12 @@
int output[3];
RegExpMacroAssemblerIA32::Result result =
- RegExpMacroAssemblerIA32::Execute(*code,
- seq_input.location(),
- start_offset,
- end_offset,
- output,
- true);
+ ExecuteIA32(*code,
+ seq_input.location(),
+ start_offset,
+ end_offset,
+ output,
+ true);
CHECK_EQ(RegExpMacroAssemblerIA32::SUCCESS, result);
CHECK_EQ(0, output[0]);
@@ -1000,29 +1034,27 @@
int end_offset = start_offset + seq_input->length();
RegExpMacroAssemblerIA32::Result result =
- RegExpMacroAssemblerIA32::Execute(*code,
- seq_input.location(),
- start_offset,
- end_offset,
- NULL,
- true);
+ ExecuteIA32(*code,
+ seq_input.location(),
+ start_offset,
+ end_offset,
+ NULL,
+ true);
CHECK_EQ(RegExpMacroAssemblerIA32::SUCCESS, result);
start_offset += 3;
- result = RegExpMacroAssemblerIA32::Execute(*code,
- seq_input.location(),
- start_offset,
- end_offset,
- NULL,
- false);
+ result = ExecuteIA32(*code,
+ seq_input.location(),
+ start_offset,
+ end_offset,
+ NULL,
+ false);
CHECK_EQ(RegExpMacroAssemblerIA32::SUCCESS, result);
}
-
-
TEST(MacroAssemblerIA32BackRefNoCase) {
v8::V8::Initialize();
ContextInitializer initializer;
@@ -1065,12 +1097,12 @@
int output[4];
RegExpMacroAssemblerIA32::Result result =
- RegExpMacroAssemblerIA32::Execute(*code,
- seq_input.location(),
- start_offset,
- end_offset,
- output,
- true);
+ ExecuteIA32(*code,
+ seq_input.location(),
+ start_offset,
+ end_offset,
+ output,
+ true);
CHECK_EQ(RegExpMacroAssemblerIA32::SUCCESS, result);
CHECK_EQ(0, output[0]);
@@ -1094,13 +1126,13 @@
Label fail;
Label backtrack;
m.WriteCurrentPositionToRegister(out1, 0); // Output: [0]
- m.PushRegister(out1);
+ m.PushRegister(out1, RegExpMacroAssembler::kNoStackLimitCheck);
m.PushBacktrack(&backtrack);
m.WriteStackPointerToRegister(sp);
// Fill stack and registers
m.AdvanceCurrentPosition(2);
m.WriteCurrentPositionToRegister(out1, 0);
- m.PushRegister(out1);
+ m.PushRegister(out1, RegExpMacroAssembler::kNoStackLimitCheck);
m.PushBacktrack(&fail);
// Drop backtrack stack frames.
m.ReadStackPointerFromRegister(sp);
@@ -1135,8 +1167,8 @@
Label loop3;
Label exit_loop3;
- m.PushRegister(out4);
- m.PushRegister(out4);
+ m.PushRegister(out4, RegExpMacroAssembler::kNoStackLimitCheck);
+ m.PushRegister(out4, RegExpMacroAssembler::kNoStackLimitCheck);
m.ReadCurrentPositionFromRegister(out3);
m.Bind(&loop3);
m.AdvanceCurrentPosition(1);
@@ -1166,12 +1198,12 @@
int output[5];
RegExpMacroAssemblerIA32::Result result =
- RegExpMacroAssemblerIA32::Execute(*code,
- seq_input.location(),
- start_offset,
- end_offset,
- output,
- true);
+ ExecuteIA32(*code,
+ seq_input.location(),
+ start_offset,
+ end_offset,
+ output,
+ true);
CHECK_EQ(RegExpMacroAssemblerIA32::SUCCESS, result);
CHECK_EQ(0, output[0]);
@@ -1207,12 +1239,12 @@
int end_offset = start_offset + seq_input->length();
RegExpMacroAssemblerIA32::Result result =
- RegExpMacroAssemblerIA32::Execute(*code,
- seq_input.location(),
- start_offset,
- end_offset,
- NULL,
- true);
+ ExecuteIA32(*code,
+ seq_input.location(),
+ start_offset,
+ end_offset,
+ NULL,
+ true);
CHECK_EQ(RegExpMacroAssemblerIA32::EXCEPTION, result);
CHECK(Top::has_pending_exception());
@@ -1220,6 +1252,56 @@
}
+TEST(MacroAssemblerIA32LotsOfRegisters) {
+ v8::V8::Initialize();
+ ContextInitializer initializer;
+
+ RegExpMacroAssemblerIA32 m(RegExpMacroAssemblerIA32::ASCII, 2);
+
+ // At least 2048, to ensure the allocated space for registers
+ // span one full page.
+ const int large_number = 8000;
+ m.WriteCurrentPositionToRegister(large_number, 42);
+ m.WriteCurrentPositionToRegister(0, 0);
+ m.WriteCurrentPositionToRegister(1, 1);
+ Label done;
+ m.CheckNotBackReference(0, &done); // Performs a system-stack push.
+ m.Bind(&done);
+ m.PushRegister(large_number, RegExpMacroAssembler::kNoStackLimitCheck);
+ m.PopRegister(1);
+ m.Succeed();
+
+ Handle<String> source =
+ Factory::NewStringFromAscii(CStrVector("<huge register space test>"));
+ Handle<Object> code_object = m.GetCode(source);
+ Handle<Code> code = Handle<Code>::cast(code_object);
+
+ // String long enough for test (content doesn't matter).
+ Handle<String> input =
+ Factory::NewStringFromAscii(CStrVector("sample text"));
+ Handle<SeqAsciiString> seq_input = Handle<SeqAsciiString>::cast(input);
+ Address start_adr = seq_input->GetCharsAddress();
+ int start_offset = start_adr - reinterpret_cast<Address>(*seq_input);
+ int end_offset = start_offset + seq_input->length();
+
+ int captures[2];
+ RegExpMacroAssemblerIA32::Result result =
+ ExecuteIA32(*code,
+ seq_input.location(),
+ start_offset,
+ end_offset,
+ captures,
+ true);
+
+ CHECK_EQ(RegExpMacroAssemblerIA32::SUCCESS, result);
+ CHECK_EQ(0, captures[0]);
+ CHECK_EQ(42, captures[1]);
+
+ Top::clear_pending_exception();
+}
+
+
+
#endif // !defined ARM
TEST(AddInverseToTable) {
@@ -1466,5 +1548,5 @@
TEST(Graph) {
V8::Initialize(NULL);
- Execute("\\b\\w+\\b", false, true, true);
+ Execute("(?:(?:x(.))?\1)+$", false, true, true);
}
diff --git a/test/cctest/test-serialize.cc b/test/cctest/test-serialize.cc
index c7dd222..03765a8 100644
--- a/test/cctest/test-serialize.cc
+++ b/test/cctest/test-serialize.cc
@@ -109,9 +109,9 @@
ExternalReference::address_of_stack_guard_limit();
CHECK_EQ(make_code(UNCLASSIFIED, 3),
encoder.Encode(stack_guard_limit_address.address()));
- CHECK_EQ(make_code(UNCLASSIFIED, 4),
- encoder.Encode(ExternalReference::debug_break().address()));
CHECK_EQ(make_code(UNCLASSIFIED, 5),
+ encoder.Encode(ExternalReference::debug_break().address()));
+ CHECK_EQ(make_code(UNCLASSIFIED, 6),
encoder.Encode(ExternalReference::new_space_start().address()));
}
@@ -141,9 +141,9 @@
CHECK_EQ(ExternalReference::address_of_stack_guard_limit().address(),
decoder.Decode(make_code(UNCLASSIFIED, 3)));
CHECK_EQ(ExternalReference::debug_break().address(),
- decoder.Decode(make_code(UNCLASSIFIED, 4)));
- CHECK_EQ(ExternalReference::new_space_start().address(),
decoder.Decode(make_code(UNCLASSIFIED, 5)));
+ CHECK_EQ(ExternalReference::new_space_start().address(),
+ decoder.Decode(make_code(UNCLASSIFIED, 6)));
}
diff --git a/test/cctest/test-threads.cc b/test/cctest/test-threads.cc
new file mode 100644
index 0000000..c0d55f2
--- /dev/null
+++ b/test/cctest/test-threads.cc
@@ -0,0 +1,54 @@
+// Copyright 2008 the V8 project authors. All rights reserved.
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following
+// disclaimer in the documentation and/or other materials provided
+// with the distribution.
+// * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived
+// from this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+#include "v8.h"
+
+#include "platform.h"
+
+#include "cctest.h"
+
+
+TEST(Preemption) {
+ v8::Locker locker;
+ v8::V8::Initialize();
+ v8::HandleScope scope;
+ v8::Context::Scope context_scope(v8::Context::New());
+
+ v8::Locker::StartPreemption(100);
+
+ v8::Handle<v8::Script> script = v8::Script::Compile(
+ v8::String::New("var count = 0; var obj = new Object(); count++;\n"));
+
+ script->Run();
+
+ v8::Locker::StopPreemption();
+ v8::internal::OS::Sleep(500); // Make sure the timer fires.
+
+ script->Run();
+}
+
+
diff --git a/test/mjsunit/array_length.js b/test/mjsunit/array-length-number-conversion.js
similarity index 100%
rename from test/mjsunit/array_length.js
rename to test/mjsunit/array-length-number-conversion.js
diff --git a/test/mjsunit/bugs/bug-187.js b/test/mjsunit/bugs/bug-187.js
new file mode 100644
index 0000000..44d8d7a
--- /dev/null
+++ b/test/mjsunit/bugs/bug-187.js
@@ -0,0 +1,30 @@
+// Copyright 2008 the V8 project authors. All rights reserved.
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following
+// disclaimer in the documentation and/or other materials provided
+// with the distribution.
+// * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived
+// from this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+// See http://code.google.com/p/v8/issues/detail?id=187
+
+assertEquals("f,", "foo".match(/(?:(?=(f)o)fx|)./));
diff --git a/test/mjsunit/debug-backtrace.js b/test/mjsunit/debug-backtrace.js
index 91e1476..db7a70a 100644
--- a/test/mjsunit/debug-backtrace.js
+++ b/test/mjsunit/debug-backtrace.js
@@ -43,14 +43,33 @@
listenerCalled = false;
exception = false;
-function safeEval(code) {
- try {
- return eval('(' + code + ')');
- } catch (e) {
- return undefined;
+
+function ParsedResponse(json) {
+ this.response_ = eval('(' + json + ')');
+ this.refs_ = [];
+ if (this.response_.refs) {
+ for (var i = 0; i < this.response_.refs.length; i++) {
+ this.refs_[this.response_.refs[i].handle] = this.response_.refs[i];
+ }
}
}
+
+ParsedResponse.prototype.response = function() {
+ return this.response_;
+}
+
+
+ParsedResponse.prototype.body = function() {
+ return this.response_.body;
+}
+
+
+ParsedResponse.prototype.lookup = function(handle) {
+ return this.refs_[handle];
+}
+
+
function listener(event, exec_state, event_data, data) {
try {
if (event == Debug.DebugEvent.Break)
@@ -60,13 +79,19 @@
// 1: g
// 2: [anonymous]
+ var response;
+ var backtrace;
+ var frame;
+ var source;
+
// Get the debug command processor.
var dcp = exec_state.debugCommandProcessor();
// Get the backtrace.
var json;
json = '{"seq":0,"type":"request","command":"backtrace"}'
- var backtrace = safeEval(dcp.processDebugJSONRequest(json)).body;
+ response = new ParsedResponse(dcp.processDebugJSONRequest(json));
+ backtrace = response.body();
assertEquals(0, backtrace.fromFrame);
assertEquals(3, backtrace.toFrame);
assertEquals(3, backtrace.totalFrames);
@@ -76,15 +101,16 @@
assertEquals('frame', frames[i].type);
}
assertEquals(0, frames[0].index);
- assertEquals("f", frames[0].func.name);
+ assertEquals("f", response.lookup(frames[0].func.ref).name);
assertEquals(1, frames[1].index);
- assertEquals("g", frames[1].func.name);
+ assertEquals("g", response.lookup(frames[1].func.ref).name);
assertEquals(2, frames[2].index);
- assertEquals("", frames[2].func.name);
+ assertEquals("", response.lookup(frames[2].func.ref).name);
// Get backtrace with two frames.
json = '{"seq":0,"type":"request","command":"backtrace","arguments":{"fromFrame":1,"toFrame":3}}'
- var backtrace = safeEval(dcp.processDebugJSONRequest(json)).body;
+ response = new ParsedResponse(dcp.processDebugJSONRequest(json));
+ backtrace = response.body();
assertEquals(1, backtrace.fromFrame);
assertEquals(3, backtrace.toFrame);
assertEquals(3, backtrace.totalFrames);
@@ -94,71 +120,77 @@
assertEquals('frame', frames[i].type);
}
assertEquals(1, frames[0].index);
- assertEquals("g", frames[0].func.name);
+ assertEquals("g", response.lookup(frames[0].func.ref).name);
assertEquals(2, frames[1].index);
- assertEquals("", frames[1].func.name);
+ assertEquals("", response.lookup(frames[1].func.ref).name);
// Get the individual frames.
- var frame;
json = '{"seq":0,"type":"request","command":"frame"}'
- frame = safeEval(dcp.processDebugJSONRequest(json)).body;
+ response = new ParsedResponse(dcp.processDebugJSONRequest(json));
+ frame = response.body();
assertEquals(0, frame.index);
- assertEquals("f", frame.func.name);
+ assertEquals("f", response.lookup(frame.func.ref).name);
assertTrue(frame.constructCall);
assertEquals(31, frame.line);
assertEquals(3, frame.column);
assertEquals(2, frame.arguments.length);
assertEquals('x', frame.arguments[0].name);
- assertEquals('number', frame.arguments[0].value.type);
- assertEquals(1, frame.arguments[0].value.value);
+ assertEquals('number', response.lookup(frame.arguments[0].value.ref).type);
+ assertEquals(1, response.lookup(frame.arguments[0].value.ref).value);
assertEquals('y', frame.arguments[1].name);
- assertEquals('undefined', frame.arguments[1].value.type);
+ assertEquals('undefined', response.lookup(frame.arguments[1].value.ref).type);
json = '{"seq":0,"type":"request","command":"frame","arguments":{"number":0}}'
- frame = safeEval(dcp.processDebugJSONRequest(json)).body;
+ response = new ParsedResponse(dcp.processDebugJSONRequest(json));
+ frame = response.body();
assertEquals(0, frame.index);
- assertEquals("f", frame.func.name);
+ assertEquals("f", response.lookup(frame.func.ref).name);
assertEquals(31, frame.line);
assertEquals(3, frame.column);
assertEquals(2, frame.arguments.length);
assertEquals('x', frame.arguments[0].name);
- assertEquals('number', frame.arguments[0].value.type);
- assertEquals(1, frame.arguments[0].value.value);
+ assertEquals('number', response.lookup(frame.arguments[0].value.ref).type);
+ assertEquals(1, response.lookup(frame.arguments[0].value.ref).value);
assertEquals('y', frame.arguments[1].name);
- assertEquals('undefined', frame.arguments[1].value.type);
+ assertEquals('undefined', response.lookup(frame.arguments[1].value.ref).type);
json = '{"seq":0,"type":"request","command":"frame","arguments":{"number":1}}'
- frame = safeEval(dcp.processDebugJSONRequest(json)).body;
+ response = new ParsedResponse(dcp.processDebugJSONRequest(json));
+ frame = response.body();
assertEquals(1, frame.index);
- assertEquals("g", frame.func.name);
+ assertEquals("g", response.lookup(frame.func.ref).name);
assertFalse(frame.constructCall);
assertEquals(35, frame.line);
assertEquals(2, frame.column);
assertEquals(0, frame.arguments.length);
json = '{"seq":0,"type":"request","command":"frame","arguments":{"number":2}}'
- frame = safeEval(dcp.processDebugJSONRequest(json)).body;
+ response = new ParsedResponse(dcp.processDebugJSONRequest(json));
+ frame = response.body();
assertEquals(2, frame.index);
- assertEquals("", frame.func.name);
+ assertEquals("", response.lookup(frame.func.ref).name);
// Source slices for the individual frames (they all refer to this script).
json = '{"seq":0,"type":"request","command":"source",' +
'"arguments":{"frame":0,"fromLine":30,"toLine":32}}'
- source = safeEval(dcp.processDebugJSONRequest(json)).body;
+ response = new ParsedResponse(dcp.processDebugJSONRequest(json));
+ source = response.body();
assertEquals("function f(x, y) {", source.source.substring(0, 18));
assertEquals(30, source.fromLine);
assertEquals(32, source.toLine);
json = '{"seq":0,"type":"request","command":"source",' +
'"arguments":{"frame":1,"fromLine":31,"toLine":32}}'
- source = safeEval(dcp.processDebugJSONRequest(json)).body;
+ response = new ParsedResponse(dcp.processDebugJSONRequest(json));
+ source = response.body();
assertEquals(" a=1;", source.source.substring(0, 6));
assertEquals(31, source.fromLine);
assertEquals(32, source.toLine);
json = '{"seq":0,"type":"request","command":"source",' +
'"arguments":{"frame":2,"fromLine":35,"toLine":36}}'
- source = safeEval(dcp.processDebugJSONRequest(json)).body;
+ response = new ParsedResponse(dcp.processDebugJSONRequest(json));
+ source = response.body();
assertEquals(" new f(1);", source.source.substring(0, 11));
assertEquals(35, source.fromLine);
assertEquals(36, source.toLine);
@@ -166,12 +198,13 @@
// Test line interval way beyond this script will result in an error.
json = '{"seq":0,"type":"request","command":"source",' +
'"arguments":{"frame":0,"fromLine":10000,"toLine":20000}}'
- response = safeEval(dcp.processDebugJSONRequest(json));
- assertFalse(response.success);
+ response = new ParsedResponse(dcp.processDebugJSONRequest(json));
+ assertFalse(response.response().success);
// Test without arguments.
json = '{"seq":0,"type":"request","command":"source"}'
- source = safeEval(dcp.processDebugJSONRequest(json)).body;
+ response = new ParsedResponse(dcp.processDebugJSONRequest(json));
+ source = response.body();
assertEquals(Debug.findScript(f).source, source.source);
listenerCalled = true;
diff --git a/test/mjsunit/debug-mirror-cache.js b/test/mjsunit/debug-mirror-cache.js
new file mode 100644
index 0000000..0dc0405
--- /dev/null
+++ b/test/mjsunit/debug-mirror-cache.js
@@ -0,0 +1,85 @@
+// Copyright 2008 the V8 project authors. All rights reserved.
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following
+// disclaimer in the documentation and/or other materials provided
+// with the distribution.
+// * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived
+// from this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+// Flags: --expose-debug-as debug
+// The functions used for testing backtraces. They are at the top to make the
+// testing of source line/column easier.
+function f(x, y) {
+ a=1;
+};
+
+function g() {
+ new f(1);
+};
+
+
+// Get the Debug object exposed from the debug context global object.
+Debug = debug.Debug
+
+listenerCallCount = 0;
+listenerExceptionCount = 0;
+
+
+function listener(event, exec_state, event_data, data) {
+ try {
+ if (event == Debug.DebugEvent.Break)
+ {
+ listenerCallCount++;
+
+ // Check that mirror cache is cleared when entering debugger.
+ assertEquals(0, debug.next_handle_, "Mirror cache not cleared");
+ assertEquals(0, debug.mirror_cache_.length, "Mirror cache not cleared");
+
+ // Get the debug command processor.
+ var dcp = exec_state.debugCommandProcessor();
+
+ // Make a backtrace request to create some mirrors.
+ var json;
+ json = '{"seq":0,"type":"request","command":"backtrace"}'
+ dcp.processDebugJSONRequest(json);
+
+ // Some mirrors where cached.
+ assertFalse(debug.next_handle_ == 0, "Mirror cache not used");
+ assertFalse(debug.mirror_cache_.length == 0, "Mirror cache not used");
+ }
+ } catch (e) {
+ print(e);
+ listenerExceptionCount++;
+ };
+};
+
+// Add the debug event listener.
+Debug.addListener(listener);
+
+// Enter the debugger twice.
+debugger;
+debugger;
+
+// Make sure that the debug event listener vas invoked.
+assertEquals(2, listenerCallCount, "Listener not called");
+assertEquals(0, listenerExceptionCount, "Exception in listener");
+
diff --git a/test/mjsunit/enumeration_order.js b/test/mjsunit/enumeration-order.js
similarity index 100%
rename from test/mjsunit/enumeration_order.js
rename to test/mjsunit/enumeration-order.js
diff --git a/test/mjsunit/fun_name.js b/test/mjsunit/fun-name.js
similarity index 100%
rename from test/mjsunit/fun_name.js
rename to test/mjsunit/fun-name.js
diff --git a/test/mjsunit/large-object-literal.js b/test/mjsunit/large-object-literal.js
index 8118afe..70a2769 100644
--- a/test/mjsunit/large-object-literal.js
+++ b/test/mjsunit/large-object-literal.js
@@ -25,25 +25,32 @@
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
-// Make sure that we can create large object literals.
-var nofProperties = 150;
+// Test that we can create object literals of various sizes.
+function testLiteral(size) {
-// Build large object literal string.
-var literal = "var o = { ";
+ // Build object-literal string.
+ var literal = "var o = { ";
-for (var i = 0; i < nofProperties; i++) {
- if (i > 0) literal += ",";
- literal += ("a" + i + ":" + i);
-}
-literal += "}";
+ for (var i = 0; i < size; i++) {
+ if (i > 0) literal += ",";
+ literal += ("a" + i + ":" + i);
+ }
+ literal += "}";
+ // Create the object literal.
+ eval(literal);
-// Create the large object literal
-eval(literal);
-
-// Check that the properties have the expected values.
-for (var i = 0; i < nofProperties; i++) {
- assertEquals(o["a"+i], i);
+ // Check that the properties have the expected values.
+ for (var i = 0; i < size; i++) {
+ assertEquals(i, o["a"+i]);
+ }
}
+// The sizes to test.
+var sizes = [0, 1, 2, 100, 200, 400, 1000];
+
+// Run the test.
+for (var i = 0; i < sizes.length; i++) {
+ testLiteral(sizes[i]);
+}
diff --git a/test/mjsunit/mirror-array.js b/test/mjsunit/mirror-array.js
index 93c87bd..1873d1e 100644
--- a/test/mjsunit/mirror-array.js
+++ b/test/mjsunit/mirror-array.js
@@ -28,50 +28,80 @@
// Flags: --expose-debug-as debug
// Test the mirror object for objects
+function MirrorRefCache(json_refs) {
+ var tmp = eval('(' + json_refs + ')');
+ this.refs_ = [];
+ for (var i = 0; i < tmp.length; i++) {
+ this.refs_[tmp[i].handle] = tmp[i];
+ }
+}
+
+MirrorRefCache.prototype.lookup = function(handle) {
+ return this.refs_[handle];
+}
+
function testArrayMirror(a, names) {
// Create mirror and JSON representation.
var mirror = debug.MakeMirror(a);
- var json = mirror.toJSONProtocol(true);
+ var serializer = debug.MakeMirrorSerializer();
+ var json = serializer.serializeValue(mirror);
+ var refs = new MirrorRefCache(serializer.serializeReferencedObjects());
// Check the mirror hierachy.
- assertTrue(mirror instanceof debug.Mirror);
- assertTrue(mirror instanceof debug.ValueMirror);
- assertTrue(mirror instanceof debug.ObjectMirror);
- assertTrue(mirror instanceof debug.ArrayMirror);
+ assertTrue(mirror instanceof debug.Mirror, 'Unexpected mirror hierachy');
+ assertTrue(mirror instanceof debug.ValueMirror, 'Unexpected mirror hierachy');
+ assertTrue(mirror instanceof debug.ObjectMirror, 'Unexpected mirror hierachy');
+ assertTrue(mirror instanceof debug.ArrayMirror, 'Unexpected mirror hierachy');
// Check the mirror properties.
- assertTrue(mirror.isArray());
- assertEquals('object', mirror.type());
- assertFalse(mirror.isPrimitive());
- assertEquals('Array', mirror.className());
- assertTrue(mirror.constructorFunction() instanceof debug.ObjectMirror);
- assertTrue(mirror.protoObject() instanceof debug.Mirror);
- assertTrue(mirror.prototypeObject() instanceof debug.Mirror);
+ assertTrue(mirror.isArray(), 'Unexpected mirror');
+ assertEquals('object', mirror.type(), 'Unexpected mirror type');
+ assertFalse(mirror.isPrimitive(), 'Unexpected primitive mirror');
+ assertEquals('Array', mirror.className(), 'Unexpected mirror class name');
+ assertTrue(mirror.constructorFunction() instanceof debug.ObjectMirror, 'Unexpected mirror hierachy');
+ assertEquals('Array', mirror.constructorFunction().name(), 'Unexpected constructor function name');
+ assertTrue(mirror.protoObject() instanceof debug.Mirror, 'Unexpected mirror hierachy');
+ assertTrue(mirror.prototypeObject() instanceof debug.Mirror, 'Unexpected mirror hierachy');
assertEquals(mirror.length(), a.length, "Length mismatch");
- var indexedValueMirrors = mirror.indexedPropertiesFromRange();
- assertEquals(indexedValueMirrors.length, a.length);
- for (var i = 0; i < indexedValueMirrors.length; i++) {
- assertTrue(indexedValueMirrors[i] instanceof debug.Mirror);
- if (a[i]) {
- assertTrue(indexedValueMirrors[i] instanceof debug.PropertyMirror);
- }
+ var indexedProperties = mirror.indexedPropertiesFromRange();
+ assertEquals(indexedProperties.length, a.length);
+ for (var i = 0; i < indexedProperties.length; i++) {
+ assertTrue(indexedProperties[i] instanceof debug.Mirror, 'Unexpected mirror hierachy');
+ assertTrue(indexedProperties[i] instanceof debug.PropertyMirror, 'Unexpected mirror hierachy');
}
// Parse JSON representation and check.
var fromJSON = eval('(' + json + ')');
- assertEquals('object', fromJSON.type);
- assertEquals('Array', fromJSON.className);
- assertEquals('function', fromJSON.constructorFunction.type);
- assertEquals('Array', fromJSON.constructorFunction.name);
- assertEquals(a.length, fromJSON.length, "Length mismatch in parsed JSON");
+ assertEquals('object', fromJSON.type, 'Unexpected mirror type in JSON');
+ assertEquals('Array', fromJSON.className, 'Unexpected mirror class name in JSON');
+ assertEquals(mirror.constructorFunction().handle(), fromJSON.constructorFunction.ref, 'Unexpected constructor function handle in JSON');
+ assertEquals('function', refs.lookup(fromJSON.constructorFunction.ref).type, 'Unexpected constructor function type in JSON');
+ assertEquals('Array', refs.lookup(fromJSON.constructorFunction.ref).name, 'Unexpected constructor function name in JSON');
+ assertEquals(void 0, fromJSON.namedInterceptor, 'No named interceptor expected in JSON');
+ assertEquals(void 0, fromJSON.indexedInterceptor, 'No indexed interceptor expected in JSON');
- // Check that the serialization contains all indexed properties.
- for (var i = 0; i < fromJSON.indexedProperties.length; i++) {
- var index = fromJSON.indexedProperties[i].name;
- assertEquals(indexedValueMirrors[index].name(), index);
- assertEquals(indexedValueMirrors[index].value().type(), fromJSON.indexedProperties[i].value.type, index);
+ // Check that the serialization contains all indexed properties and the length property.
+ var length_found = false;
+ for (var i = 0; i < fromJSON.properties.length; i++) {
+ if (fromJSON.properties[i].name == 'length') {
+ length_found = true;
+ assertEquals('number', refs.lookup(fromJSON.properties[i].ref).type, "Unexpected type of the length property");
+ assertEquals(a.length, refs.lookup(fromJSON.properties[i].ref).value, "Length mismatch in parsed JSON");
+ } else {
+ var index = parseInt(fromJSON.properties[i].name);
+ print(index);
+ if (!isNaN(index)) {
+ print(index);
+ // This test assumes that the order of the indexeed properties is in the
+ // same order in the serialization as returned from
+ // indexedPropertiesFromRange()
+ assertEquals(indexedProperties[index].name(), index);
+ assertEquals(indexedProperties[index].value().type(), refs.lookup(fromJSON.properties[i].ref).type, 'Unexpected serialized type');
+ }
+ }
}
+ assertTrue(length_found, 'Property length not found');
// Check that the serialization contains all names properties.
if (names) {
@@ -84,8 +114,6 @@
}
assertTrue(found, names[i])
}
- } else {
- assertEquals(1, fromJSON.properties.length)
}
}
diff --git a/test/mjsunit/mirror-boolean.js b/test/mjsunit/mirror-boolean.js
index 2147048..4f93089 100644
--- a/test/mjsunit/mirror-boolean.js
+++ b/test/mjsunit/mirror-boolean.js
@@ -31,7 +31,8 @@
function testBooleanMirror(b) {
// Create mirror and JSON representation.
var mirror = debug.MakeMirror(b);
- var json = mirror.toJSONProtocol(true);
+ var serializer = debug.MakeMirrorSerializer();
+ var json = serializer.serializeValue(mirror);
// Check the mirror hierachy.
assertTrue(mirror instanceof debug.Mirror);
diff --git a/test/mjsunit/mirror-date.js b/test/mjsunit/mirror-date.js
index e96badc..a6334d0 100644
--- a/test/mjsunit/mirror-date.js
+++ b/test/mjsunit/mirror-date.js
@@ -31,7 +31,8 @@
function testDateMirror(d, iso8601) {
// Create mirror and JSON representation.
var mirror = debug.MakeMirror(d);
- var json = mirror.toJSONProtocol(true);
+ var serializer = debug.MakeMirrorSerializer();
+ var json = serializer.serializeValue(mirror);
// Check the mirror hierachy.
assertTrue(mirror instanceof debug.Mirror);
diff --git a/test/mjsunit/mirror-error.js b/test/mjsunit/mirror-error.js
index 49eaaff..37ec46c 100644
--- a/test/mjsunit/mirror-error.js
+++ b/test/mjsunit/mirror-error.js
@@ -28,10 +28,24 @@
// Flags: --expose-debug-as debug
// Test the mirror object for regular error objects
+function MirrorRefCache(json_refs) {
+ var tmp = eval('(' + json_refs + ')');
+ this.refs_ = [];
+ for (var i = 0; i < tmp.length; i++) {
+ this.refs_[tmp[i].handle] = tmp[i];
+ }
+}
+
+MirrorRefCache.prototype.lookup = function(handle) {
+ return this.refs_[handle];
+}
+
function testErrorMirror(e) {
// Create mirror and JSON representation.
var mirror = debug.MakeMirror(e);
- var json = mirror.toJSONProtocol(true);
+ var serializer = debug.MakeMirrorSerializer();
+ var json = serializer.serializeValue(mirror);
+ var refs = new MirrorRefCache(serializer.serializeReferencedObjects());
// Check the mirror hierachy.
assertTrue(mirror instanceof debug.Mirror);
@@ -49,7 +63,18 @@
var fromJSON = eval('(' + json + ')');
assertEquals('error', fromJSON.type);
assertEquals('Error', fromJSON.className);
- assertEquals(fromJSON.message, e.message, 'message');
+ if (e.message) {
+ var found_message = false;
+ for (var i in fromJSON.properties) {
+ var p = fromJSON.properties[i];
+ print(p.name);
+ if (p.name == 'message') {
+ assertEquals(e.message, refs.lookup(p.ref).value);
+ found_message = true;
+ }
+ }
+ assertTrue(found_message, 'Property message not found');
+ }
// Check the formatted text (regress 1231579).
assertEquals(fromJSON.text, e.toString(), 'toString');
diff --git a/test/mjsunit/mirror-function.js b/test/mjsunit/mirror-function.js
index 43ff50f..59d9e78 100644
--- a/test/mjsunit/mirror-function.js
+++ b/test/mjsunit/mirror-function.js
@@ -28,10 +28,24 @@
// Flags: --expose-debug-as debug
// Test the mirror object for functions.
+function MirrorRefCache(json_refs) {
+ var tmp = eval('(' + json_refs + ')');
+ this.refs_ = [];
+ for (var i = 0; i < tmp.length; i++) {
+ this.refs_[tmp[i].handle] = tmp[i];
+ }
+}
+
+MirrorRefCache.prototype.lookup = function(handle) {
+ return this.refs_[handle];
+}
+
function testFunctionMirror(f) {
// Create mirror and JSON representation.
var mirror = debug.MakeMirror(f);
- var json = mirror.toJSONProtocol(true);
+ var serializer = debug.MakeMirrorSerializer();
+ var json = serializer.serializeValue(mirror);
+ var refs = new MirrorRefCache(serializer.serializeReferencedObjects());
// Check the mirror hierachy.
assertTrue(mirror instanceof debug.Mirror);
@@ -58,8 +72,8 @@
var fromJSON = eval('(' + json + ')');
assertEquals('function', fromJSON.type);
assertEquals('Function', fromJSON.className);
- assertEquals('function', fromJSON.constructorFunction.type);
- assertEquals('Function', fromJSON.constructorFunction.name);
+ assertEquals('function', refs.lookup(fromJSON.constructorFunction.ref).type);
+ assertEquals('Function', refs.lookup(fromJSON.constructorFunction.ref).name);
assertTrue(fromJSON.resolved);
assertEquals(f.name, fromJSON.name);
assertEquals(f.toString(), fromJSON.source);
diff --git a/test/mjsunit/mirror-null.js b/test/mjsunit/mirror-null.js
index 4233efa..db68966 100644
--- a/test/mjsunit/mirror-null.js
+++ b/test/mjsunit/mirror-null.js
@@ -30,7 +30,8 @@
// Create mirror and JSON representation.
var mirror = debug.MakeMirror(null);
-var json = mirror.toJSONProtocol(true);
+var serializer = debug.MakeMirrorSerializer();
+var json = serializer.serializeValue(mirror);
// Check the mirror hierachy.
assertTrue(mirror instanceof debug.Mirror);
diff --git a/test/mjsunit/mirror-number.js b/test/mjsunit/mirror-number.js
index 20280c3..33488ef 100644
--- a/test/mjsunit/mirror-number.js
+++ b/test/mjsunit/mirror-number.js
@@ -31,7 +31,8 @@
function testNumberMirror(n) {
// Create mirror and JSON representation.
var mirror = debug.MakeMirror(n);
- var json = mirror.toJSONProtocol(true);
+ var serializer = debug.MakeMirrorSerializer();
+ var json = serializer.serializeValue(mirror);
// Check the mirror hierachy.
assertTrue(mirror instanceof debug.Mirror);
diff --git a/test/mjsunit/mirror-object.js b/test/mjsunit/mirror-object.js
index ff896fc..b9b0e1a 100644
--- a/test/mjsunit/mirror-object.js
+++ b/test/mjsunit/mirror-object.js
@@ -28,38 +28,53 @@
// Flags: --expose-debug-as debug
// Test the mirror object for objects
-function testObjectMirror(o, cls_name, ctor_name, hasSpecialProperties) {
+function MirrorRefCache(json_refs) {
+ var tmp = eval('(' + json_refs + ')');
+ this.refs_ = [];
+ for (var i = 0; i < tmp.length; i++) {
+ this.refs_[tmp[i].handle] = tmp[i];
+ }
+}
+
+MirrorRefCache.prototype.lookup = function(handle) {
+ return this.refs_[handle];
+}
+
+function testObjectMirror(obj, cls_name, ctor_name, hasSpecialProperties) {
// Create mirror and JSON representation.
- var mirror = debug.MakeMirror(o);
- var json = mirror.toJSONProtocol(true);
+ var mirror = debug.MakeMirror(obj);
+ var serializer = debug.MakeMirrorSerializer();
+ var json = serializer.serializeValue(mirror);
+ var refs = new MirrorRefCache(serializer.serializeReferencedObjects());
// Check the mirror hierachy.
- assertTrue(mirror instanceof debug.Mirror);
- assertTrue(mirror instanceof debug.ValueMirror);
- assertTrue(mirror instanceof debug.ObjectMirror);
+ assertTrue(mirror instanceof debug.Mirror, 'Unexpected mirror hierachy');
+ assertTrue(mirror instanceof debug.ValueMirror, 'Unexpected mirror hierachy');
+ assertTrue(mirror instanceof debug.ObjectMirror, 'Unexpected mirror hierachy');
// Check the mirror properties.
- assertTrue(mirror.isObject());
- assertEquals('object', mirror.type());
- assertFalse(mirror.isPrimitive());
- assertEquals(cls_name, mirror.className());
- assertTrue(mirror.constructorFunction() instanceof debug.ObjectMirror);
- assertTrue(mirror.protoObject() instanceof debug.Mirror);
- assertTrue(mirror.prototypeObject() instanceof debug.Mirror);
- assertFalse(mirror.hasNamedInterceptor(), "hasNamedInterceptor()");
- assertFalse(mirror.hasIndexedInterceptor(), "hasIndexedInterceptor()");
+ assertTrue(mirror.isObject(), 'Unexpected mirror');
+ assertEquals('object', mirror.type(), 'Unexpected mirror type');
+ assertFalse(mirror.isPrimitive(), 'Unexpected primitive mirror');
+ assertEquals(cls_name, mirror.className(), 'Unexpected mirror class name');
+ assertTrue(mirror.constructorFunction() instanceof debug.ObjectMirror, 'Unexpected mirror hierachy');
+ assertEquals(ctor_name, mirror.constructorFunction().name(), 'Unexpected constructor function name');
+ assertTrue(mirror.protoObject() instanceof debug.Mirror, 'Unexpected mirror hierachy');
+ assertTrue(mirror.prototypeObject() instanceof debug.Mirror, 'Unexpected mirror hierachy');
+ assertFalse(mirror.hasNamedInterceptor(), 'No named interceptor expected');
+ assertFalse(mirror.hasIndexedInterceptor(), 'No indexed interceptor expected');
var names = mirror.propertyNames();
var properties = mirror.properties()
assertEquals(names.length, properties.length);
for (var i = 0; i < properties.length; i++) {
- assertTrue(properties[i] instanceof debug.Mirror);
- assertTrue(properties[i] instanceof debug.PropertyMirror);
- assertEquals('property', properties[i].type());
- assertEquals(names[i], properties[i].name());
+ assertTrue(properties[i] instanceof debug.Mirror, 'Unexpected mirror hierachy');
+ assertTrue(properties[i] instanceof debug.PropertyMirror, 'Unexpected mirror hierachy');
+ assertEquals('property', properties[i].type(), 'Unexpected mirror type');
+ assertEquals(names[i], properties[i].name(), 'Unexpected property name');
}
- for (var p in o) {
+ for (var p in obj) {
var property_mirror = mirror.property(p);
assertTrue(property_mirror instanceof debug.PropertyMirror);
assertEquals(p, property_mirror.name());
@@ -74,47 +89,55 @@
// Parse JSON representation and check.
var fromJSON = eval('(' + json + ')');
- assertEquals('object', fromJSON.type);
- assertEquals(cls_name, fromJSON.className);
- assertEquals('function', fromJSON.constructorFunction.type);
- if (ctor_name !== undefined)
- assertEquals(ctor_name, fromJSON.constructorFunction.name);
- assertEquals(void 0, fromJSON.namedInterceptor);
- assertEquals(void 0, fromJSON.indexedInterceptor);
-
- // For array the index properties are seperate from named properties.
- if (!cls_name == 'Array') {
- assertEquals(names.length, fromJSON.properties.length, 'Some properties missing in JSON');
- }
+ assertEquals('object', fromJSON.type, 'Unexpected mirror type in JSON');
+ assertEquals(cls_name, fromJSON.className, 'Unexpected mirror class name in JSON');
+ assertEquals(mirror.constructorFunction().handle(), fromJSON.constructorFunction.ref, 'Unexpected constructor function handle in JSON');
+ assertEquals('function', refs.lookup(fromJSON.constructorFunction.ref).type, 'Unexpected constructor function type in JSON');
+ assertEquals(ctor_name, refs.lookup(fromJSON.constructorFunction.ref).name, 'Unexpected constructor function name in JSON');
+ assertEquals(mirror.protoObject().handle(), fromJSON.protoObject.ref, 'Unexpected proto object handle in JSON');
+ assertEquals(mirror.protoObject().type(), refs.lookup(fromJSON.protoObject.ref).type, 'Unexpected proto object type in JSON');
+ assertEquals(mirror.prototypeObject().handle(), fromJSON.prototypeObject.ref, 'Unexpected prototype object handle in JSON');
+ assertEquals(mirror.prototypeObject().type(), refs.lookup(fromJSON.prototypeObject.ref).type, 'Unexpected prototype object type in JSON');
+ assertEquals(void 0, fromJSON.namedInterceptor, 'No named interceptor expected in JSON');
+ assertEquals(void 0, fromJSON.indexedInterceptor, 'No indexed interceptor expected in JSON');
// Check that the serialization contains all properties.
+ assertEquals(names.length, fromJSON.properties.length, 'Some properties missing in JSON');
for (var i = 0; i < fromJSON.properties.length; i++) {
var name = fromJSON.properties[i].name;
if (!name) name = fromJSON.properties[i].index;
var found = false;
for (var j = 0; j < names.length; j++) {
if (names[j] == name) {
- assertEquals(properties[i].value().type(), fromJSON.properties[i].value.type);
- // If property type is normal nothing is serialized.
+ // Check that serialized handle is correct.
+ assertEquals(properties[i].value().handle(), fromJSON.properties[i].ref, 'Unexpected serialized handle');
+
+ // Check that serialized name is correct.
+ assertEquals(properties[i].name(), fromJSON.properties[i].name, 'Unexpected serialized name');
+
+ // If property type is normal property type is not serialized.
if (properties[i].propertyType() != debug.PropertyType.Normal) {
- assertEquals(properties[i].propertyType(), fromJSON.properties[i].propertyType);
+ assertEquals(properties[i].propertyType(), fromJSON.properties[i].propertyType, 'Unexpected serialized property type');
} else {
- assertTrue(typeof(fromJSON.properties[i].propertyType) === 'undefined');
+ assertTrue(typeof(fromJSON.properties[i].propertyType) === 'undefined', 'Unexpected serialized property type');
}
- // If there are no attributes nothing is serialized.
+
+ // If there are no attributes attributes are not serialized.
if (properties[i].attributes() != debug.PropertyAttribute.None) {
- assertEquals(properties[i].attributes(), fromJSON.properties[i].attributes);
+ assertEquals(properties[i].attributes(), fromJSON.properties[i].attributes, 'Unexpected serialized attributes');
} else {
- assertTrue(typeof(fromJSON.properties[i].attributes) === 'undefined');
+ assertTrue(typeof(fromJSON.properties[i].attributes) === 'undefined', 'Unexpected serialized attributes');
}
- if (!properties[i].value() instanceof debug.AccessorMirror &&
- properties[i].value().isPrimitive()) {
- // NaN is not equal to NaN.
- if (isNaN(properties[i].value().value())) {
- assertTrue(isNaN(fromJSON.properties[i].value.value));
- } else {
- assertEquals(properties[i].value().value(), fromJSON.properties[i].value.value);
- }
+
+ // Lookup the serialized object from the handle reference.
+ var o = refs.lookup(fromJSON.properties[i].ref);
+ assertTrue(o != void 0, 'Referenced object is not serialized');
+
+ 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);
+ } else if (properties[i].value().isFunction()) {
+ assertEquals(properties[i].value().source(), o.source, 'Unexpected serialized property value for ' + name);
}
found = true;
}
@@ -135,7 +158,7 @@
testObjectMirror({'a':1,'b':2}, 'Object', 'Object');
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', undefined, true); // Global object has special properties
+testObjectMirror(this, 'global', '', true); // Global object has special properties
testObjectMirror([], 'Array', 'Array');
testObjectMirror([1,2], 'Array', 'Array');
@@ -157,27 +180,46 @@
// Test objects with JavaScript accessors.
o = {}
-o.__defineGetter__('a', function(){throw 'a';})
-o.__defineSetter__('b', function(){throw 'b';})
-o.__defineGetter__('c', function(){throw 'c';})
-o.__defineSetter__('c', function(){throw 'c';})
+o.__defineGetter__('a', function(){return 'a';});
+o.__defineSetter__('b', function(){});
+o.__defineGetter__('c', function(){throw 'c';});
+o.__defineSetter__('c', function(){throw 'c';});
testObjectMirror(o, 'Object', 'Object');
mirror = debug.MakeMirror(o);
// a has getter but no setter.
-assertTrue(mirror.property('a').value() instanceof debug.AccessorMirror);
+assertTrue(mirror.property('a').hasGetter());
+assertFalse(mirror.property('a').hasSetter());
assertEquals(debug.PropertyType.Callbacks, mirror.property('a').propertyType());
+assertEquals('function', mirror.property('a').getter().type());
+assertEquals('undefined', mirror.property('a').setter().type());
+assertEquals('function (){return \'a\';}', mirror.property('a').getter().source());
+assertEquals('a', mirror.property('a').value().value());
+assertFalse(mirror.property('a').isException());
// b has setter but no getter.
-assertTrue(mirror.property('b').value() instanceof debug.AccessorMirror);
+assertFalse(mirror.property('b').hasGetter());
+assertTrue(mirror.property('b').hasSetter());
assertEquals(debug.PropertyType.Callbacks, mirror.property('b').propertyType());
-// c has both getter and setter.
-assertTrue(mirror.property('c').value() instanceof debug.AccessorMirror);
+assertEquals('undefined', mirror.property('b').getter().type());
+assertEquals('function', mirror.property('b').setter().type());
+assertEquals('function (){}', mirror.property('b').setter().source());
+assertFalse(mirror.property('b').isException());
+// c has both getter and setter. The getter throws an exception.
+assertTrue(mirror.property('c').hasGetter());
+assertTrue(mirror.property('c').hasSetter());
assertEquals(debug.PropertyType.Callbacks, mirror.property('c').propertyType());
+assertEquals('function', mirror.property('c').getter().type());
+assertEquals('function', mirror.property('c').setter().type());
+assertEquals('function (){throw \'c\';}', mirror.property('c').getter().source());
+assertEquals('function (){throw \'c\';}', mirror.property('c').setter().source());
+assertEquals('c', mirror.property('c').value().value());
+assertTrue(mirror.property('c').isException());
// Test objects with native accessors.
mirror = debug.MakeMirror(new String('abc'));
assertTrue(mirror instanceof debug.ObjectMirror);
-assertTrue(mirror.property('length').value() instanceof debug.AccessorMirror);
-assertTrue(mirror.property('length').value().isNative());
+assertFalse(mirror.property('length').hasGetter());
+assertFalse(mirror.property('length').hasSetter());
+assertTrue(mirror.property('length').isNative());
assertEquals('a', mirror.property(0).value().value());
assertEquals('b', mirror.property(1).value().value());
assertEquals('c', mirror.property(2).value().value());
diff --git a/test/mjsunit/mirror-regexp.js b/test/mjsunit/mirror-regexp.js
index 443caeb..0490b17 100644
--- a/test/mjsunit/mirror-regexp.js
+++ b/test/mjsunit/mirror-regexp.js
@@ -39,10 +39,24 @@
'lastIndex': debug.PropertyAttribute.DontEnum | debug.PropertyAttribute.DontDelete
};
+function MirrorRefCache(json_refs) {
+ var tmp = eval('(' + json_refs + ')');
+ this.refs_ = [];
+ for (var i = 0; i < tmp.length; i++) {
+ this.refs_[tmp[i].handle] = tmp[i];
+ }
+}
+
+MirrorRefCache.prototype.lookup = function(handle) {
+ return this.refs_[handle];
+}
+
function testRegExpMirror(r) {
// Create mirror and JSON representation.
var mirror = debug.MakeMirror(r);
- var json = mirror.toJSONProtocol(true);
+ var serializer = debug.MakeMirrorSerializer();
+ var json = serializer.serializeValue(mirror);
+ var refs = new MirrorRefCache(serializer.serializeReferencedObjects());
// Check the mirror hierachy.
assertTrue(mirror instanceof debug.Mirror);
@@ -54,10 +68,6 @@
assertTrue(mirror.isRegExp());
assertEquals('regexp', mirror.type());
assertFalse(mirror.isPrimitive());
- assertEquals(mirror.source(), r.source, 'source');
- assertEquals(mirror.global(), r.global, 'global');
- assertEquals(mirror.ignoreCase(), r.ignoreCase, 'ignoreCase');
- assertEquals(mirror.multiline(), r.multiline, 'multiline');
for (var p in expected_attributes) {
assertEquals(mirror.property(p).attributes(),
expected_attributes[p],
@@ -71,16 +81,21 @@
var fromJSON = eval('(' + json + ')');
assertEquals('regexp', fromJSON.type);
assertEquals('RegExp', fromJSON.className);
- assertEquals(fromJSON.source, r.source, 'source');
- assertEquals(fromJSON.global, r.global, 'global');
- assertEquals(fromJSON.ignoreCase, r.ignoreCase, 'ignoreCase');
- assertEquals(fromJSON.multiline, r.multiline, 'multiline');
for (var p in expected_attributes) {
for (var i = 0; i < fromJSON.properties.length; i++) {
if (fromJSON.properties[i].name == p) {
- assertEquals(fromJSON.properties[i].attributes,
- expected_attributes[p],
- p + ' attributes');
+ assertEquals(expected_attributes[p],
+ fromJSON.properties[i].attributes,
+ 'Unexpected value for ' + p + ' attributes');
+ assertEquals(mirror.property(p).propertyType(),
+ fromJSON.properties[i].propertyType,
+ 'Unexpected value for ' + p + ' propertyType');
+ assertEquals(mirror.property(p).value().handle(),
+ fromJSON.properties[i].ref,
+ 'Unexpected handle for ' + p);
+ assertEquals(mirror.property(p).value().value(),
+ refs.lookup(fromJSON.properties[i].ref).value,
+ 'Unexpected value for ' + p);
}
}
}
diff --git a/test/mjsunit/mirror-script.js b/test/mjsunit/mirror-script.js
index 0034829..c029645 100644
--- a/test/mjsunit/mirror-script.js
+++ b/test/mjsunit/mirror-script.js
@@ -31,8 +31,8 @@
function testScriptMirror(f, file_name, file_lines, script_type) {
// Create mirror and JSON representation.
var mirror = debug.MakeMirror(f).script();
- var json = mirror.toJSONProtocol(true);
- print(json);
+ var serializer = debug.MakeMirrorSerializer();
+ var json = serializer.serializeValue(mirror);
// Check the mirror hierachy.
assertTrue(mirror instanceof debug.Mirror);
diff --git a/test/mjsunit/mirror-string.js b/test/mjsunit/mirror-string.js
index 0a382d3..eeabc5f 100644
--- a/test/mjsunit/mirror-string.js
+++ b/test/mjsunit/mirror-string.js
@@ -33,7 +33,8 @@
function testStringMirror(s) {
// Create mirror and JSON representation.
var mirror = debug.MakeMirror(s);
- var json = mirror.toJSONProtocol(true);
+ var serializer = debug.MakeMirrorSerializer();
+ var json = serializer.serializeValue(mirror);
// Check the mirror hierachy.
assertTrue(mirror instanceof debug.Mirror);
diff --git a/test/mjsunit/mirror-undefined.js b/test/mjsunit/mirror-undefined.js
index fc35c8d..2b5b84c 100644
--- a/test/mjsunit/mirror-undefined.js
+++ b/test/mjsunit/mirror-undefined.js
@@ -30,7 +30,8 @@
// Create mirror and JSON representation.
var mirror = debug.MakeMirror(void 0);
-var json = mirror.toJSONProtocol(true);
+var serializer = debug.MakeMirrorSerializer();
+var json = serializer.serializeValue(mirror);
// Check the mirror hierachy.
assertTrue(mirror instanceof debug.Mirror);
diff --git a/test/mjsunit/mirror-unresolved-function.js b/test/mjsunit/mirror-unresolved-function.js
index ab49508..21d0e56 100644
--- a/test/mjsunit/mirror-unresolved-function.js
+++ b/test/mjsunit/mirror-unresolved-function.js
@@ -28,8 +28,22 @@
// Flags: --expose-debug-as debug
// Test the mirror object for unresolved functions.
+function MirrorRefCache(json_refs) {
+ var tmp = eval('(' + json_refs + ')');
+ this.refs_ = [];
+ for (var i = 0; i < tmp.length; i++) {
+ this.refs_[tmp[i].handle] = tmp[i];
+ }
+}
+
+MirrorRefCache.prototype.lookup = function(handle) {
+ return this.refs_[handle];
+}
+
var mirror = new debug.UnresolvedFunctionMirror("f");
-var json = mirror.toJSONProtocol(true);
+var serializer = debug.MakeMirrorSerializer();
+var json = serializer.serializeValue(mirror);
+var refs = new MirrorRefCache(serializer.serializeReferencedObjects());
// Check the mirror hierachy for unresolved functions.
assertTrue(mirror instanceof debug.Mirror);
@@ -50,12 +64,15 @@
assertEquals('undefined', mirror.prototypeObject().type());
// Parse JSON representation of unresolved functions and check.
-/*var fromJSON = eval('(' + json + ')');
-assertEquals('function', fromJSON.type);
-assertEquals('Function', fromJSON.className);
-assertEquals('undefined', fromJSON.constructorFunction.type);
-assertEquals('undefined', fromJSON.protoObject.type);
-assertEquals('undefined', fromJSON.prototypeObject.type);
+var fromJSON = eval('(' + json + ')');
+assertEquals('function', fromJSON.type, 'Unexpected mirror type in JSON');
+assertEquals('Function', fromJSON.className, 'Unexpected mirror class name in JSON');
+assertEquals(mirror.constructorFunction().handle(), fromJSON.constructorFunction.ref, 'Unexpected constructor function handle in JSON');
+assertEquals('undefined', refs.lookup(fromJSON.constructorFunction.ref).type, 'Unexpected constructor function type in JSON');
+assertEquals(mirror.protoObject().handle(), fromJSON.protoObject.ref, 'Unexpected proto object handle in JSON');
+assertEquals('undefined', refs.lookup(fromJSON.protoObject.ref).type, 'Unexpected proto object type in JSON');
+assertEquals(mirror.prototypeObject().handle(), fromJSON.prototypeObject.ref, 'Unexpected prototype object handle in JSON');
+assertEquals('undefined', refs.lookup(fromJSON.prototypeObject.ref).type, 'Unexpected prototype object type in JSON');
assertFalse(fromJSON.resolved);
assertEquals("f", fromJSON.name);
-assertEquals(void 0, fromJSON.source);*/
+assertEquals(void 0, fromJSON.source);
diff --git a/test/mjsunit/mjsunit.js b/test/mjsunit/mjsunit.js
index 1d1e260..3570d68 100644
--- a/test/mjsunit/mjsunit.js
+++ b/test/mjsunit/mjsunit.js
@@ -51,8 +51,32 @@
}
+function deepEquals(a, b) {
+ if (a == b) return true;
+ if ((typeof a) !== 'object' || (typeof b) !== 'object' ||
+ (a === null) || (b === null))
+ return false;
+ if (a.constructor === Array) {
+ if (b.constructor !== Array)
+ return false;
+ if (a.length != b.length)
+ return false;
+ for (var i = 0; i < a.length; i++) {
+ if (i in a) {
+ if (!(i in b) || !(deepEquals(a[i], b[i])))
+ return false;
+ } else if (i in b) {
+ return false;
+ }
+ }
+ return true;
+ }
+ return false;
+}
+
+
function assertEquals(expected, found, name_opt) {
- if (expected != found) {
+ if (!deepEquals(found, expected)) {
fail(expected, found, name_opt);
}
}
diff --git a/test/mjsunit/mjsunit.status b/test/mjsunit/mjsunit.status
index e030878..c354300 100644
--- a/test/mjsunit/mjsunit.status
+++ b/test/mjsunit/mjsunit.status
@@ -38,6 +38,10 @@
# no longer using JSCRE.
regexp-UC16: PASS || FAIL
+# These tests pass with irregexp but fail with jscre
+regress/regress-176: PASS || FAIL
+regexp-loop-capture: PASS || FAIL
+
[ $arch == arm ]
# Slow tests which times out in debug mode.
diff --git a/test/mjsunit/object-literal-gc.js b/test/mjsunit/object-literal-gc.js
new file mode 100644
index 0000000..b9d6285
--- /dev/null
+++ b/test/mjsunit/object-literal-gc.js
@@ -0,0 +1,66 @@
+// 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-gc
+
+// Test that the clearing of object literal when normalizing objects
+// works. In particular, test that the garbage collector handles the
+// normalized object literals correctly.
+function testLiteral(size) {
+
+ // Build object-literal string.
+ var literal = "var o = { ";
+
+ for (var i = 0; i < size; i++) {
+ if (i > 0) literal += ",";
+ literal += ("a" + i + ":" + i);
+ }
+ literal += "}";
+
+ // Create the object literal.
+ eval(literal);
+
+ // Force normalization of the properties.
+ delete o["a" + (size - 1)];
+
+ // Perform GC.
+ gc();
+
+ // Check that the properties have the expected values.
+ for (var i = 0; i < size - 1; i++) {
+ assertEquals(i, o["a"+i]);
+ }
+}
+
+// The sizes to test.
+var sizes = [0, 1, 2, 100, 200, 400, 1000];
+
+// Run the test.
+for (var i = 0; i < sizes.length; i++) {
+ testLiteral(sizes[i]);
+}
+
diff --git a/test/mjsunit/regexp-loop-capture.js b/test/mjsunit/regexp-loop-capture.js
new file mode 100644
index 0000000..9a0c99c
--- /dev/null
+++ b/test/mjsunit/regexp-loop-capture.js
@@ -0,0 +1,29 @@
+// 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.
+
+assertEquals(["abc",undefined,undefined,"c"], /(?:(a)|(b)|(c))+/.exec("abc"));
+assertEquals(["ab",undefined], /(?:(a)|b)*/.exec("ab"));
diff --git a/test/mjsunit/regress/regress-1081309.js b/test/mjsunit/regress/regress-1081309.js
index 6871039..3da3386 100644
--- a/test/mjsunit/regress/regress-1081309.js
+++ b/test/mjsunit/regress/regress-1081309.js
@@ -34,14 +34,32 @@
listenerCalled = false;
exception = false;
-function safeEval(code) {
- try {
- return eval('(' + code + ')');
- } catch (e) {
- return undefined;
+function ParsedResponse(json) {
+ this.response_ = eval('(' + json + ')');
+ this.refs_ = [];
+ if (this.response_.refs) {
+ for (var i = 0; i < this.response_.refs.length; i++) {
+ this.refs_[this.response_.refs[i].handle] = this.response_.refs[i];
+ }
}
}
+
+ParsedResponse.prototype.response = function() {
+ return this.response_;
+}
+
+
+ParsedResponse.prototype.body = function() {
+ return this.response_.body;
+}
+
+
+ParsedResponse.prototype.lookup = function(handle) {
+ return this.refs_[handle];
+}
+
+
function listener(event, exec_state, event_data, data) {
try {
if (event == Debug.DebugEvent.Exception)
@@ -56,12 +74,13 @@
// Get the backtrace.
var json;
json = '{"seq":0,"type":"request","command":"backtrace"}'
- var backtrace = safeEval(dcp.processDebugJSONRequest(json)).body;
+ var response = new ParsedResponse(dcp.processDebugJSONRequest(json));
+ var backtrace = response.body();
assertEquals(2, backtrace.totalFrames);
assertEquals(2, backtrace.frames.length);
- assertEquals("g", backtrace.frames[0].func.name);
- assertEquals("", backtrace.frames[1].func.name);
+ assertEquals("g", response.lookup(backtrace.frames[0].func.ref).name);
+ assertEquals("", response.lookup(backtrace.frames[1].func.ref).name);
listenerCalled = true;
}
diff --git a/test/mjsunit/regress/regress-171.js b/test/mjsunit/regress/regress-171.js
new file mode 100644
index 0000000..fe981da
--- /dev/null
+++ b/test/mjsunit/regress/regress-171.js
@@ -0,0 +1,41 @@
+// 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.
+
+function f(s) { return s.length; }
+function g(s, key) { return s[key]; }
+
+assertEquals(f(new String("a")), 1);
+assertEquals(f(new String("a")), 1);
+assertEquals(f(new String("a")), 1);
+assertEquals(f("a"), 1);
+assertEquals(f(new String("a")), 1);
+
+assertEquals(g(new String("a"), "length"), 1);
+assertEquals(g(new String("a"), "length"), 1);
+assertEquals(g(new String("a"), "length"), 1);
+assertEquals(g("a", "length"), 1);
+assertEquals(g(new String("a"), "length"), 1);
diff --git a/test/mjsunit/bugs/bug-176.js b/test/mjsunit/regress/regress-176.js
similarity index 100%
rename from test/mjsunit/bugs/bug-176.js
rename to test/mjsunit/regress/regress-176.js
diff --git a/test/mjsunit/regress/regress-186.js b/test/mjsunit/regress/regress-186.js
new file mode 100644
index 0000000..335869d
--- /dev/null
+++ b/test/mjsunit/regress/regress-186.js
@@ -0,0 +1,72 @@
+// 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.
+
+// Make sure that eval can introduce a local variable called __proto__.
+// See http://code.google.com/p/v8/issues/detail?id=186
+
+var setterCalled = false;
+
+var o = {};
+o.__defineSetter__("x", function() { setterCalled = true; });
+
+function runTest(test) {
+ setterCalled = false;
+ test();
+}
+
+function testLocal() {
+ // Add property called __proto__ to the extension object.
+ eval("var __proto__ = o");
+ // Check that the extension object's prototype did not change.
+ eval("var x = 27");
+ assertFalse(setterCalled, "prototype of extension object changed");
+ assertEquals(o, eval("__proto__"));
+}
+
+function testConstLocal() {
+ // Add const property called __proto__ to the extension object.
+ eval("const __proto__ = o");
+ // Check that the extension object's prototype did not change.
+ eval("var x = 27");
+ assertFalse(setterCalled, "prototype of extension object changed");
+ assertEquals(o, eval("__proto__"));
+}
+
+function testGlobal() {
+ // Assign to the global __proto__ property.
+ eval("__proto__ = o");
+ // Check that the prototype of the global object changed.
+ eval("x = 27");
+ assertTrue(setterCalled, "prototype of global object did not change");
+ setterCalled = false;
+ assertEquals(o, eval("__proto__"));
+}
+
+runTest(testLocal);
+runTest(testConstLocal);
+runTest(testGlobal);
+
diff --git a/test/mjsunit/regress/regress-191.js b/test/mjsunit/regress/regress-191.js
new file mode 100644
index 0000000..ca513c9
--- /dev/null
+++ b/test/mjsunit/regress/regress-191.js
@@ -0,0 +1,42 @@
+// 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.
+
+// Make sure that accessor setters are ignored on context extension
+// objects.
+// See http://code.google.com/p/v8/issues/detail?id=191
+
+var setterCalled = false;
+
+Object.prototype.__defineSetter__("x", function() { setterCalled = true; });
+
+function test() {
+ eval("var x = 42");
+ assertFalse(setterCalled, "accessor setter call on context object");
+ assertEquals(42, eval("x"));
+}
+
+test();
diff --git a/test/mjsunit/regress/regress-74.js b/test/mjsunit/regress/regress-74.js
new file mode 100644
index 0000000..f22b33c
--- /dev/null
+++ b/test/mjsunit/regress/regress-74.js
@@ -0,0 +1,41 @@
+// 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 the variable introduced by catch blocks is DontDelete.
+// See http://code.google.com/p/v8/issues/detail?id=74
+
+function test() {
+ try {
+ throw 42;
+ } catch(e) {
+ assertFalse(delete e, "deleting catch variable");
+ assertEquals(42, e);
+ }
+}
+
+test();
+
diff --git a/test/mjsunit/throw-and-catch-function.js b/test/mjsunit/throw-and-catch-function.js
new file mode 100644
index 0000000..fd24f6e
--- /dev/null
+++ b/test/mjsunit/throw-and-catch-function.js
@@ -0,0 +1,50 @@
+// Copyright 2008 the V8 project authors. All rights reserved.
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following
+// disclaimer in the documentation and/or other materials provided
+// with the distribution.
+// * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived
+// from this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+var g = this;
+var x = new Object();
+x.e = function() { return this; };
+try {
+ throw x.e;
+} catch (e) {
+ assertTrue(e() === g);
+}
+try {
+ throw x.e;
+} catch (e) {
+ with(x) { assertTrue(e() === x); }
+}
+with(x) {
+ try { throw e; } catch (e) { assertTrue(e() === g); }
+}
+var e = 0;
+try {
+ throw x.e;
+} catch (e) {
+ var e = 7;
+}
+assertEquals(0, e);
diff --git a/test/mjsunit/try-catch-extension-object.js b/test/mjsunit/try-catch-extension-object.js
new file mode 100644
index 0000000..efab821
--- /dev/null
+++ b/test/mjsunit/try-catch-extension-object.js
@@ -0,0 +1,58 @@
+// 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.
+
+// Try catch scopes should be implemented with special extension
+// objects so that __proto__ accessors and accessor setters in the
+// Object prototype have no effect.
+
+var setterCalled = false;
+Object.prototype.__defineSetter__("x", function() { setterCalled = true; });
+
+function runTest(test) {
+ setterCalled = false;
+ test();
+}
+
+function testProto() {
+ try {
+ throw 42;
+ } catch(__proto__) {
+ assertEquals(42, __proto__);
+ }
+}
+
+function testAccessorSetter() {
+ try {
+ throw 42;
+ } catch(x) {
+ assertFalse(setterCalled);
+ assertEquals(42, x);
+ }
+}
+
+runTest(testProto);
+runTest(testAccessorSetter);
diff --git a/test/mjsunit/try_catch_scopes.js b/test/mjsunit/try-catch-scopes.js
similarity index 100%
rename from test/mjsunit/try_catch_scopes.js
rename to test/mjsunit/try-catch-scopes.js
diff --git a/tools/visual_studio/v8_base.vcproj b/tools/visual_studio/v8_base.vcproj
index 558b026..7340fe5 100644
--- a/tools/visual_studio/v8_base.vcproj
+++ b/tools/visual_studio/v8_base.vcproj
@@ -301,6 +301,10 @@
>
</File>
<File
+ RelativePath="..\..\src\regexp-stack.h"
+ >
+ </File>
+ <File
RelativePath="..\..\src\assembler.cc"
>
</File>
@@ -745,6 +749,10 @@
>
</File>
<File
+ RelativePath="..\..\src\regexp-stack.cc"
+ >
+ </File>
+ <File
RelativePath="..\..\src\rewriter.cc"
>
</File>
diff --git a/tools/visual_studio/v8_base_arm.vcproj b/tools/visual_studio/v8_base_arm.vcproj
index 7f1adb3..3462835 100644
--- a/tools/visual_studio/v8_base_arm.vcproj
+++ b/tools/visual_studio/v8_base_arm.vcproj
@@ -297,6 +297,10 @@
>
</File>
<File
+ RelativePath="..\..\src\regexp-stack.h"
+ >
+ </File>
+ <File
RelativePath="..\..\src\regexp-macro-assembler-irregexp-inl.h"
>
</File>
@@ -749,6 +753,10 @@
>
</File>
<File
+ RelativePath="..\..\src\regexp-stack.cc"
+ >
+ </File>
+ <File
RelativePath="..\..\src\rewriter.cc"
>
</File>