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