Fix interceptor handling in crankshaft.
authorverwaest@chromium.org <verwaest@chromium.org@ce2b1a6d-e550-0410-aec6-3dcde31c8c00>
Tue, 3 Sep 2013 08:55:52 +0000 (08:55 +0000)
committerverwaest@chromium.org <verwaest@chromium.org@ce2b1a6d-e550-0410-aec6-3dcde31c8c00>
Tue, 3 Sep 2013 08:55:52 +0000 (08:55 +0000)
R=dcarney@chromium.org

Review URL: https://chromiumcodereview.appspot.com/23614011

git-svn-id: http://v8.googlecode.com/svn/branches/bleeding_edge@16495 ce2b1a6d-e550-0410-aec6-3dcde31c8c00

src/hydrogen.cc
test/cctest/test-api.cc

index 0b1fe09..c42d035 100644 (file)
@@ -4003,13 +4003,18 @@ void HOptimizedGraphBuilder::VisitRegExpLiteral(RegExpLiteral* expr) {
 }
 
 
+static bool CanInlinePropertyAccess(Map* type) {
+  return !type->is_dictionary_map() && !type->has_named_interceptor();
+}
+
+
 static void LookupInPrototypes(Handle<Map> map,
                                Handle<String> name,
                                LookupResult* lookup) {
   while (map->prototype()->IsJSObject()) {
     Handle<JSObject> holder(JSObject::cast(map->prototype()));
-    if (!holder->HasFastProperties()) break;
     map = Handle<Map>(holder->map());
+    if (!CanInlinePropertyAccess(*map)) break;
     map->LookupDescriptor(*holder, *name, lookup);
     if (lookup->IsFound()) return;
   }
@@ -4397,8 +4402,8 @@ static bool ComputeLoadStoreField(Handle<Map> type,
                                   LookupResult* lookup,
                                   bool is_store) {
   ASSERT(!is_store || !type->is_observed());
-  if (type->has_named_interceptor()) {
-    lookup->InterceptorResult(NULL);
+  if (!CanInlinePropertyAccess(*type)) {
+    lookup->NotFound();
     return false;
   }
   // If we directly find a field, the access can be inlined.
@@ -4541,8 +4546,7 @@ HInstruction* HOptimizedGraphBuilder::BuildStoreNamedMonomorphic(
 static bool CanLoadPropertyFromPrototype(Handle<Map> map,
                                          Handle<Name> name,
                                          LookupResult* lookup) {
-  if (map->has_named_interceptor()) return false;
-  if (map->is_dictionary_map()) return false;
+  if (!CanInlinePropertyAccess(*map)) return false;
   map->LookupDescriptor(NULL, *name, lookup);
   if (lookup->IsFound()) return false;
   return true;
@@ -4634,9 +4638,8 @@ static bool PrototypeChainCanNeverResolve(
     if (current->IsJSGlobalProxy() ||
         current->IsGlobalObject() ||
         !current->IsJSObject() ||
-        JSObject::cast(current)->map()->has_named_interceptor() ||
-        JSObject::cast(current)->IsAccessCheckNeeded() ||
-        !JSObject::cast(current)->HasFastProperties()) {
+        !CanInlinePropertyAccess(JSObject::cast(current)->map()) ||
+        JSObject::cast(current)->IsAccessCheckNeeded()) {
       return false;
     }
 
@@ -4671,8 +4674,7 @@ void HOptimizedGraphBuilder::HandlePolymorphicLoadNamedField(
     LookupResult lookup(isolate());
     if (ComputeLoadStoreField(map, name, &lookup, false) ||
         (lookup.IsCacheable() &&
-         !map->is_dictionary_map() &&
-         !map->has_named_interceptor() &&
+         CanInlinePropertyAccess(*map) &&
          (lookup.IsConstant() ||
           (!lookup.IsFound() &&
            PrototypeChainCanNeverResolve(map, name))))) {
@@ -4997,7 +4999,7 @@ void HOptimizedGraphBuilder::BuildStoreNamed(Expression* expr,
   Handle<Map> map;
   if (monomorphic) {
     map = types->first();
-    if (map->is_dictionary_map()) monomorphic = false;
+    monomorphic = CanInlinePropertyAccess(*map);
   }
   if (monomorphic) {
     Handle<JSFunction> setter;
@@ -5138,7 +5140,7 @@ void HOptimizedGraphBuilder::HandleCompoundAssignment(Assignment* expr) {
         map = types->first();
         // We can't generate code for a monomorphic dict mode load so
         // just pretend it is not monomorphic.
-        if (map->is_dictionary_map()) monomorphic = false;
+        monomorphic = CanInlinePropertyAccess(*map);
       }
       if (monomorphic) {
         Handle<JSFunction> getter;
@@ -5896,10 +5898,10 @@ void HOptimizedGraphBuilder::VisitProperty(Property* expr) {
     bool monomorphic = false;
     if (expr->IsMonomorphic()) {
       map = types->first();
-      monomorphic = !map->is_dictionary_map();
+      monomorphic = CanInlinePropertyAccess(*map);
     } else if (object->HasMonomorphicJSObjectType()) {
       map = object->GetMonomorphicJSObjectMap();
-      monomorphic = !map->is_dictionary_map();
+      monomorphic = CanInlinePropertyAccess(*map);
     }
     if (monomorphic) {
       Handle<JSFunction> getter;
@@ -7582,7 +7584,7 @@ void HOptimizedGraphBuilder::VisitCountOperation(CountOperation* expr) {
       SmallMapList* types = prop->GetReceiverTypes();
       if (monomorphic) {
         map = types->first();
-        if (map->is_dictionary_map()) monomorphic = false;
+        monomorphic = CanInlinePropertyAccess(*map);
       }
       if (monomorphic) {
         Handle<JSFunction> getter;
index c90fea0..4468e65 100644 (file)
@@ -1828,7 +1828,17 @@ void InterceptorGetter(Local<String> name,
 void InterceptorSetter(Local<String> name,
                        Local<Value> value,
                        const v8::PropertyCallbackInfo<v8::Value>& info) {
-  // Intercept accesses that set certain integer values.
+  // Intercept accesses that set certain integer values, for which the name does
+  // not start with 'accessor_'.
+  String::Utf8Value utf8(name);
+  char* name_str = *utf8;
+  char prefix[] = "accessor_";
+  int i;
+  for (i = 0; name_str[i] && prefix[i]; ++i) {
+    if (name_str[i] != prefix[i]) break;
+  }
+  if (!prefix[i]) return;
+
   if (value->IsInt32() && value->Int32Value() < 10000) {
     Handle<Object> self = info.This();
     self->SetHiddenValue(name, value);
@@ -20319,4 +20329,93 @@ THREADED_TEST(Regress256330) {
   ExpectBoolean("%GetOptimizationStatus(f) != 2", true);
 }
 
+
+THREADED_TEST(CrankshaftInterceptorSetter) {
+  i::FLAG_allow_natives_syntax = true;
+  v8::HandleScope scope(v8::Isolate::GetCurrent());
+  Handle<FunctionTemplate> templ = FunctionTemplate::New();
+  AddInterceptor(templ, InterceptorGetter, InterceptorSetter);
+  LocalContext env;
+  env->Global()->Set(v8_str("Obj"), templ->GetFunction());
+  CompileRun("var obj = new Obj;"
+             // Initialize fields to avoid transitions later.
+             "obj.age = 0;"
+             "obj.accessor_age = 42;"
+             "function setter(i) { this.accessor_age = i; };"
+             "function getter() { return this.accessor_age; };"
+             "function setAge(i) { obj.age = i; };"
+             "Object.defineProperty(obj, 'age', { get:getter, set:setter });"
+             "setAge(1);"
+             "setAge(2);"
+             "setAge(3);"
+             "%OptimizeFunctionOnNextCall(setAge);"
+             "setAge(4);");
+  // All stores went through the interceptor.
+  ExpectInt32("obj.interceptor_age", 4);
+  ExpectInt32("obj.accessor_age", 42);
+}
+
+
+THREADED_TEST(CrankshaftInterceptorGetter) {
+  i::FLAG_allow_natives_syntax = true;
+  v8::HandleScope scope(v8::Isolate::GetCurrent());
+  Handle<FunctionTemplate> templ = FunctionTemplate::New();
+  AddInterceptor(templ, InterceptorGetter, InterceptorSetter);
+  LocalContext env;
+  env->Global()->Set(v8_str("Obj"), templ->GetFunction());
+  CompileRun("var obj = new Obj;"
+             // Initialize fields to avoid transitions later.
+             "obj.age = 1;"
+             "obj.accessor_age = 42;"
+             "function getter() { return this.accessor_age; };"
+             "function getAge() { return obj.interceptor_age; };"
+             "Object.defineProperty(obj, 'interceptor_age', { get:getter });"
+             "getAge();"
+             "getAge();"
+             "getAge();"
+             "%OptimizeFunctionOnNextCall(getAge);");
+  // Access through interceptor.
+  ExpectInt32("getAge()", 1);
+}
+
+
+THREADED_TEST(CrankshaftInterceptorFieldRead) {
+  i::FLAG_allow_natives_syntax = true;
+  v8::HandleScope scope(v8::Isolate::GetCurrent());
+  Handle<FunctionTemplate> templ = FunctionTemplate::New();
+  AddInterceptor(templ, InterceptorGetter, InterceptorSetter);
+  LocalContext env;
+  env->Global()->Set(v8_str("Obj"), templ->GetFunction());
+  CompileRun("var obj = new Obj;"
+             "obj.__proto__.interceptor_age = 42;"
+             "obj.age = 100;"
+             "function getAge() { return obj.interceptor_age; };");
+  ExpectInt32("getAge();", 100);
+  ExpectInt32("getAge();", 100);
+  ExpectInt32("getAge();", 100);
+  CompileRun("%OptimizeFunctionOnNextCall(getAge);");
+  // Access through interceptor.
+  ExpectInt32("getAge();", 100);
+}
+
+
+THREADED_TEST(CrankshaftInterceptorFieldWrite) {
+  i::FLAG_allow_natives_syntax = true;
+  v8::HandleScope scope(v8::Isolate::GetCurrent());
+  Handle<FunctionTemplate> templ = FunctionTemplate::New();
+  AddInterceptor(templ, InterceptorGetter, InterceptorSetter);
+  LocalContext env;
+  env->Global()->Set(v8_str("Obj"), templ->GetFunction());
+  CompileRun("var obj = new Obj;"
+             "obj.age = 100000;"
+             "function setAge(i) { obj.age = i };"
+             "setAge(100);"
+             "setAge(101);"
+             "setAge(102);"
+             "%OptimizeFunctionOnNextCall(setAge);"
+             "setAge(103);");
+  ExpectInt32("obj.age", 100000);
+  ExpectInt32("obj.interceptor_age", 103);
+}
+
 #endif  // V8_OS_POSIX