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:
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/. */
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_
15 #include "vm/NativeObject-inl.h"
19 static bool constructHook(JSContext
* cx
, unsigned argc
, JS::Value
* vp
) {
20 JS::CallArgs args
= CallArgsFromVp(argc
, vp
);
22 // Check that arguments were passed properly from JS_New.
24 JS::RootedObject
obj(cx
, JS_NewPlainObject(cx
));
26 JS_ReportErrorASCII(cx
, "test failed, could not construct object");
29 if (strcmp(JS::GetClass(obj
)->name
, "Object") != 0) {
30 JS_ReportErrorASCII(cx
, "test failed, wrong class for 'this'");
33 if (args
.length() != 3) {
34 JS_ReportErrorASCII(cx
, "test failed, argc == %d", args
.length());
37 if (!args
[0].isInt32() || args
[2].toInt32() != 2) {
38 JS_ReportErrorASCII(cx
, "test failed, wrong value in args[2]");
41 if (!args
.isConstructing()) {
42 JS_ReportErrorASCII(cx
, "test failed, not constructing");
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
)) {
53 args
.rval().setObject(*obj
);
55 // trash the argv, perversely
56 args
[0].setUndefined();
57 args
[1].setUndefined();
58 args
[2].setUndefined();
63 BEGIN_TEST(testNewObject_1
) {
64 static const size_t N
= 1000;
65 JS::RootedValueVector
argv(cx
);
66 CHECK(argv
.resize(N
));
68 JS::RootedValue
Array(cx
);
69 EVAL("Array", &Array
);
74 JS::RootedObject
obj(cx
);
75 CHECK(JS::Construct(cx
, Array
, JS::HandleValueArray::empty(), &obj
));
76 CHECK(JS::IsArrayObject(cx
, obj
, &isArray
));
79 CHECK(JS::GetArrayLength(cx
, obj
, &len
));
84 CHECK(JS::Construct(cx
, Array
, JS::HandleValueArray::subarray(argv
, 0, 1),
86 CHECK(JS::IsArrayObject(cx
, obj
, &isArray
));
88 CHECK(JS::GetArrayLength(cx
, obj
, &len
));
92 for (size_t i
= 0; i
< N
; i
++) {
95 CHECK(JS::Construct(cx
, Array
, JS::HandleValueArray::subarray(argv
, 0, N
),
97 CHECK(JS::IsArrayObject(cx
, obj
, &isArray
));
99 CHECK(JS::GetArrayLength(cx
, obj
, &len
));
101 JS::RootedValue
v(cx
);
102 CHECK(JS_GetElement(cx
, obj
, N
- 1, &v
));
103 CHECK(v
.isInt32(N
- 1));
105 // With JSClass.construct.
106 static const JSClassOps clsOps
= {
107 nullptr, // addProperty
108 nullptr, // delProperty
109 nullptr, // enumerate
110 nullptr, // newEnumerate
112 nullptr, // mayResolve
115 constructHook
, // construct
118 static const JSClass cls
= {"testNewObject_1", 0, &clsOps
};
119 JS::RootedObject
ctor(cx
, JS_NewObject(cx
, &cls
));
121 JS::RootedValue
ctorVal(cx
, JS::ObjectValue(*ctor
));
122 CHECK(JS::Construct(cx
, ctorVal
, JS::HandleValueArray::subarray(argv
, 0, 3),
124 CHECK(JS_GetElement(cx
, ctor
, 0, &v
));
129 END_TEST(testNewObject_1
)
131 BEGIN_TEST(testNewObject_IsMapObject
) {
132 // Test IsMapObject and IsSetObject
134 JS::RootedValue
vMap(cx
);
139 JS::RootedObject
mapObj(cx
);
140 CHECK(JS::Construct(cx
, vMap
, JS::HandleValueArray::empty(), &mapObj
));
141 CHECK(JS::IsMapObject(cx
, mapObj
, &isMap
));
143 CHECK(JS::IsSetObject(cx
, mapObj
, &isSet
));
146 JS::RootedValue
vSet(cx
);
149 JS::RootedObject
setObj(cx
);
150 CHECK(JS::Construct(cx
, vSet
, JS::HandleValueArray::empty(), &setObj
));
151 CHECK(JS::IsMapObject(cx
, setObj
, &isMap
));
153 CHECK(JS::IsSetObject(cx
, setObj
, &isSet
));
158 END_TEST(testNewObject_IsMapObject
)
160 static const JSClass Base_class
= {
162 JSCLASS_HAS_RESERVED_SLOTS(8), // flags
165 BEGIN_TEST(testNewObject_Subclassing
) {
167 JS_InitClass(cx
, global
, nullptr, nullptr, "Base", Base_constructor
, 0,
168 nullptr, nullptr, nullptr, nullptr);
173 CHECK_EQUAL(JS::GetClass(proto
), &PlainObject::class_
);
175 // Calling Base without `new` should fail with a TypeError.
176 JS::RootedValue
expectedError(cx
);
177 EVAL("TypeError", &expectedError
);
178 JS::RootedValue
actualError(cx
);
186 CHECK_SAME(actualError
, expectedError
);
188 // Check prototype chains when a JS class extends a base class that's
189 // implemented in C++ using JS_NewObjectForConstructor.
191 "class MyClass extends Base {\n"
192 " ok() { return true; }\n"
194 "let myObj = new MyClass();\n");
196 JS::RootedValue
result(cx
);
197 EVAL("myObj.ok()", &result
);
198 CHECK_SAME(result
, JS::TrueValue());
200 EVAL("myObj.__proto__ === MyClass.prototype", &result
);
201 CHECK_SAME(result
, JS::TrueValue());
202 EVAL("myObj.__proto__.__proto__ === Base.prototype", &result
);
203 CHECK_SAME(result
, JS::TrueValue());
205 EVAL("myObj", &result
);
206 CHECK_EQUAL(JS::GetClass(&result
.toObject()), &Base_class
);
208 // All reserved slots are initialized to undefined.
209 for (uint32_t i
= 0; i
< JSCLASS_RESERVED_SLOTS(&Base_class
); i
++) {
210 CHECK_SAME(JS::GetReservedSlot(&result
.toObject(), i
),
211 JS::UndefinedValue());
217 static bool Base_constructor(JSContext
* cx
, unsigned argc
, JS::Value
* vp
) {
218 JS::CallArgs args
= CallArgsFromVp(argc
, vp
);
219 JS::RootedObject
obj(cx
, JS_NewObjectForConstructor(cx
, &Base_class
, args
));
223 args
.rval().setObject(*obj
);
227 END_TEST(testNewObject_Subclassing
)
229 static const JSClass TestClass
= {"TestObject", JSCLASS_HAS_RESERVED_SLOTS(0)};
231 BEGIN_TEST(testNewObject_elements
) {
232 Rooted
<NativeObject
*> obj(
233 cx
, NewBuiltinClassInstance(cx
, &TestClass
, GenericObject
));
235 CHECK(!obj
->isTenured());
236 CHECK(obj
->hasEmptyElements());
237 CHECK(!obj
->hasFixedElements());
238 CHECK(!obj
->hasDynamicElements());
240 CHECK(obj
->ensureElements(cx
, 1));
241 CHECK(!obj
->hasEmptyElements());
242 CHECK(!obj
->hasFixedElements());
243 CHECK(obj
->hasDynamicElements());
245 RootedObject
array(cx
, NewArrayObject(cx
, 1));
247 obj
= &array
->as
<NativeObject
>();
248 CHECK(!obj
->isTenured());
249 CHECK(!obj
->hasEmptyElements());
250 CHECK(obj
->hasFixedElements());
251 CHECK(!obj
->hasDynamicElements());
255 END_TEST(testNewObject_elements
)