1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21 package org.millscript.millscript.functions;
22
23 import org.millscript.commons.util.map.EHashMap;
24 import org.millscript.millscript.alert.Alerts;
25 import org.millscript.millscript.datatypes.SpiceClass;
26 import org.millscript.millscript.datatypes.SpiceObject;
27 import org.millscript.millscript.tools.CastLibrary;
28 import org.millscript.millscript.vm.Machine;
29
30 /**
31 * This class provides multiple dispatch functions for MillScript. The point
32 * here is to allow multiple function definitions with different type
33 * signatures, that dispatch on specified arguments.
34 */
35 public class ManyBodyFunction extends Function {
36
37 /**
38 * Maps the type signature of a function definition to it's Function. We
39 * currently support single dispatch on the first argument, so this is a
40 * simple mapping of a Spice class to a Function.
41 */
42 private final EHashMap< SpiceClass, Function > definitions = new EHashMap< SpiceClass, Function >();
43
44 /**
45 * Maps the type signature of the calling arguments to the relevant
46 * Function. This is slightly different to the definition map, as
47 * we might dispatch on a subclass. e.g. if a definition is made for a
48 * method in class <code>Foo</code>, there will be a mapping for
49 * <code>Foo</code> in the definitions. Assume a sub-class is made of type
50 * <code>Bar</code>, which doesn't override this method. If we dispatch
51 * this function on type <code>Bar</code>, we will actually have to lookup
52 * and call the method associated with type <code>Foo</code>. This map
53 * exists to cache this lookup.
54 */
55 private final EHashMap< SpiceClass, Function > callCache = new EHashMap< SpiceClass, Function >();
56
57 /**
58 * Constructs a new ManyBodyFunction with the specified initial method
59 * entry.
60 *
61 * @param mc the current Machine
62 * @param sc the spice class to dispatch on
63 * @param lf the Function to execute for the specified type signature
64 */
65 public ManyBodyFunction( final Machine mc, final SpiceClass sc, final Function lf ) {
66 this.setMethodEntry( mc, sc, lf );
67 }
68
69 /**
70 * @see org.millscript.millscript.functions.Function#apply(org.millscript.millscript.vm.Machine, int)
71 */
72 @Override
73 public void apply( final Machine mc, final int nargs ) {
74
75 checkNargsGT( mc, 1, nargs );
76
77 final SpiceObject dispatchObject = CastLibrary.toSpiceObject( mc.getIndex( mc.getCount() - nargs ) );
78
79 final SpiceClass sc = dispatchObject.getSpiceClass();
80
81 final Function specificBodyFunction = findMethod( sc );
82
83 specificBodyFunction.apply( mc, nargs );
84 }
85
86 /**
87 * Returns the specific Function body for the specified type signature.
88 * This method will search for a body Function which matches the specified
89 * type, or one of it's parents.
90 *
91 * @param sc the type to dispatch on
92 * @return a Function for the specific body to use for the specified type
93 */
94 private Function findMethod( final SpiceClass sc ) {
95
96
97
98
99 final Function cachedFunc = callCache.get( sc );
100 if ( cachedFunc == null ) {
101
102
103
104
105 final Function func = definitions.get( sc );
106
107 if ( func == null ) {
108
109
110 final SpiceClass parentSpiceClass = sc.getParentSpiceClass();
111 if ( parentSpiceClass == null ) {
112
113 throw(
114 Alerts.eval(
115 "No matching method definition",
116 "There is no suitable method for the specified type signature"
117 ).culprit( "type", sc.getName() ).mishap()
118 );
119 } else {
120
121 return findMethod( parentSpiceClass );
122 }
123 } else {
124
125 callCache.insert( sc, func );
126
127 return func;
128 }
129 } else {
130 return cachedFunc;
131 }
132 }
133
134 /**
135 * Sets the Function body for the specified type signature.
136 *
137 * @param mc the current Machine
138 * @param sc the spice class to dispatch on
139 * @param func the Function to execute for the specified type signature
140 */
141 public void setMethodEntry( final Machine mc, final SpiceClass sc, final Function func ) {
142
143
144
145 if ( definitions.containsKey( sc ) ) {
146 mc.getConfig().reportAlertAsWarning(
147 Alerts.compile(
148 "A method with this type signature is already declared",
149 null
150 ).culprit( "method", this ).culprit( "type", sc )
151 );
152 }
153
154
155 callCache.removeAll();
156
157 definitions.insert( sc, func );
158 }
159
160 }