/ runtime / ReflectObject.cpp
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