Modified: trunk/Source/_javascript_Core/runtime/ProxyObject.cpp (197532 => 197533)
--- trunk/Source/_javascript_Core/runtime/ProxyObject.cpp 2016-03-04 00:54:21 UTC (rev 197532)
+++ trunk/Source/_javascript_Core/runtime/ProxyObject.cpp 2016-03-04 01:07:04 UTC (rev 197533)
@@ -708,6 +708,99 @@
return jsCast<ProxyObject*>(object)->performIsExtensible(exec);
}
+bool ProxyObject::performDefineOwnProperty(ExecState* exec, PropertyName propertyName, const PropertyDescriptor& descriptor, bool shouldThrow)
+{
+ VM& vm = exec->vm();
+
+ JSObject* target = this->target();
+ auto performDefaultDefineOwnProperty = [&] {
+ return target->methodTable(vm)->defineOwnProperty(target, exec, propertyName, descriptor, shouldThrow);
+ };
+
+ if (vm.propertyNames->isPrivateName(Identifier::fromUid(&vm, propertyName.uid())))
+ return performDefaultDefineOwnProperty();
+
+ JSValue handlerValue = this->handler();
+ if (handlerValue.isNull()) {
+ throwVMTypeError(exec, ASCIILiteral("Proxy 'handler' is null. It should be an Object."));
+ return false;
+ }
+
+ JSObject* handler = jsCast<JSObject*>(handlerValue);
+ CallData callData;
+ CallType callType;
+ JSValue definePropertyMethod = handler->getMethod(exec, callData, callType, vm.propertyNames->defineProperty, ASCIILiteral("'defineProperty' property of a Proxy's handler should be callable."));
+ if (vm.exception())
+ return false;
+
+ if (definePropertyMethod.isUndefined())
+ return performDefaultDefineOwnProperty();
+
+ JSObject* descriptorObject = constructObjectFromPropertyDescriptor(exec, descriptor);
+ if (vm.exception())
+ return false;
+
+ MarkedArgumentBuffer arguments;
+ arguments.append(target);
+ arguments.append(identifierToSafePublicJSValue(vm, Identifier::fromUid(&vm, propertyName.uid())));
+ arguments.append(descriptorObject);
+ JSValue trapResult = call(exec, definePropertyMethod, callType, callData, handler, arguments);
+ if (vm.exception())
+ return false;
+
+ bool trapResultAsBool = trapResult.toBoolean(exec);
+ if (vm.exception())
+ return false;
+
+ if (!trapResultAsBool)
+ return false;
+
+ PropertyDescriptor targetDescriptor;
+ bool isTargetDescriptorDefined = target->getOwnPropertyDescriptor(exec, propertyName, targetDescriptor);
+ if (vm.exception())
+ return false;
+
+ bool targetIsExtensible = target->isExtensible(exec);
+ if (vm.exception())
+ return false;
+ bool settingConfigurableToFalse = descriptor.configurablePresent() && !descriptor.configurable();
+
+ if (!isTargetDescriptorDefined) {
+ if (!targetIsExtensible) {
+ throwVMTypeError(exec, ASCIILiteral("Proxy's 'defineProperty' trap returned true even though getOwnPropertyDescriptor of the Proxy's target returned undefined and the target is non-extensible."));
+ return false;
+ }
+ if (settingConfigurableToFalse) {
+ throwVMTypeError(exec, ASCIILiteral("Proxy's 'defineProperty' trap returned true for a non-configurable field even though getOwnPropertyDescriptor of the Proxy's target returned undefined."));
+ return false;
+ }
+
+ return true;
+ }
+
+ ASSERT(isTargetDescriptorDefined);
+ bool isCurrentDefined = isTargetDescriptorDefined;
+ const PropertyDescriptor& current = targetDescriptor;
+ bool throwException = false;
+ bool isCompatibleDescriptor = validateAndApplyPropertyDescriptor(exec, nullptr, propertyName, targetIsExtensible, descriptor, isCurrentDefined, current, throwException);
+ if (!isCompatibleDescriptor) {
+ throwVMTypeError(exec, ASCIILiteral("Proxy's 'defineProperty' trap did not define a property on its target that is compatible with the trap's input descriptor."));
+ return false;
+ }
+ if (settingConfigurableToFalse && targetDescriptor.configurable()) {
+ throwVMTypeError(exec, ASCIILiteral("Proxy's 'defineProperty' trap did not define a non-configurable property on its target even though the input descriptor to the trap said it must do so."));
+ return false;
+ }
+
+ return true;
+}
+
+bool ProxyObject::defineOwnProperty(JSObject* object, ExecState* exec, PropertyName propertyName, const PropertyDescriptor& descriptor, bool shouldThrow)
+{
+ ProxyObject* thisObject = jsCast<ProxyObject*>(object);
+ return thisObject->performDefineOwnProperty(exec, propertyName, descriptor, shouldThrow);
+}
+
void ProxyObject::visitChildren(JSCell* cell, SlotVisitor& visitor)
{
ProxyObject* thisObject = jsCast<ProxyObject*>(cell);
Added: trunk/Source/_javascript_Core/tests/stress/proxy-define-own-property.js (0 => 197533)
--- trunk/Source/_javascript_Core/tests/stress/proxy-define-own-property.js (rev 0)
+++ trunk/Source/_javascript_Core/tests/stress/proxy-define-own-property.js 2016-03-04 01:07:04 UTC (rev 197533)
@@ -0,0 +1,614 @@
+function assert(b) {
+ if (!b)
+ throw new Error("Bad assertion.");
+}
+
+{
+ let target = {};
+ let handler = {
+ defineProperty: 25
+ };
+
+ let proxy = new Proxy(target, handler);
+ for (let i = 0; i < 500; i++) {
+ let threw = false;
+ try {
+ Object.defineProperty(proxy, "x", {
+ enumerable: true,
+ configurable: true,
+ value: 55
+ });
+ } catch(e) {
+ threw = true;
+ assert(e.toString() === "TypeError: 'defineProperty' property of a Proxy's handler should be callable.");
+ }
+
+ assert(threw);
+ }
+
+}
+
+{
+ let target = {};
+ let error = null;
+ let handler = {
+ get defineProperty() {
+ error = new Error;
+ throw error;
+ }
+ };
+
+ let proxy = new Proxy(target, handler);
+ for (let i = 0; i < 500; i++) {
+ let threw = false;
+ try {
+ Object.defineProperty(proxy, "x", {
+ enumerable: true,
+ configurable: true,
+ value: 55
+ });
+ } catch(e) {
+ threw = true;
+ assert(e === error);
+ }
+
+ assert(threw);
+ }
+}
+
+{
+ let target = {};
+ let error = null;
+ let handler = {
+ defineProperty: function() {
+ error = new Error;
+ throw error;
+ }
+ };
+
+ let proxy = new Proxy(target, handler);
+ for (let i = 0; i < 500; i++) {
+ let threw = false;
+ try {
+ Object.defineProperty(proxy, "x", {
+ enumerable: true,
+ configurable: true,
+ value: 55
+ });
+ } catch(e) {
+ threw = true;
+ assert(e === error);
+ }
+
+ assert(threw);
+ }
+}
+
+{
+ let target = {};
+ let handler = {
+ defineProperty: null
+ };
+
+ let proxy = new Proxy(target, handler);
+ for (let i = 0; i < 500; i++) {
+ Object.defineProperty(proxy, "x", {
+ enumerable: true,
+ configurable: true,
+ value: i
+ });
+
+ for (let obj of [target, proxy]) {
+ let pDesc = Object.getOwnPropertyDescriptor(obj, "x");
+ assert(pDesc.value === i);
+ assert(pDesc.configurable === true);
+ assert(pDesc.enumerable === true);
+ }
+ }
+}
+
+{
+ let target = {};
+ let handler = {
+ defineProperty: undefined
+ };
+
+ let proxy = new Proxy(target, handler);
+ for (let i = 0; i < 500; i++) {
+ let result = Reflect.defineProperty(proxy, "x", {
+ enumerable: true,
+ configurable: true,
+ value: i
+ });
+ assert(result);
+
+ for (let obj of [target, proxy]) {
+ let pDesc = Object.getOwnPropertyDescriptor(obj, "x");
+ assert(pDesc.value === i);
+ assert(pDesc.configurable === true);
+ assert(pDesc.enumerable === true);
+ }
+ }
+}
+
+{
+ let target = {};
+ let called = false;
+ let handler = {
+ defineProperty: function(theTarget, propName, descriptor) {
+ called = true;
+ return Reflect.defineProperty(theTarget, propName, descriptor);
+ }
+ };
+
+ let proxy = new Proxy(target, handler);
+ for (let i = 0; i < 500; i++) {
+ let result = Reflect.defineProperty(proxy, "x", {
+ enumerable: true,
+ configurable: true,
+ value: i
+ });
+ assert(result);
+ assert(called);
+ called = false;
+
+ for (let obj of [target, proxy]) {
+ let pDesc = Object.getOwnPropertyDescriptor(obj, "x");
+ assert(pDesc.value === i);
+ assert(pDesc.configurable === true);
+ assert(pDesc.enumerable === true);
+ }
+ }
+}
+
+{
+ let target = {};
+ let called = false;
+ let handler = {
+ defineProperty: function(theTarget, propName, descriptor) {
+ called = true;
+ Reflect.defineProperty(theTarget, propName, descriptor);
+ return false;
+ }
+ };
+
+ let proxy = new Proxy(target, handler);
+ for (let i = 0; i < 500; i++) {
+ let result = Reflect.defineProperty(proxy, "x", {
+ enumerable: true,
+ configurable: true,
+ value: i
+ });
+ assert(!result);
+ assert(called);
+ called = false;
+
+ for (let obj of [target, proxy]) {
+ let pDesc = Object.getOwnPropertyDescriptor(obj, "x");
+ assert(pDesc.value === i);
+ assert(pDesc.configurable === true);
+ assert(pDesc.enumerable === true);
+ }
+ }
+}
+
+{
+ let target = {};
+ let called = false;
+ let handler = {
+ defineProperty: function(theTarget, propName, descriptor) {
+ called = true;
+ return true;
+ }
+ };
+
+ let proxy = new Proxy(target, handler);
+ for (let i = 0; i < 500; i++) {
+ let threw = false;
+ try {
+ Reflect.defineProperty(proxy, "x", {
+ enumerable: true,
+ configurable: false,
+ value: i
+ });
+ } catch(e) {
+ threw = true;
+ assert(e.toString() === "TypeError: Proxy's 'defineProperty' trap returned true for a non-configurable field even though getOwnPropertyDescriptor of the Proxy's target returned undefined.");
+ }
+ assert(called);
+ assert(threw);
+ called = false;
+ }
+}
+
+{
+ let target = {};
+ let called = false;
+ Object.preventExtensions(target);
+ let handler = {
+ defineProperty: function(theTarget, propName, descriptor) {
+ called = true;
+ return true;
+ }
+ };
+
+ let proxy = new Proxy(target, handler);
+ for (let i = 0; i < 500; i++) {
+ let threw = false;
+ try {
+ Reflect.defineProperty(proxy, "x", {
+ enumerable: true,
+ configurable: true,
+ value: i
+ });
+ } catch(e) {
+ threw = true;
+ assert(e.toString() === "TypeError: Proxy's 'defineProperty' trap returned true even though getOwnPropertyDescriptor of the Proxy's target returned undefined and the target is non-extensible.");
+ }
+ assert(called);
+ assert(threw);
+ called = false;
+ }
+}
+
+{
+ let target = {};
+ let called = false;
+ let handler = {
+ defineProperty: function(theTarget, propName, descriptor) {
+ assert(propName === "x");
+ assert(descriptor.configurable === false);
+ called = true;
+ return Reflect.defineProperty(theTarget, "x", {
+ enumerable: true,
+ configurable: true,
+ value: descriptor.value
+ });
+ }
+ };
+
+ let proxy = new Proxy(target, handler);
+ for (let i = 0; i < 500; i++) {
+ try {
+ Reflect.defineProperty(proxy, "x", {
+ enumerable: true,
+ configurable: false,
+ value: i
+ });
+ } catch(e) {
+ threw = true;
+ assert(e.toString() === "TypeError: Proxy's 'defineProperty' trap did not define a non-configurable property on its target even though the input descriptor to the trap said it must do so.");
+ }
+ assert(called);
+ assert(threw);
+ called = false;
+
+ for (let obj of [target, proxy]) {
+ let pDesc = Object.getOwnPropertyDescriptor(obj, "x");
+ assert(pDesc.value === i);
+ assert(pDesc.configurable === true);
+ assert(pDesc.enumerable === true);
+ }
+ }
+}
+
+{
+ let target = {};
+ let called = false;
+ Object.defineProperty(target, "x", {
+ configurable: false,
+ writable: true,
+ value: 55
+ });
+ let handler = {
+ defineProperty: function(theTarget, propName, descriptor) {
+ assert(propName === "x");
+ assert(descriptor.configurable === true);
+ called = true;
+ Reflect.defineProperty(theTarget, "x", {
+ enumerable: true,
+ configurable: true,
+ value: descriptor.value
+ });
+ return true;
+ }
+ };
+
+ let proxy = new Proxy(target, handler);
+ for (let i = 0; i < 500; i++) {
+ let threw = false;
+ try {
+ Reflect.defineProperty(proxy, "x", {
+ configurable: true,
+ writable: false,
+ value: 45
+ });
+ } catch(e) {
+ threw = true;
+ assert(e.toString() === "TypeError: Proxy's 'defineProperty' trap did not define a property on its target that is compatible with the trap's input descriptor.");
+ }
+ assert(called);
+ assert(threw);
+ called = false;
+
+ for (let obj of [target, proxy]) {
+ let pDesc = Object.getOwnPropertyDescriptor(obj, "x");
+ assert(pDesc.value === 55);
+ assert(pDesc.configurable === false);
+ }
+ }
+}
+
+{
+ let target = {};
+ let called = false;
+ Object.defineProperty(target, "x", {
+ configurable: false,
+ writable: true,
+ value: 55
+ });
+ let handler = {
+ defineProperty: function(theTarget, propName, descriptor) {
+ assert(propName === "x");
+ assert(descriptor.configurable === true);
+ called = true;
+ Reflect.defineProperty(theTarget, "x", descriptor);
+ return true;
+ }
+ };
+
+ let proxy = new Proxy(target, handler);
+ for (let i = 0; i < 500; i++) {
+ let threw = false;
+ try {
+ Reflect.defineProperty(proxy, "x", {
+ configurable: true,
+ set:function(){},
+ get:function(){}
+ });
+ } catch(e) {
+ threw = true;
+ assert(e.toString() === "TypeError: Proxy's 'defineProperty' trap did not define a property on its target that is compatible with the trap's input descriptor.");
+ }
+ assert(called);
+ assert(threw);
+ called = false;
+
+ for (let obj of [target, proxy]) {
+ let pDesc = Object.getOwnPropertyDescriptor(obj, "x");
+ assert(pDesc.value === 55);
+ assert(pDesc.configurable === false);
+ }
+ }
+}
+
+{
+ let target = {};
+ let called = false;
+ let setter = function(){};
+ let getter = function(){};
+ Object.defineProperty(target, "x", {
+ configurable: false,
+ get: getter,
+ set: setter
+ });
+ let handler = {
+ defineProperty: function(theTarget, propName, descriptor) {
+ assert(propName === "x");
+ assert(descriptor.configurable === true);
+ called = true;
+ Reflect.defineProperty(theTarget, "x", descriptor);
+ return true;
+ }
+ };
+
+ let proxy = new Proxy(target, handler);
+ for (let i = 0; i < 500; i++) {
+ let threw = false;
+ try {
+ Reflect.defineProperty(proxy, "x", {
+ configurable: true,
+ value: 45
+ });
+ } catch(e) {
+ threw = true;
+ assert(e.toString() === "TypeError: Proxy's 'defineProperty' trap did not define a property on its target that is compatible with the trap's input descriptor.");
+ }
+ assert(called);
+ assert(threw);
+ called = false;
+
+ for (let obj of [target, proxy]) {
+ let pDesc = Object.getOwnPropertyDescriptor(obj, "x");
+ assert(pDesc.value === undefined);
+ assert(pDesc.configurable === false);
+ assert(pDesc.get === getter);
+ assert(pDesc.set === setter);
+ }
+ }
+}
+
+{
+ let target = {};
+ let called = false;
+ let setter = function(){};
+ let getter = function(){};
+ Object.defineProperty(target, "x", {
+ configurable: false,
+ get: getter,
+ set: setter
+ });
+ let handler = {
+ defineProperty: function(theTarget, propName, descriptor) {
+ assert(propName === "x");
+ assert(descriptor.configurable === true);
+ called = true;
+ return Reflect.defineProperty(theTarget, "x", descriptor);
+ }
+ };
+
+ let proxy = new Proxy(target, handler);
+ for (let i = 0; i < 500; i++) {
+ let result = Reflect.defineProperty(proxy, "x", {
+ configurable: true,
+ value: 45
+ });
+ assert(!result);
+ assert(called);
+ called = false;
+
+ for (let obj of [target, proxy]) {
+ let pDesc = Object.getOwnPropertyDescriptor(obj, "x");
+ assert(pDesc.value === undefined);
+ assert(pDesc.configurable === false);
+ assert(pDesc.get === getter);
+ assert(pDesc.set === setter);
+ }
+ }
+}
+
+{
+ let error = false;
+ let target = new Proxy({}, {
+ getOwnPropertyDescriptor: function() {
+ error = new Error;
+ throw error;
+ }
+ });
+
+ let handler = {
+ defineProperty: function(theTarget, propName, descriptor) {
+ return Reflect.defineProperty(theTarget, propName, descriptor);
+ }
+ };
+
+ let proxy = new Proxy(target, handler);
+ for (let i = 0; i < 500; i++) {
+ let threw = false;
+ try {
+ Reflect.defineProperty(proxy, "x", {
+ configurable: true,
+ value: 45
+ });
+ } catch(e) {
+ threw = true;
+ assert(e === error);
+ }
+ assert(threw);
+ }
+}
+
+{
+ let target = {};
+ Reflect.defineProperty(target, "x", {
+ writable: true,
+ configurable: false,
+ value: 55
+ });
+
+ let handler = {
+ defineProperty: function(theTarget, propName, descriptor) {
+ assert(theTarget === target);
+ return Reflect.defineProperty(theTarget, propName, descriptor);
+ }
+ };
+
+ let proxy = new Proxy(target, handler);
+ for (let i = 0; i < 500; i++) {
+ let result = Reflect.defineProperty(proxy, "x", {
+ configurable: false,
+ value: 55
+ });
+ assert(result);
+ assert(target.x === 55);
+ delete target.x;
+ assert(target.x === 55);
+ }
+}
+
+{
+ let target = {};
+ Reflect.defineProperty(target, "x", {
+ writable: false,
+ configurable: false,
+ value: 55
+ });
+
+ let handler = {
+ defineProperty: function(theTarget, propName, descriptor) {
+ assert(theTarget === target);
+ return Reflect.defineProperty(theTarget, propName, descriptor);
+ }
+ };
+
+ let proxy = new Proxy(target, handler);
+ for (let i = 0; i < 500; i++) {
+ let result = Reflect.defineProperty(proxy, "x", {
+ writable: false,
+ configurable: false,
+ value: 55
+ });
+ assert(result);
+ assert(target.x === 55);
+ delete target.x;
+ assert(target.x === 55);
+ }
+}
+
+{
+ let target = {};
+ Reflect.defineProperty(target, "x", {
+ writable: false,
+ configurable: false,
+ value: 55
+ });
+
+ let handler = {
+ defineProperty: function(theTarget, propName, descriptor) {
+ assert(theTarget === target);
+ return Reflect.defineProperty(theTarget, propName, descriptor);
+ }
+ };
+
+ let proxy = new Proxy(target, handler);
+ for (let i = 0; i < 500; i++) {
+ let result = Reflect.defineProperty(proxy, "x", {
+ writable: false,
+ configurable: false,
+ value: "not 55"
+ });
+ assert(!result);
+ assert(target.x === 55);
+ delete target.x;
+ assert(target.x === 55);
+ }
+}
+
+{
+ let target = {};
+ Reflect.defineProperty(target, "x", {
+ writable: false,
+ configurable: false,
+ value: 55
+ });
+
+ let handler = {
+ defineProperty: function(theTarget, propName, descriptor) {
+ assert(theTarget === target);
+ return Reflect.defineProperty(theTarget, propName, descriptor);
+ }
+ };
+
+ let proxy = new Proxy(target, handler);
+ for (let i = 0; i < 500; i++) {
+ let result = Reflect.defineProperty(proxy, "x", {
+ writable: true,
+ configurable: false,
+ value: "whatever value goes here."
+ });
+ assert(!result);
+ assert(target.x === 55);
+ delete target.x;
+ assert(target.x === 55);
+ }
+}