tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

testNewObject.cpp (7502B)


      1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*-
      2 * vim: set ts=8 sts=2 et sw=2 tw=80:
      3 */
      4 /* This Source Code Form is subject to the terms of the Mozilla Public
      5 * License, v. 2.0. If a copy of the MPL was not distributed with this
      6 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
      7 
      8 #include "js/Array.h"               // JS::GetArrayLength, JS::IsArrayObject
      9 #include "js/CallAndConstruct.h"    // JS::Construct
     10 #include "js/Object.h"              // JS::GetClass
     11 #include "js/PropertyAndElement.h"  // JS_GetElement, JS_SetElement
     12 #include "jsapi-tests/tests.h"
     13 #include "vm/PlainObject.h"  // js::PlainObject::class_
     14 
     15 #include "vm/NativeObject-inl.h"
     16 
     17 using namespace js;
     18 
     19 static bool constructHook(JSContext* cx, unsigned argc, JS::Value* vp) {
     20  JS::CallArgs args = CallArgsFromVp(argc, vp);
     21 
     22  // Check that arguments were passed properly from JS_New.
     23 
     24  JS::RootedObject obj(cx, JS_NewPlainObject(cx));
     25  if (!obj) {
     26    JS_ReportErrorASCII(cx, "test failed, could not construct object");
     27    return false;
     28  }
     29  if (strcmp(JS::GetClass(obj)->name, "Object") != 0) {
     30    JS_ReportErrorASCII(cx, "test failed, wrong class for 'this'");
     31    return false;
     32  }
     33  if (args.length() != 3) {
     34    JS_ReportErrorASCII(cx, "test failed, argc == %d", args.length());
     35    return false;
     36  }
     37  if (!args[0].isInt32() || args[2].toInt32() != 2) {
     38    JS_ReportErrorASCII(cx, "test failed, wrong value in args[2]");
     39    return false;
     40  }
     41  if (!args.isConstructing()) {
     42    JS_ReportErrorASCII(cx, "test failed, not constructing");
     43    return false;
     44  }
     45 
     46  // Perform a side-effect to indicate that this hook was actually called.
     47  JS::RootedValue value(cx, args[0]);
     48  JS::RootedObject callee(cx, &args.callee());
     49  if (!JS_SetElement(cx, callee, 0, value)) {
     50    return false;
     51  }
     52 
     53  args.rval().setObject(*obj);
     54 
     55  // trash the argv, perversely
     56  args[0].setUndefined();
     57  args[1].setUndefined();
     58  args[2].setUndefined();
     59 
     60  return true;
     61 }
     62 
     63 BEGIN_TEST(testNewObject_1) {
     64  static const size_t N = 1000;
     65  JS::RootedValueVector argv(cx);
     66  CHECK(argv.resize(N));
     67 
     68  JS::RootedValue Array(cx);
     69  EVAL("Array", &Array);
     70 
     71  bool isArray;
     72 
     73  // With no arguments.
     74  JS::RootedObject obj(cx);
     75  CHECK(JS::Construct(cx, Array, JS::HandleValueArray::empty(), &obj));
     76  CHECK(JS::IsArrayObject(cx, obj, &isArray));
     77  CHECK(isArray);
     78  uint32_t len;
     79  CHECK(JS::GetArrayLength(cx, obj, &len));
     80  CHECK_EQUAL(len, 0u);
     81 
     82  // With one argument.
     83  argv[0].setInt32(4);
     84  CHECK(JS::Construct(cx, Array, JS::HandleValueArray::subarray(argv, 0, 1),
     85                      &obj));
     86  CHECK(JS::IsArrayObject(cx, obj, &isArray));
     87  CHECK(isArray);
     88  CHECK(JS::GetArrayLength(cx, obj, &len));
     89  CHECK_EQUAL(len, 4u);
     90 
     91  // With N arguments.
     92  for (size_t i = 0; i < N; i++) {
     93    argv[i].setInt32(i);
     94  }
     95  CHECK(JS::Construct(cx, Array, JS::HandleValueArray::subarray(argv, 0, N),
     96                      &obj));
     97  CHECK(JS::IsArrayObject(cx, obj, &isArray));
     98  CHECK(isArray);
     99  CHECK(JS::GetArrayLength(cx, obj, &len));
    100  CHECK_EQUAL(len, N);
    101  JS::RootedValue v(cx);
    102  CHECK(JS_GetElement(cx, obj, N - 1, &v));
    103  CHECK(v.isInt32(N - 1));
    104 
    105  // With JSClass.construct.
    106  static const JSClassOps clsOps = {
    107      nullptr,        // addProperty
    108      nullptr,        // delProperty
    109      nullptr,        // enumerate
    110      nullptr,        // newEnumerate
    111      nullptr,        // resolve
    112      nullptr,        // mayResolve
    113      nullptr,        // finalize
    114      nullptr,        // call
    115      constructHook,  // construct
    116      nullptr,        // trace
    117  };
    118  static const JSClass cls = {
    119      "testNewObject_1",
    120      0,
    121      &clsOps,
    122  };
    123  JS::RootedObject ctor(cx, JS_NewObject(cx, &cls));
    124  CHECK(ctor);
    125  JS::RootedValue ctorVal(cx, JS::ObjectValue(*ctor));
    126  CHECK(JS::Construct(cx, ctorVal, JS::HandleValueArray::subarray(argv, 0, 3),
    127                      &obj));
    128  CHECK(JS_GetElement(cx, ctor, 0, &v));
    129  CHECK(v.isInt32(0));
    130 
    131  return true;
    132 }
    133 END_TEST(testNewObject_1)
    134 
    135 BEGIN_TEST(testNewObject_IsMapObject) {
    136  // Test IsMapObject and IsSetObject
    137 
    138  JS::RootedValue vMap(cx);
    139  EVAL("Map", &vMap);
    140 
    141  bool isMap = false;
    142  bool isSet = false;
    143  JS::RootedObject mapObj(cx);
    144  CHECK(JS::Construct(cx, vMap, JS::HandleValueArray::empty(), &mapObj));
    145  CHECK(JS::IsMapObject(cx, mapObj, &isMap));
    146  CHECK(isMap);
    147  CHECK(JS::IsSetObject(cx, mapObj, &isSet));
    148  CHECK(!isSet);
    149 
    150  JS::RootedValue vSet(cx);
    151  EVAL("Set", &vSet);
    152 
    153  JS::RootedObject setObj(cx);
    154  CHECK(JS::Construct(cx, vSet, JS::HandleValueArray::empty(), &setObj));
    155  CHECK(JS::IsMapObject(cx, setObj, &isMap));
    156  CHECK(!isMap);
    157  CHECK(JS::IsSetObject(cx, setObj, &isSet));
    158  CHECK(isSet);
    159 
    160  return true;
    161 }
    162 END_TEST(testNewObject_IsMapObject)
    163 
    164 static const JSClass Base_class = {
    165    "Base",
    166    JSCLASS_HAS_RESERVED_SLOTS(8),  // flags
    167 };
    168 
    169 BEGIN_TEST(testNewObject_Subclassing) {
    170  JSObject* proto =
    171      JS_InitClass(cx, global, nullptr, nullptr, "Base", Base_constructor, 0,
    172                   nullptr, nullptr, nullptr, nullptr);
    173  if (!proto) {
    174    return false;
    175  }
    176 
    177  CHECK_EQUAL(JS::GetClass(proto), &PlainObject::class_);
    178 
    179  // Calling Base without `new` should fail with a TypeError.
    180  JS::RootedValue expectedError(cx);
    181  EVAL("TypeError", &expectedError);
    182  JS::RootedValue actualError(cx);
    183  EVAL(
    184      "try {\n"
    185      "  Base();\n"
    186      "} catch (e) {\n"
    187      "  e.constructor;\n"
    188      "}\n",
    189      &actualError);
    190  CHECK_SAME(actualError, expectedError);
    191 
    192  // Check prototype chains when a JS class extends a base class that's
    193  // implemented in C++ using JS_NewObjectForConstructor.
    194  EXEC(
    195      "class MyClass extends Base {\n"
    196      "  ok() { return true; }\n"
    197      "}\n"
    198      "let myObj = new MyClass();\n");
    199 
    200  JS::RootedValue result(cx);
    201  EVAL("myObj.ok()", &result);
    202  CHECK_SAME(result, JS::TrueValue());
    203 
    204  EVAL("myObj.__proto__ === MyClass.prototype", &result);
    205  CHECK_SAME(result, JS::TrueValue());
    206  EVAL("myObj.__proto__.__proto__ === Base.prototype", &result);
    207  CHECK_SAME(result, JS::TrueValue());
    208 
    209  EVAL("myObj", &result);
    210  CHECK_EQUAL(JS::GetClass(&result.toObject()), &Base_class);
    211 
    212  // All reserved slots are initialized to undefined.
    213  for (uint32_t i = 0; i < JSCLASS_RESERVED_SLOTS(&Base_class); i++) {
    214    CHECK_SAME(JS::GetReservedSlot(&result.toObject(), i),
    215               JS::UndefinedValue());
    216  }
    217 
    218  return true;
    219 }
    220 
    221 static bool Base_constructor(JSContext* cx, unsigned argc, JS::Value* vp) {
    222  JS::CallArgs args = CallArgsFromVp(argc, vp);
    223  JS::RootedObject obj(cx, JS_NewObjectForConstructor(cx, &Base_class, args));
    224  if (!obj) {
    225    return false;
    226  }
    227  args.rval().setObject(*obj);
    228  return true;
    229 }
    230 
    231 END_TEST(testNewObject_Subclassing)
    232 
    233 static const JSClass TestClass = {
    234    "TestObject",
    235    JSCLASS_HAS_RESERVED_SLOTS(0),
    236 };
    237 
    238 BEGIN_TEST(testNewObject_elements) {
    239  Rooted<NativeObject*> obj(
    240      cx, NewBuiltinClassInstance(cx, &TestClass, GenericObject));
    241  CHECK(obj);
    242  CHECK(!obj->isTenured());
    243  CHECK(obj->hasEmptyElements());
    244  CHECK(!obj->hasFixedElements());
    245  CHECK(!obj->hasDynamicElements());
    246 
    247  CHECK(obj->ensureElements(cx, 1));
    248  CHECK(!obj->hasEmptyElements());
    249  CHECK(!obj->hasFixedElements());
    250  CHECK(obj->hasDynamicElements());
    251 
    252  RootedObject array(cx, NewArrayObject(cx, 1));
    253  CHECK(array);
    254  obj = &array->as<NativeObject>();
    255  CHECK(!obj->isTenured());
    256  CHECK(!obj->hasEmptyElements());
    257  CHECK(obj->hasFixedElements());
    258  CHECK(!obj->hasDynamicElements());
    259 
    260  return true;
    261 }
    262 END_TEST(testNewObject_elements)