#include "tests.h" #include "jsfun.h" #include "jscntxt.h" // For TRACING_ENABLED #include "jstracer.h" #ifdef MOZ_TRACE_JSCALLS static int depth = 0; static int enters = 0; static int leaves = 0; static int interpreted = 0; static void funcTransition(const JSFunction *, const JSScript *, const JSContext *cx, JSBool entering) { if (entering) { ++depth; ++enters; if (! JS_ON_TRACE(cx)) ++interpreted; } else { --depth; ++leaves; } } static JSBool called2 = false; static void funcTransition2(const JSFunction *, const JSScript*, const JSContext*, JSBool) { called2 = true; } static int overlays = 0; static JSFunctionCallback innerCallback = NULL; static void funcTransitionOverlay(const JSFunction *fun, const JSScript *script, const JSContext *cx, JSBool entering) { (*innerCallback)(fun, script, cx, entering); overlays++; } #endif BEGIN_TEST(testFuncCallback_bug507012) { #ifdef MOZ_TRACE_JSCALLS // Call funcTransition() whenever a Javascript method is invoked JS_SetFunctionCallback(cx, funcTransition); EXEC("x = 0; function f (n) { if (n > 1) { f(n - 1); } }"); interpreted = enters = leaves = depth = 0; // Check whether JS_Execute() tracking works EXEC("42"); CHECK(enters == 1 && leaves == 1 && depth == 0); interpreted = enters = leaves = depth = 0; // Check whether the basic function tracking works EXEC("f(1)"); CHECK(enters == 2 && leaves == 2 && depth == 0); // Can we switch to a different callback? enters = 777; JS_SetFunctionCallback(cx, funcTransition2); EXEC("f(1)"); CHECK(called2 && enters == 777); // Check whether we can turn off function tracing JS_SetFunctionCallback(cx, NULL); EXEC("f(1)"); CHECK(enters == 777); interpreted = enters = leaves = depth = 0; // Check nested invocations JS_SetFunctionCallback(cx, funcTransition); enters = leaves = depth = 0; EXEC("f(3)"); CHECK(enters == 1+3 && leaves == 1+3 && depth == 0); interpreted = enters = leaves = depth = 0; // Check calls invoked while running on trace EXEC("function g () { ++x; }"); interpreted = enters = leaves = depth = 0; EXEC("for (i = 0; i < 50; ++i) { g(); }"); CHECK(enters == 50+1 && leaves == 50+1 && depth == 0); // If this fails, it means that the code was interpreted rather // than trace-JITted, and so is not testing what it's supposed to // be testing. Which doesn't necessarily imply that the // functionality is broken. #ifdef JS_TRACER if (TRACING_ENABLED(cx)) CHECK(interpreted < enters); #endif // Test nesting callbacks via JS_GetFunctionCallback() JS_SetFunctionCallback(cx, funcTransition); innerCallback = JS_GetFunctionCallback(cx); JS_SetFunctionCallback(cx, funcTransitionOverlay); EXEC("x = 0; function f (n) { if (n > 1) { f(n - 1); } }"); interpreted = enters = leaves = depth = overlays = 0; EXEC("42.5"); CHECK(enters == 1); CHECK(leaves == 1); CHECK(depth == 0); CHECK(overlays == 2); // 1 each for enter and exit interpreted = enters = leaves = depth = overlays = 0; #endif return true; } // Not strictly necessary, but part of the test attempts to check // whether these callbacks still trigger when traced, so force // JSOPTION_JIT just to be sure. Once the method jit and tracing jit // are integrated, this'll probably have to change (and we'll probably // want to test in all modes.) virtual JSContext *createContext() { JSContext *cx = JSAPITest::createContext(); if (cx) JS_SetOptions(cx, JS_GetOptions(cx) | JSOPTION_JIT); return cx; } END_TEST(testFuncCallback_bug507012)