ReflectObject.cpp
1 /* 2 * Copyright (C) 2015-2017 Apple Inc. All Rights Reserved. 3 * 4 * Redistribution and use in source and binary forms, with or without 5 * modification, are permitted provided that the following conditions 6 * are met: 7 * 1. Redistributions of source code must retain the above copyright 8 * notice, this list of conditions and the following disclaimer. 9 * 2. Redistributions in binary form must reproduce the above copyright 10 * notice, this list of conditions and the following disclaimer in the 11 * documentation and/or other materials provided with the distribution. 12 * 13 * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY 14 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 15 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 16 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR 17 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, 18 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, 19 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR 20 * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY 21 * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 22 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 23 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 24 */ 25 26 #include "config.h" 27 #include "ReflectObject.h" 28 29 #include "JSCInlines.h" 30 #include "ObjectConstructor.h" 31 32 namespace JSC { 33 34 static JSC_DECLARE_HOST_FUNCTION(reflectObjectConstruct); 35 static JSC_DECLARE_HOST_FUNCTION(reflectObjectDefineProperty); 36 static JSC_DECLARE_HOST_FUNCTION(reflectObjectGet); 37 static JSC_DECLARE_HOST_FUNCTION(reflectObjectGetOwnPropertyDescriptor); 38 static JSC_DECLARE_HOST_FUNCTION(reflectObjectGetPrototypeOf); 39 static JSC_DECLARE_HOST_FUNCTION(reflectObjectIsExtensible); 40 static JSC_DECLARE_HOST_FUNCTION(reflectObjectOwnKeys); 41 static JSC_DECLARE_HOST_FUNCTION(reflectObjectPreventExtensions); 42 static JSC_DECLARE_HOST_FUNCTION(reflectObjectSet); 43 static JSC_DECLARE_HOST_FUNCTION(reflectObjectSetPrototypeOf); 44 45 } 46 47 #include "ReflectObject.lut.h" 48 49 namespace JSC { 50 51 STATIC_ASSERT_IS_TRIVIALLY_DESTRUCTIBLE(ReflectObject); 52 53 const ClassInfo ReflectObject::s_info = { "Reflect", &Base::s_info, &reflectObjectTable, nullptr, CREATE_METHOD_TABLE(ReflectObject) }; 54 55 /* Source for ReflectObject.lut.h 56 @begin reflectObjectTable 57 apply JSBuiltin DontEnum|Function 3 58 construct reflectObjectConstruct DontEnum|Function 2 59 defineProperty reflectObjectDefineProperty DontEnum|Function 3 60 deleteProperty JSBuiltin DontEnum|Function 2 61 get reflectObjectGet DontEnum|Function 2 62 getOwnPropertyDescriptor reflectObjectGetOwnPropertyDescriptor DontEnum|Function 2 63 getPrototypeOf reflectObjectGetPrototypeOf DontEnum|Function 1 ReflectGetPrototypeOfIntrinsic 64 has JSBuiltin DontEnum|Function 2 65 isExtensible reflectObjectIsExtensible DontEnum|Function 1 66 ownKeys reflectObjectOwnKeys DontEnum|Function 1 67 preventExtensions reflectObjectPreventExtensions DontEnum|Function 1 68 set reflectObjectSet DontEnum|Function 3 69 setPrototypeOf reflectObjectSetPrototypeOf DontEnum|Function 2 70 @end 71 */ 72 73 ReflectObject::ReflectObject(VM& vm, Structure* structure) 74 : JSNonFinalObject(vm, structure) 75 { 76 } 77 78 void ReflectObject::finishCreation(VM& vm, JSGlobalObject*) 79 { 80 Base::finishCreation(vm); 81 ASSERT(inherits(vm, info())); 82 JSC_TO_STRING_TAG_WITHOUT_TRANSITION(); 83 } 84 85 // ------------------------------ Functions -------------------------------- 86 87 // https://tc39.github.io/ecma262/#sec-reflect.construct 88 JSC_DEFINE_HOST_FUNCTION(reflectObjectConstruct, (JSGlobalObject* globalObject, CallFrame* callFrame)) 89 { 90 VM& vm = globalObject->vm(); 91 auto scope = DECLARE_THROW_SCOPE(vm); 92 93 JSValue target = callFrame->argument(0); 94 if (!target.isObject()) 95 return JSValue::encode(throwTypeError(globalObject, scope, "Reflect.construct requires the first argument be a constructor"_s)); 96 97 auto constructData = getConstructData(vm, target); 98 if (constructData.type == CallData::Type::None) 99 return JSValue::encode(throwTypeError(globalObject, scope, "Reflect.construct requires the first argument be a constructor"_s)); 100 101 JSValue newTarget = target; 102 if (callFrame->argumentCount() >= 3) { 103 newTarget = callFrame->argument(2); 104 if (!newTarget.isConstructor(vm)) 105 return JSValue::encode(throwTypeError(globalObject, scope, "Reflect.construct requires the third argument be a constructor if present"_s)); 106 } 107 108 MarkedArgumentBuffer arguments; 109 JSObject* argumentsObject = jsDynamicCast<JSObject*>(vm, callFrame->argument(1)); 110 if (!argumentsObject) 111 return JSValue::encode(throwTypeError(globalObject, scope, "Reflect.construct requires the second argument be an object"_s)); 112 113 createListFromArrayLike(globalObject, argumentsObject, RuntimeTypeMaskAllTypes, "This error must not be raised"_s, "This error must not be raised"_s, [&] (JSValue value, RuntimeType) -> bool { 114 arguments.append(value); 115 return false; 116 }); 117 RETURN_IF_EXCEPTION(scope, (arguments.overflowCheckNotNeeded(), encodedJSValue())); 118 if (UNLIKELY(arguments.hasOverflowed())) { 119 throwOutOfMemoryError(globalObject, scope); 120 return encodedJSValue(); 121 } 122 123 RELEASE_AND_RETURN(scope, JSValue::encode(construct(globalObject, target, constructData, arguments, newTarget))); 124 } 125 126 // https://tc39.github.io/ecma262/#sec-reflect.defineproperty 127 JSC_DEFINE_HOST_FUNCTION(reflectObjectDefineProperty, (JSGlobalObject* globalObject, CallFrame* callFrame)) 128 { 129 VM& vm = globalObject->vm(); 130 auto scope = DECLARE_THROW_SCOPE(vm); 131 132 JSValue target = callFrame->argument(0); 133 if (!target.isObject()) 134 return JSValue::encode(throwTypeError(globalObject, scope, "Reflect.defineProperty requires the first argument be an object"_s)); 135 auto propertyName = callFrame->argument(1).toPropertyKey(globalObject); 136 RETURN_IF_EXCEPTION(scope, encodedJSValue()); 137 138 PropertyDescriptor descriptor; 139 bool success = toPropertyDescriptor(globalObject, callFrame->argument(2), descriptor); 140 EXCEPTION_ASSERT(!scope.exception() == success); 141 if (UNLIKELY(!success)) 142 return encodedJSValue(); 143 ASSERT((descriptor.attributes() & PropertyAttribute::Accessor) || (!descriptor.isAccessorDescriptor())); 144 scope.assertNoException(); 145 146 // Reflect.defineProperty should not throw an error when the defineOwnProperty operation fails. 147 bool shouldThrow = false; 148 JSObject* targetObject = asObject(target); 149 RELEASE_AND_RETURN(scope, JSValue::encode(jsBoolean(targetObject->methodTable(vm)->defineOwnProperty(targetObject, globalObject, propertyName, descriptor, shouldThrow)))); 150 } 151 152 // https://tc39.github.io/ecma262/#sec-reflect.get 153 JSC_DEFINE_HOST_FUNCTION(reflectObjectGet, (JSGlobalObject* globalObject, CallFrame* callFrame)) 154 { 155 VM& vm = globalObject->vm(); 156 auto scope = DECLARE_THROW_SCOPE(vm); 157 158 JSValue target = callFrame->argument(0); 159 if (!target.isObject()) 160 return JSValue::encode(throwTypeError(globalObject, scope, "Reflect.get requires the first argument be an object"_s)); 161 162 const Identifier propertyName = callFrame->argument(1).toPropertyKey(globalObject); 163 RETURN_IF_EXCEPTION(scope, encodedJSValue()); 164 165 JSValue receiver = target; 166 if (callFrame->argumentCount() >= 3) 167 receiver = callFrame->argument(2); 168 169 PropertySlot slot(receiver, PropertySlot::InternalMethodType::Get); 170 RELEASE_AND_RETURN(scope, JSValue::encode(target.get(globalObject, propertyName, slot))); 171 } 172 173 // https://tc39.github.io/ecma262/#sec-reflect.getownpropertydescriptor 174 JSC_DEFINE_HOST_FUNCTION(reflectObjectGetOwnPropertyDescriptor, (JSGlobalObject* globalObject, CallFrame* callFrame)) 175 { 176 VM& vm = globalObject->vm(); 177 auto scope = DECLARE_THROW_SCOPE(vm); 178 179 JSValue target = callFrame->argument(0); 180 if (!target.isObject()) 181 return JSValue::encode(throwTypeError(globalObject, scope, "Reflect.getOwnPropertyDescriptor requires the first argument be an object"_s)); 182 183 auto key = callFrame->argument(1).toPropertyKey(globalObject); 184 RETURN_IF_EXCEPTION(scope, encodedJSValue()); 185 186 RELEASE_AND_RETURN(scope, JSValue::encode(objectConstructorGetOwnPropertyDescriptor(globalObject, asObject(target), key))); 187 } 188 189 // https://tc39.github.io/ecma262/#sec-reflect.getprototypeof 190 JSC_DEFINE_HOST_FUNCTION(reflectObjectGetPrototypeOf, (JSGlobalObject* globalObject, CallFrame* callFrame)) 191 { 192 VM& vm = globalObject->vm(); 193 auto scope = DECLARE_THROW_SCOPE(vm); 194 195 JSValue target = callFrame->argument(0); 196 if (!target.isObject()) 197 return JSValue::encode(throwTypeError(globalObject, scope, "Reflect.getPrototypeOf requires the first argument be an object"_s)); 198 RELEASE_AND_RETURN(scope, JSValue::encode(asObject(target)->getPrototype(vm, globalObject))); 199 } 200 201 // https://tc39.github.io/ecma262/#sec-reflect.isextensible 202 JSC_DEFINE_HOST_FUNCTION(reflectObjectIsExtensible, (JSGlobalObject* globalObject, CallFrame* callFrame)) 203 { 204 VM& vm = globalObject->vm(); 205 auto scope = DECLARE_THROW_SCOPE(vm); 206 207 JSValue target = callFrame->argument(0); 208 if (!target.isObject()) 209 return JSValue::encode(throwTypeError(globalObject, scope, "Reflect.isExtensible requires the first argument be an object"_s)); 210 211 bool isExtensible = asObject(target)->isExtensible(globalObject); 212 RETURN_IF_EXCEPTION(scope, encodedJSValue()); 213 return JSValue::encode(jsBoolean(isExtensible)); 214 } 215 216 // https://tc39.github.io/ecma262/#sec-reflect.ownkeys 217 JSC_DEFINE_HOST_FUNCTION(reflectObjectOwnKeys, (JSGlobalObject* globalObject, CallFrame* callFrame)) 218 { 219 VM& vm = globalObject->vm(); 220 auto scope = DECLARE_THROW_SCOPE(vm); 221 222 JSValue target = callFrame->argument(0); 223 if (!target.isObject()) 224 return JSValue::encode(throwTypeError(globalObject, scope, "Reflect.ownKeys requires the first argument be an object"_s)); 225 RELEASE_AND_RETURN(scope, JSValue::encode(ownPropertyKeys(globalObject, jsCast<JSObject*>(target), PropertyNameMode::StringsAndSymbols, DontEnumPropertiesMode::Include, WTF::nullopt))); 226 } 227 228 // https://tc39.github.io/ecma262/#sec-reflect.preventextensions 229 JSC_DEFINE_HOST_FUNCTION(reflectObjectPreventExtensions, (JSGlobalObject* globalObject, CallFrame* callFrame)) 230 { 231 VM& vm = globalObject->vm(); 232 auto scope = DECLARE_THROW_SCOPE(vm); 233 234 JSValue target = callFrame->argument(0); 235 if (!target.isObject()) 236 return JSValue::encode(throwTypeError(globalObject, scope, "Reflect.preventExtensions requires the first argument be an object"_s)); 237 JSObject* object = asObject(target); 238 bool result = object->methodTable(vm)->preventExtensions(object, globalObject); 239 RETURN_IF_EXCEPTION(scope, encodedJSValue()); 240 return JSValue::encode(jsBoolean(result)); 241 } 242 243 // https://tc39.github.io/ecma262/#sec-reflect.set 244 JSC_DEFINE_HOST_FUNCTION(reflectObjectSet, (JSGlobalObject* globalObject, CallFrame* callFrame)) 245 { 246 VM& vm = globalObject->vm(); 247 auto scope = DECLARE_THROW_SCOPE(vm); 248 249 JSValue target = callFrame->argument(0); 250 if (!target.isObject()) 251 return JSValue::encode(throwTypeError(globalObject, scope, "Reflect.set requires the first argument be an object"_s)); 252 JSObject* targetObject = asObject(target); 253 254 auto propertyName = callFrame->argument(1).toPropertyKey(globalObject); 255 RETURN_IF_EXCEPTION(scope, encodedJSValue()); 256 257 JSValue receiver = target; 258 if (callFrame->argumentCount() >= 4) 259 receiver = callFrame->argument(3); 260 261 // Do not raise any readonly errors that happen in strict mode. 262 bool shouldThrowIfCantSet = false; 263 PutPropertySlot slot(receiver, shouldThrowIfCantSet); 264 RELEASE_AND_RETURN(scope, JSValue::encode(jsBoolean(targetObject->methodTable(vm)->put(targetObject, globalObject, propertyName, callFrame->argument(2), slot)))); 265 } 266 267 // https://tc39.github.io/ecma262/#sec-reflect.setprototypeof 268 JSC_DEFINE_HOST_FUNCTION(reflectObjectSetPrototypeOf, (JSGlobalObject* globalObject, CallFrame* callFrame)) 269 { 270 VM& vm = globalObject->vm(); 271 auto scope = DECLARE_THROW_SCOPE(vm); 272 273 JSValue target = callFrame->argument(0); 274 if (!target.isObject()) 275 return JSValue::encode(throwTypeError(globalObject, scope, "Reflect.setPrototypeOf requires the first argument be an object"_s)); 276 JSValue proto = callFrame->argument(1); 277 if (!proto.isObject() && !proto.isNull()) 278 return JSValue::encode(throwTypeError(globalObject, scope, "Reflect.setPrototypeOf requires the second argument be either an object or null"_s)); 279 280 JSObject* object = asObject(target); 281 282 bool shouldThrowIfCantSet = false; 283 bool didSetPrototype = object->setPrototype(vm, globalObject, proto, shouldThrowIfCantSet); 284 RETURN_IF_EXCEPTION(scope, encodedJSValue()); 285 return JSValue::encode(jsBoolean(didSetPrototype)); 286 } 287 288 } // namespace JSC