Author: olehougaard
Date: Thu Dec 18 03:28:13 2008
New Revision: 999

Added:
    branches/bleeding_edge/test/mjsunit/throw-and-catch-function.js
Modified:
    branches/bleeding_edge/src/ast.h
    branches/bleeding_edge/src/codegen-arm.cc
    branches/bleeding_edge/src/codegen-ia32.cc
    branches/bleeding_edge/src/contexts.h
    branches/bleeding_edge/src/factory.cc
    branches/bleeding_edge/src/factory.h
    branches/bleeding_edge/src/heap.cc
    branches/bleeding_edge/src/heap.h
    branches/bleeding_edge/src/objects-inl.h
    branches/bleeding_edge/src/objects.h
    branches/bleeding_edge/src/parser.cc
    branches/bleeding_edge/src/runtime.cc
    branches/bleeding_edge/src/runtime.h

Log:
Fixing a subtle bug in receiver resolution when a thrown and caught  
function is called from a catch-block.
Second attempt - now with better memory efficiency.
Review URL: http://codereview.chromium.org/14834

Modified: branches/bleeding_edge/src/ast.h
==============================================================================
--- branches/bleeding_edge/src/ast.h    (original)
+++ branches/bleeding_edge/src/ast.h    Thu Dec 18 03:28:13 2008
@@ -409,15 +409,18 @@

  class WithEnterStatement: public Statement {
   public:
-  explicit WithEnterStatement(Expression* expression)
-      : expression_(expression) { }
+  explicit WithEnterStatement(Expression* expression, bool is_catch_block)
+      : expression_(expression), is_catch_block_(is_catch_block) { }

    virtual void Accept(AstVisitor* v);

    Expression* expression() const  { return expression_; }

+  bool is_catch_block() const { return is_catch_block_; }
+
   private:
    Expression* expression_;
+  bool is_catch_block_;
  };



Modified: branches/bleeding_edge/src/codegen-arm.cc
==============================================================================
--- branches/bleeding_edge/src/codegen-arm.cc   (original)
+++ branches/bleeding_edge/src/codegen-arm.cc   Thu Dec 18 03:28:13 2008
@@ -1276,7 +1276,11 @@
    Comment cmnt(masm_, "[ WithEnterStatement");
    CodeForStatement(node);
    Load(node->expression());
-  __ CallRuntime(Runtime::kPushContext, 1);
+  if (node->is_catch_block()) {
+    __ CallRuntime(Runtime::kPushCatchContext, 1);
+  } else {
+    __ CallRuntime(Runtime::kPushContext, 1);
+  }
    if (kDebug) {
      Label verified_true;
      __ cmp(r0, Operand(cp));

Modified: branches/bleeding_edge/src/codegen-ia32.cc
==============================================================================
--- branches/bleeding_edge/src/codegen-ia32.cc  (original)
+++ branches/bleeding_edge/src/codegen-ia32.cc  Thu Dec 18 03:28:13 2008
@@ -1586,7 +1586,11 @@
    Comment cmnt(masm_, "[ WithEnterStatement");
    CodeForStatement(node);
    Load(node->expression());
-  __ CallRuntime(Runtime::kPushContext, 1);
+  if (node->is_catch_block()) {
+    __ CallRuntime(Runtime::kPushCatchContext, 1);
+  } else {
+    __ CallRuntime(Runtime::kPushContext, 1);
+  }

    if (kDebug) {
      Label verified_true;

Modified: branches/bleeding_edge/src/contexts.h
==============================================================================
--- branches/bleeding_edge/src/contexts.h       (original)
+++ branches/bleeding_edge/src/contexts.h       Thu Dec 18 03:28:13 2008
@@ -264,6 +264,12 @@
      global_context()->set_out_of_memory(Heap::true_value());
    }

+  // The exception holder is the object used as a with object in
+  // the implementation of a catch block.
+  bool is_exception_holder(Object* object) {
+    return IsCatchContext() && extension() == object;
+  }
+
  #define GLOBAL_CONTEXT_FIELD_ACCESSORS(index, type, name) \
    void  set_##name(type* value) {                         \
      ASSERT(IsGlobalContext());                            \

Modified: branches/bleeding_edge/src/factory.cc
==============================================================================
--- branches/bleeding_edge/src/factory.cc       (original)
+++ branches/bleeding_edge/src/factory.cc       Thu Dec 18 03:28:13 2008
@@ -130,8 +130,12 @@


  Handle<Context> Factory::NewWithContext(Handle<Context> previous,
-                                        Handle<JSObject> extension) {
-  CALL_HEAP_FUNCTION(Heap::AllocateWithContext(*previous, *extension),  
Context);
+                                        Handle<JSObject> extension,
+                                        bool is_catch_context) {
+  CALL_HEAP_FUNCTION(Heap::AllocateWithContext(*previous,
+                                               *extension,
+                                               is_catch_context),
+                     Context);
  }



Modified: branches/bleeding_edge/src/factory.h
==============================================================================
--- branches/bleeding_edge/src/factory.h        (original)
+++ branches/bleeding_edge/src/factory.h        Thu Dec 18 03:28:13 2008
@@ -126,7 +126,8 @@

    // Create a 'with' context.
    static Handle<Context> NewWithContext(Handle<Context> previous,
-                                        Handle<JSObject> extension);
+                                        Handle<JSObject> extension,
+                                        bool is_catch_context);

    // Return the Symbol maching the passed in string.
    static Handle<String> SymbolFromString(Handle<String> value);

Modified: branches/bleeding_edge/src/heap.cc
==============================================================================
--- branches/bleeding_edge/src/heap.cc  (original)
+++ branches/bleeding_edge/src/heap.cc  Thu Dec 18 03:28:13 2008
@@ -1017,6 +1017,10 @@

    obj = AllocateMap(FIXED_ARRAY_TYPE, HeapObject::kHeaderSize);
    if (obj->IsFailure()) return false;
+  catch_context_map_ = Map::cast(obj);
+
+  obj = AllocateMap(FIXED_ARRAY_TYPE, HeapObject::kHeaderSize);
+  if (obj->IsFailure()) return false;
    global_context_map_ = Map::cast(obj);

    obj = AllocateMap(JS_FUNCTION_TYPE, JSFunction::kSize);
@@ -2387,11 +2391,13 @@
  }


-Object* Heap::AllocateWithContext(Context* previous, JSObject* extension) {
+Object* Heap::AllocateWithContext(Context* previous,
+                                  JSObject* extension,
+                                  bool is_catch_context) {
    Object* result = Heap::AllocateFixedArray(Context::MIN_CONTEXT_SLOTS);
    if (result->IsFailure()) return result;
    Context* context = reinterpret_cast<Context*>(result);
-  context->set_map(context_map());
+  context->set_map(is_catch_context ? catch_context_map() : context_map());
    context->set_closure(previous->closure());
    context->set_fcontext(previous->fcontext());
    context->set_previous(previous);

Modified: branches/bleeding_edge/src/heap.h
==============================================================================
--- branches/bleeding_edge/src/heap.h   (original)
+++ branches/bleeding_edge/src/heap.h   Thu Dec 18 03:28:13 2008
@@ -92,6 +92,7 @@
    V(Map, fixed_array_map)                               \
    V(Map, hash_table_map)                                \
    V(Map, context_map)                                   \
+  V(Map, catch_context_map)                             \
    V(Map, global_context_map)                            \
    V(Map, code_map)                                      \
    V(Map, oddball_map)                                   \
@@ -430,7 +431,9 @@
    static Object* AllocateFunctionContext(int length, JSFunction* closure);

    // Allocate a 'with' context.
-  static Object* AllocateWithContext(Context* previous, JSObject*  
extension);
+  static Object* AllocateWithContext(Context* previous,
+                                     JSObject* extension,
+                                     bool is_catch_context);

    // Allocates a new utility object in the old generation.
    static Object* AllocateStruct(InstanceType type);

Modified: branches/bleeding_edge/src/objects-inl.h
==============================================================================
--- branches/bleeding_edge/src/objects-inl.h    (original)
+++ branches/bleeding_edge/src/objects-inl.h    Thu Dec 18 03:28:13 2008
@@ -348,7 +348,14 @@
  bool Object::IsContext() {
    return Object::IsHeapObject()
      && (HeapObject::cast(this)->map() == Heap::context_map() ||
+        HeapObject::cast(this)->map() == Heap::catch_context_map() ||
          HeapObject::cast(this)->map() == Heap::global_context_map());
+}
+
+
+bool Object::IsCatchContext() {
+  return Object::IsHeapObject()
+    && HeapObject::cast(this)->map() == Heap::catch_context_map();
  }



Modified: branches/bleeding_edge/src/objects.h
==============================================================================
--- branches/bleeding_edge/src/objects.h        (original)
+++ branches/bleeding_edge/src/objects.h        Thu Dec 18 03:28:13 2008
@@ -616,6 +616,7 @@
    inline bool IsFixedArray();
    inline bool IsDescriptorArray();
    inline bool IsContext();
+  inline bool IsCatchContext();
    inline bool IsGlobalContext();
    inline bool IsJSFunction();
    inline bool IsCode();

Modified: branches/bleeding_edge/src/parser.cc
==============================================================================
--- branches/bleeding_edge/src/parser.cc        (original)
+++ branches/bleeding_edge/src/parser.cc        Thu Dec 18 03:28:13 2008
@@ -120,7 +120,10 @@
    Statement* ParseContinueStatement(bool* ok);
    Statement* ParseBreakStatement(ZoneStringList* labels, bool* ok);
    Statement* ParseReturnStatement(bool* ok);
-  Block* WithHelper(Expression* obj, ZoneStringList* labels, bool* ok);
+  Block* WithHelper(Expression* obj,
+                    ZoneStringList* labels,
+                    bool is_catch_block,
+                    bool* ok);
    Statement* ParseWithStatement(ZoneStringList* labels, bool* ok);
    CaseClause* ParseCaseClause(bool* default_seen_ptr, bool* ok);
    SwitchStatement* ParseSwitchStatement(ZoneStringList* labels, bool* ok);
@@ -1923,7 +1926,10 @@
  }


-Block* Parser::WithHelper(Expression* obj, ZoneStringList* labels, bool*  
ok) {
+Block* Parser::WithHelper(Expression* obj,
+                          ZoneStringList* labels,
+                          bool is_catch_block,
+                          bool* ok) {
    // Parse the statement and collect escaping labels.
    ZoneList<Label*>* label_list = NEW(ZoneList<Label*>(0));
    LabelCollector collector(label_list);
@@ -1940,7 +1946,7 @@
    Block* result = NEW(Block(NULL, 2, false));

    if (result) {
-    result->AddStatement(NEW(WithEnterStatement(obj)));
+    result->AddStatement(NEW(WithEnterStatement(obj, is_catch_block)));

      // Create body block.
      Block* body = NEW(Block(NULL, 1, false));
@@ -1976,7 +1982,7 @@
    Expression* expr = ParseExpression(true, CHECK_OK);
    Expect(Token::RPAREN, CHECK_OK);

-  return WithHelper(expr, labels, CHECK_OK);
+  return WithHelper(expr, labels, false, CHECK_OK);
  }


@@ -2137,7 +2143,7 @@
        catch_var = top_scope_->NewTemporary(Factory::catch_var_symbol());
        Expression* obj = MakeCatchContext(name, catch_var);
        { Target target(this, &catch_collector);
-        catch_block = WithHelper(obj, NULL, CHECK_OK);
+        catch_block = WithHelper(obj, NULL, true, CHECK_OK);
        }
      } else {
        Expect(Token::LBRACE, CHECK_OK);

Modified: branches/bleeding_edge/src/runtime.cc
==============================================================================
--- branches/bleeding_edge/src/runtime.cc       (original)
+++ branches/bleeding_edge/src/runtime.cc       Thu Dec 18 03:28:13 2008
@@ -3428,19 +3428,15 @@
    return result;  // non-failure
  }

-
-static Object* Runtime_PushContext(Arguments args) {
-  NoHandleAllocation ha;
-  ASSERT(args.length() == 1);
-
+static Object* PushContextHelper(Object* object, bool is_catch_context) {
    // Convert the object to a proper JavaScript object.
-  Object* object = args[0];
-  if (!object->IsJSObject()) {
-    object = object->ToObject();
-    if (object->IsFailure()) {
-      if (!Failure::cast(object)->IsInternalError()) return object;
+  Object* js_object = object;
+  if (!js_object->IsJSObject()) {
+    js_object = js_object->ToObject();
+    if (js_object->IsFailure()) {
+      if (!Failure::cast(js_object)->IsInternalError()) return js_object;
        HandleScope scope;
-      Handle<Object> handle(args[0]);
+      Handle<Object> handle(object);
        Handle<Object> result =
            Factory::NewTypeError("with_expression", HandleVector(&handle,  
1));
        return Top::Throw(*result);
@@ -3448,15 +3444,32 @@
    }

    Object* result =
-      Heap::AllocateWithContext(Top::context(), JSObject::cast(object));
+      Heap::AllocateWithContext(Top::context(),
+                                JSObject::cast(js_object),
+                                is_catch_context);
    if (result->IsFailure()) return result;

-  Top::set_context(Context::cast(result));
+  Context* context = Context::cast(result);
+  Top::set_context(context);

    return result;
  }


+static Object* Runtime_PushContext(Arguments args) {
+  NoHandleAllocation ha;
+  ASSERT(args.length() == 1);
+  return PushContextHelper(args[0], false);
+}
+
+
+static Object* Runtime_PushCatchContext(Arguments args) {
+  NoHandleAllocation ha;
+  ASSERT(args.length() == 1);
+  return PushContextHelper(args[0], true);
+}
+
+
  static Object* Runtime_LookupContext(Arguments args) {
    HandleScope scope;
    ASSERT(args.length() == 2);
@@ -3554,9 +3567,14 @@
    if (!holder.is_null() && holder->IsJSObject()) {
      ASSERT(Handle<JSObject>::cast(holder)->HasProperty(*name));
      JSObject* object = JSObject::cast(*holder);
-    JSObject* receiver = (object->IsGlobalObject())
-        ? GlobalObject::cast(object)->global_receiver()
-        : ComputeReceiverForNonGlobal(object);
+    JSObject* receiver;
+    if (object->IsGlobalObject()) {
+      receiver = GlobalObject::cast(object)->global_receiver();
+    } else if (context->is_exception_holder(*holder)) {
+      receiver = Top::context()->global()->global_receiver();
+    } else {
+      receiver = ComputeReceiverForNonGlobal(object);
+    }
      // No need to unhole the value here. This is taken care of by the
      // GetProperty function.
      Object* value = object->GetProperty(*name);
@@ -5220,7 +5238,9 @@
    Handle<Context> previous(context_chain->previous());
    Handle<JSObject> extension(JSObject::cast(context_chain->extension()));
    return Factory::NewWithContext(
-      CopyWithContextChain(function_context, previous), extension);
+      CopyWithContextChain(function_context, previous),
+      extension,
+      context_chain->IsCatchContext());
  }



Modified: branches/bleeding_edge/src/runtime.h
==============================================================================
--- branches/bleeding_edge/src/runtime.h        (original)
+++ branches/bleeding_edge/src/runtime.h        Thu Dec 18 03:28:13 2008
@@ -264,6 +264,7 @@
    /* Contexts */ \
    F(NewContext, 1) \
    F(PushContext, 1) \
+  F(PushCatchContext, 1) \
    F(LookupContext, 2) \
    F(LoadContextSlot, 2) \
    F(LoadContextSlotNoReferenceError, 2) \

Added: branches/bleeding_edge/test/mjsunit/throw-and-catch-function.js
==============================================================================
--- (empty file)
+++ branches/bleeding_edge/test/mjsunit/throw-and-catch-function.js     Thu Dec 
 
18 03:28:13 2008
@@ -0,0 +1,50 @@
+// Copyright 2008 the V8 project authors. All rights reserved.
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+//     * Redistributions of source code must retain the above copyright
+//       notice, this list of conditions and the following disclaimer.
+//     * Redistributions in binary form must reproduce the above
+//       copyright notice, this list of conditions and the following
+//       disclaimer in the documentation and/or other materials provided
+//       with the distribution.
+//     * Neither the name of Google Inc. nor the names of its
+//       contributors may be used to endorse or promote products derived
+//       from this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+var g = this;
+var x = new Object();
+x.e = function() { return this; };
+try {
+  throw x.e;
+} catch (e) {
+  assertTrue(e() === g);
+}
+try {
+  throw x.e;
+} catch (e) {
+  with(x) { assertTrue(e() === x); }
+}
+with(x) {
+  try { throw e; } catch (e) { assertTrue(e() === g); }
+}
+var e = 0;
+try {
+  throw x.e;
+} catch (e) {
+  var e = 7;
+}
+assertEquals(0, e);

--~--~---------~--~----~------------~-------~--~----~
v8-dev mailing list
[email protected]
http://groups.google.com/group/v8-dev
-~----------~----~----~----~------~----~------~--~---

Reply via email to