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, &not_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(&not_identical);
+    }
 
-    // Check for undetectability of the other operand.
-    Label not_strictly_equal;
-    __ bind(&check_undetectable);
-    __ test(edx, Immediate(kSmiTagMask));
-    __ j(zero, &not_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, &not_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, &not_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(&not_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(&not_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);