1 module as.engine;
2 import as.def;
3 import std..string;
4 import std.traits;
5 import as;
6 
7 class ScriptEngine {
8 private:
9     asIScriptEngine* engine;
10 
11     ~this() {
12         this.shutDownAndRelease();
13     }
14 
15     this(asIScriptEngine* engine) {
16         this.engine = engine;
17     }
18 
19 public:
20 
21     /**
22         Creates a new script engine
23     */
24     static ScriptEngine create(asDWORD version_ = ANGELSCRIPT_VERSION) {
25         return new ScriptEngine(asCreateScriptEngine(version_));
26     }
27 
28     /**
29         Adds reference
30     */
31     int addRef() {
32         return asEngine_AddRef(engine);
33     }
34 
35     /**
36         Releases reference
37     */
38     int release() {
39         return asEngine_Release(engine);
40     }
41 
42     /**
43         Shuts down the scripting engine and releases it
44     */
45     int shutDownAndRelease() {
46         return asEngine_ShutDownAndRelease(engine);
47     }
48 
49     /**
50         Sets an engine property
51     */
52     void setProperty(asEEngineProp property, asPWORD value) {
53         int err = asEngine_SetEngineProperty(engine, property, value);
54         assert(err != asERetCodes.asINVALID_ARG, "Invalid argument");
55     }
56 
57     /**
58         Gets an engine property
59     */
60     asPWORD getProperty(asEEngineProp property) {
61         return asEngine_GetEngineProperty(engine, property);
62     }
63 
64     /**
65         Sets the message callback
66 
67         BUG: Writing message will cause a crash currently.
68     */
69     void setMessageCallback(MessageCallback callback) {
70         int err = asEngine_SetMessageCallback(engine, cast(asFUNCTION_t)callback, null, CDECL);
71         assert(err != asERetCodes.asINVALID_ARG, "Invalid argument");
72         assert(err != asERetCodes.asNOT_SUPPORTED, "Not supported");
73     }
74 
75     /**
76         Clears the message callback
77     */
78     void clearMessageCallback() {
79         asEngine_ClearMessageCallback(engine);
80     }
81 
82     /**
83         Writes a message
84     */
85     void writeMessage(string section, int row, int col, MessageType type, string message) {
86         asEngine_WriteMessage(engine, section.toStringz, row, col, cast(asEMsgType)type, message.toStringz);
87     }
88 
89     // TODO: Jit Compiler
90 
91     /**
92         Register a global function
93     */
94     void registerGlobalFunction(T)(string declaration, T func, void* aux = null) if (isFunctionPointer!T) {
95         int err = asEngine_RegisterGlobalFunction(engine, declaration.toStringz, cast(asFUNCTION_t)func, DCall, aux);
96         assert(err != asERetCodes.asNOT_SUPPORTED, "Not supported");
97         assert(err != asERetCodes.asWRONG_CALLING_CONV, "Wrong calling convetion");
98         assert(err != asERetCodes.asINVALID_DECLARATION, "Function declaration is invalid");
99         assert(err != asERetCodes.asNAME_TAKEN, "Function name is already taken");
100         assert(err != asERetCodes.asALREADY_REGISTERED, "Function is already registered");
101         assert(err != asERetCodes.asINVALID_ARG, "Invalid argument");
102     }
103 
104     /**
105         Gets the amount of global functions
106     */
107     asUINT getGlobalFunctionCount() {
108         return asEngine_GetGlobalFunctionCount(engine);
109     }
110     
111     /**
112         Gets a global function by its index
113     */
114     Function getGlobalFunctionByIndex(uint index) {
115         return new Function(this, asEngine_GetGlobalFunctionByIndex(engine, index));
116     }
117     
118     /**
119         Gets a global function by its declaration
120     */
121     Function getGlobalFunctionByDecl(string decl) {
122         return new Function(this, asEngine_GetGlobalFunctionByDecl(engine, decl.toStringz));
123     }
124 
125     /**
126         Registers a global property
127     */
128     void registerGlobalProperty(T)(string declaration, ref T pointer) {
129         int err = asEngine_RegisterGlobalProperty(engine, declaration.toStringz, cast(void*)&pointer);
130         assert(err != asERetCodes.asINVALID_DECLARATION, "Declaration is invalid");
131         assert(err != asERetCodes.asINVALID_TYPE, "Declaration type is invalid");
132         assert(err != asERetCodes.asINVALID_ARG, "Reference is null");
133         assert(err != asERetCodes.asNAME_TAKEN, "Name is already taken");
134     }
135 
136     /**
137         Gets the count of global properties
138     */
139     asUINT getGlobalPropertyCount() {
140         return asEngine_GetGlobalPropertyCount(engine);
141     }
142 
143     /**
144         Gets a global property by its index
145     */
146     void getGlobalPropertyByIndex(uint index, ref string name, ref string namespace, ref int typeId, ref bool isConst, ref string configGroup, void* ptr, ref asDWORD accessMask) {
147         const(char)* c_name;
148         const(char)* c_namespace;
149         int c_typeId;
150         bool c_isConst;
151         const(char)* c_configGroup;
152         void* c_ptr;
153         asDWORD c_accessMask;
154 
155         int err = asEngine_GetGlobalPropertyByIndex(engine, index, &c_name, &c_namespace, &c_typeId, &c_isConst, &c_configGroup, &c_ptr, &c_accessMask);
156         assert(err != asERetCodes.asINVALID_ARG, "Index is too large");
157         
158         // move all this data to the appropriate place
159         name = cast(string)c_name.fromStringz.idup;
160         namespace = cast(string)c_namespace.fromStringz.idup;
161         typeId = c_typeId;
162         isConst = c_isConst;
163         configGroup = cast(string)c_configGroup.fromStringz.idup;
164         ptr = c_ptr;
165         accessMask = c_accessMask;
166     }
167 
168     /**
169         Gets the index of a global property by its name
170     */
171     int getGlobalPropertyIndexByName(string name) {
172         int err = asEngine_GetGlobalPropertyIndexByName(engine, name.toStringz);
173         assert(err != asERetCodes.asNO_GLOBAL_VAR, "No matching property was found");
174         return err;
175     }
176 
177     /**
178         Gets the index of a global property by its declaration
179     */
180     int getGlobalPropertyIndexByDecl(string decl) {
181         int err = asEngine_GetGlobalPropertyIndexByDecl(engine, decl.toStringz);
182         assert(err != asERetCodes.asNO_GLOBAL_VAR, "No matching property was found");
183         assert(err != asERetCodes.asINVALID_DECLARATION, "Invalid declaration");
184         return err;
185     }
186 
187     /**
188         Registers an object type
189     */
190     void registerObjectType(string name, int byteSize, asDWORD flags) {
191         int err = asEngine_RegisterObjectType(engine, name.toStringz, byteSize, flags);
192         assert(err != asERetCodes.asINVALID_ARG, "Invalid flags");
193         assert(err != asERetCodes.asINVALID_NAME, "Invalid name");
194         assert(err != asERetCodes.asALREADY_REGISTERED, "An object with the same name already exists");
195         assert(err != asERetCodes.asNAME_TAKEN, "Name is already taken by an other symbol");
196         assert(err != asERetCodes.asLOWER_ARRAY_DIMENSION_NOT_REGISTERED, "Registered array type element must be a primitive or registered type");
197         assert(err != asERetCodes.asINVALID_TYPE, "Array type was malformed");
198         assert(err != asERetCodes.asNOT_SUPPORTED, "Array type is not supported or already in use.");
199     }
200 
201     /**
202         Registers a property for an object
203     */
204     void registerObjectProperty(string obj, string decl, int byteOffset) {
205         int err = asEngine_RegisterObjectProperty(engine, obj.toStringz, decl.toStringz, byteOffset);
206         assert(err != asERetCodes.asWRONG_CONFIG_GROUP, "Object type was registered in a different config group");
207         assert(err != asERetCodes.asINVALID_OBJECT, "obj does not specify an object type");
208         assert(err != asERetCodes.asINVALID_TYPE, "obj parameter syntax invalid");
209         assert(err != asERetCodes.asNAME_TAKEN, "Name conflicts with other members");
210     }
211 
212     /**
213         Registers a method for an object
214     */
215     void registerObjectMethod(T)(string obj, string decl, T func, asDWORD callConv = DCall, void* aux = null) if (isFunctionPointer!T) {
216         int err = asEngine_RegisterObjectMethod(engine, obj.toStringz, decl.toStringz, cast(asFUNCTION_t)func, callConv, aux);
217         assert(err != asERetCodes.asWRONG_CONFIG_GROUP, "Object type was registered in a different config group");
218         assert(err != asERetCodes.asNOT_SUPPORTED, "The calling convention is not supported");
219         assert(err != asERetCodes.asINVALID_TYPE, "obj parameter syntax invalid");
220         assert(err != asERetCodes.asINVALID_DECLARATION, "Invalid declaration");
221         assert(err != asERetCodes.asNAME_TAKEN, "Name conflicts with other members");
222         assert(err != asERetCodes.asWRONG_CALLING_CONV, "The function's calling convention is not compatible with callConv");
223         assert(err != asERetCodes.asALREADY_REGISTERED, "The method is already registered with the same parameter list");
224         assert(err != asERetCodes.asINVALID_ARG, "aux pointer was not set according to calling convention");
225     }
226 
227     /**
228         Registers a behaviour for an object
229     */
230     void registerObjectBehaviour(T)(string obj, asEBehaviours behaviour, string decl, T func, asDWORD callConv = DCall, void* aux = null) if (isFunctionPointer!T) {
231         int err = asEngine_RegisterObjectBehaviour(engine, obj.toStringz, behaviour, decl.toStringz, cast(asFUNCTION_t)func, callConv, aux);
232         assert(err != asERetCodes.asWRONG_CONFIG_GROUP, "Object type was registered in a different config group");
233         assert(err != asERetCodes.asINVALID_ARG, "obj not set, global behaviour given in behaviour or the objForThiscall pointer wasn't set correctly");
234         assert(err != asERetCodes.asWRONG_CALLING_CONV, "The function's calling convention is not compatible with callConv");
235         assert(err != asERetCodes.asNOT_SUPPORTED, "The calling convention or behaviour signature is not supported");
236         assert(err != asERetCodes.asINVALID_TYPE, "Invalid obj parameter");
237         assert(err != asERetCodes.asINVALID_DECLARATION, "Invalid declaration");
238         assert(err != asERetCodes.asILLEGAL_BEHAVIOUR_FOR_TYPE, "Illegal behaviour for type");
239         assert(err != asERetCodes.asALREADY_REGISTERED, "The method is already registered with the same parameter list");
240     }
241 
242     /**
243         Registers a new interface
244     */
245     void registerInterface(string name) {
246         int err = asEngine_RegisterInterface(engine, name.toStringz);
247         assert(err != asERetCodes.asINVALID_NAME, "Name is null or reserved keyword");
248         assert(err != asERetCodes.asALREADY_REGISTERED, "Object type with this name already exists");
249         assert(err != asERetCodes.asERROR, "Name is not a proper identifier");
250         assert(err != asERetCodes.asNAME_TAKEN, "Name is already used elsewhere");
251     }
252 
253     /**
254         Registers an interface method
255     */
256     void registerInterfaceMethod(string intf, string decl) {
257         int err = asEngine_RegisterInterfaceMethod(engine, intf.toStringz, decl.toStringz);
258         assert(err != asERetCodes.asWRONG_CONFIG_GROUP, "Interface was registered in a different config group");
259         assert(err != asERetCodes.asINVALID_TYPE, "intf is not an interface");
260         assert(err != asERetCodes.asINVALID_DECLARATION, "Invalid declaration");
261         assert(err != asERetCodes.asNAME_TAKEN, "Name is already taken");
262     }
263 
264     /**
265         Gets the count of the objects in the engine
266     */
267     asUINT getObjectTypeCount() {
268         return asEngine_GetObjectTypeCount(engine);
269     }
270 
271     /**
272         Gets the type info of an object by its index
273     */
274     Type getObjectTypeByIndex(asUINT index) {
275         auto type = asEngine_GetObjectTypeByIndex(engine, index);
276         return type !is null ? new Type(this, type) : null;
277     }
278 
279     /**
280         Registers a string factory type with the specified creation functions
281     */
282     void registerStringFactory(string type, asGETSTRINGCONSTFUNC_t getStr, asRELEASESTRINGCONSTFUNC_t releaseStr, asGETRAWSTRINGDATAFUNC_t getRawStr) {
283         int err = asEngine_RegisterStringFactory(engine, type.toStringz, getStr, releaseStr, getRawStr);
284         assert(err != asERetCodes.asINVALID_ARG, "The factory is null");
285         assert(err != asERetCodes.asINVALID_TYPE, "Type is not valid or it is a reference/handle.");
286     }
287 
288     /**
289         Gets the return type id of the string type the factory returns
290     */
291     asUINT getStringFactoryReturnTypeId(asDWORD* flags = null) {
292         asUINT err = asEngine_GetStringFactoryReturnTypeId(engine, flags);
293         assert(err != asERetCodes.asNO_FUNCTION, "String factory has not been registered");
294         return err;
295     }
296 
297     /**
298         Registers the default array type
299     */
300     void registerDefaultArrayType(string type) {
301         int err = asEngine_RegisterDefaultArrayType(engine, type.toStringz);
302         assert(err != asERetCodes.asINVALID_TYPE, "Type is not a template type");
303     }
304 
305     /**
306         Gets the type id of the default array type
307     */
308     int getDefaultArrayTypeId() {
309         int err = asEngine_GetDefaultArrayTypeId(engine);
310         assert(err != asERetCodes.asINVALID_TYPE, "Array type has not been registered");
311         return err;
312     }
313 
314     /**
315         Registers a D enum in Angelscript
316     */
317     void registerEnum(T)() if (is(T == enum)) {
318         mixin("import ", moduleName!T, ";");
319         registerEnum(T.stringof);
320         static foreach(member; __traits(allMembers, T)) {
321             registerEnumValue(T.stringof, member, cast(int)mixin(T.stringof, ".", member));
322         }
323     }
324 
325     /**
326         Registers an enum
327     */
328     void registerEnum(string type) {
329         int err = asEngine_RegisterEnum(engine, type.toStringz);
330         assert(err != asERetCodes.asINVALID_NAME, "Invalid name");
331         assert(err != asERetCodes.asALREADY_REGISTERED, "Already registered");
332         assert(err != asERetCodes.asERROR, "Couldn't parse type");
333         assert(err != asERetCodes.asNAME_TAKEN, "Name already taken");
334     }
335 
336     /**
337         Registers a value for the enum
338     */
339     void registerEnumValue(string type, string name, int value) {
340         int err = asEngine_RegisterEnumValue(engine, type.toStringz, name.toStringz, value);
341         assert(err != asERetCodes.asWRONG_CONFIG_GROUP, "Wrong config group");
342         assert(err != asERetCodes.asINVALID_TYPE, "Invalid type");
343         assert(err != asERetCodes.asALREADY_REGISTERED, "Already registered");
344     }
345 
346     /**
347         Gets the amount of enums registered
348     */
349     asUINT getEnumCount() {
350         return asEngine_GetEnumCount(engine);
351     }
352 
353     /**
354         Gets the type of an enum by its index
355     */
356     Type getEnumByindex(asUINT index) {
357         auto type = asEngine_GetEnumByIndex(engine, index);
358         return type !is null ? new Type(this, type) : null;
359     }
360 
361     void registerFuncDef(string decl) {
362         int err = asEngine_RegisterFuncdef(engine, decl.toStringz);
363         assert(err != asERetCodes.asINVALID_ARG, "Declaration not given");
364         assert(err != asERetCodes.asINVALID_DECLARATION, "Invalid function definition");
365         assert(err != asERetCodes.asNAME_TAKEN, "Name conflicts with an other name");
366     }
367 
368     /**
369         Gets the amount of enums registered
370     */
371     asUINT getFuncdefConut() {
372         return asEngine_GetFuncdefCount(engine);
373     }
374 
375     /**
376         Gets the type of a fundef by its index
377     */
378     Type getFuncdefByIndex(asUINT index) {
379         auto type = asEngine_GetFuncdefByIndex(engine, index);
380         return type !is null ? new Type(this, type) : null;
381     }
382 
383     /**
384         Starts a new dynamic configuration group
385     */
386     void beginConfigGroup(string groupName) {
387         int err = asEngine_BeginConfigGroup(engine, groupName.toStringz);
388         assert(err != asERetCodes.asNAME_TAKEN, "Group name is already in use");
389         assert(err != asERetCodes.asNOT_SUPPORTED, "Nested configuration groups isn't supported");
390     }
391 
392     /**
393         Ends the configuration group
394     */
395     void endConfigGroup() {
396         int err = asEngine_EndConfigGroup(engine);
397         assert(err != asERetCodes.asERROR, "No configuration groups to end");
398     }
399 
400     /**
401         Removes a configuration group
402     */
403     void removeConfigGroup(string groupName) {
404         int err = asEngine_RemoveConfigGroup(engine, groupName.toStringz);
405         assert(err != asERetCodes.asCONFIG_GROUP_IS_IN_USE, "Group is in use and can't be removed");
406     }
407 
408     /**
409         Sets the access mask that should be used for subsequent registered entities
410     */
411     asDWORD setDefaultAccessMask(asDWORD defaultMask) {
412         return asEngine_SetDefaultAccessMask(engine, defaultMask);
413     }
414 
415     /**
416         Sets the default namespace
417     */
418     void setDefaultNamespace(string nameSpace) {
419         int err = asEngine_SetDefaultNamespace(engine, nameSpace.toStringz);
420         assert(err != asERetCodes.asINVALID_ARG, "Invalid namespace");
421     }
422 
423     /**
424         Gets the current default namespace
425     */
426     string getDefaultNamespace() {
427         return cast(string)asEngine_GetDefaultNamespace(engine).fromStringz;
428     }
429 
430     /**
431         Gets a module in the engine by name
432     */
433     Module getModule(string name, ModuleCreateFlags flags = ModuleCreateFlags.OnlyIfExists) {
434         auto mod = asEngine_GetModule(engine, name.toStringz, flags);
435         return mod !is null ? new Module(this, mod) : null;
436     }
437 
438     /**
439         Gets a module in the engine by index
440     */
441     Module getModule(asUINT index) {
442         auto mod = asEngine_GetModuleByIndex(engine, index);
443         return mod !is null ? new Module(this, mod) : null;
444     }
445 
446     /**
447         Discards a module by name
448         Note: Any class instances of the module in question will be rendered invalid.
449     */
450     void discardModule(string name) {
451         asEngine_DiscardModule(engine, name.toStringz);
452     }
453 
454     /**
455         Gets the count of modules
456     */
457     asUINT getModuleCount() {
458         return asEngine_GetModuleCount(engine);
459     }
460 
461     /**
462         Get a function by its ID in the engine
463     */
464     Function getFunctionById(int funcId) {
465         return new Function(this, asEngine_GetFunctionById(engine, funcId));
466     }
467 
468     /**
469         Creates a new script context
470     */
471     ScriptContext createContext() {
472         return new ScriptContext(this, asEngine_CreateContext(engine));
473     }
474 }