+/**
+ * \file
+ * Call specification facilities for the Mono Runtime.
+ *
+ * Author:
+ * Paolo Molaro (lupus@ximian.com)
+ * Dietmar Maurer (dietmar@ximian.com)
+ *
+ * (C) 2002 Ximian, Inc.
+ * Copyright 2011 Xamarin, Inc (http://www.xamarin.com)
+ * Licensed under the MIT license. See LICENSE file in the project root for full
+ * license information.
+ */
+#include "metadata.h"
+#include "callspec.h"
+#include "assembly.h"
+#include "class-internals.h"
+#include "debug-helpers.h"
+
+static MonoAssembly *prog_assembly;
+
+gboolean
+mono_callspec_eval_exception (MonoClass *klass, MonoCallSpec *spec)
+{
+ int include = 0;
+ int i;
+
+ if (!klass)
+ return FALSE;
+
+ for (i = 0; i < spec->len; i++) {
+ MonoTraceOperation *op = &spec->ops [i];
+ int inc = 0;
+
+ switch (op->op) {
+ case MONO_TRACEOP_EXCEPTION:
+ if (strcmp ("", op->data) == 0 &&
+ strcmp ("all", op->data2) == 0)
+ inc = 1;
+ else if (strcmp ("", op->data) == 0 ||
+ strcmp (klass->name_space, op->data) == 0)
+ if (strcmp (klass->name, op->data2) == 0)
+ inc = 1;
+ break;
+ default:
+ break;
+ }
+ if (op->exclude) {
+ if (inc)
+ include = 0;
+ } else if (inc)
+ include = 1;
+ }
+
+ return include;
+}
+
+gboolean mono_callspec_eval (MonoMethod *method, const MonoCallSpec *spec)
+{
+ int include = 0;
+ int i;
+
+ for (i = 0; i < spec->len; i++) {
+ MonoTraceOperation *op = &spec->ops[i];
+ int inc = 0;
+
+ switch (op->op) {
+ case MONO_TRACEOP_ALL:
+ inc = 1;
+ break;
+ case MONO_TRACEOP_PROGRAM:
+ if (prog_assembly &&
+ (method->klass->image ==
+ mono_assembly_get_image (prog_assembly)))
+ inc = 1;
+ break;
+ case MONO_TRACEOP_WRAPPER:
+ if ((method->wrapper_type ==
+ MONO_WRAPPER_NATIVE_TO_MANAGED) ||
+ (method->wrapper_type ==
+ MONO_WRAPPER_MANAGED_TO_NATIVE))
+ inc = 1;
+ break;
+ case MONO_TRACEOP_METHOD:
+ if (mono_method_desc_full_match (
+ (MonoMethodDesc *)op->data, method))
+ inc = 1;
+ break;
+ case MONO_TRACEOP_CLASS:
+ if (strcmp (method->klass->name_space, op->data) == 0)
+ if (strcmp (method->klass->name, op->data2) ==
+ 0)
+ inc = 1;
+ break;
+ case MONO_TRACEOP_ASSEMBLY:
+ if (strcmp (mono_image_get_name (method->klass->image),
+ op->data) == 0)
+ inc = 1;
+ break;
+ case MONO_TRACEOP_NAMESPACE:
+ if (strcmp (method->klass->name_space, op->data) == 0)
+ inc = 1;
+ break;
+ case MONO_TRACEOP_EXCEPTION:
+ break;
+ }
+ if (op->exclude) {
+ if (inc)
+ include = 0;
+ } else if (inc) {
+ include = 1;
+ }
+ }
+ return include;
+}
+
+static int is_filenamechar (char p)
+{
+ if (p >= 'A' && p <= 'Z')
+ return TRUE;
+ if (p >= 'a' && p <= 'z')
+ return TRUE;
+ if (p >= '0' && p <= '9')
+ return TRUE;
+ if (p == '.' || p == ':' || p == '_' || p == '-' || p == '`')
+ return TRUE;
+ return FALSE;
+}
+
+static char *get_string (char **in)
+{
+ char *start = *in;
+ char *p = *in;
+ while (is_filenamechar (*p)) {
+ p++;
+ }
+ size_t len = p - start;
+ char *ret = (char *)g_malloc (len + 1);
+ memcpy (ret, start, len);
+ ret [len] = 0;
+ *in = p;
+ return ret;
+}
+
+enum Token {
+ TOKEN_METHOD,
+ TOKEN_CLASS,
+ TOKEN_ALL,
+ TOKEN_PROGRAM,
+ TOKEN_EXCEPTION,
+ TOKEN_NAMESPACE,
+ TOKEN_WRAPPER,
+ TOKEN_STRING,
+ TOKEN_EXCLUDE,
+ TOKEN_DISABLED,
+ TOKEN_SEPARATOR,
+ TOKEN_END,
+ TOKEN_ERROR
+};
+
+static int get_token (char **in, char **extra, char **errstr)
+{
+ char *p = *in;
+ while (p[0] == '+')
+ p++;
+
+ *extra = NULL;
+
+ if (p[0] == '\0') {
+ *in = p;
+ return TOKEN_END;
+ }
+ if (p[0] == 'M' && p[1] == ':') {
+ p += 2;
+ *extra = get_string (&p);
+ *in = p;
+ return TOKEN_METHOD;
+ }
+ if (p[0] == 'N' && p[1] == ':') {
+ p += 2;
+ *extra = get_string (&p);
+ *in = p;
+ return TOKEN_NAMESPACE;
+ }
+ if (p[0] == 'T' && p[1] == ':') {
+ p += 2;
+ *extra = get_string (&p);
+ *in = p;
+ return TOKEN_CLASS;
+ }
+ if (p[0] == 'E' && p[1] == ':') {
+ p += 2;
+ *extra = get_string (&p);
+ *in = p;
+ return TOKEN_EXCEPTION;
+ }
+ if (*p == '-') {
+ p++;
+ *in = p;
+ return TOKEN_EXCLUDE;
+ }
+ if (is_filenamechar (*p)) {
+ *extra = get_string (&p);
+ *in = p;
+ if (strcmp (*extra, "all") == 0)
+ return TOKEN_ALL;
+ if (strcmp (*extra, "program") == 0)
+ return TOKEN_PROGRAM;
+ if (strcmp (*extra, "wrapper") == 0)
+ return TOKEN_WRAPPER;
+ if (strcmp (*extra, "disabled") == 0)
+ return TOKEN_DISABLED;
+ return TOKEN_STRING;
+ }
+ if (*p == ',') {
+ p++;
+ *in = p;
+ return TOKEN_SEPARATOR;
+ }
+
+ *errstr = g_strdup_printf ("Syntax error at or around '%s'", p);
+ return TOKEN_ERROR;
+}
+
+static int get_spec (char **in, MonoCallSpec *spec, char **errstr)
+{
+ int n = spec->len;
+ char *extra = NULL;
+
+ int token = get_token (in, &extra, errstr);
+ gboolean exclude = FALSE;
+ if (token == TOKEN_EXCLUDE) {
+ exclude = TRUE;
+ token = get_token (in, &extra, errstr);
+ if (token == TOKEN_EXCLUDE || token == TOKEN_DISABLED) {
+ *errstr = g_strdup_printf ("Expecting an expression");
+ token = TOKEN_ERROR;
+ goto out;
+ }
+ }
+ if (token == TOKEN_END || token == TOKEN_SEPARATOR ||
+ token == TOKEN_ERROR)
+ goto out;
+
+ if (token == TOKEN_DISABLED) {
+ spec->enabled = FALSE;
+ goto out;
+ }
+
+ if (token == TOKEN_METHOD) {
+ MonoMethodDesc *desc = mono_method_desc_new (extra, TRUE);
+ if (desc == NULL) {
+ *errstr =
+ g_strdup_printf ("Invalid method name: %s", extra);
+ token = TOKEN_ERROR;
+ goto out;
+ }
+ spec->ops[n].op = MONO_TRACEOP_METHOD;
+ spec->ops[n].data = desc;
+ } else if (token == TOKEN_ALL)
+ spec->ops[n].op = MONO_TRACEOP_ALL;
+ else if (token == TOKEN_PROGRAM)
+ spec->ops[n].op = MONO_TRACEOP_PROGRAM;
+ else if (token == TOKEN_WRAPPER)
+ spec->ops[n].op = MONO_TRACEOP_WRAPPER;
+ else if (token == TOKEN_NAMESPACE) {
+ spec->ops[n].op = MONO_TRACEOP_NAMESPACE;
+ spec->ops[n].data = g_strdup (extra);
+ } else if (token == TOKEN_CLASS || token == TOKEN_EXCEPTION) {
+ char *p = strrchr (extra, '.');
+ if (p) {
+ *p++ = 0;
+ spec->ops[n].data = g_strdup (extra);
+ spec->ops[n].data2 = g_strdup (p);
+ } else {
+ spec->ops[n].data = g_strdup ("");
+ spec->ops[n].data2 = g_strdup (extra);
+ }
+ spec->ops[n].op = token == TOKEN_CLASS ? MONO_TRACEOP_CLASS
+ : MONO_TRACEOP_EXCEPTION;
+ } else if (token == TOKEN_STRING) {
+ spec->ops[n].op = MONO_TRACEOP_ASSEMBLY;
+ spec->ops[n].data = g_strdup (extra);
+ } else {
+ *errstr =
+ g_strdup_printf ("Syntax error in method specification");
+ token = TOKEN_ERROR;
+ goto out;
+ }
+
+ if (exclude)
+ spec->ops[n].exclude = 1;
+
+ spec->len = n + 1;
+ token = TOKEN_SEPARATOR;
+out:
+ if (extra != NULL) {
+ g_free (extra);
+ }
+ return token;
+}
+
+gboolean
+mono_callspec_parse (const char *options, MonoCallSpec *spec, char **errstr)
+{
+ char *p = (char *)options;
+ int size = 1;
+ int token;
+
+ memset (spec, 0, sizeof (*spec));
+ *errstr = NULL;
+
+ spec->enabled = TRUE;
+ if (*p == 0) {
+ spec->len = 1;
+ spec->ops = g_new0 (MonoTraceOperation, 1);
+ spec->ops[0].op = MONO_TRACEOP_ALL;
+ return TRUE;
+ }
+
+ for (p = (char *)options; *p != 0; p++)
+ if (*p == ',')
+ size++;
+
+ spec->ops = g_new0 (MonoTraceOperation, size);
+
+ p = (char *)options;
+
+ while ((token = (get_spec (&p, spec, errstr))) != TOKEN_END) {
+ if (token == TOKEN_ERROR)
+ return FALSE;
+ }
+ return TRUE;
+}
+
+void mono_callspec_cleanup (MonoCallSpec *spec)
+{
+ if (spec->ops != NULL) {
+ g_free (spec->ops);
+ }
+ memset (spec, 0, sizeof (*spec));
+}
+
+void
+mono_callspec_set_assembly (MonoAssembly *assembly)
+{
+ prog_assembly = assembly;
+}