1 /**
2  * This module implements the ScriptFunction class, which holds script defined functions as well as native D
3  * functions or delegates with the correct signature.
4  */
5 module mildew.types.func;
6 
7 import mildew.context: Context;
8 import mildew.types.any: ScriptAny;
9 import mildew.types.object: ScriptObject;
10 
11 /** When a native function or delegate encounters an error with the arguments sent,
12  *  the last reference parameter should be set to the appropriate enum value.
13  *  A specific exception can be thrown by setting the flag to RETURN_VALUE_IS_EXCEPTION and
14  *  returning a string.
15  */
16 enum NativeFunctionError 
17 {
18     NO_ERROR = 0,
19     WRONG_NUMBER_OF_ARGS,
20     WRONG_TYPE_OF_ARG,
21     RETURN_VALUE_IS_EXCEPTION
22 }
23 
24 /// native function signature to be usable by scripting language
25 alias NativeFunction = ScriptAny function(Context, ScriptAny* thisObj, ScriptAny[] args, ref NativeFunctionError);
26 /// native delegate signature to be usable by scripting language
27 alias NativeDelegate = ScriptAny delegate(Context, ScriptAny* thisObj, ScriptAny[] args, ref NativeFunctionError);
28 
29 /**
30  * This class encapsulates all types of script functions including native D functions and delegates. A
31  * native function must first be wrapped in this class before it can be given to a ScriptAny assignment.
32  * When an object is created with "new FunctionName()" its __proto__ is assigned to the function's "prototype"
33  * field. This allows OOP in the scripting language and is analogous to JavaScript.
34  */
35 class ScriptFunction : ScriptObject
36 {
37     import mildew.nodes: StatementNode;
38     import mildew.interpreter: Interpreter;
39 public:
40     /// The type of function held by the object
41     enum Type { SCRIPT_FUNCTION, NATIVE_FUNCTION, NATIVE_DELEGATE }
42 
43     /**
44      * Constructs a new ScriptFunction out of a native D function.
45      * Params:
46      *  fname = The name of the function.
47      *  nfunc = The address of the native function. See NativeFunction alias for correct signature
48      *  isClass = Whether or not this function is a constructor. This information is used when printing
49      */
50     this(string fname, NativeFunction nfunc, bool isClass = false)
51     {
52         immutable tname = isClass? "class" : "function";
53         import mildew.types.bindings: getFunctionPrototype;
54         super(tname, getFunctionPrototype, null);
55         _functionName = fname;
56 		_isClass = isClass;
57         initializePrototypeProperty();
58         _type = Type.NATIVE_FUNCTION;
59         _nativeFunction = nfunc;
60     }
61 
62     /**
63      * Constructs a new ScriptFunction out of a native D delegate.
64      * Params:
65      *  fname = The name of the function.
66      *  nfunc = The address of the native delegate. See NativeDelegate alias for correct signature
67      *  isClass = Whether or not this function is a constructor. This information is used when printing
68      */
69     this(string fname, NativeDelegate ndele, bool isClass = false)
70     {
71         immutable tname = isClass? "class" : "function";
72         import mildew.types.bindings: getFunctionPrototype;
73         super(tname, getFunctionPrototype, null);
74         _functionName = fname;
75 		_isClass = isClass;
76         initializePrototypeProperty();
77         _type = Type.NATIVE_DELEGATE;
78         _nativeDelegate = ndele;
79     }
80 
81     /// Returns a string representing the type and name.
82     override string toString() const
83     {
84         return name ~ " " ~ _functionName;
85     }
86 
87     /// Returns the type of function stored, such as native function, delegate, or script function
88     auto type() const { return _type; }
89     /// Returns the name of the function
90     auto functionName() const { return _functionName; }
91     /// Property argNames
92     auto argNames() { return _argNames; }
93 
94 package(mildew):
95 
96     /**
97      * Constructor for creating script defined functions.
98      */
99     this(string fnname, string[] args, StatementNode[] statementNodes, Context clos, 
100             bool isClass=false)
101     {
102         immutable tname = isClass? "class" : "function";
103         import mildew.types.bindings: getFunctionPrototype;
104         super(tname, getFunctionPrototype(), null);
105         _functionName = fnname;
106         _argNames = args;
107         _statementNodes = statementNodes;
108 		_closure = clos;
109 		_isClass = isClass;
110         initializePrototypeProperty();
111         _type = Type.SCRIPT_FUNCTION;
112     }
113 
114     // must check type before using these properties or one gets an exception
115 
116     /// get the native function ONLY if it is one
117     auto nativeFunction()
118     {
119         if(_type == Type.NATIVE_FUNCTION)
120             return _nativeFunction;
121         else
122             throw new Exception("This is not a native function");
123     }
124 
125     /// get the delegate only if it is one
126     auto nativeDelegate()
127     {
128         if(_type == Type.NATIVE_DELEGATE)
129             return _nativeDelegate;
130         else
131             throw new Exception("This is not a native delegate");
132     }
133 
134     /// Sets the function name
135     auto functionName(in string fnName) { return _functionName = fnName; }
136 
137     /// Property statementNodes
138     auto statementNodes() { return _statementNodes; }
139 
140 	/// Property closure
141 	auto closure() { return _closure; }
142 	auto closure(Context clos) { return _closure = clos; }
143 
144     /// used by the parser for missing constructors in classes that don't extend
145     static ScriptFunction emptyFunction(in string name, bool isClass)
146     {
147         return new ScriptFunction(name, &native_EMPTY_FUNCTION, isClass);
148     }
149 
150 private:
151     Type _type;
152     string _functionName;
153     string[] _argNames;
154     StatementNode[] _statementNodes;
155 	Context _closure = null;
156 	bool _isClass = false;
157     union {
158         NativeFunction _nativeFunction;
159         NativeDelegate _nativeDelegate;
160     }
161 
162     void initializePrototypeProperty()
163     {
164         _dictionary["prototype"] = ScriptAny(new ScriptObject("Object", null));
165         _dictionary["prototype"]["constructor"] = ScriptAny(this);
166     }
167 
168 }
169 
170 private ScriptAny native_EMPTY_FUNCTION(Context c, ScriptAny* thisObj, ScriptAny[] args, ref NativeFunctionError nfe)
171 {
172     return ScriptAny.UNDEFINED;
173 }
174