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
This commit is contained in:
parent
edac3d93c2
commit
e14402b1df
@ -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++) {
|
||||
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);
|
||||
|
||||
|
21
src/heap.cc
21
src/heap.cc
@ -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
|
||||
|
@ -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
|
||||
|
37
src/proxy.js
37
src/proxy.js
@ -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));
|
||||
|
Loading…
Reference in New Issue
Block a user