Commit e14402b1 authored by rossberg@chromium.org's avatar rossberg@chromium.org

A first skeleton for introducing Harmony proxies.

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
parent edac3d93
......@@ -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;
......
......@@ -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;
}
}
......
......@@ -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,
......
......@@ -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);
......
......@@ -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();
......
......@@ -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
......
......@@ -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;
......
......@@ -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());
}
......
......@@ -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));
}
......
......@@ -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()));
......
......@@ -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,
......
......@@ -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;
......
......@@ -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
......
......@@ -26,3 +26,40 @@
// 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)
}
......@@ -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]);
......
......@@ -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) \
\
......
......@@ -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));
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment