From 5820adf276e46ab75e84438ffbe3a017efee44fc Mon Sep 17 00:00:00 2001 From: neis Date: Fri, 30 Oct 2015 04:48:19 -0700 Subject: [PATCH] [es6] Partially implement Reflect.set. Proxies are not properly supported yet. R=rossberg BUG=v8:3931 LOG=n Review URL: https://codereview.chromium.org/1415883007 Cr-Commit-Position: refs/heads/master@{#31685} --- src/bootstrapper.cc | 2 + src/builtins.cc | 29 +++++++ src/builtins.h | 1 + test/mjsunit/harmony/reflect.js | 147 +++++++++++++++++++++++++++++++- 4 files changed, 176 insertions(+), 3 deletions(-) diff --git a/src/bootstrapper.cc b/src/bootstrapper.cc index 7b35506134..6d887bc692 100644 --- a/src/bootstrapper.cc +++ b/src/bootstrapper.cc @@ -2207,6 +2207,8 @@ void Genesis::InitializeGlobal_harmony_reflect() { Builtins::kReflectIsExtensible, 1, true); SimpleInstallFunction(reflect, "preventExtensions", Builtins::kReflectPreventExtensions, 1, true); + SimpleInstallFunction(reflect, "set", + Builtins::kReflectSet, 3, false); SimpleInstallFunction(reflect, "setPrototypeOf", Builtins::kReflectSetPrototypeOf, 2, true); } diff --git a/src/builtins.cc b/src/builtins.cc index e1b5a591cc..7a2e980b21 100644 --- a/src/builtins.cc +++ b/src/builtins.cc @@ -1652,6 +1652,35 @@ BUILTIN(ReflectPreventExtensions) { } +// ES6 section 26.1.13 Reflect.set +BUILTIN(ReflectSet) { + HandleScope scope(isolate); + Handle undef = isolate->factory()->undefined_value(); + Handle target = args.length() > 1 ? args.at(1) : undef; + Handle key = args.length() > 2 ? args.at(2) : undef; + Handle value = args.length() > 3 ? args.at(3) : undef; + Handle receiver = args.length() > 4 ? args.at(4) : target; + + if (!target->IsJSReceiver()) { + THROW_NEW_ERROR_RETURN_FAILURE( + isolate, NewTypeError(MessageTemplate::kCalledOnNonObject, + isolate->factory()->NewStringFromAsciiChecked( + "Reflect.set"))); + } + + Handle name; + ASSIGN_RETURN_FAILURE_ON_EXCEPTION(isolate, name, + Object::ToName(isolate, key)); + + LookupIterator it = LookupIterator::PropertyOrElement( + isolate, receiver, name, Handle::cast(target)); + Maybe result = Object::SetSuperProperty( + &it, value, SLOPPY, Object::MAY_BE_STORE_FROM_KEYED); + MAYBE_RETURN(result, isolate->heap()->exception()); + return *isolate->factory()->ToBoolean(result.FromJust()); +} + + // ES6 section 26.1.14 Reflect.setPrototypeOf BUILTIN(ReflectSetPrototypeOf) { HandleScope scope(isolate); diff --git a/src/builtins.h b/src/builtins.h index cd6585396e..dfca7e0ffe 100644 --- a/src/builtins.h +++ b/src/builtins.h @@ -67,6 +67,7 @@ enum BuiltinExtraArguments { V(ReflectHas, NO_EXTRA_ARGUMENTS) \ V(ReflectIsExtensible, NO_EXTRA_ARGUMENTS) \ V(ReflectPreventExtensions, NO_EXTRA_ARGUMENTS) \ + V(ReflectSet, NO_EXTRA_ARGUMENTS) \ V(ReflectSetPrototypeOf, NO_EXTRA_ARGUMENTS) \ \ V(SymbolConstructor, NO_EXTRA_ARGUMENTS) \ diff --git a/test/mjsunit/harmony/reflect.js b/test/mjsunit/harmony/reflect.js index f94e98b221..e61b65e143 100644 --- a/test/mjsunit/harmony/reflect.js +++ b/test/mjsunit/harmony/reflect.js @@ -52,11 +52,16 @@ function prepare(target) { target[4] = 42; target[sym] = "foo"; target["noconf"] = 43; - Object.defineProperty(target, "noconf", {configurable: false}); + Object.defineProperty(target, "noconf", + { configurable: false }); + Object.defineProperty(target, "nowrite", + { writable: false, configurable: true, value: 44 }); Object.defineProperty(target, "getter", - { get: function () {return this.bla}, configurable: true }); + { get: function () {return this.bla}, configurable: true }); Object.defineProperty(target, "setter", - { set: function () {}, configurable: true }); + { set: function (x) {this.gaga = x}, configurable: true }); + Object.defineProperty(target, "setter2", + { set: function (x) {}, configurable: true }); } @@ -129,6 +134,142 @@ function prepare(target) { +//////////////////////////////////////////////////////////////////////////////// +// Reflect.set + + +(function testReflectSetArity() { + assertEquals(3, Reflect.set.length); +})(); + + +(function testReflectSetOnNonObject() { + assertThrows(function() { Reflect.set(); }, TypeError); + assertThrows(function() { Reflect.set(42, "bla"); }, TypeError); + assertThrows(function() { Reflect.set(null, "bla"); }, TypeError); +})(); + + +(function testReflectSetKeyConversion() { + var target = {}; + var a = { [Symbol.toPrimitive]: function() { return "bla" } }; + var b = { [Symbol.toPrimitive]: function() { throw "gaga" } }; + assertTrue(Reflect.set(target, a, 42)); + assertEquals(42, target.bla); + assertThrows(function() { Reflect.set(target, b, 42); }, "gaga"); +})(); + + +(function testReflectSetOnObject() { + var receiver = {bla: false}; + var value = 34234; + for (let target of objects) { + prepare(target); + assertTrue(Reflect.set(target, "bla", value)); + assertEquals(value, target.bla); + + prepare(target); + assertTrue(Reflect.set(target, "bla", value, target)); + assertEquals(value, target.bla); + + prepare(target); + assertTrue(Reflect.set(target, "bla", value, receiver)); + assertEquals(true, target.bla); + assertEquals(value, receiver.bla); + receiver.bla = false; + + prepare(target); + assertTrue(Reflect.set(target, 4, value)); + assertEquals(value, target[4]); + + prepare(target); + assertTrue(Reflect.set(target, 4, value, target)); + assertEquals(value, target[4]); + + prepare(target); + assertTrue(Reflect.set(target, 4, value, receiver)); + assertEquals(42, target[4]); + assertEquals(value, receiver[4]); + delete receiver[4]; + + prepare(target); + assertTrue(Reflect.set(target, sym, value)); + assertEquals(value, target[sym]); + + prepare(target); + assertTrue(Reflect.set(target, sym, value, target)); + assertEquals(value, target[sym]); + + prepare(target); + assertTrue(Reflect.set(target, sym, value, receiver)); + assertEquals("foo", target[sym]); + assertEquals(value, receiver[sym]); + delete receiver[sym]; + + prepare(target); + assertTrue(Reflect.set(target, "noconf", value)); + assertEquals(value, target.noconf); + + prepare(target); + assertTrue(Reflect.set(target, "noconf", value, target)); + assertEquals(value, target.noconf); + + prepare(target); + assertTrue(Reflect.set(target, "noconf", value, receiver)); + assertEquals(43, target.noconf); + assertEquals(value, receiver.noconf); + delete receiver.noconf; + + assertTrue(Reflect.set(target, "setter", value)); + assertEquals(value, target.gaga) + delete target.gaga; + + assertTrue(Reflect.set(target, "setter", value, target)); + assertEquals(value, target.gaga) + delete target.gaga; + + assertTrue(Reflect.set(target, "setter", value, receiver)); + assertFalse("gaga" in target); + assertEquals(value, receiver.gaga); + delete receiver.gaga; + + assertFalse(Reflect.set(target, "nowrite", value)); + assertEquals(44, target.nowrite); + + assertFalse(Reflect.set(target, "nowrite", value, target)); + assertEquals(44, target.nowrite); + + assertFalse(Reflect.set(target, "nowrite", value, receiver)); + assertEquals(44, target.nowrite); + assertFalse("nowrite" in receiver); + + // Data vs Non-Writable + // TODO(neis): This must return false but currently doesn't. + // assertFalse(Reflect.set({}, "nowrite", value, target)); + + // Data vs Accessor + // TODO(neis): These must return false but currently don't. + // assertFalse(Reflect.set(target, "unknown", value, {set bla(x) {}})); + // assertFalse(Reflect.set(target, "unknown", value, {get bla() {}})); + // assertFalse(Reflect.set(target, "bla", value, {set bla(x) {}})); + // assertFalse(Reflect.set(target, "bla", value, {get bla() {}})); + + // Accessor vs Data + assertTrue(Reflect.set({set bla(x) {}}), "bla", value, target); + assertFalse(Reflect.set({get bla() {}}, "bla", value, target)); + + // Data vs Non-Object + assertFalse(Reflect.set({}, "bla", value, null)); + assertFalse(Reflect.set({bla: 42}, "bla", value, null)); + + // Accessor vs Non-Object + assertTrue(Reflect.set(target, "setter2", value, null)); + assertFalse(Reflect.set(target, "getter", value, null)); + } +})(); + + + //////////////////////////////////////////////////////////////////////////////// // Reflect.has