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 120803 843a3db9c0457acbdf91e257a927ac97ea4a7dc5
parent 120802 7dbb863bed7ca9906af2df81569f0d42f15d2808
child 120804 5fd176fb2acaec963539d0a9ce513aec76b22169
push id1997
push userakeybl@mozilla.com
push dateMon, 07 Jan 2013 21:25:26 +0000
treeherdermozilla-beta@4baf45cdcf21 [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);