A first skeleton for introducing Harmony proxies.
authorrossberg@chromium.org <rossberg@chromium.org@ce2b1a6d-e550-0410-aec6-3dcde31c8c00>
Fri, 13 May 2011 10:58:25 +0000 (10:58 +0000)
committerrossberg@chromium.org <rossberg@chromium.org@ce2b1a6d-e550-0410-aec6-3dcde31c8c00>
Fri, 13 May 2011 10:58:25 +0000 (10:58 +0000)
1) Add new type JSProxy for representing proxy objects.
   Currently devoid of functionality, i.e., all properties are undefined.

2) Some rudimentary global $Proxy functions to create proxies.

Next step: Hook up getProperty and getOwnProperty handlers. Will probably
require introducing a new LookupResult type, which is a mixture of
INTERCEPTOR (handles any property) and CALLBACK (calls back to JS).
Can we unify this somehow?

TODO: Should probably rename existing Proxy type to something like
"Foreign", to avoid confusion.

Review URL: http://codereview.chromium.org/6932068

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

17 files changed:
include/v8.h
src/bootstrapper.cc
src/factory.cc
src/factory.h
src/heap.cc
src/heap.h
src/ia32/frames-ia32.h
src/objects-debug.cc
src/objects-inl.h
src/objects-printer.cc
src/objects-visiting.cc
src/objects.cc
src/objects.h
src/proxy.js
src/runtime.cc
src/runtime.h
src/runtime.js

index 457633a6e80f4f15226ea92ce519101c49c37987..9c737fe80be150c7a124c21b4e39d3af5e520f1b 100644 (file)
@@ -3682,7 +3682,7 @@ class Internals {
   static const int kFullStringRepresentationMask = 0x07;
   static const int kExternalTwoByteRepresentationTag = 0x02;
 
-  static const int kJSObjectType = 0xa1;
+  static const int kJSObjectType = 0xa2;
   static const int kFirstNonstringType = 0x80;
   static const int kProxyType = 0x85;
 
index eb7a4ce98a4ad3c1213f81c0e67a69a5070fb0c2..f33143925cc40716b108f01360dcff052d189741 100644 (file)
@@ -1638,10 +1638,12 @@ bool Genesis::InstallNatives() {
 
 
 bool Genesis::InstallExperimentalNatives() {
-  if (FLAG_harmony_proxies) {
-    for (int i = ExperimentalNatives::GetDebuggerCount();
-         i < ExperimentalNatives::GetBuiltinsCount();
-         i++) {
+  for (int i = ExperimentalNatives::GetDebuggerCount();
+       i < ExperimentalNatives::GetBuiltinsCount();
+       i++) {
+    if (FLAG_harmony_proxies &&
+        strcmp(ExperimentalNatives::GetScriptName(i).start(),
+               "native proxy.js") == 0) {
       if (!CompileExperimentalBuiltin(isolate(), i)) return false;
     }
   }
index 7dee66f6de955f76e1100f2cd1ccde8e7f1475f1..a8634acdd6fec2ae833c02d6d5a3d97e9c90b9dc 100644 (file)
@@ -832,6 +832,15 @@ Handle<JSArray> Factory::NewJSArrayWithElements(Handle<FixedArray> elements,
 }
 
 
+Handle<JSProxy> Factory::NewJSProxy(Handle<Object> handler,
+                                    Handle<Object> prototype) {
+  CALL_HEAP_FUNCTION(
+      isolate(),
+      isolate()->heap()->AllocateJSProxy(*handler, *prototype),
+      JSProxy);
+}
+
+
 Handle<SharedFunctionInfo> Factory::NewSharedFunctionInfo(
     Handle<String> name,
     int number_of_literals,
index 71bfdc4c4b55b126fc959d7e9cf36ab8545ead37..b1d46822b559a88dc41a2f09308718c1b26ac122 100644 (file)
@@ -231,6 +231,8 @@ class Factory {
       Handle<FixedArray> elements,
       PretenureFlag pretenure = NOT_TENURED);
 
+  Handle<JSProxy> NewJSProxy(Handle<Object> handler, Handle<Object> prototype);
+
   Handle<JSFunction> NewFunction(Handle<String> name,
                                  Handle<Object> prototype);
 
index 8f21669cc715f3f85dd68f0f27df54426168f4dc..222484aba1d4844f4fdcf5f17dde54df8fdf9091 100644 (file)
@@ -3213,6 +3213,27 @@ MaybeObject* Heap::AllocateJSObject(JSFunction* constructor,
 }
 
 
+MaybeObject* Heap::AllocateJSProxy(Object* handler, Object* prototype) {
+  // Allocate map.
+  // TODO(rossberg): Once we optimize proxies, think about a scheme to share
+  // maps. Will probably depend on the identity of the handler object, too.
+  Object* map_obj;
+  MaybeObject* maybe_map_obj = AllocateMap(JS_PROXY_TYPE, JSProxy::kSize);
+  if (!maybe_map_obj->ToObject(&map_obj)) return maybe_map_obj;
+  Map* map = Map::cast(map_obj);
+  map->set_prototype(prototype);
+  map->set_pre_allocated_property_fields(1);
+  map->set_inobject_properties(1);
+
+  // Allocate the proxy object.
+  Object* result;
+  MaybeObject* maybe_result = Allocate(map, NEW_SPACE);
+  if (!maybe_result->ToObject(&result)) return maybe_result;
+  JSProxy::cast(result)->set_handler(handler);
+  return result;
+}
+
+
 MaybeObject* Heap::AllocateGlobalObject(JSFunction* constructor) {
   ASSERT(constructor->has_initial_map());
   Map* map = constructor->initial_map();
index 2febb5df38dde821efa0f201c8a0384f28aafaf0..6c29a0afc2498af37ed3760f01337d83843d0226 100644 (file)
@@ -452,6 +452,13 @@ class Heap {
   // Please note this does not perform a garbage collection.
   MUST_USE_RESULT MaybeObject* AllocateFunctionPrototype(JSFunction* function);
 
+  // Allocates a Harmony Proxy.
+  // Returns Failure::RetryAfterGC(requested_bytes, space) if the allocation
+  // failed.
+  // Please note this does not perform a garbage collection.
+  MUST_USE_RESULT MaybeObject* AllocateJSProxy(Object* handler,
+                                               Object* prototype);
+
   // Reinitialize an JSGlobalProxy based on a constructor.  The object
   // must have the same size as objects allocated using the
   // constructor.  The object is reinitialized and behaves as an
index 0f95abd874f7cb328b589651fbd311b10a0f4565..bc65ddfaddbd6365ba544a507a15c1d9f38c507a 100644 (file)
@@ -80,8 +80,8 @@ class EntryFrameConstants : public AllStatic {
 
 class ExitFrameConstants : public AllStatic {
  public:
-  static const int kCodeOffset      = -2 * kPointerSize;
-  static const int kSPOffset        = -1 * kPointerSize;
+  static const int kCodeOffset     = -2 * kPointerSize;
+  static const int kSPOffset       = -1 * kPointerSize;
 
   static const int kCallerFPOffset =  0 * kPointerSize;
   static const int kCallerPCOffset = +1 * kPointerSize;
@@ -94,7 +94,9 @@ class ExitFrameConstants : public AllStatic {
 
 class StandardFrameConstants : public AllStatic {
  public:
-  static const int kFixedFrameSize    =  4;
+  // StandardFrame::IterateExpressions assumes that kContextOffset is the last
+  // object pointer.
+  static const int kFixedFrameSize    =  4;  // Currently unused.
   static const int kExpressionsOffset = -3 * kPointerSize;
   static const int kMarkerOffset      = -2 * kPointerSize;
   static const int kContextOffset     = -1 * kPointerSize;
index a0ce6050e529b8d4005f2da6236ad83adc83b9b4..ba78253ea064f7b0746ca1046c9721c4ee9629b0 100644 (file)
@@ -155,6 +155,9 @@ void HeapObject::HeapObjectVerify() {
       break;
     case FILLER_TYPE:
       break;
+    case JS_PROXY_TYPE:
+      JSProxy::cast(this)->JSProxyVerify();
+      break;
     case PROXY_TYPE:
       Proxy::cast(this)->ProxyVerify();
       break;
@@ -461,6 +464,11 @@ void JSRegExp::JSRegExpVerify() {
 }
 
 
+void JSProxy::JSProxyVerify() {
+  ASSERT(IsJSProxy());
+  VerifyPointer(handler());
+}
+
 void Proxy::ProxyVerify() {
   ASSERT(IsProxy());
 }
index f4de359b4694719eae23f67739371cb2d5060a44..c96bf56f9fbde5228973e6d00ba8bb0431365e49 100644 (file)
@@ -584,6 +584,12 @@ bool Object::IsStringWrapper() {
 }
 
 
+bool Object::IsJSProxy() {
+  return Object::IsHeapObject()
+      && HeapObject::cast(this)->map()->instance_type() == JS_PROXY_TYPE;
+}
+
+
 bool Object::IsProxy() {
   return Object::IsHeapObject()
       && HeapObject::cast(this)->map()->instance_type() == PROXY_TYPE;
@@ -1898,6 +1904,7 @@ CAST_ACCESSOR(JSBuiltinsObject)
 CAST_ACCESSOR(Code)
 CAST_ACCESSOR(JSArray)
 CAST_ACCESSOR(JSRegExp)
+CAST_ACCESSOR(JSProxy)
 CAST_ACCESSOR(Proxy)
 CAST_ACCESSOR(ByteArray)
 CAST_ACCESSOR(ExternalArray)
@@ -3521,6 +3528,9 @@ void JSBuiltinsObject::set_javascript_builtin_code(Builtins::JavaScript id,
 }
 
 
+ACCESSORS(JSProxy, handler, Object, kHandlerOffset)
+
+
 Address Proxy::proxy() {
   return AddressFrom<Address>(READ_INTPTR_FIELD(this, kProxyOffset));
 }
index 5b1f5a7ad06fed5499c751c788227c4b86b11730..7a584baef581547e33cbff8e3aa004a5c5a47c2c 100644 (file)
@@ -148,6 +148,9 @@ void HeapObject::HeapObjectPrint(FILE* out) {
     case CODE_TYPE:
       Code::cast(this)->CodePrint(out);
       break;
+    case JS_PROXY_TYPE:
+      JSProxy::cast(this)->JSProxyPrint(out);
+      break;
     case PROXY_TYPE:
       Proxy::cast(this)->ProxyPrint(out);
       break;
@@ -408,6 +411,7 @@ static const char* TypeToString(InstanceType type) {
     case JS_FUNCTION_TYPE: return "JS_FUNCTION";
     case CODE_TYPE: return "CODE";
     case JS_ARRAY_TYPE: return "JS_ARRAY";
+    case JS_PROXY_TYPE: return "JS_PROXY";
     case JS_REGEXP_TYPE: return "JS_REGEXP";
     case JS_VALUE_TYPE: return "JS_VALUE";
     case JS_GLOBAL_OBJECT_TYPE: return "JS_GLOBAL_OBJECT";
@@ -530,6 +534,15 @@ void String::StringPrint(FILE* out) {
 }
 
 
+void JSProxy::JSProxyPrint(FILE* out) {
+  HeapObject::PrintHeader(out, "JSProxy");
+  PrintF(out, " - map = 0x%p\n", reinterpret_cast<void*>(map()));
+  PrintF(out, " - handler = ");
+  handler()->Print(out);
+  PrintF(out, "\n");
+}
+
+
 void JSFunction::JSFunctionPrint(FILE* out) {
   HeapObject::PrintHeader(out, "Function");
   PrintF(out, " - map = 0x%p\n", reinterpret_cast<void*>(map()));
index bcb48ea5a8abccccfb858fe6866a08584fc30e8f..eab864b12de10b89553a0856acf9f58dfa5a52d5 100644 (file)
@@ -88,6 +88,11 @@ StaticVisitorBase::VisitorId StaticVisitorBase::GetVisitorId(
     case SHARED_FUNCTION_INFO_TYPE:
       return kVisitSharedFunctionInfo;
 
+    case JS_PROXY_TYPE:
+      return GetVisitorIdForSize(kVisitDataObject,
+                                 kVisitDataObjectGeneric,
+                                 JSProxy::kSize);
+
     case PROXY_TYPE:
       return GetVisitorIdForSize(kVisitDataObject,
                                  kVisitDataObjectGeneric,
index 724a734a2145d6b682725a6b9b7e9d5b7c2f26af..12bda591c82421266895ef4ab4fe7b6cec7b2403 100644 (file)
@@ -134,24 +134,22 @@ Object* Object::ToBoolean() {
 void Object::Lookup(String* name, LookupResult* result) {
   Object* holder = NULL;
   if (IsSmi()) {
-    Heap* heap = Isolate::Current()->heap();
-    Context* global_context = heap->isolate()->context()->global_context();
+    Context* global_context = Isolate::Current()->context()->global_context();
     holder = global_context->number_function()->instance_prototype();
   } else {
     HeapObject* heap_object = HeapObject::cast(this);
     if (heap_object->IsJSObject()) {
       return JSObject::cast(this)->Lookup(name, result);
     }
-    Heap* heap = heap_object->GetHeap();
+    Context* global_context = Isolate::Current()->context()->global_context();
     if (heap_object->IsString()) {
-      Context* global_context = heap->isolate()->context()->global_context();
       holder = global_context->string_function()->instance_prototype();
     } else if (heap_object->IsHeapNumber()) {
-      Context* global_context = heap->isolate()->context()->global_context();
       holder = global_context->number_function()->instance_prototype();
     } else if (heap_object->IsBoolean()) {
-      Context* global_context = heap->isolate()->context()->global_context();
       holder = global_context->boolean_function()->instance_prototype();
+    } else if (heap_object->IsJSProxy()) {
+      return result->NotFound();  // For now...
     }
   }
   ASSERT(holder != NULL);  // Cannot handle null or undefined.
@@ -494,12 +492,13 @@ MaybeObject* Object::GetProperty(Object* receiver,
   Heap* heap = name->GetHeap();
 
   // Traverse the prototype chain from the current object (this) to
-  // the holder and check for access rights. This avoid traversing the
+  // the holder and check for access rights. This avoids traversing the
   // objects more than once in case of interceptors, because the
   // holder will always be the interceptor holder and the search may
   // only continue with a current object just after the interceptor
   // holder in the prototype chain.
   Object* last = result->IsProperty() ? result->holder() : heap->null_value();
+  ASSERT(this != this->GetPrototype());
   for (Object* current = this; true; current = current->GetPrototype()) {
     if (current->IsAccessCheckNeeded()) {
       // Check if we're allowed to read from the current object. Note
@@ -575,6 +574,8 @@ MaybeObject* Object::GetElementWithReceiver(Object* receiver, uint32_t index) {
       holder = global_context->number_function()->instance_prototype();
     } else if (heap_object->IsBoolean()) {
       holder = global_context->boolean_function()->instance_prototype();
+    } else if (heap_object->IsJSProxy()) {
+      return heap->undefined_value();  // For now...
     } else {
       // Undefined and null have no indexed properties.
       ASSERT(heap_object->IsUndefined() || heap_object->IsNull());
@@ -595,9 +596,10 @@ Object* Object::GetPrototype() {
 
   HeapObject* heap_object = HeapObject::cast(this);
 
-  // The object is either a number, a string, a boolean, or a real JS object.
-  if (heap_object->IsJSObject()) {
-    return JSObject::cast(this)->map()->prototype();
+  // The object is either a number, a string, a boolean,
+  // a real JS object, or a Harmony proxy.
+  if (heap_object->IsJSObject() || heap_object->IsJSProxy()) {
+    return heap_object->map()->prototype();
   }
   Heap* heap = heap_object->GetHeap();
   Context* context = heap->isolate()->context()->global_context();
@@ -1154,6 +1156,9 @@ void HeapObject::IterateBody(InstanceType type, int object_size,
     case ODDBALL_TYPE:
       Oddball::BodyDescriptor::IterateBody(this, v);
       break;
+    case JS_PROXY_TYPE:
+      JSProxy::BodyDescriptor::IterateBody(this, v);
+      break;
     case PROXY_TYPE:
       reinterpret_cast<Proxy*>(this)->ProxyIterateBody(v);
       break;
index 3570579714a17e5df626a4c59575b63f8a990f0d..cb4a420ba943c128331eb779bf84cf5a8cfaaf94 100644 (file)
@@ -90,6 +90,7 @@
 //       - Code
 //       - Map
 //       - Oddball
+//       - JSProxy
 //       - Proxy
 //       - SharedFunctionInfo
 //       - Struct
@@ -287,6 +288,7 @@ static const int kVariableSizeSentinel = 0;
   V(JS_GLOBAL_PROPERTY_CELL_TYPE)                                              \
                                                                                \
   V(HEAP_NUMBER_TYPE)                                                          \
+  V(JS_PROXY_TYPE)                                                             \
   V(PROXY_TYPE)                                                                \
   V(BYTE_ARRAY_TYPE)                                                           \
   /* Note: the order of these external array */                                \
@@ -515,6 +517,7 @@ enum InstanceType {
   // objects.
   HEAP_NUMBER_TYPE,
   PROXY_TYPE,
+  JS_PROXY_TYPE,
   BYTE_ARRAY_TYPE,
   EXTERNAL_BYTE_ARRAY_TYPE,  // FIRST_EXTERNAL_ARRAY_TYPE
   EXTERNAL_UNSIGNED_BYTE_ARRAY_TYPE,
@@ -720,6 +723,7 @@ class MaybeObject BASE_EMBEDDED {
   V(Proxy)                                     \
   V(Boolean)                                   \
   V(JSArray)                                   \
+  V(JSProxy)                                   \
   V(JSRegExp)                                  \
   V(HashTable)                                 \
   V(Dictionary)                                \
@@ -3956,7 +3960,7 @@ class Map: public HeapObject {
 
 
 // An abstract superclass, a marker class really, for simple structure classes.
-// It doesn't carry much functionality but allows struct classes to me
+// It doesn't carry much functionality but allows struct classes to be
 // identified in the type system.
 class Struct: public HeapObject {
  public:
@@ -6103,6 +6107,39 @@ class JSGlobalPropertyCell: public HeapObject {
 };
 
 
+// The JSProxy describes EcmaScript Harmony proxies
+class JSProxy: public HeapObject {
+ public:
+  // [handler]: The handler property.
+  DECL_ACCESSORS(handler, Object)
+
+  // Casting.
+  static inline JSProxy* cast(Object* obj);
+
+  // Dispatched behavior.
+#ifdef OBJECT_PRINT
+  inline void JSProxyPrint() {
+    JSProxyPrint(stdout);
+  }
+  void JSProxyPrint(FILE* out);
+#endif
+#ifdef DEBUG
+  void JSProxyVerify();
+#endif
+
+  // Layout description.
+  static const int kHandlerOffset = HeapObject::kHeaderSize;
+  static const int kSize = kHandlerOffset + kPointerSize;
+
+  typedef FixedBodyDescriptor<kHandlerOffset,
+                              kHandlerOffset + kPointerSize,
+                              kSize> BodyDescriptor;
+
+ private:
+  DISALLOW_IMPLICIT_CONSTRUCTORS(JSProxy);
+};
+
+
 
 // Proxy describes objects pointing from JavaScript to C structures.
 // Since they cannot contain references to JS HeapObjects they can be
index 25169831a8bdbcb55b479d341989b91fb9e64ab8..01d48b485ac193a19c6cc681b4e88a1533f56945 100644 (file)
 // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 
 global.Proxy = new $Object();
+
+var $Proxy = global.Proxy
+
+var fundamentalTraps = [
+  "getOwnPropertyDescriptor",
+  "getPropertyDescriptor",
+  "getOwnPropertyNames",
+  "getPropertyNames",
+  "defineProperty",
+  "delete",
+  "fix",
+]
+
+var derivedTraps = [
+  "has",
+  "hasOwn",
+  "get",
+  "set",
+  "enumerate",
+  "keys",
+]
+
+var functionTraps = [
+  "callTrap",
+  "constructTrap",
+]
+
+$Proxy.createFunction = function(handler, callTrap, constructTrap) {
+  handler.callTrap = callTrap
+  handler.constructTrap = constructTrap
+  $Proxy.create(handler)
+}
+
+$Proxy.create = function(handler, proto) {
+  if (!IS_SPEC_OBJECT(proto)) proto = $Object.prototype
+  return %CreateJSProxy(handler, proto)
+}
index 2093983aedc48fd8cb5bc89a27489b4305ae2c83..8062926505d75a39cc01aa25104a3ec67b8bb64c 100644 (file)
@@ -588,6 +588,17 @@ RUNTIME_FUNCTION(MaybeObject*, Runtime_CreateArrayLiteralShallow) {
 }
 
 
+RUNTIME_FUNCTION(MaybeObject*, Runtime_CreateJSProxy) {
+  ASSERT(args.length() == 2);
+  Object* handler = args[0];
+  Object* prototype = args[1];
+  Object* used_prototype =
+      (prototype->IsJSObject() || prototype->IsJSProxy()) ? prototype
+          : isolate->heap()->null_value();
+  return isolate->heap()->AllocateJSProxy(handler, used_prototype);
+}
+
+
 RUNTIME_FUNCTION(MaybeObject*, Runtime_CreateCatchExtensionObject) {
   ASSERT(args.length() == 2);
   CONVERT_CHECKED(String, key, args[0]);
index a61e68134ced3120ac20a78f2b6011105aeede9b..d3223d1a8223f9b5b860b27698b17aff035b76fa 100644 (file)
@@ -275,6 +275,9 @@ namespace internal {
   F(CreateArrayLiteral, 3, 1) \
   F(CreateArrayLiteralShallow, 3, 1) \
   \
+  /* Harmony proxies */ \
+  F(CreateJSProxy, 2, 1) \
+  \
   /* Catch context extension objects */ \
   F(CreateCatchExtensionObject, 2, 1) \
   \
index 4f53efe0b064c1c6e508ae6c455566276f8fd794..77b97aed837d654f8f8a3fda74cce5393674c25b 100644 (file)
@@ -644,6 +644,6 @@ function DefaultString(x) {
 // NOTE: Setting the prototype for Array must take place as early as
 // possible due to code generation for array literals.  When
 // generating code for a array literal a boilerplate array is created
-// that is cloned when running the code.  It is essiential that the
+// that is cloned when running the code.  It is essential that the
 // boilerplate gets the right prototype.
 %FunctionSetPrototype($Array, new $Array(0));