summaryrefslogtreecommitdiff
path: root/libgo/runtime/go-convert-interface.c
blob: 259456cda160fcb63138d8c8083a95335e64c593 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
/* go-convert-interface.c -- convert interfaces for Go.

   Copyright 2009 The Go Authors. All rights reserved.
   Use of this source code is governed by a BSD-style
   license that can be found in the LICENSE file.  */

#include "go-alloc.h"
#include "go-assert.h"
#include "go-panic.h"
#include "interface.h"

/* This is called when converting one interface type into another
   interface type.  LHS_DESCRIPTOR is the type descriptor of the
   resulting interface.  RHS_DESCRIPTOR is the type descriptor of the
   object being converted.  This builds and returns a new interface
   method table.  If any method in the LHS_DESCRIPTOR interface is not
   implemented by the object, the conversion fails.  If the conversion
   fails, then if MAY_FAIL is true this returns NULL; otherwise, it
   panics.  */

void *
__go_convert_interface_2 (const struct __go_type_descriptor *lhs_descriptor,
			  const struct __go_type_descriptor *rhs_descriptor,
			  _Bool may_fail)
{
  const struct __go_interface_type *lhs_interface;
  int lhs_method_count;
  const struct __go_interface_method* lhs_methods;
  const void **methods;
  const struct __go_uncommon_type *rhs_uncommon;
  int rhs_method_count;
  const struct __go_method *p_rhs_method;
  int i;

  if (rhs_descriptor == NULL)
    {
      /* A nil value always converts to nil.  */
      return NULL;
    }

  __go_assert (lhs_descriptor->__code == GO_INTERFACE);
  lhs_interface = (const struct __go_interface_type *) lhs_descriptor;
  lhs_method_count = lhs_interface->__methods.__count;
  lhs_methods = ((const struct __go_interface_method *)
		 lhs_interface->__methods.__values);

  /* This should not be called for an empty interface.  */
  __go_assert (lhs_method_count > 0);

  rhs_uncommon = rhs_descriptor->__uncommon;
  if (rhs_uncommon == NULL || rhs_uncommon->__methods.__count == 0)
    {
      struct __go_empty_interface panic_arg;

      if (may_fail)
	return NULL;

      newTypeAssertionError (NULL,
			     rhs_descriptor,
			     lhs_descriptor,
			     NULL,
			     rhs_descriptor->__reflection,
			     lhs_descriptor->__reflection,
			     lhs_methods[0].__name,
			     &panic_arg);
      __go_panic (panic_arg);
    }

  rhs_method_count = rhs_uncommon->__methods.__count;
  p_rhs_method = ((const struct __go_method *)
		  rhs_uncommon->__methods.__values);

  methods = NULL;

  for (i = 0; i < lhs_method_count; ++i)
    {
      const struct __go_interface_method *p_lhs_method;

      p_lhs_method = &lhs_methods[i];

      while (rhs_method_count > 0
	     && (!__go_ptr_strings_equal (p_lhs_method->__name,
					  p_rhs_method->__name)
		 || !__go_ptr_strings_equal (p_lhs_method->__pkg_path,
					     p_rhs_method->__pkg_path)))
	{
	  ++p_rhs_method;
	  --rhs_method_count;
	}

      if (rhs_method_count == 0
	  || !__go_type_descriptors_equal (p_lhs_method->__type,
					   p_rhs_method->__mtype))
	{
	  struct __go_empty_interface panic_arg;

	  if (methods != NULL)
	    __go_free (methods);

	  if (may_fail)
	    return NULL;

	  newTypeAssertionError (NULL,
				 rhs_descriptor,
				 lhs_descriptor,
				 NULL,
				 rhs_descriptor->__reflection,
				 lhs_descriptor->__reflection,
				 p_lhs_method->__name,
				 &panic_arg);
	  __go_panic (panic_arg);
	}

      if (methods == NULL)
	{
	  methods = (const void **) __go_alloc ((lhs_method_count + 1)
						* sizeof (void *));

	  /* The first field in the method table is always the type of
	     the object.  */
	  methods[0] = rhs_descriptor;
	}

      methods[i + 1] = p_rhs_method->__function;
    }

  return methods;
}

/* This is called by the compiler to convert a value from one
   interface type to another.  */

void *
__go_convert_interface (const struct __go_type_descriptor *lhs_descriptor,
			const struct __go_type_descriptor *rhs_descriptor)
{
  return __go_convert_interface_2 (lhs_descriptor, rhs_descriptor, 0);
}