Bug 909623 (part 4) - Create js/CallNonGenericMethod.h and vm/CallNonGenericMethod.cpp. r=luke.
authorNicholas Nethercote <nnethercote@mozilla.com>
Mon, 26 Aug 2013 21:39:38 -0700
changeset 144691 3f686383d8070b86692dcf948bdb13e2fb7bc8ea
parent 144690 597530d998cc658cb0436386d7751cf1bcd3a6af
child 144692 9cc68dacc74cbf452e7d4198583e7e697f3c4071
push id25171
push useremorley@mozilla.com
push dateWed, 28 Aug 2013 15:20:13 +0000
treeherdermozilla-central@f280351a238f [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersluke
bugs909623
milestone26.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 909623 (part 4) - Create js/CallNonGenericMethod.h and vm/CallNonGenericMethod.cpp. r=luke.
js/public/CallNonGenericMethod.h
js/src/jsapi.cpp
js/src/jsapi.h
js/src/jsfriendapi.h
js/src/jsproxy.h
js/src/moz.build
js/src/vm/CallNonGenericMethod.cpp
new file mode 100644
--- /dev/null
+++ b/js/public/CallNonGenericMethod.h
@@ -0,0 +1,117 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
+ * vim: set ts=8 sts=4 et sw=4 tw=99:
+ * 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/. */
+
+#ifndef js_CallNonGenericMethod_h
+#define js_CallNonGenericMethod_h
+
+#include "jstypes.h"
+
+#include "js/CallArgs.h"
+
+namespace JS {
+
+// Returns true if |v| is considered an acceptable this-value.
+typedef bool (*IsAcceptableThis)(Handle<Value> v);
+
+// Implements the guts of a method; guaranteed to be provided an acceptable
+// this-value, as determined by a corresponding IsAcceptableThis method.
+typedef bool (*NativeImpl)(JSContext *cx, CallArgs args);
+
+namespace detail {
+
+// DON'T CALL THIS DIRECTLY.  It's for use only by CallNonGenericMethod!
+extern JS_PUBLIC_API(bool)
+CallMethodIfWrapped(JSContext *cx, IsAcceptableThis test, NativeImpl impl, CallArgs args);
+
+} // namespace detail
+
+// Methods usually act upon |this| objects only from a single global object and
+// compartment.  Sometimes, however, a method must act upon |this| values from
+// multiple global objects or compartments.  In such cases the |this| value a
+// method might see will be wrapped, such that various access to the object --
+// to its class, its private data, its reserved slots, and so on -- will not
+// work properly without entering that object's compartment.  This method
+// implements a solution to this problem.
+//
+// To implement a method that accepts |this| values from multiple compartments,
+// define two functions.  The first function matches the IsAcceptableThis type
+// and indicates whether the provided value is an acceptable |this| for the
+// method; it must be a pure function only of its argument.
+//
+//   static JSClass AnswerClass = { ... };
+//
+//   static bool
+//   IsAnswerObject(const Value &v)
+//   {
+//       if (!v.isObject())
+//           return false;
+//       return JS_GetClass(&v.toObject()) == &AnswerClass;
+//   }
+//
+// The second function implements the NativeImpl signature and defines the
+// behavior of the method when it is provided an acceptable |this| value.
+// Aside from some typing niceties -- see the CallArgs interface for details --
+// its interface is the same as that of JSNative.
+//
+//   static bool
+//   answer_getAnswer_impl(JSContext *cx, JS::CallArgs args)
+//   {
+//       args.rval().setInt32(42);
+//       return true;
+//   }
+//
+// The implementation function is guaranteed to be called *only* with a |this|
+// value which is considered acceptable.
+//
+// Now to implement the actual method, write a JSNative that calls the method
+// declared below, passing the appropriate template and runtime arguments.
+//
+//   static bool
+//   answer_getAnswer(JSContext *cx, unsigned argc, JS::Value *vp)
+//   {
+//       JS::CallArgs args = JS::CallArgsFromVp(argc, vp);
+//       return JS::CallNonGenericMethod<IsAnswerObject, answer_getAnswer_impl>(cx, args);
+//   }
+//
+// Note that, because they are used as template arguments, the predicate
+// and implementation functions must have external linkage. (This is
+// unfortunate, but GCC wasn't inlining things as one would hope when we
+// passed them as function arguments.)
+//
+// JS::CallNonGenericMethod will test whether |args.thisv()| is acceptable.  If
+// it is, it will call the provided implementation function, which will return
+// a value and indicate success.  If it is not, it will attempt to unwrap
+// |this| and call the implementation function on the unwrapped |this|.  If
+// that succeeds, all well and good.  If it doesn't succeed, a TypeError will
+// be thrown.
+//
+// Note: JS::CallNonGenericMethod will only work correctly if it's called in
+//       tail position in a JSNative.  Do not call it from any other place.
+//
+template<IsAcceptableThis Test, NativeImpl Impl>
+JS_ALWAYS_INLINE bool
+CallNonGenericMethod(JSContext *cx, CallArgs args)
+{
+    HandleValue thisv = args.thisv();
+    if (Test(thisv))
+        return Impl(cx, args);
+
+    return detail::CallMethodIfWrapped(cx, Test, Impl, args);
+}
+
+JS_ALWAYS_INLINE bool
+CallNonGenericMethod(JSContext *cx, IsAcceptableThis Test, NativeImpl Impl, CallArgs args)
+{
+    HandleValue thisv = args.thisv();
+    if (Test(thisv))
+        return Impl(cx, args);
+
+    return detail::CallMethodIfWrapped(cx, Test, Impl, args);
+}
+
+} // namespace JS
+
+#endif /* js_CallNonGenericMethod_h */
--- a/js/src/jsapi.cpp
+++ b/js/src/jsapi.cpp
@@ -95,33 +95,16 @@ using namespace js::gc;
 using namespace js::types;
 
 using mozilla::Maybe;
 using mozilla::PodCopy;
 using mozilla::PodZero;
 
 using js::frontend::Parser;
 
-bool
-JS::detail::CallMethodIfWrapped(JSContext *cx, IsAcceptableThis test, NativeImpl impl,
-                               CallArgs args)
-{
-    HandleValue thisv = args.thisv();
-    JS_ASSERT(!test(thisv));
-
-    if (thisv.isObject()) {
-        JSObject &thisObj = args.thisv().toObject();
-        if (thisObj.is<ProxyObject>())
-            return Proxy::nativeCall(cx, test, impl, args);
-    }
-
-    ReportIncompatible(cx, args);
-    return false;
-}
-
 #ifdef HAVE_VA_LIST_AS_ARRAY
 #define JS_ADDRESSOF_VA_LIST(ap) ((va_list *)(ap))
 #else
 #define JS_ADDRESSOF_VA_LIST(ap) (&(ap))
 #endif
 
 const jsid voidIdValue = JSID_VOID;
 const jsid emptyIdValue = JSID_EMPTY;
--- a/js/src/jsapi.h
+++ b/js/src/jsapi.h
@@ -19,16 +19,17 @@
 #include <stddef.h>
 #include <stdint.h>
 #include <stdio.h>
 
 #include "jsalloc.h"
 #include "jspubtd.h"
 
 #include "js/CallArgs.h"
+#include "js/CallNonGenericMethod.h"
 #include "js/Class.h"
 #include "js/HashTable.h"
 #include "js/Id.h"
 #include "js/RootingAPI.h"
 #include "js/Utility.h"
 #include "js/Value.h"
 #include "js/Vector.h"
 
@@ -641,118 +642,16 @@ class JS_PUBLIC_API(CustomAutoRooter) : 
   protected:
     /* Supplied by derived class to trace roots. */
     virtual void trace(JSTracer *trc) = 0;
 
   private:
     MOZ_DECL_USE_GUARD_OBJECT_NOTIFIER
 };
 
-/* Returns true if |v| is considered an acceptable this-value. */
-typedef bool (*IsAcceptableThis)(JS::Handle<JS::Value> v);
-
-/*
- * Implements the guts of a method; guaranteed to be provided an acceptable
- * this-value, as determined by a corresponding IsAcceptableThis method.
- */
-typedef bool (*NativeImpl)(JSContext *cx, CallArgs args);
-
-namespace detail {
-
-/* DON'T CALL THIS DIRECTLY.  It's for use only by CallNonGenericMethod! */
-extern JS_PUBLIC_API(bool)
-CallMethodIfWrapped(JSContext *cx, IsAcceptableThis test, NativeImpl impl, CallArgs args);
-
-} /* namespace detail */
-
-/*
- * Methods usually act upon |this| objects only from a single global object and
- * compartment.  Sometimes, however, a method must act upon |this| values from
- * multiple global objects or compartments.  In such cases the |this| value a
- * method might see will be wrapped, such that various access to the object --
- * to its class, its private data, its reserved slots, and so on -- will not
- * work properly without entering that object's compartment.  This method
- * implements a solution to this problem.
- *
- * To implement a method that accepts |this| values from multiple compartments,
- * define two functions.  The first function matches the IsAcceptableThis type
- * and indicates whether the provided value is an acceptable |this| for the
- * method; it must be a pure function only of its argument.
- *
- *   static JSClass AnswerClass = { ... };
- *
- *   static bool
- *   IsAnswerObject(const Value &v)
- *   {
- *       if (!v.isObject())
- *           return false;
- *       return JS_GetClass(&v.toObject()) == &AnswerClass;
- *   }
- *
- * The second function implements the NativeImpl signature and defines the
- * behavior of the method when it is provided an acceptable |this| value.
- * Aside from some typing niceties -- see the CallArgs interface for details --
- * its interface is the same as that of JSNative.
- *
- *   static bool
- *   answer_getAnswer_impl(JSContext *cx, JS::CallArgs args)
- *   {
- *       args.rval().setInt32(42);
- *       return true;
- *   }
- *
- * The implementation function is guaranteed to be called *only* with a |this|
- * value which is considered acceptable.
- *
- * Now to implement the actual method, write a JSNative that calls the method
- * declared below, passing the appropriate template and runtime arguments.
- *
- *   static bool
- *   answer_getAnswer(JSContext *cx, unsigned argc, JS::Value *vp)
- *   {
- *       JS::CallArgs args = JS::CallArgsFromVp(argc, vp);
- *       return JS::CallNonGenericMethod<IsAnswerObject, answer_getAnswer_impl>(cx, args);
- *   }
- *
- * Note that, because they are used as template arguments, the predicate
- * and implementation functions must have external linkage. (This is
- * unfortunate, but GCC wasn't inlining things as one would hope when we
- * passed them as function arguments.)
- *
- * JS::CallNonGenericMethod will test whether |args.thisv()| is acceptable.  If
- * it is, it will call the provided implementation function, which will return
- * a value and indicate success.  If it is not, it will attempt to unwrap
- * |this| and call the implementation function on the unwrapped |this|.  If
- * that succeeds, all well and good.  If it doesn't succeed, a TypeError will
- * be thrown.
- *
- * Note: JS::CallNonGenericMethod will only work correctly if it's called in
- *       tail position in a JSNative.  Do not call it from any other place.
- */
-template<IsAcceptableThis Test, NativeImpl Impl>
-JS_ALWAYS_INLINE bool
-CallNonGenericMethod(JSContext *cx, CallArgs args)
-{
-    HandleValue thisv = args.thisv();
-    if (Test(thisv))
-        return Impl(cx, args);
-
-    return detail::CallMethodIfWrapped(cx, Test, Impl, args);
-}
-
-JS_ALWAYS_INLINE bool
-CallNonGenericMethod(JSContext *cx, IsAcceptableThis Test, NativeImpl Impl, CallArgs args)
-{
-    HandleValue thisv = args.thisv();
-    if (Test(thisv))
-        return Impl(cx, args);
-
-    return detail::CallMethodIfWrapped(cx, Test, Impl, args);
-}
-
 }  /* namespace JS */
 
 /************************************************************************/
 
 struct JSFreeOp {
   private:
     JSRuntime   *runtime_;
 
--- a/js/src/jsfriendapi.h
+++ b/js/src/jsfriendapi.h
@@ -11,16 +11,17 @@
 #include "mozilla/MemoryReporting.h"
 #endif
 
 #include "jsapi.h"
 #include "jsbytecode.h"
 #include "jspubtd.h"
 
 #include "js/CallArgs.h"
+#include "js/CallNonGenericMethod.h"
 
 /*
  * This macro checks if the stack pointer has exceeded a given limit. If
  * |tolerance| is non-zero, it returns true only if the stack pointer has
  * exceeded the limit by more than |tolerance| bytes.
  */
 #if JS_STACK_GROWTH_DIRECTION > 0
 # define JS_CHECK_STACK_SIZE_WITH_TOLERANCE(limit, sp, tolerance)  \
--- a/js/src/jsproxy.h
+++ b/js/src/jsproxy.h
@@ -7,16 +7,18 @@
 #ifndef jsproxy_h
 #define jsproxy_h
 
 #include "mozilla/Maybe.h"
 
 #include "jsapi.h"
 #include "jsfriendapi.h"
 
+#include "js/CallNonGenericMethod.h"
+
 namespace js {
 
 class RegExpGuard;
 class JS_FRIEND_API(Wrapper);
 
 /*
  * A proxy is a JSObject that implements generic behavior by providing custom
  * implementations for each object trap. The implementation for each trap is
--- a/js/src/moz.build
+++ b/js/src/moz.build
@@ -54,16 +54,17 @@ if CONFIG['HAVE_DTRACE']:
     ]
 
 # If you add a header here, add it to js/src/jsapi-tests/testIntTypesABI.cpp so
 # that we ensure we don't over-expose our internal integer typedefs.  Note that
 # LegacyIntTypes.h below is deliberately exempted from this requirement.
 EXPORTS.js += [
     '../public/Anchor.h',
     '../public/CallArgs.h',
+    '../public/CallNonGenericMethod.h',
     '../public/CharacterEncoding.h',
     '../public/Class.h',
     '../public/Date.h',
     '../public/GCAPI.h',
     '../public/HashTable.h',
     '../public/HeapAPI.h',
     '../public/Id.h',
     '../public/IdForward.h',
@@ -80,16 +81,17 @@ EXPORTS.js += [
     '../public/Vector.h',
 ]
 
 CPP_SOURCES += [
     'ArgumentsObject.cpp',
     'BinaryData.cpp',
     'BytecodeCompiler.cpp',
     'BytecodeEmitter.cpp',
+    'CallNonGenericMethod.cpp',
     'CharacterEncoding.cpp',
     'DateTime.cpp',
     'Debugger.cpp',
     'Eval.cpp',
     'ExecutableAllocator.cpp',
     'FoldConstants.cpp',
     'ForkJoin.cpp',
     'GlobalObject.cpp',
new file mode 100644
--- /dev/null
+++ b/js/src/vm/CallNonGenericMethod.cpp
@@ -0,0 +1,32 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
+ * vim: set ts=8 sts=4 et sw=4 tw=99:
+ * 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 "js/CallNonGenericMethod.h"
+
+#include "jsfun.h"
+#include "jsobj.h"
+
+#include "vm/ProxyObject.h"
+
+using namespace js;
+
+bool
+JS::detail::CallMethodIfWrapped(JSContext *cx, IsAcceptableThis test, NativeImpl impl,
+                                CallArgs args)
+{
+    HandleValue thisv = args.thisv();
+    JS_ASSERT(!test(thisv));
+
+    if (thisv.isObject()) {
+        JSObject &thisObj = args.thisv().toObject();
+        if (thisObj.is<ProxyObject>())
+            return Proxy::nativeCall(cx, test, impl, args);
+    }
+
+    ReportIncompatible(cx, args);
+    return false;
+}
+