From 523160aa40b17f473f32ec665474e14b1a73bc71 Mon Sep 17 00:00:00 2001 From: Jason Orendorff Date: Wed, 17 Mar 2010 10:22:13 -0500 Subject: [PATCH] Bug 480850 - Add JSAPI function JS_New (identical to tracemonkey/rev/869bebcf1f21). r=mrbkap. --- js/src/jsapi-tests/Makefile.in | 1 + js/src/jsapi-tests/testNewObject.cpp | 101 +++++++++++++++++++++++++++ js/src/jsapi.cpp | 25 +++++++ js/src/jsapi.h | 3 + 4 files changed, 130 insertions(+) create mode 100644 js/src/jsapi-tests/testNewObject.cpp diff --git a/js/src/jsapi-tests/Makefile.in b/js/src/jsapi-tests/Makefile.in index e338907d08c..f41b13fb805 100644 --- a/js/src/jsapi-tests/Makefile.in +++ b/js/src/jsapi-tests/Makefile.in @@ -55,6 +55,7 @@ CPPSRCS = \ testIntString.cpp \ testIsAboutToBeFinalized.cpp \ testLookup.cpp \ + testNewObject.cpp \ testPropCache.cpp \ testTrap.cpp \ testSameValue.cpp \ diff --git a/js/src/jsapi-tests/testNewObject.cpp b/js/src/jsapi-tests/testNewObject.cpp new file mode 100644 index 00000000000..5b637453d32 --- /dev/null +++ b/js/src/jsapi-tests/testNewObject.cpp @@ -0,0 +1,101 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- + * vim: set ts=4 sw=4 et tw=99: + */ + +#include "tests.h" + +const int N = 1000; +static jsval argv[N]; + +static JSBool +constructHook(JSContext *cx, JSObject *thisobj, uintN argc, jsval *argv, jsval *rval) +{ + // Check that arguments were passed properly from JS_New. + JSObject *callee = JSVAL_TO_OBJECT(JS_ARGV_CALLEE(argv)); + if (!thisobj) { + JS_ReportError(cx, "test failed, null 'this'"); + return false; + } + if (strcmp(JS_GET_CLASS(cx, thisobj)->name, "Object") != 0) { + JS_ReportError(cx, "test failed, wrong class for 'this'"); + return false; + } + if (argc != 3) { + JS_ReportError(cx, "test failed, argc == %d", argc); + return false; + } + if (!JSVAL_IS_INT(argv[2]) || JSVAL_TO_INT(argv[2]) != 2) { + JS_ReportError(cx, "test failed, wrong value in argv[2]"); + return false; + } + if (!JS_IsConstructing(cx)) { + JS_ReportError(cx, "test failed, not constructing"); + return false; + } + + // Perform a side-effect to indicate that this hook was actually called. + if (!JS_SetElement(cx, callee, 0, &argv[0])) + return false; + + *rval = OBJECT_TO_JSVAL(callee); // return the callee, perversely + argv[0] = argv[1] = argv[2] = JSVAL_VOID; // trash the argv, perversely + return true; +} + +BEGIN_TEST(testNewObject_1) +{ + jsval v; + EVAL("Array", &v); + JSObject *Array = JSVAL_TO_OBJECT(v); + + // With no arguments. + JSObject *obj = JS_New(cx, Array, 0, NULL); + CHECK(obj); + jsvalRoot rt(cx, OBJECT_TO_JSVAL(obj)); + CHECK(JS_IsArrayObject(cx, obj)); + jsuint len; + CHECK(JS_GetArrayLength(cx, obj, &len)); + CHECK(len == 0); + + // With one argument. + argv[0] = INT_TO_JSVAL(4); + obj = JS_New(cx, Array, 1, argv); + CHECK(obj); + rt = OBJECT_TO_JSVAL(obj); + CHECK(JS_IsArrayObject(cx, obj)); + CHECK(JS_GetArrayLength(cx, obj, &len)); + CHECK(len == 4); + + // With N arguments. + JS_ASSERT(INT_FITS_IN_JSVAL(N)); + for (int i = 0; i < N; i++) + argv[i] = INT_TO_JSVAL(i); + obj = JS_New(cx, Array, N, argv); + CHECK(obj); + rt = OBJECT_TO_JSVAL(obj); + CHECK(JS_IsArrayObject(cx, obj)); + CHECK(JS_GetArrayLength(cx, obj, &len)); + CHECK(len == N); + CHECK(JS_GetElement(cx, obj, N - 1, &v)); + CHECK_SAME(v, INT_TO_JSVAL(N - 1)); + + // With JSClass.construct. + static JSClass cls = { + "testNewObject_1", + 0, + JS_PropertyStub, JS_PropertyStub, JS_PropertyStub, JS_PropertyStub, + JS_EnumerateStub, JS_ResolveStub, JS_ConvertStub, NULL, + NULL, NULL, NULL, constructHook, NULL, NULL, NULL, NULL + }; + JSObject *ctor = JS_NewObject(cx, &cls, NULL, NULL); + CHECK(ctor); + jsvalRoot rt2(cx, OBJECT_TO_JSVAL(ctor)); + obj = JS_New(cx, ctor, 3, argv); + CHECK(obj); + CHECK(obj == ctor); // constructHook returns ctor, perversely + CHECK(JS_GetElement(cx, ctor, 0, &v)); + CHECK_SAME(v, JSVAL_ZERO); + CHECK_SAME(argv[0], JSVAL_ZERO); // original argv should not have been trashed + CHECK_SAME(argv[1], JSVAL_ONE); +} +END_TEST(testNewObject_1) diff --git a/js/src/jsapi.cpp b/js/src/jsapi.cpp index 279076f3021..92ff3e61469 100644 --- a/js/src/jsapi.cpp +++ b/js/src/jsapi.cpp @@ -4953,6 +4953,31 @@ JS_CallFunctionValue(JSContext *cx, JSObject *obj, jsval fval, uintN argc, return ok; } +JS_PUBLIC_API(JSObject *) +JS_New(JSContext *cx, JSObject *ctor, uintN argc, jsval *argv) +{ + CHECK_REQUEST(cx); + + // This is not a simple variation of JS_CallFunctionValue because JSOP_NEW + // is not a simple variation of JSOP_CALL. We have to determine what class + // of object to create, create it, and clamp the return value to an object, + // among other details. js_InvokeConstructor does the hard work. + void *mark; + jsval *vp = js_AllocStack(cx, 2 + argc, &mark); + if (!vp) + return NULL; + vp[0] = OBJECT_TO_JSVAL(ctor); + vp[1] = JSVAL_NULL; + memcpy(vp + 2, argv, argc * sizeof(jsval)); + + JSBool ok = js_InvokeConstructor(cx, argc, JS_TRUE, vp); + JSObject *obj = ok ? JSVAL_TO_OBJECT(vp[0]) : NULL; + + js_FreeStack(cx, mark); + LAST_FRAME_CHECKS(cx, ok); + return obj; +} + JS_PUBLIC_API(JSOperationCallback) JS_SetOperationCallback(JSContext *cx, JSOperationCallback callback) { diff --git a/js/src/jsapi.h b/js/src/jsapi.h index 083de69200d..de5b085d273 100644 --- a/js/src/jsapi.h +++ b/js/src/jsapi.h @@ -1678,6 +1678,9 @@ extern JS_PUBLIC_API(JSObject *) JS_ConstructObjectWithArguments(JSContext *cx, JSClass *clasp, JSObject *proto, JSObject *parent, uintN argc, jsval *argv); +extern JS_PUBLIC_API(JSObject *) +JS_New(JSContext *cx, JSObject *ctor, uintN argc, jsval *argv); + extern JS_PUBLIC_API(JSObject *) JS_DefineObject(JSContext *cx, JSObject *obj, const char *name, JSClass *clasp, JSObject *proto, uintN attrs);