예제 #1
0
파일: jsiter.cpp 프로젝트: ahadzi/celtx
static JSBool
NewKeyValuePair(JSContext *cx, jsid key, jsval val, jsval *rval)
{
    jsval vec[2];
    JSTempValueRooter tvr;
    JSObject *aobj;

    vec[0] = ID_TO_VALUE(key);
    vec[1] = val;

    JS_PUSH_TEMP_ROOT(cx, 2, vec, &tvr);
    aobj = js_NewArrayObject(cx, 2, vec);
    *rval = OBJECT_TO_JSVAL(aobj);
    JS_POP_TEMP_ROOT(cx, &tvr);

    return aobj != NULL;
}
예제 #2
0
파일: jsexn.cpp 프로젝트: ahadzi/celtx
/*
 * Return a string that may eval to something similar to the original object.
 */
static JSBool
exn_toSource(JSContext *cx, uintN argc, jsval *vp)
{
    JSObject *obj;
    JSString *name, *message, *filename, *lineno_as_str, *result;
    jsval localroots[3] = {JSVAL_NULL, JSVAL_NULL, JSVAL_NULL};
    JSTempValueRooter tvr;
    JSBool ok;
    uint32 lineno;
    size_t lineno_length, name_length, message_length, filename_length, length;
    jschar *chars, *cp;

    obj = JS_THIS_OBJECT(cx, vp);
    if (!obj ||
        !OBJ_GET_PROPERTY(cx, obj,
                          ATOM_TO_JSID(cx->runtime->atomState.nameAtom),
                          vp)) {
        return JS_FALSE;
    }
    name = js_ValueToString(cx, *vp);
    if (!name)
        return JS_FALSE;
    *vp = STRING_TO_JSVAL(name);

    /* After this, control must flow through label out: to exit. */
    JS_PUSH_TEMP_ROOT(cx, 3, localroots, &tvr);

#ifdef __GNUC__
    message = filename = NULL;
#endif
    ok = JS_GetProperty(cx, obj, js_message_str, &localroots[0]) &&
         (message = js_ValueToSource(cx, localroots[0]));
    if (!ok)
        goto out;
    localroots[0] = STRING_TO_JSVAL(message);

    ok = JS_GetProperty(cx, obj, js_fileName_str, &localroots[1]) &&
         (filename = js_ValueToSource(cx, localroots[1]));
    if (!ok)
        goto out;
    localroots[1] = STRING_TO_JSVAL(filename);

    ok = JS_GetProperty(cx, obj, js_lineNumber_str, &localroots[2]);
    if (!ok)
        goto out;
    lineno = js_ValueToECMAUint32 (cx, &localroots[2]);
    ok = !JSVAL_IS_NULL(localroots[2]);
    if (!ok)
        goto out;

    if (lineno != 0) {
        lineno_as_str = js_ValueToString(cx, localroots[2]);
        if (!lineno_as_str) {
            ok = JS_FALSE;
            goto out;
        }
        lineno_length = JSSTRING_LENGTH(lineno_as_str);
    } else {
        lineno_as_str = NULL;
        lineno_length = 0;
    }

    /* Magic 8, for the characters in ``(new ())''. */
    name_length = JSSTRING_LENGTH(name);
    message_length = JSSTRING_LENGTH(message);
    length = 8 + name_length + message_length;

    filename_length = JSSTRING_LENGTH(filename);
    if (filename_length != 0) {
        /* append filename as ``, {filename}'' */
        length += 2 + filename_length;
        if (lineno_as_str) {
            /* append lineno as ``, {lineno_as_str}'' */
            length += 2 + lineno_length;
        }
    } else {
        if (lineno_as_str) {
            /*
             * no filename, but have line number,
             * need to append ``, "", {lineno_as_str}''
             */
            length += 6 + lineno_length;
        }
    }

    cp = chars = (jschar *) JS_malloc(cx, (length + 1) * sizeof(jschar));
    if (!chars) {
        ok = JS_FALSE;
        goto out;
    }

    *cp++ = '('; *cp++ = 'n'; *cp++ = 'e'; *cp++ = 'w'; *cp++ = ' ';
    js_strncpy(cp, JSSTRING_CHARS(name), name_length);
    cp += name_length;
    *cp++ = '(';
    if (message_length != 0) {
        js_strncpy(cp, JSSTRING_CHARS(message), message_length);
        cp += message_length;
    }

    if (filename_length != 0) {
        /* append filename as ``, {filename}'' */
        *cp++ = ','; *cp++ = ' ';
        js_strncpy(cp, JSSTRING_CHARS(filename), filename_length);
        cp += filename_length;
    } else {
        if (lineno_as_str) {
            /*
             * no filename, but have line number,
             * need to append ``, "", {lineno_as_str}''
             */
            *cp++ = ','; *cp++ = ' '; *cp++ = '"'; *cp++ = '"';
        }
    }
    if (lineno_as_str) {
        /* append lineno as ``, {lineno_as_str}'' */
        *cp++ = ','; *cp++ = ' ';
        js_strncpy(cp, JSSTRING_CHARS(lineno_as_str), lineno_length);
        cp += lineno_length;
    }

    *cp++ = ')'; *cp++ = ')'; *cp = 0;

    result = js_NewString(cx, chars, length);
    if (!result) {
        JS_free(cx, chars);
        ok = JS_FALSE;
        goto out;
    }
    *vp = STRING_TO_JSVAL(result);
    ok = JS_TRUE;

out:
    JS_POP_TEMP_ROOT(cx, &tvr);
    return ok;
}
예제 #3
0
파일: jsexn.cpp 프로젝트: ahadzi/celtx
JSBool
js_ReportUncaughtException(JSContext *cx)
{
    jsval exn;
    JSObject *exnObject;
    jsval roots[5];
    JSTempValueRooter tvr;
    JSErrorReport *reportp, report;
    JSString *str;
    const char *bytes;
    JSBool ok;

    if (!JS_IsExceptionPending(cx))
        return JS_TRUE;

    if (!JS_GetPendingException(cx, &exn))
        return JS_FALSE;

    memset(roots, 0, sizeof roots);
    JS_PUSH_TEMP_ROOT(cx, JS_ARRAY_LENGTH(roots), roots, &tvr);

    /*
     * Because js_ValueToString below could error and an exception object
     * could become unrooted, we must root exnObject.  Later, if exnObject is
     * non-null, we need to root other intermediates, so allocate an operand
     * stack segment to protect all of these values.
     */
    if (JSVAL_IS_PRIMITIVE(exn)) {
        exnObject = NULL;
    } else {
        exnObject = JSVAL_TO_OBJECT(exn);
        roots[0] = exn;
    }

    JS_ClearPendingException(cx);
    reportp = js_ErrorFromException(cx, exn);

    /* XXX L10N angels cry once again (see also jsemit.c, /L10N gaffes/) */
    str = js_ValueToString(cx, exn);
    if (!str) {
        bytes = "unknown (can't convert to string)";
    } else {
        roots[1] = STRING_TO_JSVAL(str);
        bytes = js_GetStringBytes(cx, str);
        if (!bytes) {
            ok = JS_FALSE;
            goto out;
        }
    }
    ok = JS_TRUE;

    if (!reportp &&
        exnObject &&
        OBJ_GET_CLASS(cx, exnObject) == &js_ErrorClass) {
        const char *filename;
        uint32 lineno;

        ok = JS_GetProperty(cx, exnObject, js_message_str, &roots[2]);
        if (!ok)
            goto out;
        if (JSVAL_IS_STRING(roots[2])) {
            bytes = js_GetStringBytes(cx, JSVAL_TO_STRING(roots[2]));
            if (!bytes) {
                ok = JS_FALSE;
                goto out;
            }
        }

        ok = JS_GetProperty(cx, exnObject, js_fileName_str, &roots[3]);
        if (!ok)
            goto out;
        str = js_ValueToString(cx, roots[3]);
        if (!str) {
            ok = JS_FALSE;
            goto out;
        }
        filename = StringToFilename(cx, str);
        if (!filename) {
            ok = JS_FALSE;
            goto out;
        }

        ok = JS_GetProperty(cx, exnObject, js_lineNumber_str, &roots[4]);
        if (!ok)
            goto out;
        lineno = js_ValueToECMAUint32 (cx, &roots[4]);
        ok = !JSVAL_IS_NULL(roots[4]);
        if (!ok)
            goto out;

        reportp = &report;
        memset(&report, 0, sizeof report);
        report.filename = filename;
        report.lineno = (uintN) lineno;
    }

    if (!reportp) {
        JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL,
                             JSMSG_UNCAUGHT_EXCEPTION, bytes);
    } else {
        /* Flag the error as an exception. */
        reportp->flags |= JSREPORT_EXCEPTION;

        /* Pass the exception object. */
        JS_SetPendingException(cx, exn);
        js_ReportErrorAgain(cx, bytes, reportp);
        JS_ClearPendingException(cx);
    }

out:
    JS_POP_TEMP_ROOT(cx, &tvr);
    return ok;
}
예제 #4
0
파일: jsexn.cpp 프로젝트: ahadzi/celtx
JSBool
js_ErrorToException(JSContext *cx, const char *message, JSErrorReport *reportp)
{
    JSErrNum errorNumber;
    const JSErrorFormatString *errorString;
    JSExnType exn;
    jsval tv[4];
    JSTempValueRooter tvr;
    JSBool ok;
    JSObject *errProto, *errObject;
    JSString *messageStr, *filenameStr;

    /*
     * Tell our caller to report immediately if cx has no active frames, or if
     * this report is just a warning.
     */
    JS_ASSERT(reportp);
    if (!cx->fp || JSREPORT_IS_WARNING(reportp->flags))
        return JS_FALSE;

    /* Find the exception index associated with this error. */
    errorNumber = (JSErrNum) reportp->errorNumber;
    errorString = js_GetLocalizedErrorMessage(cx, NULL, NULL, errorNumber);
    exn = errorString ? (JSExnType) errorString->exnType : JSEXN_NONE;
    JS_ASSERT(exn < JSEXN_LIMIT);

#if defined( DEBUG_mccabe ) && defined ( PRINTNAMES )
    /* Print the error name and the associated exception name to stderr */
    fprintf(stderr, "%s\t%s\n",
            errortoexnname[errorNumber].name,
            errortoexnname[errorNumber].exception);
#endif

    /*
     * Return false (no exception raised) if no exception is associated
     * with the given error number.
     */
    if (exn == JSEXN_NONE)
        return JS_FALSE;

    /*
     * Prevent runaway recursion, via cx->generatingError.  If an out-of-memory
     * error occurs, no exception object will be created, but we don't assume
     * that OOM is the only kind of error that subroutines of this function
     * called below might raise.
     */
    if (cx->generatingError)
        return JS_FALSE;

    /* After this point the control must flow through the label out. */
    cx->generatingError = JS_TRUE;

    /* Protect the newly-created strings below from nesting GCs. */
    memset(tv, 0, sizeof tv);
    JS_PUSH_TEMP_ROOT(cx, JS_ARRAY_LENGTH(tv), tv, &tvr);

    /*
     * Try to get an appropriate prototype by looking up the corresponding
     * exception constructor name in the scope chain of the current context's
     * top stack frame, or in the global object if no frame is active.
     */
    ok = js_GetClassPrototype(cx, NULL, INT_TO_JSID(exceptions[exn].key),
                              &errProto);
    if (!ok)
        goto out;
    tv[0] = OBJECT_TO_JSVAL(errProto);

    errObject = js_NewObject(cx, &js_ErrorClass, errProto, NULL, 0);
    if (!errObject) {
        ok = JS_FALSE;
        goto out;
    }
    tv[1] = OBJECT_TO_JSVAL(errObject);

    messageStr = JS_NewStringCopyZ(cx, message);
    if (!messageStr) {
        ok = JS_FALSE;
        goto out;
    }
    tv[2] = STRING_TO_JSVAL(messageStr);

    filenameStr = JS_NewStringCopyZ(cx, reportp->filename);
    if (!filenameStr) {
        ok = JS_FALSE;
        goto out;
    }
    tv[3] = STRING_TO_JSVAL(filenameStr);

    ok = InitExnPrivate(cx, errObject, messageStr, filenameStr,
                        reportp->lineno, reportp);
    if (!ok)
        goto out;

    JS_SetPendingException(cx, OBJECT_TO_JSVAL(errObject));

    /* Flag the error report passed in to indicate an exception was raised. */
    reportp->flags |= JSREPORT_EXCEPTION;

out:
    JS_POP_TEMP_ROOT(cx, &tvr);
    cx->generatingError = JS_FALSE;
    return ok;
}
예제 #5
0
LRESULT WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam) {

	CxObj *cxobj = (CxObj*)GetWindowLong(hWnd, GWL_USERDATA);
	if ( cxobj == NULL )
		return DefWindowProc(hWnd, message, wParam, lParam);

	JSContext *cx = cxobj->cx;
	JS::RootedObject obj(cx, cxobj->obj);

	jsval rval;
	jsval functionVal;

	LRESULT returnValue = 0;

	switch (message) {

		//case WM_COMMAND:
		//case WM_PAINT

		case WM_DESTROY:
			PostQuitMessage(0);
			return 0;
		case WM_CHAR:
			JS_GetProperty(cx, obj, "onchar", &functionVal);
			if ( !functionVal.isUndefined() ) {

				JL_ASSERT_FUNCTION(functionVal); // (TBD) return value of assert is not compatible with this function (WndProc)
				char c = wParam;
				jsval argv[] = { STRING_TO_JSVAL(JS_NewStringCopyN(cx, &c, 1)), INT_TO_JSVAL(lParam) };
				JSTempValueRooter tvr;
				JS_PUSH_TEMP_ROOT(cx, COUNTOF(argv), argv, &tvr); // needed to protect the new string
				bool status = JS_CallFunctionValue(cx, obj, functionVal, COUNTOF(argv), argv, &rval);
				JS_POP_TEMP_ROOT(cx, &tvr);
				JL_CHK( status );
				return 0;
			}
			break;
		case WM_KEYUP:
			JS_GetProperty(cx, obj, "onkeyup", &functionVal);
			if ( !functionVal.isUndefined() ) {

				JL_ASSERT_FUNCTION(functionVal); // (TBD) return value of assert is not compatible with this function (WndProc)
				jsval argv[] = { INT_TO_JSVAL(wParam), INT_TO_JSVAL(lParam) };
				JSTempValueRooter tvr;
				JS_PUSH_TEMP_ROOT(cx, COUNTOF(argv), argv, &tvr); // not really needed
				bool status = JS_CallFunctionValue(cx, obj, functionVal, COUNTOF(argv), argv, &rval);
				JS_POP_TEMP_ROOT(cx, &tvr);
				JL_CHK( status );
				return 0;
			}
			break;
		case WM_KEYDOWN:
			JS_GetProperty(cx, obj, "onkeydown", &functionVal);
			if ( !functionVal.isUndefined() ) {

				JL_ASSERT_FUNCTION(functionVal); // (TBD) return value of assert is not compatible with this function (WndProc)
				jsval argv[] = { INT_TO_JSVAL(wParam), INT_TO_JSVAL(lParam) };
				JSTempValueRooter tvr;
				JS_PUSH_TEMP_ROOT(cx, COUNTOF(argv), argv, &tvr); // not really needed
				bool status = JS_CallFunctionValue(cx, obj, functionVal, COUNTOF(argv), argv, &rval);
				JS_POP_TEMP_ROOT(cx, &tvr);
				JL_CHK( status );
				return 0;
			}
			break;
		case WM_ACTIVATE:
			JS_GetProperty(cx, obj, "onactivate", &functionVal);
			if ( !functionVal.isUndefined() ) {

				JL_ASSERT_FUNCTION(functionVal); // (TBD) return value of assert is not compatible with this function (WndProc)
				jsval argv[] = { BOOLEAN_TO_JSVAL(wParam != WA_INACTIVE) };
				JSTempValueRooter tvr;
				JS_PUSH_TEMP_ROOT(cx, COUNTOF(argv), argv, &tvr); // not really needed
				bool status = JS_CallFunctionValue(cx, obj, functionVal, COUNTOF(argv), argv, &rval);
				JS_POP_TEMP_ROOT(cx, &tvr);
				JL_CHK( status );
				return 0;
			}
			break;
//		case WM_SIZING:
		case WM_SIZE:
			JS_GetProperty(cx, obj, "onsize", &functionVal);
			if ( !functionVal.isUndefined() ) {

				JL_ASSERT_FUNCTION(functionVal); // (TBD) return value of assert is not compatible with this function (WndProc)
				jsval argv[] = { INT_TO_JSVAL((short)LOWORD(lParam)), INT_TO_JSVAL((short)HIWORD(lParam)) };
				JSTempValueRooter tvr;
				JS_PUSH_TEMP_ROOT(cx, COUNTOF(argv), argv, &tvr); // not really needed
				bool status = JS_CallFunctionValue(cx, obj, functionVal, COUNTOF(argv), argv, &rval);
				JS_POP_TEMP_ROOT(cx, &tvr);
				JL_CHK( status );
				return 0;
			}
			break;
		case WM_MOUSEMOVE:
			JS_GetProperty(cx, obj, "onmousemove", &functionVal);
			if ( !functionVal.isUndefined() ) {

				JL_ASSERT_FUNCTION(functionVal); // (TBD) return value of assert is not compatible with this function (WndProc)
				jsval argv[] = { INT_TO_JSVAL(MAKEPOINTS(lParam).x), INT_TO_JSVAL(MAKEPOINTS(lParam).y), BOOLEAN_TO_JSVAL(wParam & MK_LBUTTON), BOOLEAN_TO_JSVAL(wParam & MK_RBUTTON), BOOLEAN_TO_JSVAL(wParam & MK_MBUTTON) };
				JSTempValueRooter tvr;
				JS_PUSH_TEMP_ROOT(cx, COUNTOF(argv), argv, &tvr); // not really needed
				bool status = JS_CallFunctionValue(cx, obj, functionVal, COUNTOF(argv), argv, &rval);
				JS_POP_TEMP_ROOT(cx, &tvr);
				JL_CHK( status );
				return 0;
			}
			break;
		case WM_MOUSEWHEEL:
			JS_GetProperty(cx, obj, "onmousewheel", &functionVal);
			if ( !functionVal.isUndefined() ) {

				JL_ASSERT_FUNCTION(functionVal); // (TBD) return value of assert is not compatible with this function (WndProc)
				jsval argv[] = { INT_TO_JSVAL( GET_WHEEL_DELTA_WPARAM(wParam)/WHEEL_DELTA ), BOOLEAN_TO_JSVAL(wParam & MK_LBUTTON), BOOLEAN_TO_JSVAL(wParam & MK_RBUTTON), BOOLEAN_TO_JSVAL(wParam & MK_MBUTTON) };
				JSTempValueRooter tvr;
				JS_PUSH_TEMP_ROOT(cx, COUNTOF(argv), argv, &tvr); // not really needed
				bool status = JS_CallFunctionValue(cx, obj, functionVal, COUNTOF(argv), argv, &rval);
				JS_POP_TEMP_ROOT(cx, &tvr);
				JL_CHK( status );
				return 0;
			}
			break;
		case WM_LBUTTONDOWN:
		case WM_MBUTTONDOWN:
		case WM_RBUTTONDOWN:
			JS_GetProperty(cx, obj, "onmousedown", &functionVal);
			if ( !functionVal.isUndefined() ) {

				// xPos = GET_X_LPARAM(lParam);
				// yPos = GET_Y_LPARAM(lParam);

				JL_ASSERT_FUNCTION(functionVal); // (TBD) return value of assert is not compatible with this function (WndProc)
				jsval argv[] = { INT_TO_JSVAL( message==WM_LBUTTONDOWN ? 1 : message==WM_RBUTTONDOWN ? 2 : message==WM_MBUTTONDOWN ? 3 : 0 ), JSVAL_TRUE };
//					BOOLEAN_TO_JSVAL(wParam & MK_LBUTTON),
//					BOOLEAN_TO_JSVAL(wParam & MK_RBUTTON),
//					BOOLEAN_TO_JSVAL(wParam & MK_MBUTTON) };
				JSTempValueRooter tvr;
				JS_PUSH_TEMP_ROOT(cx, COUNTOF(argv), argv, &tvr); // not really needed
				bool status = JS_CallFunctionValue(cx, obj, functionVal, COUNTOF(argv), argv, &rval);
				JS_POP_TEMP_ROOT(cx, &tvr);
				JL_CHK( status );
				return 0;
			}
			break;
		case WM_LBUTTONUP:
		case WM_MBUTTONUP:
		case WM_RBUTTONUP:
			JS_GetProperty(cx, obj, "onmouseup", &functionVal);
			if ( !functionVal.isUndefined() ) {

				// xPos = GET_X_LPARAM(lParam);
				// yPos = GET_Y_LPARAM(lParam);

				JL_ASSERT( JS_TypeOfValue( cx, functionVal ) == JSTYPE_FUNCTION, "Need a function." );
				jsval argv[] = { INT_TO_JSVAL( message==WM_LBUTTONUP ? 1 : message==WM_RBUTTONUP ? 2 : message==WM_MBUTTONUP ? 3 : 0 ), JSVAL_FALSE };
				JSTempValueRooter tvr;
				JS_PUSH_TEMP_ROOT(cx, COUNTOF(argv), argv, &tvr); // not really needed
				bool status = JS_CallFunctionValue(cx, obj, functionVal, COUNTOF(argv), argv, &rval);
				JS_POP_TEMP_ROOT(cx, &tvr);
				JL_CHK( status );
				return 0;
			}
			break;

		//case WM_MOUSELEAVE: // need TrackMouseEvent() ...
		//	JS_GetProperty(cx, obj, "onmouseleave", &functionVal);
		//	if ( !JSVAL_IS_VOID( functionVal ) ) {

		//		JS_CallFunctionValue(cx, obj, functionVal, 0, NULL, &rval);
		//	}
		//	break;
	}
	return DefWindowProc(hWnd, message, wParam, lParam); // We do not want to handle this message so pass back to Windows to handle it in a default way
}
예제 #6
0
파일: jsiter.cpp 프로젝트: ahadzi/celtx
js_ValueToIterator(JSContext *cx, uintN flags, jsval *vp)
{
    JSObject *obj;
    JSTempValueRooter tvr;
    JSAtom *atom;
    JSClass *clasp;
    JSExtendedClass *xclasp;
    JSBool ok;
    JSObject *iterobj;
    jsval arg;

    JS_ASSERT(!(flags & ~(JSITER_ENUMERATE |
                          JSITER_FOREACH |
                          JSITER_KEYVALUE)));

    /* JSITER_KEYVALUE must always come with JSITER_FOREACH */
    JS_ASSERT(!(flags & JSITER_KEYVALUE) || (flags & JSITER_FOREACH));

    /* XXX work around old valueOf call hidden beneath js_ValueToObject */
    if (!JSVAL_IS_PRIMITIVE(*vp)) {
        obj = JSVAL_TO_OBJECT(*vp);
    } else {
        /*
         * Enumerating over null and undefined gives an empty enumerator.
         * This is contrary to ECMA-262 9.9 ToObject, invoked from step 3 of
         * the first production in 12.6.4 and step 4 of the second production,
         * but it's "web JS" compatible.
         */
        if ((flags & JSITER_ENUMERATE)) {
            if (!js_ValueToObject(cx, *vp, &obj))
                return JS_FALSE;
            if (!obj)
                goto default_iter;
        } else {
            obj = js_ValueToNonNullObject(cx, *vp);
            if (!obj)
                return JS_FALSE;
        }
    }

    JS_ASSERT(obj);
    JS_PUSH_TEMP_ROOT_OBJECT(cx, obj, &tvr);

    clasp = OBJ_GET_CLASS(cx, obj);
    if ((clasp->flags & JSCLASS_IS_EXTENDED) &&
        (xclasp = (JSExtendedClass *) clasp)->iteratorObject) {
        iterobj = xclasp->iteratorObject(cx, obj, !(flags & JSITER_FOREACH));
        if (!iterobj)
            goto bad;
        *vp = OBJECT_TO_JSVAL(iterobj);
    } else {
        atom = cx->runtime->atomState.iteratorAtom;
#if JS_HAS_XML_SUPPORT
        if (OBJECT_IS_XML(cx, obj)) {
            if (!js_GetXMLFunction(cx, obj, ATOM_TO_JSID(atom), vp))
                goto bad;
        } else
#endif
        {
            if (!OBJ_GET_PROPERTY(cx, obj, ATOM_TO_JSID(atom), vp))
                goto bad;
        }

        if (JSVAL_IS_VOID(*vp)) {
          default_iter:
            /*
             * Fail over to the default enumerating native iterator.
             *
             * Create iterobj with a NULL parent to ensure that we use the
             * correct scope chain to lookup the iterator's constructor. Since
             * we use the parent slot to keep track of the iterable, we must
             * fix it up after.
             */
            iterobj = js_NewObject(cx, &js_IteratorClass, NULL, NULL, 0);
            if (!iterobj)
                goto bad;

            /* Store in *vp to protect it from GC (callers must root vp). */
            *vp = OBJECT_TO_JSVAL(iterobj);

            if (!InitNativeIterator(cx, iterobj, obj, flags))
                goto bad;
        } else {
            arg = BOOLEAN_TO_JSVAL((flags & JSITER_FOREACH) == 0);
            if (!js_InternalInvoke(cx, obj, *vp, JSINVOKE_ITERATOR, 1, &arg,
                                   vp)) {
                goto bad;
            }
            if (JSVAL_IS_PRIMITIVE(*vp)) {
                const char *printable = js_AtomToPrintableString(cx, atom);
                if (printable) {
                    js_ReportValueError2(cx, JSMSG_BAD_ITERATOR_RETURN,
                                         JSDVG_SEARCH_STACK, *vp, NULL,
                                         printable);
                }
                goto bad;
            }
        }
    }

    ok = JS_TRUE;
  out:
    if (obj)
        JS_POP_TEMP_ROOT(cx, &tvr);
    return ok;
  bad:
    ok = JS_FALSE;
    goto out;
}