Added support for easily importing additional environment variables into the SCons build.
Optimized strict equality checks.
Fixed crash in indexed setters on objects without a corresponding getter (issue 298).
Re-enabled script compilation cache.
git-svn-id: http://v8.googlecode.com/svn/trunk@1682 ce2b1a6d-e550-0410-aec6-3dcde31c8c00
diff --git a/ChangeLog b/ChangeLog
index 437388f..5433a70 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,16 @@
+2009-04-07: Version 1.1.7
+
+ Added support for easily importing additional environment
+ variables into the SCons build.
+
+ Optimized strict equality checks.
+
+ Fixed crash in indexed setters on objects without a corresponding
+ getter (issue 298).
+
+ Re-enabled script compilation cache.
+
+
2009-04-01: Version 1.1.6
Reverted an unsafe code generator change.
@@ -33,7 +46,7 @@
Fixed a number of memory leaks in tests.
Fixed crash bug in connection with script source code and external
- strings.
+ strings.
2009-03-24: Version 1.1.3
diff --git a/SConstruct b/SConstruct
index 68b7793..9654ebd 100644
--- a/SConstruct
+++ b/SConstruct
@@ -507,7 +507,8 @@
result = Options()
result.Add('mode', 'compilation mode (debug, release)', 'release')
result.Add('sample', 'build sample (shell, process)', '')
- result.Add('env', 'override environment settings (NAME1:value1,NAME2:value2)', '')
+ result.Add('env', 'override environment settings (NAME0:value0,NAME1:value1,...)', '')
+ result.Add('importenv', 'import environment settings (NAME0,NAME1,...)', '')
for (name, option) in SIMPLE_OPTIONS.iteritems():
help = '%s (%s)' % (name, ", ".join(option['values']))
result.Add(name, help, option.get('default'))
@@ -626,9 +627,13 @@
options['arch'] = options['simulator']
-def ParseEnvOverrides(arg):
- # The environment overrides are in the format NAME1:value1,NAME2:value2
+def ParseEnvOverrides(arg, imports):
+ # The environment overrides are in the format NAME0:value0,NAME1:value1,...
+ # The environment imports are in the format NAME0,NAME1,...
overrides = {}
+ for var in imports.split(','):
+ if var in os.environ:
+ overrides[var] = os.environ[var]
for override in arg.split(','):
pos = override.find(':')
if pos == -1:
@@ -726,7 +731,7 @@
env = Environment(options=opts)
Help(opts.GenerateHelpText(env))
VerifyOptions(env)
- env_overrides = ParseEnvOverrides(env['env'])
+ env_overrides = ParseEnvOverrides(env['env'], env['importenv'])
SourceSignatures(env['sourcesignatures'])
diff --git a/src/api.cc b/src/api.cc
index 876120a..1ddb2c6 100644
--- a/src/api.cc
+++ b/src/api.cc
@@ -140,10 +140,7 @@
bool Utils::ReportApiFailure(const char* location, const char* message) {
FatalErrorCallback callback = GetFatalErrorHandler();
- {
- LEAVE_V8;
- callback(location, message);
- }
+ callback(location, message);
has_shut_down = true;
return false;
}
@@ -163,20 +160,14 @@
static bool ReportV8Dead(const char* location) {
FatalErrorCallback callback = GetFatalErrorHandler();
- {
- LEAVE_V8;
- callback(location, "V8 is no longer usable");
- }
+ callback(location, "V8 is no longer usable");
return true;
}
static bool ReportEmptyHandle(const char* location) {
FatalErrorCallback callback = GetFatalErrorHandler();
- {
- LEAVE_V8;
- callback(location, "Reading from empty handle");
- }
+ callback(location, "Reading from empty handle");
return true;
}
@@ -214,7 +205,6 @@
static void EnsureInitialized(const char* location) {
- ENTER_V8;
if (IsDeadCheck(location)) return;
ApiCheck(v8::V8::Initialize(), location, "Error initializing V8");
}
@@ -234,7 +224,6 @@
v8::Handle<v8::Primitive> ImplementationUtilities::Undefined() {
- ENTER_V8;
if (IsDeadCheck("v8::Undefined()")) return v8::Handle<v8::Primitive>();
EnsureInitialized("v8::Undefined()");
return v8::Handle<Primitive>(ToApi<Primitive>(i::Factory::undefined_value()));
@@ -242,7 +231,6 @@
v8::Handle<v8::Primitive> ImplementationUtilities::Null() {
- ENTER_V8;
if (IsDeadCheck("v8::Null()")) return v8::Handle<v8::Primitive>();
EnsureInitialized("v8::Null()");
return v8::Handle<Primitive>(ToApi<Primitive>(i::Factory::null_value()));
@@ -250,7 +238,6 @@
v8::Handle<v8::Boolean> ImplementationUtilities::True() {
- ENTER_V8;
if (IsDeadCheck("v8::True()")) return v8::Handle<v8::Boolean>();
EnsureInitialized("v8::True()");
return v8::Handle<v8::Boolean>(ToApi<Boolean>(i::Factory::true_value()));
@@ -258,7 +245,6 @@
v8::Handle<v8::Boolean> ImplementationUtilities::False() {
- ENTER_V8;
if (IsDeadCheck("v8::False()")) return v8::Handle<v8::Boolean>();
EnsureInitialized("v8::False()");
return v8::Handle<v8::Boolean>(ToApi<Boolean>(i::Factory::false_value()));
@@ -276,8 +262,8 @@
v8::Handle<Value> ThrowException(v8::Handle<v8::Value> value) {
- ENTER_V8;
if (IsDeadCheck("v8::ThrowException()")) return v8::Handle<Value>();
+ ENTER_V8;
// If we're passed an empty handle, we throw an undefined exception
// to deal more gracefully with out of memory situations.
if (value.IsEmpty()) {
@@ -362,9 +348,8 @@
void** V8::GlobalizeReference(void** obj) {
- ENTER_V8;
- LOG_API("Persistent::New");
if (IsDeadCheck("V8::Persistent::New")) return NULL;
+ LOG_API("Persistent::New");
i::Handle<i::Object> result =
i::GlobalHandles::Create(*reinterpret_cast<i::Object**>(obj));
return reinterpret_cast<void**>(result.location());
@@ -434,8 +419,8 @@
void Context::Enter() {
- ENTER_V8;
if (IsDeadCheck("v8::Context::Enter()")) return;
+ ENTER_V8;
i::Handle<i::Context> env = Utils::OpenHandle(this);
thread_local.EnterContext(env);
@@ -487,8 +472,8 @@
// NeanderObject constructor. When you add one to the site calling the
// constructor you should check that you ensured the VM was not dead first.
NeanderObject::NeanderObject(int size) {
- ENTER_V8;
EnsureInitialized("v8::Nowhere");
+ ENTER_V8;
value_ = i::Factory::NewNeanderObject();
i::Handle<i::FixedArray> elements = i::Factory::NewFixedArray(size);
value_->set_elements(*elements);
@@ -552,8 +537,8 @@
void Template::Set(v8::Handle<String> name, v8::Handle<Data> value,
v8::PropertyAttribute attribute) {
- ENTER_V8;
if (IsDeadCheck("v8::Template::SetProperty()")) return;
+ ENTER_V8;
HandleScope scope;
i::Handle<i::Object> list(Utils::OpenHandle(this)->property_list());
if (list->IsUndefined()) {
@@ -576,10 +561,10 @@
Local<ObjectTemplate> FunctionTemplate::PrototypeTemplate() {
- ENTER_V8;
if (IsDeadCheck("v8::FunctionTemplate::PrototypeTemplate()")) {
return Local<ObjectTemplate>();
}
+ ENTER_V8;
i::Handle<i::Object> result(Utils::OpenHandle(this)->prototype_template());
if (result->IsUndefined()) {
result = Utils::OpenHandle(*ObjectTemplate::New());
@@ -590,8 +575,8 @@
void FunctionTemplate::Inherit(v8::Handle<FunctionTemplate> value) {
- ENTER_V8;
if (IsDeadCheck("v8::FunctionTemplate::Inherit()")) return;
+ ENTER_V8;
Utils::OpenHandle(this)->set_parent_template(*Utils::OpenHandle(*value));
}
@@ -603,9 +588,9 @@
Local<FunctionTemplate> FunctionTemplate::New(InvocationCallback callback,
v8::Handle<Value> data, v8::Handle<Signature> signature) {
- ENTER_V8;
EnsureInitialized("v8::FunctionTemplate::New()");
LOG_API("FunctionTemplate::New");
+ ENTER_V8;
i::Handle<i::Struct> struct_obj =
i::Factory::NewStruct(i::FUNCTION_TEMPLATE_INFO_TYPE);
i::Handle<i::FunctionTemplateInfo> obj =
@@ -627,9 +612,9 @@
Local<Signature> Signature::New(Handle<FunctionTemplate> receiver,
int argc, Handle<FunctionTemplate> argv[]) {
- ENTER_V8;
EnsureInitialized("v8::Signature::New()");
LOG_API("Signature::New");
+ ENTER_V8;
i::Handle<i::Struct> struct_obj =
i::Factory::NewStruct(i::SIGNATURE_INFO_TYPE);
i::Handle<i::SignatureInfo> obj =
@@ -654,9 +639,9 @@
Local<TypeSwitch> TypeSwitch::New(int argc, Handle<FunctionTemplate> types[]) {
- ENTER_V8;
EnsureInitialized("v8::TypeSwitch::New()");
LOG_API("TypeSwitch::New");
+ ENTER_V8;
i::Handle<i::FixedArray> vector = i::Factory::NewFixedArray(argc);
for (int i = 0; i < argc; i++)
vector->set(i, *Utils::OpenHandle(*types[i]));
@@ -684,8 +669,8 @@
void FunctionTemplate::SetCallHandler(InvocationCallback callback,
v8::Handle<Value> data) {
- ENTER_V8;
if (IsDeadCheck("v8::FunctionTemplate::SetCallHandler()")) return;
+ ENTER_V8;
HandleScope scope;
i::Handle<i::Struct> struct_obj =
i::Factory::NewStruct(i::CALL_HANDLER_INFO_TYPE);
@@ -705,10 +690,10 @@
v8::Handle<Value> data,
v8::AccessControl settings,
v8::PropertyAttribute attributes) {
- ENTER_V8;
if (IsDeadCheck("v8::FunctionTemplate::AddInstancePropertyAccessor()")) {
return;
}
+ ENTER_V8;
HandleScope scope;
i::Handle<i::AccessorInfo> obj = i::Factory::NewAccessorInfo();
ASSERT(getter != NULL);
@@ -733,10 +718,10 @@
Local<ObjectTemplate> FunctionTemplate::InstanceTemplate() {
- ENTER_V8;
if (IsDeadCheck("v8::FunctionTemplate::InstanceTemplate()")
|| EmptyCheck("v8::FunctionTemplate::InstanceTemplate()", this))
return Local<ObjectTemplate>();
+ ENTER_V8;
if (Utils::OpenHandle(this)->instance_template()->IsUndefined()) {
Local<ObjectTemplate> templ =
ObjectTemplate::New(v8::Handle<FunctionTemplate>(this));
@@ -749,15 +734,15 @@
void FunctionTemplate::SetClassName(Handle<String> name) {
- ENTER_V8;
if (IsDeadCheck("v8::FunctionTemplate::SetClassName()")) return;
+ ENTER_V8;
Utils::OpenHandle(this)->set_class_name(*Utils::OpenHandle(*name));
}
void FunctionTemplate::SetHiddenPrototype(bool value) {
- ENTER_V8;
if (IsDeadCheck("v8::FunctionTemplate::SetHiddenPrototype()")) return;
+ ENTER_V8;
Utils::OpenHandle(this)->set_hidden_prototype(value);
}
@@ -769,10 +754,10 @@
NamedPropertyDeleter remover,
NamedPropertyEnumerator enumerator,
Handle<Value> data) {
- ENTER_V8;
if (IsDeadCheck("v8::FunctionTemplate::SetNamedInstancePropertyHandler()")) {
return;
}
+ ENTER_V8;
HandleScope scope;
i::Handle<i::Struct> struct_obj =
i::Factory::NewStruct(i::INTERCEPTOR_INFO_TYPE);
@@ -796,11 +781,11 @@
IndexedPropertyDeleter remover,
IndexedPropertyEnumerator enumerator,
Handle<Value> data) {
- ENTER_V8;
if (IsDeadCheck(
"v8::FunctionTemplate::SetIndexedInstancePropertyHandler()")) {
return;
}
+ ENTER_V8;
HandleScope scope;
i::Handle<i::Struct> struct_obj =
i::Factory::NewStruct(i::INTERCEPTOR_INFO_TYPE);
@@ -820,10 +805,10 @@
void FunctionTemplate::SetInstanceCallAsFunctionHandler(
InvocationCallback callback,
Handle<Value> data) {
- ENTER_V8;
if (IsDeadCheck("v8::FunctionTemplate::SetInstanceCallAsFunctionHandler()")) {
return;
}
+ ENTER_V8;
HandleScope scope;
i::Handle<i::Struct> struct_obj =
i::Factory::NewStruct(i::CALL_HANDLER_INFO_TYPE);
@@ -846,10 +831,10 @@
Local<ObjectTemplate> ObjectTemplate::New(
v8::Handle<FunctionTemplate> constructor) {
- ENTER_V8;
if (IsDeadCheck("v8::ObjectTemplate::New()")) return Local<ObjectTemplate>();
EnsureInitialized("v8::ObjectTemplate::New()");
LOG_API("ObjectTemplate::New");
+ ENTER_V8;
i::Handle<i::Struct> struct_obj =
i::Factory::NewStruct(i::OBJECT_TEMPLATE_INFO_TYPE);
i::Handle<i::ObjectTemplateInfo> obj =
@@ -880,8 +865,8 @@
v8::Handle<Value> data,
AccessControl settings,
PropertyAttribute attribute) {
- ENTER_V8;
if (IsDeadCheck("v8::ObjectTemplate::SetAccessor()")) return;
+ ENTER_V8;
HandleScope scope;
EnsureConstructor(this);
i::FunctionTemplateInfo* constructor =
@@ -902,8 +887,8 @@
NamedPropertyDeleter remover,
NamedPropertyEnumerator enumerator,
Handle<Value> data) {
- ENTER_V8;
if (IsDeadCheck("v8::ObjectTemplate::SetNamedPropertyHandler()")) return;
+ ENTER_V8;
HandleScope scope;
EnsureConstructor(this);
i::FunctionTemplateInfo* constructor =
@@ -919,8 +904,8 @@
void ObjectTemplate::MarkAsUndetectable() {
- ENTER_V8;
if (IsDeadCheck("v8::ObjectTemplate::MarkAsUndetectable()")) return;
+ ENTER_V8;
HandleScope scope;
EnsureConstructor(this);
i::FunctionTemplateInfo* constructor =
@@ -935,8 +920,8 @@
IndexedSecurityCallback indexed_callback,
Handle<Value> data,
bool turned_on_by_default) {
- ENTER_V8;
if (IsDeadCheck("v8::ObjectTemplate::SetAccessCheckCallbacks()")) return;
+ ENTER_V8;
HandleScope scope;
EnsureConstructor(this);
@@ -964,8 +949,8 @@
IndexedPropertyDeleter remover,
IndexedPropertyEnumerator enumerator,
Handle<Value> data) {
- ENTER_V8;
if (IsDeadCheck("v8::ObjectTemplate::SetIndexedPropertyHandler()")) return;
+ ENTER_V8;
HandleScope scope;
EnsureConstructor(this);
i::FunctionTemplateInfo* constructor =
@@ -982,8 +967,8 @@
void ObjectTemplate::SetCallAsFunctionHandler(InvocationCallback callback,
Handle<Value> data) {
- ENTER_V8;
if (IsDeadCheck("v8::ObjectTemplate::SetCallAsFunctionHandler()")) return;
+ ENTER_V8;
HandleScope scope;
EnsureConstructor(this);
i::FunctionTemplateInfo* constructor =
@@ -994,7 +979,6 @@
int ObjectTemplate::InternalFieldCount() {
- ENTER_V8;
if (IsDeadCheck("v8::ObjectTemplate::InternalFieldCount()")) {
return 0;
}
@@ -1003,13 +987,13 @@
void ObjectTemplate::SetInternalFieldCount(int value) {
- ENTER_V8;
if (IsDeadCheck("v8::ObjectTemplate::SetInternalFieldCount()")) return;
if (!ApiCheck(i::Smi::IsValid(value),
"v8::ObjectTemplate::SetInternalFieldCount()",
"Invalid internal field count")) {
return;
}
+ ENTER_V8;
if (value > 0) {
// The internal field count is set by the constructor function's
// construct code, so we ensure that there is a constructor
@@ -1040,9 +1024,9 @@
Local<Script> Script::Compile(v8::Handle<String> source,
v8::ScriptOrigin* origin,
v8::ScriptData* script_data) {
- ENTER_V8;
ON_BAILOUT("v8::Script::Compile()", return Local<Script>());
LOG_API("Script::Compile");
+ ENTER_V8;
i::Handle<i::String> str = Utils::OpenHandle(*source);
i::Handle<i::Object> name_obj;
int line_offset = 0;
@@ -1089,9 +1073,9 @@
Local<Value> Script::Run() {
- ENTER_V8;
ON_BAILOUT("v8::Script::Run()", return Local<Value>());
LOG_API("Script::Run");
+ ENTER_V8;
i::Object* raw_result = NULL;
{
HandleScope scope;
@@ -1109,7 +1093,6 @@
Local<Value> Script::Id() {
- ENTER_V8;
ON_BAILOUT("v8::Script::Id()", return Local<Value>());
LOG_API("Script::Id");
i::Object* raw_id = NULL;
@@ -1190,8 +1173,8 @@
Local<String> Message::Get() const {
- ENTER_V8;
ON_BAILOUT("v8::Message::Get()", return Local<String>());
+ ENTER_V8;
HandleScope scope;
i::Handle<i::Object> obj = Utils::OpenHandle(this);
i::Handle<i::String> raw_result = i::MessageHandler::GetMessage(obj);
@@ -1201,10 +1184,10 @@
v8::Handle<Value> Message::GetScriptResourceName() const {
- ENTER_V8;
if (IsDeadCheck("v8::Message::GetScriptResourceName()")) {
return Local<String>();
}
+ ENTER_V8;
HandleScope scope;
i::Handle<i::JSObject> obj =
i::Handle<i::JSObject>::cast(Utils::OpenHandle(this));
@@ -1244,8 +1227,8 @@
int Message::GetLineNumber() const {
- ENTER_V8;
ON_BAILOUT("v8::Message::GetLineNumber()", return -1);
+ ENTER_V8;
HandleScope scope;
EXCEPTION_PREAMBLE();
i::Handle<i::Object> result = CallV8HeapFunction("GetLineNumber",
@@ -1257,8 +1240,8 @@
int Message::GetStartPosition() const {
- ENTER_V8;
if (IsDeadCheck("v8::Message::GetStartPosition()")) return 0;
+ ENTER_V8;
HandleScope scope;
i::Handle<i::JSObject> data_obj = Utils::OpenHandle(this);
@@ -1267,8 +1250,8 @@
int Message::GetEndPosition() const {
- ENTER_V8;
if (IsDeadCheck("v8::Message::GetEndPosition()")) return 0;
+ ENTER_V8;
HandleScope scope;
i::Handle<i::JSObject> data_obj = Utils::OpenHandle(this);
return static_cast<int>(GetProperty(data_obj, "endPos")->Number());
@@ -1276,8 +1259,8 @@
int Message::GetStartColumn() const {
- ENTER_V8;
if (IsDeadCheck("v8::Message::GetStartColumn()")) return 0;
+ ENTER_V8;
HandleScope scope;
i::Handle<i::JSObject> data_obj = Utils::OpenHandle(this);
EXCEPTION_PREAMBLE();
@@ -1291,8 +1274,8 @@
int Message::GetEndColumn() const {
- ENTER_V8;
if (IsDeadCheck("v8::Message::GetEndColumn()")) return 0;
+ ENTER_V8;
HandleScope scope;
i::Handle<i::JSObject> data_obj = Utils::OpenHandle(this);
EXCEPTION_PREAMBLE();
@@ -1308,8 +1291,8 @@
Local<String> Message::GetSourceLine() const {
- ENTER_V8;
ON_BAILOUT("v8::Message::GetSourceLine()", return Local<String>());
+ ENTER_V8;
HandleScope scope;
EXCEPTION_PREAMBLE();
i::Handle<i::Object> result = CallV8HeapFunction("GetSourceLine",
@@ -1325,8 +1308,8 @@
void Message::PrintCurrentStackTrace(FILE* out) {
- ENTER_V8;
if (IsDeadCheck("v8::Message::PrintCurrentStackTrace()")) return;
+ ENTER_V8;
i::Top::PrintCurrentStackTrace(out);
}
@@ -1334,84 +1317,72 @@
// --- D a t a ---
bool Value::IsUndefined() const {
- ENTER_V8;
if (IsDeadCheck("v8::Value::IsUndefined()")) return false;
return Utils::OpenHandle(this)->IsUndefined();
}
bool Value::IsNull() const {
- ENTER_V8;
if (IsDeadCheck("v8::Value::IsNull()")) return false;
return Utils::OpenHandle(this)->IsNull();
}
bool Value::IsTrue() const {
- ENTER_V8;
if (IsDeadCheck("v8::Value::IsTrue()")) return false;
return Utils::OpenHandle(this)->IsTrue();
}
bool Value::IsFalse() const {
- ENTER_V8;
if (IsDeadCheck("v8::Value::IsFalse()")) return false;
return Utils::OpenHandle(this)->IsFalse();
}
bool Value::IsFunction() const {
- ENTER_V8;
if (IsDeadCheck("v8::Value::IsFunction()")) return false;
return Utils::OpenHandle(this)->IsJSFunction();
}
bool Value::IsString() const {
- ENTER_V8;
if (IsDeadCheck("v8::Value::IsString()")) return false;
return Utils::OpenHandle(this)->IsString();
}
bool Value::IsArray() const {
- ENTER_V8;
if (IsDeadCheck("v8::Value::IsArray()")) return false;
return Utils::OpenHandle(this)->IsJSArray();
}
bool Value::IsObject() const {
- ENTER_V8;
if (IsDeadCheck("v8::Value::IsObject()")) return false;
return Utils::OpenHandle(this)->IsJSObject();
}
bool Value::IsNumber() const {
- ENTER_V8;
if (IsDeadCheck("v8::Value::IsNumber()")) return false;
return Utils::OpenHandle(this)->IsNumber();
}
bool Value::IsBoolean() const {
- ENTER_V8;
if (IsDeadCheck("v8::Value::IsBoolean()")) return false;
return Utils::OpenHandle(this)->IsBoolean();
}
bool Value::IsExternal() const {
- ENTER_V8;
if (IsDeadCheck("v8::Value::IsExternal()")) return false;
return Utils::OpenHandle(this)->IsProxy();
}
bool Value::IsInt32() const {
- ENTER_V8;
if (IsDeadCheck("v8::Value::IsInt32()")) return false;
i::Handle<i::Object> obj = Utils::OpenHandle(this);
if (obj->IsSmi()) return true;
@@ -1424,7 +1395,6 @@
bool Value::IsDate() const {
- ENTER_V8;
if (IsDeadCheck("v8::Value::IsDate()")) return false;
i::Handle<i::Object> obj = Utils::OpenHandle(this);
return obj->HasSpecificClassOf(i::Heap::Date_symbol());
@@ -1432,7 +1402,6 @@
Local<String> Value::ToString() const {
- ENTER_V8;
if (IsDeadCheck("v8::Value::ToString()")) return Local<String>();
LOG_API("ToString");
i::Handle<i::Object> obj = Utils::OpenHandle(this);
@@ -1440,6 +1409,7 @@
if (obj->IsString()) {
str = obj;
} else {
+ ENTER_V8;
EXCEPTION_PREAMBLE();
str = i::Execution::ToString(obj, &has_pending_exception);
EXCEPTION_BAILOUT_CHECK(Local<String>());
@@ -1449,7 +1419,6 @@
Local<String> Value::ToDetailString() const {
- ENTER_V8;
if (IsDeadCheck("v8::Value::ToDetailString()")) return Local<String>();
LOG_API("ToDetailString");
i::Handle<i::Object> obj = Utils::OpenHandle(this);
@@ -1457,6 +1426,7 @@
if (obj->IsString()) {
str = obj;
} else {
+ ENTER_V8;
EXCEPTION_PREAMBLE();
str = i::Execution::ToDetailString(obj, &has_pending_exception);
EXCEPTION_BAILOUT_CHECK(Local<String>());
@@ -1466,7 +1436,6 @@
Local<v8::Object> Value::ToObject() const {
- ENTER_V8;
if (IsDeadCheck("v8::Value::ToObject()")) return Local<v8::Object>();
LOG_API("ToObject");
i::Handle<i::Object> obj = Utils::OpenHandle(this);
@@ -1474,6 +1443,7 @@
if (obj->IsJSObject()) {
val = obj;
} else {
+ ENTER_V8;
EXCEPTION_PREAMBLE();
val = i::Execution::ToObject(obj, &has_pending_exception);
EXCEPTION_BAILOUT_CHECK(Local<v8::Object>());
@@ -1483,18 +1453,20 @@
Local<Boolean> Value::ToBoolean() const {
- ENTER_V8;
if (IsDeadCheck("v8::Value::ToBoolean()")) return Local<Boolean>();
LOG_API("ToBoolean");
i::Handle<i::Object> obj = Utils::OpenHandle(this);
- i::Handle<i::Object> val =
- obj->IsBoolean() ? obj : i::Execution::ToBoolean(obj);
- return Local<Boolean>(ToApi<Boolean>(val));
+ if (obj->IsBoolean()) {
+ return Local<Boolean>(ToApi<Boolean>(obj));
+ } else {
+ ENTER_V8;
+ i::Handle<i::Object> val = i::Execution::ToBoolean(obj);
+ return Local<Boolean>(ToApi<Boolean>(val));
+ }
}
Local<Number> Value::ToNumber() const {
- ENTER_V8;
if (IsDeadCheck("v8::Value::ToNumber()")) return Local<Number>();
LOG_API("ToNumber");
i::Handle<i::Object> obj = Utils::OpenHandle(this);
@@ -1502,6 +1474,7 @@
if (obj->IsNumber()) {
num = obj;
} else {
+ ENTER_V8;
EXCEPTION_PREAMBLE();
num = i::Execution::ToNumber(obj, &has_pending_exception);
EXCEPTION_BAILOUT_CHECK(Local<Number>());
@@ -1511,7 +1484,6 @@
Local<Integer> Value::ToInteger() const {
- ENTER_V8;
if (IsDeadCheck("v8::Value::ToInteger()")) return Local<Integer>();
LOG_API("ToInteger");
i::Handle<i::Object> obj = Utils::OpenHandle(this);
@@ -1519,6 +1491,7 @@
if (obj->IsSmi()) {
num = obj;
} else {
+ ENTER_V8;
EXCEPTION_PREAMBLE();
num = i::Execution::ToInteger(obj, &has_pending_exception);
EXCEPTION_BAILOUT_CHECK(Local<Integer>());
@@ -1528,7 +1501,6 @@
External* External::Cast(v8::Value* that) {
- ENTER_V8;
if (IsDeadCheck("v8::External::Cast()")) return 0;
i::Handle<i::Object> obj = Utils::OpenHandle(that);
ApiCheck(obj->IsProxy(),
@@ -1539,7 +1511,6 @@
v8::Object* v8::Object::Cast(Value* that) {
- ENTER_V8;
if (IsDeadCheck("v8::Object::Cast()")) return 0;
i::Handle<i::Object> obj = Utils::OpenHandle(that);
ApiCheck(obj->IsJSObject(),
@@ -1550,7 +1521,6 @@
v8::Function* v8::Function::Cast(Value* that) {
- ENTER_V8;
if (IsDeadCheck("v8::Function::Cast()")) return 0;
i::Handle<i::Object> obj = Utils::OpenHandle(that);
ApiCheck(obj->IsJSFunction(),
@@ -1561,7 +1531,6 @@
v8::String* v8::String::Cast(v8::Value* that) {
- ENTER_V8;
if (IsDeadCheck("v8::String::Cast()")) return 0;
i::Handle<i::Object> obj = Utils::OpenHandle(that);
ApiCheck(obj->IsString(),
@@ -1572,7 +1541,6 @@
v8::Number* v8::Number::Cast(v8::Value* that) {
- ENTER_V8;
if (IsDeadCheck("v8::Number::Cast()")) return 0;
i::Handle<i::Object> obj = Utils::OpenHandle(that);
ApiCheck(obj->IsNumber(),
@@ -1583,7 +1551,6 @@
v8::Integer* v8::Integer::Cast(v8::Value* that) {
- ENTER_V8;
if (IsDeadCheck("v8::Integer::Cast()")) return 0;
i::Handle<i::Object> obj = Utils::OpenHandle(that);
ApiCheck(obj->IsNumber(),
@@ -1594,7 +1561,6 @@
v8::Array* v8::Array::Cast(Value* that) {
- ENTER_V8;
if (IsDeadCheck("v8::Array::Cast()")) return 0;
i::Handle<i::Object> obj = Utils::OpenHandle(that);
ApiCheck(obj->IsJSArray(),
@@ -1605,7 +1571,6 @@
v8::Date* v8::Date::Cast(v8::Value* that) {
- ENTER_V8;
if (IsDeadCheck("v8::Date::Cast()")) return 0;
i::Handle<i::Object> obj = Utils::OpenHandle(that);
ApiCheck(obj->HasSpecificClassOf(i::Heap::Date_symbol()),
@@ -1616,18 +1581,20 @@
bool Value::BooleanValue() const {
- ENTER_V8;
if (IsDeadCheck("v8::Value::BooleanValue()")) return false;
LOG_API("BooleanValue");
i::Handle<i::Object> obj = Utils::OpenHandle(this);
- i::Handle<i::Object> value =
- obj->IsBoolean() ? obj : i::Execution::ToBoolean(obj);
- return value->IsTrue();
+ if (obj->IsBoolean()) {
+ return obj->IsTrue();
+ } else {
+ ENTER_V8;
+ i::Handle<i::Object> value = i::Execution::ToBoolean(obj);
+ return value->IsTrue();
+ }
}
double Value::NumberValue() const {
- ENTER_V8;
if (IsDeadCheck("v8::Value::NumberValue()")) return i::OS::nan_value();
LOG_API("NumberValue");
i::Handle<i::Object> obj = Utils::OpenHandle(this);
@@ -1635,6 +1602,7 @@
if (obj->IsNumber()) {
num = obj;
} else {
+ ENTER_V8;
EXCEPTION_PREAMBLE();
num = i::Execution::ToNumber(obj, &has_pending_exception);
EXCEPTION_BAILOUT_CHECK(i::OS::nan_value());
@@ -1644,7 +1612,6 @@
int64_t Value::IntegerValue() const {
- ENTER_V8;
if (IsDeadCheck("v8::Value::IntegerValue()")) return 0;
LOG_API("IntegerValue");
i::Handle<i::Object> obj = Utils::OpenHandle(this);
@@ -1652,6 +1619,7 @@
if (obj->IsNumber()) {
num = obj;
} else {
+ ENTER_V8;
EXCEPTION_PREAMBLE();
num = i::Execution::ToInteger(obj, &has_pending_exception);
EXCEPTION_BAILOUT_CHECK(0);
@@ -1665,7 +1633,6 @@
Local<Int32> Value::ToInt32() const {
- ENTER_V8;
if (IsDeadCheck("v8::Value::ToInt32()")) return Local<Int32>();
LOG_API("ToInt32");
i::Handle<i::Object> obj = Utils::OpenHandle(this);
@@ -1673,6 +1640,7 @@
if (obj->IsSmi()) {
num = obj;
} else {
+ ENTER_V8;
EXCEPTION_PREAMBLE();
num = i::Execution::ToInt32(obj, &has_pending_exception);
EXCEPTION_BAILOUT_CHECK(Local<Int32>());
@@ -1682,7 +1650,6 @@
Local<Uint32> Value::ToUint32() const {
- ENTER_V8;
if (IsDeadCheck("v8::Value::ToUint32()")) return Local<Uint32>();
LOG_API("ToUInt32");
i::Handle<i::Object> obj = Utils::OpenHandle(this);
@@ -1690,6 +1657,7 @@
if (obj->IsSmi()) {
num = obj;
} else {
+ ENTER_V8;
EXCEPTION_PREAMBLE();
num = i::Execution::ToUint32(obj, &has_pending_exception);
EXCEPTION_BAILOUT_CHECK(Local<Uint32>());
@@ -1699,7 +1667,6 @@
Local<Uint32> Value::ToArrayIndex() const {
- ENTER_V8;
if (IsDeadCheck("v8::Value::ToArrayIndex()")) return Local<Uint32>();
LOG_API("ToArrayIndex");
i::Handle<i::Object> obj = Utils::OpenHandle(this);
@@ -1707,6 +1674,7 @@
if (i::Smi::cast(*obj)->value() >= 0) return Utils::Uint32ToLocal(obj);
return Local<Uint32>();
}
+ ENTER_V8;
EXCEPTION_PREAMBLE();
i::Handle<i::Object> string_obj =
i::Execution::ToString(obj, &has_pending_exception);
@@ -1727,7 +1695,6 @@
int32_t Value::Int32Value() const {
- ENTER_V8;
if (IsDeadCheck("v8::Value::Int32Value()")) return 0;
LOG_API("Int32Value");
i::Handle<i::Object> obj = Utils::OpenHandle(this);
@@ -1735,6 +1702,7 @@
return i::Smi::cast(*obj)->value();
} else {
LOG_API("Int32Value (slow)");
+ ENTER_V8;
EXCEPTION_PREAMBLE();
i::Handle<i::Object> num =
i::Execution::ToInt32(obj, &has_pending_exception);
@@ -1749,12 +1717,13 @@
bool Value::Equals(Handle<Value> that) const {
- ENTER_V8;
if (IsDeadCheck("v8::Value::Equals()")
|| EmptyCheck("v8::Value::Equals()", this)
- || EmptyCheck("v8::Value::Equals()", that))
+ || EmptyCheck("v8::Value::Equals()", that)) {
return false;
+ }
LOG_API("Equals");
+ ENTER_V8;
i::Handle<i::Object> obj = Utils::OpenHandle(this);
i::Handle<i::Object> other = Utils::OpenHandle(*that);
i::Object** args[1] = { other.location() };
@@ -1767,11 +1736,11 @@
bool Value::StrictEquals(Handle<Value> that) const {
- ENTER_V8;
if (IsDeadCheck("v8::Value::StrictEquals()")
|| EmptyCheck("v8::Value::StrictEquals()", this)
- || EmptyCheck("v8::Value::StrictEquals()", that))
+ || EmptyCheck("v8::Value::StrictEquals()", that)) {
return false;
+ }
LOG_API("StrictEquals");
i::Handle<i::Object> obj = Utils::OpenHandle(this);
i::Handle<i::Object> other = Utils::OpenHandle(*that);
@@ -1798,13 +1767,13 @@
uint32_t Value::Uint32Value() const {
- ENTER_V8;
if (IsDeadCheck("v8::Value::Uint32Value()")) return 0;
LOG_API("Uint32Value");
i::Handle<i::Object> obj = Utils::OpenHandle(this);
if (obj->IsSmi()) {
return i::Smi::cast(*obj)->value();
} else {
+ ENTER_V8;
EXCEPTION_PREAMBLE();
i::Handle<i::Object> num =
i::Execution::ToUint32(obj, &has_pending_exception);
@@ -1820,8 +1789,8 @@
bool v8::Object::Set(v8::Handle<Value> key, v8::Handle<Value> value,
v8::PropertyAttribute attribs) {
- ENTER_V8;
ON_BAILOUT("v8::Object::Set()", return false);
+ ENTER_V8;
i::Handle<i::Object> self = Utils::OpenHandle(this);
i::Handle<i::Object> key_obj = Utils::OpenHandle(*key);
i::Handle<i::Object> value_obj = Utils::OpenHandle(*value);
@@ -1838,8 +1807,8 @@
Local<Value> v8::Object::Get(v8::Handle<Value> key) {
- ENTER_V8;
ON_BAILOUT("v8::Object::Get()", return Local<v8::Value>());
+ ENTER_V8;
i::Handle<i::Object> self = Utils::OpenHandle(this);
i::Handle<i::Object> key_obj = Utils::OpenHandle(*key);
EXCEPTION_PREAMBLE();
@@ -1851,8 +1820,8 @@
Local<Value> v8::Object::GetPrototype() {
- ENTER_V8;
ON_BAILOUT("v8::Object::GetPrototype()", return Local<v8::Value>());
+ ENTER_V8;
i::Handle<i::Object> self = Utils::OpenHandle(this);
i::Handle<i::Object> result = i::GetPrototype(self);
return Utils::ToLocal(result);
@@ -1860,8 +1829,8 @@
Local<Array> v8::Object::GetPropertyNames() {
- ENTER_V8;
ON_BAILOUT("v8::Object::GetPropertyNames()", return Local<v8::Array>());
+ ENTER_V8;
v8::HandleScope scope;
i::Handle<i::JSObject> self = Utils::OpenHandle(this);
i::Handle<i::FixedArray> value = i::GetKeysInFixedArrayFor(self);
@@ -1875,8 +1844,8 @@
Local<String> v8::Object::ObjectProtoToString() {
- ENTER_V8;
ON_BAILOUT("v8::Object::ObjectProtoToString()", return Local<v8::String>());
+ ENTER_V8;
i::Handle<i::JSObject> self = Utils::OpenHandle(this);
i::Handle<i::Object> name(self->class_name());
@@ -1928,8 +1897,8 @@
bool v8::Object::Delete(v8::Handle<String> key) {
- ENTER_V8;
ON_BAILOUT("v8::Object::Delete()", return false);
+ ENTER_V8;
HandleScope scope;
i::Handle<i::JSObject> self = Utils::OpenHandle(this);
i::Handle<i::String> key_obj = Utils::OpenHandle(*key);
@@ -1938,8 +1907,8 @@
bool v8::Object::Has(v8::Handle<String> key) {
- ENTER_V8;
ON_BAILOUT("v8::Object::Has()", return false);
+ ENTER_V8;
i::Handle<i::JSObject> self = Utils::OpenHandle(this);
i::Handle<i::String> key_obj = Utils::OpenHandle(*key);
return self->HasProperty(*key_obj);
@@ -1947,8 +1916,8 @@
bool v8::Object::Delete(uint32_t index) {
- ENTER_V8;
ON_BAILOUT("v8::Object::DeleteProperty()", return false);
+ ENTER_V8;
HandleScope scope;
i::Handle<i::JSObject> self = Utils::OpenHandle(this);
return i::DeleteElement(self, index)->IsTrue();
@@ -1956,7 +1925,6 @@
bool v8::Object::Has(uint32_t index) {
- ENTER_V8;
ON_BAILOUT("v8::Object::HasProperty()", return false);
i::Handle<i::JSObject> self = Utils::OpenHandle(this);
return self->HasElement(index);
@@ -1964,7 +1932,6 @@
bool v8::Object::HasRealNamedProperty(Handle<String> key) {
- ENTER_V8;
ON_BAILOUT("v8::Object::HasRealNamedProperty()", return false);
return Utils::OpenHandle(this)->HasRealNamedProperty(
*Utils::OpenHandle(*key));
@@ -1972,29 +1939,26 @@
bool v8::Object::HasRealIndexedProperty(uint32_t index) {
- ENTER_V8;
ON_BAILOUT("v8::Object::HasRealIndexedProperty()", return false);
return Utils::OpenHandle(this)->HasRealElementProperty(index);
}
bool v8::Object::HasRealNamedCallbackProperty(Handle<String> key) {
- ENTER_V8;
ON_BAILOUT("v8::Object::HasRealNamedCallbackProperty()", return false);
+ ENTER_V8;
return Utils::OpenHandle(this)->HasRealNamedCallbackProperty(
*Utils::OpenHandle(*key));
}
bool v8::Object::HasNamedLookupInterceptor() {
- ENTER_V8;
ON_BAILOUT("v8::Object::HasNamedLookupInterceptor()", return false);
return Utils::OpenHandle(this)->HasNamedInterceptor();
}
bool v8::Object::HasIndexedLookupInterceptor() {
- ENTER_V8;
ON_BAILOUT("v8::Object::HasIndexedLookupInterceptor()", return false);
return Utils::OpenHandle(this)->HasIndexedInterceptor();
}
@@ -2002,9 +1966,9 @@
Handle<Value> v8::Object::GetRealNamedPropertyInPrototypeChain(
Handle<String> key) {
- ENTER_V8;
ON_BAILOUT("v8::Object::GetRealNamedPropertyInPrototypeChain()",
return Local<Value>());
+ ENTER_V8;
i::Handle<i::JSObject> self_obj = Utils::OpenHandle(this);
i::Handle<i::String> key_obj = Utils::OpenHandle(*key);
i::LookupResult lookup;
@@ -2025,8 +1989,8 @@
// Because the object gets a new map, existing inline cache caching
// the old map of this object will fail.
void v8::Object::TurnOnAccessCheck() {
- ENTER_V8;
ON_BAILOUT("v8::Object::TurnOnAccessCheck()", return);
+ ENTER_V8;
i::Handle<i::JSObject> obj = Utils::OpenHandle(this);
i::Handle<i::Map> new_map =
@@ -2037,8 +2001,8 @@
Local<v8::Object> v8::Object::Clone() {
- ENTER_V8;
ON_BAILOUT("v8::Object::Clone()", return Local<Object>());
+ ENTER_V8;
i::Handle<i::JSObject> self = Utils::OpenHandle(this);
EXCEPTION_PREAMBLE();
i::Handle<i::JSObject> result = i::Copy(self);
@@ -2049,8 +2013,8 @@
int v8::Object::GetIdentityHash() {
- ENTER_V8;
ON_BAILOUT("v8::Object::GetIdentityHash()", return 0);
+ ENTER_V8;
i::Handle<i::JSObject> self = Utils::OpenHandle(this);
i::Handle<i::Object> hidden_props(i::GetHiddenProperties(self, true));
i::Handle<i::Object> hash_symbol = i::Factory::identity_hash_symbol();
@@ -2071,8 +2035,8 @@
bool v8::Object::SetHiddenValue(v8::Handle<v8::String> key,
v8::Handle<v8::Value> value) {
- ENTER_V8;
ON_BAILOUT("v8::Object::SetHiddenValue()", return false);
+ ENTER_V8;
i::Handle<i::JSObject> self = Utils::OpenHandle(this);
i::Handle<i::Object> hidden_props(i::GetHiddenProperties(self, true));
i::Handle<i::Object> key_obj = Utils::OpenHandle(*key);
@@ -2090,8 +2054,8 @@
v8::Local<v8::Value> v8::Object::GetHiddenValue(v8::Handle<v8::String> key) {
- ENTER_V8;
ON_BAILOUT("v8::Object::GetHiddenValue()", return Local<v8::Value>());
+ ENTER_V8;
i::Handle<i::JSObject> self = Utils::OpenHandle(this);
i::Handle<i::Object> hidden_props(i::GetHiddenProperties(self, false));
if (hidden_props->IsUndefined()) {
@@ -2110,8 +2074,8 @@
bool v8::Object::DeleteHiddenValue(v8::Handle<v8::String> key) {
- ENTER_V8;
ON_BAILOUT("v8::DeleteHiddenValue()", return false);
+ ENTER_V8;
i::Handle<i::JSObject> self = Utils::OpenHandle(this);
i::Handle<i::JSObject> hidden_props(
i::JSObject::cast(*i::GetHiddenProperties(self, false)));
@@ -2130,9 +2094,9 @@
Local<v8::Object> Function::NewInstance(int argc,
v8::Handle<v8::Value> argv[]) const {
- ENTER_V8;
ON_BAILOUT("v8::Function::NewInstance()", return Local<v8::Object>());
LOG_API("Function::NewInstance");
+ ENTER_V8;
HandleScope scope;
i::Handle<i::JSFunction> function = Utils::OpenHandle(this);
STATIC_ASSERT(sizeof(v8::Handle<v8::Value>) == sizeof(i::Object**));
@@ -2147,9 +2111,9 @@
Local<v8::Value> Function::Call(v8::Handle<v8::Object> recv, int argc,
v8::Handle<v8::Value> argv[]) {
- ENTER_V8;
ON_BAILOUT("v8::Function::Call()", return Local<v8::Value>());
LOG_API("Function::Call");
+ ENTER_V8;
i::Object* raw_result = NULL;
{
HandleScope scope;
@@ -2182,23 +2146,21 @@
int String::Length() const {
- ENTER_V8;
if (IsDeadCheck("v8::String::Length()")) return 0;
return Utils::OpenHandle(this)->length();
}
int String::Utf8Length() const {
- ENTER_V8;
if (IsDeadCheck("v8::String::Utf8Length()")) return 0;
return Utils::OpenHandle(this)->Utf8Length();
}
int String::WriteUtf8(char* buffer, int capacity) const {
- ENTER_V8;
if (IsDeadCheck("v8::String::WriteUtf8()")) return 0;
LOG_API("String::WriteUtf8");
+ ENTER_V8;
i::Handle<i::String> str = Utils::OpenHandle(this);
write_input_buffer.Reset(0, *str);
int len = str->length();
@@ -2238,9 +2200,9 @@
int String::WriteAscii(char* buffer, int start, int length) const {
- ENTER_V8;
if (IsDeadCheck("v8::String::WriteAscii()")) return 0;
LOG_API("String::WriteAscii");
+ ENTER_V8;
ASSERT(start >= 0 && length >= -1);
i::Handle<i::String> str = Utils::OpenHandle(this);
// Flatten the string for efficiency. This applies whether we are
@@ -2264,9 +2226,9 @@
int String::Write(uint16_t* buffer, int start, int length) const {
- ENTER_V8;
if (IsDeadCheck("v8::String::Write()")) return 0;
LOG_API("String::Write");
+ ENTER_V8;
ASSERT(start >= 0 && length >= -1);
i::Handle<i::String> str = Utils::OpenHandle(this);
// Flatten the string for efficiency. This applies whether we are
@@ -2287,7 +2249,6 @@
bool v8::String::IsExternal() const {
- ENTER_V8;
EnsureInitialized("v8::String::IsExternal()");
i::Handle<i::String> str = Utils::OpenHandle(this);
return i::StringShape(*str).IsExternalTwoByte();
@@ -2295,7 +2256,6 @@
bool v8::String::IsExternalAscii() const {
- ENTER_V8;
EnsureInitialized("v8::String::IsExternalAscii()");
i::Handle<i::String> str = Utils::OpenHandle(this);
return i::StringShape(*str).IsExternalAscii();
@@ -2304,7 +2264,6 @@
v8::String::ExternalStringResource*
v8::String::GetExternalStringResource() const {
- ENTER_V8;
EnsureInitialized("v8::String::GetExternalStringResource()");
i::Handle<i::String> str = Utils::OpenHandle(this);
ASSERT(str->IsExternalTwoByteString());
@@ -2315,7 +2274,6 @@
v8::String::ExternalAsciiStringResource*
v8::String::GetExternalAsciiStringResource() const {
- ENTER_V8;
EnsureInitialized("v8::String::GetExternalAsciiStringResource()");
i::Handle<i::String> str = Utils::OpenHandle(this);
ASSERT(str->IsExternalAsciiString());
@@ -2325,7 +2283,6 @@
double Number::Value() const {
- ENTER_V8;
if (IsDeadCheck("v8::Number::Value()")) return 0;
i::Handle<i::Object> obj = Utils::OpenHandle(this);
return obj->Number();
@@ -2333,7 +2290,6 @@
bool Boolean::Value() const {
- ENTER_V8;
if (IsDeadCheck("v8::Boolean::Value()")) return false;
i::Handle<i::Object> obj = Utils::OpenHandle(this);
return obj->IsTrue();
@@ -2341,7 +2297,6 @@
int64_t Integer::Value() const {
- ENTER_V8;
if (IsDeadCheck("v8::Integer::Value()")) return 0;
i::Handle<i::Object> obj = Utils::OpenHandle(this);
if (obj->IsSmi()) {
@@ -2353,7 +2308,6 @@
int32_t Int32::Value() const {
- ENTER_V8;
if (IsDeadCheck("v8::Int32::Value()")) return 0;
i::Handle<i::Object> obj = Utils::OpenHandle(this);
if (obj->IsSmi()) {
@@ -2365,7 +2319,6 @@
int v8::Object::InternalFieldCount() {
- ENTER_V8;
if (IsDeadCheck("v8::Object::InternalFieldCount()")) return 0;
i::Handle<i::JSObject> obj = Utils::OpenHandle(this);
return obj->GetInternalFieldCount();
@@ -2373,7 +2326,6 @@
Local<Value> v8::Object::GetInternalField(int index) {
- ENTER_V8;
if (IsDeadCheck("v8::Object::GetInternalField()")) return Local<Value>();
i::Handle<i::JSObject> obj = Utils::OpenHandle(this);
if (!ApiCheck(index < obj->GetInternalFieldCount(),
@@ -2387,7 +2339,6 @@
void v8::Object::SetInternalField(int index, v8::Handle<Value> value) {
- ENTER_V8;
if (IsDeadCheck("v8::Object::SetInternalField()")) return;
i::Handle<i::JSObject> obj = Utils::OpenHandle(this);
if (!ApiCheck(index < obj->GetInternalFieldCount(),
@@ -2395,6 +2346,7 @@
"Writing internal field out of bounds")) {
return;
}
+ ENTER_V8;
i::Handle<i::Object> val = Utils::OpenHandle(*value);
obj->SetInternalField(index, *val);
}
@@ -2403,8 +2355,8 @@
// --- E n v i r o n m e n t ---
bool v8::V8::Initialize() {
- ENTER_V8;
if (i::V8::HasBeenSetup()) return true;
+ ENTER_V8;
HandleScope scope;
if (i::Snapshot::Initialize()) {
return true;
@@ -2421,7 +2373,7 @@
const char* v8::V8::GetVersion() {
- return "1.1.6";
+ return "1.1.7";
}
@@ -2441,59 +2393,66 @@
v8::ExtensionConfiguration* extensions,
v8::Handle<ObjectTemplate> global_template,
v8::Handle<Value> global_object) {
- ENTER_V8;
EnsureInitialized("v8::Context::New()");
LOG_API("Context::New");
ON_BAILOUT("v8::Context::New()", return Persistent<Context>());
- // Give the heap a chance to cleanup if we've disposed contexts.
- i::Heap::CollectAllGarbageIfContextDisposed();
+ // Enter V8 via an ENTER_V8 scope.
+ i::Handle<i::Context> env;
+ {
+ ENTER_V8;
+ // Give the heap a chance to cleanup if we've disposed contexts.
+ i::Heap::CollectAllGarbageIfContextDisposed();
- v8::Handle<ObjectTemplate> proxy_template = global_template;
- i::Handle<i::FunctionTemplateInfo> proxy_constructor;
- i::Handle<i::FunctionTemplateInfo> global_constructor;
+ v8::Handle<ObjectTemplate> proxy_template = global_template;
+ i::Handle<i::FunctionTemplateInfo> proxy_constructor;
+ i::Handle<i::FunctionTemplateInfo> global_constructor;
- if (!global_template.IsEmpty()) {
- // Make sure that the global_template has a constructor.
- global_constructor = EnsureConstructor(Utils::OpenHandle(*global_template));
+ if (!global_template.IsEmpty()) {
+ // Make sure that the global_template has a constructor.
+ global_constructor =
+ EnsureConstructor(Utils::OpenHandle(*global_template));
- // Create a fresh template for the global proxy object.
- proxy_template = ObjectTemplate::New();
- proxy_constructor = EnsureConstructor(Utils::OpenHandle(*proxy_template));
+ // Create a fresh template for the global proxy object.
+ proxy_template = ObjectTemplate::New();
+ proxy_constructor =
+ EnsureConstructor(Utils::OpenHandle(*proxy_template));
- // Set the global template to be the prototype template of global
- // proxy template.
- proxy_constructor->set_prototype_template(
- *Utils::OpenHandle(*global_template));
+ // Set the global template to be the prototype template of
+ // global proxy template.
+ proxy_constructor->set_prototype_template(
+ *Utils::OpenHandle(*global_template));
- // Migrate security handlers from global_template to
- // proxy_template. Temporarily removing access check information
- // from the global template.
- if (!global_constructor->access_check_info()->IsUndefined()) {
- proxy_constructor->set_access_check_info(
- global_constructor->access_check_info());
- proxy_constructor->set_needs_access_check(
- global_constructor->needs_access_check());
- global_constructor->set_needs_access_check(false);
- global_constructor->set_access_check_info(i::Heap::undefined_value());
+ // Migrate security handlers from global_template to
+ // proxy_template. Temporarily removing access check
+ // information from the global template.
+ if (!global_constructor->access_check_info()->IsUndefined()) {
+ proxy_constructor->set_access_check_info(
+ global_constructor->access_check_info());
+ proxy_constructor->set_needs_access_check(
+ global_constructor->needs_access_check());
+ global_constructor->set_needs_access_check(false);
+ global_constructor->set_access_check_info(i::Heap::undefined_value());
+ }
+ }
+
+ // Create the environment.
+ env = i::Bootstrapper::CreateEnvironment(
+ Utils::OpenHandle(*global_object),
+ proxy_template,
+ extensions);
+
+ // Restore the access check info on the global template.
+ if (!global_template.IsEmpty()) {
+ ASSERT(!global_constructor.is_null());
+ ASSERT(!proxy_constructor.is_null());
+ global_constructor->set_access_check_info(
+ proxy_constructor->access_check_info());
+ global_constructor->set_needs_access_check(
+ proxy_constructor->needs_access_check());
}
}
-
- // Create the environment.
- i::Handle<i::Context> env = i::Bootstrapper::CreateEnvironment(
- Utils::OpenHandle(*global_object),
- proxy_template,
- extensions);
-
- // Restore the access check info on the global template.
- if (!global_template.IsEmpty()) {
- ASSERT(!global_constructor.is_null());
- ASSERT(!proxy_constructor.is_null());
- global_constructor->set_access_check_info(
- proxy_constructor->access_check_info());
- global_constructor->set_needs_access_check(
- proxy_constructor->needs_access_check());
- }
+ // Leave V8.
if (!ApiCheck(!env.is_null(),
"v8::Context::New()",
@@ -2504,8 +2463,8 @@
void v8::Context::SetSecurityToken(Handle<Value> token) {
- ENTER_V8;
if (IsDeadCheck("v8::Context::SetSecurityToken()")) return;
+ ENTER_V8;
i::Handle<i::Context> env = Utils::OpenHandle(this);
i::Handle<i::Object> token_handle = Utils::OpenHandle(*token);
env->set_security_token(*token_handle);
@@ -2513,15 +2472,14 @@
void v8::Context::UseDefaultSecurityToken() {
- ENTER_V8;
if (IsDeadCheck("v8::Context::UseDefaultSecurityToken()")) return;
+ ENTER_V8;
i::Handle<i::Context> env = Utils::OpenHandle(this);
env->set_security_token(env->global());
}
Handle<Value> v8::Context::GetSecurityToken() {
- ENTER_V8;
if (IsDeadCheck("v8::Context::GetSecurityToken()")) return Handle<Value>();
i::Handle<i::Context> env = Utils::OpenHandle(this);
i::Object* security_token = env->security_token();
@@ -2542,7 +2500,6 @@
v8::Local<v8::Context> Context::GetEntered() {
- ENTER_V8;
if (IsDeadCheck("v8::Context::GetEntered()")) return Local<Context>();
i::Handle<i::Object> last = thread_local.LastEnteredContext();
if (last.is_null()) return Local<Context>();
@@ -2552,7 +2509,6 @@
v8::Local<v8::Context> Context::GetCurrent() {
- ENTER_V8;
if (IsDeadCheck("v8::Context::GetCurrent()")) return Local<Context>();
i::Handle<i::Context> context(i::Top::global_context());
return Utils::ToLocal(context);
@@ -2560,7 +2516,6 @@
v8::Local<v8::Object> Context::Global() {
- ENTER_V8;
if (IsDeadCheck("v8::Context::Global()")) return Local<v8::Object>();
i::Object** ctx = reinterpret_cast<i::Object**>(this);
i::Handle<i::Context> context =
@@ -2571,8 +2526,8 @@
void Context::DetachGlobal() {
- ENTER_V8;
if (IsDeadCheck("v8::Context::DetachGlobal()")) return;
+ ENTER_V8;
i::Object** ctx = reinterpret_cast<i::Object**>(this);
i::Handle<i::Context> context =
i::Handle<i::Context>::cast(i::Handle<i::Object>(ctx));
@@ -2581,9 +2536,9 @@
Local<v8::Object> ObjectTemplate::NewInstance() {
- ENTER_V8;
ON_BAILOUT("v8::ObjectTemplate::NewInstance()", return Local<v8::Object>());
LOG_API("ObjectTemplate::NewInstance");
+ ENTER_V8;
EXCEPTION_PREAMBLE();
i::Handle<i::Object> obj =
i::Execution::InstantiateObject(Utils::OpenHandle(this),
@@ -2594,10 +2549,10 @@
Local<v8::Function> FunctionTemplate::GetFunction() {
- ENTER_V8;
ON_BAILOUT("v8::FunctionTemplate::GetFunction()",
return Local<v8::Function>());
LOG_API("FunctionTemplate::GetFunction");
+ ENTER_V8;
EXCEPTION_PREAMBLE();
i::Handle<i::Object> obj =
i::Execution::InstantiateFunction(Utils::OpenHandle(this),
@@ -2608,7 +2563,6 @@
bool FunctionTemplate::HasInstance(v8::Handle<v8::Value> value) {
- ENTER_V8;
ON_BAILOUT("v8::FunctionTemplate::HasInstanceOf()", return false);
i::Object* obj = *Utils::OpenHandle(*value);
return obj->IsInstanceOf(*Utils::OpenHandle(this));
@@ -2629,10 +2583,10 @@
Local<Value> v8::External::Wrap(void* data) {
- ENTER_V8;
STATIC_ASSERT(sizeof(data) == sizeof(i::Address));
LOG_API("External::Wrap");
EnsureInitialized("v8::External::Wrap()");
+ ENTER_V8;
if ((reinterpret_cast<intptr_t>(data) & kAlignedPointerMask) == 0) {
uintptr_t data_ptr = reinterpret_cast<uintptr_t>(data);
int data_value = static_cast<int>(data_ptr >> kAlignedPointerShift);
@@ -2645,7 +2599,6 @@
void* v8::External::Unwrap(v8::Handle<v8::Value> value) {
- ENTER_V8;
if (IsDeadCheck("v8::External::Unwrap()")) return 0;
i::Handle<i::Object> obj = Utils::OpenHandle(*value);
if (obj->IsSmi()) {
@@ -2658,16 +2611,15 @@
Local<External> v8::External::New(void* data) {
- ENTER_V8;
STATIC_ASSERT(sizeof(data) == sizeof(i::Address));
LOG_API("External::New");
EnsureInitialized("v8::External::New()");
+ ENTER_V8;
return ExternalNewImpl(data);
}
void* External::Value() const {
- ENTER_V8;
if (IsDeadCheck("v8::External::Value()")) return 0;
i::Handle<i::Object> obj = Utils::OpenHandle(this);
return ExternalValueImpl(obj);
@@ -2675,7 +2627,6 @@
Local<String> v8::String::Empty() {
- ENTER_V8;
EnsureInitialized("v8::String::Empty()");
LOG_API("String::Empty()");
return Utils::ToLocal(i::Factory::empty_symbol());
@@ -2683,10 +2634,10 @@
Local<String> v8::String::New(const char* data, int length) {
- ENTER_V8;
EnsureInitialized("v8::String::New()");
LOG_API("String::New(char)");
if (length == 0) return Empty();
+ ENTER_V8;
if (length == -1) length = strlen(data);
i::Handle<i::String> result =
i::Factory::NewStringFromUtf8(i::Vector<const char>(data, length));
@@ -2695,9 +2646,9 @@
Local<String> v8::String::NewUndetectable(const char* data, int length) {
- ENTER_V8;
EnsureInitialized("v8::String::NewUndetectable()");
LOG_API("String::NewUndetectable(char)");
+ ENTER_V8;
if (length == -1) length = strlen(data);
i::Handle<i::String> result =
i::Factory::NewStringFromUtf8(i::Vector<const char>(data, length));
@@ -2714,10 +2665,10 @@
Local<String> v8::String::New(const uint16_t* data, int length) {
- ENTER_V8;
EnsureInitialized("v8::String::New()");
LOG_API("String::New(uint16_)");
if (length == 0) return Empty();
+ ENTER_V8;
if (length == -1) length = TwoByteStringLength(data);
i::Handle<i::String> result =
i::Factory::NewStringFromTwoByte(i::Vector<const uint16_t>(data, length));
@@ -2726,9 +2677,9 @@
Local<String> v8::String::NewUndetectable(const uint16_t* data, int length) {
- ENTER_V8;
EnsureInitialized("v8::String::NewUndetectable()");
LOG_API("String::NewUndetectable(uint16_)");
+ ENTER_V8;
if (length == -1) length = TwoByteStringLength(data);
i::Handle<i::String> result =
i::Factory::NewStringFromTwoByte(i::Vector<const uint16_t>(data, length));
@@ -2815,9 +2766,9 @@
Local<String> v8::String::NewExternal(
v8::String::ExternalStringResource* resource) {
- ENTER_V8;
EnsureInitialized("v8::String::NewExternal()");
LOG_API("String::NewExternal");
+ ENTER_V8;
const size_t total_size = resource->length() * sizeof(*resource->data());
i::Counters::total_external_string_memory.Increment(total_size);
i::Handle<i::String> result = NewExternalStringHandle(resource);
@@ -2830,9 +2781,9 @@
bool v8::String::MakeExternal(v8::String::ExternalStringResource* resource) {
- ENTER_V8;
if (IsDeadCheck("v8::String::MakeExternal()")) return false;
if (this->IsExternal()) return false; // Already an external string.
+ ENTER_V8;
i::Handle <i::String> obj = Utils::OpenHandle(this);
bool result = obj->MakeExternal(resource);
if (result && !obj->IsSymbol()) {
@@ -2850,9 +2801,9 @@
Local<String> v8::String::NewExternal(
v8::String::ExternalAsciiStringResource* resource) {
- ENTER_V8;
EnsureInitialized("v8::String::NewExternal()");
LOG_API("String::NewExternal");
+ ENTER_V8;
const size_t total_size = resource->length() * sizeof(*resource->data());
i::Counters::total_external_string_memory.Increment(total_size);
i::Handle<i::String> result = NewExternalAsciiStringHandle(resource);
@@ -2866,9 +2817,9 @@
bool v8::String::MakeExternal(
v8::String::ExternalAsciiStringResource* resource) {
- ENTER_V8;
if (IsDeadCheck("v8::String::MakeExternal()")) return false;
if (this->IsExternal()) return false; // Already an external string.
+ ENTER_V8;
i::Handle <i::String> obj = Utils::OpenHandle(this);
bool result = obj->MakeExternal(resource);
if (result && !obj->IsSymbol()) {
@@ -2885,9 +2836,9 @@
Local<v8::Object> v8::Object::New() {
- ENTER_V8;
EnsureInitialized("v8::Object::New()");
LOG_API("Object::New");
+ ENTER_V8;
i::Handle<i::JSObject> obj =
i::Factory::NewJSObject(i::Top::object_function());
return Utils::ToLocal(obj);
@@ -2895,9 +2846,9 @@
Local<v8::Value> v8::Date::New(double time) {
- ENTER_V8;
EnsureInitialized("v8::Date::New()");
LOG_API("Date::New");
+ ENTER_V8;
EXCEPTION_PREAMBLE();
i::Handle<i::Object> obj =
i::Execution::NewDate(time, &has_pending_exception);
@@ -2907,7 +2858,6 @@
double v8::Date::NumberValue() const {
- ENTER_V8;
if (IsDeadCheck("v8::Date::NumberValue()")) return 0;
LOG_API("Date::NumberValue");
i::Handle<i::Object> obj = Utils::OpenHandle(this);
@@ -2917,16 +2867,15 @@
Local<v8::Array> v8::Array::New(int length) {
- ENTER_V8;
EnsureInitialized("v8::Array::New()");
LOG_API("Array::New");
+ ENTER_V8;
i::Handle<i::JSArray> obj = i::Factory::NewJSArray(length);
return Utils::ToLocal(obj);
}
uint32_t v8::Array::Length() const {
- ENTER_V8;
if (IsDeadCheck("v8::Array::Length()")) return 0;
i::Handle<i::JSArray> obj = Utils::OpenHandle(this);
i::Object* length = obj->length();
@@ -2939,9 +2888,9 @@
Local<String> v8::String::NewSymbol(const char* data, int length) {
- ENTER_V8;
EnsureInitialized("v8::String::NewSymbol()");
LOG_API("String::NewSymbol(char)");
+ ENTER_V8;
if (length == -1) length = strlen(data);
i::Handle<i::String> result =
i::Factory::LookupSymbol(i::Vector<const char>(data, length));
@@ -2950,19 +2899,19 @@
Local<Number> v8::Number::New(double value) {
- ENTER_V8;
EnsureInitialized("v8::Number::New()");
+ ENTER_V8;
i::Handle<i::Object> result = i::Factory::NewNumber(value);
return Utils::NumberToLocal(result);
}
Local<Integer> v8::Integer::New(int32_t value) {
- ENTER_V8;
EnsureInitialized("v8::Integer::New()");
if (i::Smi::IsValid(value)) {
return Utils::IntegerToLocal(i::Handle<i::Object>(i::Smi::FromInt(value)));
}
+ ENTER_V8;
i::Handle<i::Object> result = i::Factory::NewNumber(value);
return Utils::IntegerToLocal(result);
}
@@ -2974,9 +2923,9 @@
bool V8::AddMessageListener(MessageCallback that, Handle<Value> data) {
- ENTER_V8;
EnsureInitialized("v8::V8::AddMessageListener()");
ON_BAILOUT("v8::V8::AddMessageListener()", return false);
+ ENTER_V8;
HandleScope scope;
NeanderArray listeners(i::Factory::message_listeners());
NeanderObject obj(2);
@@ -2990,9 +2939,9 @@
void V8::RemoveMessageListeners(MessageCallback that) {
- ENTER_V8;
EnsureInitialized("v8::V8::RemoveMessageListener()");
ON_BAILOUT("v8::V8::RemoveMessageListeners()", return);
+ ENTER_V8;
HandleScope scope;
NeanderArray listeners(i::Factory::message_listeners());
for (int i = 0; i < listeners.length(); i++) {
@@ -3008,25 +2957,21 @@
void V8::SetCounterFunction(CounterLookupCallback callback) {
- ENTER_V8;
if (IsDeadCheck("v8::V8::SetCounterFunction()")) return;
i::StatsTable::SetCounterFunction(callback);
}
void V8::SetCreateHistogramFunction(CreateHistogramCallback callback) {
- ENTER_V8;
if (IsDeadCheck("v8::V8::SetCreateHistogramFunction()")) return;
i::StatsTable::SetCreateHistogramFunction(callback);
}
void V8::SetAddHistogramSampleFunction(AddHistogramSampleCallback callback) {
- ENTER_V8;
if (IsDeadCheck("v8::V8::SetAddHistogramSampleFunction()")) return;
i::StatsTable::SetAddHistogramSampleFunction(callback);
}
void V8::EnableSlidingStateWindow() {
- ENTER_V8;
if (IsDeadCheck("v8::V8::EnableSlidingStateWindow()")) return;
i::Logger::EnableSlidingStateWindow();
}
@@ -3034,14 +2979,12 @@
void V8::SetFailedAccessCheckCallbackFunction(
FailedAccessCheckCallback callback) {
- ENTER_V8;
if (IsDeadCheck("v8::V8::SetFailedAccessCheckCallbackFunction()")) return;
i::Top::SetFailedAccessCheckCallback(callback);
}
void V8::AddObjectGroup(Persistent<Value>* objects, size_t length) {
- ENTER_V8;
if (IsDeadCheck("v8::V8::AddObjectGroup()")) return;
STATIC_ASSERT(sizeof(Persistent<Value>) == sizeof(i::Object**));
i::GlobalHandles::AddGroup(reinterpret_cast<i::Object***>(objects), length);
@@ -3049,21 +2992,18 @@
int V8::AdjustAmountOfExternalAllocatedMemory(int change_in_bytes) {
- ENTER_V8;
if (IsDeadCheck("v8::V8::AdjustAmountOfExternalAllocatedMemory()")) return 0;
return i::Heap::AdjustAmountOfExternalAllocatedMemory(change_in_bytes);
}
void V8::SetGlobalGCPrologueCallback(GCCallback callback) {
- ENTER_V8;
if (IsDeadCheck("v8::V8::SetGlobalGCPrologueCallback()")) return;
i::Heap::SetGlobalGCPrologueCallback(callback);
}
void V8::SetGlobalGCEpilogueCallback(GCCallback callback) {
- ENTER_V8;
if (IsDeadCheck("v8::V8::SetGlobalGCEpilogueCallback()")) return;
i::Heap::SetGlobalGCEpilogueCallback(callback);
}
@@ -3083,13 +3023,13 @@
String::Utf8Value::Utf8Value(v8::Handle<v8::Value> obj) {
- ENTER_V8;
EnsureInitialized("v8::String::Utf8Value::Utf8Value()");
if (obj.IsEmpty()) {
str_ = NULL;
length_ = 0;
return;
}
+ ENTER_V8;
HandleScope scope;
TryCatch try_catch;
Handle<String> str = obj->ToString();
@@ -3110,13 +3050,13 @@
String::AsciiValue::AsciiValue(v8::Handle<v8::Value> obj) {
- ENTER_V8;
EnsureInitialized("v8::String::AsciiValue::AsciiValue()");
if (obj.IsEmpty()) {
str_ = NULL;
length_ = 0;
return;
}
+ ENTER_V8;
HandleScope scope;
TryCatch try_catch;
Handle<String> str = obj->ToString();
@@ -3137,13 +3077,13 @@
String::Value::Value(v8::Handle<v8::Value> obj) {
- ENTER_V8;
EnsureInitialized("v8::String::Value::Value()");
if (obj.IsEmpty()) {
str_ = NULL;
length_ = 0;
return;
}
+ ENTER_V8;
HandleScope scope;
TryCatch try_catch;
Handle<String> str = obj->ToString();
@@ -3163,9 +3103,9 @@
}
Local<Value> Exception::RangeError(v8::Handle<v8::String> raw_message) {
- ENTER_V8;
LOG_API("RangeError");
ON_BAILOUT("v8::Exception::RangeError()", return Local<Value>());
+ ENTER_V8;
i::Object* error;
{
HandleScope scope;
@@ -3178,9 +3118,9 @@
}
Local<Value> Exception::ReferenceError(v8::Handle<v8::String> raw_message) {
- ENTER_V8;
LOG_API("ReferenceError");
ON_BAILOUT("v8::Exception::ReferenceError()", return Local<Value>());
+ ENTER_V8;
i::Object* error;
{
HandleScope scope;
@@ -3193,9 +3133,9 @@
}
Local<Value> Exception::SyntaxError(v8::Handle<v8::String> raw_message) {
- ENTER_V8;
LOG_API("SyntaxError");
ON_BAILOUT("v8::Exception::SyntaxError()", return Local<Value>());
+ ENTER_V8;
i::Object* error;
{
HandleScope scope;
@@ -3208,9 +3148,9 @@
}
Local<Value> Exception::TypeError(v8::Handle<v8::String> raw_message) {
- ENTER_V8;
LOG_API("TypeError");
ON_BAILOUT("v8::Exception::TypeError()", return Local<Value>());
+ ENTER_V8;
i::Object* error;
{
HandleScope scope;
@@ -3223,9 +3163,9 @@
}
Local<Value> Exception::Error(v8::Handle<v8::String> raw_message) {
- ENTER_V8;
LOG_API("Error");
ON_BAILOUT("v8::Exception::Error()", return Local<Value>());
+ ENTER_V8;
i::Object* error;
{
HandleScope scope;
@@ -3242,9 +3182,9 @@
bool Debug::SetDebugEventListener(DebugEventCallback that, Handle<Value> data) {
- ENTER_V8;
EnsureInitialized("v8::Debug::SetDebugEventListener()");
ON_BAILOUT("v8::Debug::SetDebugEventListener()", return false);
+ ENTER_V8;
HandleScope scope;
i::Handle<i::Object> proxy = i::Factory::undefined_value();
if (that != NULL) {
@@ -3257,8 +3197,8 @@
bool Debug::SetDebugEventListener(v8::Handle<v8::Object> that,
Handle<Value> data) {
- ENTER_V8;
ON_BAILOUT("v8::Debug::SetDebugEventListener()", return false);
+ ENTER_V8;
i::Debugger::SetEventListener(Utils::OpenHandle(*that),
Utils::OpenHandle(*data));
return true;
@@ -3273,8 +3213,8 @@
void Debug::SetMessageHandler(v8::DebugMessageHandler handler, void* data,
bool message_handler_thread) {
- ENTER_V8;
EnsureInitialized("v8::Debug::SetMessageHandler");
+ ENTER_V8;
i::Debugger::SetMessageHandler(handler, data, message_handler_thread);
}
@@ -3287,8 +3227,8 @@
void Debug::SetHostDispatchHandler(v8::DebugHostDispatchHandler handler,
void* data) {
- ENTER_V8;
EnsureInitialized("v8::Debug::SetHostDispatchHandler");
+ ENTER_V8;
i::Debugger::SetHostDispatchHandler(handler, data);
}
@@ -3301,9 +3241,9 @@
Handle<Value> Debug::Call(v8::Handle<v8::Function> fun,
v8::Handle<v8::Value> data) {
- ENTER_V8;
if (!i::V8::HasBeenSetup()) return Handle<Value>();
ON_BAILOUT("v8::Debug::Call()", return Handle<Value>());
+ ENTER_V8;
i::Handle<i::Object> result;
EXCEPTION_PREAMBLE();
if (data.IsEmpty()) {
diff --git a/src/ast.h b/src/ast.h
index 0da58f1..5dfd21d 100644
--- a/src/ast.h
+++ b/src/ast.h
@@ -163,10 +163,10 @@
virtual void MarkAsStatement() { /* do nothing */ }
// Static type information for this expression.
- StaticType* type() { return &type_; }
+ SmiAnalysis* type() { return &type_; }
private:
- StaticType type_;
+ SmiAnalysis type_;
};
diff --git a/src/builtins.h b/src/builtins.h
index fa443fa..853c90e 100644
--- a/src/builtins.h
+++ b/src/builtins.h
@@ -105,8 +105,6 @@
V(MUL, 1) \
V(DIV, 1) \
V(MOD, 1) \
- V(INC, 0) \
- V(DEC, 0) \
V(BIT_OR, 1) \
V(BIT_AND, 1) \
V(BIT_XOR, 1) \
@@ -124,6 +122,8 @@
V(TO_OBJECT, 0) \
V(TO_NUMBER, 0) \
V(TO_STRING, 0) \
+ V(STRING_ADD_LEFT, 1) \
+ V(STRING_ADD_RIGHT, 1) \
V(APPLY_PREPARE, 1) \
V(APPLY_OVERFLOW, 1)
diff --git a/src/codegen-arm.cc b/src/codegen-arm.cc
index 0823f77..37a7cf2 100644
--- a/src/codegen-arm.cc
+++ b/src/codegen-arm.cc
@@ -708,29 +708,6 @@
};
-class InvokeBuiltinStub : public CodeStub {
- public:
- enum Kind { Inc, Dec, ToNumber };
- InvokeBuiltinStub(Kind kind, int argc) : kind_(kind), argc_(argc) { }
-
- private:
- Kind kind_;
- int argc_;
-
- Major MajorKey() { return InvokeBuiltin; }
- int MinorKey() { return (argc_ << 3) | static_cast<int>(kind_); }
- void Generate(MacroAssembler* masm);
-
-#ifdef DEBUG
- void Print() {
- PrintF("InvokeBuiltinStub (kind %d, argc, %d)\n",
- static_cast<int>(kind_),
- argc_);
- }
-#endif
-};
-
-
void CodeGenerator::GenericBinaryOperation(Token::Value op) {
VirtualFrame::SpilledScope spilled_scope(this);
// sp[0] : y
@@ -3696,22 +3673,27 @@
// Slow case: Convert to number.
slow.Bind();
-
- // Postfix: Convert the operand to a number and store it as the result.
+ {
+ // Convert the operand to a number.
+ frame_->EmitPush(r0);
+ Result arg_count = allocator_->Allocate(r0);
+ ASSERT(arg_count.is_valid());
+ __ mov(arg_count.reg(), Operand(0));
+ frame_->InvokeBuiltin(Builtins::TO_NUMBER, CALL_JS, &arg_count, 1);
+ }
if (is_postfix) {
- InvokeBuiltinStub stub(InvokeBuiltinStub::ToNumber, 2);
- frame_->CallStub(&stub, 0);
- // Store to result (on the stack).
+ // Postfix: store to result (on the stack).
__ str(r0, frame_->ElementAt(target.size()));
}
- // Compute the new value by calling the right JavaScript native.
+ // Compute the new value.
+ __ mov(r1, Operand(Smi::FromInt(1)));
+ frame_->EmitPush(r0);
+ frame_->EmitPush(r1);
if (is_increment) {
- InvokeBuiltinStub stub(InvokeBuiltinStub::Inc, 1);
- frame_->CallStub(&stub, 0);
+ frame_->CallRuntime(Runtime::kNumberAdd, 2);
} else {
- InvokeBuiltinStub stub(InvokeBuiltinStub::Dec, 1);
- frame_->CallStub(&stub, 0);
+ frame_->CallRuntime(Runtime::kNumberSub, 2);
}
// Store the new value in the target if not const.
@@ -4718,19 +4700,6 @@
}
-void InvokeBuiltinStub::Generate(MacroAssembler* masm) {
- __ push(r0);
- __ mov(r0, Operand(0)); // set number of arguments
- switch (kind_) {
- case ToNumber: __ InvokeBuiltin(Builtins::TO_NUMBER, JUMP_JS); break;
- case Inc: __ InvokeBuiltin(Builtins::INC, JUMP_JS); break;
- case Dec: __ InvokeBuiltin(Builtins::DEC, JUMP_JS); break;
- default: UNREACHABLE();
- }
- __ StubReturn(argc_);
-}
-
-
void CEntryStub::GenerateThrowTOS(MacroAssembler* masm) {
// r0 holds exception
ASSERT(StackHandlerConstants::kSize == 6 * kPointerSize); // adjust this code
diff --git a/src/codegen-arm.h b/src/codegen-arm.h
index 7c4b069..54c5e88 100644
--- a/src/codegen-arm.h
+++ b/src/codegen-arm.h
@@ -394,6 +394,7 @@
// positions are collected by the assembler and emitted with the relocation
// information.
void CodeForFunctionPosition(FunctionLiteral* fun);
+ void CodeForReturnPosition(FunctionLiteral* fun);
void CodeForStatementPosition(Node* node);
void CodeForSourcePosition(int pos);
diff --git a/src/codegen-ia32.cc b/src/codegen-ia32.cc
index b6d8c01..714e5bd 100644
--- a/src/codegen-ia32.cc
+++ b/src/codegen-ia32.cc
@@ -274,25 +274,29 @@
if (has_valid_frame()) {
// If there is a valid frame, control flow can fall off the end of
// the body. In that case there is an implicit return statement.
- // Compiling a return statement will jump to the return sequence if
- // it is already generated or generate it if not.
ASSERT(!function_return_is_shadowed_);
- Literal undefined(Factory::undefined_value());
- ReturnStatement statement(&undefined);
- statement.set_statement_pos(fun->end_position());
- VisitReturnStatement(&statement);
+ CodeForReturnPosition(fun);
+ frame_->PrepareForReturn();
+ Result undefined(Factory::undefined_value(), this);
+ if (function_return_.is_bound()) {
+ function_return_.Jump(&undefined);
+ } else {
+ // Though this is a (possibly) backward block, the frames
+ // can only differ on their top element.
+ function_return_.Bind(&undefined, 1);
+ GenerateReturnSequence(&undefined);
+ }
} else if (function_return_.is_linked()) {
// If the return target has dangling jumps to it, then we have not
// yet generated the return sequence. This can happen when (a)
// control does not flow off the end of the body so we did not
// compile an artificial return statement just above, and (b) there
// are return statements in the body but (c) they are all shadowed.
- //
- // There is no valid frame here but it is safe (also necessary) to
- // load the return value into eax.
- __ mov(eax, Immediate(Factory::undefined_value()));
- function_return_.Bind();
- GenerateReturnSequence();
+ Result return_value(this);
+ // Though this is a (possibly) backward block, the frames can
+ // only differ on their top element.
+ function_return_.Bind(&return_value, 1);
+ GenerateReturnSequence(&return_value);
}
}
}
@@ -803,7 +807,7 @@
void CodeGenerator::GenericBinaryOperation(Token::Value op,
- StaticType* type,
+ SmiAnalysis* type,
OverwriteMode overwrite_mode) {
Comment cmnt(masm_, "[ BinaryOperation");
Comment cmnt_token(masm_, Token::String(op));
@@ -841,6 +845,34 @@
Result right = frame_->Pop();
Result left = frame_->Pop();
+
+ if (op == Token::ADD) {
+ bool left_is_string = left.static_type().is_jsstring();
+ bool right_is_string = right.static_type().is_jsstring();
+ if (left_is_string || right_is_string) {
+ frame_->Push(&left);
+ frame_->Push(&right);
+ Result answer(this);
+ if (left_is_string) {
+ if (right_is_string) {
+ // TODO(lrn): if (left.is_constant() && right.is_constant())
+ // -- do a compile time cons, if allocation during codegen is allowed.
+ answer = frame_->CallRuntime(Runtime::kStringAdd, 2);
+ } else {
+ answer =
+ frame_->InvokeBuiltin(Builtins::STRING_ADD_LEFT, CALL_FUNCTION, 2);
+ }
+ } else if (right_is_string) {
+ answer =
+ frame_->InvokeBuiltin(Builtins::STRING_ADD_RIGHT, CALL_FUNCTION, 2);
+ }
+ answer.set_static_type(StaticType::jsstring());
+ frame_->Push(&answer);
+ return;
+ }
+ // Neither operand is known to be a string.
+ }
+
bool left_is_smi = left.is_constant() && left.handle()->IsSmi();
bool left_is_non_smi = left.is_constant() && !left.handle()->IsSmi();
bool right_is_smi = right.is_constant() && right.handle()->IsSmi();
@@ -1185,7 +1217,7 @@
void CodeGenerator::ConstantSmiBinaryOperation(Token::Value op,
Result* operand,
Handle<Object> value,
- StaticType* type,
+ SmiAnalysis* type,
bool reversed,
OverwriteMode overwrite_mode) {
// NOTE: This is an attempt to inline (a bit) more of the code for
@@ -1943,52 +1975,37 @@
ASSERT(!in_spilled_code());
Comment cmnt(masm_, "[ ReturnStatement");
+ CodeForStatementPosition(node);
+ Load(node->expression());
+ Result return_value = frame_->Pop();
if (function_return_is_shadowed_) {
- // If the function return is shadowed, we spill all information
- // and just jump to the label.
- VirtualFrame::SpilledScope spilled_scope(this);
- CodeForStatementPosition(node);
- LoadAndSpill(node->expression());
- frame_->EmitPop(eax);
- function_return_.Jump();
+ function_return_.Jump(&return_value);
} else {
- // Load the returned value.
- CodeForStatementPosition(node);
- Load(node->expression());
-
- // Pop the result from the frame and prepare the frame for
- // returning thus making it easier to merge.
- Result result = frame_->Pop();
frame_->PrepareForReturn();
-
- // Move the result into register eax where it belongs.
- result.ToRegister(eax);
- // TODO(203): Instead of explictly calling Unuse on the result, it
- // might be better to pass the result to Jump and Bind below.
- result.Unuse();
-
- // If the function return label is already bound, we reuse the
- // code by jumping to the return site.
if (function_return_.is_bound()) {
- function_return_.Jump();
+ // If the function return label is already bound we reuse the
+ // code by jumping to the return site.
+ function_return_.Jump(&return_value);
} else {
- function_return_.Bind();
- GenerateReturnSequence();
+ // Though this is a (possibly) backward block, the frames can
+ // only differ on their top element.
+ function_return_.Bind(&return_value, 1);
+ GenerateReturnSequence(&return_value);
}
}
}
-void CodeGenerator::GenerateReturnSequence() {
+void CodeGenerator::GenerateReturnSequence(Result* return_value) {
// The return value is a live (but not currently reference counted)
// reference to eax. This is safe because the current frame does not
// contain a reference to eax (it is prepared for the return by spilling
// all registers).
- ASSERT(has_valid_frame());
if (FLAG_trace) {
- frame_->Push(eax); // Materialize result on the stack.
- frame_->CallRuntime(Runtime::kTraceExit, 1);
+ frame_->Push(return_value);
+ *return_value = frame_->CallRuntime(Runtime::kTraceExit, 1);
}
+ return_value->ToRegister(eax);
// Add a label for checking the size of the code used for returning.
Label check_exit_codesize;
@@ -2921,14 +2938,22 @@
}
}
- // Generate unlink code for the (formerly) shadowing targets that have been
- // jumped to. Deallocate each shadow target.
+ // Generate unlink code for the (formerly) shadowing targets that
+ // have been jumped to. Deallocate each shadow target.
+ Result return_value(this);
for (int i = 0; i < shadows.length(); i++) {
if (shadows[i]->is_linked()) {
- // Unlink from try chain; be careful not to destroy the TOS.
- shadows[i]->Bind();
- // Because we can be jumping here (to spilled code) from unspilled
- // code, we need to reestablish a spilled frame at this block.
+ // Unlink from try chain; be careful not to destroy the TOS if
+ // there is one.
+ if (i == kReturnShadowIndex) {
+ shadows[i]->Bind(&return_value);
+ return_value.ToRegister(eax);
+ } else {
+ shadows[i]->Bind();
+ }
+ // Because we can be jumping here (to spilled code) from
+ // unspilled code, we need to reestablish a spilled frame at
+ // this block.
frame_->SpillAll();
// Reload sp from the top handler, because some statements that we
@@ -2943,10 +2968,12 @@
frame_->Drop(StackHandlerConstants::kSize / kPointerSize - 1);
// next_sp popped.
- if (!function_return_is_shadowed_ && i == kReturnShadowIndex) {
- frame_->PrepareForReturn();
+ if (i == kReturnShadowIndex) {
+ if (!function_return_is_shadowed_) frame_->PrepareForReturn();
+ shadows[i]->other_target()->Jump(&return_value);
+ } else {
+ shadows[i]->other_target()->Jump();
}
- shadows[i]->other_target()->Jump();
}
delete shadows[i];
}
@@ -3043,13 +3070,18 @@
for (int i = 0; i < shadows.length(); i++) {
if (shadows[i]->is_linked()) {
// If we have come from the shadowed return, the return value is
- // in (a non-refcounted reference to) eax. We must preserve it
- // until it is pushed.
- //
+ // on the virtual frame. We must preserve it until it is
+ // pushed.
+ if (i == kReturnShadowIndex) {
+ Result return_value(this);
+ shadows[i]->Bind(&return_value);
+ return_value.ToRegister(eax);
+ } else {
+ shadows[i]->Bind();
+ }
// Because we can be jumping here (to spilled code) from
// unspilled code, we need to reestablish a spilled frame at
// this block.
- shadows[i]->Bind();
frame_->SpillAll();
// Reload sp from the top handler, because some statements that
@@ -3103,14 +3135,23 @@
// formerly shadowing targets. Deallocate each shadow target.
for (int i = 0; i < shadows.length(); i++) {
if (has_valid_frame() && shadows[i]->is_bound()) {
- JumpTarget* original = shadows[i]->other_target();
+ BreakTarget* original = shadows[i]->other_target();
__ cmp(Operand(ecx), Immediate(Smi::FromInt(JUMPING + i)));
- if (!function_return_is_shadowed_ && i == kReturnShadowIndex) {
- JumpTarget skip(this);
- skip.Branch(not_equal);
- frame_->PrepareForReturn();
- original->Jump();
- skip.Bind();
+ if (i == kReturnShadowIndex) {
+ // The return value is (already) in eax.
+ Result return_value = allocator_->Allocate(eax);
+ ASSERT(return_value.is_valid());
+ if (function_return_is_shadowed_) {
+ original->Branch(equal, &return_value);
+ } else {
+ // Branch around the preparation for return which may emit
+ // code.
+ JumpTarget skip(this);
+ skip.Branch(not_equal);
+ frame_->PrepareForReturn();
+ original->Jump(&return_value);
+ skip.Bind();
+ }
} else {
original->Branch(equal);
}
@@ -3486,8 +3527,8 @@
void CodeGenerator::VisitLiteral(Literal* node) {
Comment cmnt(masm_, "[ Literal");
- frame_->Push(node->handle());
- }
+ frame_->Push(node->handle());
+}
void CodeGenerator::LoadUnsafeSmi(Register target, Handle<Object> value) {
@@ -4683,11 +4724,11 @@
DeferredCountOperation(CodeGenerator* generator,
bool is_postfix,
bool is_increment,
- int result_offset)
+ int target_size)
: DeferredCode(generator),
is_postfix_(is_postfix),
is_increment_(is_increment),
- result_offset_(result_offset) {
+ target_size_(target_size) {
set_comment("[ DeferredCountOperation");
}
@@ -4696,75 +4737,38 @@
private:
bool is_postfix_;
bool is_increment_;
- int result_offset_;
-};
-
-
-class RevertToNumberStub: public CodeStub {
- public:
- explicit RevertToNumberStub(bool is_increment)
- : is_increment_(is_increment) { }
-
- private:
- bool is_increment_;
-
- Major MajorKey() { return RevertToNumber; }
- int MinorKey() { return is_increment_ ? 1 : 0; }
- void Generate(MacroAssembler* masm);
-
-#ifdef DEBUG
- void Print() {
- PrintF("RevertToNumberStub (is_increment %s)\n",
- is_increment_ ? "true" : "false");
- }
-#endif
-};
-
-
-class CounterOpStub: public CodeStub {
- public:
- CounterOpStub(int result_offset, bool is_postfix, bool is_increment)
- : result_offset_(result_offset),
- is_postfix_(is_postfix),
- is_increment_(is_increment) { }
-
- private:
- int result_offset_;
- bool is_postfix_;
- bool is_increment_;
-
- Major MajorKey() { return CounterOp; }
- int MinorKey() {
- return ((result_offset_ << 2) |
- (is_postfix_ ? 2 : 0) |
- (is_increment_ ? 1 : 0));
- }
- void Generate(MacroAssembler* masm);
-
-#ifdef DEBUG
- void Print() {
- PrintF("CounterOpStub (result_offset %d), (is_postfix %s),"
- " (is_increment %s)\n",
- result_offset_,
- is_postfix_ ? "true" : "false",
- is_increment_ ? "true" : "false");
- }
-#endif
+ int target_size_;
};
void DeferredCountOperation::Generate() {
CodeGenerator* cgen = generator();
-
Result value(cgen);
enter()->Bind(&value);
- if (is_postfix_) {
- RevertToNumberStub to_number_stub(is_increment_);
- value = generator()->frame()->CallStub(&to_number_stub, &value);
+ VirtualFrame* frame = cgen->frame();
+ // Undo the optimistic smi operation.
+ value.ToRegister();
+ frame->Spill(value.reg());
+ if (is_increment_) {
+ __ sub(Operand(value.reg()), Immediate(Smi::FromInt(1)));
+ } else {
+ __ add(Operand(value.reg()), Immediate(Smi::FromInt(1)));
}
-
- CounterOpStub stub(result_offset_, is_postfix_, is_increment_);
- value = generator()->frame()->CallStub(&stub, &value);
+ frame->Push(&value);
+ value = frame->InvokeBuiltin(Builtins::TO_NUMBER, CALL_FUNCTION, 1);
+ frame->Push(&value);
+ if (is_postfix_) { // Fix up copy of old value with ToNumber(value).
+ // This is only safe because VisitCountOperation makes this frame slot
+ // beneath the reference a register, which is spilled at the above call.
+ // We cannot safely write to constants or copies below the water line.
+ frame->StoreToElementAt(target_size_ + 1);
+ }
+ frame->Push(Smi::FromInt(1));
+ if (is_increment_) {
+ value = frame->CallRuntime(Runtime::kNumberAdd, 2);
+ } else {
+ value = frame->CallRuntime(Runtime::kNumberSub, 2);
+ }
exit_.Jump(&value);
}
@@ -4778,7 +4782,8 @@
Variable* var = node->expression()->AsVariableProxy()->AsVariable();
bool is_const = (var != NULL && var->mode() == Variable::CONST);
- // Postfix: Make room for the result.
+ // Postfix operators need a stack slot under the reference to hold
+ // the old value while the new one is being stored.
if (is_postfix) {
frame_->Push(Smi::FromInt(0));
}
@@ -4795,16 +4800,21 @@
target.TakeValue(NOT_INSIDE_TYPEOF);
DeferredCountOperation* deferred =
- new DeferredCountOperation(this, is_postfix, is_increment,
- target.size() * kPointerSize);
+ new DeferredCountOperation(this, is_postfix,
+ is_increment, target.size());
Result value = frame_->Pop();
value.ToRegister();
- ASSERT(value.is_valid());
// Postfix: Store the old value as the result.
if (is_postfix) {
- Result old_value = value;
+ // Explicitly back the slot for the old value with a new register.
+ // This improves performance in some cases.
+ Result old_value = allocator_->Allocate();
+ ASSERT(old_value.is_valid());
+ __ mov(old_value.reg(), value.reg());
+ // SetElement must not create a constant element or a copy in this slot,
+ // since we will write to it, below the waterline, in deferred code.
frame_->SetElementAt(target.size(), &old_value);
}
@@ -4844,7 +4854,7 @@
tmp.Unuse();
__ test(value.reg(), Immediate(kSmiTagMask));
deferred->enter()->Branch(not_zero, &value, not_taken);
- } else {
+ } else { // Otherwise we test separately for overflow and smi check.
deferred->enter()->Branch(overflow, &value, not_taken);
__ test(value.reg(), Immediate(kSmiTagMask));
deferred->enter()->Branch(not_zero, &value, not_taken);
@@ -6500,69 +6510,130 @@
void CompareStub::Generate(MacroAssembler* masm) {
Label call_builtin, done;
- // If we're doing a strict equality comparison, we generate code
- // to do fast comparison for objects and oddballs. Numbers and
- // strings still go through the usual slow-case code.
- if (strict_) {
- Label slow;
- __ test(eax, Immediate(kSmiTagMask));
- __ j(zero, &slow);
+ // NOTICE! This code is only reached after a smi-fast-case check, so
+ // it is certain that at least one operand isn't a smi.
- // Get the type of the first operand.
- __ mov(ecx, FieldOperand(eax, HeapObject::kMapOffset));
- __ movzx_b(ecx, FieldOperand(ecx, Map::kInstanceTypeOffset));
+ if (cc_ == equal) { // Both strict and non-strict.
+ Label slow; // Fallthrough label.
+ // Equality is almost reflexive (everything but NaN), so start by testing
+ // for "identity and not NaN".
+ {
+ Label not_identical;
+ __ cmp(eax, Operand(edx));
+ __ j(not_equal, ¬_identical);
+ // Test for NaN. Sadly, we can't just compare to Factory::nan_value(),
+ // so we do the second best thing - test it ourselves.
- // If the first object is an object, we do pointer comparison.
- ASSERT(LAST_TYPE == JS_FUNCTION_TYPE);
- Label non_object;
- __ cmp(ecx, FIRST_JS_OBJECT_TYPE);
- __ j(less, &non_object);
- __ sub(eax, Operand(edx));
- __ ret(0);
+ Label return_equal;
+ Label heap_number;
+ // If it's not a heap number, then return equal.
+ __ cmp(FieldOperand(edx, HeapObject::kMapOffset),
+ Immediate(Factory::heap_number_map()));
+ __ j(equal, &heap_number);
+ __ bind(&return_equal);
+ __ Set(eax, Immediate(0));
+ __ ret(0);
- // Check for oddballs: true, false, null, undefined.
- __ bind(&non_object);
- __ cmp(ecx, ODDBALL_TYPE);
- __ j(not_equal, &slow);
+ __ bind(&heap_number);
+ // It is a heap number, so return non-equal if it's NaN and equal if it's
+ // not NaN.
+ // The representation of NaN values has all exponent bits (52..62) set,
+ // and not all mantissa bits (0..51) clear.
+ // Read top bits of double representation (second word of value).
+ __ mov(eax, FieldOperand(edx, HeapNumber::kValueOffset + kPointerSize));
+ // Test that exponent bits are all set.
+ __ not_(eax);
+ __ test(eax, Immediate(0x7ff00000));
+ __ j(not_zero, &return_equal);
+ __ not_(eax);
- // If the oddball isn't undefined, we do pointer comparison. For
- // the undefined value, we have to be careful and check for
- // 'undetectable' objects too.
- Label undefined;
- __ cmp(Operand(eax), Immediate(Factory::undefined_value()));
- __ j(equal, &undefined);
- __ sub(eax, Operand(edx));
- __ ret(0);
+ // Shift out flag and all exponent bits, retaining only mantissa.
+ __ shl(eax, 12);
+ // Or with all low-bits of mantissa.
+ __ or_(eax, FieldOperand(edx, HeapNumber::kValueOffset));
+ // Return zero equal if all bits in mantissa is zero (it's an Infinity)
+ // and non-zero if not (it's a NaN).
+ __ ret(0);
- // Undefined case: If the other operand isn't undefined too, we
- // have to check if it's 'undetectable'.
- Label check_undetectable;
- __ bind(&undefined);
- __ cmp(Operand(edx), Immediate(Factory::undefined_value()));
- __ j(not_equal, &check_undetectable);
- __ Set(eax, Immediate(0));
- __ ret(0);
+ __ bind(¬_identical);
+ }
- // Check for undetectability of the other operand.
- Label not_strictly_equal;
- __ bind(&check_undetectable);
- __ test(edx, Immediate(kSmiTagMask));
- __ j(zero, ¬_strictly_equal);
- __ mov(ecx, FieldOperand(edx, HeapObject::kMapOffset));
- __ movzx_b(ecx, FieldOperand(ecx, Map::kBitFieldOffset));
- __ and_(ecx, 1 << Map::kIsUndetectable);
- __ cmp(ecx, 1 << Map::kIsUndetectable);
- __ j(not_equal, ¬_strictly_equal);
- __ Set(eax, Immediate(0));
- __ ret(0);
+ // If we're doing a strict equality comparison, we don't have to do
+ // type conversion, so we generate code to do fast comparison for objects
+ // and oddballs. Non-smi numbers and strings still go through the usual
+ // slow-case code.
+ if (strict_) {
+ // If either is a Smi (we know that not both are), then they can only
+ // be equal if the other is a HeapNumber. If so, use the slow case.
+ {
+ Label not_smis;
+ ASSERT_EQ(0, kSmiTag);
+ ASSERT_EQ(0, Smi::FromInt(0));
+ __ mov(ecx, Immediate(kSmiTagMask));
+ __ and_(ecx, Operand(eax));
+ __ test(ecx, Operand(edx));
+ __ j(not_zero, ¬_smis);
+ // One operand is a smi.
- // No cigar: Objects aren't strictly equal. Register eax contains
- // a non-smi value so it can't be 0. Just return.
- ASSERT(kHeapObjectTag != 0);
- __ bind(¬_strictly_equal);
- __ ret(0);
+ // Check whether the non-smi is a heap number.
+ ASSERT_EQ(1, kSmiTagMask);
+ // ecx still holds eax & kSmiTag, which is either zero or one.
+ __ sub(Operand(ecx), Immediate(0x01));
+ __ mov(ebx, edx);
+ __ xor_(ebx, Operand(eax));
+ __ and_(ebx, Operand(ecx)); // ebx holds either 0 or eax ^ edx.
+ __ xor_(ebx, Operand(eax));
+ // if eax was smi, ebx is now edx, else eax.
- // Fall through to the general case.
+ // Check if the non-smi operand is a heap number.
+ __ cmp(FieldOperand(ebx, HeapObject::kMapOffset),
+ Immediate(Factory::heap_number_map()));
+ // If heap number, handle it in the slow case.
+ __ j(equal, &slow);
+ // Return non-equal (ebx is not zero)
+ __ mov(eax, ebx);
+ __ ret(0);
+
+ __ bind(¬_smis);
+ }
+
+ // If either operand is a JSObject or an oddball value, then they are not
+ // equal since their pointers are different
+ // There is no test for undetectability in strict equality.
+
+ // Get the type of the first operand.
+ __ mov(ecx, FieldOperand(eax, HeapObject::kMapOffset));
+ __ movzx_b(ecx, FieldOperand(ecx, Map::kInstanceTypeOffset));
+
+ // If the first object is a JS object, we have done pointer comparison.
+ ASSERT(LAST_TYPE == JS_FUNCTION_TYPE);
+ Label first_non_object;
+ __ cmp(ecx, FIRST_JS_OBJECT_TYPE);
+ __ j(less, &first_non_object);
+
+ // Return non-zero (eax is not zero)
+ Label return_not_equal;
+ ASSERT(kHeapObjectTag != 0);
+ __ bind(&return_not_equal);
+ __ ret(0);
+
+ __ bind(&first_non_object);
+ // Check for oddballs: true, false, null, undefined.
+ __ cmp(ecx, ODDBALL_TYPE);
+ __ j(equal, &return_not_equal);
+
+ __ mov(ecx, FieldOperand(edx, HeapObject::kMapOffset));
+ __ movzx_b(ecx, FieldOperand(ecx, Map::kInstanceTypeOffset));
+
+ __ cmp(ecx, FIRST_JS_OBJECT_TYPE);
+ __ j(greater_equal, &return_not_equal);
+
+ // Check for oddballs: true, false, null, undefined.
+ __ cmp(ecx, ODDBALL_TYPE);
+ __ j(equal, &return_not_equal);
+
+ // Fall through to the general case.
+ }
__ bind(&slow);
}
@@ -6675,49 +6746,6 @@
}
-void RevertToNumberStub::Generate(MacroAssembler* masm) {
- // Revert optimistic increment/decrement.
- if (is_increment_) {
- __ sub(Operand(eax), Immediate(Smi::FromInt(1)));
- } else {
- __ add(Operand(eax), Immediate(Smi::FromInt(1)));
- }
-
- __ pop(ecx);
- __ push(eax);
- __ push(ecx);
- __ InvokeBuiltin(Builtins::TO_NUMBER, JUMP_FUNCTION);
- // Code never returns due to JUMP_FUNCTION.
-}
-
-
-void CounterOpStub::Generate(MacroAssembler* masm) {
- // Store to the result on the stack (skip return address) before
- // performing the count operation.
- if (is_postfix_) {
- __ mov(Operand(esp, result_offset_ + kPointerSize), eax);
- }
-
- // Revert optimistic increment/decrement but only for prefix
- // counts. For postfix counts it has already been reverted before
- // the conversion to numbers.
- if (!is_postfix_) {
- if (is_increment_) {
- __ sub(Operand(eax), Immediate(Smi::FromInt(1)));
- } else {
- __ add(Operand(eax), Immediate(Smi::FromInt(1)));
- }
- }
-
- // Compute the new value by calling the right JavaScript native.
- __ pop(ecx);
- __ push(eax);
- __ push(ecx);
- Builtins::JavaScript builtin = is_increment_ ? Builtins::INC : Builtins::DEC;
- __ InvokeBuiltin(builtin, JUMP_FUNCTION);
- // Code never returns due to JUMP_FUNCTION.
-}
-
void CEntryStub::GenerateThrowTOS(MacroAssembler* masm) {
ASSERT(StackHandlerConstants::kSize == 6 * kPointerSize); // adjust this code
diff --git a/src/codegen-ia32.h b/src/codegen-ia32.h
index ceede0c..165f877 100644
--- a/src/codegen-ia32.h
+++ b/src/codegen-ia32.h
@@ -371,11 +371,10 @@
// Main code generation function
void GenCode(FunctionLiteral* fun);
- // Generate the return sequence code. Should be called no more than once
- // per compiled function (it binds the return target, which can not be
- // done more than once). The return value is assumed to be in eax by the
- // code generated.
- void GenerateReturnSequence();
+ // Generate the return sequence code. Should be called no more than
+ // once per compiled function, immediately after binding the return
+ // target (which can not be done more than once).
+ void GenerateReturnSequence(Result* return_value);
// The following are used by class Reference.
void LoadReference(Reference* ref);
@@ -432,8 +431,9 @@
// control destination.
void ToBoolean(ControlDestination* destination);
- void GenericBinaryOperation(Token::Value op,
- StaticType* type,
+ void GenericBinaryOperation(
+ Token::Value op,
+ SmiAnalysis* type,
const OverwriteMode overwrite_mode = NO_OVERWRITE);
// If possible, combine two constant smi values using op to produce
@@ -446,7 +446,7 @@
void ConstantSmiBinaryOperation(Token::Value op,
Result* operand,
Handle<Object> constant_operand,
- StaticType* type,
+ SmiAnalysis* type,
bool reversed,
OverwriteMode overwrite_mode);
@@ -567,6 +567,7 @@
// positions are collected by the assembler and emitted with the relocation
// information.
void CodeForFunctionPosition(FunctionLiteral* fun);
+ void CodeForReturnPosition(FunctionLiteral* fun);
void CodeForStatementPosition(Node* node);
void CodeForSourcePosition(int pos);
diff --git a/src/codegen.cc b/src/codegen.cc
index b00f002..c8f69c7 100644
--- a/src/codegen.cc
+++ b/src/codegen.cc
@@ -566,12 +566,23 @@
}
+void CodeGenerator::CodeForReturnPosition(FunctionLiteral* fun) {
+ if (FLAG_debug_info) {
+ int pos = fun->start_position();
+ if (pos != RelocInfo::kNoPosition) {
+ masm()->RecordStatementPosition(pos);
+ masm()->RecordPosition(pos);
+ }
+ }
+}
+
+
void CodeGenerator::CodeForStatementPosition(Node* node) {
if (FLAG_debug_info) {
int pos = node->statement_pos();
if (pos != RelocInfo::kNoPosition) {
masm()->RecordStatementPosition(pos);
- CodeForSourcePosition(pos);
+ masm()->RecordPosition(pos);
}
}
}
diff --git a/src/codegen.h b/src/codegen.h
index 3086638..dd43cc0 100644
--- a/src/codegen.h
+++ b/src/codegen.h
@@ -35,37 +35,41 @@
// Include the declaration of the architecture defined class CodeGenerator.
// The contract to the shared code is that the the CodeGenerator is a subclass
// of Visitor and that the following methods are available publicly:
-// CodeGenerator::MakeCode
-// CodeGenerator::SetFunctionInfo
-// CodeGenerator::masm
-// CodeGenerator::frame
-// CodeGenerator::has_valid_frame
-// CodeGenerator::SetFrame
-// CodeGenerator::DeleteFrame
-// CodeGenerator::allocator
-// CodeGenerator::AddDeferred
-// CodeGenerator::in_spilled_code
-// CodeGenerator::set_in_spilled_code
+// MakeCode
+// SetFunctionInfo
+// masm
+// frame
+// has_valid_frame
+// SetFrame
+// DeleteFrame
+// allocator
+// AddDeferred
+// in_spilled_code
+// set_in_spilled_code
//
// These methods are either used privately by the shared code or implemented as
// shared code:
-// CodeGenerator::CodeGenerator
-// CodeGenerator::~CodeGenerator
-// CodeGenerator::ProcessDeferred
-// CodeGenerator::ClearDeferred
-// CodeGenerator::GenCode
-// CodeGenerator::BuildBoilerplate
-// CodeGenerator::ComputeCallInitialize
-// CodeGenerator::ComputeCallInitializeInLoop
-// CodeGenerator::ProcessDeclarations
-// CodeGenerator::DeclareGlobals
-// CodeGenerator::CheckForInlineRuntimeCall
-// CodeGenerator::GenerateFastCaseSwitchStatement
-// CodeGenerator::GenerateFastCaseSwitchCases
-// CodeGenerator::TryGenerateFastCaseSwitchStatement
-// CodeGenerator::GenerateFastCaseSwitchJumpTable
-// CodeGenerator::FastCaseSwitchMinCaseCount
-// CodeGenerator::FastCaseSwitchMaxOverheadFactor
+// CodeGenerator
+// ~CodeGenerator
+// ProcessDeferred
+// ClearDeferred
+// GenCode
+// BuildBoilerplate
+// ComputeCallInitialize
+// ComputeCallInitializeInLoop
+// ProcessDeclarations
+// DeclareGlobals
+// CheckForInlineRuntimeCall
+// GenerateFastCaseSwitchStatement
+// GenerateFastCaseSwitchCases
+// TryGenerateFastCaseSwitchStatement
+// GenerateFastCaseSwitchJumpTable
+// FastCaseSwitchMinCaseCount
+// FastCaseSwitchMaxOverheadFactor
+// CodeForFunctionPosition
+// CodeForReturnPosition
+// CodeForStatementPosition
+// CodeForSourcePosition
#ifdef ARM
#include "codegen-arm.h"
diff --git a/src/compilation-cache.cc b/src/compilation-cache.cc
index 3775bc2..4c02d86 100644
--- a/src/compilation-cache.cc
+++ b/src/compilation-cache.cc
@@ -97,14 +97,65 @@
}
+// We only re-use a cached function for some script source code if the
+// script originates from the same place. This is to avoid issues
+// when reporting errors, etc.
+static bool HasOrigin(Handle<JSFunction> boilerplate,
+ Handle<Object> name,
+ int line_offset,
+ int column_offset) {
+ Handle<Script> script =
+ Handle<Script>(Script::cast(boilerplate->shared()->script()));
+ // If the script name isn't set, the boilerplate script should have
+ // an undefined name to have the same origin.
+ if (name.is_null()) {
+ return script->name()->IsUndefined();
+ }
+ // Do the fast bailout checks first.
+ if (line_offset != script->line_offset()->value()) return false;
+ if (column_offset != script->column_offset()->value()) return false;
+ // Check that both names are strings. If not, no match.
+ if (!name->IsString() || !script->name()->IsString()) return false;
+ // Compare the two name strings for equality.
+ return String::cast(*name)->Equals(String::cast(script->name()));
+}
+
+
+static Handle<JSFunction> Lookup(Handle<String> source,
+ CompilationCache::Entry entry) {
+ // Make sure not to leak the table into the surrounding handle
+ // scope. Otherwise, we risk keeping old tables around even after
+ // having cleared the cache.
+ Object* result;
+ { HandleScope scope;
+ Handle<CompilationCacheTable> table = GetTable(entry);
+ result = table->Lookup(*source);
+ }
+ if (result->IsJSFunction()) {
+ return Handle<JSFunction>(JSFunction::cast(result));
+ } else {
+ return Handle<JSFunction>::null();
+ }
+}
+
+
+// TODO(245): Need to allow identical code from different contexts to be
+// cached. Currently the first use will be cached, but subsequent code
+// from different source / line won't.
Handle<JSFunction> CompilationCache::LookupScript(Handle<String> source,
Handle<Object> name,
int line_offset,
int column_offset) {
- // TODO(245): Start caching scripts again but make it local to a
- // global context to avoid sharing code between independent
- // environments.
- return Handle<JSFunction>::null();
+ Handle<JSFunction> result = Lookup(source, SCRIPT);
+ if (result.is_null()) {
+ Counters::compilation_cache_misses.Increment();
+ } else if (HasOrigin(result, name, line_offset, column_offset)) {
+ Counters::compilation_cache_hits.Increment();
+ } else {
+ result = Handle<JSFunction>::null();
+ Counters::compilation_cache_misses.Increment();
+ }
+ return result;
}
@@ -135,11 +186,11 @@
void CompilationCache::PutScript(Handle<String> source,
- Entry entry,
Handle<JSFunction> boilerplate) {
- // TODO(245): Start caching scripts again but make it local to a
- // global context to avoid sharing code between independent
- // environments.
+ HandleScope scope;
+ ASSERT(boilerplate->IsBoilerplate());
+ Handle<CompilationCacheTable> table = GetTable(SCRIPT);
+ CALL_HEAP_FUNCTION_VOID(table->Put(*source, *boilerplate));
}
diff --git a/src/compilation-cache.h b/src/compilation-cache.h
index 045a6f8..38a9e3a 100644
--- a/src/compilation-cache.h
+++ b/src/compilation-cache.h
@@ -70,7 +70,6 @@
// Associate the (source, kind) pair to the boilerplate. This may
// overwrite an existing mapping.
static void PutScript(Handle<String> source,
- Entry entry,
Handle<JSFunction> boilerplate);
// Associate the (source, context->closure()->shared(), kind) triple
diff --git a/src/compiler.cc b/src/compiler.cc
index e2bf960..ced094c 100644
--- a/src/compiler.cc
+++ b/src/compiler.cc
@@ -215,7 +215,7 @@
extension,
pre_data);
if (extension == NULL && !result.is_null()) {
- CompilationCache::PutScript(source, CompilationCache::SCRIPT, result);
+ CompilationCache::PutScript(source, result);
}
// Get rid of the pre-parsing data (if necessary).
diff --git a/src/jump-target-arm.cc b/src/jump-target-arm.cc
index 41b80e2..3ce5f30 100644
--- a/src/jump-target-arm.cc
+++ b/src/jump-target-arm.cc
@@ -37,7 +37,7 @@
#define __ masm_->
-void JumpTarget::Jump() {
+void JumpTarget::DoJump() {
ASSERT(cgen_ != NULL);
ASSERT(cgen_->has_valid_frame());
// Live non-frame registers are not allowed at unconditional jumps
@@ -65,7 +65,7 @@
}
-void JumpTarget::Branch(Condition cc, Hint ignored) {
+void JumpTarget::DoBranch(Condition cc, Hint ignored) {
ASSERT(cgen_ != NULL);
ASSERT(cgen_->has_valid_frame());
@@ -148,7 +148,7 @@
}
-void JumpTarget::Bind(int mergable_elements) {
+void JumpTarget::DoBind(int mergable_elements) {
ASSERT(cgen_ != NULL);
ASSERT(!is_bound());
diff --git a/src/jump-target-ia32.cc b/src/jump-target-ia32.cc
index d2d35ca..8afb0a8 100644
--- a/src/jump-target-ia32.cc
+++ b/src/jump-target-ia32.cc
@@ -37,7 +37,7 @@
#define __ masm_->
-void JumpTarget::Jump() {
+void JumpTarget::DoJump() {
ASSERT(cgen_ != NULL);
ASSERT(cgen_->has_valid_frame());
// Live non-frame registers are not allowed at unconditional jumps
@@ -65,7 +65,7 @@
}
-void JumpTarget::Branch(Condition cc, Hint hint) {
+void JumpTarget::DoBranch(Condition cc, Hint hint) {
ASSERT(cgen_ != NULL);
ASSERT(cgen_->has_valid_frame());
@@ -148,7 +148,7 @@
}
-void JumpTarget::Bind(int mergable_elements) {
+void JumpTarget::DoBind(int mergable_elements) {
ASSERT(cgen_ != NULL);
ASSERT(!is_bound());
diff --git a/src/jump-target.cc b/src/jump-target.cc
index 04affc9..047588b 100644
--- a/src/jump-target.cc
+++ b/src/jump-target.cc
@@ -72,7 +72,7 @@
void JumpTarget::Unuse() {
// We should not deallocate jump targets that have unresolved jumps
// to them. In the event of a compile-time stack overflow or an
- // unitialized jump target, we don't care.
+ // uninitialized jump target, we don't care.
ASSERT(!is_linked() || cgen_ == NULL || cgen_->HasStackOverflow());
for (int i = 0; i < reaching_frames_.length(); i++) {
delete reaching_frames_[i];
@@ -101,9 +101,9 @@
if (!left->is_valid()) return left;
if (!right->is_valid()) return right;
- // If they have the same value, the result is the same. (Exception:
- // bidirectional frames cannot have constants or copies.) If either
+ // If they have the same value, the result is the same. If either
// is unsynced, the result is.
+
if (left->is_memory() && right->is_memory()) return left;
if (left->is_register() && right->is_register() &&
@@ -115,8 +115,7 @@
}
}
- if (direction_ == FORWARD_ONLY &&
- left->is_constant() &&
+ if (left->is_constant() &&
right->is_constant() &&
left->handle().is_identical_to(right->handle())) {
if (!left->is_synced()) {
@@ -126,8 +125,7 @@
}
}
- if (direction_ == FORWARD_ONLY &&
- left->is_copy() &&
+ if (left->is_copy() &&
right->is_copy() &&
left->index() == right->index()) {
if (!left->is_synced()) {
@@ -168,8 +166,7 @@
for (int i = 0; i < length; i++) {
FrameElement element = initial_frame->elements_[i];
// We do not allow copies or constants in bidirectional frames.
- if (direction_ == BIDIRECTIONAL &&
- i > high_water_mark &&
+ if (direction_ == BIDIRECTIONAL && i > high_water_mark &&
(element.is_constant() || element.is_copy())) {
elements.Add(NULL);
} else {
@@ -274,12 +271,25 @@
// the backing store of copies is always lower in the frame.
// Set the register locations to their index in the frame.
for (int i = 0; i < length; i++) {
- FrameElement current = entry_frame_->elements_[i];
- entry_frame_->elements_[i].clear_copied();
- if (current.is_copy()) {
- entry_frame_->elements_[current.index()].set_copied();
- } else if (current.is_register()) {
- entry_frame_->register_locations_[current.reg().code()] = i;
+ FrameElement* current = &entry_frame_->elements_[i];
+ current->clear_copied();
+ if (current->is_copy()) {
+ entry_frame_->elements_[current->index()].set_copied();
+ } else if (current->is_register()) {
+ entry_frame_->register_locations_[current->reg().code()] = i;
+ }
+
+ if (direction_ == BIDIRECTIONAL && i >= high_water_mark) {
+ current->set_static_type(StaticType::unknown());
+ } else {
+ StaticType merged_type = reaching_frames_[0]->elements_[i].static_type();
+ for (int j = 1, n = reaching_frames_.length();
+ !merged_type.is_unknown() && j < n;
+ j++) {
+ merged_type =
+ merged_type.merge(reaching_frames_[j]->elements_[i].static_type());
+ }
+ current->set_static_type(merged_type);
}
}
@@ -298,12 +308,17 @@
}
+void JumpTarget::Jump() {
+ DoJump();
+}
+
+
void JumpTarget::Jump(Result* arg) {
ASSERT(cgen_ != NULL);
ASSERT(cgen_->has_valid_frame());
cgen_->frame()->Push(arg);
- Jump();
+ DoJump();
}
@@ -313,7 +328,7 @@
cgen_->frame()->Push(arg0);
cgen_->frame()->Push(arg1);
- Jump();
+ DoJump();
}
@@ -324,7 +339,12 @@
cgen_->frame()->Push(arg0);
cgen_->frame()->Push(arg1);
cgen_->frame()->Push(arg2);
- Jump();
+ DoJump();
+}
+
+
+void JumpTarget::Branch(Condition cc, Hint hint) {
+ DoBranch(cc, hint);
}
@@ -352,7 +372,7 @@
DECLARE_ARGCHECK_VARS(arg);
cgen_->frame()->Push(arg);
- Branch(cc, hint);
+ DoBranch(cc, hint);
*arg = cgen_->frame()->Pop();
ASSERT_ARGCHECK(arg);
@@ -370,7 +390,7 @@
cgen_->frame()->Push(arg0);
cgen_->frame()->Push(arg1);
- Branch(cc, hint);
+ DoBranch(cc, hint);
*arg1 = cgen_->frame()->Pop();
*arg0 = cgen_->frame()->Pop();
@@ -396,7 +416,7 @@
cgen_->frame()->Push(arg0);
cgen_->frame()->Push(arg1);
cgen_->frame()->Push(arg2);
- Branch(cc, hint);
+ DoBranch(cc, hint);
*arg2 = cgen_->frame()->Pop();
*arg1 = cgen_->frame()->Pop();
*arg0 = cgen_->frame()->Pop();
@@ -427,7 +447,7 @@
cgen_->frame()->Push(arg1);
cgen_->frame()->Push(arg2);
cgen_->frame()->Push(arg3);
- Branch(cc, hint);
+ DoBranch(cc, hint);
*arg3 = cgen_->frame()->Pop();
*arg2 = cgen_->frame()->Pop();
*arg1 = cgen_->frame()->Pop();
@@ -439,17 +459,47 @@
ASSERT_ARGCHECK(arg3);
}
+
+void BreakTarget::Branch(Condition cc, Result* arg, Hint hint) {
+ ASSERT(cgen_ != NULL);
+ ASSERT(cgen_->has_valid_frame());
+
+ int count = cgen_->frame()->height() - expected_height_;
+ if (count > 0) {
+ // We negate and branch here rather than using DoBranch's negate
+ // and branch. This gives us a hook to remove statement state
+ // from the frame.
+ JumpTarget fall_through(cgen_);
+ // Branch to fall through will not negate, because it is a
+ // forward-only target.
+ fall_through.Branch(NegateCondition(cc), NegateHint(hint));
+ Jump(arg); // May emit merge code here.
+ fall_through.Bind();
+ } else {
+ DECLARE_ARGCHECK_VARS(arg);
+ cgen_->frame()->Push(arg);
+ DoBranch(cc, hint);
+ *arg = cgen_->frame()->Pop();
+ ASSERT_ARGCHECK(arg);
+ }
+}
+
#undef DECLARE_ARGCHECK_VARS
#undef ASSERT_ARGCHECK
+void JumpTarget::Bind(int mergable_elements) {
+ DoBind(mergable_elements);
+}
+
+
void JumpTarget::Bind(Result* arg, int mergable_elements) {
ASSERT(cgen_ != NULL);
if (cgen_->has_valid_frame()) {
cgen_->frame()->Push(arg);
}
- Bind(mergable_elements);
+ DoBind(mergable_elements);
*arg = cgen_->frame()->Pop();
}
@@ -461,7 +511,7 @@
cgen_->frame()->Push(arg0);
cgen_->frame()->Push(arg1);
}
- Bind(mergable_elements);
+ DoBind(mergable_elements);
*arg1 = cgen_->frame()->Pop();
*arg0 = cgen_->frame()->Pop();
}
@@ -478,7 +528,7 @@
cgen_->frame()->Push(arg1);
cgen_->frame()->Push(arg2);
}
- Bind(mergable_elements);
+ DoBind(mergable_elements);
*arg2 = cgen_->frame()->Pop();
*arg1 = cgen_->frame()->Pop();
*arg0 = cgen_->frame()->Pop();
@@ -498,7 +548,7 @@
cgen_->frame()->Push(arg2);
cgen_->frame()->Push(arg3);
}
- Bind(mergable_elements);
+ DoBind(mergable_elements);
*arg3 = cgen_->frame()->Pop();
*arg2 = cgen_->frame()->Pop();
*arg1 = cgen_->frame()->Pop();
@@ -548,10 +598,20 @@
ASSERT(cgen_ != NULL);
ASSERT(cgen_->has_valid_frame());
- // This is a break target so drop leftover statement state from the
- // frame before merging.
+ // Drop leftover statement state from the frame before merging.
cgen_->frame()->ForgetElements(cgen_->frame()->height() - expected_height_);
- JumpTarget::Jump();
+ DoJump();
+}
+
+
+void BreakTarget::Jump(Result* arg) {
+ ASSERT(cgen_ != NULL);
+ ASSERT(cgen_->has_valid_frame());
+
+ // Drop leftover statement state from the frame before merging.
+ cgen_->frame()->ForgetElements(cgen_->frame()->height() - expected_height_);
+ cgen_->frame()->Push(arg);
+ DoJump();
}
@@ -561,9 +621,9 @@
int count = cgen_->frame()->height() - expected_height_;
if (count > 0) {
- // We negate and branch here rather than using
- // JumpTarget::Branch's negate and branch. This gives us a hook
- // to remove statement state from the frame.
+ // We negate and branch here rather than using DoBranch's negate
+ // and branch. This gives us a hook to remove statement state
+ // from the frame.
JumpTarget fall_through(cgen_);
// Branch to fall through will not negate, because it is a
// forward-only target.
@@ -571,14 +631,13 @@
Jump(); // May emit merge code here.
fall_through.Bind();
} else {
- JumpTarget::Branch(cc, hint);
+ DoBranch(cc, hint);
}
}
void BreakTarget::Bind(int mergable_elements) {
#ifdef DEBUG
- ASSERT(mergable_elements == kAllElements);
ASSERT(cgen_ != NULL);
// All the forward-reaching frames should have been adjusted at the
// jumps to this target.
@@ -587,13 +646,35 @@
reaching_frames_[i]->height() == expected_height_);
}
#endif
- // This is a break target so we drop leftover statement state from
- // the frame before merging, even on the fall through. This is
- // because we can bind the return target with state on the frame.
+ // Drop leftover statement state from the frame before merging, even
+ // on the fall through. This is so we can bind the return target
+ // with state on the frame.
if (cgen_->has_valid_frame()) {
cgen_->frame()->ForgetElements(cgen_->frame()->height() - expected_height_);
}
- JumpTarget::Bind(mergable_elements);
+ DoBind(mergable_elements);
+}
+
+
+void BreakTarget::Bind(Result* arg, int mergable_elements) {
+#ifdef DEBUG
+ ASSERT(cgen_ != NULL);
+ // All the forward-reaching frames should have been adjusted at the
+ // jumps to this target.
+ for (int i = 0; i < reaching_frames_.length(); i++) {
+ ASSERT(reaching_frames_[i] == NULL ||
+ reaching_frames_[i]->height() == expected_height_ + 1);
+ }
+#endif
+ // Drop leftover statement state from the frame before merging, even
+ // on the fall through. This is so we can bind the return target
+ // with state on the frame.
+ if (cgen_->has_valid_frame()) {
+ cgen_->frame()->ForgetElements(cgen_->frame()->height() - expected_height_);
+ cgen_->frame()->Push(arg);
+ }
+ DoBind(mergable_elements);
+ *arg = cgen_->frame()->Pop();
}
diff --git a/src/jump-target.h b/src/jump-target.h
index 3a57302..1cfbe29 100644
--- a/src/jump-target.h
+++ b/src/jump-target.h
@@ -105,7 +105,7 @@
// Emit a jump to the target. There must be a current frame at the
// jump and there will be no current frame after the jump.
virtual void Jump();
- void Jump(Result* arg);
+ virtual void Jump(Result* arg);
void Jump(Result* arg0, Result* arg1);
void Jump(Result* arg0, Result* arg1, Result* arg2);
@@ -113,7 +113,7 @@
// frame at the branch. The current frame will fall through to the
// code after the branch.
virtual void Branch(Condition cc, Hint hint = no_hint);
- void Branch(Condition cc, Result* arg, Hint hint = no_hint);
+ virtual void Branch(Condition cc, Result* arg, Hint hint = no_hint);
void Branch(Condition cc, Result* arg0, Result* arg1, Hint hint = no_hint);
void Branch(Condition cc,
Result* arg0,
@@ -141,7 +141,7 @@
// frame elements must be mergable. Mergable elements are ignored
// completely for forward-only jump targets.
virtual void Bind(int mergable_elements = kAllElements);
- void Bind(Result* arg, int mergable_elements = kAllElements);
+ virtual void Bind(Result* arg, int mergable_elements = kAllElements);
void Bind(Result* arg0, Result* arg1, int mergable_elements = kAllElements);
void Bind(Result* arg0,
Result* arg1,
@@ -191,6 +191,12 @@
bool is_bound_;
bool is_linked_;
+ // Implementations of Jump, Branch, and Bind with all arguments and
+ // return values using the virtual frame.
+ void DoJump();
+ void DoBranch(Condition cc, Hint hint);
+ void DoBind(int mergable_elements);
+
private:
// Add a virtual frame reaching this labeled block via a forward
// jump, and a fresh label for its merge code.
@@ -243,16 +249,19 @@
// Emit a jump to the target. There must be a current frame at the
// jump and there will be no current frame after the jump.
virtual void Jump();
+ virtual void Jump(Result* arg);
// Emit a conditional branch to the target. There must be a current
// frame at the branch. The current frame will fall through to the
// code after the branch.
virtual void Branch(Condition cc, Hint hint = no_hint);
+ virtual void Branch(Condition cc, Result* arg, Hint hint = no_hint);
// Bind a break target. If there is no current frame at the binding
// site, there must be at least one frame reaching via a forward
// jump.
virtual void Bind(int mergable_elements = kAllElements);
+ virtual void Bind(Result* arg, int mergable_elements = kAllElements);
// Setter for expected height.
void set_expected_height(int expected) { expected_height_ = expected; }
diff --git a/src/objects.cc b/src/objects.cc
index 03e4072..6806829 100644
--- a/src/objects.cc
+++ b/src/objects.cc
@@ -1,4 +1,4 @@
-// Copyright 2006-2008 the V8 project authors. All rights reserved.
+// Copyright 2006-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:
@@ -5521,6 +5521,9 @@
if (getter->IsJSFunction()) {
return GetPropertyWithDefinedGetter(receiver,
JSFunction::cast(getter));
+ } else {
+ // Getter is not a function.
+ return Heap::undefined_value();
}
}
return element;
diff --git a/src/prettyprinter.cc b/src/prettyprinter.cc
index 0a1a169..7f8c567 100644
--- a/src/prettyprinter.cc
+++ b/src/prettyprinter.cc
@@ -602,11 +602,11 @@
ast_printer_->inc_indent();
}
- explicit IndentedScope(const char* txt, StaticType* type = NULL) {
+ explicit IndentedScope(const char* txt, SmiAnalysis* type = NULL) {
ast_printer_->PrintIndented(txt);
if ((type != NULL) && (type->IsKnown())) {
ast_printer_->Print(" (type = ");
- ast_printer_->Print(StaticType::Type2String(type));
+ ast_printer_->Print(SmiAnalysis::Type2String(type));
ast_printer_->Print(")");
}
ast_printer_->Print("\n");
@@ -665,7 +665,7 @@
void AstPrinter::PrintLiteralWithModeIndented(const char* info,
Variable* var,
Handle<Object> value,
- StaticType* type) {
+ SmiAnalysis* type) {
if (var == NULL) {
PrintLiteralIndented(info, value, true);
} else {
@@ -673,7 +673,7 @@
if (type->IsKnown()) {
OS::SNPrintF(buf, "%s (mode = %s, type = %s)", info,
Variable::Mode2String(var->mode()),
- StaticType::Type2String(type));
+ SmiAnalysis::Type2String(type));
} else {
OS::SNPrintF(buf, "%s (mode = %s)", info,
Variable::Mode2String(var->mode()));
@@ -1066,7 +1066,7 @@
OS::SNPrintF(buf, "%s %s (type = %s)",
(node->is_prefix() ? "PRE" : "POST"),
Token::Name(node->op()),
- StaticType::Type2String(node->type()));
+ SmiAnalysis::Type2String(node->type()));
} else {
OS::SNPrintF(buf, "%s %s", (node->is_prefix() ? "PRE" : "POST"),
Token::Name(node->op()));
diff --git a/src/prettyprinter.h b/src/prettyprinter.h
index 1c94635..720fe7b 100644
--- a/src/prettyprinter.h
+++ b/src/prettyprinter.h
@@ -102,7 +102,7 @@
void PrintLiteralWithModeIndented(const char* info,
Variable* var,
Handle<Object> value,
- StaticType* type);
+ SmiAnalysis* type);
void PrintLabelsIndented(const char* info, ZoneStringList* labels);
void inc_indent() { indent_++; }
diff --git a/src/register-allocator-inl.h b/src/register-allocator-inl.h
index 8611d6a..9e745b5 100644
--- a/src/register-allocator-inl.h
+++ b/src/register-allocator-inl.h
@@ -28,6 +28,7 @@
#ifndef V8_REGISTER_ALLOCATOR_INL_H_
#define V8_REGISTER_ALLOCATOR_INL_H_
+#include "register-allocator.h"
#include "virtual-frame.h"
namespace v8 { namespace internal {
diff --git a/src/register-allocator.cc b/src/register-allocator.cc
index 028baeb..94e031f 100644
--- a/src/register-allocator.cc
+++ b/src/register-allocator.cc
@@ -36,8 +36,19 @@
// Result implementation.
Result::Result(Register reg, CodeGenerator* cgen)
- : type_(REGISTER),
- cgen_(cgen) {
+ : static_type_(),
+ type_(REGISTER),
+ cgen_(cgen) {
+ data_.reg_ = reg;
+ ASSERT(reg.is_valid());
+ cgen_->allocator()->Use(reg);
+}
+
+
+Result::Result(Register reg, CodeGenerator* cgen, StaticType static_type)
+ : static_type_(static_type),
+ type_(REGISTER),
+ cgen_(cgen) {
data_.reg_ = reg;
ASSERT(reg.is_valid());
cgen_->allocator()->Use(reg);
@@ -45,6 +56,7 @@
void Result::CopyTo(Result* destination) const {
+ destination->static_type_ = static_type_;
destination->type_ = type();
destination->cgen_ = cgen_;
diff --git a/src/register-allocator.h b/src/register-allocator.h
index f6db62c..dcc2eb7 100644
--- a/src/register-allocator.h
+++ b/src/register-allocator.h
@@ -32,6 +32,78 @@
namespace v8 { namespace internal {
+
+// -------------------------------------------------------------------------
+// StaticType
+//
+// StaticType represent the type of an expression or a word at runtime.
+// The types are ordered by knowledge, so that if a value can come about
+// in more than one way, and there are different static types inferred
+// for the different ways, the types can be combined to a type that we
+// are still certain of (possibly just "unknown").
+
+class StaticType BASE_EMBEDDED {
+ public:
+ StaticType() : static_type_(UNKNOWN_TYPE) {}
+
+ static StaticType unknown() { return StaticType(); }
+ static StaticType smi() { return StaticType(SMI_TYPE); }
+ static StaticType jsstring() { return StaticType(STRING_TYPE); }
+ static StaticType heap_object() { return StaticType(HEAP_OBJECT_TYPE); }
+
+ // Accessors
+ bool is_unknown() { return static_type_ == UNKNOWN_TYPE; }
+ bool is_smi() { return static_type_ == SMI_TYPE; }
+ bool is_heap_object() { return (static_type_ & HEAP_OBJECT_TYPE) != 0; }
+ bool is_jsstring() { return static_type_ == STRING_TYPE; }
+
+ bool operator==(StaticType other) const {
+ return static_type_ == other.static_type_;
+ }
+
+ // Find the best approximating type for a value.
+ // The argument must not be NULL.
+ static StaticType TypeOf(Object* object) {
+ // Remember to make the most specific tests first. A string is also a heap
+ // object, so test for string-ness first.
+ if (object->IsSmi()) return smi();
+ if (object->IsString()) return jsstring();
+ if (object->IsHeapObject()) return heap_object();
+ return unknown();
+ }
+
+ // Merges two static types to a type that combines the knowledge
+ // of both. If there is no way to combine (e.g., being a string *and*
+ // being a smi), the resulting type is unknown.
+ StaticType merge(StaticType other) {
+ StaticType x(
+ static_cast<StaticTypeEnum>(static_type_ & other.static_type_));
+ return x;
+ }
+
+ private:
+ enum StaticTypeEnum {
+ // Numbers are chosen so that least upper bound of the following
+ // partial order is implemented by bitwise "and":
+ //
+ // string
+ // |
+ // heap-object smi
+ // \ /
+ // unknown
+ //
+ UNKNOWN_TYPE = 0x00,
+ SMI_TYPE = 0x01,
+ HEAP_OBJECT_TYPE = 0x02,
+ STRING_TYPE = 0x04 | HEAP_OBJECT_TYPE
+ };
+ explicit StaticType(StaticTypeEnum static_type) : static_type_(static_type) {}
+
+ // StaticTypeEnum static_type_;
+ byte static_type_;
+};
+
+
// -------------------------------------------------------------------------
// Results
//
@@ -47,14 +119,24 @@
};
// Construct an invalid result.
- explicit Result(CodeGenerator* cgen) : type_(INVALID), cgen_(cgen) {}
+ explicit Result(CodeGenerator* cgen)
+ : static_type_(),
+ type_(INVALID),
+ cgen_(cgen) {}
// Construct a register Result.
- Result(Register reg, CodeGenerator* cgen);
+ Result(Register reg,
+ CodeGenerator* cgen);
+
+ // Construct a register Result with a known static type.
+ Result(Register reg,
+ CodeGenerator* cgen,
+ StaticType static_type);
// Construct a Result whose value is a compile-time constant.
Result(Handle<Object> value, CodeGenerator * cgen)
- : type_(CONSTANT),
+ : static_type_(StaticType::TypeOf(*value)),
+ type_(CONSTANT),
cgen_(cgen) {
data_.handle_ = value.location();
}
@@ -77,7 +159,10 @@
inline void Unuse();
- Type type() const { return type_; }
+ StaticType static_type() const { return static_type_; }
+ void set_static_type(StaticType static_type) { static_type_ = static_type; }
+
+ Type type() const { return static_cast<Type>(type_); }
bool is_valid() const { return type() != INVALID; }
bool is_register() const { return type() == REGISTER; }
@@ -104,7 +189,8 @@
void ToRegister(Register reg);
private:
- Type type_;
+ StaticType static_type_;
+ byte type_;
union {
Register reg_;
diff --git a/src/rewriter.cc b/src/rewriter.cc
index c713612..1aa24aa 100644
--- a/src/rewriter.cc
+++ b/src/rewriter.cc
@@ -327,7 +327,7 @@
if (proxy != NULL) {
Variable* var = proxy->AsVariable();
if (var != NULL) {
- StaticType* var_type = var->type();
+ SmiAnalysis* var_type = var->type();
if (var_type->IsUnknown()) {
var_type->CopyFrom(node->type());
} else if (var_type->IsLikelySmi()) {
diff --git a/src/runtime.js b/src/runtime.js
index 5e73d41..63e9292 100644
--- a/src/runtime.js
+++ b/src/runtime.js
@@ -104,13 +104,9 @@
return %NumberEquals(this, x);
}
- if (IS_UNDEFINED(this)) {
- // Both undefined and undetectable.
- return IS_UNDEFINED(x) ? 0 : 1;
- }
-
- // Objects, null, booleans and functions are all that's left.
- // They can all be compared with a simple identity check.
+ // If anything else gets here, we just do simple identity check.
+ // Objects (including functions), null, undefined and booleans were
+ // checked in the CompareStub, so there should be nothing left.
return %_ObjectEquals(this, x) ? 0 : 1;
}
@@ -163,6 +159,20 @@
}
+// Left operand (this) is already a string.
+function STRING_ADD_LEFT(x) {
+ x = %ToString(%ToPrimitive(x, NO_HINT));
+ return %StringAdd(this, x);
+}
+
+
+// Right operand (x) is already a string.
+function STRING_ADD_RIGHT(x) {
+ var a = %ToString(%ToPrimitive(this, NO_HINT));
+ return %StringAdd(a, x);
+}
+
+
// ECMA-262, section 11.6.2, page 50.
function SUB(x) {
return %NumberSub(%ToNumber(this), %ToNumber(x));
@@ -187,18 +197,6 @@
}
-// ECMA-262, section 11.4.4, page 47.
-function INC() {
- return %NumberAdd(%ToNumber(this), 1);
-}
-
-
-// ECMA-262, section 11.4.5, page 48.
-function DEC() {
- return %NumberSub(%ToNumber(this), 1);
-}
-
-
/* -------------------------------------------
- - - B i t o p e r a t i o n s - - -
@@ -275,7 +273,7 @@
// ECMA-262, section 11.8.6, page 54. To make the implementation more
-// efficient, the return value should be zero if the 'this' is an
+// efficient, the return value should be zero if the 'this' is an
// instance of F, and non-zero if not. This makes it possible to avoid
// an expensive ToBoolean conversion in the generated code.
function INSTANCE_OF(F) {
diff --git a/src/variables.cc b/src/variables.cc
index 1d7d6e4..51eb8ca 100644
--- a/src/variables.cc
+++ b/src/variables.cc
@@ -85,10 +85,10 @@
// ----------------------------------------------------------------------------
-// Implementation StaticType.
+// Implementation SmiAnalysis.
-const char* StaticType::Type2String(StaticType* type) {
+const char* SmiAnalysis::Type2String(SmiAnalysis* type) {
switch (type->kind_) {
case UNKNOWN:
return "UNKNOWN";
diff --git a/src/variables.h b/src/variables.h
index 00ba345..275f498 100644
--- a/src/variables.h
+++ b/src/variables.h
@@ -64,14 +64,14 @@
// Variables and AST expression nodes can track their "type" to enable
// optimizations and removal of redundant checks when generating code.
-class StaticType BASE_EMBEDDED {
+class SmiAnalysis {
public:
enum Kind {
UNKNOWN,
LIKELY_SMI
};
- StaticType() : kind_(UNKNOWN) {}
+ SmiAnalysis() : kind_(UNKNOWN) {}
bool Is(Kind kind) const { return kind_ == kind; }
@@ -79,11 +79,11 @@
bool IsUnknown() const { return Is(UNKNOWN); }
bool IsLikelySmi() const { return Is(LIKELY_SMI); }
- void CopyFrom(StaticType* other) {
+ void CopyFrom(SmiAnalysis* other) {
kind_ = other->kind_;
}
- static const char* Type2String(StaticType* type);
+ static const char* Type2String(SmiAnalysis* type);
// LIKELY_SMI accessors
void SetAsLikelySmi() {
@@ -99,7 +99,7 @@
private:
Kind kind_;
- DISALLOW_COPY_AND_ASSIGN(StaticType);
+ DISALLOW_COPY_AND_ASSIGN(SmiAnalysis);
};
@@ -185,7 +185,7 @@
Expression* rewrite() const { return rewrite_; }
Slot* slot() const;
- StaticType* type() { return &type_; }
+ SmiAnalysis* type() { return &type_; }
private:
Variable(Scope* scope, Handle<String> name, Mode mode, bool is_valid_LHS,
@@ -205,7 +205,7 @@
UseCount obj_uses_; // uses of the object the variable points to
// Static type information
- StaticType type_;
+ SmiAnalysis type_;
// Code generation.
// rewrite_ is usually a Slot or a Property, but maybe any expression.
diff --git a/src/virtual-frame-arm.h b/src/virtual-frame-arm.h
index 7910201..f15eec2 100644
--- a/src/virtual-frame-arm.h
+++ b/src/virtual-frame-arm.h
@@ -312,12 +312,11 @@
void EmitPush(Register reg);
// Push an element on the virtual frame.
- void Push(Register reg);
+ void Push(Register reg, StaticType static_type = StaticType());
void Push(Handle<Object> value);
void Push(Smi* value) { Push(Handle<Object>(value)); }
- // Pushing a result invalidates it (its contents become owned by the
- // frame).
+ // Pushing a result invalidates it (its contents become owned by the frame).
void Push(Result* result);
// Nip removes zero or more elements from immediately below the top
diff --git a/src/virtual-frame-ia32.cc b/src/virtual-frame-ia32.cc
index ed0d3d5..067ecec 100644
--- a/src/virtual-frame-ia32.cc
+++ b/src/virtual-frame-ia32.cc
@@ -191,9 +191,10 @@
if (elements_[i].is_copy()) {
elements_[elements_[i].index()].set_copied();
}
+ elements_[i].set_static_type(target.static_type());
}
- // Adjust the stack point downard if necessary.
+ // Adjust the stack pointer downward if necessary.
if (stack_pointer_ > expected->stack_pointer_) {
int difference = stack_pointer_ - expected->stack_pointer_;
stack_pointer_ = expected->stack_pointer_;
@@ -950,6 +951,7 @@
if (element.is_memory()) {
Result temp = cgen_->allocator()->Allocate();
ASSERT(temp.is_valid());
+ temp.set_static_type(element.static_type());
__ pop(temp.reg());
return temp;
}
@@ -981,11 +983,12 @@
FrameElement::RegisterElement(temp.reg(), FrameElement::SYNCED);
// Preserve the copy flag on the element.
if (element.is_copied()) new_element.set_copied();
+ new_element.set_static_type(element.static_type());
elements_[index] = new_element;
__ mov(temp.reg(), Operand(ebp, fp_relative(index)));
- return Result(temp.reg(), cgen_);
+ return Result(temp.reg(), cgen_, element.static_type());
} else if (element.is_register()) {
- return Result(element.reg(), cgen_);
+ return Result(element.reg(), cgen_, element.static_type());
} else {
ASSERT(element.is_constant());
return Result(element.handle(), cgen_);
diff --git a/src/virtual-frame-ia32.h b/src/virtual-frame-ia32.h
index 6e54976..113ccc6 100644
--- a/src/virtual-frame-ia32.h
+++ b/src/virtual-frame-ia32.h
@@ -172,6 +172,10 @@
PushFrameSlotAt(elements_.length() - index - 1);
}
+ void StoreToElementAt(int index) {
+ StoreToFrameSlotAt(elements_.length() - index - 1);
+ }
+
// A frame-allocated local as an assembly operand.
Operand LocalAt(int index) const {
ASSERT(0 <= index);
@@ -321,7 +325,7 @@
void EmitPush(Immediate immediate);
// Push an element on the virtual frame.
- void Push(Register reg);
+ void Push(Register reg, StaticType static_type = StaticType());
void Push(Handle<Object> value);
void Push(Smi* value) { Push(Handle<Object>(value)); }
diff --git a/src/virtual-frame.cc b/src/virtual-frame.cc
index 73c8b15..b5682cb 100644
--- a/src/virtual-frame.cc
+++ b/src/virtual-frame.cc
@@ -93,10 +93,10 @@
case FrameElement::MEMORY: // Fall through.
case FrameElement::REGISTER:
// All copies are backed by memory or register locations.
- result.type_ =
- FrameElement::TypeField::encode(FrameElement::COPY)
- | FrameElement::IsCopiedField::encode(false)
- | FrameElement::SyncField::encode(FrameElement::NOT_SYNCED);
+ result.set_static_type(target.static_type());
+ result.type_ = FrameElement::COPY;
+ result.copied_ = false;
+ result.synced_ = false;
result.data_.index_ = index;
elements_[index].set_copied();
break;
@@ -208,6 +208,7 @@
if (elements_[index].is_register()) {
Unuse(elements_[index].reg());
}
+ new_element.set_static_type(elements_[index].static_type());
elements_[index] = new_element;
}
@@ -388,6 +389,8 @@
// register element, or the new element at frame_index, must be made
// a copy.
int i = register_index(value->reg());
+ ASSERT(value->static_type() == elements_[i].static_type());
+
if (i < frame_index) {
// The register FrameElement is lower in the frame than the new copy.
elements_[frame_index] = CopyElementAt(i);
@@ -413,7 +416,8 @@
Use(value->reg(), frame_index);
elements_[frame_index] =
FrameElement::RegisterElement(value->reg(),
- FrameElement::NOT_SYNCED);
+ FrameElement::NOT_SYNCED,
+ value->static_type());
}
} else {
ASSERT(value->is_constant());
@@ -437,25 +441,33 @@
}
-void VirtualFrame::Push(Register reg) {
+void VirtualFrame::Push(Register reg, StaticType static_type) {
if (is_used(reg)) {
- elements_.Add(CopyElementAt(register_index(reg)));
+ int index = register_index(reg);
+ FrameElement element = CopyElementAt(index);
+ ASSERT(static_type.merge(element.static_type()) == element.static_type());
+ elements_.Add(element);
} else {
Use(reg, elements_.length());
- elements_.Add(FrameElement::RegisterElement(reg, FrameElement::NOT_SYNCED));
+ FrameElement element =
+ FrameElement::RegisterElement(reg,
+ FrameElement::NOT_SYNCED,
+ static_type);
+ elements_.Add(element);
}
}
void VirtualFrame::Push(Handle<Object> value) {
- elements_.Add(FrameElement::ConstantElement(value,
- FrameElement::NOT_SYNCED));
+ FrameElement element =
+ FrameElement::ConstantElement(value, FrameElement::NOT_SYNCED);
+ elements_.Add(element);
}
void VirtualFrame::Push(Result* result) {
if (result->is_register()) {
- Push(result->reg());
+ Push(result->reg(), result->static_type());
} else {
ASSERT(result->is_constant());
Push(result->handle());
@@ -476,7 +488,9 @@
bool FrameElement::Equals(FrameElement other) {
- if (type_ != other.type_) return false;
+ if (type_ != other.type_ ||
+ copied_ != other.copied_ ||
+ synced_ != other.synced_) return false;
if (is_register()) {
if (!reg().is(other.reg())) return false;
diff --git a/src/virtual-frame.h b/src/virtual-frame.h
index 9f5cf01..99b4f76 100644
--- a/src/virtual-frame.h
+++ b/src/virtual-frame.h
@@ -47,13 +47,14 @@
class FrameElement BASE_EMBEDDED {
public:
enum SyncFlag {
- SYNCED,
- NOT_SYNCED
+ NOT_SYNCED,
+ SYNCED
};
// The default constructor creates an invalid frame element.
- FrameElement() {
- Initialize(INVALID, no_reg, NOT_SYNCED);
+ FrameElement()
+ : static_type_(), type_(INVALID), copied_(false), synced_(false) {
+ data_.reg_ = no_reg;
}
// Factory function to construct an invalid frame element.
@@ -69,9 +70,10 @@
}
// Factory function to construct an in-register frame element.
- static FrameElement RegisterElement(Register reg, SyncFlag is_synced) {
- FrameElement result(REGISTER, reg, is_synced);
- return result;
+ static FrameElement RegisterElement(Register reg,
+ SyncFlag is_synced,
+ StaticType static_type = StaticType()) {
+ return FrameElement(REGISTER, reg, is_synced, static_type);
}
// Factory function to construct a frame element whose value is known at
@@ -82,16 +84,16 @@
return result;
}
- bool is_synced() const { return SyncField::decode(type_) == SYNCED; }
+ bool is_synced() const { return synced_; }
void set_sync() {
ASSERT(type() != MEMORY);
- type_ = (type_ & ~SyncField::mask()) | SyncField::encode(SYNCED);
+ synced_ = true;
}
void clear_sync() {
ASSERT(type() != MEMORY);
- type_ = (type_ & ~SyncField::mask()) | SyncField::encode(NOT_SYNCED);
+ synced_ = false;
}
bool is_valid() const { return type() != INVALID; }
@@ -100,15 +102,9 @@
bool is_constant() const { return type() == CONSTANT; }
bool is_copy() const { return type() == COPY; }
- bool is_copied() const { return IsCopiedField::decode(type_); }
-
- void set_copied() {
- type_ = (type_ & ~IsCopiedField::mask()) | IsCopiedField::encode(true);
- }
-
- void clear_copied() {
- type_ = (type_ & ~IsCopiedField::mask()) | IsCopiedField::encode(false);
- }
+ bool is_copied() const { return copied_; }
+ void set_copied() { copied_ = true; }
+ void clear_copied() { copied_ = false; }
Register reg() const {
ASSERT(is_register());
@@ -127,6 +123,14 @@
bool Equals(FrameElement other);
+ StaticType static_type() { return static_type_; }
+
+ void set_static_type(StaticType static_type) {
+ // TODO(lrn): If it's s copy, it would be better to update the real one,
+ // but we can't from here. The caller must handle this.
+ static_type_ = static_type;
+ }
+
private:
enum Type {
INVALID,
@@ -136,17 +140,19 @@
COPY
};
- // BitField is <type, shift, size>.
- class SyncField : public BitField<SyncFlag, 0, 1> {};
- class IsCopiedField : public BitField<bool, 1, 1> {};
- class TypeField : public BitField<Type, 2, 32 - 2> {};
+ Type type() const { return static_cast<Type>(type_); }
- Type type() const { return TypeField::decode(type_); }
+ StaticType static_type_;
- // The element's type and a dirty bit. The dirty bit can be cleared
+ // The element's type.
+ byte type_;
+
+ bool copied_;
+
+ // The element's dirty-bit. The dirty bit can be cleared
// for non-memory elements to indicate that the element agrees with
// the value in memory in the actual frame.
- int type_;
+ bool synced_;
union {
Register reg_;
@@ -155,15 +161,30 @@
} data_;
// Used to construct memory and register elements.
- FrameElement(Type type, Register reg, SyncFlag is_synced) {
- Initialize(type, reg, is_synced);
+ FrameElement(Type type, Register reg, SyncFlag is_synced)
+ : static_type_(),
+ type_(type),
+ copied_(false),
+ synced_(is_synced != NOT_SYNCED) {
+ data_.reg_ = reg;
+ }
+
+ FrameElement(Type type, Register reg, SyncFlag is_synced, StaticType stype)
+ : static_type_(stype),
+ type_(type),
+ copied_(false),
+ synced_(is_synced != NOT_SYNCED) {
+ data_.reg_ = reg;
}
// Used to construct constant elements.
- inline FrameElement(Handle<Object> value, SyncFlag is_synced);
-
- // Used to initialize invalid, memory, and register elements.
- inline void Initialize(Type type, Register reg, SyncFlag is_synced);
+ FrameElement(Handle<Object> value, SyncFlag is_synced)
+ : static_type_(StaticType::TypeOf(*value)),
+ type_(CONSTANT),
+ copied_(false),
+ synced_(is_synced != NOT_SYNCED) {
+ data_.handle_ = value.location();
+ }
void set_index(int new_index) {
ASSERT(is_copy());
@@ -182,25 +203,4 @@
#include "virtual-frame-ia32.h"
#endif
-
-namespace v8 { namespace internal {
-
-FrameElement::FrameElement(Handle<Object> value, SyncFlag is_synced) {
- type_ = TypeField::encode(CONSTANT)
- | IsCopiedField::encode(false)
- | SyncField::encode(is_synced);
- data_.handle_ = value.location();
-}
-
-
-void FrameElement::Initialize(Type type, Register reg, SyncFlag is_synced) {
- type_ = TypeField::encode(type)
- | IsCopiedField::encode(false)
- | SyncField::encode(is_synced);
- data_.reg_ = reg;
-}
-
-
-} } // namespace v8::internal
-
#endif // V8_VIRTUAL_FRAME_H_
diff --git a/test/cctest/test-api.cc b/test/cctest/test-api.cc
index cf5c5a5..dd705d2 100644
--- a/test/cctest/test-api.cc
+++ b/test/cctest/test-api.cc
@@ -2383,11 +2383,16 @@
ExpectBoolean("undetectable||false", false);
ExpectBoolean("undetectable==null", true);
+ ExpectBoolean("null==undetectable", true);
ExpectBoolean("undetectable==undefined", true);
+ ExpectBoolean("undefined==undetectable", true);
ExpectBoolean("undetectable==undetectable", true);
+
ExpectBoolean("undetectable===null", false);
- ExpectBoolean("undetectable===undefined", true);
+ ExpectBoolean("null===undetectable", false);
+ ExpectBoolean("undetectable===undefined", false);
+ ExpectBoolean("undefined===undetectable", false);
ExpectBoolean("undetectable===undetectable", true);
}
@@ -2418,11 +2423,16 @@
ExpectBoolean("undetectable||false", false);
ExpectBoolean("undetectable==null", true);
+ ExpectBoolean("null==undetectable", true);
ExpectBoolean("undetectable==undefined", true);
+ ExpectBoolean("undefined==undetectable", true);
ExpectBoolean("undetectable==undetectable", true);
+
ExpectBoolean("undetectable===null", false);
- ExpectBoolean("undetectable===undefined", true);
+ ExpectBoolean("null===undetectable", false);
+ ExpectBoolean("undetectable===undefined", false);
+ ExpectBoolean("undefined===undetectable", false);
ExpectBoolean("undetectable===undetectable", true);
}
diff --git a/test/mjsunit/compare-constants.js b/test/mjsunit/constant-folding.js
similarity index 71%
rename from test/mjsunit/compare-constants.js
rename to test/mjsunit/constant-folding.js
index e11ac0c..41b632f 100644
--- a/test/mjsunit/compare-constants.js
+++ b/test/mjsunit/constant-folding.js
@@ -25,6 +25,57 @@
// (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 operations that involve one or more constants.
+// The code generator now handles compile-time constants specially.
+// Test the code generated when operands are known at compile time
+
+// Test count operations involving constants
+function test_count() {
+ var x = "foo";
+ var y = "3";
+
+ x += x++; // ++ and -- apply ToNumber to their operand, even for postfix.
+ assertEquals(x, "fooNaN", "fooNaN test");
+ x = "luft";
+ x += ++x;
+ assertEquals(x, "luftNaN", "luftNaN test");
+
+ assertTrue(y++ === 3, "y++ === 3, where y = \"3\"");
+ y = 3;
+ assertEquals(y++, 3, "y++ == 3, where y = 3");
+ y = "7.1";
+ assertTrue(y++ === 7.1, "y++ === 7.1, where y = \"7.1\"");
+ var z = y = x = "9";
+ assertEquals( z++ + (++y) + x++, 28, "z++ + (++y) + x++ == 28");
+ z = y = x = 13;
+ assertEquals( z++ + (++y) + x++, 40, "z++ + (++y) + x++ == 40");
+ z = y = x = -5.5;
+ assertEquals( z++ + (++y) + x++, -15.5, "z++ + (++y) + x++ == -15.5");
+
+ assertEquals(y, -4.5);
+ z = y;
+ z++;
+ assertEquals(y, -4.5);
+ z = y;
+ y++;
+ assertEquals(z, -4.5);
+
+ y = 20;
+ z = y;
+ z++;
+ assertEquals(y, 20);
+ z = y;
+ y++;
+ assertEquals(z, 20);
+
+ const w = 30;
+ assertEquals(w++, 30);
+ assertEquals(++w, 31);
+ assertEquals(++w, 31);
+}
+
+test_count();
+
// Test comparison operations that involve one or two constant smis.
function test() {
@@ -118,4 +169,3 @@
}
test();
-
diff --git a/test/mjsunit/indexed-accessors.js b/test/mjsunit/indexed-accessors.js
index 07ce243..395f2ab 100644
--- a/test/mjsunit/indexed-accessors.js
+++ b/test/mjsunit/indexed-accessors.js
@@ -98,3 +98,23 @@
var q = {};
q.__defineGetter__('0', function() { return 42; });
assertThrows('q[0] = 7');
+
+// Using a getter where only a setter is defined returns undefined.
+var q1 = {};
+q1.__defineSetter__('0', function() {q1.b = 17;});
+assertEquals(q1[0], undefined);
+// Setter works
+q1[0] = 3;
+assertEquals(q1[0], undefined);
+assertEquals(q1.b, 17);
+
+// Complex case of using an undefined getter.
+// From http://code.google.com/p/v8/issues/detail?id=298
+// Reported by nth10sd.
+
+a = function() {};
+__defineSetter__("0", function() {});
+if (a |= '') {};
+assertThrows('this[a].__parent__');
+assertEquals(a, 0);
+assertEquals(this[a], undefined);
diff --git a/test/mjsunit/string-add.js b/test/mjsunit/string-add.js
new file mode 100644
index 0000000..c42cf79
--- /dev/null
+++ b/test/mjsunit/string-add.js
@@ -0,0 +1,175 @@
+// 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("ab", "a" + "b", "ll");
+
+assertEquals("12", "1" + "2", "dd");
+assertEquals("123", "1" + "2" + "3", "ddd");
+assertEquals("123", 1 + "2" + "3", "ndd");
+assertEquals("123", "1" + 2 + "3", "dnd");
+assertEquals("123", "1" + "2" + 3, "ddn");
+
+assertEquals("123", "1" + 2 + 3, "dnn");
+assertEquals("123", 1 + "2" + 3, "ndn");
+assertEquals("33", 1 + 2 + "3", "nnd");
+
+var x = "1";
+assertEquals("12", x + 2, "vn");
+assertEquals("12", x + "2", "vd");
+assertEquals("21", 2 + x, "nv");
+assertEquals("21", "2" + x, "dv");
+
+var y = "2";
+assertEquals("12", x + y, "vdvd");
+
+x = 1;
+assertEquals("12", x + y, "vnvd");
+
+y = 2;
+assertEquals(3, x + y, "vnvn");
+
+x = "1";
+assertEquals("12", x + y, "vdvn");
+
+y = "2";
+assertEquals("12", x + y, "vdvd2");
+
+(function(x, y) {
+ var z = "3";
+ var w = "4";
+
+ assertEquals("11", x + x, "xx");
+ assertEquals("12", x + y, "xy");
+ assertEquals("13", x + z, "xz");
+ assertEquals("14", x + w, "xw");
+
+ assertEquals("21", y + x, "yx");
+ assertEquals("22", y + y, "yy");
+ assertEquals("23", y + z, "yz");
+ assertEquals("24", y + w, "yw");
+
+ assertEquals("31", z + x, "zx");
+ assertEquals("32", z + y, "zy");
+ assertEquals("33", z + z, "zz");
+ assertEquals("34", z + w, "zw");
+
+ assertEquals("41", w + x, "wx");
+ assertEquals("42", w + y, "wy");
+ assertEquals("43", w + z, "wz");
+ assertEquals("44", w + w, "ww");
+
+ (function(){x = 1; z = 3;})();
+
+ assertEquals(2, x + x, "x'x");
+ assertEquals("12", x + y, "x'y");
+ assertEquals(4, x + z, "x'z'");
+ assertEquals("14", x + w, "x'w");
+
+ assertEquals("21", y + x, "yx'");
+ assertEquals("22", y + y, "yy");
+ assertEquals("23", y + z, "yz'");
+ assertEquals("24", y + w, "yw");
+
+ assertEquals(4, z + x, "z'x'");
+ assertEquals("32", z + y, "z'y");
+ assertEquals(6, z + z, "z'z'");
+ assertEquals("34", z + w, "z'w");
+
+ assertEquals("41", w + x, "wx'");
+ assertEquals("42", w + y, "wy");
+ assertEquals("43", w + z, "wz'");
+ assertEquals("44", w + w, "ww");
+})("1", "2");
+
+assertEquals("142", "1" + new Number(42), "sN");
+assertEquals("421", new Number(42) + "1", "Ns");
+assertEquals(84, new Number(42) + new Number(42), "NN");
+
+assertEquals("142", "1" + new String("42"), "sS");
+assertEquals("421", new String("42") + "1", "Ss");
+assertEquals("142", "1" + new String("42"), "sS");
+assertEquals("4242", new String("42") + new String("42"), "SS");
+
+assertEquals("1true", "1" + true, "sb");
+assertEquals("true1", true + "1", "bs");
+assertEquals(2, true + true, "bs");
+
+assertEquals("1true", "1" + new Boolean(true), "sB");
+assertEquals("true1", new Boolean(true) + "1", "Bs");
+assertEquals(2, new Boolean(true) + new Boolean(true), "Bs");
+
+assertEquals("1undefined", "1" + void 0, "sv");
+assertEquals("undefined1", (void 0) + "1", "vs");
+assertTrue(isNaN(void 0 + void 0), "vv");
+
+assertEquals("1null", "1" + null, "su");
+assertEquals("null1", null + "1", "us");
+assertEquals(0, null + null, "uu");
+
+(function (i) {
+ // Check that incoming frames are merged correctly.
+ var x;
+ var y;
+ var z;
+ var w;
+ switch (i) {
+ case 1: x = 42; y = "stry"; z = "strz"; w = 42; break;
+ default: x = "strx", y = 42; z = "strz"; w = 42; break;
+ }
+ var resxx = x + x;
+ var resxy = x + y;
+ var resxz = x + z;
+ var resxw = x + w;
+ var resyx = y + x;
+ var resyy = y + y;
+ var resyz = y + z;
+ var resyw = y + w;
+ var reszx = z + x;
+ var reszy = z + y;
+ var reszz = z + z;
+ var reszw = z + w;
+ var reswx = w + x;
+ var reswy = w + y;
+ var reswz = w + z;
+ var resww = w + w;
+ assertEquals(84, resxx, "swxx");
+ assertEquals("42stry", resxy, "swxy");
+ assertEquals("42strz", resxz, "swxz");
+ assertEquals(84, resxw, "swxw");
+ assertEquals("stry42", resyx, "swyx");
+ assertEquals("strystry", resyy, "swyy");
+ assertEquals("strystrz", resyz, "swyz");
+ assertEquals("stry42", resyw, "swyw");
+ assertEquals("strz42", reszx, "swzx");
+ assertEquals("strzstry", reszy, "swzy");
+ assertEquals("strzstrz", reszz, "swzz");
+ assertEquals("strz42", reszw, "swzw");
+ assertEquals(84, reswx, "swwx");
+ assertEquals("42stry", reswy, "swwy");
+ assertEquals("42strz", reswz, "swwz");
+ assertEquals(84, resww, "swww");
+})(1);