[gjs/gnome-3-34] cairo: Add type checking to Path, Pattern, and Surface



commit 96412af103c43bba80ce424e975b20d6636bba17
Author: Philip Chimento <philip chimento gmail com>
Date:   Sat Oct 26 22:52:35 2019 -0700

    cairo: Add type checking to Path, Pattern, and Surface
    
    Previously, passing the wrong kind of object where a Cairo.Path,
    Cairo.Pattern (subclass), or Cairo.Surface (subclass) was expected,
    would crash. This adds type checking to avoid these crashes.
    
    The normal way to do this would be with JS_HasInstance(), but since
    Cairo.Pattern and Cairo.Surface are abstract classes, that won't work.
    JS_HasInstance() takes a constructor, and abstract classes don't have a
    constructor. Instead, we have to check the passed-in object's prototype
    chain. It turns out there isn't a JSAPI function to do that, so we add
    gjs_object_in_prototype_chain() for this purpose.
    
    In addition we add missing error checking in a few places.
    
    Closes: #49.

 gjs/jsapi-class.h                 |  5 ++++
 gjs/jsapi-dynamic-class.cpp       | 35 +++++++++++++++++++++++++
 installed-tests/js/testCairo.js   | 22 ++++++++++++++++
 modules/cairo-context.cpp         | 47 ++++++++++++---------------------
 modules/cairo-gradient.cpp        | 11 +++++---
 modules/cairo-image-surface.cpp   | 24 +++++++++++------
 modules/cairo-path.cpp            | 30 ++++++++++-----------
 modules/cairo-pattern.cpp         | 39 ++++++++++++++++-----------
 modules/cairo-private.h           | 18 ++++++-------
 modules/cairo-surface-pattern.cpp | 32 ++++++++++++++---------
 modules/cairo-surface.cpp         | 55 +++++++++++++++++++++++++--------------
 11 files changed, 204 insertions(+), 114 deletions(-)
---
diff --git a/gjs/jsapi-class.h b/gjs/jsapi-class.h
index c090336f..757a65dd 100644
--- a/gjs/jsapi-class.h
+++ b/gjs/jsapi-class.h
@@ -325,4 +325,9 @@ GJS_DEFINE_PROTO_FUNCS_WITH_PARENT(cname, no_parent)
 GJS_USE
 JS::Value gjs_dynamic_property_private_slot(JSObject *accessor_obj);
 
+GJS_JSAPI_RETURN_CONVENTION
+bool gjs_object_in_prototype_chain(JSContext* cx, JS::HandleObject proto,
+                                   JS::HandleObject check_obj,
+                                   bool* is_in_chain);
+
 #endif  // GJS_JSAPI_CLASS_H_
diff --git a/gjs/jsapi-dynamic-class.cpp b/gjs/jsapi-dynamic-class.cpp
index 43365bb3..478aae02 100644
--- a/gjs/jsapi-dynamic-class.cpp
+++ b/gjs/jsapi-dynamic-class.cpp
@@ -280,3 +280,38 @@ gjs_dynamic_property_private_slot(JSObject *accessor_obj)
     return js::GetFunctionNativeReserved(accessor_obj,
                                          DYNAMIC_PROPERTY_PRIVATE_SLOT);
 }
+
+/**
+ * gjs_object_in_prototype_chain:
+ * @cx:
+ * @proto: The prototype which we are checking if @check_obj has in its chain
+ * @check_obj: The object to check
+ * @is_in_chain: (out): Whether @check_obj has @proto in its prototype chain
+ *
+ * Similar to JS_HasInstance() but takes into account abstract classes defined
+ * with JS_InitClass(), which JS_HasInstance() does not. Abstract classes don't
+ * have constructors, and JS_HasInstance() requires a constructor.
+ *
+ * Returns: false if an exception was thrown, true otherwise.
+ */
+bool gjs_object_in_prototype_chain(JSContext* cx, JS::HandleObject proto,
+                                   JS::HandleObject check_obj,
+                                   bool* is_in_chain) {
+    JS::RootedObject object_prototype(cx, JS_GetObjectPrototype(cx, check_obj));
+    if (!object_prototype)
+        return false;
+
+    JS::RootedObject proto_iter(cx);
+    if (!JS_GetPrototype(cx, check_obj, &proto_iter))
+        return false;
+    while (proto_iter != object_prototype) {
+        if (proto_iter == proto) {
+            *is_in_chain = true;
+            return true;
+        }
+        if (!JS_GetPrototype(cx, proto_iter, &proto_iter))
+            return false;
+    }
+    *is_in_chain = false;
+    return true;
+}
diff --git a/installed-tests/js/testCairo.js b/installed-tests/js/testCairo.js
index f3d6fd41..08686960 100644
--- a/installed-tests/js/testCairo.js
+++ b/installed-tests/js/testCairo.js
@@ -177,6 +177,13 @@ describe('Cairo', function () {
         });
     });
 
+    describe('pattern', function () {
+        it('has typechecks', function () {
+            expect(() => cr.setSource({})).toThrow();
+            expect(() => cr.setSource(surface)).toThrow();
+        });
+    });
+
     describe('solid pattern', function () {
         it('can be created from RGB static method', function () {
             let p1 = Cairo.SolidPattern.createRGB(1, 2, 3);
@@ -220,6 +227,21 @@ describe('Cairo', function () {
         });
     });
 
+    describe('path', function () {
+        it('has typechecks', function () {
+            expect(() => cr.appendPath({})).toThrow();
+            expect(() => cr.appendPath(surface)).toThrow();
+        });
+    });
+
+    describe('surface', function () {
+        it('has typechecks', function () {
+            expect(() => new Cairo.Context({})).toThrow();
+            const pattern = new Cairo.SurfacePattern(surface);
+            expect(() => new Cairo.Context(pattern)).toThrow();
+        });
+    });
+
     describe('GI test suite', function () {
         describe('for context', function () {
             it('can be marshalled as a return value', function () {
diff --git a/modules/cairo-context.cpp b/modules/cairo-context.cpp
index 963d7663..e7b79632 100644
--- a/modules/cairo-context.cpp
+++ b/modules/cairo-context.cpp
@@ -268,7 +268,6 @@ _gjs_cairo_context_construct_internal(JSContext       *context,
 GJS_NATIVE_CONSTRUCTOR_DECLARE(cairo_context)
 {
     GJS_NATIVE_CONSTRUCTOR_VARIABLES(cairo_context)
-    cairo_surface_t *surface;
     cairo_t *cr;
 
     GJS_NATIVE_CONSTRUCTOR_PRELUDE(cairo_context);
@@ -278,11 +277,10 @@ GJS_NATIVE_CONSTRUCTOR_DECLARE(cairo_context)
                              "surface", &surface_wrapper))
         return false;
 
-    surface = gjs_cairo_surface_get_surface(context, surface_wrapper);
-    if (!surface) {
-        gjs_throw(context, "first argument to Context() should be a surface");
+    cairo_surface_t* surface =
+        gjs_cairo_surface_get_surface(context, surface_wrapper);
+    if (!surface)
         return false;
-    }
 
     cr = cairo_create(surface);
 
@@ -415,18 +413,15 @@ appendPath_func(JSContext *context,
 {
     GJS_GET_PRIV(context, argc, vp, argv, obj, GjsCairoContext, priv);
     JS::RootedObject path_wrapper(context);
-    cairo_path_t *path;
     cairo_t* cr = priv ? priv->cr : nullptr;
 
     if (!gjs_parse_call_args(context, "path", argv, "o",
                              "path", &path_wrapper))
         return false;
 
-    path = gjs_cairo_path_get_path(context, path_wrapper);
-    if (!path) {
-        gjs_throw(context, "first argument to appendPath() should be a path");
+    cairo_path_t* path = gjs_cairo_path_get_path(context, path_wrapper);
+    if (!path)
         return false;
-    }
 
     cairo_append_path(cr, path);
     argv.rval().setUndefined();
@@ -477,18 +472,16 @@ mask_func(JSContext *context,
 {
     GJS_GET_PRIV(context, argc, vp, argv, obj, GjsCairoContext, priv);
     JS::RootedObject pattern_wrapper(context);
-    cairo_pattern_t *pattern;
     cairo_t* cr = priv ? priv->cr : nullptr;
 
     if (!gjs_parse_call_args(context, "mask", argv, "o",
                              "pattern", &pattern_wrapper))
         return false;
 
-    pattern = gjs_cairo_pattern_get_pattern(context, pattern_wrapper);
-    if (!pattern) {
-        gjs_throw(context, "first argument to mask() should be a pattern");
+    cairo_pattern_t* pattern =
+        gjs_cairo_pattern_get_pattern(context, pattern_wrapper);
+    if (!pattern)
         return false;
-    }
 
     cairo_mask(cr, pattern);
 
@@ -508,7 +501,6 @@ maskSurface_func(JSContext *context,
     GJS_GET_PRIV(context, argc, vp, argv, obj, GjsCairoContext, priv);
     JS::RootedObject surface_wrapper(context);
     double x, y;
-    cairo_surface_t *surface;
     cairo_t* cr = priv ? priv->cr : nullptr;
 
     if (!gjs_parse_call_args(context, "maskSurface", argv, "off",
@@ -517,11 +509,10 @@ maskSurface_func(JSContext *context,
                              "y", &y))
         return false;
 
-    surface = gjs_cairo_surface_get_surface(context, surface_wrapper);
-    if (!surface) {
-        gjs_throw(context, "first argument to maskSurface() should be a surface");
+    cairo_surface_t* surface =
+        gjs_cairo_surface_get_surface(context, surface_wrapper);
+    if (!surface)
         return false;
-    }
 
     cairo_mask_surface(cr, surface, x, y);
 
@@ -599,18 +590,16 @@ setSource_func(JSContext *context,
 {
     GJS_GET_PRIV(context, argc, vp, argv, obj, GjsCairoContext, priv);
     JS::RootedObject pattern_wrapper(context);
-    cairo_pattern_t *pattern;
     cairo_t* cr = priv ? priv->cr : nullptr;
 
     if (!gjs_parse_call_args(context, "setSource", argv, "o",
                              "pattern", &pattern_wrapper))
         return false;
 
-    pattern = gjs_cairo_pattern_get_pattern(context, pattern_wrapper);
-    if (!pattern) {
-        gjs_throw(context, "first argument to setSource() should be a pattern");
+    cairo_pattern_t* pattern =
+        gjs_cairo_pattern_get_pattern(context, pattern_wrapper);
+    if (!pattern)
         return false;
-    }
 
     cairo_set_source(cr, pattern);
 
@@ -631,7 +620,6 @@ setSourceSurface_func(JSContext *context,
     GJS_GET_PRIV(context, argc, vp, argv, obj, GjsCairoContext, priv);
     JS::RootedObject surface_wrapper(context);
     double x, y;
-    cairo_surface_t *surface;
     cairo_t* cr = priv ? priv->cr : nullptr;
 
     if (!gjs_parse_call_args(context, "setSourceSurface", argv, "off",
@@ -640,11 +628,10 @@ setSourceSurface_func(JSContext *context,
                              "y", &y))
         return false;
 
-    surface = gjs_cairo_surface_get_surface(context, surface_wrapper);
-    if (!surface) {
-        gjs_throw(context, "first argument to setSourceSurface() should be a surface");
+    cairo_surface_t* surface =
+        gjs_cairo_surface_get_surface(context, surface_wrapper);
+    if (!surface)
         return false;
-    }
 
     cairo_set_source_surface(cr, surface, x, y);
 
diff --git a/modules/cairo-gradient.cpp b/modules/cairo-gradient.cpp
index e6f7461b..05c086d5 100644
--- a/modules/cairo-gradient.cpp
+++ b/modules/cairo-gradient.cpp
@@ -56,7 +56,6 @@ addColorStopRGB_func(JSContext *context,
 {
     GJS_GET_THIS(context, argc, vp, argv, obj);
     double offset, red, green, blue;
-    cairo_pattern_t *pattern;
 
     if (!gjs_parse_call_args(context, "addColorStopRGB", argv, "ffff",
                              "offset", &offset,
@@ -65,7 +64,9 @@ addColorStopRGB_func(JSContext *context,
                              "blue", &blue))
         return false;
 
-    pattern = gjs_cairo_pattern_get_pattern(context, obj);
+    cairo_pattern_t* pattern = gjs_cairo_pattern_get_pattern(context, obj);
+    if (!pattern)
+        return false;
 
     cairo_pattern_add_color_stop_rgb(pattern, offset, red, green, blue);
 
@@ -84,7 +85,6 @@ addColorStopRGBA_func(JSContext *context,
 {
     GJS_GET_THIS(context, argc, vp, argv, obj);
     double offset, red, green, blue, alpha;
-    cairo_pattern_t *pattern;
 
     if (!gjs_parse_call_args(context, "addColorStopRGBA", argv, "fffff",
                              "offset", &offset,
@@ -94,7 +94,10 @@ addColorStopRGBA_func(JSContext *context,
                              "alpha", &alpha))
         return false;
 
-    pattern = gjs_cairo_pattern_get_pattern(context, obj);
+    cairo_pattern_t* pattern = gjs_cairo_pattern_get_pattern(context, obj);
+    if (!pattern)
+        return false;
+
     cairo_pattern_add_color_stop_rgba(pattern, offset, red, green, blue, alpha);
 
     if (!gjs_cairo_check_status(context, cairo_pattern_status(pattern), "pattern"))
diff --git a/modules/cairo-image-surface.cpp b/modules/cairo-image-surface.cpp
index f2382e8b..590f2df9 100644
--- a/modules/cairo-image-surface.cpp
+++ b/modules/cairo-image-surface.cpp
@@ -117,7 +117,6 @@ getFormat_func(JSContext *context,
                JS::Value *vp)
 {
     GJS_GET_THIS(context, argc, vp, rec, obj);
-    cairo_surface_t *surface;
     cairo_format_t format;
 
     if (argc > 1) {
@@ -125,7 +124,10 @@ getFormat_func(JSContext *context,
         return false;
     }
 
-    surface = gjs_cairo_surface_get_surface(context, obj);
+    cairo_surface_t* surface = gjs_cairo_surface_get_surface(context, obj);
+    if (!surface)
+        return false;
+
     format = cairo_image_surface_get_format(surface);
 
     if (!gjs_cairo_check_status(context, cairo_surface_status(surface), "surface"))
@@ -142,7 +144,6 @@ getWidth_func(JSContext *context,
               JS::Value *vp)
 {
     GJS_GET_THIS(context, argc, vp, rec, obj);
-    cairo_surface_t *surface;
     int width;
 
     if (argc > 1) {
@@ -150,7 +151,10 @@ getWidth_func(JSContext *context,
         return false;
     }
 
-    surface = gjs_cairo_surface_get_surface(context, obj);
+    cairo_surface_t* surface = gjs_cairo_surface_get_surface(context, obj);
+    if (!surface)
+        return false;
+
     width = cairo_image_surface_get_width(surface);
 
     if (!gjs_cairo_check_status(context, cairo_surface_status(surface), "surface"))
@@ -167,7 +171,6 @@ getHeight_func(JSContext *context,
                JS::Value *vp)
 {
     GJS_GET_THIS(context, argc, vp, rec, obj);
-    cairo_surface_t *surface;
     int height;
 
     if (argc > 1) {
@@ -175,7 +178,10 @@ getHeight_func(JSContext *context,
         return false;
     }
 
-    surface = gjs_cairo_surface_get_surface(context, obj);
+    cairo_surface_t* surface = gjs_cairo_surface_get_surface(context, obj);
+    if (!surface)
+        return false;
+
     height = cairo_image_surface_get_height(surface);
 
     if (!gjs_cairo_check_status(context, cairo_surface_status(surface), "surface"))
@@ -192,7 +198,6 @@ getStride_func(JSContext *context,
                JS::Value *vp)
 {
     GJS_GET_THIS(context, argc, vp, rec, obj);
-    cairo_surface_t *surface;
     int stride;
 
     if (argc > 1) {
@@ -200,7 +205,10 @@ getStride_func(JSContext *context,
         return false;
     }
 
-    surface = gjs_cairo_surface_get_surface(context, obj);
+    cairo_surface_t* surface = gjs_cairo_surface_get_surface(context, obj);
+    if (!surface)
+        return false;
+
     stride = cairo_image_surface_get_stride(surface);
 
     if (!gjs_cairo_check_status(context, cairo_surface_status(surface), "surface"))
diff --git a/modules/cairo-path.cpp b/modules/cairo-path.cpp
index a5052515..62ed78f6 100644
--- a/modules/cairo-path.cpp
+++ b/modules/cairo-path.cpp
@@ -99,26 +99,26 @@ gjs_cairo_path_from_path(JSContext    *context,
     return object;
 }
 
-
 /**
  * gjs_cairo_path_get_path:
- * @context: the context
- * @object: path wrapper
+ * @cx: the context
+ * @path_wrapper: path wrapper
  *
  * Returns: the path attached to the wrapper.
- *
  */
-cairo_path_t *
-gjs_cairo_path_get_path(JSContext *context,
-                        JSObject  *object)
-{
-    GjsCairoPath *priv;
-
-    g_return_val_if_fail(context, nullptr);
-    g_return_val_if_fail(object, nullptr);
-
-    priv = (GjsCairoPath*) JS_GetPrivate(object);
-    if (!priv)
+cairo_path_t* gjs_cairo_path_get_path(JSContext* cx,
+                                      JS::HandleObject path_wrapper) {
+    g_return_val_if_fail(cx, nullptr);
+    g_return_val_if_fail(path_wrapper, nullptr);
+
+    auto* priv = static_cast<GjsCairoPath*>(JS_GetInstancePrivate(
+        cx, path_wrapper, &gjs_cairo_path_class, nullptr));
+    if (!priv) {
+        gjs_throw(cx, "Expected Cairo.Path but got %s",
+                  JS_GetClass(path_wrapper)->name);
         return nullptr;
+    }
+
+    g_assert(priv->path);
     return priv->path;
 }
diff --git a/modules/cairo-pattern.cpp b/modules/cairo-pattern.cpp
index c2a307dc..c9adf6c1 100644
--- a/modules/cairo-pattern.cpp
+++ b/modules/cairo-pattern.cpp
@@ -66,7 +66,6 @@ getType_func(JSContext *context,
              JS::Value *vp)
 {
     GJS_GET_THIS(context, argc, vp, rec, obj);
-    cairo_pattern_t *pattern;
     cairo_pattern_type_t type;
 
     if (argc > 1) {
@@ -74,7 +73,10 @@ getType_func(JSContext *context,
         return false;
     }
 
-    pattern = gjs_cairo_pattern_get_pattern(context, obj);
+    cairo_pattern_t* pattern = gjs_cairo_pattern_get_pattern(context, obj);
+    if (!pattern)
+        return false;
+
     type = cairo_pattern_get_type(pattern);
 
     if (!gjs_cairo_check_status(context, cairo_pattern_status(pattern), "pattern"))
@@ -183,25 +185,32 @@ gjs_cairo_pattern_from_pattern(JSContext       *context,
 
 /**
  * gjs_cairo_pattern_get_pattern:
- * @context: the context
- * @object: pattern wrapper
- *
- * Returns: the pattern attaches to the wrapper.
+ * @cx: the context
+ * @pattern_wrapper: pattern wrapper
  *
+ * Returns: the pattern attached to the wrapper.
  */
-cairo_pattern_t *
-gjs_cairo_pattern_get_pattern(JSContext *context,
-                              JSObject  *object)
-{
-    GjsCairoPattern *priv;
+cairo_pattern_t* gjs_cairo_pattern_get_pattern(
+    JSContext* cx, JS::HandleObject pattern_wrapper) {
+    g_return_val_if_fail(cx, nullptr);
+    g_return_val_if_fail(pattern_wrapper, nullptr);
 
-    g_return_val_if_fail(context, nullptr);
-    g_return_val_if_fail(object, nullptr);
+    JS::RootedObject proto(cx, gjs_cairo_pattern_get_proto(cx));
+
+    bool is_pattern_subclass = false;
+    if (!gjs_object_in_prototype_chain(cx, proto, pattern_wrapper,
+                                       &is_pattern_subclass))
+        return nullptr;
+    if (!is_pattern_subclass) {
+        gjs_throw(cx, "Expected Cairo.Pattern but got %s",
+                  JS_GetClass(pattern_wrapper)->name);
+        return nullptr;
+    }
 
-    priv = (GjsCairoPattern*) JS_GetPrivate(object);
+    auto* priv = static_cast<GjsCairoPattern*>(JS_GetPrivate(pattern_wrapper));
     if (!priv)
         return nullptr;
 
+    g_assert(priv->pattern);
     return priv->pattern;
 }
-
diff --git a/modules/cairo-private.h b/modules/cairo-private.h
index 7634c76f..29083901 100644
--- a/modules/cairo-private.h
+++ b/modules/cairo-private.h
@@ -65,9 +65,9 @@ bool gjs_cairo_path_define_proto(JSContext              *cx,
 GJS_JSAPI_RETURN_CONVENTION
 JSObject *       gjs_cairo_path_from_path               (JSContext       *context,
                                                          cairo_path_t    *path);
-GJS_USE
-cairo_path_t *   gjs_cairo_path_get_path                (JSContext       *context,
-                                                         JSObject        *path_wrapper);
+GJS_JSAPI_RETURN_CONVENTION
+cairo_path_t* gjs_cairo_path_get_path(JSContext* cx,
+                                      JS::HandleObject path_wrapper);
 
 /* surface */
 GJS_USE
@@ -86,9 +86,9 @@ void             gjs_cairo_surface_finalize_surface     (JSFreeOp        *fop,
 GJS_JSAPI_RETURN_CONVENTION
 JSObject *       gjs_cairo_surface_from_surface         (JSContext       *context,
                                                          cairo_surface_t *surface);
-GJS_USE
-cairo_surface_t* gjs_cairo_surface_get_surface          (JSContext       *context,
-                                                         JSObject        *object);
+GJS_JSAPI_RETURN_CONVENTION
+cairo_surface_t* gjs_cairo_surface_get_surface(
+    JSContext* cx, JS::HandleObject surface_wrapper);
 
 /* image surface */
 GJS_JSAPI_RETURN_CONVENTION
@@ -153,9 +153,9 @@ void             gjs_cairo_pattern_finalize_pattern     (JSFreeOp        *fop,
 GJS_JSAPI_RETURN_CONVENTION
 JSObject*        gjs_cairo_pattern_from_pattern         (JSContext       *context,
                                                          cairo_pattern_t *pattern);
-GJS_USE
-cairo_pattern_t* gjs_cairo_pattern_get_pattern          (JSContext       *context,
-                                                         JSObject        *object);
+GJS_JSAPI_RETURN_CONVENTION
+cairo_pattern_t* gjs_cairo_pattern_get_pattern(
+    JSContext* cx, JS::HandleObject pattern_wrapper);
 
 /* gradient */
 GJS_USE
diff --git a/modules/cairo-surface-pattern.cpp b/modules/cairo-surface-pattern.cpp
index 613b9049..d8dc7e2f 100644
--- a/modules/cairo-surface-pattern.cpp
+++ b/modules/cairo-surface-pattern.cpp
@@ -40,7 +40,6 @@ GJS_DEFINE_PROTO_WITH_PARENT("SurfacePattern", cairo_surface_pattern,
 GJS_NATIVE_CONSTRUCTOR_DECLARE(cairo_surface_pattern)
 {
     GJS_NATIVE_CONSTRUCTOR_VARIABLES(cairo_surface_pattern)
-    cairo_surface_t *surface;
     cairo_pattern_t *pattern;
 
     GJS_NATIVE_CONSTRUCTOR_PRELUDE(cairo_surface_pattern);
@@ -50,11 +49,10 @@ GJS_NATIVE_CONSTRUCTOR_DECLARE(cairo_surface_pattern)
                              "surface", &surface_wrapper))
         return false;
 
-    surface = gjs_cairo_surface_get_surface(context, surface_wrapper);
-    if (!surface) {
-        gjs_throw(context, "first argument to SurfacePattern() should be a surface");
+    cairo_surface_t* surface =
+        gjs_cairo_surface_get_surface(context, surface_wrapper);
+    if (!surface)
         return false;
-    }
 
     pattern = cairo_pattern_create_for_surface(surface);
 
@@ -89,13 +87,15 @@ setExtend_func(JSContext *context,
 {
     GJS_GET_THIS(context, argc, vp, argv, obj);
     cairo_extend_t extend;
-    cairo_pattern_t *pattern;
 
     if (!gjs_parse_call_args(context, "setExtend", argv, "i",
                              "extend", &extend))
         return false;
 
-    pattern = gjs_cairo_pattern_get_pattern(context, obj);
+    cairo_pattern_t* pattern = gjs_cairo_pattern_get_pattern(context, obj);
+    if (!pattern)
+        return false;
+
     cairo_pattern_set_extend(pattern, extend);
 
     if (!gjs_cairo_check_status(context, cairo_pattern_status(pattern), "pattern"))
@@ -113,14 +113,16 @@ getExtend_func(JSContext *context,
 {
     GJS_GET_THIS(context, argc, vp, rec, obj);
     cairo_extend_t extend;
-    cairo_pattern_t *pattern;
 
     if (argc > 0) {
         gjs_throw(context, "SurfacePattern.getExtend() requires no arguments");
         return false;
     }
 
-    pattern = gjs_cairo_pattern_get_pattern(context, obj);
+    cairo_pattern_t* pattern = gjs_cairo_pattern_get_pattern(context, obj);
+    if (!pattern)
+        return false;
+
     extend = cairo_pattern_get_extend(pattern);
 
     if (!gjs_cairo_check_status(context, cairo_pattern_status(pattern), "pattern"))
@@ -139,13 +141,15 @@ setFilter_func(JSContext *context,
 {
     GJS_GET_THIS(context, argc, vp, argv, obj);
     cairo_filter_t filter;
-    cairo_pattern_t *pattern;
 
     if (!gjs_parse_call_args(context, "setFilter", argv, "i",
                              "filter", &filter))
         return false;
 
-    pattern = gjs_cairo_pattern_get_pattern(context, obj);
+    cairo_pattern_t* pattern = gjs_cairo_pattern_get_pattern(context, obj);
+    if (!pattern)
+        return false;
+
     cairo_pattern_set_filter(pattern, filter);
 
     if (!gjs_cairo_check_status(context, cairo_pattern_status(pattern), "pattern"))
@@ -163,14 +167,16 @@ getFilter_func(JSContext *context,
 {
     GJS_GET_THIS(context, argc, vp, rec, obj);
     cairo_filter_t filter;
-    cairo_pattern_t *pattern;
 
     if (argc > 0) {
         gjs_throw(context, "SurfacePattern.getFilter() requires no arguments");
         return false;
     }
 
-    pattern = gjs_cairo_pattern_get_pattern(context, obj);
+    cairo_pattern_t* pattern = gjs_cairo_pattern_get_pattern(context, obj);
+    if (!pattern)
+        return false;
+
     filter = cairo_pattern_get_filter(pattern);
 
     if (!gjs_cairo_check_status(context, cairo_pattern_status(pattern), "pattern"))
diff --git a/modules/cairo-surface.cpp b/modules/cairo-surface.cpp
index a2bb9514..5b98c1b7 100644
--- a/modules/cairo-surface.cpp
+++ b/modules/cairo-surface.cpp
@@ -70,13 +70,12 @@ writeToPNG_func(JSContext *context,
 {
     GJS_GET_THIS(context, argc, vp, argv, obj);
     GjsAutoChar filename;
-    cairo_surface_t *surface;
 
     if (!gjs_parse_call_args(context, "writeToPNG", argv, "F",
                              "filename", &filename))
         return false;
 
-    surface = gjs_cairo_surface_get_surface(context, obj);
+    cairo_surface_t* surface = gjs_cairo_surface_get_surface(context, obj);
     if (!surface)
         return false;
 
@@ -95,7 +94,6 @@ getType_func(JSContext *context,
              JS::Value *vp)
 {
     GJS_GET_THIS(context, argc, vp, rec, obj);
-    cairo_surface_t *surface;
     cairo_surface_type_t type;
 
     if (argc > 1) {
@@ -103,7 +101,10 @@ getType_func(JSContext *context,
         return false;
     }
 
-    surface = gjs_cairo_surface_get_surface(context, obj);
+    cairo_surface_t* surface = gjs_cairo_surface_get_surface(context, obj);
+    if (!surface)
+        return false;
+
     type = cairo_surface_get_type(surface);
     if (!gjs_cairo_check_status(context, cairo_surface_status(surface),
                                 "surface"))
@@ -226,24 +227,33 @@ gjs_cairo_surface_from_surface(JSContext       *context,
 
 /**
  * gjs_cairo_surface_get_surface:
- * @context: the context
- * @object: surface wrapper
- *
- * Returns: the surface attaches to the wrapper.
+ * @cx: the context
+ * @surface_wrapper: surface wrapper
  *
+ * Returns: the surface attached to the wrapper.
  */
-cairo_surface_t *
-gjs_cairo_surface_get_surface(JSContext *context,
-                              JSObject *object)
-{
-    GjsCairoSurface *priv;
+cairo_surface_t* gjs_cairo_surface_get_surface(
+    JSContext* cx, JS::HandleObject surface_wrapper) {
+    g_return_val_if_fail(cx, nullptr);
+    g_return_val_if_fail(surface_wrapper, nullptr);
 
-    g_return_val_if_fail(context, nullptr);
-    g_return_val_if_fail(object, nullptr);
+    JS::RootedObject proto(cx, gjs_cairo_surface_get_proto(cx));
+
+    bool is_surface_subclass = false;
+    if (!gjs_object_in_prototype_chain(cx, proto, surface_wrapper,
+                                       &is_surface_subclass))
+        return nullptr;
+    if (!is_surface_subclass) {
+        gjs_throw(cx, "Expected Cairo.Surface but got %s",
+                  JS_GetClass(surface_wrapper)->name);
+        return nullptr;
+    }
 
-    priv = (GjsCairoSurface*) JS_GetPrivate(object);
+    auto* priv = static_cast<GjsCairoSurface*>(JS_GetPrivate(surface_wrapper));
     if (!priv)
         return nullptr;
+
+    g_assert(priv->surface);
     return priv->surface;
 }
 
@@ -269,11 +279,16 @@ surface_to_g_argument(JSContext      *context,
         return true;
     }
 
-    JSObject *obj;
-    cairo_surface_t *s;
+    if (!value.isObject()) {
+        GjsAutoChar display_name =
+            gjs_argument_display_name(arg_name, argument_type);
+        gjs_throw(context, "%s is not a Cairo.Surface", display_name.get());
+        return false;
+    }
 
-    obj = &value.toObject();
-    s = gjs_cairo_surface_get_surface(context, obj);
+    JS::RootedObject surface_wrapper(context, &value.toObject());
+    cairo_surface_t* s =
+        gjs_cairo_surface_get_surface(context, surface_wrapper);
     if (!s)
         return false;
     if (transfer == GI_TRANSFER_EVERYTHING)


[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]