Title: [197732] trunk/Source/_javascript_Core
Revision
197732
Author
[email protected]
Date
2016-03-07 23:16:21 -0800 (Mon, 07 Mar 2016)

Log Message

[ES6] Implement revocable proxies
https://bugs.webkit.org/show_bug.cgi?id=154321

Reviewed by Mark Lam.

This patch is a straight forward implementation of Proxy.revocable
with respect to section 26.2.2.1 of the ECMAScript spec.
https://tc39.github.io/ecma262/#sec-proxy.revocable

This patch also fixes a bug in Proxy where we
were incorrectly caching "in", i.e, `"x" in proxy`.
We should never blatantly cache this because caching is observable
behavior by users of the language. We could come up with
a smarter caching scheme that caches only if the Proxy's
handler doesn't have a "has" property, i.e, we don't have
to call out to JS code. But for now, it's easiest to disable
caching.

* CMakeLists.txt:
* _javascript_Core.xcodeproj/project.pbxproj:
* runtime/JSGlobalObject.cpp:
(JSC::JSGlobalObject::init):
(JSC::JSGlobalObject::visitChildren):
* runtime/JSGlobalObject.h:
(JSC::JSGlobalObject::moduleRecordStructure):
(JSC::JSGlobalObject::moduleNamespaceObjectStructure):
(JSC::JSGlobalObject::proxyObjectStructure):
(JSC::JSGlobalObject::proxyRevokeStructure):
(JSC::JSGlobalObject::wasmModuleStructure):
* runtime/ProxyConstructor.cpp:
(JSC::ProxyConstructor::create):
(JSC::ProxyConstructor::ProxyConstructor):
(JSC::makeRevocableProxy):
(JSC::proxyRevocableConstructorThrowError):
(JSC::ProxyConstructor::finishCreation):
(JSC::constructProxyObject):
* runtime/ProxyConstructor.h:
(JSC::ProxyConstructor::createStructure):
* runtime/ProxyObject.cpp:
(JSC::ProxyObject::finishCreation):
(JSC::performProxyGet):
(JSC::ProxyObject::performInternalMethodGetOwnProperty):
(JSC::ProxyObject::performHasProperty):
(JSC::ProxyObject::performPut):
(JSC::performProxyCall):
(JSC::performProxyConstruct):
(JSC::ProxyObject::performDelete):
(JSC::ProxyObject::performPreventExtensions):
(JSC::ProxyObject::performIsExtensible):
(JSC::ProxyObject::performDefineOwnProperty):
(JSC::ProxyObject::performGetOwnPropertyNames):
(JSC::ProxyObject::performSetPrototype):
(JSC::ProxyObject::performGetPrototype):
(JSC::ProxyObject::getPrototype):
(JSC::ProxyObject::revoke):
(JSC::ProxyObject::visitChildren):
* runtime/ProxyObject.h:
(JSC::ProxyObject::create):
* runtime/ProxyRevoke.cpp: Added.
(JSC::ProxyRevoke::create):
(JSC::ProxyRevoke::ProxyRevoke):
(JSC::ProxyRevoke::finishCreation):
(JSC::performProxyRevoke):
(JSC::ProxyRevoke::getCallData):
(JSC::ProxyRevoke::visitChildren):
* runtime/ProxyRevoke.h: Added.
(JSC::ProxyRevoke::createStructure):
(JSC::ProxyRevoke::proxy):
(JSC::ProxyRevoke::setProxyToNull):
* tests/stress/proxy-has-property.js:
(assert):
(assert.let.handler.has):
(assert.let.foo):
* tests/stress/proxy-revoke.js: Added.
(assert):
(throw.new.Error.):
(throw.new.Error):
(callAllHandlers):
(shouldThrowNullHandler):
(allHandlersShouldThrow):
(i.let.trap.of.traps.trap.string_appeared_here.func):
(i.let.trap.of.traps.else.func):
(i.Proxy.revocable):

Modified Paths

Added Paths

Diff

Modified: trunk/Source/_javascript_Core/CMakeLists.txt (197731 => 197732)


--- trunk/Source/_javascript_Core/CMakeLists.txt	2016-03-08 07:09:23 UTC (rev 197731)
+++ trunk/Source/_javascript_Core/CMakeLists.txt	2016-03-08 07:16:21 UTC (rev 197732)
@@ -764,6 +764,7 @@
     runtime/PrototypeMap.cpp
     runtime/ProxyConstructor.cpp
     runtime/ProxyObject.cpp
+    runtime/ProxyRevoke.cpp
     runtime/ReflectObject.cpp
     runtime/RegExp.cpp
     runtime/RegExpCache.cpp

Modified: trunk/Source/_javascript_Core/ChangeLog (197731 => 197732)


--- trunk/Source/_javascript_Core/ChangeLog	2016-03-08 07:09:23 UTC (rev 197731)
+++ trunk/Source/_javascript_Core/ChangeLog	2016-03-08 07:16:21 UTC (rev 197732)
@@ -1,3 +1,89 @@
+2016-03-07  Saam barati  <[email protected]>
+
+        [ES6] Implement revocable proxies
+        https://bugs.webkit.org/show_bug.cgi?id=154321
+
+        Reviewed by Mark Lam.
+
+        This patch is a straight forward implementation of Proxy.revocable
+        with respect to section 26.2.2.1 of the ECMAScript spec.
+        https://tc39.github.io/ecma262/#sec-proxy.revocable
+
+        This patch also fixes a bug in Proxy where we
+        were incorrectly caching "in", i.e, `"x" in proxy`.
+        We should never blatantly cache this because caching is observable
+        behavior by users of the language. We could come up with
+        a smarter caching scheme that caches only if the Proxy's
+        handler doesn't have a "has" property, i.e, we don't have
+        to call out to JS code. But for now, it's easiest to disable
+        caching.
+
+        * CMakeLists.txt:
+        * _javascript_Core.xcodeproj/project.pbxproj:
+        * runtime/JSGlobalObject.cpp:
+        (JSC::JSGlobalObject::init):
+        (JSC::JSGlobalObject::visitChildren):
+        * runtime/JSGlobalObject.h:
+        (JSC::JSGlobalObject::moduleRecordStructure):
+        (JSC::JSGlobalObject::moduleNamespaceObjectStructure):
+        (JSC::JSGlobalObject::proxyObjectStructure):
+        (JSC::JSGlobalObject::proxyRevokeStructure):
+        (JSC::JSGlobalObject::wasmModuleStructure):
+        * runtime/ProxyConstructor.cpp:
+        (JSC::ProxyConstructor::create):
+        (JSC::ProxyConstructor::ProxyConstructor):
+        (JSC::makeRevocableProxy):
+        (JSC::proxyRevocableConstructorThrowError):
+        (JSC::ProxyConstructor::finishCreation):
+        (JSC::constructProxyObject):
+        * runtime/ProxyConstructor.h:
+        (JSC::ProxyConstructor::createStructure):
+        * runtime/ProxyObject.cpp:
+        (JSC::ProxyObject::finishCreation):
+        (JSC::performProxyGet):
+        (JSC::ProxyObject::performInternalMethodGetOwnProperty):
+        (JSC::ProxyObject::performHasProperty):
+        (JSC::ProxyObject::performPut):
+        (JSC::performProxyCall):
+        (JSC::performProxyConstruct):
+        (JSC::ProxyObject::performDelete):
+        (JSC::ProxyObject::performPreventExtensions):
+        (JSC::ProxyObject::performIsExtensible):
+        (JSC::ProxyObject::performDefineOwnProperty):
+        (JSC::ProxyObject::performGetOwnPropertyNames):
+        (JSC::ProxyObject::performSetPrototype):
+        (JSC::ProxyObject::performGetPrototype):
+        (JSC::ProxyObject::getPrototype):
+        (JSC::ProxyObject::revoke):
+        (JSC::ProxyObject::visitChildren):
+        * runtime/ProxyObject.h:
+        (JSC::ProxyObject::create):
+        * runtime/ProxyRevoke.cpp: Added.
+        (JSC::ProxyRevoke::create):
+        (JSC::ProxyRevoke::ProxyRevoke):
+        (JSC::ProxyRevoke::finishCreation):
+        (JSC::performProxyRevoke):
+        (JSC::ProxyRevoke::getCallData):
+        (JSC::ProxyRevoke::visitChildren):
+        * runtime/ProxyRevoke.h: Added.
+        (JSC::ProxyRevoke::createStructure):
+        (JSC::ProxyRevoke::proxy):
+        (JSC::ProxyRevoke::setProxyToNull):
+        * tests/stress/proxy-has-property.js:
+        (assert):
+        (assert.let.handler.has):
+        (assert.let.foo):
+        * tests/stress/proxy-revoke.js: Added.
+        (assert):
+        (throw.new.Error.):
+        (throw.new.Error):
+        (callAllHandlers):
+        (shouldThrowNullHandler):
+        (allHandlersShouldThrow):
+        (i.let.trap.of.traps.trap.string_appeared_here.func):
+        (i.let.trap.of.traps.else.func):
+        (i.Proxy.revocable):
+
 2016-03-07  Csaba Osztrogonác  <[email protected]>
 
         Fix the ARM build after r197687

Modified: trunk/Source/_javascript_Core/_javascript_Core.xcodeproj/project.pbxproj (197731 => 197732)


--- trunk/Source/_javascript_Core/_javascript_Core.xcodeproj/project.pbxproj	2016-03-08 07:09:23 UTC (rev 197731)
+++ trunk/Source/_javascript_Core/_javascript_Core.xcodeproj/project.pbxproj	2016-03-08 07:16:21 UTC (rev 197732)
@@ -1269,6 +1269,8 @@
 		70ECA6071AFDBEA200449739 /* TemplateRegistry.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 70ECA6021AFDBEA200449739 /* TemplateRegistry.cpp */; };
 		70ECA6081AFDBEA200449739 /* TemplateRegistry.h in Headers */ = {isa = PBXBuildFile; fileRef = 70ECA6031AFDBEA200449739 /* TemplateRegistry.h */; settings = {ATTRIBUTES = (Private, ); }; };
 		70ECA6091AFDBEA200449739 /* TemplateRegistryKey.h in Headers */ = {isa = PBXBuildFile; fileRef = 70ECA6041AFDBEA200449739 /* TemplateRegistryKey.h */; settings = {ATTRIBUTES = (Private, ); }; };
+		79160DBD1C8E3EC8008C085A /* ProxyRevoke.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 79160DBB1C8E3EC8008C085A /* ProxyRevoke.cpp */; };
+		79160DBE1C8E3EC8008C085A /* ProxyRevoke.h in Headers */ = {isa = PBXBuildFile; fileRef = 79160DBC1C8E3EC8008C085A /* ProxyRevoke.h */; settings = {ATTRIBUTES = (Private, ); }; };
 		792CB3491C4EED5C00D13AF3 /* PCToCodeOriginMap.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 792CB3471C4EED5C00D13AF3 /* PCToCodeOriginMap.cpp */; };
 		792CB34A1C4EED5C00D13AF3 /* PCToCodeOriginMap.h in Headers */ = {isa = PBXBuildFile; fileRef = 792CB3481C4EED5C00D13AF3 /* PCToCodeOriginMap.h */; settings = {ATTRIBUTES = (Private, ); }; };
 		7964656A1B952FF0003059EE /* GetPutInfo.h in Headers */ = {isa = PBXBuildFile; fileRef = 796465681B952FF0003059EE /* GetPutInfo.h */; settings = {ATTRIBUTES = (Private, ); }; };
@@ -3428,6 +3430,8 @@
 		70ECA6021AFDBEA200449739 /* TemplateRegistry.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = TemplateRegistry.cpp; sourceTree = "<group>"; };
 		70ECA6031AFDBEA200449739 /* TemplateRegistry.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = TemplateRegistry.h; sourceTree = "<group>"; };
 		70ECA6041AFDBEA200449739 /* TemplateRegistryKey.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = TemplateRegistryKey.h; sourceTree = "<group>"; };
+		79160DBB1C8E3EC8008C085A /* ProxyRevoke.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = ProxyRevoke.cpp; sourceTree = "<group>"; };
+		79160DBC1C8E3EC8008C085A /* ProxyRevoke.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ProxyRevoke.h; sourceTree = "<group>"; };
 		792CB3471C4EED5C00D13AF3 /* PCToCodeOriginMap.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = PCToCodeOriginMap.cpp; sourceTree = "<group>"; };
 		792CB3481C4EED5C00D13AF3 /* PCToCodeOriginMap.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = PCToCodeOriginMap.h; sourceTree = "<group>"; };
 		796465681B952FF0003059EE /* GetPutInfo.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = GetPutInfo.h; sourceTree = "<group>"; };
@@ -5855,6 +5859,8 @@
 				79B00CB91C6AB07E0088C65D /* ProxyConstructor.h */,
 				79B00CBA1C6AB07E0088C65D /* ProxyObject.cpp */,
 				79B00CBB1C6AB07E0088C65D /* ProxyObject.h */,
+				79160DBB1C8E3EC8008C085A /* ProxyRevoke.cpp */,
+				79160DBC1C8E3EC8008C085A /* ProxyRevoke.h */,
 				0F5780A118FE1E98001E72D9 /* PureNaN.h */,
 				0F0CD4C015F1A6040032F1C0 /* PutDirectIndexMode.h */,
 				147B84620E6DE6B1004775A4 /* PutPropertySlot.h */,
@@ -6902,6 +6908,7 @@
 				990DA67F1C8E316A00295159 /* generate_objc_protocol_type_conversions_implementation.py in Headers */,
 				799EF7C41C56ED96002B0534 /* B3PCToOriginMap.h in Headers */,
 				79B00CBF1C6AB07E0088C65D /* ProxyObject.h in Headers */,
+				79160DBE1C8E3EC8008C085A /* ProxyRevoke.h in Headers */,
 				792CB34A1C4EED5C00D13AF3 /* PCToCodeOriginMap.h in Headers */,
 				79CFC6F01C33B10000C768EA /* LLIntPCRanges.h in Headers */,
 				79D5CD5B1C1106A900CECA07 /* SamplingProfiler.h in Headers */,
@@ -9162,6 +9169,7 @@
 				E355F3521B7DC85300C50DC5 /* ModuleLoaderObject.cpp in Sources */,
 				0F338E0B1BF0276C0013C88F /* B3Compilation.cpp in Sources */,
 				14469DE0107EC7E700650446 /* NativeErrorConstructor.cpp in Sources */,
+				79160DBD1C8E3EC8008C085A /* ProxyRevoke.cpp in Sources */,
 				14469DE1107EC7E700650446 /* NativeErrorPrototype.cpp in Sources */,
 				E33E8D201B9013DE00346B52 /* NativeStdFunctionCell.cpp in Sources */,
 				148F21B7107EC5470042EC2C /* Nodes.cpp in Sources */,

Modified: trunk/Source/_javascript_Core/runtime/JSGlobalObject.cpp (197731 => 197732)


--- trunk/Source/_javascript_Core/runtime/JSGlobalObject.cpp	2016-03-08 07:09:23 UTC (rev 197731)
+++ trunk/Source/_javascript_Core/runtime/JSGlobalObject.cpp	2016-03-08 07:16:21 UTC (rev 197732)
@@ -130,6 +130,7 @@
 #include "ParserError.h"
 #include "ProxyConstructor.h"
 #include "ProxyObject.h"
+#include "ProxyRevoke.h"
 #include "ReflectObject.h"
 #include "RegExpConstructor.h"
 #include "RegExpMatchesArray.h"
@@ -370,6 +371,7 @@
     m_moduleRecordStructure.set(vm, this, JSModuleRecord::createStructure(vm, this, m_objectPrototype.get()));
     m_moduleNamespaceObjectStructure.set(vm, this, JSModuleNamespaceObject::createStructure(vm, this, jsNull()));
     m_proxyObjectStructure.set(vm, this, ProxyObject::createStructure(vm, this, m_objectPrototype.get()));
+    m_proxyRevokeStructure.set(vm, this, ProxyRevoke::createStructure(vm, this, m_functionPrototype.get()));
     
 #if ENABLE(WEBASSEMBLY)
     m_wasmModuleStructure.set(vm, this, JSWASMModule::createStructure(vm, this));
@@ -911,6 +913,7 @@
     visitor.append(&thisObject->m_dollarVMStructure);
     visitor.append(&thisObject->m_internalFunctionStructure);
     visitor.append(&thisObject->m_proxyObjectStructure);
+    visitor.append(&thisObject->m_proxyRevokeStructure);
 #if ENABLE(WEBASSEMBLY)
     visitor.append(&thisObject->m_wasmModuleStructure);
 #endif

Modified: trunk/Source/_javascript_Core/runtime/JSGlobalObject.h (197731 => 197732)


--- trunk/Source/_javascript_Core/runtime/JSGlobalObject.h	2016-03-08 07:09:23 UTC (rev 197731)
+++ trunk/Source/_javascript_Core/runtime/JSGlobalObject.h	2016-03-08 07:16:21 UTC (rev 197732)
@@ -282,6 +282,7 @@
     WriteBarrier<Structure> m_moduleRecordStructure;
     WriteBarrier<Structure> m_moduleNamespaceObjectStructure;
     WriteBarrier<Structure> m_proxyObjectStructure;
+    WriteBarrier<Structure> m_proxyRevokeStructure;
 #if ENABLE(WEBASSEMBLY)
     WriteBarrier<Structure> m_wasmModuleStructure;
 #endif
@@ -535,6 +536,7 @@
     Structure* moduleRecordStructure() const { return m_moduleRecordStructure.get(); }
     Structure* moduleNamespaceObjectStructure() const { return m_moduleNamespaceObjectStructure.get(); }
     Structure* proxyObjectStructure() const { return m_proxyObjectStructure.get(); }
+    Structure* proxyRevokeStructure() const { return m_proxyRevokeStructure.get(); }
 #if ENABLE(WEBASSEMBLY)
     Structure* wasmModuleStructure() const { return m_wasmModuleStructure.get(); }
 #endif

Modified: trunk/Source/_javascript_Core/runtime/ProxyConstructor.cpp (197731 => 197732)


--- trunk/Source/_javascript_Core/runtime/ProxyConstructor.cpp	2016-03-08 07:09:23 UTC (rev 197731)
+++ trunk/Source/_javascript_Core/runtime/ProxyConstructor.cpp	2016-03-08 07:16:21 UTC (rev 197732)
@@ -27,10 +27,13 @@
 #include "ProxyConstructor.h"
 
 #include "Error.h"
+#include "IdentifierInlines.h"
 #include "JSCJSValueInlines.h"
 #include "JSCellInlines.h"
+#include "ObjectConstructor.h"
 #include "ObjectPrototype.h"
 #include "ProxyObject.h"
+#include "ProxyRevoke.h"
 #include "StructureInlines.h"
 
 namespace JSC {
@@ -42,7 +45,7 @@
 ProxyConstructor* ProxyConstructor::create(VM& vm, Structure* structure)
 {
     ProxyConstructor* constructor = new (NotNull, allocateCell<ProxyConstructor>(vm.heap)) ProxyConstructor(vm, structure);
-    constructor->finishCreation(vm, "Proxy");
+    constructor->finishCreation(vm, "Proxy", structure->globalObject());
     return constructor;
 }
 
@@ -51,11 +54,42 @@
 {
 }
 
-void ProxyConstructor::finishCreation(VM& vm, const char* name)
+static EncodedJSValue JSC_HOST_CALL makeRevocableProxy(ExecState* exec)
 {
+    if (exec->argumentCount() < 2)
+        return throwVMTypeError(exec, ASCIILiteral("Proxy.revocable needs to be called with two arguments: the target and the handler."));
+
+    VM& vm = exec->vm();
+    ArgList args(exec);
+    JSValue target = args.at(0);
+    JSValue handler = args.at(1);
+    ProxyObject* proxy = ProxyObject::create(exec, exec->lexicalGlobalObject()->proxyObjectStructure(), target, handler);
+    if (vm.exception())
+        return JSValue::encode(JSValue());
+    ProxyRevoke* revoke = ProxyRevoke::create(vm, exec->lexicalGlobalObject()->proxyRevokeStructure(), proxy);
+    if (vm.exception())
+        return JSValue::encode(JSValue());
+
+    JSObject* result = constructEmptyObject(exec);
+    if (vm.exception())
+        return JSValue::encode(JSValue());
+    result->putDirect(vm, makeIdentifier(vm, "proxy"), proxy, None);
+    result->putDirect(vm, makeIdentifier(vm, "revoke"), revoke, None);
+
+    return JSValue::encode(result);
+}
+
+static EncodedJSValue JSC_HOST_CALL proxyRevocableConstructorThrowError(ExecState* exec)
+{
+    return throwVMTypeError(exec, ASCIILiteral("Proxy.revocable can not be constructed. It can only be called."));
+}
+
+void ProxyConstructor::finishCreation(VM& vm, const char* name, JSGlobalObject* globalObject)
+{
     Base::finishCreation(vm, name);
 
-    putDirectWithoutTransition(vm, vm.propertyNames->length, jsNumber(2), ReadOnly | DontDelete | DontEnum);
+    putDirect(vm, vm.propertyNames->length, jsNumber(2), ReadOnly | DontDelete | DontEnum);
+    putDirect(vm, makeIdentifier(vm, "revocable"), JSFunction::create(vm, globalObject, 2, ASCIILiteral("revocable"), makeRevocableProxy, NoIntrinsic, proxyRevocableConstructorThrowError));
 }
 
 static EncodedJSValue JSC_HOST_CALL constructProxyObject(ExecState* exec)

Modified: trunk/Source/_javascript_Core/runtime/ProxyConstructor.h (197731 => 197732)


--- trunk/Source/_javascript_Core/runtime/ProxyConstructor.h	2016-03-08 07:09:23 UTC (rev 197731)
+++ trunk/Source/_javascript_Core/runtime/ProxyConstructor.h	2016-03-08 07:16:21 UTC (rev 197732)
@@ -44,7 +44,7 @@
         return Structure::create(vm, globalObject, prototype, TypeInfo(ObjectType, StructureFlags), info()); 
     }
 
-    void finishCreation(VM&, const char* name);
+    void finishCreation(VM&, const char* name, JSGlobalObject*);
 
 private:
     ProxyConstructor(VM&, Structure*);

Modified: trunk/Source/_javascript_Core/runtime/ProxyObject.cpp (197731 => 197732)


--- trunk/Source/_javascript_Core/runtime/ProxyObject.cpp	2016-03-08 07:09:23 UTC (rev 197731)
+++ trunk/Source/_javascript_Core/runtime/ProxyObject.cpp	2016-03-08 07:16:21 UTC (rev 197732)
@@ -53,10 +53,8 @@
         return;
     }
     if (ProxyObject* targetAsProxy = jsDynamicCast<ProxyObject*>(target)) {
-        // FIXME: Add tests for this once we implement Proxy.revoke(.).
-        // https://bugs.webkit.org/show_bug.cgi?id=154321
         if (targetAsProxy->handler().isNull()) {
-            throwTypeError(exec, ASCIILiteral("If a Proxy's handler is another Proxy object, the other Proxy object must have a non-null handler."));
+            throwTypeError(exec, ASCIILiteral("If a Proxy's handler is another Proxy object, the other Proxy should not have been revoked."));
             return;
         }
     }
@@ -77,6 +75,8 @@
     m_handler.set(vm, this, handler);
 }
 
+static const char* s_proxyAlreadyRevokedErrorMessage = "Proxy has already been revoked. No more operations are allowed to be performed on it.";
+
 static EncodedJSValue performProxyGet(ExecState* exec, EncodedJSValue thisValue, PropertyName propertyName)
 {
     VM& vm = exec->vm();
@@ -113,7 +113,7 @@
 
     JSValue handlerValue = proxyObject->handler();
     if (handlerValue.isNull())
-        return throwVMTypeError(exec, ASCIILiteral("Proxy 'handler' is null. It should be an Object."));
+        return throwVMTypeError(exec, ASCIILiteral(s_proxyAlreadyRevokedErrorMessage));
 
     JSObject* handler = jsCast<JSObject*>(handlerValue);
     CallData callData;
@@ -164,7 +164,7 @@
 
     JSValue handlerValue = this->handler();
     if (handlerValue.isNull()) {
-        throwVMTypeError(exec, ASCIILiteral("Proxy 'handler' is null. It should be an Object."));
+        throwVMTypeError(exec, ASCIILiteral(s_proxyAlreadyRevokedErrorMessage));
         return false;
     }
 
@@ -267,7 +267,7 @@
 
     JSValue handlerValue = this->handler();
     if (handlerValue.isNull()) {
-        throwVMTypeError(exec, ASCIILiteral("Proxy 'handler' is null. It should be an Object."));
+        throwVMTypeError(exec, ASCIILiteral(s_proxyAlreadyRevokedErrorMessage));
         return false;
     }
 
@@ -360,7 +360,7 @@
 
     JSValue handlerValue = this->handler();
     if (handlerValue.isNull()) {
-        throwVMTypeError(exec, ASCIILiteral("Proxy 'handler' is null. It should be an Object."));
+        throwVMTypeError(exec, ASCIILiteral(s_proxyAlreadyRevokedErrorMessage));
         return;
     }
 
@@ -447,7 +447,7 @@
     ProxyObject* proxy = jsCast<ProxyObject*>(exec->callee());
     JSValue handlerValue = proxy->handler();
     if (handlerValue.isNull())
-        return throwVMTypeError(exec, ASCIILiteral("Proxy 'handler' is null. It should be an Object."));
+        return throwVMTypeError(exec, ASCIILiteral(s_proxyAlreadyRevokedErrorMessage));
 
     JSObject* handler = jsCast<JSObject*>(handlerValue);
     CallData callData;
@@ -492,7 +492,7 @@
     ProxyObject* proxy = jsCast<ProxyObject*>(exec->callee());
     JSValue handlerValue = proxy->handler();
     if (handlerValue.isNull())
-        return throwVMTypeError(exec, ASCIILiteral("Proxy 'handler' is null. It should be an Object."));
+        return throwVMTypeError(exec, ASCIILiteral(s_proxyAlreadyRevokedErrorMessage));
 
     JSObject* handler = jsCast<JSObject*>(handlerValue);
     CallData callData;
@@ -546,7 +546,7 @@
 
     JSValue handlerValue = this->handler();
     if (handlerValue.isNull()) {
-        throwVMTypeError(exec, ASCIILiteral("Proxy 'handler' is null. It should be an Object."));
+        throwVMTypeError(exec, ASCIILiteral(s_proxyAlreadyRevokedErrorMessage));
         return false;
     }
 
@@ -615,7 +615,7 @@
 
     JSValue handlerValue = this->handler();
     if (handlerValue.isNull()) {
-        throwVMTypeError(exec, ASCIILiteral("Proxy 'handler' is null. It should be an Object."));
+        throwVMTypeError(exec, ASCIILiteral(s_proxyAlreadyRevokedErrorMessage));
         return false;
     }
 
@@ -663,7 +663,7 @@
 
     JSValue handlerValue = this->handler();
     if (handlerValue.isNull()) {
-        throwVMTypeError(exec, ASCIILiteral("Proxy 'handler' is null. It should be an Object."));
+        throwVMTypeError(exec, ASCIILiteral(s_proxyAlreadyRevokedErrorMessage));
         return false;
     }
 
@@ -725,7 +725,7 @@
 
     JSValue handlerValue = this->handler();
     if (handlerValue.isNull()) {
-        throwVMTypeError(exec, ASCIILiteral("Proxy 'handler' is null. It should be an Object."));
+        throwVMTypeError(exec, ASCIILiteral(s_proxyAlreadyRevokedErrorMessage));
         return false;
     }
 
@@ -809,7 +809,7 @@
     VM& vm = exec->vm();
     JSValue handlerValue = this->handler();
     if (handlerValue.isNull()) {
-        throwVMTypeError(exec, ASCIILiteral("Proxy 'handler' is null. It should be an Object."));
+        throwVMTypeError(exec, ASCIILiteral(s_proxyAlreadyRevokedErrorMessage));
         return;
     }
 
@@ -970,7 +970,7 @@
 
     JSValue handlerValue = this->handler();
     if (handlerValue.isNull()) {
-        throwVMTypeError(exec, ASCIILiteral("Proxy 'handler' is null. It should be an Object."));
+        throwVMTypeError(exec, ASCIILiteral(s_proxyAlreadyRevokedErrorMessage));
         return false;
     }
 
@@ -1030,7 +1030,7 @@
 
     JSValue handlerValue = this->handler();
     if (handlerValue.isNull()) {
-        throwVMTypeError(exec, ASCIILiteral("Proxy 'handler' is null. It should be an Object."));
+        throwVMTypeError(exec, ASCIILiteral(s_proxyAlreadyRevokedErrorMessage));
         return JSValue();
     }
 
@@ -1078,6 +1078,13 @@
     return jsCast<ProxyObject*>(object)->performGetPrototype(exec);
 }
 
+void ProxyObject::revoke(VM& vm)
+{ 
+    // This should only ever be called once and we should strictly transition from Object to null.
+    RELEASE_ASSERT(!m_handler.get().isNull() && m_handler.get().isObject());
+    m_handler.set(vm, this, jsNull());
+}
+
 void ProxyObject::visitChildren(JSCell* cell, SlotVisitor& visitor)
 {
     ProxyObject* thisObject = jsCast<ProxyObject*>(cell);

Modified: trunk/Source/_javascript_Core/runtime/ProxyObject.h (197731 => 197732)


--- trunk/Source/_javascript_Core/runtime/ProxyObject.h	2016-03-08 07:09:23 UTC (rev 197731)
+++ trunk/Source/_javascript_Core/runtime/ProxyObject.h	2016-03-08 07:16:21 UTC (rev 197732)
@@ -38,7 +38,7 @@
 
     // We lie an say we override getPropertyNames() because it prevents
     // property name enumeration caching.
-    const static unsigned StructureFlags = Base::StructureFlags | OverridesGetOwnPropertySlot | TypeOfShouldCallGetCallData | InterceptsGetOwnPropertySlotByIndexEvenWhenLengthIsNotZero | OverridesGetPropertyNames;
+    const static unsigned StructureFlags = Base::StructureFlags | OverridesGetOwnPropertySlot | TypeOfShouldCallGetCallData | InterceptsGetOwnPropertySlotByIndexEvenWhenLengthIsNotZero | OverridesGetPropertyNames | ProhibitsPropertyCaching;
 
     static ProxyObject* create(ExecState* exec, Structure* structure, JSValue target, JSValue handler)
     {
@@ -66,6 +66,7 @@
     static void putByIndex(JSCell*, ExecState*, unsigned propertyName, JSValue, bool shouldThrow);
     void putByIndexCommon(ExecState*, JSValue thisValue, unsigned propertyName, JSValue putValue, bool shouldThrow);
     JSValue performGetPrototype(ExecState*);
+    void revoke(VM&);
 
 private:
     ProxyObject(VM&, Structure*);

Added: trunk/Source/_javascript_Core/runtime/ProxyRevoke.cpp (0 => 197732)


--- trunk/Source/_javascript_Core/runtime/ProxyRevoke.cpp	                        (rev 0)
+++ trunk/Source/_javascript_Core/runtime/ProxyRevoke.cpp	2016-03-08 07:16:21 UTC (rev 197732)
@@ -0,0 +1,89 @@
+/*
+ * Copyright (C) 2016 Apple Inc. All Rights Reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. 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.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``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 APPLE INC. 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.
+ */
+
+#include "config.h"
+#include "ProxyRevoke.h"
+
+#include "JSCJSValueInlines.h"
+#include "ProxyObject.h"
+#include "SlotVisitorInlines.h"
+#include "StructureInlines.h"
+
+namespace JSC {
+
+STATIC_ASSERT_IS_TRIVIALLY_DESTRUCTIBLE(ProxyRevoke);
+
+const ClassInfo ProxyRevoke::s_info = { "ProxyRevoke", &Base::s_info, 0, CREATE_METHOD_TABLE(ProxyRevoke) };
+
+ProxyRevoke* ProxyRevoke::create(VM& vm, Structure* structure, ProxyObject* proxy)
+{
+    ProxyRevoke* revoke = new (NotNull, allocateCell<ProxyRevoke>(vm.heap)) ProxyRevoke(vm, structure);
+    revoke->finishCreation(vm, "revoke", proxy);
+    return revoke;
+}
+
+ProxyRevoke::ProxyRevoke(VM& vm, Structure* structure)
+    : Base(vm, structure)
+{
+}
+
+void ProxyRevoke::finishCreation(VM& vm, const char* name, ProxyObject* proxy)
+{
+    Base::finishCreation(vm, String(name));
+    m_proxy.set(vm, this, proxy);
+
+    putDirect(vm, vm.propertyNames->length, jsNumber(0), ReadOnly | DontDelete | DontEnum);
+}
+
+static EncodedJSValue JSC_HOST_CALL performProxyRevoke(ExecState* exec)
+{
+    ProxyRevoke* proxyRevoke = jsCast<ProxyRevoke*>(exec->callee());
+    JSValue proxyValue = proxyRevoke->proxy();
+    if (proxyValue.isNull())
+        return JSValue::encode(jsUndefined());
+
+    ProxyObject* proxy = jsCast<ProxyObject*>(proxyValue);
+    VM& vm = exec->vm();
+    proxy->revoke(vm);
+    proxyRevoke->setProxyToNull(vm);
+    return JSValue::encode(jsUndefined());
+}
+
+CallType ProxyRevoke::getCallData(JSCell*, CallData& callData)
+{
+    callData.native.function = performProxyRevoke;
+    return CallType::Host;
+}
+
+void ProxyRevoke::visitChildren(JSCell* cell, SlotVisitor& visitor)
+{
+    ProxyRevoke* thisObject = jsCast<ProxyRevoke*>(cell);
+    ASSERT_GC_OBJECT_INHERITS(thisObject, info());
+    Base::visitChildren(thisObject, visitor);
+
+    visitor.append(&thisObject->m_proxy);
+}
+
+} // namespace JSC

Copied: trunk/Source/_javascript_Core/runtime/ProxyRevoke.h (from rev 197731, trunk/Source/_javascript_Core/runtime/ProxyConstructor.h) (0 => 197732)


--- trunk/Source/_javascript_Core/runtime/ProxyRevoke.h	                        (rev 0)
+++ trunk/Source/_javascript_Core/runtime/ProxyRevoke.h	2016-03-08 07:16:21 UTC (rev 197732)
@@ -0,0 +1,63 @@
+/*
+ * Copyright (C) 2016 Apple Inc. All Rights Reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. 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.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``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 APPLE INC. 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.
+ */
+
+#ifndef ProxyRevoke_h
+#define ProxyRevoke_h
+
+#include "InternalFunction.h"
+
+namespace JSC {
+
+class ProxyObject;
+
+class ProxyRevoke : public InternalFunction {
+public:
+    typedef InternalFunction Base;
+    static const unsigned StructureFlags = Base::StructureFlags;
+
+    static ProxyRevoke* create(VM&, Structure*, ProxyObject*);
+
+    DECLARE_INFO;
+
+    static Structure* createStructure(VM& vm, JSGlobalObject* globalObject, JSValue prototype) 
+    { 
+        return Structure::create(vm, globalObject, prototype, TypeInfo(ObjectType, StructureFlags), info()); 
+    }
+
+    void finishCreation(VM&, const char* name, ProxyObject*);
+    static void visitChildren(JSCell*, SlotVisitor&);
+    JSValue proxy() { return m_proxy.get(); }
+    void setProxyToNull(VM& vm) { return m_proxy.set(vm, this, jsNull()); }
+
+private:
+    ProxyRevoke(VM&, Structure*);
+    static CallType getCallData(JSCell*, CallData&);
+
+    WriteBarrier<Unknown> m_proxy;
+};
+
+} // namespace JSC
+
+#endif // ProxyRevoke_h

Modified: trunk/Source/_javascript_Core/tests/es6.yaml (197731 => 197732)


--- trunk/Source/_javascript_Core/tests/es6.yaml	2016-03-08 07:09:23 UTC (rev 197731)
+++ trunk/Source/_javascript_Core/tests/es6.yaml	2016-03-08 07:16:21 UTC (rev 197732)
@@ -1063,7 +1063,7 @@
 - path: es6/Proxy_preventExtensions_handler.js
   cmd: runES6 :normal
 - path: es6/Proxy_Proxy.revocable.js
-  cmd: runES6 :fail
+  cmd: runES6 :normal
 - path: es6/Proxy_set_handler.js
   cmd: runES6 :normal
 - path: es6/Proxy_set_handler_instances_of_proxies.js

Modified: trunk/Source/_javascript_Core/tests/stress/proxy-has-property.js (197731 => 197732)


--- trunk/Source/_javascript_Core/tests/stress/proxy-has-property.js	2016-03-08 07:09:23 UTC (rev 197731)
+++ trunk/Source/_javascript_Core/tests/stress/proxy-has-property.js	2016-03-08 07:16:21 UTC (rev 197732)
@@ -443,3 +443,23 @@
         assert(!called1);
     }
 }
+
+{
+    let called = false;
+    let handler = {
+        has: function(...args) {
+            called = true;
+            return Reflect.has(...args);
+        }
+    };
+    let proxy = new Proxy({}, handler);
+    let foo = function() {
+        assert(!Reflect.has(proxy, "x"));
+        assert(called);
+        called = false;
+    }
+    noInline(foo)
+    for (let i = 0; i < 10000; i++) {
+        foo();
+    }
+}

Added: trunk/Source/_javascript_Core/tests/stress/proxy-revoke.js (0 => 197732)


--- trunk/Source/_javascript_Core/tests/stress/proxy-revoke.js	                        (rev 0)
+++ trunk/Source/_javascript_Core/tests/stress/proxy-revoke.js	2016-03-08 07:16:21 UTC (rev 197732)
@@ -0,0 +1,191 @@
+function assert(b) {
+    if (!b)
+        throw new Error("bad assertion.");
+}
+
+{
+    assert(Proxy.revocable.length === 2);
+    assert(Proxy.revocable.name === "revocable");
+
+    {
+        let threw = false;
+        try {
+            new Proxy.revocable;
+        } catch(e) {
+            threw = true;
+            assert(e.toString() === "TypeError: Proxy.revocable can not be constructed. It can only be called.");
+        }
+        assert(threw);
+    }
+
+    {
+        let threw = false;
+        try {
+            Proxy.revocable();
+        } catch(e) {
+            threw = true;
+            assert(e.toString() === "TypeError: Proxy.revocable needs to be called with two arguments: the target and the handler.");
+        }
+        assert(threw);
+    }
+
+    {
+        let threw = false;
+        try {
+            Proxy.revocable({});
+        } catch(e) {
+            threw = true;
+            assert(e.toString() === "TypeError: Proxy.revocable needs to be called with two arguments: the target and the handler.");
+        }
+        assert(threw);
+    }
+
+    {
+        let threw = false;
+        try {
+            Proxy.revocable({}, null);
+        } catch(e) {
+            threw = true;
+            assert(e.toString() === "TypeError: A Proxy's 'handler' should be an Object");
+        }
+        assert(threw);
+    }
+
+    {
+        let threw = false;
+        try {
+            Proxy.revocable(null, {});
+        } catch(e) {
+            threw = true;
+            assert(e.toString() === "TypeError: A Proxy's 'target' should be an Object");
+        }
+        assert(threw);
+    }
+
+    {
+        let threw = false;
+        try {
+            Proxy.revocable({}, {}, {}); // It's okay to call with > 2 arguments.
+        } catch(e) {
+            threw = true;
+        }
+        assert(!threw);
+    }
+
+    for (let i = 0; i < 500; i++) {
+        let threw = false;
+        try {
+            let {revoke} =  Proxy.revocable({}, {}); // It's okay to call with > 2 arguments.
+            new revoke;
+        } catch(e) {
+            threw = true;
+            assert(e.toString() === "TypeError: function is not a constructor (evaluating 'new revoke')");
+        }
+        assert(threw);
+    }
+
+    for (let i = 0; i < 500; i++) {
+        function foo() {
+            let threw = false;
+            let {proxy, revoke} = Proxy.revocable({}, {});
+            revoke();
+            try {
+                new Proxy(proxy, {});
+            } catch(e) {
+                threw = true;
+                assert(e.toString() === "TypeError: If a Proxy's handler is another Proxy object, the other Proxy should not have been revoked.");
+            }
+            assert(threw);
+        }
+        foo();
+    }
+}
+
+function callAllHandlers(proxy) {
+    Reflect.getPrototypeOf(proxy);
+    Reflect.setPrototypeOf(proxy, null);
+    Reflect.isExtensible(proxy);
+    Reflect.preventExtensions(proxy);
+    Reflect.getOwnPropertyDescriptor(proxy, "x")
+    Reflect.has(proxy, "x")
+    Reflect.get(proxy, "x")
+    proxy["x"] = 20; // Reflect.set
+    Reflect.deleteProperty(proxy, "x");
+    Reflect.defineProperty(proxy, "x", {value: 40, enumerable: true, configurable: true});
+    Reflect.ownKeys(proxy);
+    Reflect.apply(proxy, this, []);
+    Reflect.construct(proxy, []);
+}
+
+function shouldThrowNullHandler(f) {
+    let threw = false;
+    try {
+        f();
+    } catch(e) {
+        threw = true;
+        assert(e.toString() === "TypeError: Proxy has already been revoked. No more operations are allowed to be performed on it.");
+    }
+    assert(threw);
+}
+
+function allHandlersShouldThrow(proxy) {
+    shouldThrowNullHandler(() => Reflect.getPrototypeOf(proxy));
+    shouldThrowNullHandler(() => Reflect.setPrototypeOf(proxy, null));
+    shouldThrowNullHandler(() => Reflect.isExtensible(proxy));
+    shouldThrowNullHandler(() => Reflect.preventExtensions(proxy));
+    shouldThrowNullHandler(() => Reflect.getOwnPropertyDescriptor(proxy, "x"));
+    shouldThrowNullHandler(() => Reflect.has(proxy, "x"));
+    shouldThrowNullHandler(() => Reflect.get(proxy, "x"));
+    shouldThrowNullHandler(() => proxy["x"] = 20); // Reflect.set
+    shouldThrowNullHandler(() => Reflect.deleteProperty(proxy, "x"));
+    shouldThrowNullHandler(() => Reflect.defineProperty(proxy, "x", {value: 40, enumerable: true, configurable: true}));
+    shouldThrowNullHandler(() => Reflect.ownKeys(proxy));
+    shouldThrowNullHandler(() => Reflect.apply(proxy, this, []));
+    shouldThrowNullHandler(() => Reflect.construct(proxy, []));
+}
+
+const traps = [
+    "getPrototypeOf",
+    "setPrototypeOf",
+    "isExtensible",
+    "preventExtensions",
+    "getOwnPropertyDescriptor",
+    "has",
+    "get",
+    "set",
+    "deleteProperty",
+    "defineProperty",
+    "ownKeys",
+    "apply",
+    "construct"
+];
+
+for (let i = 0; i < 500; i++) {
+    let handler = {};
+    let count = 0;
+    let trapsCalled = new Set;
+    for (let trap of traps) {
+        let func;
+        if (trap !== "set") {
+            func = `(function ${trap}(...args) { count++; trapsCalled.add(${"'" + trap + "'"}); return Reflect.${trap}(...args); })`;
+        } else {
+            func = `(function ${trap}(proxy, prop, v) { trapsCalled.add(${"'" + trap + "'"}); count++; proxy[prop] = v; })`;
+        }
+        handler[trap] = eval(func);
+    }
+
+
+    let {proxy, revoke} = Proxy.revocable(function(){}, handler); // To make callable and constructible true.
+    callAllHandlers(proxy);
+    assert(count >= traps.length);
+    for (let trap of traps) {
+        let result = trapsCalled.has(trap);
+        assert(result);
+    }
+
+    revoke();
+    allHandlersShouldThrow(proxy);
+
+    for (let i = 0; i < 50; i++)
+        revoke(); // Repeatedly calling revoke is OK.
+}
_______________________________________________
webkit-changes mailing list
[email protected]
https://lists.webkit.org/mailman/listinfo/webkit-changes

Reply via email to