Error checking / reporting API
authorhumper@google.com <humper@google.com@2bbb7eff-a529-9590-31e7-b0007b416f81>
Mon, 8 Apr 2013 21:44:11 +0000 (21:44 +0000)
committerhumper@google.com <humper@google.com@2bbb7eff-a529-9590-31e7-b0007b416f81>
Mon, 8 Apr 2013 21:44:11 +0000 (21:44 +0000)
Review URL: https://codereview.chromium.org/13699004

git-svn-id: http://skia.googlecode.com/svn/trunk@8566 2bbb7eff-a529-9590-31e7-b0007b416f81

gyp/core.gypi
gyp/tests.gyp
include/core/SkError.h [new file with mode: 0644]
src/core/SkError.cpp [new file with mode: 0644]
src/core/SkErrorInternals.h [new file with mode: 0644]
src/core/SkPath.cpp
tests/ErrorTest.cpp [new file with mode: 0644]

index 3fc5263c6bf2253d41b31c2f2f1b85a3c0549c40..ac66eedcc957e82fc4af4bac48c5eb244a921dca 100644 (file)
@@ -76,6 +76,8 @@
         '<(skia_src_path)/core/SkEdgeClipper.cpp',
         '<(skia_src_path)/core/SkEdge.cpp',
         '<(skia_src_path)/core/SkEdge.h',
+        '<(skia_src_path)/core/SkError.cpp',
+        '<(skia_src_path)/core/SkErrorInternals.h',
         '<(skia_src_path)/core/SkFDStream.cpp',
         '<(skia_src_path)/core/SkFP.h',
         '<(skia_src_path)/core/SkFilterProc.cpp',
         '<(skia_include_path)/core/SkDrawFilter.h',
         '<(skia_include_path)/core/SkDrawLooper.h',
         '<(skia_include_path)/core/SkEndian.h',
+        '<(skia_include_path)/core/SkError.h',
         '<(skia_include_path)/core/SkFixed.h',
         '<(skia_include_path)/core/SkFlattenable.h',
         '<(skia_include_path)/core/SkFloatBits.h',
index cd9e1b80c7a307e8c11b83e296da14c768c01a5f..4f9fefeff61edd98983a9b4246c8ab90fd30360a 100644 (file)
@@ -44,6 +44,7 @@
         '../tests/DrawPathTest.cpp',
         '../tests/DrawTextTest.cpp',
         '../tests/EmptyPathTest.cpp',
+        '../tests/ErrorTest.cpp',
         '../tests/FillPathTest.cpp',
         '../tests/FlatDataTest.cpp',
         '../tests/FlateTest.cpp',
diff --git a/include/core/SkError.h b/include/core/SkError.h
new file mode 100644 (file)
index 0000000..ce2994c
--- /dev/null
@@ -0,0 +1,86 @@
+/*
+ * Copyright 2013 Google Inc.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+#ifndef SkError_DEFINED
+#define SkError_DEFINED
+
+
+/** \file SkError.h
+*/
+
+enum SkError {
+    /** All is well
+     */
+    kNoError_SkError=0,
+    
+    /** User argument passed to Skia function was invalid: NULL when that’s 
+     *  not allowed, out of numeric range, bad enum, or violating some 
+     *  other general precondition.
+     */
+    kInvalidArgument_SkError,
+    
+    /** User tried to perform some operation in a state when the operation 
+     *  was not legal, or the operands make no sense (e.g., asking for 
+     *  pixels from an SkPictureCanvas).  Other examples might be 
+     *  inset()’ing a rectangle to make it degenerate (negative width/height).
+     */
+    kInvalidOperation_SkError,
+    
+    /** Probably not needed right now, but in the future we could have opaque 
+     *  handles for SkPictures floating around, and it would be a good idea 
+     *  to anticipate this kind of issue.
+     */
+    kInvalidHandle_SkError,
+    
+    /** This is probably not possible because paint surely has defaults for 
+     *  everything, but perhaps a paint can get into a bad state somehow.
+     */
+    kInvalidPaint_SkError,
+    
+    /** Skia was unable to allocate memory to perform some task.
+     */
+    kOutOfMemory_SkError,
+    
+    /** Skia failed while trying to consume some external resource.
+     */
+    kParseError_SkError
+};
+
+/** Return the current per-thread error code.  Error codes are "sticky"; they
+ *  are not not reset by subsequent successful operations.
+ */ 
+SkError SkGetLastError();
+
+/** Clear the current per-thread error code back to kNoError_SkError.
+ */
+void SkClearLastError();
+
+/** Type for callback functions to be invoked whenever an error is registered.
+ *  Callback functions take the error code being set, as well as a context 
+ *  argument that is provided when the callback is registered.
+ */
+typedef void (*SkErrorCallbackFunction)(SkError, void *);
+
+/** Set the current per-thread error callback.
+ *  
+ *  @param cb The callback function to be invoked.  Passing NULL
+ *            for cb will revert to the default error callback which
+ *            does nothing on release builds, but on debug builds will
+ *            print an informative error message to the screen.
+ *  @param context An arbitrary pointer that will be passed to 
+ *                 the provided callback function.
+ */
+void SkSetErrorCallback(SkErrorCallbackFunction cb, void *context);
+
+/** Get a human-readable description of the last (per-thread) error that 
+ *  occurred.  The returned error message will include not only a human 
+ *  readable version of the error code, but also information about the 
+ *  conditions that led to the error itself.
+ */
+const char *SkGetLastErrorString();
+
+#endif /* SkError_DEFINED */
diff --git a/src/core/SkError.cpp b/src/core/SkError.cpp
new file mode 100644 (file)
index 0000000..3555c69
--- /dev/null
@@ -0,0 +1,143 @@
+
+/*
+ * Copyright 2013 Google Inc.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+#include "SkTLS.h"
+#include "SkTypes.h"
+#include "SkError.h"
+#include "SkErrorInternals.h"
+
+#include <stdio.h>
+#include <stdarg.h>
+
+namespace {
+    void *CreateThreadError() {
+        return SkNEW_ARGS(SkError, (kNoError_SkError));
+    }
+    void DeleteThreadError(void* v) {
+        SkDELETE(reinterpret_cast<SkError*>(v));
+    }
+    #define THREAD_ERROR \
+        (*reinterpret_cast<SkError*>(SkTLS::Get(CreateThreadError, DeleteThreadError)))
+
+    void *CreateThreadErrorCallback() {
+        return SkNEW_ARGS(SkErrorCallbackFunction, (SkErrorInternals::DefaultErrorCallback));
+    }
+    void DeleteThreadErrorCallback(void* v) {
+        SkDELETE(reinterpret_cast<SkErrorCallbackFunction *>(v));
+    }
+
+    #define THREAD_ERROR_CALLBACK                                                             \
+        *(reinterpret_cast<SkErrorCallbackFunction *>(SkTLS::Get(CreateThreadErrorCallback,   \
+                                                                 DeleteThreadErrorCallback)))
+   
+    void *CreateThreadErrorContext() {
+        return SkNEW_ARGS(void **, (NULL));
+    }
+    void DeleteThreadErrorContext(void* v) {
+        SkDELETE(reinterpret_cast<void **>(v));
+    }
+    #define THREAD_ERROR_CONTEXT \
+        (*reinterpret_cast<void **>(SkTLS::Get(CreateThreadErrorContext, DeleteThreadErrorContext)))
+
+    #define ERROR_STRING_LENGTH 2048
+
+    void *CreateThreadErrorString() {
+        return SkNEW_ARRAY(char, (ERROR_STRING_LENGTH));
+    }
+    void DeleteThreadErrorString(void* v) {
+        SkDELETE_ARRAY(reinterpret_cast<char *>(v));
+    }
+    #define THREAD_ERROR_STRING \
+        (reinterpret_cast<char *>(SkTLS::Get(CreateThreadErrorString, DeleteThreadErrorString)))
+}
+
+SkError SkGetLastError() {
+    return SkErrorInternals::GetLastError();
+}
+void SkClearLastError() {
+    SkErrorInternals::ClearError();
+}
+void SkSetErrorCallback(SkErrorCallbackFunction cb, void *context) {
+    SkErrorInternals::SetErrorCallback(cb, context);
+}
+const char *SkGetLastErrorString() {
+    return SkErrorInternals::GetLastErrorString();
+}
+
+// ------------ Private Error functions ---------
+
+void SkErrorInternals::SetErrorCallback(SkErrorCallbackFunction cb, void *context) {
+    if (cb == NULL) {
+        THREAD_ERROR_CALLBACK = SkErrorInternals::DefaultErrorCallback;
+    } else {
+        THREAD_ERROR_CALLBACK = cb;
+    }
+    THREAD_ERROR_CONTEXT = context;
+}
+
+void SkErrorInternals::DefaultErrorCallback(SkError code, void *context) {
+    SkDebugf("Skia Error: %s\n", SkGetLastErrorString());
+}
+
+void SkErrorInternals::ClearError() {
+    SkErrorInternals::SetError( kNoError_SkError, "All is well" );
+}
+
+SkError SkErrorInternals::GetLastError() {
+    return THREAD_ERROR;
+}
+
+const char *SkErrorInternals::GetLastErrorString() {
+    return THREAD_ERROR_STRING;
+}
+
+void SkErrorInternals::SetError(SkError code, const char *fmt, ...) {
+    THREAD_ERROR = code;
+    va_list args;
+    
+    char *str = THREAD_ERROR_STRING;
+    const char *error_name = NULL;
+    switch( code ) {
+        case kNoError_SkError:
+            error_name = "No Error";
+            break;
+        case kInvalidArgument_SkError:
+            error_name = "Invalid Argument";  
+            break;
+        case kInvalidOperation_SkError: 
+            error_name = "Invalid Operation"; 
+            break;
+        case kInvalidHandle_SkError:
+            error_name = "Invalid Handle";
+            break;
+        case kInvalidPaint_SkError:
+            error_name = "Invalid Paint";
+            break;
+        case kOutOfMemory_SkError:
+            error_name = "Out Of Memory";
+            break;
+        case kParseError_SkError:
+            error_name = "Parse Error";
+            break;
+        default:
+            error_name = "Unknown error";
+            break;
+    }
+    
+    sprintf( str, "%s: ", error_name );
+    int string_left = ERROR_STRING_LENGTH - strlen( str );
+    str += strlen(str);
+    
+    va_start( args, fmt );
+    vsnprintf( str, string_left, fmt, args );
+    va_end( args );
+    SkErrorCallbackFunction fn = THREAD_ERROR_CALLBACK;
+    if (fn && code != kNoError_SkError) {
+        fn(code, THREAD_ERROR_CONTEXT);
+    }
+}
diff --git a/src/core/SkErrorInternals.h b/src/core/SkErrorInternals.h
new file mode 100644 (file)
index 0000000..778d539
--- /dev/null
@@ -0,0 +1,27 @@
+
+/*
+ * Copyright 2013 Google Inc.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+#ifndef SkErrorInternals_DEFINED
+#define SkErrorInternals_DEFINED
+
+#include "SkError.h"
+
+class SkErrorInternals {
+    
+public:
+    static void ClearError();
+    static void SetError(SkError code, const char *fmt, ...);
+    static SkError GetLastError();
+    static const char *GetLastErrorString();
+    static void SetErrorCallback(SkErrorCallbackFunction cb, void *context);
+    static void DefaultErrorCallback(SkError code, void *context);
+};
+
+
+#endif /* SkErrorInternals_DEFINED */
index 006fcf85f24d79bf700d33aad8ea86703d4c57ec..e029d438fd08ab7b317c37f07e31369f8f525cc5 100644 (file)
@@ -7,9 +7,10 @@
  */
 
 
-#include "SkPath.h"
 #include "SkBuffer.h"
+#include "SkErrorInternals.h"
 #include "SkMath.h"
+#include "SkPath.h"
 #include "SkPathRef.h"
 #include "SkRRect.h"
 #include "SkThread.h"
@@ -1034,6 +1035,14 @@ bool SkPath::hasOnlyMoveTos() const {
 void SkPath::addRoundRect(const SkRect& rect, SkScalar rx, SkScalar ry,
                           Direction dir) {
     assert_known_direction(dir);
+    
+    if (rx < 0 || ry < 0) {
+        SkErrorInternals::SetError( kInvalidArgument_SkError, 
+                                    "I got %f and %f as radii to SkPath::AddRoundRect, "
+                                    "but negative radii are not allowed.", 
+                                    SkScalarToDouble(rx), SkScalarToDouble(ry) );
+        return;
+    }
 
     SkScalar    w = rect.width();
     SkScalar    halfW = SkScalarHalf(w);
diff --git a/tests/ErrorTest.cpp b/tests/ErrorTest.cpp
new file mode 100644 (file)
index 0000000..cbaf9d1
--- /dev/null
@@ -0,0 +1,62 @@
+
+/*
+ * Copyright 2013 Google Inc.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+#include "Test.h"
+#include "SkError.h"
+#include "SkPath.h"
+#include "SkRect.h"
+
+#define CHECK(errcode)                                                        \
+  REPORTER_ASSERT( reporter, (err = SkGetLastError()) == errcode);            \
+  if (err != kNoError_SkError)                                                \
+  {                                                                           \
+     SkDebugf("Last error string: %s\n", SkGetLastErrorString());             \
+     SkClearLastError();                                                      \
+  }
+                       
+void cb(SkError err, void *context) {
+    int *context_ptr = static_cast<int *>(context);
+    SkDebugf("CB (0x%x): %s\n", *context_ptr, SkGetLastErrorString());
+}
+
+static void ErrorTest(skiatest::Reporter* reporter) {
+    SkError err;
+    
+    CHECK(kNoError_SkError);
+    
+    SkRect r = SkRect::MakeWH(50, 100);
+    CHECK(kNoError_SkError);
+
+    SkPath path;
+    path.addRect(r);
+    CHECK(kNoError_SkError);
+    
+    path.addRoundRect(r, 10, 10);
+    CHECK(kNoError_SkError);
+
+    // should trigger the default error callback, which just prints to the screen.
+    path.addRoundRect(r, -10, -10);
+    CHECK(kInvalidArgument_SkError);
+    CHECK(kNoError_SkError);
+
+    int test_value = 0xdeadbeef;
+    SkSetErrorCallback(cb, &test_value);
+
+    // should trigger *our* callback.
+    path.addRoundRect(r, -10, -10);
+    CHECK(kInvalidArgument_SkError);
+    CHECK(kNoError_SkError);
+
+    // Should trigger the default one again.
+    SkSetErrorCallback(NULL, NULL);
+    path.addRoundRect(r, -10, -10);
+    CHECK(kInvalidArgument_SkError);
+    CHECK(kNoError_SkError);
+}
+
+#include "TestClassDef.h"
+DEFINE_TESTCLASS("Error", ErrorTestClass, ErrorTest)