Bug 807845 - provide a jsfriendapi to query the caller's outermost enclosing function (r=jimb)
authorLuke Wagner <luke@mozilla.com>
Fri, 09 Nov 2012 11:39:52 -0800
changeset 112868 843a3db9c0457acbdf91e257a927ac97ea4a7dc5
parent 112867 7dbb863bed7ca9906af2df81569f0d42f15d2808
child 112869 5fd176fb2acaec963539d0a9ce513aec76b22169
push id23839
push userryanvm@gmail.com
push dateSat, 10 Nov 2012 01:34:49 +0000
treeherderautoland@a47525b93528 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersjimb
bugs807845
milestone19.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 807845 - provide a jsfriendapi to query the caller's outermost enclosing function (r=jimb)
js/src/jsapi-tests/Makefile.in
js/src/jsapi-tests/testEnclosingFunction.cpp
js/src/jsfriendapi.cpp
js/src/jsfriendapi.h
--- a/js/src/jsapi-tests/Makefile.in
+++ b/js/src/jsapi-tests/Makefile.in
@@ -60,16 +60,17 @@ CPPSRCS = \
   testStringBuffer.cpp \
   testTrap.cpp \
   testTypedArrays.cpp \
   testVersion.cpp \
   testXDR.cpp \
   testProfileStrings.cpp \
   testJSEvaluateScript.cpp \
   testErrorCopying.cpp \
+  testEnclosingFunction.cpp \
   $(NULL)
 
 # Disabled: an entirely unrelated test seems to cause this to fail.  Moreover,
 # given the test's dependence on interactions between the compiler, the GC, and
 # conservative stack scanning, the fix isn't obvious: more investigation
 # needed.
 #CPPSRCS += \
 #  testRegExpInstanceProperties.cpp \
new file mode 100644
--- /dev/null
+++ b/js/src/jsapi-tests/testEnclosingFunction.cpp
@@ -0,0 +1,58 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
+ * vim: set ts=8 sw=4 et tw=99:
+ *
+ * Test script cloning.
+ */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "tests.h"
+#include "jsfriendapi.h"
+#include "jsdbgapi.h"
+
+using namespace js;
+
+JSScript *found = NULL;
+
+JSBool
+CheckEnclosing(JSContext *cx, unsigned argc, Value *vp)
+{
+    CallArgs args = CallArgsFromVp(argc, vp);
+
+    found = js::GetOutermostEnclosingFunctionOfScriptedCaller(cx);
+
+    args.rval().set(UndefinedValue());
+    return true;
+}
+
+BEGIN_TEST(test_enclosingFunction)
+{
+    CHECK(JS_DefineFunction(cx, global, "checkEnclosing", CheckEnclosing, 0, 0));
+
+    EXEC("checkEnclosing()");
+    CHECK(found == NULL);
+
+    RootedFunction fun(cx);
+
+    const char s1chars[] = "checkEnclosing()";
+    fun = JS_CompileFunction(cx, global, "s1", 0, NULL, s1chars, strlen(s1chars), __FILE__, __LINE__);
+    CHECK(fun);
+    EXEC("s1()");
+    CHECK(found == JS_GetFunctionScript(cx, fun));
+
+    const char s2chars[] = "return function() { checkEnclosing() }";
+    fun = JS_CompileFunction(cx, global, "s2", 0, NULL, s2chars, strlen(s2chars), __FILE__, __LINE__);
+    CHECK(fun);
+    EXEC("s2()()");
+    CHECK(found == JS_GetFunctionScript(cx, fun));
+
+    const char s3chars[] = "return function() { let (x) { function g() { checkEnclosing() } return g() } }";
+    fun = JS_CompileFunction(cx, global, "s3", 0, NULL, s3chars, strlen(s3chars), __FILE__, __LINE__);
+    CHECK(fun);
+    EXEC("s3()()");
+    CHECK(found == JS_GetFunctionScript(cx, fun));
+
+    return true;
+}
+END_TEST(test_enclosingFunction)
--- a/js/src/jsfriendapi.cpp
+++ b/js/src/jsfriendapi.cpp
@@ -366,16 +366,35 @@ js::IsObjectInContextCompartment(RawObje
 }
 
 JS_FRIEND_API(bool)
 js::IsOriginalScriptFunction(JSFunction *fun)
 {
     return fun->script()->function() == fun;
 }
 
+JS_FRIEND_API(JSScript *)
+js::GetOutermostEnclosingFunctionOfScriptedCaller(JSContext *cx)
+{
+    if (!cx->hasfp())
+        return NULL;
+
+    StackFrame *fp = cx->fp();
+    if (!fp->isFunctionFrame())
+        return NULL;
+
+    JSFunction *scriptedCaller = fp->fun();
+    RootedScript outermost(cx, scriptedCaller->script());
+    for (StaticScopeIter i(scriptedCaller); !i.done(); i++) {
+        if (i.type() == StaticScopeIter::FUNCTION)
+            outermost = i.funScript();
+    }
+    return outermost;
+}
+
 JS_FRIEND_API(JSFunction *)
 js::DefineFunctionWithReserved(JSContext *cx, JSObject *objArg, const char *name, JSNative call,
                                unsigned nargs, unsigned attrs)
 {
     RootedObject obj(cx, objArg);
     JS_THREADSAFE_ASSERT(cx->compartment != cx->runtime->atomsCompartment);
     CHECK_REQUEST(cx);
     assertSameCompartment(cx, obj);
--- a/js/src/jsfriendapi.h
+++ b/js/src/jsfriendapi.h
@@ -393,16 +393,28 @@ JS_FRIEND_API(JSObject *)
 GetGlobalForObjectCrossCompartment(RawObject obj);
 
 JS_FRIEND_API(void)
 NotifyAnimationActivity(RawObject obj);
 
 JS_FRIEND_API(bool)
 IsOriginalScriptFunction(JSFunction *fun);
 
+/*
+ * Return the outermost enclosing function (script) of the scripted caller.
+ * This function returns NULL in several cases:
+ *  - no script is running on the context
+ *  - the caller is in global or eval code
+ * In particular, this function will "stop" its outermost search at eval() and
+ * thus it will really return the outermost enclosing function *since the
+ * innermost eval*.
+ */
+JS_FRIEND_API(JSScript *)
+GetOutermostEnclosingFunctionOfScriptedCaller(JSContext *cx);
+
 JS_FRIEND_API(JSFunction *)
 DefineFunctionWithReserved(JSContext *cx, JSObject *obj, const char *name, JSNative call,
                            unsigned nargs, unsigned attrs);
 
 JS_FRIEND_API(JSFunction *)
 NewFunctionWithReserved(JSContext *cx, JSNative call, unsigned nargs, unsigned flags,
                         JSObject *parent, const char *name);