AngelScript To Do's
AngelScript To Do's
AngelScript To Do's
Items marked with (priority) are currently being prioritized for the upcoming releases.
Items marked as (unplanned) will probably never bety implemented.
Improve manual
Explain better how to use weakref from C++ - 2014/12/01
http://www.angelcode.com/angelscript/sdk/docs/manual/doc_addon_weakref.html
asIScriptObject *my_obj = 0;
asILockableSharedBool *my_obj_isDead = 0;
// Get the weakref flag for the object so we can check if it is still alive before accessing it
my_obj_isDead = engine->GetWeakRefFlagOfScriptObject(obj, obj->GetObjectType());
my_obj_isDead->AddRef();
// Release the object handle we received from the script so we don't keep the object alive
obj->Release();
}
void do_something()
{
// Before accessing the stored object, verify that it is still alive
if( my_obj_isDead->Get() )
return;
- char *
- std::vector / std::list
How to register std::function - 2014/07/10
Add example in documentation for how to register std::function
http://www.gamedev.net/topic/658633-registering-a-stdfunction/
Make sure the manual explains why it is currently not possible to have function take object types
by value if the types include a self reference (which would be invalid when the object is
moved/copied byte-for-byte from the context’s stack to the application’s stack inside the code to
support native calling conventions).
This article is meant for script writers who wishes to have better understanding of how the
language works to better optimize the script code, and also for application developers who
wishes to customize the library.
Compiler messages
Improve compiler error message #1 - 2012/01/02
funcdef void OnActionCB();
OnActionCB @actionCb;
void onActionF() { }
void main() {
addAction(@onActionF);
}
The error message that was generated in the above example was “There is no copy operator for
the type '_builtin_function_' available."
A better message would be something like “Cannot do a value assign for function handle. Did
you forget @?”. Or something similar.
An example error is when the constructor for an object refers to a global variable, but that other
global variable has not been initialized when this constructor is called.
Parser gives a slightly cryptic error message when the argument name in the arglist uses a
reserved keyword instead of an identifier.
Improve compiler error message #4 - 2015/02/02
Improve error message, when trying to pass function pointer to function but the signature
doesn't match the expected funcdef.
http://www.gamedev.net/topic/664944-stack-corruption-when-using-funcdef-and-class-property/
http://www.gamedev.net/topic/663696-bug-in-type-casting/
http://www.gamedev.net/topic/658737-line-numbers-for-non-terminated-string-literal/
The error message ‘No appropriate opEquals method found’ should also inform which types are
being compared to make it easier for the user to understand why the opEquals method cannot
be found.
void func() {}
class A {
void func() {}
void test() {
func(); // which function did the programmer really want to call
here?
}
}
It has to be possible to work around the warning by explicitly mentioning which one is called,
e.g.
::func(); // for calling the global function
this.func(); // for calling the class method
This is different from C++. In C++ the base class' methods are hidden when the Derived class
declared any method of the same name.
http://stackoverflow.com/questions/1628768/why-does-an-overridden-function-in-the-derived-cla
ss-hide-other-overloads-of-the
void Func()
{
Object(); // Warn here as the created object is not used
}
void Func()
{
Object a(); // Don't warn, it is assumed the object was meant to
be created
}
The error message for invalid use of registered properties in switch cases is misleading and
needs to be improved.
// C++
RegisterGlobalProperty("const int val", &val);
// AS
switch( expr )
{
case val: // wrong, val is not a true literal constant
break;
}
Warn when global entity has same name as enum value - 2015/10/23
enum tiles
{
wall,
grass,
gravel,
sand
}
void wall()
{
// do something.
}
void main()
{
wall();
int current_tile = wall; // This only sees the function
}
I need to look into how to best handle this situation. Perhaps the compiler should require the
scope operator to be used, even for the function due to the ambiguous interpretation? Or
perhaps the compiler should warn that the enum value has the same name as a global entity
when it is declared?
When the engine property asEP_REQUIRE_ENUM_SCOPE is used the ambiguity doesn’t exist
as then the script writer is required to prefix the enum values with the enum type anyway, i.e.
tiles::wall.
The error message in the situation reported by Solokiller needs to be improved to better show
that the class method doesn’t match the signature for the funcdef used to create the delegate.
Warn when not informing ‘override’ for method that overrides parent
method - 2016/02/22
http://www.gamedev.net/topic/676214-warn-about-overriding-functions-without-override-keywor
d/
For this I should study better how things work in other languages (C++, C#, Java, D), so I do not
end up doing something totally different.
I should probably also enhance the warning system so that the application developers can more
easily decide which warnings they want to see, and which not.
@h.method(); should warn since the expression is not really using the address of whatever is
returned by method().
@h.prop should also warn if the expression isn't using the address for prop.
Library
(priority) Remove the asBC_SUSPEND instruction - 2017/02/16
The asBC_SUSPEND instruction was included since the beginning to have a marker in the
bytecode where the VM can be safely interrupted. It is normally included at every line, or at least
once per loop so that there is always at least one such instruction in the code to allow even
badly written code with infinite loops to be interrupted.
It may be possible to remove the asBC_SUSPEND instruction completely from the code.
Instead the places where the VM can be safely interrupted should be stored as a separate list of
positions.
With this the VM execution speed won’t have the overhead of processing the asBC_SUSPEND
instructions anymore.
To still allow infinite loops to be interrupted JMP and CALL instructions can be changed to allow
the script to interrupt.
To still allow debugger to step through the code statement by statement, the VM can prepare a
temporary array of bytecode that will be executed instead of the original bytecode. In the
temporary array the VM only includes the instructions for the current statement so it ends when
the statement finishes. The debugger will of course become much slower this way, but the
normal execution will be faster.
The compiler should do a deep scan of the bytecode used in the initialization of global variables
to verify all other globals accessed and then order the initialization accordingly.
It will not always be able to determine the order. For example a function can be accessing two
different global variables in different conditions. In this case the compiler will see both, even
though during initialization only one of those are actually accessed.
It will probably be necessary to have some manual way of ordering the initializations when
absolutely necessary. Perhaps some specific tag like:
after a:
int b = a;
before b:
int a = 42;
Food for thought: perhaps this could be used to allow global statements too. One of the reasons
for not allowing global statements is exactly due to the difficulty in determining precise order of
execution, especially when involving multiple script sections in the module.
By disabling the floats through an engine property, the compiler will be able to give appropriate
error messages if the script writer attempts to use floats, unaware of the fact that they have
been disabled by the application.
The reason for this is that it should be as transparent as possible to choose between native
calling convention and auto-wrappers.
As this will break backwards compatibility I need to think how to avoid impacting unsuspecting
developers. Perhaps I’ll wait until starting version 3.0.0. Or perhaps I’ll create a
asCALL_GENERIC2.
This change should also preferably be done together with the change to have all object handles
owned by the caller instead of the callee (to-do already mentioned on the AngelScript WIP
page),
For example, the intermediate bytecode may have the instruction asBC_CALL for calling a
function. But after the local optimization this bytecode is exchanged for
asBC_CALL_WITH_CDECL, asBC_CALL_WITH_GENERIC, asBC_CALL_WITH_THISCALL,
etc depending on the calling convention of the registered function.
Of course, the saved bytecode would be on the intermediate bytecode, so after the final step
has been performed and the intermediate bytecode is removed it would no longer be possible to
save the bytecode.
The separation of data and code is interesting, but there are some questions that needs to be
answered:
- how should the context stack be considered? is it a data or code memory block?
- how should reference counting into code objects, e.g. the object type, be handled? If the entire
block is freed without calling the object's destructors it is possible that the code objects won't
have their external ref count reach 0, thus cannot be destroyed by the engine.
- how to deal with pointers into the data memory pool from outside the pool, e.g. the application,
or from the global variables in the scripts?
The actual function signature would be stored in a vector with a hash-key for quick comparison.
And the asCScriptFunction would refer to the signature object.
With around 100 functions with a simple signature of ‘void f(void)’ perhaps around 10KB of
memory would be saved.
http://www.gamedev.net/topic/664275-angelscript-operators/
If the symbol names are obfuscated, preferably through an application registered callback, it will
be less obvious where things are stored in memory.
http://www.gamedev.net/topic/660038-failure-in-createscriptobject-when-adding-class-instance-t
o-dictionary/
Possibly addition of compiler callback for validating function calls with variable argument. That
would be the only way I could catch above error at compile time rather than runtime.
Should the callback be global? Or should I have a callback for each registered function?
Callback must be called when loading bytecode too.
2016/09/01 - Another case where compiler callback for function calls is useful is for template
types where the template type may call a method on a script class. With a compiler callback the
template type can check at compile time if the script class actually implements the expected
method, instead of just throwing an error at run-time.
http://www.gamedev.net/topic/681790-problem-in-arrayinterface/
The biggest problem with this is that the application has no control over what pointers exist to
the char buffers, thus it cannot easily determine the lifetime of a particular buffer. This in turn
can lead to buffers being overwritten at inconvenient times.
Consider a C++ function that takes two char*, e.g. strcmp(const char*, const char*). It might be
registered with the script as “strcmp(charptr, charptr)”. If the opImplConv method returns a
pointer to a static buffer there will not be any memory leaks, but on the other hand the two
arguments will always be the same as the buffer will be overwritten in the second call to
opImplConv. On the otherhand, if opImplConv returns a pointer to a new buffer each time, how
can the application know when this buffer can be freed?
This dilemma can perhaps be solved by allowing the application to register the charptr with an
extra flag asOBJ_NOSTORE to tell AngelScript that it must not allow the script to declare any
variables with this type. This way the application can implement a destructor for the charptr to
free the buffer when the temporary variable is no longer in use, i.e. after the function call.
The load times for large application registered interfaces has been improved by a factor of 4 in
version 2.30.2 WIP.
Improve the logic for matching script functions in the module too, to improve load times for large
scripts too. The logic should be similar to the matching of engine functions.
Messages that take multiple arguments, such as %d and %s should number the arguments, as
depending on the translation the argument may not appear in the same order.
The add-ons should be allowed to use the same message catalogue via public methods in the
scripting engine.
http://www.gamedev.net/topic/679958-angelscript-compiler-exhaustive-switch/
asOBJ_NOSTORE
What it does is to tell the compiler that this type cannot be used to declare global variables or
script class members.
The application must be careful not to store the type if it receives it in a function call.
It might be possible to allow a script class to hold the object type, but then the script class must
also become NOSTORE. It must also not be allowed to inherit from non-NOSTORE classes or
interfaces if this happens.
It should still be possible to declare templates, but then the template will also become a
asOBJ_NOSTORE. The template callback should provide the modification of the template
instance flags. This will require some changes, but without it, the engine would have to assume
all templates will store the value, which may not be true.
The type can still be used to declare local variables and function parameters.
The usefulness of this is to control the lifetime of the variable. Applications often have objects
that they need full control over when and where they are created/destroyed, for example, pooled
game entity objects. There is currently no good way of allowing a script to interact with this type
of object, while preventing the script from storing the object for later use (when the object may
no longer exist).
The interface should have members to get type, name, namespace, declaration, offset,
accessMask, configGroup, address, etc
This will remove some members from asIScriptEngine, asIScriptModule, and asIObjectType,
e.g.:
It might even be used to represent a local variable in functions and can be returned by
asIScriptContext for debugging
AngelScript will not guarantee however that all uninitialized variables will have the value 0. For
example in a loop, variables that are not explicitly initialized will take the value of the last
iteration.
forum post
● The calling function should store a copy of the value on the stack as a local temporary
variable, and then just pass a reference to the value to the function.
● The called function will be allowed to modify the value without impacting the calling
function.
● Who should be responsible for calling the destructor on the value? The calling function
or the called function? The answer to this depends on what will be most performatic in
the native function calls.
When a class holds value types the compiler should allocate a bitmask for checking if the value
type has been constructed before access, and if the type has to be destroyed upon exception.
Value types without registered destructors do not need to be checked as it can be considered to
be safe to access the uninitialized memory (even though .
Each bitmask of 8bits can represent 8 members. The compiler should allocate the bitmask
intelligently as pad-byte to properly align the other members.
Specific bytecode instructions should be used to optimize the validation. It can possibly be done
with a single instruction, perhaps even as a replacement for the current asBC_ADDSi instruction
used to offset to object pointer to the member. As currently the value types are stored on the
heap, this check ought to be faster than the current pointer dereference to get to the member.
Support return values with suspending functions - <2014/12/31
Implement a way to suspend the script from a system function, and then return a value when
resuming the execution.
int Wait()
{
Suspend();
return GetReturnValue();
}
Ideally the application should be able to register the int Wait() function, with some special flag,
e.g. SUSPEND. The context should then provide a way to set the returned value before the
Execute() function is called again.
Currently AngelScript doesn't guarantee this alignment for value types. The work around is to
use scoped reference types, where the application can guarantee the alignment as the type is
allocated on the heap.
AngelScript should be able to provide this alignment. There should be a new flag
asOBJ_APP_ALIGN16 that will tell AngelScript the alignment must happen. A flag
asOBJ_APP_ALIGN8 may also be needed.
forum thread
This would require the ability to declare functions and classes in header files, so script sections
can know how to call them without actually knowing the implementation.
Append to existing modules - <2014/12/31
Allow the compilation of new script sections to existing modules. The code should be merged
with the already built code. When the functions overlap, they should either be replaced, or an
error should be given.
I'm not quite sure how this will work, but it is something I would like to add.
The way I'm thinking about implementing this is to have the context call a callback routine when
the current function exits. The callback routine can then set up the next function that should be
executed, thus allowing the context to continue without leaving the Execute function. This will
also make it safer to allow statements in the global scope, as the application would be able to
control and abort any misbehaving scripts.
Another improvement is to have allow an application called function to attach a new script
execution to the current context. This will allow the context to be suspended and resumed, as if
the current context had called the script function directly. When the secondary context is
suspended, it will also suspend the primary context, thus returning from the original Execute()
call. When the primary context is resumed, it will resume the secondary context as well.
This second improvement would be useful for 'dispatch' calls, where the original script calls
another function through an application function by giving the name of the function. It can also
be used by container classes that initialize script objects, e.g. array of elements. The secondary
context can use the chained execution functionality to initialize each element in sequence.
The debug interface must be able to see the secondary contexts so that both can be properly
inspected.
The sequence number given to objects added to the GC can possibly be used to better
determine the age of objects so that they are moved into the old generation at a more optimal
moment.
Perhaps it may be useful to have a method for removing an object from the GC. Of course, this
must not be done for script objects, but it could be useful for application objects. To be quick the
GC would need a structure to allow lookup, e.g. a hash map.
A lot of potential garbage collected object never actually forms circular references. Ideally they
would not be placed in the garbage collector while a known owner is holding it. For example: an
object allocated on the stack wouldn't have to be placed in the garbage collector until the
variable goes out of scope, and then only if the refCount is higher than 1 when that happens.
The longer an object is kept out of the garbage collector, the less work the garbage collector will
have.
(update on 2017/12/07) If I can somehow let the object constructor/factory know if the object is
being created for a local variable on the stack, then the constructor could defer the notification
to the garbage collector to the asBC_FREE call, and the asBC_FREE call could then notify the
gc only if the refCount is higher than 1, which means no other references to the object were
taken.
Perhaps a flag in the script context can be set, which the constructor/factory can check before
calling NotifyGarbageCollectorOfNewObject (or perhaps NotifyGarbageCollectorOfNewObject
itself could check the flag).
Update 2018/01/08
The GC could hold a hash of known owners to objects. This would allow the GC to keep these
objects out of the gc routine until the owner is removed. It would also allow the script engine to
maintain these owners without requiring changes to registered objects, as the owner could be
informed after the creation of the object. The application could also be allowed to add owners to
optimize memory management. Owners should only be added when it is known a circular ref
cannot be created, e.g for stack and global variables. Member variables should only be owners
if it is known that the actual object itself has an owner.
(priority) asOBJ_GC for value types - <2014/12/31
Must be possible to use asOBJ_GC for value types, e.g. the CScriptHandle can indirectly form
circular reference with an object.
Only execute it if the object being freed has the gc behaviours, so we don't impact the execution
negatively for objects that are not even garbage collected.
Some other stuff that isn't necessary to keep around is the enums and typedefs that won't be
used again unless other compilations are done.
Smart pointers is a class that holds a pointer to the real object. The class usually has the ->
operator overloaded so that accessing members of the real object works just like accessing
members through a normal pointer. The smart pointer may also take care of reference counting.
When registering the type controlled by a smart pointer it would be necessary to tell AngelScript
that it is a smart pointer. A new special behaviour is necessary to register the -> operator so that
the VM can automatically access the true object. When registering the members of the object it
would also be necessary to indicate which members are accessed through the -> operator, and
which are accessed directly.
How would it work to pass objects of this type to application functions, or return them from
application functions? Usually objects held in smart pointers, are passed as normal references
to application functions, but sometimes it really is a reference to the smart pointer itself.
It should be completely transparent to the scripts if an object is a smart pointer or not.
Custom operator tokens - <2014/12/31
It would be interesting to allow the application to define it's own operator tokens and the class
methods that implement them. The application could then add operators for things like dot
product or cross product. It may even be possible to simplify the core library with this.
Allow applications to disable global variables in scripts through an engine property. This is
useful if the application plan on having multiple threads execute scripts from the same module
simultaneously and don't want to burden the script writer with having to think about how to
control the access to the global variables to avoid memory corruption.
A switch of fibers from within asCScriptContext::Execute may also leave loose reference on the
context stack if the application registered function that performs the switch is not properly
implemented. To guarantee no loose references, the function should be a void function that
takes no arguments. This way the script can only call it as a simple function call, rather than in a
complex expression.
A script like this would call AddRef and Release on the member variable mArr on each access
to the index operator. This is done because the compiler currently has no way of knowing that
mArr is actually going to stay alive throughout the call to the index operator. Without the AddRef
and Release calls the array could potentially be destroyed in the middle of the call, causing a
crash on the application.
To improve performance the compiler could be changed to recognize that the same member is
accessed multiple times in the function and only calls the AddRef and Release once.
I could also add an engine property to allow the application developer to decide whether the
AddRef and Release calls aren’t necessary if he/she trusts the script writers not to write bad
scripts.
Stack corruption happens for example when AngelScript passes a structure by value, but the
C++ function expects a reference, or if the C++ function returns a value in memory, but the
function was registered as void.
Can the calling convention be determined with means of templates? I.e. is it possible to use
templates to determine whether a function should be asCALL_CDECL or asCALL_STDCALL?
Example code from SiCrane
When the argument is an object handle and takeOwnership is false, the context will
automatically increase the reference count. If takeOwnership is true, the context won't increase
the reference count, which means the application must not release it.
When the argument is an object by value, the context will automatically create a copy of the
object, unless takeOwnership is true, in which case the context will store the object pointer and
then release it upon completion.
Parameters can still be declared as const, but it won't make a difference to the caller, as it will
always pass parameters expecting them not to be modified. This may affect function
overloading.
Object handles
The caller should be the owner of the object handles passed to functions. This will reduce the
number of addref/release calls made during script execution. It will also simplify the registered
application functions, as they will no longer have to release the handles received in parameters.
The script compiler will need to make sure that all object handles passed to functions are stored
in local variables (or parameters in the function). This is similar to how the compiler currently
guarantees the validity of references.
In order to avoid abrupt changes to applications I'll implement this feature as an engine
property: asEP_CALLER_OWNS_HANDLE. The value will be false by default. After a few
releases I can change the default to true. Then after even more releases, the engine property
can be removed all together.
Can this be allowed even for safe references? Should an engine property by used to turn on/off
this feature?
Can we determine the asOBJ_APP flags through templates without loosing portability?
This could even be used to access properties, using the property accessor style. An expression
like this: int val = object->prop; would be compiled as GetObjectProp(object, 'prop', val), where
the last parameter is an output parameter. If the property type doesn't match the output
parameter then an exception is thrown.
It will probably need a new behaviour on the string type, as well as a new indicator when
registering the function that needs the translation.
Suggestion by Dentoid.
Statement blocks can be parsed one statement at a time, which will improve memory utilization
when compiling large script functions.
Need engine properties to discard information that are not needed for runtime operation, i.e.
enum values, type defs, local variable names, line numbers, etc.
References to members of objects can probably be identified if the serializer knows the size of
objects. But if the reference is to some random location then the serializer would have no way of
knowing how to identify it.
Basically, the application would indicate functions that may be evaluated at compile time, if the
arguments are all constants, e.g. sin(0.5), or similar constant expressions. The compiler would
then replace these expressions with the final result, thus avoiding the runtime call to the
function.
I'm in doubt how useful this would actually be. After all, how often are functions that can be
evaluated at compile time used in scripts? Also, it would probably only be possible to use when
the function returned a primitive type, which makes the feature even more restrictive.
Perhaps there is a way to make it work for functions that return non-primitives. If so, it could
possibly be used on the string factory which is definitely used sufficiently to make something like
this worth it.
Update 2017/02/20:
https://www.gamedev.net/topic/686589-debugger-determine-a-breakpoint-line-is-valid/
Look into what can be done to optimize the application interface to better support what is done
in above thread.
Consider changing the methods that register properties, methods, and behaviours for objects to
take an asITypeInfo pointer instead of the name of the object. Example:
asITypeInfo *type = 0;
engine->RegisterObjectType(“object”, 0, asOBJ_REF, &type);
engine->RegisterObjectBehaviour(type, asBEHAVE_ADDREF, “void f()”, …
Applications that cannot pre-cache function and method pointers that will be called, are severely
penalized when doing dynamic lookup of method pointers and functions.
The methods in the objects must be ordered alphabetically so that it is easy to do a quick lookup
by name.
The comparison of signature can perhaps be made quicker by creating a separate object to
store the signature, i.e. parameter types and return type. This way the lookup can simply
compare the pointers to check if they are identical.
Evaluate some way of allowing the application to store user data for all registered entities:
- global properties
- member properties
The properties would require a new interface. Though the solution should avoid adding an extra
pointer for each property declaration to avoid unnecessary memory usage for applications that
don’t use userdata. (Perhaps this is not such a big concern considering that it is only for the
declarations?)
When the function is called by the script, the type of T would be passed to the application along
with the argument, so that the application can dynamically determine the type of the argument.
A compiler callback would also be needed to allow the application to evaluate if the function can
be used with the desired type or not.
The restrictions would be similar to member methods of template types.
The string add-on should be implicitly constructed from the text type. The text type itself has no
operators, but a string type can be directly compared with the text type.
The text literal should be stored as a char array. The length of the array should be stored 4
bytes before the address. This way registered functions that receive a text literal can
immediately know the size without doing a strlen().
Application registered functions that can receive a normal const char* or a text type must always
use strlen() to determine the length.
If the string factory is registered then the built-in type is not used, instead the string literals will
work as they do today.
2016/11/16 - With the external string cache that I’m planning to implement, does it really make
sense to add the internal built-in type? Probably not, as the problems that I meant to solve with
this will already be solved with the external string cache.
2017/11/19 - The external string cache has now been implemented in version 2.32.0.
Adding a UTF-16 code scanner for the compiler might be interesting. This would allow
applications to avoid having to convert script code to UTF-8 before compiling it.
Update 2017/Nov/19 - The conversion from UTF16 to UTF9 should be done by the
preprocessing pass.
Update 2017/Nov/19 - The removal of function calls and arguments should be done in a
pre-processor pass.
(unplanned) Allow script classes to inherit from application classes -
2009/05/22
Investigate the possiblity of allowing script classes to derive from application registered classes.
May work if the application class derives from the internal asCScriptStruct class.
Some info
Given that inheritance can be supported through proxy classes as described in the manual, I’ll
probably not implement this. (2015/07/13)
forum thread
Use an engine property to determine whether system errors should be sent to the message
callback or just the geterrors function. Default to sending to message callback too.
I currently don't think this is necessary. Someone that would prefer a GetErrors like function can
easily implement it using the existing message callback. (2015/06/30)
Do I really want to allow this? What would it mean? AngelScript doesn't allow implicit
conversions of types to bool. Why should it allow a non boolean type to use the 'not' operator?
Interface ids will however remain the same for both modules.
A module can also be cloned by saving the bytecode from one module and then reloading it into
another.
Script language
(priority) Disable bitwise operators on non-integer types - 2017/11/02
https://www.gamedev.net/forums/topic/693281-bitwise-operator/
It is not meant to be possible to perform bitwise operations on float and double. Currently the
compiler implicitly converts the types to integers and then do the operation. This is likely to
produce unexpected results. The compiler should instead give an error.
I would like to request a pure specifier a-la GNUC--i don't know if it's in other versions--but it indicates that the
function does not access memory outside of it's own arguments or call any non-pure functions. If this is
specified for a registered function it would indicate that it may be evaluated at compile time, which is useful if
you're hashing a lot of strings, or something similar; otherwise it would indicate other optimizations to the
compiler, but to me the valuable thing is just the first part without the optimizations, because if i'm calling a pure
function from C++ then i know what mutexes i need to lock before calling it.
https://en.wikipedia.org/wiki/Pure_function
Patrick Jeeves also extended this idea to script entities, as some of the application interface may be
implemented in the script.
It would be necessary to be able to mark any entity as deprecated, functions, global variables, types, class
members, enum values.
Study how it can be allowed to implement the << operator the way it works in C++ for value
types in AngelScript.
At least with asEP_ALLOW_UNSAFE_REFERENCES turned on, the compiler should call the
<< operator on the actual object rather than making a copy first.
With asEP_ALLOW_UNSAFE_REFERENCES turned off it might be possible to support this by
having a keyword to tell the compiler that the << operator will always return a self reference.
That way the compiler can ignore the actual returned reference and instead reuse the original
reference that it knows is safe.
Thoughts:
- non-const object methods must be guaranteed to work upon the original object, not a copy of the object
- object methods that return a reference must be assumed the reference can be to itself, thus the object the
method was called upon must be kept alive until the returned reference is no longer in use. (in long chained
expressions this may mean the object must be kept alive until the very end of the statement)
- the problem with these two statements above in combination with a value type is that it is not always possible
to guarantee the life time of the original object
- if the original object is stored in a volatile location, e.g. in a dynamically allocated buffer in an array object. the
buffer can easily be freed during an expression, thus destroying the original object
To make it work without sacrificing safety, the compiler must work on a temporary copy of the object, that it can
guarantee to stay alive. At the end of the statement, the value of the temporary object must be assigned back
to the original object (if it can be guaranteed to still be alive).
To be one perfectly safe, I would have to prohibit self modifying methods on value types. All operations with
value types must be expected to create a new instance of a value. This would severely limit the use of
registered object types.
This would be useful for container classes as it would be possible to implicitly return the
contained object to invoke a method on it, or access a property. The weakref add-on for
example could allow a syntax like this:
weakref<character> player;
player->doSomething();
weakref<character> player;
player.get().doSomething();
It wouldn’t be so useful for generic containers like any or dictionary, since the -> wouldn’t
know what type to return at compile time. For those type of containers something else is
needed, e.g. a mechanism for dynamically invoke methods.
How useful is this really? Is it truly worth it to add the -> operator just for cases like weakref?
Currently it isn’t possible to chain assignments when the lvalue is a property accessor because
after evaluating the set accessor the expression becomes void.
The compiler needs to verify if there is still another use of the property and keep the value, or
perhaps evaluate the get accessor to get the updated value.
Suggested syntax:
class Foo
{
Foo() { value = 0; }
int value;
}
Apparently C++17 has introduced the concept of declaring variables within the if-condition. The
variable is visible in both the possible outcomes, but not outside the if-statement.
Int b;
if( int a = func(); a < 0 )
b = -a;
else
b = a;
It follows the same way of thinking as that of the for-loop, which makes it consistent and more
easy to understand.
Default implementation of opEquals and opCmp - 2016/09/04
http://www.gamedev.net/topic/681790-problem-in-arrayinterface/
As IronHawk suggests in the post, perhaps the pointer can be directly compared for
equality/relation if no opEquals or opCmp method is implemented.
I can’t think of a simple syntax for this though, given that AngelScript is strongly typed.
if( a isnt b )
Though perfectly understandable I’m not a huge fan of having lots of aliases in the language.
There are already more than I would like.
class Dummy
{
Dummy(int i){};
};
Dummy testConstruct()
{
return 2; // error, requires Dummy(2)
};
Implicit type conversion for is operator - 2013/02/15
class A{}
class B : A{}
if( a is b )
{}
https://gcc.gnu.org/onlinedocs/cpp/Line-Control.html
The CScriptBuilder can use this to inject code in the script section rather than include code as a
separate script section.
If a module doesn't declare one of the shared variables the corresponding slot can be left
empty. That way it would still be possible to dynamically include it afterwards with
CompileGlobalVar.
When a shared global var is compiled in a new module it must not use a slot that is currently
occupied in any module.
When loading bytecodes the global variable slots may need to be adjusted if the existing global
vars use different slots.
With unsafe references turned on, they would also support non-reference types and work the
same way as & in C++.
Even with unsafe references turned off, it might be possible to support references to primitive
types, as long as the compiler can guarantee that the reference will stay valid for the entire
scope. It might for example hold a reference to a member of a class, by storing a hidden handle
to the class that will guarantee that the object stays alive.
One possible solution that would allow the implementation of object states without causing
impact to all object types is to have a specific keyword for it.
state Calm
{
void DoSomething() {}
}
}
Or perhaps simply the existence of a state in the class implements the state machine for the
object.
All calls to methods in states must go through the state machine lookup, transparently, even
calls made internally by the class methods. This should be similar to virtual method lookups,
except the lookup table changes for each state.
The state could be kept in the object as a pointer to the lookup table for the object methods.
When the state is changed the lookup table pointer is modified in the object.
1. Any script can declare shared entities, and can provide different implementations. The
order in which the scripts are compiled would in such cases produce different results.
2. A shared entity cannot use a non-shared entity in the module in which it is implemented,
thus it is not possible to have public interface with private implementation.
3. Currently there is no such thing as a shared global variable (though it might be
implemented someday)
4. There is no way to control which scripts share which entities.
5. Hot-loading scripts after change with shared entities can be difficult, since the original
shared entity is still in memory and will not be modified.
Some of these flaws can probably be solved with a more explicit ‘export’ and ‘import’ feature.
When a script ‘export’s an entity, the compiler will give an error if another script has already
exported the same entity. When a script ‘import’s an entity, the compiler will give an error if no
other script has exported it first.
An imported entity doesn’t mean it is owned by the module that imported it, instead the module
that imported it is simply allowed to see and use it. When the module that exported the entity is
discarded the exported entity will become invisible (and freed when the last reference is
removed). This will allow the script to be hot-loaded without getting any conflict with the
originally compiled exported entity.
The exported entity can access non-exported entities in the same module it was declared in, for
example a global variable, or classes, that shouldn’t be shared with other modules.
Control over which features that can import exported entities can obey the access flags of the
modules. Unless both modules have at least one bit in common the import won’t be allowed.
The shared entities feature doesn’t have to be removed by the export and import feature, but it
may likely become obsolete and unused.
To better support mixin functions the compiler should have the concept of auto variable types.
This is a variable type that assumes the type of the assigned expression. For example:
The return type would be the type of the operator + that works on the types of a and b,
depending on how the mixin is used in the code.
Closure - 2011/10/10
Forum post. C++11 closures
This would avoid safety issues with the closure potentially accessing values that are no longer
valid if the closure is invoked after the original scope no longer exists.
On the other hand, it will not support multithreading (or co-routines) updating the same
variables, but that would likely not be a concern anyway.
(update 2015/07/17) Another option could be that a weakref is placed on the stack at the same
scope as any variables the closure accesses. The closure can then check the weakref behind
the scenes before accessing any variables from outside the function.
(update 2015/07/17) Anonymous functions are available in the current 2.30.2 WIP, with the
following syntax: (update 2018/02/17) Added support for explicitly informing parameter types to
resolve ambiguities.
In the future I may perhaps allow explicitly giving the return type too:
This concept could be used for global variables too. A shared global variable would be shared
between all modules, and could thus be accessed by shared classes and functions too.
When global variables are reset, should the shared globals also be reset? Probably need a flag
to let the application choose.
Should probably have an engine property to disallow sharing global variables all together.
Sharing of global variables may provide a security risk as the application may not have full
control over who has access to what.
The sharing at this level will only be implemented once I can find a good way of providing proper
protection. I'm thinking that maybe the use of namespace will be helpful. I could provide a way
to allow or disallow sharing in specific namespaces on a module by module basis.
The idea is to expand on these to let the application set access masks for specific namespaces,
and also to disallow sharing in namespaces that haven't been given an access mask. It should
of course also be possible leave sharing open for all namespaces.
A namespace with a stricter access mask may call code in a namespace with looser access
mask, but not the other way. This is to prevent that a module shares a function from a
namespace it has access to, and that function without knowledge of the module uses methods
or worse, global variables from another namespace that the module doesn't have access to.
class Level
{
Level() default;
Level(const Level &in copy) default;
Level &opAssign(const Level &in copy) default;
}
The compiler should of course warn when providing the default implementation. If the return
value cannot be created, e.g. if there is no default constructor the compiler will give an error
instead.
It must be possible to have simultaneous loops on the same container, so the current loop
position must be external to the container (like a C++ std::vector iterator).
If the content of the container is modified during the execution of the loop, or the container itself
is destroyed somehow, then the application must not crash. The actual behaviour would be
undefined though, an exception may be raised, or the loop may continue as normal, perhaps
skipping some elements.
The one thing you lose with this is an explicit assignment operator for copying content as the
assignment operator will perform a handle assignment rather than copying the content of the
object. (Though it may be possible to create another operator for this.)
The advantage of implementing this would be a cleaner language syntax, in that the @ can be
completely removed, thus also simplifying the compiler.
I haven't decided whether I really want to implement this, but I don't think it would be too difficult.
If I decide to implement this, it would be for version 3.0.0 together with the change to the
parameter references.
Note, AngelScript already supports implicit handle types by turning on the engine property
asEP_ALLOW_IMPLICIT_HANDLE_TYPES.
If I do this, I have to remember that variables should not just be initialized to zero when entering
a new function. The variables must be initialized to zero every time a new scope is entered, e.g.
each iteration of a loop.
This might be solved by using push and pop to handle the scope. When push is used to
increase the stack space it will be incremented with zeroes.
I may be able to do this initialization only if the compiler discovers that the variable is used
without being initialized first. That would avoid unecessary bytecode being generated, and thus
not impact performance.
Another case for the need for default initialization: forum post
It should be possible to throw uncatchable exceptions. SetException needs an optional flag for
this.
This also means that global variables will be initialized in the order they are declared, and that a
global variable doesn't see variables declared after it.
Functions and class declarations will still be seen from above themselves. This also means that
if a function is called from the top of the file, it can access variables that haven't been fully
initialized yet (it will only see the default value). In case of accessing object variables, this will
result in a null pointer exception.
The static array types should be treated as an easy way to declare multiple variables that can
be accessed through an index.
The static array type can have a size that is defined at run time, e.g. with a parameter name. In
this case the memory for the array is allocated on the heap, plus a hidden variable that gives the
size of the array. The same thing happens if the size of the array is fixed, but too large, e.g.
larger than 100 elements.
The static array type will only be implemented when the dynamic array type has been removed
from the language.
When passing a static array by parameter, or returning it, the size of the array must be fixed.
That way it is not necessary to pass the size of the type as a separate variable.
It will not be possible to store a handle to a struct, nor will the struct be able to inherit from
another class or struct.
Reference types must not be allowed in data structures. Handles can be safely used in the
structures though.
To guarantee safety in the scripts the implicit copy constructor of a value type must have certain
restriction. This is to guarantee that the constructor itself doesn't destroy the structure that is
being copied before it is time. Should I prevent the user from implementing a copy constructor,
or should I simply automatically generate a copy constructor that will be used for the implicit
calls?
A method implemented for a data structure must not allow returning a reference to a member as
there is no guarantee the object itself is not destroyed by the method.
In that same sense, how can I guarantee that the object isn't destroyed during the execution of
the function.
Maybe data structures will not be allowed to have any member methods at all.
Even though pointers cannot be used in an environment where security is wanted, they are still
very useful when the scripts are written for prototyping.
Implement class methods outside the class declaration - <2014/12/31
Yes, I intend to allow implementation of class methods outside the class, just as in C++. I just
haven't gotten around to it yet.
The asCDataType object that defines a typedef should have a subtype to the true type. All
functions that ask what kind of type the typedef is should return what the subtype returns.
http://www.gamedev.net/topic/675384-typedef-for-application-registered-types/
The script will have to add an extra keyword for parameters if the argument is support to be a
return value, something like: func( (out)arg );
Forum threads
http://www.gamedev.net/topic/680460-delayed-calls/ - 2016/07/23
https://www.gamedev.net/forums/topic/692430-variadic-arguments-support/ - 2017/10/02
Doing this would make the language much more consistent in the way it works, thus easier to
understand for the script writers. The impact in performance and to the application developers
shouldn't be too big.
AngelScript would still permit the application to register functions that take parameters by
reference, but only were it can be translated to the permitted AngelScript syntax. A const type &
could just as well be sent by value, and a type & would be translated to either type @ or out
type.
This is obviously a bit of a step backwards, so it's not a decision to be taken lightly. If I do it, I'll
probably change the version number to 3.0.0. A replacement should also be available at the
time this is implemented, e.g. the option to turn on unsafe references, and/or allow use of
pointers.
In theory the compiler could create a list factory for the script class automatically. But I’m not so
sure if this will cause conflicts with more complex list patterns, e.g. like those used by the
dictionary.
When a function with many arguments have default arguments and the caller just want to
change the last, it is only possible by providing all the arguments (or use the named arguments).
With a ‘default’ keyword the caller wouldn’t have to explicitly inform all the arguments, instead
just tell the compiler to use whatever was declared as a default argument.
This simply makes all shared entities in the namespace bar visible to the module.
Update 2017/03/08
The problem with this is that a namespace is not closed, and can have other entities added to it
afterwards. This could mean that if two different modules add different shared entities in the
same namespace, and a third module uses that shared namespace to get access to the shared
entities, the result will be different depending on the order of the compiliation of the modules. It
may not be such a terrible problem, but since it may be difficult for the script writer to control the
order the modules are loaded (unless the application provides this control) I’ll have to give this
some more thought before implementing it.
I don't feel the benefit with this feature is high enough to warrant the added complexity in the
engine.
When the property is accessed through the interface, the VM would have to use a lookup table
to determine the exact location of the property in the object (similar to how virtual functions
work).
I don't feel the benefit with this feature is high enough to warrant the added complexity in the
engine.
Potential drawback: Application registered classes won't always be derived from the super
class, thus separating them from the script classes. It might be possible to allow application
registered classes to implement script interfaces through automatically generated proxy classes.
Advantage: All script classes will have a common denominator, facilitating the storage of them.
An application registered class that derive from an AngelScript class interface, can be registered
to have this same common denominator which should make it possible to have script classes
inherit from application registered classes.
With the use of the ASHANDLE type, which work for both script classes and application types,
there is no longer any need for this.
I don’t consider this valuable enough in a script language to add this complexity to the engine.
I don't think I'll implement this, since it is a rather obscure syntax, and not really necessary.
Especially after all reference types will be treated as handles.
Mix-ins is planned for a future release and will provide the support for code re-use, and is much
easier to implement. Once that is implemented, there is really very little that multiple
inheritances provides as additional advantages, and is far outweighed by the extra complexity in
the library.
The case of symbols should be stored as when first declared, but the matching can be done
using caseless comparisons.
I’m not so sure about this. It would have little benefit, and would probably decrease the
performance of the compiler.
Add-ons
Script array
(unplanned) Declaring multidimensional arrays - 2010/02/07
Allow multidimensional arrays to be given size upon initialization, e.g.
I’m not so sure about this anymore. It makes more sense with the syntax int[][], but not so much
for array<array<int>>. Besides, the grid add-on is a better solution for 2D arrays. - 2015/07/28
If value types are stored inline, then a resize of the array must not use memmove and memcpy,
because the objects may potentially hold internal references to themselves. Instead the resize
will have to use a copy constructor (or move constructor in C++11) to initialize each member of
the new buffer, then destroy the members of the old buffer. (See:
http://www.gamedev.net/topic/646508-weird-string-crash/)
I’m not sure this optimization is actually for the better. In some cases storing the objects inline
will give a better performance, but in other cases it will give a worse performance. For example,
a resize or sort will perform much better the way the implementation is as it will not be
necessary to copy the actual objects with the change in the buffer. - 2016/03/28
The foreach method should take a function pointer that takes a reference to the element, and a
handle to a generic type. The scripts will then be able to implement custom logic to do for each
of the entries, and have a way to return that to the called through the generic handle.
The array needs to implement a lock mechanism to prevent crashes in case the script callback
attempts to resize the array, which would invalidate the reference to the element. If this is done
the resize will fail with a script exception.
Another example would be to have a custom compare callback routine for sorting the elements
in the array.
Though the arguments are correct, I’ll probably keep the code as it is, i.e. returning a signed -1
when not finding the value. I don’t really think that any array in a script will have more than
2^31-1 elements anyway.
If I do change this, remember to do the same for the string type, which also returns -1 when not
finding a value.
String add-on
Add methods for working with Unicode - 2015/06/18
The strings are already stored as UTF8 by default (can be modified by engine property to store
as ANSI or UTF16). The string add-on needs to have methods for manipulating the encoded
characters in the string. For example:
● countChars() would traverse the string and count all the characters
● getCharAt(uint) would return the unicode character. The index would be the byte
position, and the code would find the start of the encoded character based on that
position.
● setCharAt(uint, uint) would replace the unicode character. As different characters can
occupy different amount of bytes this function may need to reallocate the string.
● nextChar(uint)/prevChar(uint) would be methods to find the byte position of the next/prev
character in the buffer.
Script builder
#inject directive in CScriptBuilder - 2015/04/25
This directive works similarly to #include, except that the directive will not add an extra script
section, instead it will inject the included code in the current script section. #inject can also be
done multiple times in the same script section.
When the CScriptBuilder pre-processes a script file and encounters the #inject directive it will
continue to pre-process inside the injected file just as if it had been part of the original file in the
first place.
Care must be taken to avoid infinite recursive #injects. The builder must be able to detect this,
perhaps by having a limit on the number of nested injects.
This feature also depends on the script compiler understanding the #line directive, so the
compiler can continue to report the error messages correctly even though the script section is
composed of multiple files.
The #include directive must be processed inline, so that #defines in an included file only affect
the rest of the script after the include directive
● Should be done by a helper function that takes a string and returns the obfuscated
string.
● The helper function should take the obfuscator function as
● Obfuscation should only be done once it is known the original script compiles correctly
● Application registered functions and types must also be registered with their obfuscated
names
● Need to be able to run the string used for registration through the obfuscation to make it
easy to register
Add support for pre-compiler directives #error and #warning (suggestion by Amir Ramezani)
Tabs are normally translated to 2, 4, 8 space characters when viewing the code in an editor.
Perhaps the builder should understand this and translate the column number to the appropriate
column using the same logic.
The application would have to inform the tab size used though.
This would make it easier for an application to implement it’s own routine for loading files. For
example to allow multiple search paths when including a script file, as mentioned by Solokiller in
the thread above.
Debugger
Allow debugger to call opIndex - 2015/06/18
If the object type support opIndex the debugger should be allowed to call it for inspecting
elements. The debugger should only call the const variant though so as not to modify the object.
It should be possible to call with both int arg (array) and string arg (dictionary), maybe even
multiple args separated by , (grid)
(priority) Allow user to set how members are expanded when printing the values -
2015/06/18
The debugger should have two commands that allow user to set how many elements should be
expanded, and how many recursive levels should be expanded.
When typed without argument the command should list the current settings and print the help
message.
It needs to show how to set up a listener socket in the application that the remote debugger can
connect to, and then how the application can be paused for debugging as the remote debugger
connects.
The library may for example be called, libangelscriptx.a, or libasextra.a. Like the main library it
should have variants for debug and 64bit.
type<int> typeOfInt;
type<Object> typeOfObject;
if( typeOfInt == typeOfObject )
{
... blah
}
It would also need a form of getting the type of an expression, or variable. Possibly with the use
of the variable argument. And of course there need to be a common base class for all of these.
http://www.gamedev.net/topic/662663-templated-type-of-object-add-on/
A built-in typeof function is not necessary, since the expression should be evaluated anyway,
especially for polymorphic objects that needs to be evaluated at runtime to get the true type.
The 'var' type should have all the mathematical operators with overloads for all primitive types (+
the string type). It should also have methods like asString, asInt, asDouble to convert the variant
to a primitive.
The 'any' type could perhaps be improved to allow storing index values by implementing the
index operator.
This function is already implemented to use the generic calling convention and doesn’t need to
be wrapped. The WRAP_FN should throw an error in this case to avoid runtime errors.
Problem identified in e-mail conversation with burtlong.
Of course, since most applications have their own way of creating the contexts, and scheduling
the executions. Perhaps it's better to just have automatic pushing of arguments on the context
stack.
// Generates
void arg_pusher(asIScriptContext *ctx, float a1, string a2)
{
new(ctx->GetAddressOfArgLocation(0)) float(a1);
new(ctx->GetAddressOfArgLocation(1)) string(a2);
}
The context manager should also implement intelligent calls to the garbage collector.
The context manager should also permit debugging, by setting breakpoints and step-by-step
execution.
It should be possible to create the weakref by passing it the objecttype of the subtype, and not
the template type itself. The weakref constructor can do the tedious work to figure out the
correct template type.
int global = 0;
void func()
{
script.SetGlobalVar('global', 42);
}
It could even allow the script to inspect local variable on the callstack.
Perhaps this same add-on could be combined with the context manager add-on to create and
manage co-routines and threads and helper add-on to provide ability to evaluate strings.
This add-on could also allow executing strings, i.e. ExecuteString, and compile new functions,
etc.
The plug-in should be loaded ‘on demand’ with a pre-processor command, e.g. #plugin math (or
#pragma plugin math).
The plug-in dynamic library will need two functions, one to check for which angelscript version it
was prepared for, and another to register the plug-in with the script engine.
I need to think about how this can best be solved. Right now the
asBEHAVE_TEMPLATE_CALLBACK has to be implemented by the offline compiler, thus
making it difficult to prepare a generic compiler that fits all.
The ‘dictionaryValue’ needs to hold a reference to the engine by itself so that it can release its
content upon exit. To avoid extra pointer in the type, this could be a union with the object
pointer, so that the extra engine pointer is only stored when actually needed.
Socket add-on - 2017/11/19
Implement a simple socket add-on. Only client socket is needed to begin with. It should have
the ability to look-up hostnames, so the script is not required to use ip addresses.
Later on asynchronous communication can be supported with buffering, and wait signals for
co-routines/threads.
AngelScript site
Add links to wiki - 2012/12/28
● 3
● 4
● 5
● http://blog.imgtec.com/powervr-developers/angelscript-2-30-1-is-out-adds-support-for-mi
ps-cpus-on-linux-and-android
● https://upvoid.com/devblog/2013/02/choosing-a-scripting-language/