initial commit

This commit is contained in:
2021-04-12 20:25:02 +02:00
commit 3d7202a915
806 changed files with 194211 additions and 0 deletions

View File

@@ -0,0 +1,581 @@
#ifndef AS_GEN_WRAPPER_H
#define AS_GEN_WRAPPER_H
#ifndef ANGELSCRIPT_H
// Avoid having to inform include path if header is already include before
#include <angelscript.h>
#endif
#include <new>
namespace gw {
template <typename T> class Proxy {
public:
T value;
Proxy(T value) : value(value) {}
static T cast(void * ptr) {
return reinterpret_cast<Proxy<T> *>(&ptr)->value;
}
private:
Proxy(const Proxy &);
Proxy & operator=(const Proxy &);
};
template <typename T> struct Wrapper {};
template <typename T> struct ObjFirst {};
template <typename T> struct ObjLast {};
template <typename T> struct Constructor {};
template <typename T>
void destroy(AS_NAMESPACE_QUALIFIER asIScriptGeneric * gen) {
static_cast<T *>(gen->GetObject())->~T();
}
template <>
struct Wrapper<void (*)(void)> {
template <void (*fp)(void)>
static void f(AS_NAMESPACE_QUALIFIER asIScriptGeneric * /*gen*/) {
((fp)());
}
};
template <typename R>
struct Wrapper<R (*)(void)> {
template <R (*fp)(void)>
static void f(AS_NAMESPACE_QUALIFIER asIScriptGeneric * gen) {
new (gen->GetAddressOfReturnLocation()) Proxy<R>((fp)());
}
};
template <typename T>
struct Wrapper<void (T::*)(void)> {
template <void (T::*fp)(void)>
static void f(AS_NAMESPACE_QUALIFIER asIScriptGeneric * gen) {
((static_cast<T *>(gen->GetObject())->*fp)());
}
};
template <typename T, typename R>
struct Wrapper<R (T::*)(void)> {
template <R (T::*fp)(void)>
static void f(AS_NAMESPACE_QUALIFIER asIScriptGeneric * gen) {
new (gen->GetAddressOfReturnLocation()) Proxy<R>((static_cast<T *>(gen->GetObject())->*fp)());
}
};
template <typename T>
struct Wrapper<void (T::*)(void) const> {
template <void (T::*fp)(void) const>
static void f(AS_NAMESPACE_QUALIFIER asIScriptGeneric * gen) {
((static_cast<T *>(gen->GetObject())->*fp)());
}
};
template <typename T, typename R>
struct Wrapper<R (T::*)(void) const> {
template <R (T::*fp)(void) const>
static void f(AS_NAMESPACE_QUALIFIER asIScriptGeneric * gen) {
new (gen->GetAddressOfReturnLocation()) Proxy<R>((static_cast<T *>(gen->GetObject())->*fp)());
}
};
template <typename T>
struct ObjFirst<void (*)(T)> {
template <void (*fp)(T)>
static void f(AS_NAMESPACE_QUALIFIER asIScriptGeneric * gen) {
((fp)(
Proxy<T>::cast(gen->GetObject())));
}
};
template <typename T, typename R>
struct ObjFirst<R (*)(T)> {
template <R (*fp)(T)>
static void f(AS_NAMESPACE_QUALIFIER asIScriptGeneric * gen) {
new (gen->GetAddressOfReturnLocation()) Proxy<R>((fp)(
Proxy<T>::cast(gen->GetObject())));
}
};
template <typename T>
struct ObjLast<void (*)(T)> {
template <void (*fp)(T)>
static void f(AS_NAMESPACE_QUALIFIER asIScriptGeneric * gen) {
((fp)(
Proxy<T>::cast(gen->GetObject())));
}
};
template <typename T, typename R>
struct ObjLast<R (*)(T)> {
template <R (*fp)(T)>
static void f(AS_NAMESPACE_QUALIFIER asIScriptGeneric * gen) {
new (gen->GetAddressOfReturnLocation()) Proxy<R>((fp)(
Proxy<T>::cast(gen->GetObject())));
}
};
template <typename T>
struct Constructor <T ()> {
static void f(AS_NAMESPACE_QUALIFIER asIScriptGeneric * gen) {
new (gen->GetObject()) T();
}
};
template <typename A0>
struct Wrapper<void (*)(A0)> {
template <void (*fp)(A0)>
static void f(AS_NAMESPACE_QUALIFIER asIScriptGeneric * gen) {
((fp)(
static_cast<Proxy <A0> *>(gen->GetAddressOfArg(0))->value));
}
};
template <typename R, typename A0>
struct Wrapper<R (*)(A0)> {
template <R (*fp)(A0)>
static void f(AS_NAMESPACE_QUALIFIER asIScriptGeneric * gen) {
new (gen->GetAddressOfReturnLocation()) Proxy<R>((fp)(
static_cast<Proxy <A0> *>(gen->GetAddressOfArg(0))->value));
}
};
template <typename T, typename A0>
struct Wrapper<void (T::*)(A0)> {
template <void (T::*fp)(A0)>
static void f(AS_NAMESPACE_QUALIFIER asIScriptGeneric * gen) {
((static_cast<T *>(gen->GetObject())->*fp)(
static_cast<Proxy <A0> *>(gen->GetAddressOfArg(0))->value));
}
};
template <typename T, typename R, typename A0>
struct Wrapper<R (T::*)(A0)> {
template <R (T::*fp)(A0)>
static void f(AS_NAMESPACE_QUALIFIER asIScriptGeneric * gen) {
new (gen->GetAddressOfReturnLocation()) Proxy<R>((static_cast<T *>(gen->GetObject())->*fp)(
static_cast<Proxy <A0> *>(gen->GetAddressOfArg(0))->value));
}
};
template <typename T, typename A0>
struct Wrapper<void (T::*)(A0) const> {
template <void (T::*fp)(A0) const>
static void f(AS_NAMESPACE_QUALIFIER asIScriptGeneric * gen) {
((static_cast<T *>(gen->GetObject())->*fp)(
static_cast<Proxy <A0> *>(gen->GetAddressOfArg(0))->value));
}
};
template <typename T, typename R, typename A0>
struct Wrapper<R (T::*)(A0) const> {
template <R (T::*fp)(A0) const>
static void f(AS_NAMESPACE_QUALIFIER asIScriptGeneric * gen) {
new (gen->GetAddressOfReturnLocation()) Proxy<R>((static_cast<T *>(gen->GetObject())->*fp)(
static_cast<Proxy <A0> *>(gen->GetAddressOfArg(0))->value));
}
};
template <typename T, typename A0>
struct ObjFirst<void (*)(T, A0)> {
template <void (*fp)(T, A0)>
static void f(AS_NAMESPACE_QUALIFIER asIScriptGeneric * gen) {
((fp)(
Proxy<T>::cast(gen->GetObject()),
static_cast<Proxy <A0> *>(gen->GetAddressOfArg(0))->value));
}
};
template <typename T, typename R, typename A0>
struct ObjFirst<R (*)(T, A0)> {
template <R (*fp)(T, A0)>
static void f(AS_NAMESPACE_QUALIFIER asIScriptGeneric * gen) {
new (gen->GetAddressOfReturnLocation()) Proxy<R>((fp)(
Proxy<T>::cast(gen->GetObject()),
static_cast<Proxy <A0> *>(gen->GetAddressOfArg(0))->value));
}
};
template <typename T, typename A0>
struct ObjLast<void (*)(A0, T)> {
template <void (*fp)(A0, T)>
static void f(AS_NAMESPACE_QUALIFIER asIScriptGeneric * gen) {
((fp)(
static_cast<Proxy <A0> *>(gen->GetAddressOfArg(0))->value,
Proxy<T>::cast(gen->GetObject())));
}
};
template <typename T, typename R, typename A0>
struct ObjLast<R (*)(A0, T)> {
template <R (*fp)(A0, T)>
static void f(AS_NAMESPACE_QUALIFIER asIScriptGeneric * gen) {
new (gen->GetAddressOfReturnLocation()) Proxy<R>((fp)(
static_cast<Proxy <A0> *>(gen->GetAddressOfArg(0))->value,
Proxy<T>::cast(gen->GetObject())));
}
};
template <typename T, typename A0>
struct Constructor <T (A0)> {
static void f(AS_NAMESPACE_QUALIFIER asIScriptGeneric * gen) {
new (gen->GetObject()) T(
static_cast<Proxy <A0> *>(gen->GetAddressOfArg(0))->value);
}
};
template <typename A0, typename A1>
struct Wrapper<void (*)(A0, A1)> {
template <void (*fp)(A0, A1)>
static void f(AS_NAMESPACE_QUALIFIER asIScriptGeneric * gen) {
((fp)(
static_cast<Proxy <A0> *>(gen->GetAddressOfArg(0))->value,
static_cast<Proxy <A1> *>(gen->GetAddressOfArg(1))->value));
}
};
template <typename R, typename A0, typename A1>
struct Wrapper<R (*)(A0, A1)> {
template <R (*fp)(A0, A1)>
static void f(AS_NAMESPACE_QUALIFIER asIScriptGeneric * gen) {
new (gen->GetAddressOfReturnLocation()) Proxy<R>((fp)(
static_cast<Proxy <A0> *>(gen->GetAddressOfArg(0))->value,
static_cast<Proxy <A1> *>(gen->GetAddressOfArg(1))->value));
}
};
template <typename T, typename A0, typename A1>
struct Wrapper<void (T::*)(A0, A1)> {
template <void (T::*fp)(A0, A1)>
static void f(AS_NAMESPACE_QUALIFIER asIScriptGeneric * gen) {
((static_cast<T *>(gen->GetObject())->*fp)(
static_cast<Proxy <A0> *>(gen->GetAddressOfArg(0))->value,
static_cast<Proxy <A1> *>(gen->GetAddressOfArg(1))->value));
}
};
template <typename T, typename R, typename A0, typename A1>
struct Wrapper<R (T::*)(A0, A1)> {
template <R (T::*fp)(A0, A1)>
static void f(AS_NAMESPACE_QUALIFIER asIScriptGeneric * gen) {
new (gen->GetAddressOfReturnLocation()) Proxy<R>((static_cast<T *>(gen->GetObject())->*fp)(
static_cast<Proxy <A0> *>(gen->GetAddressOfArg(0))->value,
static_cast<Proxy <A1> *>(gen->GetAddressOfArg(1))->value));
}
};
template <typename T, typename A0, typename A1>
struct Wrapper<void (T::*)(A0, A1) const> {
template <void (T::*fp)(A0, A1) const>
static void f(AS_NAMESPACE_QUALIFIER asIScriptGeneric * gen) {
((static_cast<T *>(gen->GetObject())->*fp)(
static_cast<Proxy <A0> *>(gen->GetAddressOfArg(0))->value,
static_cast<Proxy <A1> *>(gen->GetAddressOfArg(1))->value));
}
};
template <typename T, typename R, typename A0, typename A1>
struct Wrapper<R (T::*)(A0, A1) const> {
template <R (T::*fp)(A0, A1) const>
static void f(AS_NAMESPACE_QUALIFIER asIScriptGeneric * gen) {
new (gen->GetAddressOfReturnLocation()) Proxy<R>((static_cast<T *>(gen->GetObject())->*fp)(
static_cast<Proxy <A0> *>(gen->GetAddressOfArg(0))->value,
static_cast<Proxy <A1> *>(gen->GetAddressOfArg(1))->value));
}
};
template <typename T, typename A0, typename A1>
struct ObjFirst<void (*)(T, A0, A1)> {
template <void (*fp)(T, A0, A1)>
static void f(AS_NAMESPACE_QUALIFIER asIScriptGeneric * gen) {
((fp)(
Proxy<T>::cast(gen->GetObject()),
static_cast<Proxy <A0> *>(gen->GetAddressOfArg(0))->value,
static_cast<Proxy <A1> *>(gen->GetAddressOfArg(1))->value));
}
};
template <typename T, typename R, typename A0, typename A1>
struct ObjFirst<R (*)(T, A0, A1)> {
template <R (*fp)(T, A0, A1)>
static void f(AS_NAMESPACE_QUALIFIER asIScriptGeneric * gen) {
new (gen->GetAddressOfReturnLocation()) Proxy<R>((fp)(
Proxy<T>::cast(gen->GetObject()),
static_cast<Proxy <A0> *>(gen->GetAddressOfArg(0))->value,
static_cast<Proxy <A1> *>(gen->GetAddressOfArg(1))->value));
}
};
template <typename T, typename A0, typename A1>
struct ObjLast<void (*)(A0, A1, T)> {
template <void (*fp)(A0, A1, T)>
static void f(AS_NAMESPACE_QUALIFIER asIScriptGeneric * gen) {
((fp)(
static_cast<Proxy <A0> *>(gen->GetAddressOfArg(0))->value,
static_cast<Proxy <A1> *>(gen->GetAddressOfArg(1))->value,
Proxy<T>::cast(gen->GetObject())));
}
};
template <typename T, typename R, typename A0, typename A1>
struct ObjLast<R (*)(A0, A1, T)> {
template <R (*fp)(A0, A1, T)>
static void f(AS_NAMESPACE_QUALIFIER asIScriptGeneric * gen) {
new (gen->GetAddressOfReturnLocation()) Proxy<R>((fp)(
static_cast<Proxy <A0> *>(gen->GetAddressOfArg(0))->value,
static_cast<Proxy <A1> *>(gen->GetAddressOfArg(1))->value,
Proxy<T>::cast(gen->GetObject())));
}
};
template <typename T, typename A0, typename A1>
struct Constructor <T (A0, A1)> {
static void f(AS_NAMESPACE_QUALIFIER asIScriptGeneric * gen) {
new (gen->GetObject()) T(
static_cast<Proxy <A0> *>(gen->GetAddressOfArg(0))->value,
static_cast<Proxy <A1> *>(gen->GetAddressOfArg(1))->value);
}
};
template <typename A0, typename A1, typename A2>
struct Wrapper<void (*)(A0, A1, A2)> {
template <void (*fp)(A0, A1, A2)>
static void f(AS_NAMESPACE_QUALIFIER asIScriptGeneric * gen) {
((fp)(
static_cast<Proxy <A0> *>(gen->GetAddressOfArg(0))->value,
static_cast<Proxy <A1> *>(gen->GetAddressOfArg(1))->value,
static_cast<Proxy <A2> *>(gen->GetAddressOfArg(2))->value));
}
};
template <typename R, typename A0, typename A1, typename A2>
struct Wrapper<R (*)(A0, A1, A2)> {
template <R (*fp)(A0, A1, A2)>
static void f(AS_NAMESPACE_QUALIFIER asIScriptGeneric * gen) {
new (gen->GetAddressOfReturnLocation()) Proxy<R>((fp)(
static_cast<Proxy <A0> *>(gen->GetAddressOfArg(0))->value,
static_cast<Proxy <A1> *>(gen->GetAddressOfArg(1))->value,
static_cast<Proxy <A2> *>(gen->GetAddressOfArg(2))->value));
}
};
template <typename T, typename A0, typename A1, typename A2>
struct Wrapper<void (T::*)(A0, A1, A2)> {
template <void (T::*fp)(A0, A1, A2)>
static void f(AS_NAMESPACE_QUALIFIER asIScriptGeneric * gen) {
((static_cast<T *>(gen->GetObject())->*fp)(
static_cast<Proxy <A0> *>(gen->GetAddressOfArg(0))->value,
static_cast<Proxy <A1> *>(gen->GetAddressOfArg(1))->value,
static_cast<Proxy <A2> *>(gen->GetAddressOfArg(2))->value));
}
};
template <typename T, typename R, typename A0, typename A1, typename A2>
struct Wrapper<R (T::*)(A0, A1, A2)> {
template <R (T::*fp)(A0, A1, A2)>
static void f(AS_NAMESPACE_QUALIFIER asIScriptGeneric * gen) {
new (gen->GetAddressOfReturnLocation()) Proxy<R>((static_cast<T *>(gen->GetObject())->*fp)(
static_cast<Proxy <A0> *>(gen->GetAddressOfArg(0))->value,
static_cast<Proxy <A1> *>(gen->GetAddressOfArg(1))->value,
static_cast<Proxy <A2> *>(gen->GetAddressOfArg(2))->value));
}
};
template <typename T, typename A0, typename A1, typename A2>
struct Wrapper<void (T::*)(A0, A1, A2) const> {
template <void (T::*fp)(A0, A1, A2) const>
static void f(AS_NAMESPACE_QUALIFIER asIScriptGeneric * gen) {
((static_cast<T *>(gen->GetObject())->*fp)(
static_cast<Proxy <A0> *>(gen->GetAddressOfArg(0))->value,
static_cast<Proxy <A1> *>(gen->GetAddressOfArg(1))->value,
static_cast<Proxy <A2> *>(gen->GetAddressOfArg(2))->value));
}
};
template <typename T, typename R, typename A0, typename A1, typename A2>
struct Wrapper<R (T::*)(A0, A1, A2) const> {
template <R (T::*fp)(A0, A1, A2) const>
static void f(AS_NAMESPACE_QUALIFIER asIScriptGeneric * gen) {
new (gen->GetAddressOfReturnLocation()) Proxy<R>((static_cast<T *>(gen->GetObject())->*fp)(
static_cast<Proxy <A0> *>(gen->GetAddressOfArg(0))->value,
static_cast<Proxy <A1> *>(gen->GetAddressOfArg(1))->value,
static_cast<Proxy <A2> *>(gen->GetAddressOfArg(2))->value));
}
};
template <typename T, typename A0, typename A1, typename A2>
struct ObjFirst<void (*)(T, A0, A1, A2)> {
template <void (*fp)(T, A0, A1, A2)>
static void f(AS_NAMESPACE_QUALIFIER asIScriptGeneric * gen) {
((fp)(
Proxy<T>::cast(gen->GetObject()),
static_cast<Proxy <A0> *>(gen->GetAddressOfArg(0))->value,
static_cast<Proxy <A1> *>(gen->GetAddressOfArg(1))->value,
static_cast<Proxy <A2> *>(gen->GetAddressOfArg(2))->value));
}
};
template <typename T, typename R, typename A0, typename A1, typename A2>
struct ObjFirst<R (*)(T, A0, A1, A2)> {
template <R (*fp)(T, A0, A1, A2)>
static void f(AS_NAMESPACE_QUALIFIER asIScriptGeneric * gen) {
new (gen->GetAddressOfReturnLocation()) Proxy<R>((fp)(
Proxy<T>::cast(gen->GetObject()),
static_cast<Proxy <A0> *>(gen->GetAddressOfArg(0))->value,
static_cast<Proxy <A1> *>(gen->GetAddressOfArg(1))->value,
static_cast<Proxy <A2> *>(gen->GetAddressOfArg(2))->value));
}
};
template <typename T, typename A0, typename A1, typename A2>
struct ObjLast<void (*)(A0, A1, A2, T)> {
template <void (*fp)(A0, A1, A2, T)>
static void f(AS_NAMESPACE_QUALIFIER asIScriptGeneric * gen) {
((fp)(
static_cast<Proxy <A0> *>(gen->GetAddressOfArg(0))->value,
static_cast<Proxy <A1> *>(gen->GetAddressOfArg(1))->value,
static_cast<Proxy <A2> *>(gen->GetAddressOfArg(2))->value,
Proxy<T>::cast(gen->GetObject())));
}
};
template <typename T, typename R, typename A0, typename A1, typename A2>
struct ObjLast<R (*)(A0, A1, A2, T)> {
template <R (*fp)(A0, A1, A2, T)>
static void f(AS_NAMESPACE_QUALIFIER asIScriptGeneric * gen) {
new (gen->GetAddressOfReturnLocation()) Proxy<R>((fp)(
static_cast<Proxy <A0> *>(gen->GetAddressOfArg(0))->value,
static_cast<Proxy <A1> *>(gen->GetAddressOfArg(1))->value,
static_cast<Proxy <A2> *>(gen->GetAddressOfArg(2))->value,
Proxy<T>::cast(gen->GetObject())));
}
};
template <typename T, typename A0, typename A1, typename A2>
struct Constructor <T (A0, A1, A2)> {
static void f(AS_NAMESPACE_QUALIFIER asIScriptGeneric * gen) {
new (gen->GetObject()) T(
static_cast<Proxy <A0> *>(gen->GetAddressOfArg(0))->value,
static_cast<Proxy <A1> *>(gen->GetAddressOfArg(1))->value,
static_cast<Proxy <A2> *>(gen->GetAddressOfArg(2))->value);
}
};
template <typename A0, typename A1, typename A2, typename A3>
struct Wrapper<void (*)(A0, A1, A2, A3)> {
template <void (*fp)(A0, A1, A2, A3)>
static void f(AS_NAMESPACE_QUALIFIER asIScriptGeneric * gen) {
((fp)(
static_cast<Proxy <A0> *>(gen->GetAddressOfArg(0))->value,
static_cast<Proxy <A1> *>(gen->GetAddressOfArg(1))->value,
static_cast<Proxy <A2> *>(gen->GetAddressOfArg(2))->value,
static_cast<Proxy <A3> *>(gen->GetAddressOfArg(3))->value));
}
};
template <typename R, typename A0, typename A1, typename A2, typename A3>
struct Wrapper<R (*)(A0, A1, A2, A3)> {
template <R (*fp)(A0, A1, A2, A3)>
static void f(AS_NAMESPACE_QUALIFIER asIScriptGeneric * gen) {
new (gen->GetAddressOfReturnLocation()) Proxy<R>((fp)(
static_cast<Proxy <A0> *>(gen->GetAddressOfArg(0))->value,
static_cast<Proxy <A1> *>(gen->GetAddressOfArg(1))->value,
static_cast<Proxy <A2> *>(gen->GetAddressOfArg(2))->value,
static_cast<Proxy <A3> *>(gen->GetAddressOfArg(3))->value));
}
};
template <typename T, typename A0, typename A1, typename A2, typename A3>
struct Wrapper<void (T::*)(A0, A1, A2, A3)> {
template <void (T::*fp)(A0, A1, A2, A3)>
static void f(AS_NAMESPACE_QUALIFIER asIScriptGeneric * gen) {
((static_cast<T *>(gen->GetObject())->*fp)(
static_cast<Proxy <A0> *>(gen->GetAddressOfArg(0))->value,
static_cast<Proxy <A1> *>(gen->GetAddressOfArg(1))->value,
static_cast<Proxy <A2> *>(gen->GetAddressOfArg(2))->value,
static_cast<Proxy <A3> *>(gen->GetAddressOfArg(3))->value));
}
};
template <typename T, typename R, typename A0, typename A1, typename A2, typename A3>
struct Wrapper<R (T::*)(A0, A1, A2, A3)> {
template <R (T::*fp)(A0, A1, A2, A3)>
static void f(AS_NAMESPACE_QUALIFIER asIScriptGeneric * gen) {
new (gen->GetAddressOfReturnLocation()) Proxy<R>((static_cast<T *>(gen->GetObject())->*fp)(
static_cast<Proxy <A0> *>(gen->GetAddressOfArg(0))->value,
static_cast<Proxy <A1> *>(gen->GetAddressOfArg(1))->value,
static_cast<Proxy <A2> *>(gen->GetAddressOfArg(2))->value,
static_cast<Proxy <A3> *>(gen->GetAddressOfArg(3))->value));
}
};
template <typename T, typename A0, typename A1, typename A2, typename A3>
struct Wrapper<void (T::*)(A0, A1, A2, A3) const> {
template <void (T::*fp)(A0, A1, A2, A3) const>
static void f(AS_NAMESPACE_QUALIFIER asIScriptGeneric * gen) {
((static_cast<T *>(gen->GetObject())->*fp)(
static_cast<Proxy <A0> *>(gen->GetAddressOfArg(0))->value,
static_cast<Proxy <A1> *>(gen->GetAddressOfArg(1))->value,
static_cast<Proxy <A2> *>(gen->GetAddressOfArg(2))->value,
static_cast<Proxy <A3> *>(gen->GetAddressOfArg(3))->value));
}
};
template <typename T, typename R, typename A0, typename A1, typename A2, typename A3>
struct Wrapper<R (T::*)(A0, A1, A2, A3) const> {
template <R (T::*fp)(A0, A1, A2, A3) const>
static void f(AS_NAMESPACE_QUALIFIER asIScriptGeneric * gen) {
new (gen->GetAddressOfReturnLocation()) Proxy<R>((static_cast<T *>(gen->GetObject())->*fp)(
static_cast<Proxy <A0> *>(gen->GetAddressOfArg(0))->value,
static_cast<Proxy <A1> *>(gen->GetAddressOfArg(1))->value,
static_cast<Proxy <A2> *>(gen->GetAddressOfArg(2))->value,
static_cast<Proxy <A3> *>(gen->GetAddressOfArg(3))->value));
}
};
template <typename T, typename A0, typename A1, typename A2, typename A3>
struct ObjFirst<void (*)(T, A0, A1, A2, A3)> {
template <void (*fp)(T, A0, A1, A2, A3)>
static void f(AS_NAMESPACE_QUALIFIER asIScriptGeneric * gen) {
((fp)(
Proxy<T>::cast(gen->GetObject()),
static_cast<Proxy <A0> *>(gen->GetAddressOfArg(0))->value,
static_cast<Proxy <A1> *>(gen->GetAddressOfArg(1))->value,
static_cast<Proxy <A2> *>(gen->GetAddressOfArg(2))->value,
static_cast<Proxy <A3> *>(gen->GetAddressOfArg(3))->value));
}
};
template <typename T, typename R, typename A0, typename A1, typename A2, typename A3>
struct ObjFirst<R (*)(T, A0, A1, A2, A3)> {
template <R (*fp)(T, A0, A1, A2, A3)>
static void f(AS_NAMESPACE_QUALIFIER asIScriptGeneric * gen) {
new (gen->GetAddressOfReturnLocation()) Proxy<R>((fp)(
Proxy<T>::cast(gen->GetObject()),
static_cast<Proxy <A0> *>(gen->GetAddressOfArg(0))->value,
static_cast<Proxy <A1> *>(gen->GetAddressOfArg(1))->value,
static_cast<Proxy <A2> *>(gen->GetAddressOfArg(2))->value,
static_cast<Proxy <A3> *>(gen->GetAddressOfArg(3))->value));
}
};
template <typename T, typename A0, typename A1, typename A2, typename A3>
struct ObjLast<void (*)(A0, A1, A2, A3, T)> {
template <void (*fp)(A0, A1, A2, A3, T)>
static void f(AS_NAMESPACE_QUALIFIER asIScriptGeneric * gen) {
((fp)(
static_cast<Proxy <A0> *>(gen->GetAddressOfArg(0))->value,
static_cast<Proxy <A1> *>(gen->GetAddressOfArg(1))->value,
static_cast<Proxy <A2> *>(gen->GetAddressOfArg(2))->value,
static_cast<Proxy <A3> *>(gen->GetAddressOfArg(3))->value,
Proxy<T>::cast(gen->GetObject())));
}
};
template <typename T, typename R, typename A0, typename A1, typename A2, typename A3>
struct ObjLast<R (*)(A0, A1, A2, A3, T)> {
template <R (*fp)(A0, A1, A2, A3, T)>
static void f(AS_NAMESPACE_QUALIFIER asIScriptGeneric * gen) {
new (gen->GetAddressOfReturnLocation()) Proxy<R>((fp)(
static_cast<Proxy <A0> *>(gen->GetAddressOfArg(0))->value,
static_cast<Proxy <A1> *>(gen->GetAddressOfArg(1))->value,
static_cast<Proxy <A2> *>(gen->GetAddressOfArg(2))->value,
static_cast<Proxy <A3> *>(gen->GetAddressOfArg(3))->value,
Proxy<T>::cast(gen->GetObject())));
}
};
template <typename T, typename A0, typename A1, typename A2, typename A3>
struct Constructor <T (A0, A1, A2, A3)> {
static void f(AS_NAMESPACE_QUALIFIER asIScriptGeneric * gen) {
new (gen->GetObject()) T(
static_cast<Proxy <A0> *>(gen->GetAddressOfArg(0))->value,
static_cast<Proxy <A1> *>(gen->GetAddressOfArg(1))->value,
static_cast<Proxy <A2> *>(gen->GetAddressOfArg(2))->value,
static_cast<Proxy <A3> *>(gen->GetAddressOfArg(3))->value);
}
};
template <typename T>
struct Id {
template <T fn_ptr> AS_NAMESPACE_QUALIFIER asSFuncPtr f(void) { return asFUNCTION(&Wrapper<T>::template f<fn_ptr>); }
template <T fn_ptr> AS_NAMESPACE_QUALIFIER asSFuncPtr of(void) { return asFUNCTION(&ObjFirst<T>::template f<fn_ptr>); }
template <T fn_ptr> AS_NAMESPACE_QUALIFIER asSFuncPtr ol(void) { return asFUNCTION(&ObjLast<T>::template f<fn_ptr>); }
};
template <typename T>
Id<T> id(T /*fn_ptr*/) { return Id<T>(); }
// On some versions of GNUC it is necessary to use the template keyword as disambiguator,
// on others the template keyword gives an error, hence the need for the following define.
// MSVC on the other hand seems to accept both with or without the template keyword.
#if defined(__GNUC__) && (__GNUC__ < 4 || (__GNUC__ == 4 && __GNUC_MINOR__ < 4))
// GNUC 4.4.3 doesn't need the template keyword, and
// hopefully upcoming versions won't need it either
#define TMPL template
#else
#define TMPL
#endif
#define WRAP_FN(name) (::gw::id(name).TMPL f< name >())
#define WRAP_MFN(ClassType, name) (::gw::id(&ClassType::name).TMPL f< &ClassType::name >())
#define WRAP_OBJ_FIRST(name) (::gw::id(name).TMPL of< name >())
#define WRAP_OBJ_LAST(name) (::gw::id(name).TMPL ol< name >())
#define WRAP_FN_PR(name, Parameters, ReturnType) asFUNCTION((::gw::Wrapper<ReturnType (*)Parameters>::TMPL f< name >))
#define WRAP_MFN_PR(ClassType, name, Parameters, ReturnType) asFUNCTION((::gw::Wrapper<ReturnType (ClassType::*)Parameters>::TMPL f< &ClassType::name >))
#define WRAP_OBJ_FIRST_PR(name, Parameters, ReturnType) asFUNCTION((::gw::ObjFirst<ReturnType (*)Parameters>::TMPL f< name >))
#define WRAP_OBJ_LAST_PR(name, Parameters, ReturnType) asFUNCTION((::gw::ObjLast<ReturnType (*)Parameters>::TMPL f< name >))
#define WRAP_CON(ClassType, Parameters) asFUNCTION((::gw::Constructor<ClassType Parameters>::f))
#define WRAP_DES(ClassType) asFUNCTION((::gw::destroy<ClassType>))
} // end namespace gw
#endif

View File

@@ -0,0 +1,166 @@
//
// This generator creates a header file that implements automatic
// wrapper functions for the generic calling convention.
//
// Originally implemented by George Yohng from 4Front Technologies in 2009-03-11
// Modifications by Pierre Fortin in order to add constructor wrapper generation
//
// A completely new implementation of automatic wrapper functions was
// implemented by SiCrane at GameDev.net in 2011-12-18. The generator was
// adapted from Python to C++ by Andreas.
//
// ref: http://www.gamedev.net/topic/617111-more-angelscript-binding-wrappers/
//
#include <stdio.h>
#include <string>
// Generate templates for up to this number of function parameters
const int max_args = 4;
using namespace std;
void PrintTemplate(const char *base, const char *typeNameList, const char *retType, const char *objType, const char *isConst, const char *newExpr, const char *objExpr, const char *argList1, const char *argList2, const char *wrapName);
void PrintConstructor(const char *comma, const char *typeNameList, const char *typeList, const char *argList);
int main()
{
printf("#ifndef AS_GEN_WRAPPER_H\n"
"#define AS_GEN_WRAPPER_H\n"
"\n"
"#ifndef ANGELSCRIPT_H\n"
"// Avoid having to inform include path if header is already include before\n"
"#include <angelscript.h>\n"
"#endif\n"
"#include <new>\n"
"\n"
"namespace gw {\n"
"\n"
"template <typename T> class Proxy {\n"
" public:\n"
" T value;\n"
" Proxy(T value) : value(value) {}\n"
" static T cast(void * ptr) {\n"
" return reinterpret_cast<Proxy<T> *>(&ptr)->value;\n"
" }\n"
" private:\n"
" Proxy(const Proxy &);\n"
" Proxy & operator=(const Proxy &);\n"
"};\n"
"\n"
"template <typename T> struct Wrapper {};\n"
"template <typename T> struct ObjFirst {};\n"
"template <typename T> struct ObjLast {};\n"
"template <typename T> struct Constructor {};\n"
"\n"
"template <typename T>\n"
"void destroy(AS_NAMESPACE_QUALIFIER asIScriptGeneric * gen) {\n"
" static_cast<T *>(gen->GetObject())->~T();\n"
"}\n");
string typename_list = "typename A0";
string type_list = "A0";
string arg_list = "\n static_cast<Proxy <A0> *>(gen->GetAddressOfArg(0))->value";
string new_exp = "new (gen->GetAddressOfReturnLocation()) Proxy<R>";
string obj_exp = "static_cast<T *>(gen->GetObject())->*";
string obj_arg_exp = "\n Proxy<T>::cast(gen->GetObject())";
PrintTemplate("", "", "void", "", "", "", "", "void", "", "Wrapper");
PrintTemplate("typename R", "", "R", "", "", new_exp.c_str(), "", "void", "", "Wrapper");
PrintTemplate("typename T", "", "void", "T::", "", "", obj_exp.c_str(), "void", "", "Wrapper");
PrintTemplate("typename T, typename R", "", "R", "T::", "", new_exp.c_str(), obj_exp.c_str(), "void", "", "Wrapper");
PrintTemplate("typename T", "", "void", "T::", " const", "", obj_exp.c_str(), "void", "", "Wrapper");
PrintTemplate("typename T, typename R", "", "R", "T::", " const", new_exp.c_str(), obj_exp.c_str(), "void", "", "Wrapper");
PrintTemplate("typename T", "", "void", "", "", "", "", "T", obj_arg_exp.c_str(), "ObjFirst");
PrintTemplate("typename T, typename R", "", "R", "", "", new_exp.c_str(), "", "T", obj_arg_exp.c_str(), "ObjFirst");
PrintTemplate("typename T", "", "void", "", "", "", "", "T", obj_arg_exp.c_str(), "ObjLast");
PrintTemplate("typename T, typename R", "", "R", "", "", new_exp.c_str(), "", "T", obj_arg_exp.c_str(), "ObjLast");
PrintConstructor("", "", "", "");
for( int i = 0; i < max_args; i++ )
{
PrintTemplate("", typename_list.c_str(), "void", "", "", "", "", type_list.c_str(), arg_list.c_str(), "Wrapper");
PrintTemplate("typename R, ", typename_list.c_str(), "R", "", "", new_exp.c_str(), "", type_list.c_str(), arg_list.c_str(), "Wrapper");
PrintTemplate("typename T, ", typename_list.c_str(), "void", "T::", "", "", obj_exp.c_str(), type_list.c_str(), arg_list.c_str(), "Wrapper");
PrintTemplate("typename T, typename R, ", typename_list.c_str(), "R", "T::", "", new_exp.c_str(), obj_exp.c_str(), type_list.c_str(), arg_list.c_str(), "Wrapper");
PrintTemplate("typename T, ", typename_list.c_str(), "void", "T::", " const", "", obj_exp.c_str(), type_list.c_str(), arg_list.c_str(), "Wrapper");
PrintTemplate("typename T, typename R, ", typename_list.c_str(), "R", "T::", " const", new_exp.c_str(), obj_exp.c_str(), type_list.c_str(), arg_list.c_str(), "Wrapper");
PrintTemplate("typename T, ", typename_list.c_str(), "void", "", "", "", "", ("T, " + type_list).c_str(), (obj_arg_exp + "," + arg_list).c_str(), "ObjFirst");
PrintTemplate("typename T, typename R, ", typename_list.c_str(), "R", "", "", new_exp.c_str(), "", ("T, " + type_list).c_str(), (obj_arg_exp + "," + arg_list).c_str(), "ObjFirst");
PrintTemplate("typename T, ", typename_list.c_str(), "void", "", "", "", "", (type_list + ", T").c_str(), (arg_list + "," + obj_arg_exp).c_str(), "ObjLast");
PrintTemplate("typename T, typename R, ", typename_list.c_str(), "R", "", "", new_exp.c_str(), "", (type_list + ", T").c_str(), (arg_list + "," + obj_arg_exp).c_str(), "ObjLast");
PrintConstructor(", ", typename_list.c_str(), type_list.c_str(), arg_list.c_str());
char buf[5];
sprintf(buf, "%d", i + 1);
typename_list += ", typename A" + string(buf);
type_list += ", A" + string(buf);
arg_list += ",\n static_cast<Proxy <A" + string(buf) + "> *>(gen->GetAddressOfArg(" + string(buf) + "))->value";
}
printf("template <typename T>\n"
"struct Id {\n"
" template <T fn_ptr> AS_NAMESPACE_QUALIFIER asSFuncPtr f(void) { return asFUNCTION(&Wrapper<T>::template f<fn_ptr>); }\n"
" template <T fn_ptr> AS_NAMESPACE_QUALIFIER asSFuncPtr of(void) { return asFUNCTION(&ObjFirst<T>::template f<fn_ptr>); }\n"
" template <T fn_ptr> AS_NAMESPACE_QUALIFIER asSFuncPtr ol(void) { return asFUNCTION(&ObjLast<T>::template f<fn_ptr>); }\n"
"};\n"
"\n"
"template <typename T>\n"
"Id<T> id(T fn_ptr) { return Id<T>(); }\n"
"\n"
"// On some versions of GNUC it is necessary to use the template keyword as disambiguator,\n"
"// on others the template keyword gives an error, hence the need for the following define.\n"
"// MSVC on the other hand seems to accept both with or without the template keyword.\n"
"#if defined(__GNUC__) && (__GNUC__ < 4 || (__GNUC__ == 4 && __GNUC_MINOR__ < 4))\n"
" // GNUC 4.4.3 doesn't need the template keyword, and\n"
" // hopefully upcoming versions won't need it either\n"
" #define TMPL template\n"
"#else\n"
" #define TMPL\n"
"#endif\n"
"\n"
"#define WRAP_FN(name) (::gw::id(name).TMPL f< name >())\n"
"#define WRAP_MFN(ClassType, name) (::gw::id(&ClassType::name).TMPL f< &ClassType::name >())\n"
"#define WRAP_OBJ_FIRST(name) (::gw::id(name).TMPL of< name >())\n"
"#define WRAP_OBJ_LAST(name) (::gw::id(name).TMPL ol< name >())\n"
"\n"
"#define WRAP_FN_PR(name, Parameters, ReturnType) asFUNCTION((::gw::Wrapper<ReturnType (*)Parameters>::TMPL f< name >))\n"
"#define WRAP_MFN_PR(ClassType, name, Parameters, ReturnType) asFUNCTION((::gw::Wrapper<ReturnType (ClassType::*)Parameters>::TMPL f< &ClassType::name >))\n"
"#define WRAP_OBJ_FIRST_PR(name, Parameters, ReturnType) asFUNCTION((::gw::ObjFirst<ReturnType (*)Parameters>::TMPL f< name >))\n"
"#define WRAP_OBJ_LAST_PR(name, Parameters, ReturnType) asFUNCTION((::gw::ObjLast<ReturnType (*)Parameters>::TMPL f< name >))\n"
"\n"
"#define WRAP_CON(ClassType, Parameters) asFUNCTION((::gw::Constructor<ClassType Parameters>::f))\n"
"#define WRAP_DES(ClassType) asFUNCTION((::gw::destroy<ClassType>))\n"
"\n"
"} // end namespace gw\n"
"\n"
"#endif\n");
return 0;
}
void PrintTemplate(const char *base, const char *typeNameList, const char *retType, const char *objType, const char *isConst, const char *newExpr, const char *objExpr, const char *argList1, const char *argList2, const char *wrapName)
{
printf("template <%s%s>\n", base, typeNameList);
printf("struct %s<%s (%s*)(%s)%s> {\n", wrapName, retType, objType, argList1, isConst);
printf(" template <%s (%s*fp)(%s)%s>\n", retType, objType, argList1, isConst);
printf(" static void f(AS_NAMESPACE_QUALIFIER asIScriptGeneric * gen) {\n");
printf(" %s((%sfp)(%s));\n", newExpr, objExpr, argList2);
printf(" }\n");
printf("};\n");
}
void PrintConstructor(const char *comma, const char *typeNameList, const char *typeList, const char *argList)
{
printf("template <typename T%s%s>\n", comma, typeNameList);
printf("struct Constructor <T (%s)> {\n", typeList);
printf(" static void f(AS_NAMESPACE_QUALIFIER asIScriptGeneric * gen) {\n");
printf(" new (gen->GetObject()) T(%s);\n", argList);
printf(" }\n");
printf("};\n");
}

View File

@@ -0,0 +1,20 @@

Microsoft Visual Studio Solution File, Format Version 10.00
# Visual C++ Express 2008
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "generator", "generator.vcproj", "{086A2F1A-01B1-4EB3-A8FA-0926FF10E953}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Win32 = Debug|Win32
Release|Win32 = Release|Win32
EndGlobalSection
GlobalSection(ProjectConfigurationPlatforms) = postSolution
{086A2F1A-01B1-4EB3-A8FA-0926FF10E953}.Debug|Win32.ActiveCfg = Debug|Win32
{086A2F1A-01B1-4EB3-A8FA-0926FF10E953}.Debug|Win32.Build.0 = Debug|Win32
{086A2F1A-01B1-4EB3-A8FA-0926FF10E953}.Release|Win32.ActiveCfg = Release|Win32
{086A2F1A-01B1-4EB3-A8FA-0926FF10E953}.Release|Win32.Build.0 = Release|Win32
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
EndGlobalSection
EndGlobal

View File

@@ -0,0 +1,236 @@
<?xml version="1.0" encoding="Windows-1252"?>
<VisualStudioProject
ProjectType="Visual C++"
Version="9,00"
Name="generator"
ProjectGUID="{086A2F1A-01B1-4EB3-A8FA-0926FF10E953}"
TargetFrameworkVersion="0"
>
<Platforms>
<Platform
Name="Win32"
/>
</Platforms>
<ToolFiles>
</ToolFiles>
<Configurations>
<Configuration
Name="Debug|Win32"
OutputDirectory=".\Debug"
IntermediateDirectory=".\Debug"
ConfigurationType="1"
InheritedPropertySheets="$(VCInstallDir)VCProjectDefaults\UpgradeFromVC60.vsprops"
UseOfMFC="0"
ATLMinimizesCRunTimeLibraryUsage="false"
CharacterSet="2"
>
<Tool
Name="VCPreBuildEventTool"
/>
<Tool
Name="VCCustomBuildTool"
/>
<Tool
Name="VCXMLDataGeneratorTool"
/>
<Tool
Name="VCWebServiceProxyGeneratorTool"
/>
<Tool
Name="VCMIDLTool"
TypeLibraryName=".\Debug/generator.tlb"
HeaderFileName=""
/>
<Tool
Name="VCCLCompilerTool"
Optimization="0"
PreprocessorDefinitions="WIN32;_DEBUG;_CONSOLE"
MinimalRebuild="true"
BasicRuntimeChecks="3"
RuntimeLibrary="1"
PrecompiledHeaderFile=".\Debug/generator.pch"
AssemblerListingLocation=".\Debug/"
ObjectFile=".\Debug/"
ProgramDataBaseFileName=".\Debug/"
WarningLevel="3"
SuppressStartupBanner="true"
DebugInformationFormat="4"
/>
<Tool
Name="VCManagedResourceCompilerTool"
/>
<Tool
Name="VCResourceCompilerTool"
PreprocessorDefinitions="_DEBUG"
Culture="1033"
/>
<Tool
Name="VCPreLinkEventTool"
/>
<Tool
Name="VCLinkerTool"
OutputFile=".\Debug/generator.exe"
LinkIncremental="2"
SuppressStartupBanner="true"
GenerateDebugInformation="true"
ProgramDatabaseFile=".\Debug/generator.pdb"
SubSystem="1"
RandomizedBaseAddress="1"
DataExecutionPrevention="0"
TargetMachine="1"
/>
<Tool
Name="VCALinkTool"
/>
<Tool
Name="VCManifestTool"
/>
<Tool
Name="VCXDCMakeTool"
/>
<Tool
Name="VCBscMakeTool"
SuppressStartupBanner="true"
OutputFile=".\Debug/generator.bsc"
/>
<Tool
Name="VCFxCopTool"
/>
<Tool
Name="VCAppVerifierTool"
/>
<Tool
Name="VCPostBuildEventTool"
/>
</Configuration>
<Configuration
Name="Release|Win32"
OutputDirectory=".\Release"
IntermediateDirectory=".\Release"
ConfigurationType="1"
InheritedPropertySheets="$(VCInstallDir)VCProjectDefaults\UpgradeFromVC60.vsprops"
UseOfMFC="0"
ATLMinimizesCRunTimeLibraryUsage="false"
CharacterSet="2"
>
<Tool
Name="VCPreBuildEventTool"
/>
<Tool
Name="VCCustomBuildTool"
/>
<Tool
Name="VCXMLDataGeneratorTool"
/>
<Tool
Name="VCWebServiceProxyGeneratorTool"
/>
<Tool
Name="VCMIDLTool"
TypeLibraryName=".\Release/generator.tlb"
HeaderFileName=""
/>
<Tool
Name="VCCLCompilerTool"
Optimization="2"
InlineFunctionExpansion="1"
PreprocessorDefinitions="WIN32;NDEBUG;_CONSOLE"
StringPooling="true"
RuntimeLibrary="0"
EnableFunctionLevelLinking="true"
PrecompiledHeaderFile=".\Release/generator.pch"
AssemblerListingLocation=".\Release/"
ObjectFile=".\Release/"
ProgramDataBaseFileName=".\Release/"
WarningLevel="3"
SuppressStartupBanner="true"
/>
<Tool
Name="VCManagedResourceCompilerTool"
/>
<Tool
Name="VCResourceCompilerTool"
PreprocessorDefinitions="NDEBUG"
Culture="1033"
/>
<Tool
Name="VCPreLinkEventTool"
/>
<Tool
Name="VCLinkerTool"
OutputFile=".\Release/generator.exe"
LinkIncremental="1"
SuppressStartupBanner="true"
ProgramDatabaseFile=".\Release/generator.pdb"
SubSystem="1"
RandomizedBaseAddress="1"
DataExecutionPrevention="0"
TargetMachine="1"
/>
<Tool
Name="VCALinkTool"
/>
<Tool
Name="VCManifestTool"
/>
<Tool
Name="VCXDCMakeTool"
/>
<Tool
Name="VCBscMakeTool"
SuppressStartupBanner="true"
OutputFile=".\Release/generator.bsc"
/>
<Tool
Name="VCFxCopTool"
/>
<Tool
Name="VCAppVerifierTool"
/>
<Tool
Name="VCPostBuildEventTool"
/>
</Configuration>
</Configurations>
<References>
</References>
<Files>
<Filter
Name="Source Files"
Filter="cpp;c;cxx;rc;def;r;odl;idl;hpj;bat"
>
<File
RelativePath="generateheader.cpp"
>
<FileConfiguration
Name="Debug|Win32"
>
<Tool
Name="VCCLCompilerTool"
PreprocessorDefinitions=""
/>
</FileConfiguration>
<FileConfiguration
Name="Release|Win32"
>
<Tool
Name="VCCLCompilerTool"
PreprocessorDefinitions=""
/>
</FileConfiguration>
</File>
</Filter>
<Filter
Name="Header Files"
Filter="h;hpp;hxx;hm;inl"
>
</Filter>
<Filter
Name="Resource Files"
Filter="ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe"
>
</Filter>
</Files>
<Globals>
</Globals>
</VisualStudioProject>

View File

@@ -0,0 +1,401 @@
#include <assert.h>
#include <string>
#include "contextmgr.h"
using namespace std;
// TODO: Should have a pool of free asIScriptContext so that new contexts
// won't be allocated every time. The application must not keep
// its own references, instead it must tell the context manager
// that it is using the context. Otherwise the context manager may
// think it can reuse the context too early.
// TODO: Need to have a callback for when scripts finishes, so that the
// application can receive return values.
BEGIN_AS_NAMESPACE
// The id for the context manager user data.
// The add-ons have reserved the numbers 1000
// through 1999 for this purpose, so we should be fine.
const asPWORD CONTEXT_MGR = 1002;
struct SContextInfo
{
asUINT sleepUntil;
vector<asIScriptContext*> coRoutines;
asUINT currentCoRoutine;
asIScriptContext * keepCtxAfterExecution;
};
static void ScriptSleep(asUINT milliSeconds)
{
// Get a pointer to the context that is currently being executed
asIScriptContext *ctx = asGetActiveContext();
if( ctx )
{
// Get the context manager from the user data
CContextMgr *ctxMgr = reinterpret_cast<CContextMgr*>(ctx->GetUserData(CONTEXT_MGR));
if( ctxMgr )
{
// Suspend its execution. The VM will continue until the current
// statement is finished and then return from the Execute() method
ctx->Suspend();
// Tell the context manager when the context is to continue execution
ctxMgr->SetSleeping(ctx, milliSeconds);
}
}
}
static void ScriptYield()
{
// Get a pointer to the context that is currently being executed
asIScriptContext *ctx = asGetActiveContext();
if( ctx )
{
// Get the context manager from the user data
CContextMgr *ctxMgr = reinterpret_cast<CContextMgr*>(ctx->GetUserData(CONTEXT_MGR));
if( ctxMgr )
{
// Let the context manager know that it should run the next co-routine
ctxMgr->NextCoRoutine();
// The current context must be suspended so that VM will return from
// the Execute() method where the context manager will continue.
ctx->Suspend();
}
}
}
void ScriptCreateCoRoutine(asIScriptFunction *func, CScriptDictionary *arg)
{
if( func == 0 )
return;
asIScriptContext *ctx = asGetActiveContext();
if( ctx )
{
// Get the context manager from the user data
CContextMgr *ctxMgr = reinterpret_cast<CContextMgr*>(ctx->GetUserData(CONTEXT_MGR));
if( ctxMgr )
{
// Create a new context for the co-routine
asIScriptContext *coctx = ctxMgr->AddContextForCoRoutine(ctx, func);
// Pass the argument to the context
coctx->SetArgObject(0, arg);
// The context manager will call Execute() on the context when it is time
}
}
}
#ifdef AS_MAX_PORTABILITY
void ScriptYield_generic(asIScriptGeneric *)
{
ScriptYield();
}
void ScriptCreateCoRoutine_generic(asIScriptGeneric *gen)
{
asIScriptFunction *func = reinterpret_cast<asIScriptFunction*>(gen->GetArgAddress(0));
CScriptDictionary *dict = reinterpret_cast<CScriptDictionary*>(gen->GetArgAddress(1));
ScriptCreateCoRoutine(func, dict);
}
#endif
CContextMgr::CContextMgr()
{
m_getTimeFunc = 0;
m_currentThread = 0;
m_numExecutions = 0;
m_numGCObjectsCreated = 0;
m_numGCObjectsDestroyed = 0;
}
CContextMgr::~CContextMgr()
{
asUINT n;
// Free the memory
for( n = 0; n < m_threads.size(); n++ )
{
if( m_threads[n] )
{
for( asUINT c = 0; c < m_threads[n]->coRoutines.size(); c++ )
{
asIScriptContext *ctx = m_threads[n]->coRoutines[c];
if( ctx )
{
// Return the context to the engine (and possible context pool configured in it)
ctx->GetEngine()->ReturnContext(ctx);
}
}
delete m_threads[n];
}
}
for( n = 0; n < m_freeThreads.size(); n++ )
{
if( m_freeThreads[n] )
{
assert( m_freeThreads[n]->coRoutines.size() == 0 );
delete m_freeThreads[n];
}
}
}
int CContextMgr::ExecuteScripts()
{
// TODO: Should have an optional time out for this function. If not all scripts executed before the
// time out, the next time the function is called the loop should continue
// where it left off.
// TODO: There should be a time out per thread as well. If a thread executes for too
// long, it should be aborted. A group of co-routines count as a single thread.
// Check if the system time is higher than the time set for the contexts
asUINT time = m_getTimeFunc ? m_getTimeFunc() : asUINT(-1);
for( m_currentThread = 0; m_currentThread < m_threads.size(); m_currentThread++ )
{
SContextInfo *thread = m_threads[m_currentThread];
if( thread->sleepUntil < time )
{
int currentCoRoutine = thread->currentCoRoutine;
// Gather some statistics from the GC
asIScriptEngine *engine = thread->coRoutines[currentCoRoutine]->GetEngine();
asUINT gcSize1, gcSize2, gcSize3;
engine->GetGCStatistics(&gcSize1);
// Execute the script for this thread and co-routine
int r = thread->coRoutines[currentCoRoutine]->Execute();
// Determine how many new objects were created in the GC
engine->GetGCStatistics(&gcSize2);
m_numGCObjectsCreated += gcSize2 - gcSize1;
m_numExecutions++;
if( r != asEXECUTION_SUSPENDED )
{
// The context has terminated execution (for one reason or other)
// Unless the application has requested to keep the context we'll return it to the pool now
if( thread->keepCtxAfterExecution != thread->coRoutines[currentCoRoutine] )
engine->ReturnContext(thread->coRoutines[currentCoRoutine]);
thread->coRoutines[currentCoRoutine] = 0;
thread->coRoutines.erase(thread->coRoutines.begin() + thread->currentCoRoutine);
if( thread->currentCoRoutine >= thread->coRoutines.size() )
thread->currentCoRoutine = 0;
// If this was the last co-routine terminate the thread
if( thread->coRoutines.size() == 0 )
{
m_freeThreads.push_back(thread);
m_threads.erase(m_threads.begin() + m_currentThread);
m_currentThread--;
}
}
// Destroy all known garbage if any new objects were created
if( gcSize2 > gcSize1 )
{
engine->GarbageCollect(asGC_FULL_CYCLE | asGC_DESTROY_GARBAGE);
// Determine how many objects were destroyed
engine->GetGCStatistics(&gcSize3);
m_numGCObjectsDestroyed += gcSize3 - gcSize2;
}
// TODO: If more objects are created per execution than destroyed on average
// then it may be necessary to run more iterations of the detection of
// cyclic references. At the startup of an application there is usually
// a lot of objects created that will live on through out the application
// so the average number of objects created per execution will be higher
// than the number of destroyed objects in the beginning, but afterwards
// it usually levels out to be more or less equal.
// Just run an incremental step for detecting cyclic references
engine->GarbageCollect(asGC_ONE_STEP | asGC_DETECT_GARBAGE);
}
}
return int(m_threads.size());
}
void CContextMgr::DoneWithContext(asIScriptContext *ctx)
{
ctx->GetEngine()->ReturnContext(ctx);
}
void CContextMgr::NextCoRoutine()
{
m_threads[m_currentThread]->currentCoRoutine++;
if( m_threads[m_currentThread]->currentCoRoutine >= m_threads[m_currentThread]->coRoutines.size() )
m_threads[m_currentThread]->currentCoRoutine = 0;
}
void CContextMgr::AbortAll()
{
// Abort all contexts and release them. The script engine will make
// sure that all resources held by the scripts are properly released.
for( asUINT n = 0; n < m_threads.size(); n++ )
{
for( asUINT c = 0; c < m_threads[n]->coRoutines.size(); c++ )
{
asIScriptContext *ctx = m_threads[n]->coRoutines[c];
if( ctx )
{
ctx->Abort();
ctx->GetEngine()->ReturnContext(ctx);
ctx = 0;
}
}
m_threads[n]->coRoutines.resize(0);
m_freeThreads.push_back(m_threads[n]);
}
m_threads.resize(0);
m_currentThread = 0;
}
asIScriptContext *CContextMgr::AddContext(asIScriptEngine *engine, asIScriptFunction *func, bool keepCtxAfterExec)
{
// Use RequestContext instead of CreateContext so we can take
// advantage of possible context pooling configured with the engine
asIScriptContext *ctx = engine->RequestContext();
if( ctx == 0 )
return 0;
// Prepare it to execute the function
int r = ctx->Prepare(func);
if( r < 0 )
{
engine->ReturnContext(ctx);
return 0;
}
// Set the context manager as user data with the context so it
// can be retrieved by the functions registered with the engine
ctx->SetUserData(this, CONTEXT_MGR);
// Add the context to the list for execution
SContextInfo *info = 0;
if( m_freeThreads.size() > 0 )
{
info = *m_freeThreads.rbegin();
m_freeThreads.pop_back();
}
else
{
info = new SContextInfo;
}
info->coRoutines.push_back(ctx);
info->currentCoRoutine = 0;
info->sleepUntil = 0;
info->keepCtxAfterExecution = keepCtxAfterExec ? ctx : 0;
m_threads.push_back(info);
return ctx;
}
asIScriptContext *CContextMgr::AddContextForCoRoutine(asIScriptContext *currCtx, asIScriptFunction *func)
{
asIScriptEngine *engine = currCtx->GetEngine();
asIScriptContext *coctx = engine->RequestContext();
if( coctx == 0 )
{
return 0;
}
// Prepare the context
int r = coctx->Prepare(func);
if( r < 0 )
{
// Couldn't prepare the context
engine->ReturnContext(coctx);
return 0;
}
// Set the context manager as user data with the context so it
// can be retrieved by the functions registered with the engine
coctx->SetUserData(this, CONTEXT_MGR);
// Find the current context thread info
// TODO: Start with the current thread so that we can find the group faster
for( asUINT n = 0; n < m_threads.size(); n++ )
{
if( m_threads[n]->coRoutines[m_threads[n]->currentCoRoutine] == currCtx )
{
// Add the coRoutine to the list
m_threads[n]->coRoutines.push_back(coctx);
}
}
return coctx;
}
void CContextMgr::SetSleeping(asIScriptContext *ctx, asUINT milliSeconds)
{
assert( m_getTimeFunc != 0 );
// Find the context and update the timeStamp
// for when the context is to be continued
// TODO: Start with the current thread
for( asUINT n = 0; n < m_threads.size(); n++ )
{
if( m_threads[n]->coRoutines[m_threads[n]->currentCoRoutine] == ctx )
{
m_threads[n]->sleepUntil = (m_getTimeFunc ? m_getTimeFunc() : 0) + milliSeconds;
}
}
}
void CContextMgr::RegisterThreadSupport(asIScriptEngine *engine)
{
int r;
// Must set the get time callback function for this to work
assert( m_getTimeFunc != 0 );
// Register the sleep function
r = engine->RegisterGlobalFunction("void sleep(uint)", asFUNCTION(ScriptSleep), asCALL_CDECL); assert( r >= 0 );
// TODO: Add support for spawning new threads, waiting for signals, etc
}
void CContextMgr::RegisterCoRoutineSupport(asIScriptEngine *engine)
{
int r;
// The dictionary add-on must have been registered already
assert( engine->GetTypeInfoByDecl("dictionary") );
#ifndef AS_MAX_PORTABILITY
r = engine->RegisterGlobalFunction("void yield()", asFUNCTION(ScriptYield), asCALL_CDECL); assert( r >= 0 );
r = engine->RegisterFuncdef("void coroutine(dictionary@)");
r = engine->RegisterGlobalFunction("void createCoRoutine(coroutine @+, dictionary @+)", asFUNCTION(ScriptCreateCoRoutine), asCALL_CDECL); assert( r >= 0 );
#else
r = engine->RegisterGlobalFunction("void yield()", asFUNCTION(ScriptYield_generic), asCALL_GENERIC); assert( r >= 0 );
r = engine->RegisterFuncdef("void coroutine(dictionary@)");
r = engine->RegisterGlobalFunction("void createCoRoutine(coroutine @+, dictionary @+)", asFUNCTION(ScriptCreateCoRoutine_generic), asCALL_GENERIC); assert( r >= 0 );
#endif
}
void CContextMgr::SetGetTimeCallback(TIMEFUNC_t func)
{
m_getTimeFunc = func;
}
END_AS_NAMESPACE

View File

@@ -0,0 +1,99 @@
#ifndef CONTEXTMGR_H
#define CONTEXTMGR_H
// The context manager simplifies the management of multiple concurrent scripts
// More than one context manager can be used, if you wish to control different
// groups of scripts separately, e.g. game object scripts, and GUI scripts.
// OBSERVATION: This class is currently not thread safe.
#ifndef ANGELSCRIPT_H
// Avoid having to inform include path if header is already include before
#include <angelscript.h>
#endif
#include <vector>
BEGIN_AS_NAMESPACE
class CScriptDictionary;
// The internal structure for holding contexts
struct SContextInfo;
// The signature of the get time callback function
typedef asUINT (*TIMEFUNC_t)();
class CContextMgr
{
public:
CContextMgr();
~CContextMgr();
// Set the function that the manager will use to obtain the time in milliseconds
void SetGetTimeCallback(TIMEFUNC_t func);
// Registers the following:
//
// void sleep(uint milliseconds)
//
// The application must set the get time callback for this to work
void RegisterThreadSupport(asIScriptEngine *engine);
// Registers the following:
//
// funcdef void coroutine(dictionary@)
// void createCoRoutine(coroutine @func, dictionary @args)
// void yield()
void RegisterCoRoutineSupport(asIScriptEngine *engine);
// Create a new context, prepare it with the function id, then return
// it so that the application can pass the argument values. The context
// will be released by the manager after the execution has completed.
// Set keepCtxAfterExecution to true if the application needs to retrieve
// information from the context after it the script has finished.
asIScriptContext *AddContext(asIScriptEngine *engine, asIScriptFunction *func, bool keepCtxAfterExecution = false);
// If the context was kept after the execution, this method must be
// called when the application is done with the context so it can be
// returned to the pool for reuse.
void DoneWithContext(asIScriptContext *ctx);
// Create a new context, prepare it with the function id, then return
// it so that the application can pass the argument values. The context
// will be added as a co-routine in the same thread as the currCtx.
asIScriptContext *AddContextForCoRoutine(asIScriptContext *currCtx, asIScriptFunction *func);
// Execute each script that is not currently sleeping. The function returns after
// each script has been executed once. The application should call this function
// for each iteration of the message pump, or game loop, or whatever.
// Returns the number of scripts still in execution.
int ExecuteScripts();
// Put a script to sleep for a while
void SetSleeping(asIScriptContext *ctx, asUINT milliSeconds);
// Switch the execution to the next co-routine in the group.
// Returns true if the switch was successful.
void NextCoRoutine();
// Abort all scripts
void AbortAll();
protected:
std::vector<SContextInfo*> m_threads;
std::vector<SContextInfo*> m_freeThreads;
asUINT m_currentThread;
TIMEFUNC_t m_getTimeFunc;
// Statistics for Garbage Collection
asUINT m_numExecutions;
asUINT m_numGCObjectsCreated;
asUINT m_numGCObjectsDestroyed;
};
END_AS_NAMESPACE
#endif

View File

@@ -0,0 +1,270 @@
#include "datetime.h"
#include "../autowrapper/aswrappedcall.h"
#include <string.h>
#include <assert.h>
#include <new>
using namespace std;
using namespace std::chrono;
BEGIN_AS_NAMESPACE
// TODO: Allow setting the timezone to use
static tm time_point_to_tm(const std::chrono::time_point<std::chrono::system_clock> &tp)
{
time_t t = system_clock::to_time_t(tp);
tm local;
// Use the universal timezone
#ifdef _MSC_VER
gmtime_s(&local, &t);
#else
local = *gmtime(&t);
#endif
return local;
}
// Returns true if successful. Doesn't modify tp if not successful
static bool tm_to_time_point(const tm &_tm, std::chrono::time_point<std::chrono::system_clock> &tp)
{
tm localTm = _tm;
// Do not rely on timezone, as it is not portable
// ref: https://stackoverflow.com/questions/38298261/why-there-is-no-inverse-function-for-gmtime-in-libc
// ref: https://stackoverflow.com/questions/8558919/mktime-and-tm-isdst
localTm.tm_isdst = -1; // Always use current settings, so mktime doesn't modify the time for daylight savings
time_t t = mktime(&localTm);
if (t == -1)
return false;
// Adjust the time_t since epoch with the difference of the local timezone to the universal timezone
t += (mktime(localtime(&t)) - mktime(gmtime(&t)));
// Verify if the members were modified, indicating an out-of-range value in input
if (localTm.tm_year != _tm.tm_year ||
localTm.tm_mon != _tm.tm_mon ||
localTm.tm_mday != _tm.tm_mday ||
localTm.tm_hour != _tm.tm_hour ||
localTm.tm_min != _tm.tm_min ||
localTm.tm_sec != _tm.tm_sec)
return false;
tp = system_clock::from_time_t(t);
return true;
}
CDateTime::CDateTime() : tp(std::chrono::system_clock::now())
{
}
CDateTime::CDateTime(const CDateTime &o) : tp(o.tp)
{
}
CDateTime &CDateTime::operator=(const CDateTime &o)
{
tp = o.tp;
return *this;
}
asUINT CDateTime::getYear() const
{
tm local = time_point_to_tm(tp);
return local.tm_year + 1900;
}
asUINT CDateTime::getMonth() const
{
tm local = time_point_to_tm(tp);
return local.tm_mon + 1;
}
asUINT CDateTime::getDay() const
{
tm local = time_point_to_tm(tp);
return local.tm_mday;
}
asUINT CDateTime::getHour() const
{
tm local = time_point_to_tm(tp);
return local.tm_hour;
}
asUINT CDateTime::getMinute() const
{
tm local = time_point_to_tm(tp);
return local.tm_min;
}
asUINT CDateTime::getSecond() const
{
tm local = time_point_to_tm(tp);
return local.tm_sec;
}
bool CDateTime::setDate(asUINT year, asUINT month, asUINT day)
{
tm local = time_point_to_tm(tp);
local.tm_year = int(year) - 1900;
local.tm_mon = month - 1;
local.tm_mday = day;
return tm_to_time_point(local, tp);
}
bool CDateTime::setTime(asUINT hour, asUINT minute, asUINT second)
{
tm local = time_point_to_tm(tp);
local.tm_hour = hour;
local.tm_min = minute;
local.tm_sec = second;
return tm_to_time_point(local, tp);
}
CDateTime::CDateTime(asUINT year, asUINT month, asUINT day, asUINT hour, asUINT minute, asUINT second)
{
tp = std::chrono::system_clock::now();
setDate(year, month, day);
setTime(hour, minute, second);
}
asINT64 CDateTime::operator-(const CDateTime &dt) const
{
return (tp - dt.tp).count() / std::chrono::system_clock::period::den * std::chrono::system_clock::period::num;
}
CDateTime CDateTime::operator+(asINT64 seconds) const
{
CDateTime dt(*this);
dt.tp += std::chrono::system_clock::duration(seconds * std::chrono::system_clock::period::den / std::chrono::system_clock::period::num);
return dt;
}
CDateTime &CDateTime::operator+=(asINT64 seconds)
{
tp += std::chrono::system_clock::duration(seconds * std::chrono::system_clock::period::den / std::chrono::system_clock::period::num);
return *this;
}
CDateTime operator+(asINT64 seconds, const CDateTime &other)
{
return other + seconds;
}
CDateTime CDateTime::operator-(asINT64 seconds) const
{
return *this + -seconds;
}
CDateTime &CDateTime::operator-=(asINT64 seconds)
{
return *this += -seconds;
}
CDateTime operator-(asINT64 seconds, const CDateTime &other)
{
return other + -seconds;
}
bool CDateTime::operator==(const CDateTime &other) const
{
return tp == other.tp;
}
bool CDateTime::operator<(const CDateTime &other) const
{
return tp < other.tp;
}
static int opCmp(const CDateTime &a, const CDateTime &b)
{
if (a < b) return -1;
if (a == b) return 0;
return 1;
}
static void Construct(CDateTime *mem)
{
new(mem) CDateTime();
}
static void ConstructCopy(CDateTime *mem, const CDateTime &o)
{
new(mem) CDateTime(o);
}
static void ConstructSet(CDateTime *mem, asUINT year, asUINT month, asUINT day, asUINT hour, asUINT minute, asUINT second)
{
new(mem) CDateTime(year, month, day, hour, minute, second);
}
static void ConstructSet_Generic(asIScriptGeneric *gen)
{
CDateTime *date = (CDateTime*)gen->GetObject();
asUINT year = *(asUINT*)gen->GetAddressOfArg(0);
asUINT month = *(asUINT*)gen->GetAddressOfArg(1);
asUINT day = *(asUINT*)gen->GetAddressOfArg(2);
asUINT hour = *(asUINT*)gen->GetAddressOfArg(3);
asUINT minute = *(asUINT*)gen->GetAddressOfArg(4);
asUINT second = *(asUINT*)gen->GetAddressOfArg(5);
ConstructSet(date, year, month, day, hour, minute, second);
}
void RegisterScriptDateTime(asIScriptEngine *engine)
{
int r = engine->RegisterObjectType("datetime", sizeof(CDateTime), asOBJ_VALUE | asOBJ_POD | asGetTypeTraits<CDateTime>()); assert(r >= 0);
if(strstr(asGetLibraryOptions(), "AS_MAX_PORTABILITY")==0)
{
r = engine->RegisterObjectBehaviour("datetime", asBEHAVE_CONSTRUCT, "void f()", asFUNCTION(Construct), asCALL_CDECL_OBJLAST); assert(r >= 0);
r = engine->RegisterObjectBehaviour("datetime", asBEHAVE_CONSTRUCT, "void f(const datetime &in)", asFUNCTION(ConstructCopy), asCALL_CDECL_OBJFIRST); assert(r >= 0);
r = engine->RegisterObjectBehaviour("datetime", asBEHAVE_CONSTRUCT, "void f(uint, uint, uint, uint = 0, uint = 0, uint = 0)", asFUNCTION(ConstructSet), asCALL_CDECL_OBJFIRST); assert(r >= 0);
r = engine->RegisterObjectMethod("datetime", "datetime &opAssign(const datetime &in)", asMETHOD(CDateTime, operator=), asCALL_THISCALL); assert(r >= 0);
r = engine->RegisterObjectMethod("datetime", "uint get_year() const property", asMETHOD(CDateTime, getYear), asCALL_THISCALL); assert(r >= 0);
r = engine->RegisterObjectMethod("datetime", "uint get_month() const property", asMETHOD(CDateTime, getMonth), asCALL_THISCALL); assert(r >= 0);
r = engine->RegisterObjectMethod("datetime", "uint get_day() const property", asMETHOD(CDateTime, getDay), asCALL_THISCALL); assert(r >= 0);
r = engine->RegisterObjectMethod("datetime", "uint get_hour() const property", asMETHOD(CDateTime, getHour), asCALL_THISCALL); assert(r >= 0);
r = engine->RegisterObjectMethod("datetime", "uint get_minute() const property", asMETHOD(CDateTime, getMinute), asCALL_THISCALL); assert(r >= 0);
r = engine->RegisterObjectMethod("datetime", "uint get_second() const property", asMETHOD(CDateTime, getSecond), asCALL_THISCALL); assert(r >= 0);
r = engine->RegisterObjectMethod("datetime", "bool setDate(uint year, uint month, uint day)", asMETHOD(CDateTime, setDate), asCALL_THISCALL); assert(r >= 0);
r = engine->RegisterObjectMethod("datetime", "bool setTime(uint hour, uint minute, uint second)", asMETHOD(CDateTime, setTime), asCALL_THISCALL); assert(r >= 0);
r = engine->RegisterObjectMethod("datetime", "int64 opSub(const datetime &in) const", asMETHODPR(CDateTime, operator-, (const CDateTime &other) const, asINT64), asCALL_THISCALL); assert(r >= 0);
r = engine->RegisterObjectMethod("datetime", "datetime opAdd(int64 seconds) const", asMETHOD(CDateTime, operator+), asCALL_THISCALL); assert(r >= 0);
r = engine->RegisterObjectMethod("datetime", "datetime opAdd_r(int64 seconds) const", asFUNCTIONPR(operator+, (asINT64 seconds, const CDateTime &other), CDateTime), asCALL_CDECL_OBJLAST); assert(r >= 0);
r = engine->RegisterObjectMethod("datetime", "datetime &opAddAssign(int64 seconds)", asMETHOD(CDateTime, operator+=), asCALL_THISCALL); assert(r >= 0);
r = engine->RegisterObjectMethod("datetime", "datetime opSub(int64 seconds) const", asMETHODPR(CDateTime, operator-, (asINT64) const, CDateTime), asCALL_THISCALL); assert(r >= 0);
r = engine->RegisterObjectMethod("datetime", "datetime opSub_r(int64 seconds) const", asFUNCTIONPR(operator-, (asINT64 seconds, const CDateTime &other), CDateTime), asCALL_CDECL_OBJLAST); assert(r >= 0);
r = engine->RegisterObjectMethod("datetime", "datetime &opSubAssign(int64 seconds)", asMETHOD(CDateTime, operator-=), asCALL_THISCALL); assert(r >= 0);
r = engine->RegisterObjectMethod("datetime", "bool opEquals(const datetime &in) const", asMETHODPR(CDateTime, operator==, (const CDateTime &other) const, bool), asCALL_THISCALL); assert(r >= 0);
r = engine->RegisterObjectMethod("datetime", "int opCmp(const datetime &in) const", asFUNCTION(opCmp), asCALL_CDECL_OBJFIRST); assert(r >= 0);
}
else
{
r = engine->RegisterObjectBehaviour("datetime", asBEHAVE_CONSTRUCT, "void f()", WRAP_OBJ_LAST(Construct), asCALL_GENERIC); assert(r >= 0);
r = engine->RegisterObjectBehaviour("datetime", asBEHAVE_CONSTRUCT, "void f(const datetime &in)", WRAP_OBJ_FIRST(ConstructCopy), asCALL_GENERIC); assert(r >= 0);
r = engine->RegisterObjectBehaviour("datetime", asBEHAVE_CONSTRUCT, "void f(uint, uint, uint, uint = 0, uint = 0, uint = 0)", asFUNCTION(ConstructSet_Generic), asCALL_GENERIC); assert(r >= 0);
r = engine->RegisterObjectMethod("datetime", "datetime &opAssign(const datetime &in)", WRAP_MFN(CDateTime, operator=), asCALL_GENERIC); assert(r >= 0);
r = engine->RegisterObjectMethod("datetime", "uint get_year() const property", WRAP_MFN(CDateTime, getYear), asCALL_GENERIC); assert(r >= 0);
r = engine->RegisterObjectMethod("datetime", "uint get_month() const property", WRAP_MFN(CDateTime, getMonth), asCALL_GENERIC); assert(r >= 0);
r = engine->RegisterObjectMethod("datetime", "uint get_day() const property", WRAP_MFN(CDateTime, getDay), asCALL_GENERIC); assert(r >= 0);
r = engine->RegisterObjectMethod("datetime", "uint get_hour() const property", WRAP_MFN(CDateTime, getHour), asCALL_GENERIC); assert(r >= 0);
r = engine->RegisterObjectMethod("datetime", "uint get_minute() const property", WRAP_MFN(CDateTime, getMinute), asCALL_GENERIC); assert(r >= 0);
r = engine->RegisterObjectMethod("datetime", "uint get_second() const property", WRAP_MFN(CDateTime, getSecond), asCALL_GENERIC); assert(r >= 0);
r = engine->RegisterObjectMethod("datetime", "bool setDate(uint year, uint month, uint day)", WRAP_MFN(CDateTime, setDate), asCALL_GENERIC); assert(r >= 0);
r = engine->RegisterObjectMethod("datetime", "bool setTime(uint hour, uint minute, uint second)", WRAP_MFN(CDateTime, setTime), asCALL_GENERIC); assert(r >= 0);
r = engine->RegisterObjectMethod("datetime", "int64 opSub(const datetime &in) const", WRAP_MFN_PR(CDateTime, operator-, (const CDateTime &other) const, asINT64), asCALL_GENERIC); assert(r >= 0);
r = engine->RegisterObjectMethod("datetime", "datetime opAdd(int64 seconds) const", WRAP_MFN(CDateTime, operator+), asCALL_GENERIC); assert(r >= 0);
r = engine->RegisterObjectMethod("datetime", "datetime opAdd_r(int64 seconds) const", WRAP_OBJ_LAST_PR(operator+, (asINT64 seconds, const CDateTime &other), CDateTime), asCALL_GENERIC); assert(r >= 0);
r = engine->RegisterObjectMethod("datetime", "datetime &opAddAssign(int64 seconds)", WRAP_MFN(CDateTime, operator+=), asCALL_GENERIC); assert(r >= 0);
r = engine->RegisterObjectMethod("datetime", "datetime opSub(int64 seconds) const", WRAP_MFN_PR(CDateTime, operator-, (asINT64) const, CDateTime), asCALL_GENERIC); assert(r >= 0);
r = engine->RegisterObjectMethod("datetime", "datetime opSub_r(int64 seconds) const", WRAP_OBJ_LAST_PR(operator-, (asINT64 seconds, const CDateTime &other), CDateTime), asCALL_GENERIC); assert(r >= 0);
r = engine->RegisterObjectMethod("datetime", "datetime &opSubAssign(int64 seconds)", WRAP_MFN(CDateTime, operator-=), asCALL_GENERIC); assert(r >= 0);
r = engine->RegisterObjectMethod("datetime", "bool opEquals(const datetime &in) const", WRAP_MFN_PR(CDateTime, operator==, (const CDateTime &other) const, bool), asCALL_GENERIC); assert(r >= 0);
r = engine->RegisterObjectMethod("datetime", "int opCmp(const datetime &in) const", WRAP_OBJ_FIRST(opCmp), asCALL_GENERIC); assert(r >= 0);
}
}
END_AS_NAMESPACE

View File

@@ -0,0 +1,61 @@
#ifndef SCRIPTDATETIME_H
#define SCRIPTDATETIME_H
#ifndef ANGELSCRIPT_H
// Avoid having to inform include path if header is already include before
#include <angelscript.h>
#endif
#ifdef AS_CAN_USE_CPP11
#include <chrono>
#else
#error Sorry, this requires C++11 which your compiler doesnt appear to support
#endif
BEGIN_AS_NAMESPACE
class CDateTime
{
public:
// Constructors
CDateTime();
CDateTime(const CDateTime &other);
CDateTime(asUINT year, asUINT month, asUINT day, asUINT hour, asUINT minute, asUINT second);
// Copy the stored value from another any object
CDateTime &operator=(const CDateTime &other);
// Accessors
asUINT getYear() const;
asUINT getMonth() const;
asUINT getDay() const;
asUINT getHour() const;
asUINT getMinute() const;
asUINT getSecond() const;
// Setters
// Returns true if valid
bool setDate(asUINT year, asUINT month, asUINT day);
bool setTime(asUINT hour, asUINT minute, asUINT second);
// Operators
// Return difference in seconds
asINT64 operator-(const CDateTime &other) const;
CDateTime operator+(asINT64 seconds) const;
friend CDateTime operator+(asINT64 seconds, const CDateTime &other);
CDateTime & operator+=(asINT64 seconds);
CDateTime operator-(asINT64 seconds) const;
friend CDateTime operator-(asINT64 seconds, const CDateTime &other);
CDateTime & operator-=(asINT64 seconds);
bool operator==(const CDateTime &other) const;
bool operator<(const CDateTime &other) const;
protected:
std::chrono::system_clock::time_point tp;
};
void RegisterScriptDateTime(asIScriptEngine *engine);
END_AS_NAMESPACE
#endif

View File

@@ -0,0 +1,852 @@
#include "debugger.h"
#include <iostream> // cout
#include <sstream> // stringstream
#include <stdlib.h> // atoi
#include <assert.h> // assert
using namespace std;
BEGIN_AS_NAMESPACE
CDebugger::CDebugger()
{
m_action = CONTINUE;
m_lastFunction = 0;
m_engine = 0;
}
CDebugger::~CDebugger()
{
SetEngine(0);
}
string CDebugger::ToString(void *value, asUINT typeId, int expandMembers, asIScriptEngine *engine)
{
if( value == 0 )
return "<null>";
// If no engine pointer was provided use the default
if( engine == 0 )
engine = m_engine;
stringstream s;
if( typeId == asTYPEID_VOID )
return "<void>";
else if( typeId == asTYPEID_BOOL )
return *(bool*)value ? "true" : "false";
else if( typeId == asTYPEID_INT8 )
s << (int)*(signed char*)value;
else if( typeId == asTYPEID_INT16 )
s << (int)*(signed short*)value;
else if( typeId == asTYPEID_INT32 )
s << *(signed int*)value;
else if( typeId == asTYPEID_INT64 )
#if defined(_MSC_VER) && _MSC_VER <= 1200
s << "{...}"; // MSVC6 doesn't like the << operator for 64bit integer
#else
s << *(asINT64*)value;
#endif
else if( typeId == asTYPEID_UINT8 )
s << (unsigned int)*(unsigned char*)value;
else if( typeId == asTYPEID_UINT16 )
s << (unsigned int)*(unsigned short*)value;
else if( typeId == asTYPEID_UINT32 )
s << *(unsigned int*)value;
else if( typeId == asTYPEID_UINT64 )
#if defined(_MSC_VER) && _MSC_VER <= 1200
s << "{...}"; // MSVC6 doesn't like the << operator for 64bit integer
#else
s << *(asQWORD*)value;
#endif
else if( typeId == asTYPEID_FLOAT )
s << *(float*)value;
else if( typeId == asTYPEID_DOUBLE )
s << *(double*)value;
else if( (typeId & asTYPEID_MASK_OBJECT) == 0 )
{
// The type is an enum
s << *(asUINT*)value;
// Check if the value matches one of the defined enums
if( engine )
{
asITypeInfo *t = engine->GetTypeInfoById(typeId);
for( int n = t->GetEnumValueCount(); n-- > 0; )
{
int enumVal;
const char *enumName = t->GetEnumValueByIndex(n, &enumVal);
if( enumVal == *(int*)value )
{
s << ", " << enumName;
break;
}
}
}
}
else if( typeId & asTYPEID_SCRIPTOBJECT )
{
// Dereference handles, so we can see what it points to
if( typeId & asTYPEID_OBJHANDLE )
value = *(void**)value;
asIScriptObject *obj = (asIScriptObject *)value;
// Print the address of the object
s << "{" << obj << "}";
// Print the members
if( obj && expandMembers > 0 )
{
asITypeInfo *type = obj->GetObjectType();
for( asUINT n = 0; n < obj->GetPropertyCount(); n++ )
{
if( n == 0 )
s << " ";
else
s << ", ";
s << type->GetPropertyDeclaration(n) << " = " << ToString(obj->GetAddressOfProperty(n), obj->GetPropertyTypeId(n), expandMembers - 1, type->GetEngine());
}
}
}
else
{
// Dereference handles, so we can see what it points to
if( typeId & asTYPEID_OBJHANDLE )
value = *(void**)value;
// Print the address for reference types so it will be
// possible to see when handles point to the same object
if( engine )
{
asITypeInfo *type = engine->GetTypeInfoById(typeId);
if( type->GetFlags() & asOBJ_REF )
s << "{" << value << "}";
if( value )
{
// Check if there is a registered to-string callback
map<const asITypeInfo*, ToStringCallback>::iterator it = m_toStringCallbacks.find(type);
if( it == m_toStringCallbacks.end() )
{
// If the type is a template instance, there might be a
// to-string callback for the generic template type
if( type->GetFlags() & asOBJ_TEMPLATE )
{
asITypeInfo *tmplType = engine->GetTypeInfoByName(type->GetName());
it = m_toStringCallbacks.find(tmplType);
}
}
if( it != m_toStringCallbacks.end() )
{
if( type->GetFlags() & asOBJ_REF )
s << " ";
// Invoke the callback to get the string representation of this type
string str = it->second(value, expandMembers, this);
s << str;
}
}
}
else
s << "{no engine}";
}
return s.str();
}
void CDebugger::RegisterToStringCallback(const asITypeInfo *ot, ToStringCallback callback)
{
if( m_toStringCallbacks.find(ot) == m_toStringCallbacks.end() )
m_toStringCallbacks.insert(map<const asITypeInfo*, ToStringCallback>::value_type(ot, callback));
}
void CDebugger::LineCallback(asIScriptContext *ctx)
{
assert( ctx );
// This should never happen, but it doesn't hurt to validate it
if( ctx == 0 )
return;
// By default we ignore callbacks when the context is not active.
// An application might override this to for example disconnect the
// debugger as the execution finished.
if( ctx->GetState() != asEXECUTION_ACTIVE )
return;
if( m_action == CONTINUE )
{
if( !CheckBreakPoint(ctx) )
return;
}
else if( m_action == STEP_OVER )
{
if( ctx->GetCallstackSize() > m_lastCommandAtStackLevel )
{
if( !CheckBreakPoint(ctx) )
return;
}
}
else if( m_action == STEP_OUT )
{
if( ctx->GetCallstackSize() >= m_lastCommandAtStackLevel )
{
if( !CheckBreakPoint(ctx) )
return;
}
}
else if( m_action == STEP_INTO )
{
CheckBreakPoint(ctx);
// Always break, but we call the check break point anyway
// to tell user when break point has been reached
}
stringstream s;
const char *file = 0;
int lineNbr = ctx->GetLineNumber(0, 0, &file);
s << (file ? file : "{unnamed}") << ":" << lineNbr << "; " << ctx->GetFunction()->GetDeclaration() << endl;
Output(s.str());
TakeCommands(ctx);
}
bool CDebugger::CheckBreakPoint(asIScriptContext *ctx)
{
if( ctx == 0 )
return false;
// TODO: Should cache the break points in a function by checking which possible break points
// can be hit when entering a function. If there are no break points in the current function
// then there is no need to check every line.
const char *tmp = 0;
int lineNbr = ctx->GetLineNumber(0, 0, &tmp);
// Consider just filename, not the full path
string file = tmp ? tmp : "";
size_t r = file.find_last_of("\\/");
if( r != string::npos )
file = file.substr(r+1);
// Did we move into a new function?
asIScriptFunction *func = ctx->GetFunction();
if( m_lastFunction != func )
{
// Check if any breakpoints need adjusting
for( size_t n = 0; n < m_breakPoints.size(); n++ )
{
// We need to check for a breakpoint at entering the function
if( m_breakPoints[n].func )
{
if( m_breakPoints[n].name == func->GetName() )
{
stringstream s;
s << "Entering function '" << m_breakPoints[n].name << "'. Transforming it into break point" << endl;
Output(s.str());
// Transform the function breakpoint into a file breakpoint
m_breakPoints[n].name = file;
m_breakPoints[n].lineNbr = lineNbr;
m_breakPoints[n].func = false;
m_breakPoints[n].needsAdjusting = false;
}
}
// Check if a given breakpoint fall on a line with code or else adjust it to the next line
else if( m_breakPoints[n].needsAdjusting &&
m_breakPoints[n].name == file )
{
int line = func->FindNextLineWithCode(m_breakPoints[n].lineNbr);
if( line >= 0 )
{
m_breakPoints[n].needsAdjusting = false;
if( line != m_breakPoints[n].lineNbr )
{
stringstream s;
s << "Moving break point " << n << " in file '" << file << "' to next line with code at line " << line << endl;
Output(s.str());
// Move the breakpoint to the next line
m_breakPoints[n].lineNbr = line;
}
}
}
}
}
m_lastFunction = func;
// Determine if there is a breakpoint at the current line
for( size_t n = 0; n < m_breakPoints.size(); n++ )
{
// TODO: do case-less comparison for file name
// Should we break?
if( !m_breakPoints[n].func &&
m_breakPoints[n].lineNbr == lineNbr &&
m_breakPoints[n].name == file )
{
stringstream s;
s << "Reached break point " << n << " in file '" << file << "' at line " << lineNbr << endl;
Output(s.str());
return true;
}
}
return false;
}
void CDebugger::TakeCommands(asIScriptContext *ctx)
{
for(;;)
{
char buf[512];
Output("[dbg]> ");
cin.getline(buf, 512);
if( InterpretCommand(string(buf), ctx) )
break;
}
}
bool CDebugger::InterpretCommand(const string &cmd, asIScriptContext *ctx)
{
if( cmd.length() == 0 ) return true;
switch( cmd[0] )
{
case 'c':
m_action = CONTINUE;
break;
case 's':
m_action = STEP_INTO;
break;
case 'n':
m_action = STEP_OVER;
m_lastCommandAtStackLevel = ctx ? ctx->GetCallstackSize() : 1;
break;
case 'o':
m_action = STEP_OUT;
m_lastCommandAtStackLevel = ctx ? ctx->GetCallstackSize() : 0;
break;
case 'b':
{
// Set break point
size_t p = cmd.find_first_not_of(" \t", 1);
size_t div = cmd.find(':');
if( div != string::npos && div > 2 && p > 1 )
{
string file = cmd.substr(2, div-2);
string line = cmd.substr(div+1);
int nbr = atoi(line.c_str());
AddFileBreakPoint(file, nbr);
}
else if( div == string::npos && p != string::npos && p > 1 )
{
string func = cmd.substr(p);
AddFuncBreakPoint(func);
}
else
{
Output("Incorrect format for setting break point, expected one of:\n"
" b <file name>:<line number>\n"
" b <function name>\n");
}
}
// take more commands
return false;
case 'r':
{
// Remove break point
size_t p = cmd.find_first_not_of(" \t", 1);
if( cmd.length() > 2 && p != string::npos && p > 1 )
{
string br = cmd.substr(2);
if( br == "all" )
{
m_breakPoints.clear();
Output("All break points have been removed\n");
}
else
{
int nbr = atoi(br.c_str());
if( nbr >= 0 && nbr < (int)m_breakPoints.size() )
m_breakPoints.erase(m_breakPoints.begin()+nbr);
ListBreakPoints();
}
}
else
{
Output("Incorrect format for removing break points, expected:\n"
" r <all|number of break point>\n");
}
}
// take more commands
return false;
case 'l':
{
// List something
bool printHelp = false;
size_t p = cmd.find_first_not_of(" \t", 1);
if( p != string::npos && p > 1 )
{
if( cmd[p] == 'b' )
{
ListBreakPoints();
}
else if( cmd[p] == 'v' )
{
ListLocalVariables(ctx);
}
else if( cmd[p] == 'g' )
{
ListGlobalVariables(ctx);
}
else if( cmd[p] == 'm' )
{
ListMemberProperties(ctx);
}
else if( cmd[p] == 's' )
{
ListStatistics(ctx);
}
else
{
Output("Unknown list option.\n");
printHelp = true;
}
}
else
{
Output("Incorrect format for list command.\n");
printHelp = true;
}
if( printHelp )
{
Output("Expected format: \n"
" l <list option>\n"
"Available options: \n"
" b - breakpoints\n"
" v - local variables\n"
" m - member properties\n"
" g - global variables\n"
" s - statistics\n");
}
}
// take more commands
return false;
case 'h':
PrintHelp();
// take more commands
return false;
case 'p':
{
// Print a value
size_t p = cmd.find_first_not_of(" \t", 1);
if( p != string::npos && p > 1 )
{
PrintValue(cmd.substr(p), ctx);
}
else
{
Output("Incorrect format for print, expected:\n"
" p <expression>\n");
}
}
// take more commands
return false;
case 'w':
// Where am I?
PrintCallstack(ctx);
// take more commands
return false;
case 'a':
// abort the execution
if( ctx == 0 )
{
Output("No script is running\n");
return false;
}
ctx->Abort();
break;
default:
Output("Unknown command\n");
// take more commands
return false;
}
// Continue execution
return true;
}
void CDebugger::PrintValue(const std::string &expr, asIScriptContext *ctx)
{
if( ctx == 0 )
{
Output("No script is running\n");
return;
}
asIScriptEngine *engine = ctx->GetEngine();
// Tokenize the input string to get the variable scope and name
asUINT len = 0;
string scope;
string name;
string str = expr;
asETokenClass t = engine->ParseToken(str.c_str(), 0, &len);
while( t == asTC_IDENTIFIER || (t == asTC_KEYWORD && len == 2 && str.compare(0, 2, "::") == 0) )
{
if( t == asTC_KEYWORD )
{
if( scope == "" && name == "" )
scope = "::"; // global scope
else if( scope == "::" || scope == "" )
scope = name; // namespace
else
scope += "::" + name; // nested namespace
name = "";
}
else if( t == asTC_IDENTIFIER )
name.assign(str.c_str(), len);
// Skip the parsed token and get the next one
str = str.substr(len);
t = engine->ParseToken(str.c_str(), 0, &len);
}
if( name.size() )
{
// Find the variable
void *ptr = 0;
int typeId = 0;
asIScriptFunction *func = ctx->GetFunction();
if( !func ) return;
// skip local variables if a scope was informed
if( scope == "" )
{
// We start from the end, in case the same name is reused in different scopes
for( asUINT n = func->GetVarCount(); n-- > 0; )
{
if( ctx->IsVarInScope(n) && name == ctx->GetVarName(n) )
{
ptr = ctx->GetAddressOfVar(n);
typeId = ctx->GetVarTypeId(n);
break;
}
}
// Look for class members, if we're in a class method
if( !ptr && func->GetObjectType() )
{
if( name == "this" )
{
ptr = ctx->GetThisPointer();
typeId = ctx->GetThisTypeId();
}
else
{
asITypeInfo *type = engine->GetTypeInfoById(ctx->GetThisTypeId());
for( asUINT n = 0; n < type->GetPropertyCount(); n++ )
{
const char *propName = 0;
int offset = 0;
bool isReference = 0;
int compositeOffset = 0;
bool isCompositeIndirect = false;
type->GetProperty(n, &propName, &typeId, 0, 0, &offset, &isReference, 0, &compositeOffset, &isCompositeIndirect);
if( name == propName )
{
ptr = (void*)(((asBYTE*)ctx->GetThisPointer())+compositeOffset);
if (isCompositeIndirect) ptr = *(void**)ptr;
ptr = (void*)(((asBYTE*)ptr) + offset);
if( isReference ) ptr = *(void**)ptr;
break;
}
}
}
}
}
// Look for global variables
if( !ptr )
{
if( scope == "" )
{
// If no explicit scope was informed then use the namespace of the current function by default
scope = func->GetNamespace();
}
else if( scope == "::" )
{
// The global namespace will be empty
scope = "";
}
asIScriptModule *mod = func->GetModule();
if( mod )
{
for( asUINT n = 0; n < mod->GetGlobalVarCount(); n++ )
{
const char *varName = 0, *nameSpace = 0;
mod->GetGlobalVar(n, &varName, &nameSpace, &typeId);
// Check if both name and namespace match
if( name == varName && scope == nameSpace )
{
ptr = mod->GetAddressOfGlobalVar(n);
break;
}
}
}
}
if( ptr )
{
// TODO: If there is a . after the identifier, check for members
// TODO: If there is a [ after the identifier try to call the 'opIndex(expr) const' method
if( str != "" )
{
Output("Invalid expression. Expression doesn't end after symbol\n");
}
else
{
stringstream s;
// TODO: Allow user to set if members should be expanded
// Expand members by default to 3 recursive levels only
s << ToString(ptr, typeId, 3, engine) << endl;
Output(s.str());
}
}
else
{
Output("Invalid expression. No matching symbol\n");
}
}
else
{
Output("Invalid expression. Expected identifier\n");
}
}
void CDebugger::ListBreakPoints()
{
// List all break points
stringstream s;
for( size_t b = 0; b < m_breakPoints.size(); b++ )
if( m_breakPoints[b].func )
s << b << " - " << m_breakPoints[b].name << endl;
else
s << b << " - " << m_breakPoints[b].name << ":" << m_breakPoints[b].lineNbr << endl;
Output(s.str());
}
void CDebugger::ListMemberProperties(asIScriptContext *ctx)
{
if( ctx == 0 )
{
Output("No script is running\n");
return;
}
void *ptr = ctx->GetThisPointer();
if( ptr )
{
stringstream s;
// TODO: Allow user to define if members should be expanded or not
// Expand members by default to 3 recursive levels only
s << "this = " << ToString(ptr, ctx->GetThisTypeId(), 3, ctx->GetEngine()) << endl;
Output(s.str());
}
}
void CDebugger::ListLocalVariables(asIScriptContext *ctx)
{
if( ctx == 0 )
{
Output("No script is running\n");
return;
}
asIScriptFunction *func = ctx->GetFunction();
if( !func ) return;
stringstream s;
for( asUINT n = 0; n < func->GetVarCount(); n++ )
{
if( ctx->IsVarInScope(n) )
{
// TODO: Allow user to set if members should be expanded or not
// Expand members by default to 3 recursive levels only
s << func->GetVarDecl(n) << " = " << ToString(ctx->GetAddressOfVar(n), ctx->GetVarTypeId(n), 3, ctx->GetEngine()) << endl;
}
}
Output(s.str());
}
void CDebugger::ListGlobalVariables(asIScriptContext *ctx)
{
if( ctx == 0 )
{
Output("No script is running\n");
return;
}
// Determine the current module from the function
asIScriptFunction *func = ctx->GetFunction();
if( !func ) return;
asIScriptModule *mod = func->GetModule();
if( !mod ) return;
stringstream s;
for( asUINT n = 0; n < mod->GetGlobalVarCount(); n++ )
{
int typeId = 0;
mod->GetGlobalVar(n, 0, 0, &typeId);
// TODO: Allow user to set how many recursive expansions should be done
// Expand members by default to 3 recursive levels only
s << mod->GetGlobalVarDeclaration(n) << " = " << ToString(mod->GetAddressOfGlobalVar(n), typeId, 3, ctx->GetEngine()) << endl;
}
Output(s.str());
}
void CDebugger::ListStatistics(asIScriptContext *ctx)
{
if( ctx == 0 )
{
Output("No script is running\n");
return;
}
asIScriptEngine *engine = ctx->GetEngine();
asUINT gcCurrSize, gcTotalDestr, gcTotalDet, gcNewObjects, gcTotalNewDestr;
engine->GetGCStatistics(&gcCurrSize, &gcTotalDestr, &gcTotalDet, &gcNewObjects, &gcTotalNewDestr);
stringstream s;
s << "Garbage collector:" << endl;
s << " current size: " << gcCurrSize << endl;
s << " total destroyed: " << gcTotalDestr << endl;
s << " total detected: " << gcTotalDet << endl;
s << " new objects: " << gcNewObjects << endl;
s << " new objects destroyed: " << gcTotalNewDestr << endl;
Output(s.str());
}
void CDebugger::PrintCallstack(asIScriptContext *ctx)
{
if( ctx == 0 )
{
Output("No script is running\n");
return;
}
stringstream s;
const char *file = 0;
int lineNbr = 0;
for( asUINT n = 0; n < ctx->GetCallstackSize(); n++ )
{
lineNbr = ctx->GetLineNumber(n, 0, &file);
s << (file ? file : "{unnamed}") << ":" << lineNbr << "; " << ctx->GetFunction(n)->GetDeclaration() << endl;
}
Output(s.str());
}
void CDebugger::AddFuncBreakPoint(const string &func)
{
// Trim the function name
size_t b = func.find_first_not_of(" \t");
size_t e = func.find_last_not_of(" \t");
string actual = func.substr(b, e != string::npos ? e-b+1 : string::npos);
stringstream s;
s << "Adding deferred break point for function '" << actual << "'" << endl;
Output(s.str());
BreakPoint bp(actual, 0, true);
m_breakPoints.push_back(bp);
}
void CDebugger::AddFileBreakPoint(const string &file, int lineNbr)
{
// Store just file name, not entire path
size_t r = file.find_last_of("\\/");
string actual;
if( r != string::npos )
actual = file.substr(r+1);
else
actual = file;
// Trim the file name
size_t b = actual.find_first_not_of(" \t");
size_t e = actual.find_last_not_of(" \t");
actual = actual.substr(b, e != string::npos ? e-b+1 : string::npos);
stringstream s;
s << "Setting break point in file '" << actual << "' at line " << lineNbr << endl;
Output(s.str());
BreakPoint bp(actual, lineNbr, false);
m_breakPoints.push_back(bp);
}
void CDebugger::PrintHelp()
{
Output(" c - Continue\n"
" s - Step into\n"
" n - Next step\n"
" o - Step out\n"
" b - Set break point\n"
" l - List various things\n"
" r - Remove break point\n"
" p - Print value\n"
" w - Where am I?\n"
" a - Abort execution\n"
" h - Print this help text\n");
}
void CDebugger::Output(const string &str)
{
// By default we just output to stdout
cout << str;
}
void CDebugger::SetEngine(asIScriptEngine *engine)
{
if( m_engine != engine )
{
if( m_engine )
m_engine->Release();
m_engine = engine;
if( m_engine )
m_engine->AddRef();
}
}
asIScriptEngine *CDebugger::GetEngine()
{
return m_engine;
}
END_AS_NAMESPACE

View File

@@ -0,0 +1,87 @@
#ifndef DEBUGGER_H
#define DEBUGGER_H
#ifndef ANGELSCRIPT_H
// Avoid having to inform include path if header is already include before
#include <angelscript.h>
#endif
#include <string>
#include <vector>
#include <map>
BEGIN_AS_NAMESPACE
class CDebugger
{
public:
CDebugger();
virtual ~CDebugger();
// Register callbacks to handle to-string conversions of application types
// The expandMembersLevel is a counter for how many recursive levels the members should be expanded.
// If the object that is being converted to a string has members of its own the callback should call
// the debugger's ToString passing in expandMembersLevel - 1.
typedef std::string (*ToStringCallback)(void *obj, int expandMembersLevel, CDebugger *dbg);
virtual void RegisterToStringCallback(const asITypeInfo *ti, ToStringCallback callback);
// User interaction
virtual void TakeCommands(asIScriptContext *ctx);
virtual void Output(const std::string &str);
// Line callback invoked by context
virtual void LineCallback(asIScriptContext *ctx);
// Commands
virtual void PrintHelp();
virtual void AddFileBreakPoint(const std::string &file, int lineNbr);
virtual void AddFuncBreakPoint(const std::string &func);
virtual void ListBreakPoints();
virtual void ListLocalVariables(asIScriptContext *ctx);
virtual void ListGlobalVariables(asIScriptContext *ctx);
virtual void ListMemberProperties(asIScriptContext *ctx);
virtual void ListStatistics(asIScriptContext *ctx);
virtual void PrintCallstack(asIScriptContext *ctx);
virtual void PrintValue(const std::string &expr, asIScriptContext *ctx);
// Helpers
virtual bool InterpretCommand(const std::string &cmd, asIScriptContext *ctx);
virtual bool CheckBreakPoint(asIScriptContext *ctx);
virtual std::string ToString(void *value, asUINT typeId, int expandMembersLevel, asIScriptEngine *engine);
// Optionally set the engine pointer in the debugger so it can be retrieved
// by callbacks that need it. This will hold a reference to the engine.
virtual void SetEngine(asIScriptEngine *engine);
virtual asIScriptEngine *GetEngine();
protected:
enum DebugAction
{
CONTINUE, // continue until next break point
STEP_INTO, // stop at next instruction
STEP_OVER, // stop at next instruction, skipping called functions
STEP_OUT // run until returning from current function
};
DebugAction m_action;
asUINT m_lastCommandAtStackLevel;
asIScriptFunction *m_lastFunction;
struct BreakPoint
{
BreakPoint(std::string f, int n, bool _func) : name(f), lineNbr(n), func(_func), needsAdjusting(true) {}
std::string name;
int lineNbr;
bool func;
bool needsAdjusting;
};
std::vector<BreakPoint> m_breakPoints;
asIScriptEngine *m_engine;
// Registered callbacks for converting types to strings
std::map<const asITypeInfo*, ToStringCallback> m_toStringCallbacks;
};
END_AS_NAMESPACE
#endif

View File

@@ -0,0 +1,490 @@
#include "scriptany.h"
#include <new>
#include <assert.h>
#include <string.h>
BEGIN_AS_NAMESPACE
// We'll use the generic interface for the factories as we need the engine pointer
static void ScriptAnyFactory_Generic(asIScriptGeneric *gen)
{
asIScriptEngine *engine = gen->GetEngine();
*(CScriptAny**)gen->GetAddressOfReturnLocation() = new CScriptAny(engine);
}
static void ScriptAnyFactory2_Generic(asIScriptGeneric *gen)
{
asIScriptEngine *engine = gen->GetEngine();
void *ref = (void*)gen->GetArgAddress(0);
int refType = gen->GetArgTypeId(0);
*(CScriptAny**)gen->GetAddressOfReturnLocation() = new CScriptAny(ref,refType,engine);
}
static CScriptAny &ScriptAnyAssignment(CScriptAny *other, CScriptAny *self)
{
return *self = *other;
}
static void ScriptAnyAssignment_Generic(asIScriptGeneric *gen)
{
CScriptAny *other = (CScriptAny*)gen->GetArgObject(0);
CScriptAny *self = (CScriptAny*)gen->GetObject();
*self = *other;
gen->SetReturnObject(self);
}
static void ScriptAny_Store_Generic(asIScriptGeneric *gen)
{
void *ref = (void*)gen->GetArgAddress(0);
int refTypeId = gen->GetArgTypeId(0);
CScriptAny *self = (CScriptAny*)gen->GetObject();
self->Store(ref, refTypeId);
}
static void ScriptAny_StoreInt_Generic(asIScriptGeneric *gen)
{
asINT64 *ref = (asINT64*)gen->GetArgAddress(0);
CScriptAny *self = (CScriptAny*)gen->GetObject();
self->Store(*ref);
}
static void ScriptAny_StoreFlt_Generic(asIScriptGeneric *gen)
{
double *ref = (double*)gen->GetArgAddress(0);
CScriptAny *self = (CScriptAny*)gen->GetObject();
self->Store(*ref);
}
static void ScriptAny_Retrieve_Generic(asIScriptGeneric *gen)
{
void *ref = (void*)gen->GetArgAddress(0);
int refTypeId = gen->GetArgTypeId(0);
CScriptAny *self = (CScriptAny*)gen->GetObject();
*(bool*)gen->GetAddressOfReturnLocation() = self->Retrieve(ref, refTypeId);
}
static void ScriptAny_RetrieveInt_Generic(asIScriptGeneric *gen)
{
asINT64 *ref = (asINT64*)gen->GetArgAddress(0);
CScriptAny *self = (CScriptAny*)gen->GetObject();
*(bool*)gen->GetAddressOfReturnLocation() = self->Retrieve(*ref);
}
static void ScriptAny_RetrieveFlt_Generic(asIScriptGeneric *gen)
{
double *ref = (double*)gen->GetArgAddress(0);
CScriptAny *self = (CScriptAny*)gen->GetObject();
*(bool*)gen->GetAddressOfReturnLocation() = self->Retrieve(*ref);
}
static void ScriptAny_AddRef_Generic(asIScriptGeneric *gen)
{
CScriptAny *self = (CScriptAny*)gen->GetObject();
self->AddRef();
}
static void ScriptAny_Release_Generic(asIScriptGeneric *gen)
{
CScriptAny *self = (CScriptAny*)gen->GetObject();
self->Release();
}
static void ScriptAny_GetRefCount_Generic(asIScriptGeneric *gen)
{
CScriptAny *self = (CScriptAny*)gen->GetObject();
*(int*)gen->GetAddressOfReturnLocation() = self->GetRefCount();
}
static void ScriptAny_SetFlag_Generic(asIScriptGeneric *gen)
{
CScriptAny *self = (CScriptAny*)gen->GetObject();
self->SetFlag();
}
static void ScriptAny_GetFlag_Generic(asIScriptGeneric *gen)
{
CScriptAny *self = (CScriptAny*)gen->GetObject();
*(bool*)gen->GetAddressOfReturnLocation() = self->GetFlag();
}
static void ScriptAny_EnumReferences_Generic(asIScriptGeneric *gen)
{
CScriptAny *self = (CScriptAny*)gen->GetObject();
asIScriptEngine *engine = *(asIScriptEngine**)gen->GetAddressOfArg(0);
self->EnumReferences(engine);
}
static void ScriptAny_ReleaseAllHandles_Generic(asIScriptGeneric *gen)
{
CScriptAny *self = (CScriptAny*)gen->GetObject();
asIScriptEngine *engine = *(asIScriptEngine**)gen->GetAddressOfArg(0);
self->ReleaseAllHandles(engine);
}
void RegisterScriptAny(asIScriptEngine *engine)
{
if( strstr(asGetLibraryOptions(), "AS_MAX_PORTABILITY") )
RegisterScriptAny_Generic(engine);
else
RegisterScriptAny_Native(engine);
}
void RegisterScriptAny_Native(asIScriptEngine *engine)
{
int r;
r = engine->RegisterObjectType("any", sizeof(CScriptAny), asOBJ_REF | asOBJ_GC); assert( r >= 0 );
// We'll use the generic interface for the constructor as we need the engine pointer
r = engine->RegisterObjectBehaviour("any", asBEHAVE_FACTORY, "any@ f()", asFUNCTION(ScriptAnyFactory_Generic), asCALL_GENERIC); assert( r >= 0 );
r = engine->RegisterObjectBehaviour("any", asBEHAVE_FACTORY, "any@ f(?&in) explicit", asFUNCTION(ScriptAnyFactory2_Generic), asCALL_GENERIC); assert( r >= 0 );
r = engine->RegisterObjectBehaviour("any", asBEHAVE_FACTORY, "any@ f(const int64&in) explicit", asFUNCTION(ScriptAnyFactory2_Generic), asCALL_GENERIC); assert(r >= 0);
r = engine->RegisterObjectBehaviour("any", asBEHAVE_FACTORY, "any@ f(const double&in) explicit", asFUNCTION(ScriptAnyFactory2_Generic), asCALL_GENERIC); assert(r >= 0);
r = engine->RegisterObjectBehaviour("any", asBEHAVE_ADDREF, "void f()", asMETHOD(CScriptAny,AddRef), asCALL_THISCALL); assert( r >= 0 );
r = engine->RegisterObjectBehaviour("any", asBEHAVE_RELEASE, "void f()", asMETHOD(CScriptAny,Release), asCALL_THISCALL); assert( r >= 0 );
r = engine->RegisterObjectMethod("any", "any &opAssign(any&in)", asFUNCTION(ScriptAnyAssignment), asCALL_CDECL_OBJLAST); assert( r >= 0 );
r = engine->RegisterObjectMethod("any", "void store(?&in)", asMETHODPR(CScriptAny,Store,(void*,int),void), asCALL_THISCALL); assert( r >= 0 );
r = engine->RegisterObjectMethod("any", "void store(const int64&in)", asMETHODPR(CScriptAny,Store,(asINT64&),void), asCALL_THISCALL); assert( r >= 0 );
r = engine->RegisterObjectMethod("any", "void store(const double&in)", asMETHODPR(CScriptAny,Store,(double&),void), asCALL_THISCALL); assert( r >= 0 );
r = engine->RegisterObjectMethod("any", "bool retrieve(?&out)", asMETHODPR(CScriptAny,Retrieve,(void*,int) const,bool), asCALL_THISCALL); assert( r >= 0 );
r = engine->RegisterObjectMethod("any", "bool retrieve(int64&out)", asMETHODPR(CScriptAny,Retrieve,(asINT64&) const,bool), asCALL_THISCALL); assert( r >= 0 );
r = engine->RegisterObjectMethod("any", "bool retrieve(double&out)", asMETHODPR(CScriptAny,Retrieve,(double&) const,bool), asCALL_THISCALL); assert( r >= 0 );
// Register GC behaviours
r = engine->RegisterObjectBehaviour("any", asBEHAVE_GETREFCOUNT, "int f()", asMETHOD(CScriptAny,GetRefCount), asCALL_THISCALL); assert( r >= 0 );
r = engine->RegisterObjectBehaviour("any", asBEHAVE_SETGCFLAG, "void f()", asMETHOD(CScriptAny,SetFlag), asCALL_THISCALL); assert( r >= 0 );
r = engine->RegisterObjectBehaviour("any", asBEHAVE_GETGCFLAG, "bool f()", asMETHOD(CScriptAny,GetFlag), asCALL_THISCALL); assert( r >= 0 );
r = engine->RegisterObjectBehaviour("any", asBEHAVE_ENUMREFS, "void f(int&in)", asMETHOD(CScriptAny,EnumReferences), asCALL_THISCALL); assert( r >= 0 );
r = engine->RegisterObjectBehaviour("any", asBEHAVE_RELEASEREFS, "void f(int&in)", asMETHOD(CScriptAny,ReleaseAllHandles), asCALL_THISCALL); assert( r >= 0 );
}
void RegisterScriptAny_Generic(asIScriptEngine *engine)
{
int r;
r = engine->RegisterObjectType("any", sizeof(CScriptAny), asOBJ_REF | asOBJ_GC); assert( r >= 0 );
// We'll use the generic interface for the constructor as we need the engine pointer
r = engine->RegisterObjectBehaviour("any", asBEHAVE_FACTORY, "any@ f()", asFUNCTION(ScriptAnyFactory_Generic), asCALL_GENERIC); assert( r >= 0 );
r = engine->RegisterObjectBehaviour("any", asBEHAVE_FACTORY, "any@ f(?&in) explicit", asFUNCTION(ScriptAnyFactory2_Generic), asCALL_GENERIC); assert( r >= 0 );
r = engine->RegisterObjectBehaviour("any", asBEHAVE_FACTORY, "any@ f(const int64&in) explicit", asFUNCTION(ScriptAnyFactory2_Generic), asCALL_GENERIC); assert(r >= 0);
r = engine->RegisterObjectBehaviour("any", asBEHAVE_FACTORY, "any@ f(const double&in) explicit", asFUNCTION(ScriptAnyFactory2_Generic), asCALL_GENERIC); assert(r >= 0);
r = engine->RegisterObjectBehaviour("any", asBEHAVE_ADDREF, "void f()", asFUNCTION(ScriptAny_AddRef_Generic), asCALL_GENERIC); assert( r >= 0 );
r = engine->RegisterObjectBehaviour("any", asBEHAVE_RELEASE, "void f()", asFUNCTION(ScriptAny_Release_Generic), asCALL_GENERIC); assert( r >= 0 );
r = engine->RegisterObjectMethod("any", "any &opAssign(any&in)", asFUNCTION(ScriptAnyAssignment_Generic), asCALL_GENERIC); assert( r >= 0 );
r = engine->RegisterObjectMethod("any", "void store(?&in)", asFUNCTION(ScriptAny_Store_Generic), asCALL_GENERIC); assert( r >= 0 );
r = engine->RegisterObjectMethod("any", "void store(const int64&in)", asFUNCTION(ScriptAny_StoreInt_Generic), asCALL_GENERIC); assert( r >= 0 );
r = engine->RegisterObjectMethod("any", "void store(const double&in)", asFUNCTION(ScriptAny_StoreFlt_Generic), asCALL_GENERIC); assert( r >= 0 );
r = engine->RegisterObjectMethod("any", "bool retrieve(?&out) const", asFUNCTION(ScriptAny_Retrieve_Generic), asCALL_GENERIC); assert( r >= 0 );
r = engine->RegisterObjectMethod("any", "bool retrieve(int64&out) const", asFUNCTION(ScriptAny_RetrieveInt_Generic), asCALL_GENERIC); assert( r >= 0 );
r = engine->RegisterObjectMethod("any", "bool retrieve(double&out) const", asFUNCTION(ScriptAny_RetrieveFlt_Generic), asCALL_GENERIC); assert( r >= 0 );
// Register GC behaviours
r = engine->RegisterObjectBehaviour("any", asBEHAVE_GETREFCOUNT, "int f()", asFUNCTION(ScriptAny_GetRefCount_Generic), asCALL_GENERIC); assert( r >= 0 );
r = engine->RegisterObjectBehaviour("any", asBEHAVE_SETGCFLAG, "void f()", asFUNCTION(ScriptAny_SetFlag_Generic), asCALL_GENERIC); assert( r >= 0 );
r = engine->RegisterObjectBehaviour("any", asBEHAVE_GETGCFLAG, "bool f()", asFUNCTION(ScriptAny_GetFlag_Generic), asCALL_GENERIC); assert( r >= 0 );
r = engine->RegisterObjectBehaviour("any", asBEHAVE_ENUMREFS, "void f(int&in)", asFUNCTION(ScriptAny_EnumReferences_Generic), asCALL_GENERIC); assert( r >= 0 );
r = engine->RegisterObjectBehaviour("any", asBEHAVE_RELEASEREFS, "void f(int&in)", asFUNCTION(ScriptAny_ReleaseAllHandles_Generic), asCALL_GENERIC); assert( r >= 0 );
}
CScriptAny &CScriptAny::operator=(const CScriptAny &other)
{
// Hold on to the object type reference so it isn't destroyed too early
if( (other.value.typeId & asTYPEID_MASK_OBJECT) )
{
asITypeInfo *ti = engine->GetTypeInfoById(other.value.typeId);
if( ti )
ti->AddRef();
}
FreeObject();
value.typeId = other.value.typeId;
if( value.typeId & asTYPEID_OBJHANDLE )
{
// For handles, copy the pointer and increment the reference count
value.valueObj = other.value.valueObj;
engine->AddRefScriptObject(value.valueObj, engine->GetTypeInfoById(value.typeId));
}
else if( value.typeId & asTYPEID_MASK_OBJECT )
{
// Create a copy of the object
value.valueObj = engine->CreateScriptObjectCopy(other.value.valueObj, engine->GetTypeInfoById(value.typeId));
}
else
{
// Primitives can be copied directly
value.valueInt = other.value.valueInt;
}
return *this;
}
int CScriptAny::CopyFrom(const CScriptAny *other)
{
if( other == 0 ) return asINVALID_ARG;
*this = *(CScriptAny*)other;
return 0;
}
CScriptAny::CScriptAny(asIScriptEngine *engine)
{
this->engine = engine;
refCount = 1;
gcFlag = false;
value.typeId = 0;
value.valueInt = 0;
// Notify the garbage collector of this object
engine->NotifyGarbageCollectorOfNewObject(this, engine->GetTypeInfoByName("any"));
}
CScriptAny::CScriptAny(void *ref, int refTypeId, asIScriptEngine *engine)
{
this->engine = engine;
refCount = 1;
gcFlag = false;
value.typeId = 0;
value.valueInt = 0;
// Notify the garbage collector of this object
engine->NotifyGarbageCollectorOfNewObject(this, engine->GetTypeInfoByName("any"));
Store(ref, refTypeId);
}
CScriptAny::~CScriptAny()
{
FreeObject();
}
void CScriptAny::Store(void *ref, int refTypeId)
{
// This method is not expected to be used for primitive types, except for bool, int64, or double
assert( refTypeId > asTYPEID_DOUBLE || refTypeId == asTYPEID_VOID || refTypeId == asTYPEID_BOOL || refTypeId == asTYPEID_INT64 || refTypeId == asTYPEID_DOUBLE );
// Hold on to the object type reference so it isn't destroyed too early
if( (refTypeId & asTYPEID_MASK_OBJECT) )
{
asITypeInfo *ti = engine->GetTypeInfoById(refTypeId);
if( ti )
ti->AddRef();
}
FreeObject();
value.typeId = refTypeId;
if( value.typeId & asTYPEID_OBJHANDLE )
{
// We're receiving a reference to the handle, so we need to dereference it
value.valueObj = *(void**)ref;
engine->AddRefScriptObject(value.valueObj, engine->GetTypeInfoById(value.typeId));
}
else if( value.typeId & asTYPEID_MASK_OBJECT )
{
// Create a copy of the object
value.valueObj = engine->CreateScriptObjectCopy(ref, engine->GetTypeInfoById(value.typeId));
}
else
{
// Primitives can be copied directly
value.valueInt = 0;
// Copy the primitive value
// We receive a pointer to the value.
int size = engine->GetSizeOfPrimitiveType(value.typeId);
memcpy(&value.valueInt, ref, size);
}
}
void CScriptAny::Store(double &ref)
{
Store(&ref, asTYPEID_DOUBLE);
}
void CScriptAny::Store(asINT64 &ref)
{
Store(&ref, asTYPEID_INT64);
}
bool CScriptAny::Retrieve(void *ref, int refTypeId) const
{
// This method is not expected to be used for primitive types, except for bool, int64, or double
assert( refTypeId > asTYPEID_DOUBLE || refTypeId == asTYPEID_BOOL || refTypeId == asTYPEID_INT64 || refTypeId == asTYPEID_DOUBLE );
if( refTypeId & asTYPEID_OBJHANDLE )
{
// Is the handle type compatible with the stored value?
// A handle can be retrieved if the stored type is a handle of same or compatible type
// or if the stored type is an object that implements the interface that the handle refer to.
if( (value.typeId & asTYPEID_MASK_OBJECT) )
{
// Don't allow the retrieval if the stored handle is to a const object but not the wanted handle
if( (value.typeId & asTYPEID_HANDLETOCONST) && !(refTypeId & asTYPEID_HANDLETOCONST) )
return false;
// RefCastObject will increment the refCount of the returned pointer if successful
engine->RefCastObject(value.valueObj, engine->GetTypeInfoById(value.typeId), engine->GetTypeInfoById(refTypeId), reinterpret_cast<void**>(ref));
if( *(asPWORD*)ref == 0 )
return false;
return true;
}
}
else if( refTypeId & asTYPEID_MASK_OBJECT )
{
// Is the object type compatible with the stored value?
// Copy the object into the given reference
if( value.typeId == refTypeId )
{
engine->AssignScriptObject(ref, value.valueObj, engine->GetTypeInfoById(value.typeId));
return true;
}
}
else
{
// Is the primitive type compatible with the stored value?
if( value.typeId == refTypeId )
{
int size = engine->GetSizeOfPrimitiveType(refTypeId);
memcpy(ref, &value.valueInt, size);
return true;
}
// We know all numbers are stored as either int64 or double, since we register overloaded functions for those
if( value.typeId == asTYPEID_INT64 && refTypeId == asTYPEID_DOUBLE )
{
*(double*)ref = double(value.valueInt);
return true;
}
else if( value.typeId == asTYPEID_DOUBLE && refTypeId == asTYPEID_INT64 )
{
*(asINT64*)ref = asINT64(value.valueFlt);
return true;
}
}
return false;
}
bool CScriptAny::Retrieve(asINT64 &outValue) const
{
return Retrieve(&outValue, asTYPEID_INT64);
}
bool CScriptAny::Retrieve(double &outValue) const
{
return Retrieve(&outValue, asTYPEID_DOUBLE);
}
int CScriptAny::GetTypeId() const
{
return value.typeId;
}
void CScriptAny::FreeObject()
{
// If it is a handle or a ref counted object, call release
if( value.typeId & asTYPEID_MASK_OBJECT )
{
// Let the engine release the object
asITypeInfo *ti = engine->GetTypeInfoById(value.typeId);
engine->ReleaseScriptObject(value.valueObj, ti);
// Release the object type info
if( ti )
ti->Release();
value.valueObj = 0;
value.typeId = 0;
}
// For primitives, there's nothing to do
}
void CScriptAny::EnumReferences(asIScriptEngine *inEngine)
{
// If we're holding a reference, we'll notify the garbage collector of it
if (value.valueObj && (value.typeId & asTYPEID_MASK_OBJECT))
{
asITypeInfo *subType = engine->GetTypeInfoById(value.typeId);
if ((subType->GetFlags() & asOBJ_REF))
{
inEngine->GCEnumCallback(value.valueObj);
}
else if ((subType->GetFlags() & asOBJ_VALUE) && (subType->GetFlags() & asOBJ_GC))
{
// For value types we need to forward the enum callback
// to the object so it can decide what to do
engine->ForwardGCEnumReferences(value.valueObj, subType);
}
// The object type itself is also garbage collected
asITypeInfo *ti = inEngine->GetTypeInfoById(value.typeId);
if (ti)
inEngine->GCEnumCallback(ti);
}
}
void CScriptAny::ReleaseAllHandles(asIScriptEngine * /*engine*/)
{
FreeObject();
}
int CScriptAny::AddRef() const
{
// Increase counter and clear flag set by GC
gcFlag = false;
return asAtomicInc(refCount);
}
int CScriptAny::Release() const
{
// Decrease the ref counter
gcFlag = false;
if( asAtomicDec(refCount) == 0 )
{
// Delete this object as no more references to it exists
delete this;
return 0;
}
return refCount;
}
int CScriptAny::GetRefCount()
{
return refCount;
}
void CScriptAny::SetFlag()
{
gcFlag = true;
}
bool CScriptAny::GetFlag()
{
return gcFlag;
}
END_AS_NAMESPACE

View File

@@ -0,0 +1,76 @@
#ifndef SCRIPTANY_H
#define SCRIPTANY_H
#ifndef ANGELSCRIPT_H
// Avoid having to inform include path if header is already include before
#include <angelscript.h>
#endif
BEGIN_AS_NAMESPACE
class CScriptAny
{
public:
// Constructors
CScriptAny(asIScriptEngine *engine);
CScriptAny(void *ref, int refTypeId, asIScriptEngine *engine);
// Memory management
int AddRef() const;
int Release() const;
// Copy the stored value from another any object
CScriptAny &operator=(const CScriptAny&);
int CopyFrom(const CScriptAny *other);
// Store the value, either as variable type, integer number, or real number
void Store(void *ref, int refTypeId);
void Store(asINT64 &value);
void Store(double &value);
// Retrieve the stored value, either as variable type, integer number, or real number
bool Retrieve(void *ref, int refTypeId) const;
bool Retrieve(asINT64 &value) const;
bool Retrieve(double &value) const;
// Get the type id of the stored value
int GetTypeId() const;
// GC methods
int GetRefCount();
void SetFlag();
bool GetFlag();
void EnumReferences(asIScriptEngine *engine);
void ReleaseAllHandles(asIScriptEngine *engine);
protected:
virtual ~CScriptAny();
void FreeObject();
mutable int refCount;
mutable bool gcFlag;
asIScriptEngine *engine;
// The structure for holding the values
struct valueStruct
{
union
{
asINT64 valueInt;
double valueFlt;
void *valueObj;
};
int typeId;
};
valueStruct value;
};
void RegisterScriptAny(asIScriptEngine *engine);
void RegisterScriptAny_Native(asIScriptEngine *engine);
void RegisterScriptAny_Generic(asIScriptEngine *engine);
END_AS_NAMESPACE
#endif

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,142 @@
#ifndef SCRIPTARRAY_H
#define SCRIPTARRAY_H
#ifndef ANGELSCRIPT_H
// Avoid having to inform include path if header is already include before
#include <angelscript.h>
#endif
// Sometimes it may be desired to use the same method names as used by C++ STL.
// This may for example reduce time when converting code from script to C++ or
// back.
//
// 0 = off
// 1 = on
#ifndef AS_USE_STLNAMES
#define AS_USE_STLNAMES 0
#endif
// Some prefer to use property accessors to get/set the length of the array
// This option registers the accessors instead of the method length()
#ifndef AS_USE_ACCESSORS
#define AS_USE_ACCESSORS 0
#endif
BEGIN_AS_NAMESPACE
struct SArrayBuffer;
struct SArrayCache;
class CScriptArray
{
public:
// Set the memory functions that should be used by all CScriptArrays
static void SetMemoryFunctions(asALLOCFUNC_t allocFunc, asFREEFUNC_t freeFunc);
// Factory functions
static CScriptArray *Create(asITypeInfo *ot);
static CScriptArray *Create(asITypeInfo *ot, asUINT length);
static CScriptArray *Create(asITypeInfo *ot, asUINT length, void *defaultValue);
static CScriptArray *Create(asITypeInfo *ot, void *listBuffer);
// Memory management
void AddRef() const;
void Release() const;
// Type information
asITypeInfo *GetArrayObjectType() const;
int GetArrayTypeId() const;
int GetElementTypeId() const;
// Get the current size
asUINT GetSize() const;
// Returns true if the array is empty
bool IsEmpty() const;
// Pre-allocates memory for elements
void Reserve(asUINT maxElements);
// Resize the array
void Resize(asUINT numElements);
// Get a pointer to an element. Returns 0 if out of bounds
void *At(asUINT index);
const void *At(asUINT index) const;
// Set value of an element.
// The value arg should be a pointer to the value that will be copied to the element.
// Remember, if the array holds handles the value parameter should be the
// address of the handle. The refCount of the object will also be incremented
void SetValue(asUINT index, void *value);
// Copy the contents of one array to another (only if the types are the same)
CScriptArray &operator=(const CScriptArray&);
// Compare two arrays
bool operator==(const CScriptArray &) const;
// Array manipulation
void InsertAt(asUINT index, void *value);
void InsertAt(asUINT index, const CScriptArray &arr);
void InsertLast(void *value);
void RemoveAt(asUINT index);
void RemoveLast();
void RemoveRange(asUINT start, asUINT count);
void SortAsc();
void SortDesc();
void SortAsc(asUINT startAt, asUINT count);
void SortDesc(asUINT startAt, asUINT count);
void Sort(asUINT startAt, asUINT count, bool asc);
void Sort(asIScriptFunction *less, asUINT startAt, asUINT count);
void Reverse();
int Find(void *value) const;
int Find(asUINT startAt, void *value) const;
int FindByRef(void *ref) const;
int FindByRef(asUINT startAt, void *ref) const;
// Return the address of internal buffer for direct manipulation of elements
void *GetBuffer();
// GC methods
int GetRefCount();
void SetFlag();
bool GetFlag();
void EnumReferences(asIScriptEngine *engine);
void ReleaseAllHandles(asIScriptEngine *engine);
protected:
mutable int refCount;
mutable bool gcFlag;
asITypeInfo *objType;
SArrayBuffer *buffer;
int elementSize;
int subTypeId;
// Constructors
CScriptArray(asITypeInfo *ot, void *initBuf); // Called from script when initialized with list
CScriptArray(asUINT length, asITypeInfo *ot);
CScriptArray(asUINT length, void *defVal, asITypeInfo *ot);
CScriptArray(const CScriptArray &other);
virtual ~CScriptArray();
bool Less(const void *a, const void *b, bool asc);
void *GetArrayItemPointer(int index);
void *GetDataPointer(void *buffer);
void Copy(void *dst, void *src);
void Precache();
bool CheckMaxSize(asUINT numElements);
void Resize(int delta, asUINT at);
void CreateBuffer(SArrayBuffer **buf, asUINT numElements);
void DeleteBuffer(SArrayBuffer *buf);
void CopyBuffer(SArrayBuffer *dst, SArrayBuffer *src);
void Construct(SArrayBuffer *buf, asUINT start, asUINT end);
void Destruct(SArrayBuffer *buf, asUINT start, asUINT end);
bool Equals(const void *a, const void *b, asIScriptContext *ctx, SArrayCache *cache) const;
};
void RegisterScriptArray(asIScriptEngine *engine, bool defaultArray);
END_AS_NAMESPACE
#endif

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,216 @@
#ifndef SCRIPTBUILDER_H
#define SCRIPTBUILDER_H
//---------------------------
// Compilation settings
//
// Set this flag to turn on/off metadata processing
// 0 = off
// 1 = on
#ifndef AS_PROCESS_METADATA
#define AS_PROCESS_METADATA 1
#endif
// TODO: Implement flags for turning on/off include directives and conditional programming
//---------------------------
// Declaration
//
#ifndef ANGELSCRIPT_H
// Avoid having to inform include path if header is already include before
#include <angelscript.h>
#endif
#if defined(_MSC_VER) && _MSC_VER <= 1200
// disable the annoying warnings on MSVC 6
#pragma warning (disable:4786)
#endif
#include <string>
#include <map>
#include <set>
#include <vector>
#include <string.h> // _strcmpi
BEGIN_AS_NAMESPACE
class CScriptBuilder;
// This callback will be called for each #include directive encountered by the
// builder. The callback should call the AddSectionFromFile or AddSectionFromMemory
// to add the included section to the script. If the include cannot be resolved
// then the function should return a negative value to abort the compilation.
typedef int (*INCLUDECALLBACK_t)(const char *include, const char *from, CScriptBuilder *builder, void *userParam);
// This callback will be called for each #pragma directive encountered by the builder.
// The application can interpret the pragmaText and decide what do to based on that.
// If the callback returns a negative value the builder will report an error and abort the compilation.
typedef int(*PRAGMACALLBACK_t)(const std::string &pragmaText, CScriptBuilder &builder, void *userParam);
// Helper class for loading and pre-processing script files to
// support include directives and metadata declarations
class CScriptBuilder
{
public:
CScriptBuilder();
// Start a new module
int StartNewModule(asIScriptEngine *engine, const char *moduleName);
// Load a script section from a file on disk
// Returns 1 if the file was included
// 0 if the file had already been included before
// <0 on error
int AddSectionFromFile(const char *filename);
// Load a script section from memory
// Returns 1 if the section was included
// 0 if a section with the same name had already been included before
// <0 on error
int AddSectionFromMemory(const char *sectionName,
const char *scriptCode,
unsigned int scriptLength = 0,
int lineOffset = 0);
// Build the added script sections
int BuildModule();
// Returns the engine
asIScriptEngine *GetEngine();
// Returns the current module
asIScriptModule *GetModule();
// Register the callback for resolving include directive
void SetIncludeCallback(INCLUDECALLBACK_t callback, void *userParam);
// Register the callback for resolving pragma directive
void SetPragmaCallback(PRAGMACALLBACK_t callback, void *userParam);
// Add a pre-processor define for conditional compilation
void DefineWord(const char *word);
// Enumerate included script sections
unsigned int GetSectionCount() const;
std::string GetSectionName(unsigned int idx) const;
#if AS_PROCESS_METADATA == 1
// Get metadata declared for classes, interfaces, and enums
std::vector<std::string> GetMetadataForType(int typeId);
// Get metadata declared for functions
std::vector<std::string> GetMetadataForFunc(asIScriptFunction *func);
// Get metadata declared for global variables
std::vector<std::string> GetMetadataForVar(int varIdx);
// Get metadata declared for class variables
std::vector<std::string> GetMetadataForTypeProperty(int typeId, int varIdx);
// Get metadata declared for class methods
std::vector<std::string> GetMetadataForTypeMethod(int typeId, asIScriptFunction *method);
#endif
protected:
void ClearAll();
int Build();
int ProcessScriptSection(const char *script, unsigned int length, const char *sectionname, int lineOffset);
int LoadScriptSection(const char *filename);
bool IncludeIfNotAlreadyIncluded(const char *filename);
int SkipStatement(int pos);
int ExcludeCode(int start);
void OverwriteCode(int start, int len);
asIScriptEngine *engine;
asIScriptModule *module;
std::string modifiedScript;
INCLUDECALLBACK_t includeCallback;
void *includeParam;
PRAGMACALLBACK_t pragmaCallback;
void *pragmaParam;
#if AS_PROCESS_METADATA == 1
int ExtractMetadata(int pos, std::vector<std::string> &outMetadata);
int ExtractDeclaration(int pos, std::string &outName, std::string &outDeclaration, int &outType);
enum METADATATYPE
{
MDT_TYPE = 1,
MDT_FUNC = 2,
MDT_VAR = 3,
MDT_VIRTPROP = 4,
MDT_FUNC_OR_VAR = 5
};
// Temporary structure for storing metadata and declaration
struct SMetadataDecl
{
SMetadataDecl(std::vector<std::string> m, std::string n, std::string d, int t, std::string c, std::string ns) : metadata(m), name(n), declaration(d), type(t), parentClass(c), nameSpace(ns) {}
std::vector<std::string> metadata;
std::string name;
std::string declaration;
int type;
std::string parentClass;
std::string nameSpace;
};
std::vector<SMetadataDecl> foundDeclarations;
std::string currentClass;
std::string currentNamespace;
// Storage of metadata for global declarations
std::map<int, std::vector<std::string> > typeMetadataMap;
std::map<int, std::vector<std::string> > funcMetadataMap;
std::map<int, std::vector<std::string> > varMetadataMap;
// Storage of metadata for class member declarations
struct SClassMetadata
{
SClassMetadata(const std::string& aName) : className(aName) {}
std::string className;
std::map<int, std::vector<std::string> > funcMetadataMap;
std::map<int, std::vector<std::string> > varMetadataMap;
};
std::map<int, SClassMetadata> classMetadataMap;
#endif
#ifdef _WIN32
// On Windows the filenames are case insensitive so the comparisons to
// avoid duplicate includes must also be case insensitive. True case insensitive
// is not easy as it must be language aware, but a simple implementation such
// as strcmpi should suffice in almost all cases.
//
// ref: http://www.gotw.ca/gotw/029.htm
// ref: https://msdn.microsoft.com/en-us/library/windows/desktop/dd317761(v=vs.85).aspx
// ref: http://site.icu-project.org/
// TODO: Strings by default are treated as UTF8 encoded. If the application choses to
// use a different encoding, the comparison algorithm should be adjusted as well
struct ci_less
{
bool operator()(const std::string &a, const std::string &b) const
{
return _stricmp(a.c_str(), b.c_str()) < 0;
}
};
std::set<std::string, ci_less> includedScripts;
#else
std::set<std::string> includedScripts;
#endif
std::set<std::string> definedWords;
};
END_AS_NAMESPACE
#endif

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,240 @@
#ifndef SCRIPTDICTIONARY_H
#define SCRIPTDICTIONARY_H
// The dictionary class relies on the script string object, thus the script
// string type must be registered with the engine before registering the
// dictionary type
#ifndef ANGELSCRIPT_H
// Avoid having to inform include path if header is already include before
#include <angelscript.h>
#endif
// By default the CScriptDictionary use the std::string for the keys.
// If the application uses a custom string type, then this typedef
// can be changed accordingly.
#include <string>
typedef std::string dictKey_t;
// Forward declare CScriptDictValue so we can typedef the internal map type
BEGIN_AS_NAMESPACE
class CScriptDictValue;
END_AS_NAMESPACE
// C++11 introduced the std::unordered_map which is a hash map which is
// is generally more performatic for lookups than the std::map which is a
// binary tree.
// TODO: memory: The map allocator should use the asAllocMem and asFreeMem
#if AS_CAN_USE_CPP11
#include <unordered_map>
typedef std::unordered_map<dictKey_t, AS_NAMESPACE_QUALIFIER CScriptDictValue> dictMap_t;
#else
#include <map>
typedef std::map<dictKey_t, AS_NAMESPACE_QUALIFIER CScriptDictValue> dictMap_t;
#endif
#ifdef _MSC_VER
// Turn off annoying warnings about truncated symbol names
#pragma warning (disable:4786)
#endif
// Sometimes it may be desired to use the same method names as used by C++ STL.
// This may for example reduce time when converting code from script to C++ or
// back.
//
// 0 = off
// 1 = on
#ifndef AS_USE_STLNAMES
#define AS_USE_STLNAMES 0
#endif
BEGIN_AS_NAMESPACE
class CScriptArray;
class CScriptDictionary;
class CScriptDictValue
{
public:
// This class must not be declared as local variable in C++, because it needs
// to receive the script engine pointer in all operations. The engine pointer
// is not kept as member in order to keep the size down
CScriptDictValue();
CScriptDictValue(asIScriptEngine *engine, void *value, int typeId);
// Destructor must not be called without first calling FreeValue, otherwise a memory leak will occur
~CScriptDictValue();
// Replace the stored value
void Set(asIScriptEngine *engine, void *value, int typeId);
void Set(asIScriptEngine *engine, const asINT64 &value);
void Set(asIScriptEngine *engine, const double &value);
void Set(asIScriptEngine *engine, CScriptDictValue &value);
// Gets the stored value. Returns false if the value isn't compatible with the informed typeId
bool Get(asIScriptEngine *engine, void *value, int typeId) const;
bool Get(asIScriptEngine *engine, asINT64 &value) const;
bool Get(asIScriptEngine *engine, double &value) const;
// Returns the address of the stored value for inspection
const void *GetAddressOfValue() const;
// Returns the type id of the stored value
int GetTypeId() const;
// Free the stored value
void FreeValue(asIScriptEngine *engine);
// GC callback
void EnumReferences(asIScriptEngine *engine);
protected:
friend class CScriptDictionary;
union
{
asINT64 m_valueInt;
double m_valueFlt;
void *m_valueObj;
};
int m_typeId;
};
class CScriptDictionary
{
public:
// Factory functions
static CScriptDictionary *Create(asIScriptEngine *engine);
// Called from the script to instantiate a dictionary from an initialization list
static CScriptDictionary *Create(asBYTE *buffer);
// Reference counting
void AddRef() const;
void Release() const;
// Reassign the dictionary
CScriptDictionary &operator =(const CScriptDictionary &other);
// Sets a key/value pair
void Set(const dictKey_t &key, void *value, int typeId);
void Set(const dictKey_t &key, const asINT64 &value);
void Set(const dictKey_t &key, const double &value);
// Gets the stored value. Returns false if the value isn't compatible with the informed typeId
bool Get(const dictKey_t &key, void *value, int typeId) const;
bool Get(const dictKey_t &key, asINT64 &value) const;
bool Get(const dictKey_t &key, double &value) const;
// Index accessors. If the dictionary is not const it inserts the value if it doesn't already exist
// If the dictionary is const then a script exception is set if it doesn't exist and a null pointer is returned
CScriptDictValue *operator[](const dictKey_t &key);
const CScriptDictValue *operator[](const dictKey_t &key) const;
// Returns the type id of the stored value, or negative if it doesn't exist
int GetTypeId(const dictKey_t &key) const;
// Returns true if the key is set
bool Exists(const dictKey_t &key) const;
// Returns true if there are no key/value pairs in the dictionary
bool IsEmpty() const;
// Returns the number of key/value pairs in the dictionary
asUINT GetSize() const;
// Deletes the key
bool Delete(const dictKey_t &key);
// Deletes all keys
void DeleteAll();
// Get an array of all keys
CScriptArray *GetKeys() const;
// STL style iterator
class CIterator
{
public:
void operator++(); // Pre-increment
void operator++(int); // Post-increment
// This is needed to support C++11 range-for
CIterator &operator*();
bool operator==(const CIterator &other) const;
bool operator!=(const CIterator &other) const;
// Accessors
const dictKey_t &GetKey() const;
int GetTypeId() const;
bool GetValue(asINT64 &value) const;
bool GetValue(double &value) const;
bool GetValue(void *value, int typeId) const;
const void * GetAddressOfValue() const;
protected:
friend class CScriptDictionary;
CIterator();
CIterator(const CScriptDictionary &dict,
dictMap_t::const_iterator it);
CIterator &operator=(const CIterator &) {return *this;} // Not used
dictMap_t::const_iterator m_it;
const CScriptDictionary &m_dict;
};
CIterator begin() const;
CIterator end() const;
CIterator find(const dictKey_t &key) const;
// Garbage collections behaviours
int GetRefCount();
void SetGCFlag();
bool GetGCFlag();
void EnumReferences(asIScriptEngine *engine);
void ReleaseAllReferences(asIScriptEngine *engine);
protected:
// Since the dictionary uses the asAllocMem and asFreeMem functions to allocate memory
// the constructors are made protected so that the application cannot allocate it
// manually in a different way
CScriptDictionary(asIScriptEngine *engine);
CScriptDictionary(asBYTE *buffer);
// We don't want anyone to call the destructor directly, it should be called through the Release method
virtual ~CScriptDictionary();
// Cache the object types needed
void Init(asIScriptEngine *engine);
// Our properties
asIScriptEngine *engine;
mutable int refCount;
mutable bool gcFlag;
dictMap_t dict;
};
// This function will determine the configuration of the engine
// and use one of the two functions below to register the dictionary object
void RegisterScriptDictionary(asIScriptEngine *engine);
// Call this function to register the math functions
// using native calling conventions
void RegisterScriptDictionary_Native(asIScriptEngine *engine);
// Use this one instead if native calling conventions
// are not supported on the target platform
void RegisterScriptDictionary_Generic(asIScriptEngine *engine);
END_AS_NAMESPACE
#endif

View File

@@ -0,0 +1,660 @@
#include "scriptfile.h"
#include <new>
#include <assert.h>
#include <string>
#include <string.h>
#include <stdio.h>
#ifdef _WIN32_WCE
#include <windows.h> // For GetModuleFileName
#ifdef GetObject
#undef GetObject
#endif
#endif
using namespace std;
BEGIN_AS_NAMESPACE
CScriptFile *ScriptFile_Factory()
{
return new CScriptFile();
}
void ScriptFile_Factory_Generic(asIScriptGeneric *gen)
{
*(CScriptFile**)gen->GetAddressOfReturnLocation() = ScriptFile_Factory();
}
void ScriptFile_AddRef_Generic(asIScriptGeneric *gen)
{
CScriptFile *file = (CScriptFile*)gen->GetObject();
file->AddRef();
}
void ScriptFile_Release_Generic(asIScriptGeneric *gen)
{
CScriptFile *file = (CScriptFile*)gen->GetObject();
file->Release();
}
void ScriptFile_Open_Generic(asIScriptGeneric *gen)
{
CScriptFile *file = (CScriptFile*)gen->GetObject();
std::string *f = (std::string*)gen->GetArgAddress(0);
std::string *m = (std::string*)gen->GetArgAddress(1);
int r = file->Open(*f, *m);
gen->SetReturnDWord(r);
}
void ScriptFile_Close_Generic(asIScriptGeneric *gen)
{
CScriptFile *file = (CScriptFile*)gen->GetObject();
int r = file->Close();
gen->SetReturnDWord(r);
}
void ScriptFile_GetSize_Generic(asIScriptGeneric *gen)
{
CScriptFile *file = (CScriptFile*)gen->GetObject();
int r = file->GetSize();
gen->SetReturnDWord(r);
}
void ScriptFile_ReadString_Generic(asIScriptGeneric *gen)
{
CScriptFile *file = (CScriptFile*)gen->GetObject();
int len = gen->GetArgDWord(0);
string str = file->ReadString(len);
gen->SetReturnObject(&str);
}
void ScriptFile_ReadLine_Generic(asIScriptGeneric *gen)
{
CScriptFile *file = (CScriptFile*)gen->GetObject();
std::string str = file->ReadLine();
gen->SetReturnObject(&str);
}
void ScriptFile_ReadInt_Generic(asIScriptGeneric *gen)
{
CScriptFile *file = (CScriptFile*)gen->GetObject();
asUINT bytes = *(asUINT*)gen->GetAddressOfArg(0);
*(asINT64*)gen->GetAddressOfReturnLocation() = file->ReadInt(bytes);
}
void ScriptFile_ReadUInt_Generic(asIScriptGeneric *gen)
{
CScriptFile *file = (CScriptFile*)gen->GetObject();
asUINT bytes = *(asUINT*)gen->GetAddressOfArg(0);
*(asQWORD*)gen->GetAddressOfReturnLocation() = file->ReadUInt(bytes);
}
void ScriptFile_ReadFloat_Generic(asIScriptGeneric *gen)
{
CScriptFile *file = (CScriptFile*)gen->GetObject();
*(float*)gen->GetAddressOfReturnLocation() = file->ReadFloat();
}
void ScriptFile_ReadDouble_Generic(asIScriptGeneric *gen)
{
CScriptFile *file = (CScriptFile*)gen->GetObject();
*(double*)gen->GetAddressOfReturnLocation() = file->ReadDouble();
}
void ScriptFile_WriteString_Generic(asIScriptGeneric *gen)
{
CScriptFile *file = (CScriptFile*)gen->GetObject();
std::string *str = (std::string*)gen->GetArgAddress(0);
gen->SetReturnDWord(file->WriteString(*str));
}
void ScriptFile_WriteInt_Generic(asIScriptGeneric *gen)
{
CScriptFile *file = (CScriptFile*)gen->GetObject();
asINT64 val = *(asINT64*)gen->GetAddressOfArg(0);
asUINT bytes = *(asUINT*)gen->GetAddressOfArg(1);
*(int*)gen->GetAddressOfReturnLocation() = file->WriteInt(val, bytes);
}
void ScriptFile_WriteUInt_Generic(asIScriptGeneric *gen)
{
CScriptFile *file = (CScriptFile*)gen->GetObject();
asQWORD val = *(asQWORD*)gen->GetAddressOfArg(0);
asUINT bytes = *(asUINT*)gen->GetAddressOfArg(1);
*(int*)gen->GetAddressOfReturnLocation() = file->WriteUInt(val, bytes);
}
void ScriptFile_WriteFloat_Generic(asIScriptGeneric *gen)
{
CScriptFile *file = (CScriptFile*)gen->GetObject();
float val = *(float*)gen->GetAddressOfArg(0);
*(int*)gen->GetAddressOfReturnLocation() = file->WriteFloat(val);
}
void ScriptFile_WriteDouble_Generic(asIScriptGeneric *gen)
{
CScriptFile *file = (CScriptFile*)gen->GetObject();
double val = *(double*)gen->GetAddressOfArg(0);
*(int*)gen->GetAddressOfReturnLocation() = file->WriteDouble(val);
}
void ScriptFile_IsEOF_Generic(asIScriptGeneric *gen)
{
CScriptFile *file = (CScriptFile*)gen->GetObject();
bool r = file->IsEOF();
gen->SetReturnByte(r);
}
void ScriptFile_GetPos_Generic(asIScriptGeneric *gen)
{
CScriptFile *file = (CScriptFile*)gen->GetObject();
gen->SetReturnDWord(file->GetPos());
}
void ScriptFile_SetPos_Generic(asIScriptGeneric *gen)
{
CScriptFile *file = (CScriptFile*)gen->GetObject();
int pos = (int)gen->GetArgDWord(0);
gen->SetReturnDWord(file->SetPos(pos));
}
void ScriptFile_MovePos_Generic(asIScriptGeneric *gen)
{
CScriptFile *file = (CScriptFile*)gen->GetObject();
int delta = (int)gen->GetArgDWord(0);
gen->SetReturnDWord(file->MovePos(delta));
}
void RegisterScriptFile_Native(asIScriptEngine *engine)
{
int r;
r = engine->RegisterObjectType("file", 0, asOBJ_REF); assert( r >= 0 );
r = engine->RegisterObjectBehaviour("file", asBEHAVE_FACTORY, "file @f()", asFUNCTION(ScriptFile_Factory), asCALL_CDECL); assert( r >= 0 );
r = engine->RegisterObjectBehaviour("file", asBEHAVE_ADDREF, "void f()", asMETHOD(CScriptFile,AddRef), asCALL_THISCALL); assert( r >= 0 );
r = engine->RegisterObjectBehaviour("file", asBEHAVE_RELEASE, "void f()", asMETHOD(CScriptFile,Release), asCALL_THISCALL); assert( r >= 0 );
r = engine->RegisterObjectMethod("file", "int open(const string &in, const string &in)", asMETHOD(CScriptFile,Open), asCALL_THISCALL); assert( r >= 0 );
r = engine->RegisterObjectMethod("file", "int close()", asMETHOD(CScriptFile,Close), asCALL_THISCALL); assert( r >= 0 );
r = engine->RegisterObjectMethod("file", "int getSize() const", asMETHOD(CScriptFile,GetSize), asCALL_THISCALL); assert( r >= 0 );
r = engine->RegisterObjectMethod("file", "bool isEndOfFile() const", asMETHOD(CScriptFile,IsEOF), asCALL_THISCALL); assert( r >= 0 );
r = engine->RegisterObjectMethod("file", "string readString(uint)", asMETHOD(CScriptFile,ReadString), asCALL_THISCALL); assert( r >= 0 );
r = engine->RegisterObjectMethod("file", "string readLine()", asMETHOD(CScriptFile,ReadLine), asCALL_THISCALL); assert( r >= 0 );
r = engine->RegisterObjectMethod("file", "int64 readInt(uint)", asMETHOD(CScriptFile,ReadInt), asCALL_THISCALL); assert( r >= 0 );
r = engine->RegisterObjectMethod("file", "uint64 readUInt(uint)", asMETHOD(CScriptFile,ReadUInt), asCALL_THISCALL); assert( r >= 0 );
r = engine->RegisterObjectMethod("file", "float readFloat()", asMETHOD(CScriptFile,ReadFloat), asCALL_THISCALL); assert( r >= 0 );
r = engine->RegisterObjectMethod("file", "double readDouble()", asMETHOD(CScriptFile,ReadDouble), asCALL_THISCALL); assert( r >= 0 );
#if AS_WRITE_OPS == 1
r = engine->RegisterObjectMethod("file", "int writeString(const string &in)", asMETHOD(CScriptFile,WriteString), asCALL_THISCALL); assert( r >= 0 );
r = engine->RegisterObjectMethod("file", "int writeInt(int64, uint)", asMETHOD(CScriptFile,WriteInt), asCALL_THISCALL); assert( r >= 0 );
r = engine->RegisterObjectMethod("file", "int writeUInt(uint64, uint)", asMETHOD(CScriptFile,WriteUInt), asCALL_THISCALL); assert( r >= 0 );
r = engine->RegisterObjectMethod("file", "int writeFloat(float)", asMETHOD(CScriptFile,WriteFloat), asCALL_THISCALL); assert( r >= 0 );
r = engine->RegisterObjectMethod("file", "int writeDouble(double)", asMETHOD(CScriptFile,WriteDouble), asCALL_THISCALL); assert( r >= 0 );
#endif
r = engine->RegisterObjectMethod("file", "int getPos() const", asMETHOD(CScriptFile,GetPos), asCALL_THISCALL); assert( r >= 0 );
r = engine->RegisterObjectMethod("file", "int setPos(int)", asMETHOD(CScriptFile,SetPos), asCALL_THISCALL); assert( r >= 0 );
r = engine->RegisterObjectMethod("file", "int movePos(int)", asMETHOD(CScriptFile,MovePos), asCALL_THISCALL); assert( r >= 0 );
r = engine->RegisterObjectProperty("file", "bool mostSignificantByteFirst", asOFFSET(CScriptFile, mostSignificantByteFirst)); assert( r >= 0 );
}
void RegisterScriptFile_Generic(asIScriptEngine *engine)
{
int r;
r = engine->RegisterObjectType("file", 0, asOBJ_REF); assert( r >= 0 );
r = engine->RegisterObjectBehaviour("file", asBEHAVE_FACTORY, "file @f()", asFUNCTION(ScriptFile_Factory_Generic), asCALL_GENERIC); assert( r >= 0 );
r = engine->RegisterObjectBehaviour("file", asBEHAVE_ADDREF, "void f()", asFUNCTION(ScriptFile_AddRef_Generic), asCALL_GENERIC); assert( r >= 0 );
r = engine->RegisterObjectBehaviour("file", asBEHAVE_RELEASE, "void f()", asFUNCTION(ScriptFile_Release_Generic), asCALL_GENERIC); assert( r >= 0 );
r = engine->RegisterObjectMethod("file", "int open(const string &in, const string &in)", asFUNCTION(ScriptFile_Open_Generic), asCALL_GENERIC); assert( r >= 0 );
r = engine->RegisterObjectMethod("file", "int close()", asFUNCTION(ScriptFile_Close_Generic), asCALL_GENERIC); assert( r >= 0 );
r = engine->RegisterObjectMethod("file", "int getSize() const", asFUNCTION(ScriptFile_GetSize_Generic), asCALL_GENERIC); assert( r >= 0 );
r = engine->RegisterObjectMethod("file", "bool isEndOfFile() const", asFUNCTION(ScriptFile_IsEOF_Generic), asCALL_GENERIC); assert( r >= 0 );
r = engine->RegisterObjectMethod("file", "string readString(uint)", asFUNCTION(ScriptFile_ReadString_Generic), asCALL_GENERIC); assert( r >= 0 );
r = engine->RegisterObjectMethod("file", "string readLine()", asFUNCTION(ScriptFile_ReadLine_Generic), asCALL_GENERIC); assert( r >= 0 );
r = engine->RegisterObjectMethod("file", "int64 readInt(uint)", asFUNCTION(ScriptFile_ReadInt_Generic), asCALL_GENERIC); assert( r >= 0 );
r = engine->RegisterObjectMethod("file", "uint64 readUInt(uint)", asFUNCTION(ScriptFile_ReadUInt_Generic), asCALL_GENERIC); assert( r >= 0 );
r = engine->RegisterObjectMethod("file", "float readFloat()", asFUNCTION(ScriptFile_ReadFloat_Generic), asCALL_GENERIC); assert( r >= 0 );
r = engine->RegisterObjectMethod("file", "double readDouble()", asFUNCTION(ScriptFile_ReadDouble_Generic), asCALL_GENERIC); assert( r >= 0 );
#if AS_WRITE_OPS == 1
r = engine->RegisterObjectMethod("file", "int writeString(const string &in)", asFUNCTION(ScriptFile_WriteString_Generic), asCALL_GENERIC); assert( r >= 0 );
r = engine->RegisterObjectMethod("file", "int writeInt(int64, uint)", asFUNCTION(ScriptFile_WriteInt_Generic), asCALL_GENERIC); assert( r >= 0 );
r = engine->RegisterObjectMethod("file", "int writeUInt(uint64, uint)", asFUNCTION(ScriptFile_WriteUInt_Generic), asCALL_GENERIC); assert( r >= 0 );
r = engine->RegisterObjectMethod("file", "int writeFloat(float)", asFUNCTION(ScriptFile_WriteFloat_Generic), asCALL_GENERIC); assert( r >= 0 );
r = engine->RegisterObjectMethod("file", "int writeDouble(double)", asFUNCTION(ScriptFile_WriteDouble_Generic), asCALL_GENERIC); assert( r >= 0 );
#endif
r = engine->RegisterObjectMethod("file", "int getPos() const", asFUNCTION(ScriptFile_GetPos_Generic), asCALL_GENERIC); assert( r >= 0 );
r = engine->RegisterObjectMethod("file", "int setPos(int)", asFUNCTION(ScriptFile_SetPos_Generic), asCALL_GENERIC); assert( r >= 0 );
r = engine->RegisterObjectMethod("file", "int movePos(int)", asFUNCTION(ScriptFile_MovePos_Generic), asCALL_GENERIC); assert( r >= 0 );
r = engine->RegisterObjectProperty("file", "bool mostSignificantByteFirst", asOFFSET(CScriptFile, mostSignificantByteFirst)); assert( r >= 0 );
}
void RegisterScriptFile(asIScriptEngine *engine)
{
if( strstr(asGetLibraryOptions(), "AS_MAX_PORTABILITY") )
RegisterScriptFile_Generic(engine);
else
RegisterScriptFile_Native(engine);
}
CScriptFile::CScriptFile()
{
refCount = 1;
file = 0;
mostSignificantByteFirst = false;
}
CScriptFile::~CScriptFile()
{
Close();
}
void CScriptFile::AddRef() const
{
asAtomicInc(refCount);
}
void CScriptFile::Release() const
{
if( asAtomicDec(refCount) == 0 )
delete this;
}
int CScriptFile::Open(const std::string &filename, const std::string &mode)
{
// Close the previously opened file handle
if( file )
Close();
std::string myFilename = filename;
// Validate the mode
string m;
#if AS_WRITE_OPS == 1
if( mode != "r" && mode != "w" && mode != "a" )
#else
if( mode != "r" )
#endif
return -1;
else
m = mode;
#ifdef _WIN32_WCE
// no relative pathing on CE
char buf[MAX_PATH];
static TCHAR apppath[MAX_PATH] = TEXT("");
if (!apppath[0])
{
GetModuleFileName(NULL, apppath, MAX_PATH);
int appLen = _tcslen(apppath);
while (appLen > 1)
{
if (apppath[appLen-1] == TEXT('\\'))
break;
appLen--;
}
// Terminate the string after the trailing backslash
apppath[appLen] = TEXT('\0');
}
#ifdef _UNICODE
wcstombs(buf, apppath, wcslen(apppath)+1);
#else
memcpy(buf, apppath, strlen(apppath));
#endif
myFilename = buf + myFilename;
#endif
// By default windows translates "\r\n" to "\n", but we want to read the file as-is.
m += "b";
// Open the file
#if _MSC_VER >= 1400 && !defined(__S3E__)
// MSVC 8.0 / 2005 introduced new functions
// Marmalade doesn't use these, even though it uses the MSVC compiler
fopen_s(&file, myFilename.c_str(), m.c_str());
#else
file = fopen(myFilename.c_str(), m.c_str());
#endif
if( file == 0 )
return -1;
return 0;
}
int CScriptFile::Close()
{
if( file == 0 )
return -1;
fclose(file);
file = 0;
return 0;
}
int CScriptFile::GetSize() const
{
if( file == 0 )
return -1;
int pos = ftell(file);
fseek(file, 0, SEEK_END);
int size = ftell(file);
fseek(file, pos, SEEK_SET);
return size;
}
int CScriptFile::GetPos() const
{
if( file == 0 )
return -1;
return ftell(file);
}
int CScriptFile::SetPos(int pos)
{
if( file == 0 )
return -1;
int r = fseek(file, pos, SEEK_SET);
// Return -1 on error
return r ? -1 : 0;
}
int CScriptFile::MovePos(int delta)
{
if( file == 0 )
return -1;
int r = fseek(file, delta, SEEK_CUR);
// Return -1 on error
return r ? -1 : 0;
}
string CScriptFile::ReadString(unsigned int length)
{
if( file == 0 )
return "";
// Read the string
string str;
str.resize(length);
int size = (int)fread(&str[0], 1, length, file);
str.resize(size);
return str;
}
string CScriptFile::ReadLine()
{
if( file == 0 )
return "";
// Read until the first new-line character
string str;
char buf[256];
do
{
// Get the current position so we can determine how many characters were read
int start = ftell(file);
// Set the last byte to something different that 0, so that we can check if the buffer was filled up
buf[255] = 1;
// Read the line (or first 255 characters, which ever comes first)
char *r = fgets(buf, 256, file);
if( r == 0 ) break;
// Get the position after the read
int end = ftell(file);
// Add the read characters to the output buffer
str.append(buf, end-start);
}
while( !feof(file) && buf[255] == 0 && buf[254] != '\n' );
return str;
}
asINT64 CScriptFile::ReadInt(asUINT bytes)
{
if( file == 0 )
return 0;
if( bytes > 8 ) bytes = 8;
if( bytes == 0 ) return 0;
unsigned char buf[8];
size_t r = fread(buf, bytes, 1, file);
if( r == 0 ) return 0;
asINT64 val = 0;
if( mostSignificantByteFirst )
{
unsigned int n = 0;
for( ; n < bytes; n++ )
val |= asQWORD(buf[n]) << ((bytes-n-1)*8);
// Check the most significant byte to determine if the rest
// of the qword must be filled to give a negative value
if( buf[0] & 0x80 )
for( ; n < 8; n++ )
val |= asQWORD(0xFF) << (n*8);
}
else
{
unsigned int n = 0;
for( ; n < bytes; n++ )
val |= asQWORD(buf[n]) << (n*8);
// Check the most significant byte to determine if the rest
// of the qword must be filled to give a negative value
if( buf[bytes-1] & 0x80 )
for( ; n < 8; n++ )
val |= asQWORD(0xFF) << (n*8);
}
return val;
}
asQWORD CScriptFile::ReadUInt(asUINT bytes)
{
if( file == 0 )
return 0;
if( bytes > 8 ) bytes = 8;
if( bytes == 0 ) return 0;
unsigned char buf[8];
size_t r = fread(buf, bytes, 1, file);
if( r == 0 ) return 0;
asQWORD val = 0;
if( mostSignificantByteFirst )
{
unsigned int n = 0;
for( ; n < bytes; n++ )
val |= asQWORD(buf[n]) << ((bytes-n-1)*8);
}
else
{
unsigned int n = 0;
for( ; n < bytes; n++ )
val |= asQWORD(buf[n]) << (n*8);
}
return val;
}
float CScriptFile::ReadFloat()
{
if( file == 0 )
return 0;
unsigned char buf[4];
size_t r = fread(buf, 4, 1, file);
if( r == 0 ) return 0;
asUINT val = 0;
if( mostSignificantByteFirst )
{
unsigned int n = 0;
for( ; n < 4; n++ )
val |= asUINT(buf[n]) << ((3-n)*8);
}
else
{
unsigned int n = 0;
for( ; n < 4; n++ )
val |= asUINT(buf[n]) << (n*8);
}
return *reinterpret_cast<float*>(&val);
}
double CScriptFile::ReadDouble()
{
if( file == 0 )
return 0;
unsigned char buf[8];
size_t r = fread(buf, 8, 1, file);
if( r == 0 ) return 0;
asQWORD val = 0;
if( mostSignificantByteFirst )
{
unsigned int n = 0;
for( ; n < 8; n++ )
val |= asQWORD(buf[n]) << ((7-n)*8);
}
else
{
unsigned int n = 0;
for( ; n < 8; n++ )
val |= asQWORD(buf[n]) << (n*8);
}
return *reinterpret_cast<double*>(&val);
}
bool CScriptFile::IsEOF() const
{
if( file == 0 )
return true;
return feof(file) ? true : false;
}
#if AS_WRITE_OPS == 1
int CScriptFile::WriteString(const std::string &str)
{
if( file == 0 )
return -1;
// Write the entire string
size_t r = fwrite(&str[0], 1, str.length(), file);
return int(r);
}
int CScriptFile::WriteInt(asINT64 val, asUINT bytes)
{
if( file == 0 )
return 0;
unsigned char buf[8];
if( mostSignificantByteFirst )
{
for( unsigned int n = 0; n < bytes; n++ )
buf[n] = (val >> ((bytes-n-1)*8)) & 0xFF;
}
else
{
for( unsigned int n = 0; n < bytes; n++ )
buf[n] = (val >> (n*8)) & 0xFF;
}
size_t r = fwrite(&buf, bytes, 1, file);
return int(r);
}
int CScriptFile::WriteUInt(asQWORD val, asUINT bytes)
{
if( file == 0 )
return 0;
unsigned char buf[8];
if( mostSignificantByteFirst )
{
for( unsigned int n = 0; n < bytes; n++ )
buf[n] = (val >> ((bytes-n-1)*8)) & 0xFF;
}
else
{
for( unsigned int n = 0; n < bytes; n++ )
buf[n] = (val >> (n*8)) & 0xFF;
}
size_t r = fwrite(&buf, bytes, 1, file);
return int(r);
}
int CScriptFile::WriteFloat(float f)
{
if( file == 0 )
return 0;
unsigned char buf[4];
asUINT val = *reinterpret_cast<asUINT*>(&f);
if( mostSignificantByteFirst )
{
for( unsigned int n = 0; n < 4; n++ )
buf[n] = (val >> ((3-n)*4)) & 0xFF;
}
else
{
for( unsigned int n = 0; n < 4; n++ )
buf[n] = (val >> (n*8)) & 0xFF;
}
size_t r = fwrite(&buf, 4, 1, file);
return int(r);
}
int CScriptFile::WriteDouble(double d)
{
if( file == 0 )
return 0;
unsigned char buf[8];
asQWORD val = *reinterpret_cast<asQWORD*>(&d);
if( mostSignificantByteFirst )
{
for( unsigned int n = 0; n < 8; n++ )
buf[n] = (val >> ((7-n)*8)) & 0xFF;
}
else
{
for( unsigned int n = 0; n < 8; n++ )
buf[n] = (val >> (n*8)) & 0xFF;
}
size_t r = fwrite(&buf, 8, 1, file);
return int(r);
}
#endif
END_AS_NAMESPACE

View File

@@ -0,0 +1,101 @@
//
// CScriptFile
//
// This class encapsulates a FILE pointer in a reference counted class for
// use within AngelScript.
//
#ifndef SCRIPTFILE_H
#define SCRIPTFILE_H
//---------------------------
// Compilation settings
//
// Set this flag to turn on/off write support
// 0 = off
// 1 = on
#ifndef AS_WRITE_OPS
#define AS_WRITE_OPS 1
#endif
//---------------------------
// Declaration
//
#ifndef ANGELSCRIPT_H
// Avoid having to inform include path if header is already include before
#include <angelscript.h>
#endif
#include <string>
#include <stdio.h>
BEGIN_AS_NAMESPACE
class CScriptFile
{
public:
CScriptFile();
void AddRef() const;
void Release() const;
// TODO: Implement the "r+", "w+" and "a+" modes
// mode = "r" -> open the file for reading
// "w" -> open the file for writing (overwrites existing file)
// "a" -> open the file for appending
int Open(const std::string &filename, const std::string &mode);
int Close();
int GetSize() const;
bool IsEOF() const;
// Reading
std::string ReadString(unsigned int length);
std::string ReadLine();
asINT64 ReadInt(asUINT bytes);
asQWORD ReadUInt(asUINT bytes);
float ReadFloat();
double ReadDouble();
// Writing
int WriteString(const std::string &str);
int WriteInt(asINT64 v, asUINT bytes);
int WriteUInt(asQWORD v, asUINT bytes);
int WriteFloat(float v);
int WriteDouble(double v);
// Cursor
int GetPos() const;
int SetPos(int pos);
int MovePos(int delta);
// Big-endian = most significant byte first
bool mostSignificantByteFirst;
protected:
~CScriptFile();
mutable int refCount;
FILE *file;
};
// This function will determine the configuration of the engine
// and use one of the two functions below to register the file type
void RegisterScriptFile(asIScriptEngine *engine);
// Call this function to register the file type
// using native calling conventions
void RegisterScriptFile_Native(asIScriptEngine *engine);
// Use this one instead if native calling conventions
// are not supported on the target platform
void RegisterScriptFile_Generic(asIScriptEngine *engine);
END_AS_NAMESPACE
#endif

View File

@@ -0,0 +1,629 @@
#include "scriptfilesystem.h"
#include "../autowrapper/aswrappedcall.h"
#if defined(_WIN32)
#include <direct.h> // _getcwd
#include <Windows.h> // FindFirstFile, GetFileAttributes
#undef DeleteFile
#undef CopyFile
#else
#include <unistd.h> // getcwd
#include <dirent.h> // opendir, readdir, closedir
#include <sys/stat.h> // stat
#endif
#include <assert.h> // assert
using namespace std;
BEGIN_AS_NAMESPACE
// TODO: The file system should have a way to allow the application to define in
// which sub directories it is allowed to make changes and/or read
CScriptFileSystem *ScriptFileSystem_Factory()
{
return new CScriptFileSystem();
}
void RegisterScriptFileSystem_Native(asIScriptEngine *engine)
{
int r;
assert( engine->GetTypeInfoByName("string") );
assert( engine->GetTypeInfoByDecl("array<string>") );
assert( engine->GetTypeInfoByName("datetime") );
r = engine->RegisterObjectType("filesystem", 0, asOBJ_REF); assert( r >= 0 );
r = engine->RegisterObjectBehaviour("filesystem", asBEHAVE_FACTORY, "filesystem @f()", asFUNCTION(ScriptFileSystem_Factory), asCALL_CDECL); assert( r >= 0 );
r = engine->RegisterObjectBehaviour("filesystem", asBEHAVE_ADDREF, "void f()", asMETHOD(CScriptFileSystem,AddRef), asCALL_THISCALL); assert( r >= 0 );
r = engine->RegisterObjectBehaviour("filesystem", asBEHAVE_RELEASE, "void f()", asMETHOD(CScriptFileSystem,Release), asCALL_THISCALL); assert( r >= 0 );
r = engine->RegisterObjectMethod("filesystem", "bool changeCurrentPath(const string &in)", asMETHOD(CScriptFileSystem, ChangeCurrentPath), asCALL_THISCALL); assert( r >= 0 );
r = engine->RegisterObjectMethod("filesystem", "string getCurrentPath() const", asMETHOD(CScriptFileSystem, GetCurrentPath), asCALL_THISCALL); assert( r >= 0 );
r = engine->RegisterObjectMethod("filesystem", "array<string> @getDirs() const", asMETHOD(CScriptFileSystem, GetDirs), asCALL_THISCALL); assert( r >= 0 );
r = engine->RegisterObjectMethod("filesystem", "array<string> @getFiles() const", asMETHOD(CScriptFileSystem, GetFiles), asCALL_THISCALL); assert( r >= 0 );
r = engine->RegisterObjectMethod("filesystem", "bool isDir(const string &in) const", asMETHOD(CScriptFileSystem, IsDir), asCALL_THISCALL); assert( r >= 0 );
r = engine->RegisterObjectMethod("filesystem", "bool isLink(const string &in) const", asMETHOD(CScriptFileSystem, IsLink), asCALL_THISCALL); assert(r >= 0);
r = engine->RegisterObjectMethod("filesystem", "int64 getSize(const string &in) const", asMETHOD(CScriptFileSystem, GetSize), asCALL_THISCALL); assert(r >= 0);
r = engine->RegisterObjectMethod("filesystem", "int makeDir(const string &in)", asMETHOD(CScriptFileSystem, MakeDir), asCALL_THISCALL); assert(r >= 0);
r = engine->RegisterObjectMethod("filesystem", "int removeDir(const string &in)", asMETHOD(CScriptFileSystem, RemoveDir), asCALL_THISCALL); assert(r >= 0);
r = engine->RegisterObjectMethod("filesystem", "int deleteFile(const string &in)", asMETHOD(CScriptFileSystem, DeleteFile), asCALL_THISCALL); assert(r >= 0);
r = engine->RegisterObjectMethod("filesystem", "int copyFile(const string &in, const string &in)", asMETHOD(CScriptFileSystem, CopyFile), asCALL_THISCALL); assert(r >= 0);
r = engine->RegisterObjectMethod("filesystem", "int move(const string &in, const string &in)", asMETHOD(CScriptFileSystem, Move), asCALL_THISCALL); assert(r >= 0);
r = engine->RegisterObjectMethod("filesystem", "datetime getCreateDateTime(const string &in) const", asMETHOD(CScriptFileSystem, GetCreateDateTime), asCALL_THISCALL); assert(r >= 0);
r = engine->RegisterObjectMethod("filesystem", "datetime getModifyDateTime(const string &in) const", asMETHOD(CScriptFileSystem, GetModifyDateTime), asCALL_THISCALL); assert(r >= 0);
}
void RegisterScriptFileSystem_Generic(asIScriptEngine *engine)
{
int r;
assert( engine->GetTypeInfoByName("string") );
assert( engine->GetTypeInfoByDecl("array<string>") );
assert( engine->GetTypeInfoByName("datetime") );
r = engine->RegisterObjectType("filesystem", 0, asOBJ_REF); assert( r >= 0 );
r = engine->RegisterObjectBehaviour("filesystem", asBEHAVE_FACTORY, "filesystem @f()", WRAP_FN(ScriptFileSystem_Factory), asCALL_GENERIC); assert( r >= 0 );
r = engine->RegisterObjectBehaviour("filesystem", asBEHAVE_ADDREF, "void f()", WRAP_MFN(CScriptFileSystem,AddRef), asCALL_GENERIC); assert( r >= 0 );
r = engine->RegisterObjectBehaviour("filesystem", asBEHAVE_RELEASE, "void f()", WRAP_MFN(CScriptFileSystem,Release), asCALL_GENERIC); assert( r >= 0 );
r = engine->RegisterObjectMethod("filesystem", "bool changeCurrentPath(const string &in)", WRAP_MFN(CScriptFileSystem, ChangeCurrentPath), asCALL_GENERIC); assert( r >= 0 );
r = engine->RegisterObjectMethod("filesystem", "string getCurrentPath() const", WRAP_MFN(CScriptFileSystem, GetCurrentPath), asCALL_GENERIC); assert( r >= 0 );
r = engine->RegisterObjectMethod("filesystem", "array<string> @getDirs() const", WRAP_MFN(CScriptFileSystem, GetDirs), asCALL_GENERIC); assert( r >= 0 );
r = engine->RegisterObjectMethod("filesystem", "array<string> @getFiles() const", WRAP_MFN(CScriptFileSystem, GetFiles), asCALL_GENERIC); assert( r >= 0 );
r = engine->RegisterObjectMethod("filesystem", "bool isDir(const string &in) const", WRAP_MFN(CScriptFileSystem, IsDir), asCALL_GENERIC); assert( r >= 0 );
r = engine->RegisterObjectMethod("filesystem", "bool isLink(const string &in) const", WRAP_MFN(CScriptFileSystem, IsLink), asCALL_GENERIC); assert(r >= 0);
r = engine->RegisterObjectMethod("filesystem", "int64 getSize(const string &in) const", WRAP_MFN(CScriptFileSystem, GetSize), asCALL_GENERIC); assert(r >= 0);
r = engine->RegisterObjectMethod("filesystem", "int makeDir(const string &in)", WRAP_MFN(CScriptFileSystem, MakeDir), asCALL_GENERIC); assert(r >= 0);
r = engine->RegisterObjectMethod("filesystem", "int removeDir(const string &in)", WRAP_MFN(CScriptFileSystem, RemoveDir), asCALL_GENERIC); assert(r >= 0);
r = engine->RegisterObjectMethod("filesystem", "int deleteFile(const string &in)", WRAP_MFN(CScriptFileSystem, DeleteFile), asCALL_GENERIC); assert(r >= 0);
r = engine->RegisterObjectMethod("filesystem", "int copyFile(const string &in, const string &in)", WRAP_MFN(CScriptFileSystem, CopyFile), asCALL_GENERIC); assert(r >= 0);
r = engine->RegisterObjectMethod("filesystem", "int move(const string &in, const string &in)", WRAP_MFN(CScriptFileSystem, Move), asCALL_GENERIC); assert(r >= 0);
r = engine->RegisterObjectMethod("filesystem", "datetime getCreateDateTime(const string &in) const", WRAP_MFN(CScriptFileSystem, GetCreateDateTime), asCALL_GENERIC); assert(r >= 0);
r = engine->RegisterObjectMethod("filesystem", "datetime getModifyDateTime(const string &in) const", WRAP_MFN(CScriptFileSystem, GetModifyDateTime), asCALL_GENERIC); assert(r >= 0);
}
void RegisterScriptFileSystem(asIScriptEngine *engine)
{
if( strstr(asGetLibraryOptions(), "AS_MAX_PORTABILITY") )
RegisterScriptFileSystem_Generic(engine);
else
RegisterScriptFileSystem_Native(engine);
}
CScriptFileSystem::CScriptFileSystem()
{
refCount = 1;
// Gets the application's current working directory as the starting point
// TODO: Replace backslash with slash to keep a unified naming convention
char buffer[1000];
#if defined(_WIN32)
currentPath = _getcwd(buffer, 1000);
#else
currentPath = getcwd(buffer, 1000);
#endif
}
CScriptFileSystem::~CScriptFileSystem()
{
}
void CScriptFileSystem::AddRef() const
{
asAtomicInc(refCount);
}
void CScriptFileSystem::Release() const
{
if( asAtomicDec(refCount) == 0 )
delete this;
}
CScriptArray *CScriptFileSystem::GetFiles() const
{
// Obtain a pointer to the engine
asIScriptContext *ctx = asGetActiveContext();
asIScriptEngine *engine = ctx->GetEngine();
// TODO: This should only be done once
// TODO: This assumes that CScriptArray was already registered
asITypeInfo *arrayType = engine->GetTypeInfoByDecl("array<string>");
// Create the array object
CScriptArray *array = CScriptArray::Create(arrayType);
#if defined(_WIN32)
// Windows uses UTF16 so it is necessary to convert the string
wchar_t bufUTF16[10000];
string searchPattern = currentPath + "/*";
MultiByteToWideChar(CP_UTF8, 0, searchPattern.c_str(), -1, bufUTF16, 10000);
WIN32_FIND_DATAW ffd;
HANDLE hFind = FindFirstFileW(bufUTF16, &ffd);
if( INVALID_HANDLE_VALUE == hFind )
return array;
do
{
// Skip directories
if( (ffd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) )
continue;
// Convert the file name back to UTF8
char bufUTF8[10000];
WideCharToMultiByte(CP_UTF8, 0, ffd.cFileName, -1, bufUTF8, 10000, 0, 0);
// Add the file to the array
array->Resize(array->GetSize()+1);
((string*)(array->At(array->GetSize()-1)))->assign(bufUTF8);
}
while( FindNextFileW(hFind, &ffd) != 0 );
FindClose(hFind);
#else
dirent *ent = 0;
DIR *dir = opendir(currentPath.c_str());
while( (ent = readdir(dir)) != NULL )
{
const string filename = ent->d_name;
// Skip . and ..
if( filename[0] == '.' )
continue;
// Skip sub directories
const string fullname = currentPath + "/" + filename;
struct stat st;
if( stat(fullname.c_str(), &st) == -1 )
continue;
if( (st.st_mode & S_IFDIR) != 0 )
continue;
// Add the file to the array
array->Resize(array->GetSize()+1);
((string*)(array->At(array->GetSize()-1)))->assign(filename);
}
closedir(dir);
#endif
return array;
}
CScriptArray *CScriptFileSystem::GetDirs() const
{
// Obtain a pointer to the engine
asIScriptContext *ctx = asGetActiveContext();
asIScriptEngine *engine = ctx->GetEngine();
// TODO: This should only be done once
// TODO: This assumes that CScriptArray was already registered
asITypeInfo *arrayType = engine->GetTypeInfoByDecl("array<string>");
// Create the array object
CScriptArray *array = CScriptArray::Create(arrayType);
#if defined(_WIN32)
// Windows uses UTF16 so it is necessary to convert the string
wchar_t bufUTF16[10000];
string searchPattern = currentPath + "/*";
MultiByteToWideChar(CP_UTF8, 0, searchPattern.c_str(), -1, bufUTF16, 10000);
WIN32_FIND_DATAW ffd;
HANDLE hFind = FindFirstFileW(bufUTF16, &ffd);
if( INVALID_HANDLE_VALUE == hFind )
return array;
do
{
// Skip files
if( !(ffd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) )
continue;
// Convert the file name back to UTF8
char bufUTF8[10000];
WideCharToMultiByte(CP_UTF8, 0, ffd.cFileName, -1, bufUTF8, 10000, 0, 0);
if( strcmp(bufUTF8, ".") == 0 || strcmp(bufUTF8, "..") == 0 )
continue;
// Add the dir to the array
array->Resize(array->GetSize()+1);
((string*)(array->At(array->GetSize()-1)))->assign(bufUTF8);
}
while( FindNextFileW(hFind, &ffd) != 0 );
FindClose(hFind);
#else
dirent *ent = 0;
DIR *dir = opendir(currentPath.c_str());
while( (ent = readdir(dir)) != NULL )
{
const string filename = ent->d_name;
// Skip . and ..
if( filename[0] == '.' )
continue;
// Skip files
const string fullname = currentPath + "/" + filename;
struct stat st;
if( stat(fullname.c_str(), &st) == -1 )
continue;
if( (st.st_mode & S_IFDIR) == 0 )
continue;
// Add the dir to the array
array->Resize(array->GetSize()+1);
((string*)(array->At(array->GetSize()-1)))->assign(filename);
}
closedir(dir);
#endif
return array;
}
// Doesn't change anything if the new path is not valid
bool CScriptFileSystem::ChangeCurrentPath(const string &path)
{
string newPath;
if( path.find(":") != string::npos || path.find("/") == 0 || path.find("\\") == 0 )
newPath = path;
else
newPath = currentPath + "/" + path;
// TODO: Resolve internal /./ and /../
// TODO: Replace backslash with slash to keep a unified naming convention
// Remove trailing slashes from the path
while(newPath.length() && (newPath[newPath.length()-1] == '/' || newPath[newPath.length()-1] == '\\') )
newPath.resize(newPath.length()-1);
if (!IsDir(newPath))
return false;
currentPath = newPath;
return true;
}
bool CScriptFileSystem::IsDir(const string &path) const
{
string search;
if( path.find(":") != string::npos || path.find("/") == 0 || path.find("\\") == 0 )
search = path;
else
search = currentPath + "/" + path;
#if defined(_WIN32)
// Windows uses UTF16 so it is necessary to convert the string
wchar_t bufUTF16[10000];
MultiByteToWideChar(CP_UTF8, 0, search.c_str(), -1, bufUTF16, 10000);
// Check if the path exists and is a directory
DWORD attrib = GetFileAttributesW(bufUTF16);
if( attrib == INVALID_FILE_ATTRIBUTES ||
!(attrib & FILE_ATTRIBUTE_DIRECTORY) )
return false;
#else
// Check if the path exists and is a directory
struct stat st;
if( stat(search.c_str(), &st) == -1 )
return false;
if( (st.st_mode & S_IFDIR) == 0 )
return false;
#endif
return true;
}
bool CScriptFileSystem::IsLink(const string &path) const
{
string search;
if (path.find(":") != string::npos || path.find("/") == 0 || path.find("\\") == 0)
search = path;
else
search = currentPath + "/" + path;
#if defined(_WIN32)
// Windows uses UTF16 so it is necessary to convert the string
wchar_t bufUTF16[10000];
MultiByteToWideChar(CP_UTF8, 0, search.c_str(), -1, bufUTF16, 10000);
// Check if the path exists and is a link
DWORD attrib = GetFileAttributesW(bufUTF16);
if (attrib == INVALID_FILE_ATTRIBUTES ||
!(attrib & FILE_ATTRIBUTE_REPARSE_POINT))
return false;
#else
// Check if the path exists and is a link
struct stat st;
if (stat(search.c_str(), &st) == -1)
return false;
if ((st.st_mode & S_IFLNK) == 0)
return false;
#endif
return true;
}
asINT64 CScriptFileSystem::GetSize(const string &path) const
{
string search;
if (path.find(":") != string::npos || path.find("/") == 0 || path.find("\\") == 0)
search = path;
else
search = currentPath + "/" + path;
#if defined(_WIN32)
// Windows uses UTF16 so it is necessary to convert the string
wchar_t bufUTF16[10000];
MultiByteToWideChar(CP_UTF8, 0, search.c_str(), -1, bufUTF16, 10000);
// Get the size of the file
LARGE_INTEGER largeInt;
HANDLE file = CreateFileW(bufUTF16, GENERIC_READ, FILE_SHARE_READ, 0, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, 0);
BOOL success = GetFileSizeEx(file, &largeInt);
CloseHandle(file);
if( !success )
return -1;
return asINT64(largeInt.QuadPart);
#else
// Get the size of the file
struct stat st;
if (stat(search.c_str(), &st) == -1)
return -1;
return asINT64(st.st_size);
#endif
}
// TODO: Should be able to return different codes for
// - directory exists
// - path not found
// - access denied
// TODO: Should be able to define the permissions for the directory
int CScriptFileSystem::MakeDir(const string &path)
{
string search;
if (path.find(":") != string::npos || path.find("/") == 0 || path.find("\\") == 0)
search = path;
else
search = currentPath + "/" + path;
#if defined(_WIN32)
// Windows uses UTF16 so it is necessary to convert the string
wchar_t bufUTF16[10000];
MultiByteToWideChar(CP_UTF8, 0, search.c_str(), -1, bufUTF16, 10000);
// Create the directory
BOOL success = CreateDirectoryW(bufUTF16, 0);
return success ? 0 : -1;
#else
// Create the directory
int failure = mkdir(search.c_str(), S_IRWXU | S_IRWXG | S_IROTH | S_IXOTH);
return !failure ? 0 : -1;
#endif
}
// TODO: Should be able to return different codes for
// - directory doesn't exist
// - directory is not empty
// - access denied
// TODO: Should have an option to remove the directory and all content recursively
int CScriptFileSystem::RemoveDir(const string &path)
{
string search;
if (path.find(":") != string::npos || path.find("/") == 0 || path.find("\\") == 0)
search = path;
else
search = currentPath + "/" + path;
#if defined(_WIN32)
// Windows uses UTF16 so it is necessary to convert the string
wchar_t bufUTF16[10000];
MultiByteToWideChar(CP_UTF8, 0, search.c_str(), -1, bufUTF16, 10000);
// Remove the directory
BOOL success = RemoveDirectoryW(bufUTF16);
return success ? 0 : -1;
#else
// Remove the directory
int failure = rmdir(search.c_str());
return !failure ? 0 : -1;
#endif
}
int CScriptFileSystem::DeleteFile(const string &path)
{
string search;
if (path.find(":") != string::npos || path.find("/") == 0 || path.find("\\") == 0)
search = path;
else
search = currentPath + "/" + path;
#if defined(_WIN32)
// Windows uses UTF16 so it is necessary to convert the string
wchar_t bufUTF16[10000];
MultiByteToWideChar(CP_UTF8, 0, search.c_str(), -1, bufUTF16, 10000);
// Remove the file
BOOL success = DeleteFileW(bufUTF16);
return success ? 0 : -1;
#else
// Remove the file
int failure = unlink(search.c_str());
return !failure ? 0 : -1;
#endif
}
int CScriptFileSystem::CopyFile(const string &source, const string &target)
{
string search1;
if (source.find(":") != string::npos || source.find("/") == 0 || source.find("\\") == 0)
search1 = source;
else
search1 = currentPath + "/" + source;
string search2;
if (target.find(":") != string::npos || target.find("/") == 0 || target.find("\\") == 0)
search2 = target;
else
search2 = currentPath + "/" + target;
#if defined(_WIN32)
// Windows uses UTF16 so it is necessary to convert the string
wchar_t bufUTF16_1[10000];
MultiByteToWideChar(CP_UTF8, 0, search1.c_str(), -1, bufUTF16_1, 10000);
wchar_t bufUTF16_2[10000];
MultiByteToWideChar(CP_UTF8, 0, search2.c_str(), -1, bufUTF16_2, 10000);
// Copy the file
BOOL success = CopyFileW(bufUTF16_1, bufUTF16_2, TRUE);
return success ? 0 : -1;
#else
// Copy the file manually as there is no posix function for this
bool failure = false;
FILE *src = 0, *tgt = 0;
src = fopen(search1.c_str(), "r");
if (src == 0) failure = true;
if( !failure ) tgt = fopen(search2.c_str(), "w");
if (tgt == 0) failure = true;
char buf[1024];
size_t n;
while (!failure && (n = fread(buf, sizeof(char), sizeof(buf), src)) > 0)
{
if (fwrite(buf, sizeof(char), n, tgt) != n)
failure = true;
}
if (src) fclose(src);
if (tgt) fclose(tgt);
return !failure ? 0 : -1;
#endif
}
int CScriptFileSystem::Move(const string &source, const string &target)
{
string search1;
if (source.find(":") != string::npos || source.find("/") == 0 || source.find("\\") == 0)
search1 = source;
else
search1 = currentPath + "/" + source;
string search2;
if (target.find(":") != string::npos || target.find("/") == 0 || target.find("\\") == 0)
search2 = target;
else
search2 = currentPath + "/" + target;
#if defined(_WIN32)
// Windows uses UTF16 so it is necessary to convert the string
wchar_t bufUTF16_1[10000];
MultiByteToWideChar(CP_UTF8, 0, search1.c_str(), -1, bufUTF16_1, 10000);
wchar_t bufUTF16_2[10000];
MultiByteToWideChar(CP_UTF8, 0, search2.c_str(), -1, bufUTF16_2, 10000);
// Move the file or directory
BOOL success = MoveFileW(bufUTF16_1, bufUTF16_2);
return success ? 0 : -1;
#else
// Move the file or directory
int failure = rename(search1.c_str(), search2.c_str());
return !failure ? 0 : -1;
#endif
}
string CScriptFileSystem::GetCurrentPath() const
{
return currentPath;
}
CDateTime CScriptFileSystem::GetCreateDateTime(const string &path) const
{
string search;
if (path.find(":") != string::npos || path.find("/") == 0 || path.find("\\") == 0)
search = path;
else
search = currentPath + "/" + path;
#if defined(_WIN32)
// Windows uses UTF16 so it is necessary to convert the string
wchar_t bufUTF16[10000];
MultiByteToWideChar(CP_UTF8, 0, search.c_str(), -1, bufUTF16, 10000);
// Get the create date/time of the file
FILETIME createTm;
HANDLE file = CreateFileW(bufUTF16, GENERIC_READ, FILE_SHARE_READ, 0, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, 0);
BOOL success = GetFileTime(file, &createTm, 0, 0);
CloseHandle(file);
if( !success )
{
asIScriptContext *ctx = asGetActiveContext();
if( ctx )
ctx->SetException("Failed to get file creation date/time");
return CDateTime();
}
SYSTEMTIME tm;
FileTimeToSystemTime(&createTm, &tm);
return CDateTime(tm.wYear, tm.wMonth, tm.wDay, tm.wHour, tm.wMinute, tm.wSecond);
#else
// Get the create date/time of the file
struct stat st;
if (stat(search.c_str(), &st) == -1)
{
asIScriptContext *ctx = asGetActiveContext();
if( ctx )
ctx->SetException("Failed to get file creation date/time");
return CDateTime();
}
tm *t = localtime(&st.st_ctime);
return CDateTime(t->tm_year + 1900, t->tm_mon + 1, t->tm_mday, t->tm_hour, t->tm_min, t->tm_sec);
#endif
}
CDateTime CScriptFileSystem::GetModifyDateTime(const string &path) const
{
string search;
if (path.find(":") != string::npos || path.find("/") == 0 || path.find("\\") == 0)
search = path;
else
search = currentPath + "/" + path;
#if defined(_WIN32)
// Windows uses UTF16 so it is necessary to convert the string
wchar_t bufUTF16[10000];
MultiByteToWideChar(CP_UTF8, 0, search.c_str(), -1, bufUTF16, 10000);
// Get the last modify date/time of the file
FILETIME modifyTm;
HANDLE file = CreateFileW(bufUTF16, GENERIC_READ, FILE_SHARE_READ, 0, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, 0);
BOOL success = GetFileTime(file, 0, 0, &modifyTm);
CloseHandle(file);
if( !success )
{
asIScriptContext *ctx = asGetActiveContext();
if( ctx )
ctx->SetException("Failed to get file modify date/time");
return CDateTime();
}
SYSTEMTIME tm;
FileTimeToSystemTime(&modifyTm, &tm);
return CDateTime(tm.wYear, tm.wMonth, tm.wDay, tm.wHour, tm.wMinute, tm.wSecond);
#else
// Get the last modify date/time of the file
struct stat st;
if (stat(search.c_str(), &st) == -1)
{
asIScriptContext *ctx = asGetActiveContext();
if( ctx )
ctx->SetException("Failed to get file modify date/time");
return CDateTime();
}
tm *t = localtime(&st.st_mtime);
return CDateTime(t->tm_year + 1900, t->tm_mon + 1, t->tm_mday, t->tm_hour, t->tm_min, t->tm_sec);
#endif
}
END_AS_NAMESPACE

View File

@@ -0,0 +1,78 @@
#ifndef AS_SCRIPTFILESYSTEM_H
#define AS_SCRIPTFILESYSTEM_H
#ifndef ANGELSCRIPT_H
// Avoid having to inform include path if header is already include before
#include <angelscript.h>
#endif
#include <string>
#include <stdio.h>
#include "../scriptarray/scriptarray.h"
#include "../datetime/datetime.h"
BEGIN_AS_NAMESPACE
class CScriptFileSystem
{
public:
CScriptFileSystem();
void AddRef() const;
void Release() const;
// Sets the current path that should be used in other calls when using relative paths
// It can use relative paths too, so moving up a directory is used by passing in ".."
bool ChangeCurrentPath(const std::string &path);
std::string GetCurrentPath() const;
// Returns true if the path is a directory. Input can be either a full path or a relative path.
// This method does not return the dirs '.' and '..'
bool IsDir(const std::string &path) const;
// Returns true if the path is a link. Input can be either a full path or a relative path
bool IsLink(const std::string &path) const;
// Returns the size of file. Input can be either a full path or a relative path
asINT64 GetSize(const std::string &path) const;
// Returns a list of the files in the current path
CScriptArray *GetFiles() const;
// Returns a list of the directories in the current path
CScriptArray *GetDirs() const;
// Creates a new directory. Returns 0 on success
int MakeDir(const std::string &path);
// Removes a directory. Will only remove the directory if it is empty. Returns 0 on success
int RemoveDir(const std::string &path);
// Deletes a file. Returns 0 on success
int DeleteFile(const std::string &path);
// Copies a file. Returns 0 on success
int CopyFile(const std::string &source, const std::string &target);
// Moves or renames a file or directory. Returns 0 on success
int Move(const std::string &source, const std::string &target);
// Gets the date and time of the file/dir creation
CDateTime GetCreateDateTime(const std::string &path) const;
// Gets the date and time of the file/dir modification
CDateTime GetModifyDateTime(const std::string &path) const;
protected:
~CScriptFileSystem();
mutable int refCount;
std::string currentPath;
};
void RegisterScriptFileSystem(asIScriptEngine *engine);
END_AS_NAMESPACE
#endif

View File

@@ -0,0 +1,807 @@
#include <new>
#include <stdlib.h>
#include <string.h>
#include <assert.h>
#include <stdio.h> // sprintf
#include "scriptgrid.h"
using namespace std;
BEGIN_AS_NAMESPACE
// Set the default memory routines
// Use the angelscript engine's memory routines by default
static asALLOCFUNC_t userAlloc = asAllocMem;
static asFREEFUNC_t userFree = asFreeMem;
// Allows the application to set which memory routines should be used by the array object
void CScriptGrid::SetMemoryFunctions(asALLOCFUNC_t allocFunc, asFREEFUNC_t freeFunc)
{
userAlloc = allocFunc;
userFree = freeFunc;
}
static void RegisterScriptGrid_Native(asIScriptEngine *engine);
struct SGridBuffer
{
asDWORD width;
asDWORD height;
asBYTE data[1];
};
CScriptGrid *CScriptGrid::Create(asITypeInfo *ti)
{
return CScriptGrid::Create(ti, 0, 0);
}
CScriptGrid *CScriptGrid::Create(asITypeInfo *ti, asUINT w, asUINT h)
{
// Allocate the memory
void *mem = userAlloc(sizeof(CScriptGrid));
if( mem == 0 )
{
asIScriptContext *ctx = asGetActiveContext();
if( ctx )
ctx->SetException("Out of memory");
return 0;
}
// Initialize the object
CScriptGrid *a = new(mem) CScriptGrid(w, h, ti);
return a;
}
CScriptGrid *CScriptGrid::Create(asITypeInfo *ti, void *initList)
{
// Allocate the memory
void *mem = userAlloc(sizeof(CScriptGrid));
if( mem == 0 )
{
asIScriptContext *ctx = asGetActiveContext();
if( ctx )
ctx->SetException("Out of memory");
return 0;
}
// Initialize the object
CScriptGrid *a = new(mem) CScriptGrid(ti, initList);
return a;
}
CScriptGrid *CScriptGrid::Create(asITypeInfo *ti, asUINT w, asUINT h, void *defVal)
{
// Allocate the memory
void *mem = userAlloc(sizeof(CScriptGrid));
if( mem == 0 )
{
asIScriptContext *ctx = asGetActiveContext();
if( ctx )
ctx->SetException("Out of memory");
return 0;
}
// Initialize the object
CScriptGrid *a = new(mem) CScriptGrid(w, h, defVal, ti);
return a;
}
// This optional callback is called when the template type is first used by the compiler.
// It allows the application to validate if the template can be instantiated for the requested
// subtype at compile time, instead of at runtime. The output argument dontGarbageCollect
// allow the callback to tell the engine if the template instance type shouldn't be garbage collected,
// i.e. no asOBJ_GC flag.
static bool ScriptGridTemplateCallback(asITypeInfo *ti, bool &dontGarbageCollect)
{
// Make sure the subtype can be instantiated with a default factory/constructor,
// otherwise we won't be able to instantiate the elements.
int typeId = ti->GetSubTypeId();
if( typeId == asTYPEID_VOID )
return false;
if( (typeId & asTYPEID_MASK_OBJECT) && !(typeId & asTYPEID_OBJHANDLE) )
{
asITypeInfo *subtype = ti->GetEngine()->GetTypeInfoById(typeId);
asDWORD flags = subtype->GetFlags();
if( (flags & asOBJ_VALUE) && !(flags & asOBJ_POD) )
{
// Verify that there is a default constructor
bool found = false;
for( asUINT n = 0; n < subtype->GetBehaviourCount(); n++ )
{
asEBehaviours beh;
asIScriptFunction *func = subtype->GetBehaviourByIndex(n, &beh);
if( beh != asBEHAVE_CONSTRUCT ) continue;
if( func->GetParamCount() == 0 )
{
// Found the default constructor
found = true;
break;
}
}
if( !found )
{
// There is no default constructor
ti->GetEngine()->WriteMessage("array", 0, 0, asMSGTYPE_ERROR, "The subtype has no default constructor");
return false;
}
}
else if( (flags & asOBJ_REF) )
{
bool found = false;
// If value assignment for ref type has been disabled then the array
// can be created if the type has a default factory function
if( !ti->GetEngine()->GetEngineProperty(asEP_DISALLOW_VALUE_ASSIGN_FOR_REF_TYPE) )
{
// Verify that there is a default factory
for( asUINT n = 0; n < subtype->GetFactoryCount(); n++ )
{
asIScriptFunction *func = subtype->GetFactoryByIndex(n);
if( func->GetParamCount() == 0 )
{
// Found the default factory
found = true;
break;
}
}
}
if( !found )
{
// No default factory
ti->GetEngine()->WriteMessage("array", 0, 0, asMSGTYPE_ERROR, "The subtype has no default factory");
return false;
}
}
// If the object type is not garbage collected then the array also doesn't need to be
if( !(flags & asOBJ_GC) )
dontGarbageCollect = true;
}
else if( !(typeId & asTYPEID_OBJHANDLE) )
{
// Arrays with primitives cannot form circular references,
// thus there is no need to garbage collect them
dontGarbageCollect = true;
}
else
{
assert( typeId & asTYPEID_OBJHANDLE );
// It is not necessary to set the array as garbage collected for all handle types.
// If it is possible to determine that the handle cannot refer to an object type
// that can potentially form a circular reference with the array then it is not
// necessary to make the array garbage collected.
asITypeInfo *subtype = ti->GetEngine()->GetTypeInfoById(typeId);
asDWORD flags = subtype->GetFlags();
if( !(flags & asOBJ_GC) )
{
if( (flags & asOBJ_SCRIPT_OBJECT) )
{
// Even if a script class is by itself not garbage collected, it is possible
// that classes that derive from it may be, so it is not possible to know
// that no circular reference can occur.
if( (flags & asOBJ_NOINHERIT) )
{
// A script class declared as final cannot be inherited from, thus
// we can be certain that the object cannot be garbage collected.
dontGarbageCollect = true;
}
}
else
{
// For application registered classes we assume the application knows
// what it is doing and don't mark the array as garbage collected unless
// the type is also garbage collected.
dontGarbageCollect = true;
}
}
}
// The type is ok
return true;
}
// Registers the template array type
void RegisterScriptGrid(asIScriptEngine *engine)
{
// TODO: Implement the generic calling convention
RegisterScriptGrid_Native(engine);
}
static void RegisterScriptGrid_Native(asIScriptEngine *engine)
{
int r;
// Register the grid type as a template
r = engine->RegisterObjectType("grid<class T>", 0, asOBJ_REF | asOBJ_GC | asOBJ_TEMPLATE); assert( r >= 0 );
// Register a callback for validating the subtype before it is used
r = engine->RegisterObjectBehaviour("grid<T>", asBEHAVE_TEMPLATE_CALLBACK, "bool f(int&in, bool&out)", asFUNCTION(ScriptGridTemplateCallback), asCALL_CDECL); assert( r >= 0 );
// Templates receive the object type as the first parameter. To the script writer this is hidden
r = engine->RegisterObjectBehaviour("grid<T>", asBEHAVE_FACTORY, "grid<T>@ f(int&in)", asFUNCTIONPR(CScriptGrid::Create, (asITypeInfo*), CScriptGrid*), asCALL_CDECL); assert( r >= 0 );
r = engine->RegisterObjectBehaviour("grid<T>", asBEHAVE_FACTORY, "grid<T>@ f(int&in, uint, uint)", asFUNCTIONPR(CScriptGrid::Create, (asITypeInfo*, asUINT, asUINT), CScriptGrid*), asCALL_CDECL); assert( r >= 0 );
r = engine->RegisterObjectBehaviour("grid<T>", asBEHAVE_FACTORY, "grid<T>@ f(int&in, uint, uint, const T &in)", asFUNCTIONPR(CScriptGrid::Create, (asITypeInfo*, asUINT, asUINT, void *), CScriptGrid*), asCALL_CDECL); assert( r >= 0 );
// Register the factory that will be used for initialization lists
r = engine->RegisterObjectBehaviour("grid<T>", asBEHAVE_LIST_FACTORY, "grid<T>@ f(int&in type, int&in list) {repeat {repeat_same T}}", asFUNCTIONPR(CScriptGrid::Create, (asITypeInfo*, void*), CScriptGrid*), asCALL_CDECL); assert( r >= 0 );
// The memory management methods
r = engine->RegisterObjectBehaviour("grid<T>", asBEHAVE_ADDREF, "void f()", asMETHOD(CScriptGrid,AddRef), asCALL_THISCALL); assert( r >= 0 );
r = engine->RegisterObjectBehaviour("grid<T>", asBEHAVE_RELEASE, "void f()", asMETHOD(CScriptGrid,Release), asCALL_THISCALL); assert( r >= 0 );
// The index operator returns the template subtype
r = engine->RegisterObjectMethod("grid<T>", "T &opIndex(uint, uint)", asMETHODPR(CScriptGrid, At, (asUINT, asUINT), void*), asCALL_THISCALL); assert( r >= 0 );
r = engine->RegisterObjectMethod("grid<T>", "const T &opIndex(uint, uint) const", asMETHODPR(CScriptGrid, At, (asUINT, asUINT) const, const void*), asCALL_THISCALL); assert( r >= 0 );
// Other methods
r = engine->RegisterObjectMethod("grid<T>", "void resize(uint width, uint height)", asMETHODPR(CScriptGrid, Resize, (asUINT, asUINT), void), asCALL_THISCALL); assert( r >= 0 );
r = engine->RegisterObjectMethod("grid<T>", "uint width() const", asMETHOD(CScriptGrid, GetWidth), asCALL_THISCALL); assert( r >= 0 );
r = engine->RegisterObjectMethod("grid<T>", "uint height() const", asMETHOD(CScriptGrid, GetHeight), asCALL_THISCALL); assert( r >= 0 );
// Register GC behaviours in case the array needs to be garbage collected
r = engine->RegisterObjectBehaviour("grid<T>", asBEHAVE_GETREFCOUNT, "int f()", asMETHOD(CScriptGrid, GetRefCount), asCALL_THISCALL); assert( r >= 0 );
r = engine->RegisterObjectBehaviour("grid<T>", asBEHAVE_SETGCFLAG, "void f()", asMETHOD(CScriptGrid, SetFlag), asCALL_THISCALL); assert( r >= 0 );
r = engine->RegisterObjectBehaviour("grid<T>", asBEHAVE_GETGCFLAG, "bool f()", asMETHOD(CScriptGrid, GetFlag), asCALL_THISCALL); assert( r >= 0 );
r = engine->RegisterObjectBehaviour("grid<T>", asBEHAVE_ENUMREFS, "void f(int&in)", asMETHOD(CScriptGrid, EnumReferences), asCALL_THISCALL); assert( r >= 0 );
r = engine->RegisterObjectBehaviour("grid<T>", asBEHAVE_RELEASEREFS, "void f(int&in)", asMETHOD(CScriptGrid, ReleaseAllHandles), asCALL_THISCALL); assert( r >= 0 );
}
CScriptGrid::CScriptGrid(asITypeInfo *ti, void *buf)
{
refCount = 1;
gcFlag = false;
objType = ti;
objType->AddRef();
buffer = 0;
subTypeId = objType->GetSubTypeId();
asIScriptEngine *engine = ti->GetEngine();
// Determine element size
if( subTypeId & asTYPEID_MASK_OBJECT )
elementSize = sizeof(asPWORD);
else
elementSize = engine->GetSizeOfPrimitiveType(subTypeId);
// Determine the initial size from the buffer
asUINT height = *(asUINT*)buf;
asUINT width = height ? *(asUINT*)((char*)(buf)+4) : 0;
// Make sure the grid size isn't too large for us to handle
if( !CheckMaxSize(width, height) )
{
// Don't continue with the initialization
return;
}
// Skip the height value at the start of the buffer
buf = (asUINT*)(buf)+1;
// Copy the values of the grid elements from the buffer
if( (ti->GetSubTypeId() & asTYPEID_MASK_OBJECT) == 0 )
{
CreateBuffer(&buffer, width, height);
// Copy the values of the primitive type into the internal buffer
for( asUINT y = 0; y < height; y++ )
{
// Skip the length value at the start of each row
buf = (asUINT*)(buf)+1;
// Copy the line
if( width > 0 )
memcpy(At(0,y), buf, width*elementSize);
// Move to next line
buf = (char*)(buf) + width*elementSize;
// Align to 4 byte boundary
if( asPWORD(buf) & 0x3 )
buf = (char*)(buf) + 4 - (asPWORD(buf) & 0x3);
}
}
else if( ti->GetSubTypeId() & asTYPEID_OBJHANDLE )
{
CreateBuffer(&buffer, width, height);
// Copy the handles into the internal buffer
for( asUINT y = 0; y < height; y++ )
{
// Skip the length value at the start of each row
buf = (asUINT*)(buf)+1;
// Copy the line
if( width > 0 )
memcpy(At(0,y), buf, width*elementSize);
// With object handles it is safe to clear the memory in the received buffer
// instead of increasing the ref count. It will save time both by avoiding the
// call the increase ref, and also relieve the engine from having to release
// its references too
memset(buf, 0, width*elementSize);
// Move to next line
buf = (char*)(buf) + width*elementSize;
// Align to 4 byte boundary
if( asPWORD(buf) & 0x3 )
buf = (char*)(buf) + 4 - (asPWORD(buf) & 0x3);
}
}
else if( ti->GetSubType()->GetFlags() & asOBJ_REF )
{
// Only allocate the buffer, but not the objects
subTypeId |= asTYPEID_OBJHANDLE;
CreateBuffer(&buffer, width, height);
subTypeId &= ~asTYPEID_OBJHANDLE;
// Copy the handles into the internal buffer
for( asUINT y = 0; y < height; y++ )
{
// Skip the length value at the start of each row
buf = (asUINT*)(buf)+1;
// Copy the line
if( width > 0 )
memcpy(At(0,y), buf, width*elementSize);
// With object handles it is safe to clear the memory in the received buffer
// instead of increasing the ref count. It will save time both by avoiding the
// call the increase ref, and also relieve the engine from having to release
// its references too
memset(buf, 0, width*elementSize);
// Move to next line
buf = (char*)(buf) + width*elementSize;
// Align to 4 byte boundary
if( asPWORD(buf) & 0x3 )
buf = (char*)(buf) + 4 - (asPWORD(buf) & 0x3);
}
}
else
{
// TODO: Optimize by calling the copy constructor of the object instead of
// constructing with the default constructor and then assigning the value
// TODO: With C++11 ideally we should be calling the move constructor, instead
// of the copy constructor as the engine will just discard the objects in the
// buffer afterwards.
CreateBuffer(&buffer, width, height);
// For value types we need to call the opAssign for each individual object
asITypeInfo *subType = ti->GetSubType();
asUINT subTypeSize = subType->GetSize();
for( asUINT y = 0;y < height; y++ )
{
// Skip the length value at the start of each row
buf = (asUINT*)(buf)+1;
// Call opAssign for each of the objects on the row
for( asUINT x = 0; x < width; x++ )
{
void *obj = At(x,y);
asBYTE *srcObj = (asBYTE*)(buf) + x*subTypeSize;
engine->AssignScriptObject(obj, srcObj, subType);
}
// Move to next line
buf = (char*)(buf) + width*subTypeSize;
// Align to 4 byte boundary
if( asPWORD(buf) & 0x3 )
buf = (char*)(buf) + 4 - (asPWORD(buf) & 0x3);
}
}
// Notify the GC of the successful creation
if( objType->GetFlags() & asOBJ_GC )
objType->GetEngine()->NotifyGarbageCollectorOfNewObject(this, objType);
}
CScriptGrid::CScriptGrid(asUINT width, asUINT height, asITypeInfo *ti)
{
refCount = 1;
gcFlag = false;
objType = ti;
objType->AddRef();
buffer = 0;
subTypeId = objType->GetSubTypeId();
// Determine element size
if( subTypeId & asTYPEID_MASK_OBJECT )
elementSize = sizeof(asPWORD);
else
elementSize = objType->GetEngine()->GetSizeOfPrimitiveType(subTypeId);
// Make sure the array size isn't too large for us to handle
if( !CheckMaxSize(width, height) )
{
// Don't continue with the initialization
return;
}
CreateBuffer(&buffer, width, height);
// Notify the GC of the successful creation
if( objType->GetFlags() & asOBJ_GC )
objType->GetEngine()->NotifyGarbageCollectorOfNewObject(this, objType);
}
void CScriptGrid::Resize(asUINT width, asUINT height)
{
// Make sure the size isn't too large for us to handle
if( !CheckMaxSize(width, height) )
return;
// Create a new buffer
SGridBuffer *tmpBuffer = 0;
CreateBuffer(&tmpBuffer, width, height);
if( tmpBuffer == 0 )
return;
if( buffer )
{
// Copy the existing values to the new buffer
asUINT w = width > buffer->width ? buffer->width : width;
asUINT h = height > buffer->height ? buffer->height : height;
for( asUINT y = 0; y < h; y++ )
for( asUINT x = 0; x < w; x++ )
SetValue(tmpBuffer, x, y, At(buffer, x, y));
// Replace the internal buffer
DeleteBuffer(buffer);
}
buffer = tmpBuffer;
}
CScriptGrid::CScriptGrid(asUINT width, asUINT height, void *defVal, asITypeInfo *ti)
{
refCount = 1;
gcFlag = false;
objType = ti;
objType->AddRef();
buffer = 0;
subTypeId = objType->GetSubTypeId();
// Determine element size
if( subTypeId & asTYPEID_MASK_OBJECT )
elementSize = sizeof(asPWORD);
else
elementSize = objType->GetEngine()->GetSizeOfPrimitiveType(subTypeId);
// Make sure the array size isn't too large for us to handle
if( !CheckMaxSize(width, height) )
{
// Don't continue with the initialization
return;
}
CreateBuffer(&buffer, width, height);
// Notify the GC of the successful creation
if( objType->GetFlags() & asOBJ_GC )
objType->GetEngine()->NotifyGarbageCollectorOfNewObject(this, objType);
// Initialize the elements with the default value
for( asUINT y = 0; y < GetHeight(); y++ )
for( asUINT x = 0; x < GetWidth(); x++ )
SetValue(x, y, defVal);
}
void CScriptGrid::SetValue(asUINT x, asUINT y, void *value)
{
SetValue(buffer, x, y, value);
}
void CScriptGrid::SetValue(SGridBuffer *buf, asUINT x, asUINT y, void *value)
{
// At() will take care of the out-of-bounds checking, though
// if called from the application then nothing will be done
void *ptr = At(buf, x, y);
if( ptr == 0 ) return;
if( (subTypeId & ~asTYPEID_MASK_SEQNBR) && !(subTypeId & asTYPEID_OBJHANDLE) )
objType->GetEngine()->AssignScriptObject(ptr, value, objType->GetSubType());
else if( subTypeId & asTYPEID_OBJHANDLE )
{
void *tmp = *(void**)ptr;
*(void**)ptr = *(void**)value;
objType->GetEngine()->AddRefScriptObject(*(void**)value, objType->GetSubType());
if( tmp )
objType->GetEngine()->ReleaseScriptObject(tmp, objType->GetSubType());
}
else if( subTypeId == asTYPEID_BOOL ||
subTypeId == asTYPEID_INT8 ||
subTypeId == asTYPEID_UINT8 )
*(char*)ptr = *(char*)value;
else if( subTypeId == asTYPEID_INT16 ||
subTypeId == asTYPEID_UINT16 )
*(short*)ptr = *(short*)value;
else if( subTypeId == asTYPEID_INT32 ||
subTypeId == asTYPEID_UINT32 ||
subTypeId == asTYPEID_FLOAT ||
subTypeId > asTYPEID_DOUBLE ) // enums have a type id larger than doubles
*(int*)ptr = *(int*)value;
else if( subTypeId == asTYPEID_INT64 ||
subTypeId == asTYPEID_UINT64 ||
subTypeId == asTYPEID_DOUBLE )
*(double*)ptr = *(double*)value;
}
CScriptGrid::~CScriptGrid()
{
if( buffer )
{
DeleteBuffer(buffer);
buffer = 0;
}
if( objType ) objType->Release();
}
asUINT CScriptGrid::GetWidth() const
{
if( buffer )
return buffer->width;
return 0;
}
asUINT CScriptGrid::GetHeight() const
{
if( buffer )
return buffer->height;
return 0;
}
// internal
bool CScriptGrid::CheckMaxSize(asUINT width, asUINT height)
{
// This code makes sure the size of the buffer that is allocated
// for the array doesn't overflow and becomes smaller than requested
asUINT maxSize = 0xFFFFFFFFul - sizeof(SGridBuffer) + 1;
if( elementSize > 0 )
maxSize /= elementSize;
asINT64 numElements = width * height;
if( (numElements >> 32) || numElements > maxSize )
{
asIScriptContext *ctx = asGetActiveContext();
if( ctx )
ctx->SetException("Too large grid size");
return false;
}
// OK
return true;
}
asITypeInfo *CScriptGrid::GetGridObjectType() const
{
return objType;
}
int CScriptGrid::GetGridTypeId() const
{
return objType->GetTypeId();
}
int CScriptGrid::GetElementTypeId() const
{
return subTypeId;
}
void *CScriptGrid::At(asUINT x, asUINT y)
{
return At(buffer, x, y);
}
// Return a pointer to the array element. Returns 0 if the index is out of bounds
void *CScriptGrid::At(SGridBuffer *buf, asUINT x, asUINT y)
{
if( buf == 0 || x >= buf->width || y >= buf->height )
{
// If this is called from a script we raise a script exception
asIScriptContext *ctx = asGetActiveContext();
if( ctx )
ctx->SetException("Index out of bounds");
return 0;
}
asUINT index = x+y*buf->width;
if( (subTypeId & asTYPEID_MASK_OBJECT) && !(subTypeId & asTYPEID_OBJHANDLE) )
return *(void**)(buf->data + elementSize*index);
else
return buf->data + elementSize*index;
}
const void *CScriptGrid::At(asUINT x, asUINT y) const
{
return const_cast<CScriptGrid*>(this)->At(const_cast<SGridBuffer*>(buffer), x, y);
}
// internal
void CScriptGrid::CreateBuffer(SGridBuffer **buf, asUINT w, asUINT h)
{
asUINT numElements = w * h;
*buf = reinterpret_cast<SGridBuffer*>(userAlloc(sizeof(SGridBuffer)-1+elementSize*numElements));
if( *buf )
{
(*buf)->width = w;
(*buf)->height = h;
Construct(*buf);
}
else
{
// Oops, out of memory
asIScriptContext *ctx = asGetActiveContext();
if( ctx )
ctx->SetException("Out of memory");
}
}
// internal
void CScriptGrid::DeleteBuffer(SGridBuffer *buf)
{
assert( buf );
Destruct(buf);
// Free the buffer
userFree(buf);
}
// internal
void CScriptGrid::Construct(SGridBuffer *buf)
{
assert( buf );
if( subTypeId & asTYPEID_OBJHANDLE )
{
// Set all object handles to null
void *d = (void*)(buf->data);
memset(d, 0, (buf->width*buf->height)*sizeof(void*));
}
else if( subTypeId & asTYPEID_MASK_OBJECT )
{
void **max = (void**)(buf->data + (buf->width*buf->height) * sizeof(void*));
void **d = (void**)(buf->data);
asIScriptEngine *engine = objType->GetEngine();
asITypeInfo *subType = objType->GetSubType();
for( ; d < max; d++ )
{
*d = (void*)engine->CreateScriptObject(subType);
if( *d == 0 )
{
// Set the remaining entries to null so the destructor
// won't attempt to destroy invalid objects later
memset(d, 0, sizeof(void*)*(max-d));
// There is no need to set an exception on the context,
// as CreateScriptObject has already done that
return;
}
}
}
}
// internal
void CScriptGrid::Destruct(SGridBuffer *buf)
{
assert( buf );
if( subTypeId & asTYPEID_MASK_OBJECT )
{
asIScriptEngine *engine = objType->GetEngine();
void **max = (void**)(buf->data + (buf->width*buf->height) * sizeof(void*));
void **d = (void**)(buf->data);
for( ; d < max; d++ )
{
if( *d )
engine->ReleaseScriptObject(*d, objType->GetSubType());
}
}
}
// GC behaviour
void CScriptGrid::EnumReferences(asIScriptEngine *engine)
{
if( buffer == 0 ) return;
// If the grid is holding handles, then we need to notify the GC of them
if (subTypeId & asTYPEID_MASK_OBJECT)
{
asUINT numElements = buffer->width * buffer->height;
void **d = (void**)buffer->data;
asITypeInfo *subType = engine->GetTypeInfoById(subTypeId);
if ((subType->GetFlags() & asOBJ_REF))
{
// For reference types we need to notify the GC of each instance
for (asUINT n = 0; n < numElements; n++)
{
if (d[n])
engine->GCEnumCallback(d[n]);
}
}
else if ((subType->GetFlags() & asOBJ_VALUE) && (subType->GetFlags() & asOBJ_GC))
{
// For value types we need to forward the enum callback
// to the object so it can decide what to do
for (asUINT n = 0; n < numElements; n++)
{
if (d[n])
engine->ForwardGCEnumReferences(d[n], subType);
}
}
}
}
// GC behaviour
void CScriptGrid::ReleaseAllHandles(asIScriptEngine*)
{
if( buffer == 0 ) return;
DeleteBuffer(buffer);
buffer = 0;
}
void CScriptGrid::AddRef() const
{
// Clear the GC flag then increase the counter
gcFlag = false;
asAtomicInc(refCount);
}
void CScriptGrid::Release() const
{
// Clearing the GC flag then descrease the counter
gcFlag = false;
if( asAtomicDec(refCount) == 0 )
{
// When reaching 0 no more references to this instance
// exists and the object should be destroyed
this->~CScriptGrid();
userFree(const_cast<CScriptGrid*>(this));
}
}
// GC behaviour
int CScriptGrid::GetRefCount()
{
return refCount;
}
// GC behaviour
void CScriptGrid::SetFlag()
{
gcFlag = true;
}
// GC behaviour
bool CScriptGrid::GetFlag()
{
return gcFlag;
}
END_AS_NAMESPACE

View File

@@ -0,0 +1,82 @@
#ifndef SCRIPTGRID_H
#define SCRIPTGRID_H
#ifndef ANGELSCRIPT_H
// Avoid having to inform include path if header is already include before
#include <angelscript.h>
#endif
BEGIN_AS_NAMESPACE
struct SGridBuffer;
class CScriptGrid
{
public:
// Set the memory functions that should be used by all CScriptGrids
static void SetMemoryFunctions(asALLOCFUNC_t allocFunc, asFREEFUNC_t freeFunc);
// Factory functions
static CScriptGrid *Create(asITypeInfo *ot);
static CScriptGrid *Create(asITypeInfo *ot, asUINT width, asUINT height);
static CScriptGrid *Create(asITypeInfo *ot, asUINT width, asUINT height, void *defaultValue);
static CScriptGrid *Create(asITypeInfo *ot, void *listBuffer);
// Memory management
void AddRef() const;
void Release() const;
// Type information
asITypeInfo *GetGridObjectType() const;
int GetGridTypeId() const;
int GetElementTypeId() const;
// Size
asUINT GetWidth() const;
asUINT GetHeight() const;
void Resize(asUINT width, asUINT height);
// Get a pointer to an element. Returns 0 if out of bounds
void *At(asUINT x, asUINT y);
const void *At(asUINT x, asUINT y) const;
// Set value of an element
// Remember, if the grid holds handles the value parameter should be the
// address of the handle. The refCount of the object will also be incremented
void SetValue(asUINT x, asUINT y, void *value);
// GC methods
int GetRefCount();
void SetFlag();
bool GetFlag();
void EnumReferences(asIScriptEngine *engine);
void ReleaseAllHandles(asIScriptEngine *engine);
protected:
mutable int refCount;
mutable bool gcFlag;
asITypeInfo *objType;
SGridBuffer *buffer;
int elementSize;
int subTypeId;
// Constructors
CScriptGrid(asITypeInfo *ot, void *initBuf); // Called from script when initialized with list
CScriptGrid(asUINT w, asUINT h, asITypeInfo *ot);
CScriptGrid(asUINT w, asUINT h, void *defVal, asITypeInfo *ot);
virtual ~CScriptGrid();
bool CheckMaxSize(asUINT x, asUINT y);
void CreateBuffer(SGridBuffer **buf, asUINT w, asUINT h);
void DeleteBuffer(SGridBuffer *buf);
void Construct(SGridBuffer *buf);
void Destruct(SGridBuffer *buf);
void SetValue(SGridBuffer *buf, asUINT x, asUINT y, void *value);
void *At(SGridBuffer *buf, asUINT x, asUINT y);
};
void RegisterScriptGrid(asIScriptEngine *engine);
END_AS_NAMESPACE
#endif

View File

@@ -0,0 +1,360 @@
#include "scripthandle.h"
#include <new>
#include <assert.h>
#include <string.h>
BEGIN_AS_NAMESPACE
static void Construct(CScriptHandle *self) { new(self) CScriptHandle(); }
static void Construct(CScriptHandle *self, const CScriptHandle &o) { new(self) CScriptHandle(o); }
// This one is not static because it needs to be friend with the CScriptHandle class
void Construct(CScriptHandle *self, void *ref, int typeId) { new(self) CScriptHandle(ref, typeId); }
static void Destruct(CScriptHandle *self) { self->~CScriptHandle(); }
CScriptHandle::CScriptHandle()
{
m_ref = 0;
m_type = 0;
}
CScriptHandle::CScriptHandle(const CScriptHandle &other)
{
m_ref = other.m_ref;
m_type = other.m_type;
AddRefHandle();
}
CScriptHandle::CScriptHandle(void *ref, asITypeInfo *type)
{
m_ref = ref;
m_type = type;
AddRefHandle();
}
// This constructor shouldn't be called from the application
// directly as it requires an active script context
CScriptHandle::CScriptHandle(void *ref, int typeId)
{
m_ref = 0;
m_type = 0;
Assign(ref, typeId);
}
CScriptHandle::~CScriptHandle()
{
ReleaseHandle();
}
void CScriptHandle::ReleaseHandle()
{
if( m_ref && m_type )
{
asIScriptEngine *engine = m_type->GetEngine();
engine->ReleaseScriptObject(m_ref, m_type);
engine->Release();
m_ref = 0;
m_type = 0;
}
}
void CScriptHandle::AddRefHandle()
{
if( m_ref && m_type )
{
asIScriptEngine *engine = m_type->GetEngine();
engine->AddRefScriptObject(m_ref, m_type);
// Hold on to the engine so it isn't destroyed while
// a reference to a script object is still held
engine->AddRef();
}
}
CScriptHandle &CScriptHandle::operator =(const CScriptHandle &other)
{
Set(other.m_ref, other.m_type);
return *this;
}
void CScriptHandle::Set(void *ref, asITypeInfo *type)
{
if( m_ref == ref ) return;
ReleaseHandle();
m_ref = ref;
m_type = type;
AddRefHandle();
}
void *CScriptHandle::GetRef()
{
return m_ref;
}
asITypeInfo *CScriptHandle::GetType() const
{
return m_type;
}
int CScriptHandle::GetTypeId() const
{
if( m_type == 0 ) return 0;
return m_type->GetTypeId() | asTYPEID_OBJHANDLE;
}
// This method shouldn't be called from the application
// directly as it requires an active script context
CScriptHandle &CScriptHandle::Assign(void *ref, int typeId)
{
// When receiving a null handle we just clear our memory
if( typeId == 0 )
{
Set(0, 0);
return *this;
}
// Dereference received handles to get the object
if( typeId & asTYPEID_OBJHANDLE )
{
// Store the actual reference
ref = *(void**)ref;
typeId &= ~asTYPEID_OBJHANDLE;
}
// Get the object type
asIScriptContext *ctx = asGetActiveContext();
asIScriptEngine *engine = ctx->GetEngine();
asITypeInfo *type = engine->GetTypeInfoById(typeId);
// If the argument is another CScriptHandle, we should copy the content instead
if( type && strcmp(type->GetName(), "ref") == 0 )
{
CScriptHandle *r = (CScriptHandle*)ref;
ref = r->m_ref;
type = r->m_type;
}
Set(ref, type);
return *this;
}
bool CScriptHandle::operator==(const CScriptHandle &o) const
{
if( m_ref == o.m_ref &&
m_type == o.m_type )
return true;
// TODO: If type is not the same, we should attempt to do a dynamic cast,
// which may change the pointer for application registered classes
return false;
}
bool CScriptHandle::operator!=(const CScriptHandle &o) const
{
return !(*this == o);
}
bool CScriptHandle::Equals(void *ref, int typeId) const
{
// Null handles are received as reference to a null handle
if( typeId == 0 )
ref = 0;
// Dereference handles to get the object
if( typeId & asTYPEID_OBJHANDLE )
{
// Compare the actual reference
ref = *(void**)ref;
typeId &= ~asTYPEID_OBJHANDLE;
}
// TODO: If typeId is not the same, we should attempt to do a dynamic cast,
// which may change the pointer for application registered classes
if( ref == m_ref ) return true;
return false;
}
// AngelScript: used as '@obj = cast<obj>(ref);'
void CScriptHandle::Cast(void **outRef, int typeId)
{
// If we hold a null handle, then just return null
if( m_type == 0 )
{
*outRef = 0;
return;
}
// It is expected that the outRef is always a handle
assert( typeId & asTYPEID_OBJHANDLE );
// Compare the type id of the actual object
typeId &= ~asTYPEID_OBJHANDLE;
asIScriptEngine *engine = m_type->GetEngine();
asITypeInfo *type = engine->GetTypeInfoById(typeId);
*outRef = 0;
// RefCastObject will increment the refCount of the returned object if successful
engine->RefCastObject(m_ref, m_type, type, outRef);
}
void CScriptHandle::EnumReferences(asIScriptEngine *inEngine)
{
// If we're holding a reference, we'll notify the garbage collector of it
if (m_ref)
inEngine->GCEnumCallback(m_ref);
// The object type itself is also garbage collected
if( m_type)
inEngine->GCEnumCallback(m_type);
}
void CScriptHandle::ReleaseReferences(asIScriptEngine *inEngine)
{
// Simply clear the content to release the references
Set(0, 0);
}
void RegisterScriptHandle_Native(asIScriptEngine *engine)
{
int r;
#if AS_CAN_USE_CPP11
// With C++11 it is possible to use asGetTypeTraits to automatically determine the flags that represent the C++ class
r = engine->RegisterObjectType("ref", sizeof(CScriptHandle), asOBJ_VALUE | asOBJ_ASHANDLE | asOBJ_GC | asGetTypeTraits<CScriptHandle>()); assert( r >= 0 );
#else
r = engine->RegisterObjectType("ref", sizeof(CScriptHandle), asOBJ_VALUE | asOBJ_ASHANDLE | asOBJ_GC | asOBJ_APP_CLASS_CDAK); assert( r >= 0 );
#endif
r = engine->RegisterObjectBehaviour("ref", asBEHAVE_CONSTRUCT, "void f()", asFUNCTIONPR(Construct, (CScriptHandle *), void), asCALL_CDECL_OBJFIRST); assert( r >= 0 );
r = engine->RegisterObjectBehaviour("ref", asBEHAVE_CONSTRUCT, "void f(const ref &in)", asFUNCTIONPR(Construct, (CScriptHandle *, const CScriptHandle &), void), asCALL_CDECL_OBJFIRST); assert( r >= 0 );
r = engine->RegisterObjectBehaviour("ref", asBEHAVE_CONSTRUCT, "void f(const ?&in)", asFUNCTIONPR(Construct, (CScriptHandle *, void *, int), void), asCALL_CDECL_OBJFIRST); assert( r >= 0 );
r = engine->RegisterObjectBehaviour("ref", asBEHAVE_DESTRUCT, "void f()", asFUNCTIONPR(Destruct, (CScriptHandle *), void), asCALL_CDECL_OBJFIRST); assert( r >= 0 );
r = engine->RegisterObjectBehaviour("ref", asBEHAVE_ENUMREFS, "void f(int&in)", asMETHOD(CScriptHandle,EnumReferences), asCALL_THISCALL); assert(r >= 0);
r = engine->RegisterObjectBehaviour("ref", asBEHAVE_RELEASEREFS, "void f(int&in)", asMETHOD(CScriptHandle, ReleaseReferences), asCALL_THISCALL); assert(r >= 0);
r = engine->RegisterObjectMethod("ref", "void opCast(?&out)", asMETHODPR(CScriptHandle, Cast, (void **, int), void), asCALL_THISCALL); assert( r >= 0 );
r = engine->RegisterObjectMethod("ref", "ref &opHndlAssign(const ref &in)", asMETHOD(CScriptHandle, operator=), asCALL_THISCALL); assert( r >= 0 );
r = engine->RegisterObjectMethod("ref", "ref &opHndlAssign(const ?&in)", asMETHOD(CScriptHandle, Assign), asCALL_THISCALL); assert( r >= 0 );
r = engine->RegisterObjectMethod("ref", "bool opEquals(const ref &in) const", asMETHODPR(CScriptHandle, operator==, (const CScriptHandle &) const, bool), asCALL_THISCALL); assert( r >= 0 );
r = engine->RegisterObjectMethod("ref", "bool opEquals(const ?&in) const", asMETHODPR(CScriptHandle, Equals, (void*, int) const, bool), asCALL_THISCALL); assert( r >= 0 );
}
void CScriptHandle_Construct_Generic(asIScriptGeneric *gen)
{
CScriptHandle *self = reinterpret_cast<CScriptHandle*>(gen->GetObject());
new(self) CScriptHandle();
}
void CScriptHandle_ConstructCopy_Generic(asIScriptGeneric *gen)
{
CScriptHandle *other = reinterpret_cast<CScriptHandle*>(gen->GetArgAddress(0));
CScriptHandle *self = reinterpret_cast<CScriptHandle*>(gen->GetObject());
new(self) CScriptHandle(*other);
}
void CScriptHandle_ConstructVar_Generic(asIScriptGeneric *gen)
{
void *ref = gen->GetArgAddress(0);
int typeId = gen->GetArgTypeId(0);
CScriptHandle *self = reinterpret_cast<CScriptHandle*>(gen->GetObject());
Construct(self, ref, typeId);
}
void CScriptHandle_Destruct_Generic(asIScriptGeneric *gen)
{
CScriptHandle *self = reinterpret_cast<CScriptHandle*>(gen->GetObject());
self->~CScriptHandle();
}
void CScriptHandle_Cast_Generic(asIScriptGeneric *gen)
{
void **ref = reinterpret_cast<void**>(gen->GetArgAddress(0));
int typeId = gen->GetArgTypeId(0);
CScriptHandle *self = reinterpret_cast<CScriptHandle*>(gen->GetObject());
self->Cast(ref, typeId);
}
void CScriptHandle_Assign_Generic(asIScriptGeneric *gen)
{
CScriptHandle *other = reinterpret_cast<CScriptHandle*>(gen->GetArgAddress(0));
CScriptHandle *self = reinterpret_cast<CScriptHandle*>(gen->GetObject());
*self = *other;
gen->SetReturnAddress(self);
}
void CScriptHandle_AssignVar_Generic(asIScriptGeneric *gen)
{
void *ref = gen->GetArgAddress(0);
int typeId = gen->GetArgTypeId(0);
CScriptHandle *self = reinterpret_cast<CScriptHandle*>(gen->GetObject());
self->Assign(ref, typeId);
gen->SetReturnAddress(self);
}
void CScriptHandle_Equals_Generic(asIScriptGeneric *gen)
{
CScriptHandle *other = reinterpret_cast<CScriptHandle*>(gen->GetArgAddress(0));
CScriptHandle *self = reinterpret_cast<CScriptHandle*>(gen->GetObject());
gen->SetReturnByte(*self == *other);
}
void CScriptHandle_EqualsVar_Generic(asIScriptGeneric *gen)
{
void *ref = gen->GetArgAddress(0);
int typeId = gen->GetArgTypeId(0);
CScriptHandle *self = reinterpret_cast<CScriptHandle*>(gen->GetObject());
gen->SetReturnByte(self->Equals(ref, typeId));
}
void CScriptHandle_EnumReferences_Generic(asIScriptGeneric *gen)
{
CScriptHandle *self = reinterpret_cast<CScriptHandle*>(gen->GetObject());
self->EnumReferences(gen->GetEngine());
}
void CScriptHandle_ReleaseReferences_Generic(asIScriptGeneric *gen)
{
CScriptHandle *self = reinterpret_cast<CScriptHandle*>(gen->GetObject());
self->ReleaseReferences(gen->GetEngine());
}
void RegisterScriptHandle_Generic(asIScriptEngine *engine)
{
int r;
r = engine->RegisterObjectType("ref", sizeof(CScriptHandle), asOBJ_VALUE | asOBJ_ASHANDLE | asOBJ_GC | asOBJ_APP_CLASS_CDAK); assert( r >= 0 );
r = engine->RegisterObjectBehaviour("ref", asBEHAVE_CONSTRUCT, "void f()", asFUNCTION(CScriptHandle_Construct_Generic), asCALL_GENERIC); assert( r >= 0 );
r = engine->RegisterObjectBehaviour("ref", asBEHAVE_CONSTRUCT, "void f(const ref &in)", asFUNCTION(CScriptHandle_ConstructCopy_Generic), asCALL_GENERIC); assert( r >= 0 );
r = engine->RegisterObjectBehaviour("ref", asBEHAVE_CONSTRUCT, "void f(const ?&in)", asFUNCTION(CScriptHandle_ConstructVar_Generic), asCALL_GENERIC); assert( r >= 0 );
r = engine->RegisterObjectBehaviour("ref", asBEHAVE_DESTRUCT, "void f()", asFUNCTION(CScriptHandle_Destruct_Generic), asCALL_GENERIC); assert( r >= 0 );
r = engine->RegisterObjectBehaviour("ref", asBEHAVE_ENUMREFS, "void f(int&in)", asFUNCTION(CScriptHandle_EnumReferences_Generic), asCALL_GENERIC); assert(r >= 0);
r = engine->RegisterObjectBehaviour("ref", asBEHAVE_RELEASEREFS, "void f(int&in)", asFUNCTION(CScriptHandle_ReleaseReferences_Generic), asCALL_GENERIC); assert(r >= 0);
r = engine->RegisterObjectMethod("ref", "void opCast(?&out)", asFUNCTION(CScriptHandle_Cast_Generic), asCALL_GENERIC); assert( r >= 0 );
r = engine->RegisterObjectMethod("ref", "ref &opHndlAssign(const ref &in)", asFUNCTION(CScriptHandle_Assign_Generic), asCALL_GENERIC); assert( r >= 0 );
r = engine->RegisterObjectMethod("ref", "ref &opHndlAssign(const ?&in)", asFUNCTION(CScriptHandle_AssignVar_Generic), asCALL_GENERIC); assert( r >= 0 );
r = engine->RegisterObjectMethod("ref", "bool opEquals(const ref &in) const", asFUNCTION(CScriptHandle_Equals_Generic), asCALL_GENERIC); assert( r >= 0 );
r = engine->RegisterObjectMethod("ref", "bool opEquals(const ?&in) const", asFUNCTION(CScriptHandle_EqualsVar_Generic), asCALL_GENERIC); assert( r >= 0 );
}
void RegisterScriptHandle(asIScriptEngine *engine)
{
if( strstr(asGetLibraryOptions(), "AS_MAX_PORTABILITY") )
RegisterScriptHandle_Generic(engine);
else
RegisterScriptHandle_Native(engine);
}
END_AS_NAMESPACE

View File

@@ -0,0 +1,69 @@
#ifndef SCRIPTHANDLE_H
#define SCRIPTHANDLE_H
#ifndef ANGELSCRIPT_H
// Avoid having to inform include path if header is already include before
#include <angelscript.h>
#endif
BEGIN_AS_NAMESPACE
class CScriptHandle
{
public:
// Constructors
CScriptHandle();
CScriptHandle(const CScriptHandle &other);
CScriptHandle(void *ref, asITypeInfo *type);
~CScriptHandle();
// Copy the stored value from another any object
CScriptHandle &operator=(const CScriptHandle &other);
// Set the reference
void Set(void *ref, asITypeInfo *type);
// Compare equalness
bool operator==(const CScriptHandle &o) const;
bool operator!=(const CScriptHandle &o) const;
bool Equals(void *ref, int typeId) const;
// Dynamic cast to desired handle type
void Cast(void **outRef, int typeId);
// Returns the type of the reference held
asITypeInfo *GetType() const;
int GetTypeId() const;
// Get the reference
void *GetRef();
// GC callback
void EnumReferences(asIScriptEngine *engine);
void ReleaseReferences(asIScriptEngine *engine);
protected:
// These functions need to have access to protected
// members in order to call them from the script engine
friend void Construct(CScriptHandle *self, void *ref, int typeId);
friend void RegisterScriptHandle_Native(asIScriptEngine *engine);
friend void CScriptHandle_AssignVar_Generic(asIScriptGeneric *gen);
void ReleaseHandle();
void AddRefHandle();
// These shouldn't be called directly by the
// application as they requires an active context
CScriptHandle(void *ref, int typeId);
CScriptHandle &Assign(void *ref, int typeId);
void *m_ref;
asITypeInfo *m_type;
};
void RegisterScriptHandle(asIScriptEngine *engine);
END_AS_NAMESPACE
#endif

View File

@@ -0,0 +1,987 @@
#include <string.h>
#include "scripthelper.h"
#include <assert.h>
#include <stdio.h>
#include <fstream>
#include <set>
#include <stdlib.h>
using namespace std;
BEGIN_AS_NAMESPACE
int CompareRelation(asIScriptEngine *engine, void *lobj, void *robj, int typeId, int &result)
{
// TODO: If a lot of script objects are going to be compared, e.g. when sorting an array,
// then the method id and context should be cached between calls.
int retval = -1;
asIScriptFunction *func = 0;
asITypeInfo *ti = engine->GetTypeInfoById(typeId);
if( ti )
{
// Check if the object type has a compatible opCmp method
for( asUINT n = 0; n < ti->GetMethodCount(); n++ )
{
asIScriptFunction *f = ti->GetMethodByIndex(n);
asDWORD flags;
if( strcmp(f->GetName(), "opCmp") == 0 &&
f->GetReturnTypeId(&flags) == asTYPEID_INT32 &&
flags == asTM_NONE &&
f->GetParamCount() == 1 )
{
int paramTypeId;
f->GetParam(0, &paramTypeId, &flags);
// The parameter must be an input reference of the same type
// If the reference is a inout reference, then it must also be read-only
if( !(flags & asTM_INREF) || typeId != paramTypeId || ((flags & asTM_OUTREF) && !(flags & asTM_CONST)) )
break;
// Found the method
func = f;
break;
}
}
}
if( func )
{
// Call the method
asIScriptContext *ctx = engine->CreateContext();
ctx->Prepare(func);
ctx->SetObject(lobj);
ctx->SetArgAddress(0, robj);
int r = ctx->Execute();
if( r == asEXECUTION_FINISHED )
{
result = (int)ctx->GetReturnDWord();
// The comparison was successful
retval = 0;
}
ctx->Release();
}
return retval;
}
int CompareEquality(asIScriptEngine *engine, void *lobj, void *robj, int typeId, bool &result)
{
// TODO: If a lot of script objects are going to be compared, e.g. when searching for an
// entry in a set, then the method and context should be cached between calls.
int retval = -1;
asIScriptFunction *func = 0;
asITypeInfo *ti = engine->GetTypeInfoById(typeId);
if( ti )
{
// Check if the object type has a compatible opEquals method
for( asUINT n = 0; n < ti->GetMethodCount(); n++ )
{
asIScriptFunction *f = ti->GetMethodByIndex(n);
asDWORD flags;
if( strcmp(f->GetName(), "opEquals") == 0 &&
f->GetReturnTypeId(&flags) == asTYPEID_BOOL &&
flags == asTM_NONE &&
f->GetParamCount() == 1 )
{
int paramTypeId;
f->GetParam(0, &paramTypeId, &flags);
// The parameter must be an input reference of the same type
// If the reference is a inout reference, then it must also be read-only
if( !(flags & asTM_INREF) || typeId != paramTypeId || ((flags & asTM_OUTREF) && !(flags & asTM_CONST)) )
break;
// Found the method
func = f;
break;
}
}
}
if( func )
{
// Call the method
asIScriptContext *ctx = engine->CreateContext();
ctx->Prepare(func);
ctx->SetObject(lobj);
ctx->SetArgAddress(0, robj);
int r = ctx->Execute();
if( r == asEXECUTION_FINISHED )
{
result = ctx->GetReturnByte() ? true : false;
// The comparison was successful
retval = 0;
}
ctx->Release();
}
else
{
// If the opEquals method doesn't exist, then we try with opCmp instead
int relation;
retval = CompareRelation(engine, lobj, robj, typeId, relation);
if( retval >= 0 )
result = relation == 0 ? true : false;
}
return retval;
}
int ExecuteString(asIScriptEngine *engine, const char *code, asIScriptModule *mod, asIScriptContext *ctx)
{
return ExecuteString(engine, code, 0, asTYPEID_VOID, mod, ctx);
}
int ExecuteString(asIScriptEngine *engine, const char *code, void *ref, int refTypeId, asIScriptModule *mod, asIScriptContext *ctx)
{
// Wrap the code in a function so that it can be compiled and executed
string funcCode = " ExecuteString() {\n";
funcCode += code;
funcCode += "\n;}";
// Determine the return type based on the type of the ref arg
funcCode = engine->GetTypeDeclaration(refTypeId, true) + funcCode;
// GetModule will free unused types, so to be on the safe side we'll hold on to a reference to the type
asITypeInfo *type = 0;
if( refTypeId & asTYPEID_MASK_OBJECT )
{
type = engine->GetTypeInfoById(refTypeId);
if( type )
type->AddRef();
}
// If no module was provided, get a dummy from the engine
asIScriptModule *execMod = mod ? mod : engine->GetModule("ExecuteString", asGM_ALWAYS_CREATE);
// Now it's ok to release the type
if( type )
type->Release();
// Compile the function that can be executed
asIScriptFunction *func = 0;
int r = execMod->CompileFunction("ExecuteString", funcCode.c_str(), -1, 0, &func);
if( r < 0 )
return r;
// If no context was provided, request a new one from the engine
asIScriptContext *execCtx = ctx ? ctx : engine->RequestContext();
r = execCtx->Prepare(func);
if (r >= 0)
{
// Execute the function
r = execCtx->Execute();
// Unless the provided type was void retrieve it's value
if (ref != 0 && refTypeId != asTYPEID_VOID)
{
if (refTypeId & asTYPEID_OBJHANDLE)
{
// Expect the pointer to be null to start with
assert(*reinterpret_cast<void**>(ref) == 0);
*reinterpret_cast<void**>(ref) = *reinterpret_cast<void**>(execCtx->GetAddressOfReturnValue());
engine->AddRefScriptObject(*reinterpret_cast<void**>(ref), engine->GetTypeInfoById(refTypeId));
}
else if (refTypeId & asTYPEID_MASK_OBJECT)
{
// Use the registered assignment operator to do a value assign.
// This assumes that the ref is pointing to a valid object instance.
engine->AssignScriptObject(ref, execCtx->GetAddressOfReturnValue(), engine->GetTypeInfoById(refTypeId));
}
else
{
// Copy the primitive value
memcpy(ref, execCtx->GetAddressOfReturnValue(), engine->GetSizeOfPrimitiveType(refTypeId));
}
}
}
// Clean up
func->Release();
if( !ctx ) engine->ReturnContext(execCtx);
return r;
}
int WriteConfigToFile(asIScriptEngine *engine, const char *filename)
{
ofstream strm;
strm.open(filename);
return WriteConfigToStream(engine, strm);
}
int WriteConfigToStream(asIScriptEngine *engine, ostream &strm)
{
// A helper function for escaping quotes in default arguments
struct Escape
{
static string Quotes(const char *decl)
{
string str = decl;
size_t pos = 0;
for(;;)
{
// Find " characters
pos = str.find("\"",pos);
if( pos == string::npos )
break;
// Add a \ to escape them
str.insert(pos, "\\");
pos += 2;
}
return str;
}
};
int c, n;
asDWORD currAccessMask = 0;
string currNamespace = "";
engine->SetDefaultNamespace("");
// Export the engine version, just for info
strm << "// AngelScript " << asGetLibraryVersion() << "\n";
strm << "// Lib options " << asGetLibraryOptions() << "\n";
// Export the relevant engine properties
strm << "// Engine properties\n";
for( n = 0; n < asEP_LAST_PROPERTY; n++ )
strm << "ep " << n << " " << engine->GetEngineProperty(asEEngineProp(n)) << "\n";
// Make sure the default array type is expanded to the template form
bool expandDefArrayToTempl = engine->GetEngineProperty(asEP_EXPAND_DEF_ARRAY_TO_TMPL) ? true : false;
engine->SetEngineProperty(asEP_EXPAND_DEF_ARRAY_TO_TMPL, true);
// Write enum types and their values
strm << "\n// Enums\n";
c = engine->GetEnumCount();
for( n = 0; n < c; n++ )
{
asITypeInfo *ti = engine->GetEnumByIndex(n);
asDWORD accessMask = ti->GetAccessMask();
if( accessMask != currAccessMask )
{
strm << "access " << hex << (unsigned int)(accessMask) << dec << "\n";
currAccessMask = accessMask;
}
const char *nameSpace = ti->GetNamespace();
if( nameSpace != currNamespace )
{
strm << "namespace \"" << nameSpace << "\"\n";
currNamespace = nameSpace;
engine->SetDefaultNamespace(currNamespace.c_str());
}
const char *enumName = ti->GetName();
strm << "enum " << enumName << "\n";
for( asUINT m = 0; m < ti->GetEnumValueCount(); m++ )
{
const char *valName;
int val;
valName = ti->GetEnumValueByIndex(m, &val);
strm << "enumval " << enumName << " " << valName << " " << val << "\n";
}
}
// Enumerate all types
strm << "\n// Types\n";
// Keep a list of the template types, as the methods for these need to be exported first
set<asITypeInfo*> templateTypes;
c = engine->GetObjectTypeCount();
for( n = 0; n < c; n++ )
{
asITypeInfo *type = engine->GetObjectTypeByIndex(n);
asDWORD accessMask = type->GetAccessMask();
if( accessMask != currAccessMask )
{
strm << "access " << hex << (unsigned int)(accessMask) << dec << "\n";
currAccessMask = accessMask;
}
const char *nameSpace = type->GetNamespace();
if( nameSpace != currNamespace )
{
strm << "namespace \"" << nameSpace << "\"\n";
currNamespace = nameSpace;
engine->SetDefaultNamespace(currNamespace.c_str());
}
if( type->GetFlags() & asOBJ_SCRIPT_OBJECT )
{
// This should only be interfaces
assert( type->GetSize() == 0 );
strm << "intf " << type->GetName() << "\n";
}
else
{
// Only the type flags are necessary. The application flags are application
// specific and doesn't matter to the offline compiler. The object size is also
// unnecessary for the offline compiler
strm << "objtype \"" << engine->GetTypeDeclaration(type->GetTypeId()) << "\" " << (unsigned int)(type->GetFlags() & asOBJ_MASK_VALID_FLAGS) << "\n";
// Store the template types (but not template instances)
if( (type->GetFlags() & asOBJ_TEMPLATE) && type->GetSubType() && (type->GetSubType()->GetFlags() & asOBJ_TEMPLATE_SUBTYPE) )
templateTypes.insert(type);
}
}
c = engine->GetTypedefCount();
for( n = 0; n < c; n++ )
{
asITypeInfo *ti = engine->GetTypedefByIndex(n);
const char *nameSpace = ti->GetNamespace();
if( nameSpace != currNamespace )
{
strm << "namespace \"" << nameSpace << "\"\n";
currNamespace = nameSpace;
engine->SetDefaultNamespace(currNamespace.c_str());
}
asDWORD accessMask = ti->GetAccessMask();
if( accessMask != currAccessMask )
{
strm << "access " << hex << (unsigned int)(accessMask) << dec << "\n";
currAccessMask = accessMask;
}
strm << "typedef " << ti->GetName() << " \"" << engine->GetTypeDeclaration(ti->GetTypedefTypeId()) << "\"\n";
}
c = engine->GetFuncdefCount();
for( n = 0; n < c; n++ )
{
asITypeInfo *funcDef = engine->GetFuncdefByIndex(n);
asDWORD accessMask = funcDef->GetAccessMask();
const char *nameSpace = funcDef->GetNamespace();
// Child funcdefs do not have any namespace, as they belong to the parent object
if( nameSpace && nameSpace != currNamespace )
{
strm << "namespace \"" << nameSpace << "\"\n";
currNamespace = nameSpace;
engine->SetDefaultNamespace(currNamespace.c_str());
}
if( accessMask != currAccessMask )
{
strm << "access " << hex << (unsigned int)(accessMask) << dec << "\n";
currAccessMask = accessMask;
}
strm << "funcdef \"" << funcDef->GetFuncdefSignature()->GetDeclaration() << "\"\n";
}
// A helper for writing object type members
struct TypeWriter
{
static void Write(asIScriptEngine *engine, ostream &strm, asITypeInfo *type, string &currNamespace, asDWORD &currAccessMask)
{
const char *nameSpace = type->GetNamespace();
if( nameSpace != currNamespace )
{
strm << "namespace \"" << nameSpace << "\"\n";
currNamespace = nameSpace;
engine->SetDefaultNamespace(currNamespace.c_str());
}
string typeDecl = engine->GetTypeDeclaration(type->GetTypeId());
if( type->GetFlags() & asOBJ_SCRIPT_OBJECT )
{
for( asUINT m = 0; m < type->GetMethodCount(); m++ )
{
asIScriptFunction *func = type->GetMethodByIndex(m);
asDWORD accessMask = func->GetAccessMask();
if( accessMask != currAccessMask )
{
strm << "access " << hex << (unsigned int)(accessMask) << dec << "\n";
currAccessMask = accessMask;
}
strm << "intfmthd " << typeDecl.c_str() << " \"" << Escape::Quotes(func->GetDeclaration(false)).c_str() << (func->IsProperty() ? " property" : "") << "\"\n";
}
}
else
{
asUINT m;
for( m = 0; m < type->GetFactoryCount(); m++ )
{
asIScriptFunction *func = type->GetFactoryByIndex(m);
asDWORD accessMask = func->GetAccessMask();
if( accessMask != currAccessMask )
{
strm << "access " << hex << (unsigned int)(accessMask) << dec << "\n";
currAccessMask = accessMask;
}
strm << "objbeh \"" << typeDecl.c_str() << "\" " << asBEHAVE_FACTORY << " \"" << Escape::Quotes(func->GetDeclaration(false)).c_str() << "\"\n";
}
for( m = 0; m < type->GetBehaviourCount(); m++ )
{
asEBehaviours beh;
asIScriptFunction *func = type->GetBehaviourByIndex(m, &beh);
if( beh == asBEHAVE_CONSTRUCT )
// Prefix 'void'
strm << "objbeh \"" << typeDecl.c_str() << "\" " << beh << " \"void " << Escape::Quotes(func->GetDeclaration(false)).c_str() << "\"\n";
else if( beh == asBEHAVE_DESTRUCT )
// Prefix 'void' and remove ~
strm << "objbeh \"" << typeDecl.c_str() << "\" " << beh << " \"void " << Escape::Quotes(func->GetDeclaration(false)).c_str()+1 << "\"\n";
else
strm << "objbeh \"" << typeDecl.c_str() << "\" " << beh << " \"" << Escape::Quotes(func->GetDeclaration(false)).c_str() << "\"\n";
}
for( m = 0; m < type->GetMethodCount(); m++ )
{
asIScriptFunction *func = type->GetMethodByIndex(m);
asDWORD accessMask = func->GetAccessMask();
if( accessMask != currAccessMask )
{
strm << "access " << hex << (unsigned int)(accessMask) << dec << "\n";
currAccessMask = accessMask;
}
strm << "objmthd \"" << typeDecl.c_str() << "\" \"" << Escape::Quotes(func->GetDeclaration(false)).c_str() << (func->IsProperty() ? " property" : "") << "\"\n";
}
for( m = 0; m < type->GetPropertyCount(); m++ )
{
asDWORD accessMask;
type->GetProperty(m, 0, 0, 0, 0, 0, 0, &accessMask);
if( accessMask != currAccessMask )
{
strm << "access " << hex << (unsigned int)(accessMask) << dec << "\n";
currAccessMask = accessMask;
}
strm << "objprop \"" << typeDecl.c_str() << "\" \"" << type->GetPropertyDeclaration(m) << "\"";
// Save information about composite properties
int compositeOffset;
bool isCompositeIndirect;
type->GetProperty(m, 0, 0, 0, 0, 0, 0, 0, &compositeOffset, &isCompositeIndirect);
strm << " " << compositeOffset << " " << (isCompositeIndirect ? "1" : "0") << "\n";
}
}
}
};
// Write the members of the template types, so they can be fully registered before any other type uses them
// TODO: Order the template types based on dependency to avoid failure if one type uses instances of another
strm << "\n// Template type members\n";
for( set<asITypeInfo*>::iterator it = templateTypes.begin(); it != templateTypes.end(); ++it )
{
asITypeInfo *type = *it;
TypeWriter::Write(engine, strm, type, currNamespace, currAccessMask);
}
// Write the object types members
strm << "\n// Type members\n";
c = engine->GetObjectTypeCount();
for( n = 0; n < c; n++ )
{
asITypeInfo *type = engine->GetObjectTypeByIndex(n);
if( templateTypes.find(type) == templateTypes.end() )
TypeWriter::Write(engine, strm, type, currNamespace, currAccessMask);
}
// Write functions
strm << "\n// Functions\n";
c = engine->GetGlobalFunctionCount();
for( n = 0; n < c; n++ )
{
asIScriptFunction *func = engine->GetGlobalFunctionByIndex(n);
const char *nameSpace = func->GetNamespace();
if( nameSpace != currNamespace )
{
strm << "namespace \"" << nameSpace << "\"\n";
currNamespace = nameSpace;
engine->SetDefaultNamespace(currNamespace.c_str());
}
asDWORD accessMask = func->GetAccessMask();
if( accessMask != currAccessMask )
{
strm << "access " << hex << (unsigned int)(accessMask) << dec << "\n";
currAccessMask = accessMask;
}
strm << "func \"" << Escape::Quotes(func->GetDeclaration()).c_str() << (func->IsProperty() ? " property" : "") << "\"\n";
}
// Write global properties
strm << "\n// Properties\n";
c = engine->GetGlobalPropertyCount();
for( n = 0; n < c; n++ )
{
const char *name;
int typeId;
bool isConst;
asDWORD accessMask;
const char *nameSpace;
engine->GetGlobalPropertyByIndex(n, &name, &nameSpace, &typeId, &isConst, 0, 0, &accessMask);
if( accessMask != currAccessMask )
{
strm << "access " << hex << (unsigned int)(accessMask) << dec << "\n";
currAccessMask = accessMask;
}
if( nameSpace != currNamespace )
{
strm << "namespace \"" << nameSpace << "\"\n";
currNamespace = nameSpace;
engine->SetDefaultNamespace(currNamespace.c_str());
}
strm << "prop \"" << (isConst ? "const " : "") << engine->GetTypeDeclaration(typeId) << " " << name << "\"\n";
}
// Write string factory
strm << "\n// String factory\n";
// Reset the namespace for the string factory and default array type
if ("" != currNamespace)
{
strm << "namespace \"\"\n";
currNamespace = "";
engine->SetDefaultNamespace("");
}
asDWORD flags = 0;
int typeId = engine->GetStringFactoryReturnTypeId(&flags);
if( typeId > 0 )
strm << "strfactory \"" << ((flags & asTM_CONST) ? "const " : "") << engine->GetTypeDeclaration(typeId) << ((flags & asTM_INOUTREF) ? "&" : "") << "\"\n";
// Write default array type
strm << "\n// Default array type\n";
typeId = engine->GetDefaultArrayTypeId();
if( typeId > 0 )
strm << "defarray \"" << engine->GetTypeDeclaration(typeId) << "\"\n";
// Restore original settings
engine->SetEngineProperty(asEP_EXPAND_DEF_ARRAY_TO_TMPL, expandDefArrayToTempl);
return 0;
}
int ConfigEngineFromStream(asIScriptEngine *engine, istream &strm, const char *configFile, asIStringFactory *stringFactory)
{
int r;
// Some helper functions for parsing the configuration
struct in
{
static asETokenClass GetToken(asIScriptEngine *engine, string &token, const string &text, asUINT &pos)
{
asUINT len = 0;
asETokenClass t = engine->ParseToken(&text[pos], text.length() - pos, &len);
while( (t == asTC_WHITESPACE || t == asTC_COMMENT) && pos < text.length() )
{
pos += len;
t = engine->ParseToken(&text[pos], text.length() - pos, &len);
}
token.assign(&text[pos], len);
pos += len;
return t;
}
static void ReplaceSlashQuote(string &str)
{
size_t pos = 0;
for(;;)
{
// Search for \" in the string
pos = str.find("\\\"", pos);
if( pos == string::npos )
break;
// Remove the \ character
str.erase(pos, 1);
}
}
static asUINT GetLineNumber(const string &text, asUINT pos)
{
asUINT count = 1;
for( asUINT n = 0; n < pos; n++ )
if( text[n] == '\n' )
count++;
return count;
}
};
// Since we are only going to compile the script and never actually execute it,
// we turn off the initialization of global variables, so that the compiler can
// just register dummy types and functions for the application interface.
r = engine->SetEngineProperty(asEP_INIT_GLOBAL_VARS_AFTER_BUILD, false); assert( r >= 0 );
// Read the entire file
char buffer[1000];
string config;
do {
strm.getline(buffer, 1000);
config += buffer;
config += "\n";
} while( !strm.eof() && strm.good() );
// Process the configuration file and register each entity
asUINT pos = 0;
while( pos < config.length() )
{
string token;
// TODO: The position where the initial token is found should be stored for error messages
in::GetToken(engine, token, config, pos);
if( token == "ep" )
{
string tmp;
in::GetToken(engine, tmp, config, pos);
asEEngineProp ep = asEEngineProp(atol(tmp.c_str()));
// Only set properties that affect the compiler
if( ep != asEP_COPY_SCRIPT_SECTIONS &&
ep != asEP_MAX_STACK_SIZE &&
ep != asEP_INIT_GLOBAL_VARS_AFTER_BUILD &&
ep != asEP_EXPAND_DEF_ARRAY_TO_TMPL &&
ep != asEP_AUTO_GARBAGE_COLLECT )
{
// Get the value for the property
in::GetToken(engine, tmp, config, pos);
stringstream s(tmp);
asPWORD value;
s >> value;
engine->SetEngineProperty(ep, value);
}
}
else if( token == "namespace" )
{
string ns;
in::GetToken(engine, ns, config, pos);
ns = ns.substr(1, ns.length() - 2);
r = engine->SetDefaultNamespace(ns.c_str());
if( r < 0 )
{
engine->WriteMessage(configFile, in::GetLineNumber(config, pos), 0, asMSGTYPE_ERROR, "Failed to set namespace");
return -1;
}
}
else if( token == "access" )
{
string maskStr;
in::GetToken(engine, maskStr, config, pos);
asDWORD mask = strtoul(maskStr.c_str(), 0, 16);
engine->SetDefaultAccessMask(mask);
}
else if( token == "objtype" )
{
string name, flags;
in::GetToken(engine, name, config, pos);
name = name.substr(1, name.length() - 2);
in::GetToken(engine, flags, config, pos);
// The size of the value type doesn't matter, because the
// engine must adjust it anyway for different platforms
r = engine->RegisterObjectType(name.c_str(), (atol(flags.c_str()) & asOBJ_VALUE) ? 1 : 0, atol(flags.c_str()));
if( r < 0 )
{
engine->WriteMessage(configFile, in::GetLineNumber(config, pos), 0, asMSGTYPE_ERROR, "Failed to register object type");
return -1;
}
}
else if( token == "objbeh" )
{
string name, behaviour, decl;
in::GetToken(engine, name, config, pos);
name = name.substr(1, name.length() - 2);
in::GetToken(engine, behaviour, config, pos);
in::GetToken(engine, decl, config, pos);
decl = decl.substr(1, decl.length() - 2);
in::ReplaceSlashQuote(decl);
// Remove the $ that the engine prefixes the behaviours with
size_t n = decl.find("$");
if( n != string::npos )
decl[n] = ' ';
asEBehaviours behave = static_cast<asEBehaviours>(atol(behaviour.c_str()));
if( behave == asBEHAVE_TEMPLATE_CALLBACK )
{
// TODO: How can we let the compiler register this? Maybe through a plug-in system? Or maybe by implementing the callback as a script itself
engine->WriteMessage(configFile, in::GetLineNumber(config, pos), 0, asMSGTYPE_WARNING, "Cannot register template callback without the actual implementation");
}
else
{
r = engine->RegisterObjectBehaviour(name.c_str(), behave, decl.c_str(), asFUNCTION(0), asCALL_GENERIC);
if( r < 0 )
{
engine->WriteMessage(configFile, in::GetLineNumber(config, pos), 0, asMSGTYPE_ERROR, "Failed to register behaviour");
return -1;
}
}
}
else if( token == "objmthd" )
{
string name, decl;
in::GetToken(engine, name, config, pos);
name = name.substr(1, name.length() - 2);
in::GetToken(engine, decl, config, pos);
decl = decl.substr(1, decl.length() - 2);
in::ReplaceSlashQuote(decl);
r = engine->RegisterObjectMethod(name.c_str(), decl.c_str(), asFUNCTION(0), asCALL_GENERIC);
if( r < 0 )
{
engine->WriteMessage(configFile, in::GetLineNumber(config, pos), 0, asMSGTYPE_ERROR, "Failed to register object method");
return -1;
}
}
else if( token == "objprop" )
{
string name, decl, compositeOffset, isCompositeIndirect;
in::GetToken(engine, name, config, pos);
name = name.substr(1, name.length() - 2);
in::GetToken(engine, decl, config, pos);
decl = decl.substr(1, decl.length() - 2);
in::GetToken(engine, compositeOffset, config, pos);
in::GetToken(engine, isCompositeIndirect, config, pos);
asITypeInfo *type = engine->GetTypeInfoById(engine->GetTypeIdByDecl(name.c_str()));
if( type == 0 )
{
engine->WriteMessage(configFile, in::GetLineNumber(config, pos), 0, asMSGTYPE_ERROR, "Type doesn't exist for property registration");
return -1;
}
// All properties must have different offsets in order to make them
// distinct, so we simply register them with an incremental offset
r = engine->RegisterObjectProperty(name.c_str(), decl.c_str(), type->GetPropertyCount(), compositeOffset != "0" ? type->GetPropertyCount() : 0, isCompositeIndirect != "0");
if( r < 0 )
{
engine->WriteMessage(configFile, in::GetLineNumber(config, pos), 0, asMSGTYPE_ERROR, "Failed to register object property");
return -1;
}
}
else if( token == "intf" )
{
string name, size, flags;
in::GetToken(engine, name, config, pos);
r = engine->RegisterInterface(name.c_str());
if( r < 0 )
{
engine->WriteMessage(configFile, in::GetLineNumber(config, pos), 0, asMSGTYPE_ERROR, "Failed to register interface");
return -1;
}
}
else if( token == "intfmthd" )
{
string name, decl;
in::GetToken(engine, name, config, pos);
in::GetToken(engine, decl, config, pos);
decl = decl.substr(1, decl.length() - 2);
in::ReplaceSlashQuote(decl);
r = engine->RegisterInterfaceMethod(name.c_str(), decl.c_str());
if( r < 0 )
{
engine->WriteMessage(configFile, in::GetLineNumber(config, pos), 0, asMSGTYPE_ERROR, "Failed to register interface method");
return -1;
}
}
else if( token == "func" )
{
string decl;
in::GetToken(engine, decl, config, pos);
decl = decl.substr(1, decl.length() - 2);
in::ReplaceSlashQuote(decl);
r = engine->RegisterGlobalFunction(decl.c_str(), asFUNCTION(0), asCALL_GENERIC);
if( r < 0 )
{
engine->WriteMessage(configFile, in::GetLineNumber(config, pos), 0, asMSGTYPE_ERROR, "Failed to register global function");
return -1;
}
}
else if( token == "prop" )
{
string decl;
in::GetToken(engine, decl, config, pos);
decl = decl.substr(1, decl.length() - 2);
// All properties must have different offsets in order to make them
// distinct, so we simply register them with an incremental offset.
// The pointer must also be non-null so we add 1 to have a value.
r = engine->RegisterGlobalProperty(decl.c_str(), reinterpret_cast<void*>(asPWORD(engine->GetGlobalPropertyCount()+1)));
if( r < 0 )
{
engine->WriteMessage(configFile, in::GetLineNumber(config, pos), 0, asMSGTYPE_ERROR, "Failed to register global property");
return -1;
}
}
else if( token == "strfactory" )
{
string type;
in::GetToken(engine, type, config, pos);
type = type.substr(1, type.length() - 2);
if (stringFactory == 0)
{
engine->WriteMessage(configFile, in::GetLineNumber(config, pos), 0, asMSGTYPE_WARNING, "Cannot register string factory without the actual implementation");
return -1;
}
else
{
r = engine->RegisterStringFactory(type.c_str(), stringFactory);
if (r < 0)
{
engine->WriteMessage(configFile, in::GetLineNumber(config, pos), 0, asMSGTYPE_ERROR, "Failed to register string factory");
return -1;
}
}
}
else if( token == "defarray" )
{
string type;
in::GetToken(engine, type, config, pos);
type = type.substr(1, type.length() - 2);
r = engine->RegisterDefaultArrayType(type.c_str());
if( r < 0 )
{
engine->WriteMessage(configFile, in::GetLineNumber(config, pos), 0, asMSGTYPE_ERROR, "Failed to register the default array type");
return -1;
}
}
else if( token == "enum" )
{
string type;
in::GetToken(engine, type, config, pos);
r = engine->RegisterEnum(type.c_str());
if( r < 0 )
{
engine->WriteMessage(configFile, in::GetLineNumber(config, pos), 0, asMSGTYPE_ERROR, "Failed to register enum type");
return -1;
}
}
else if( token == "enumval" )
{
string type, name, value;
in::GetToken(engine, type, config, pos);
in::GetToken(engine, name, config, pos);
in::GetToken(engine, value, config, pos);
r = engine->RegisterEnumValue(type.c_str(), name.c_str(), atol(value.c_str()));
if( r < 0 )
{
engine->WriteMessage(configFile, in::GetLineNumber(config, pos), 0, asMSGTYPE_ERROR, "Failed to register enum value");
return -1;
}
}
else if( token == "typedef" )
{
string type, decl;
in::GetToken(engine, type, config, pos);
in::GetToken(engine, decl, config, pos);
decl = decl.substr(1, decl.length() - 2);
r = engine->RegisterTypedef(type.c_str(), decl.c_str());
if( r < 0 )
{
engine->WriteMessage(configFile, in::GetLineNumber(config, pos), 0, asMSGTYPE_ERROR, "Failed to register typedef");
return -1;
}
}
else if( token == "funcdef" )
{
string decl;
in::GetToken(engine, decl, config, pos);
decl = decl.substr(1, decl.length() - 2);
r = engine->RegisterFuncdef(decl.c_str());
if( r < 0 )
{
engine->WriteMessage(configFile, in::GetLineNumber(config, pos), 0, asMSGTYPE_ERROR, "Failed to register funcdef");
return -1;
}
}
}
return 0;
}
string GetExceptionInfo(asIScriptContext *ctx, bool showStack)
{
if( ctx->GetState() != asEXECUTION_EXCEPTION ) return "";
stringstream text;
const asIScriptFunction *function = ctx->GetExceptionFunction();
text << "func: " << function->GetDeclaration() << "\n";
text << "modl: " << (function->GetModuleName() ? function->GetModuleName() : "") << "\n";
text << "sect: " << (function->GetScriptSectionName() ? function->GetScriptSectionName() : "") << "\n";
text << "line: " << ctx->GetExceptionLineNumber() << "\n";
text << "desc: " << ctx->GetExceptionString() << "\n";
if( showStack )
{
text << "--- call stack ---\n";
for( asUINT n = 1; n < ctx->GetCallstackSize(); n++ )
{
function = ctx->GetFunction(n);
if( function )
{
if( function->GetFuncType() == asFUNC_SCRIPT )
{
text << (function->GetScriptSectionName() ? function->GetScriptSectionName() : "") << " (" << ctx->GetLineNumber(n) << "): " << function->GetDeclaration() << "\n";
}
else
{
// The context is being reused by the application for a nested call
text << "{...application...}: " << function->GetDeclaration() << "\n";
}
}
else
{
// The context is being reused by the script engine for a nested call
text << "{...script engine...}\n";
}
}
}
return text.str();
}
void ScriptThrow(const string &msg)
{
asIScriptContext *ctx = asGetActiveContext();
if (ctx)
ctx->SetException(msg.c_str());
}
string ScriptGetExceptionInfo()
{
asIScriptContext *ctx = asGetActiveContext();
if (!ctx)
return "";
const char *msg = ctx->GetExceptionString();
if (msg == 0)
return "";
return string(msg);
}
void RegisterExceptionRoutines(asIScriptEngine *engine)
{
int r;
// The string type must be available
assert(engine->GetTypeInfoByDecl("string"));
r = engine->RegisterGlobalFunction("void throw(const string &in)", asFUNCTION(ScriptThrow), asCALL_CDECL); assert(r >= 0);
r = engine->RegisterGlobalFunction("string getExceptionInfo()", asFUNCTION(ScriptGetExceptionInfo), asCALL_CDECL); assert(r >= 0);
}
END_AS_NAMESPACE

View File

@@ -0,0 +1,53 @@
#ifndef SCRIPTHELPER_H
#define SCRIPTHELPER_H
#include <sstream>
#include <string>
#ifndef ANGELSCRIPT_H
// Avoid having to inform include path if header is already include before
#include <angelscript.h>
#endif
BEGIN_AS_NAMESPACE
// Compare relation between two objects of the same type
int CompareRelation(asIScriptEngine *engine, void *lobj, void *robj, int typeId, int &result);
// Compare equality between two objects of the same type
int CompareEquality(asIScriptEngine *engine, void *lobj, void *robj, int typeId, bool &result);
// Compile and execute simple statements
// The module is optional. If given the statements can access the entities compiled in the module.
// The caller can optionally provide its own context, for example if a context should be reused.
int ExecuteString(asIScriptEngine *engine, const char *code, asIScriptModule *mod = 0, asIScriptContext *ctx = 0);
// Compile and execute simple statements with option of return value
// The module is optional. If given the statements can access the entitites compiled in the module.
// The caller can optionally provide its own context, for example if a context should be reused.
int ExecuteString(asIScriptEngine *engine, const char *code, void *ret, int retTypeId, asIScriptModule *mod = 0, asIScriptContext *ctx = 0);
// Write the registered application interface to a file for an offline compiler.
// The format is compatible with the offline compiler in /sdk/samples/asbuild/.
int WriteConfigToFile(asIScriptEngine *engine, const char *filename);
// Write the registered application interface to a text stream.
int WriteConfigToStream(asIScriptEngine *engine, std::ostream &strm);
// Loads an interface from a text stream and configures the engine with it. This will not
// set the correct function pointers, so it is not possible to use this engine to execute
// scripts, but it can be used to compile scripts and save the byte code.
int ConfigEngineFromStream(asIScriptEngine *engine, std::istream &strm, const char *nameOfStream = "config", asIStringFactory *stringFactory = 0);
// Format the details of the script exception into a human readable text
std::string GetExceptionInfo(asIScriptContext *ctx, bool showStack = false);
// Register the exception routines
// 'void throw(const string &msg)'
// 'string getExceptionInfo()'
void RegisterExceptionRoutines(asIScriptEngine *engine);
END_AS_NAMESPACE
#endif

View File

@@ -0,0 +1,347 @@
#include <assert.h>
#include <math.h>
#include <float.h>
#include <string.h>
#include "scriptmath.h"
#ifdef __BORLANDC__
#include <cmath>
// The C++Builder RTL doesn't pull the *f functions into the global namespace per default.
using namespace std;
#if __BORLANDC__ < 0x580
// C++Builder 6 and earlier don't come with any *f variants of the math functions at all.
inline float cosf (float arg) { return std::cos (arg); }
inline float sinf (float arg) { return std::sin (arg); }
inline float tanf (float arg) { return std::tan (arg); }
inline float atan2f (float y, float x) { return std::atan2 (y, x); }
inline float logf (float arg) { return std::log (arg); }
inline float powf (float x, float y) { return std::pow (x, y); }
inline float sqrtf (float arg) { return std::sqrt (arg); }
#endif
// C++Builder doesn't define most of the non-standard float-specific math functions with
// "*f" suffix; instead it provides overloads for the standard math functions which take
// "float" arguments.
inline float acosf (float arg) { return std::acos (arg); }
inline float asinf (float arg) { return std::asin (arg); }
inline float atanf (float arg) { return std::atan (arg); }
inline float coshf (float arg) { return std::cosh (arg); }
inline float sinhf (float arg) { return std::sinh (arg); }
inline float tanhf (float arg) { return std::tanh (arg); }
inline float log10f (float arg) { return std::log10 (arg); }
inline float ceilf (float arg) { return std::ceil (arg); }
inline float fabsf (float arg) { return std::fabs (arg); }
inline float floorf (float arg) { return std::floor (arg); }
// C++Builder doesn't define a non-standard "modff" function but rather an overload of "modf"
// for float arguments. However, BCC's float overload of fmod() is broken (QC #74816; fixed
// in C++Builder 2010).
inline float modff (float x, float *y)
{
double d;
float f = (float) modf((double) x, &d);
*y = (float) d;
return f;
}
#endif
BEGIN_AS_NAMESPACE
// Determine whether the float version should be registered, or the double version
#ifndef AS_USE_FLOAT
#if !defined(_WIN32_WCE) // WinCE doesn't have the float versions of the math functions
#define AS_USE_FLOAT 1
#endif
#endif
// The modf function doesn't seem very intuitive, so I'm writing this
// function that simply returns the fractional part of the float value
#if AS_USE_FLOAT
float fractionf(float v)
{
float intPart;
return modff(v, &intPart);
}
#else
double fraction(double v)
{
double intPart;
return modf(v, &intPart);
}
#endif
// As AngelScript doesn't allow bitwise manipulation of float types we'll provide a couple of
// functions for converting float values to IEEE 754 formatted values etc. This also allow us to
// provide a platform agnostic representation to the script so the scripts don't have to worry
// about whether the CPU uses IEEE 754 floats or some other representation
float fpFromIEEE(asUINT raw)
{
// TODO: Identify CPU family to provide proper conversion
// if the CPU doesn't natively use IEEE style floats
return *reinterpret_cast<float*>(&raw);
}
asUINT fpToIEEE(float fp)
{
return *reinterpret_cast<asUINT*>(&fp);
}
double fpFromIEEE(asQWORD raw)
{
return *reinterpret_cast<double*>(&raw);
}
asQWORD fpToIEEE(double fp)
{
return *reinterpret_cast<asQWORD*>(&fp);
}
// closeTo() is used to determine if the binary representation of two numbers are
// relatively close to each other. Numerical errors due to rounding errors build
// up over many operations, so it is almost impossible to get exact numbers and
// this is where closeTo() comes in.
//
// It shouldn't be used to determine if two numbers are mathematically close to
// each other.
//
// ref: http://www.cygnus-software.com/papers/comparingfloats/comparingfloats.htm
// ref: http://www.gamedev.net/topic/653449-scriptmath-and-closeto/
bool closeTo(float a, float b, float epsilon)
{
// Equal numbers and infinity will return immediately
if( a == b ) return true;
// When very close to 0, we can use the absolute comparison
float diff = fabsf(a - b);
if( (a == 0 || b == 0) && (diff < epsilon) )
return true;
// Otherwise we need to use relative comparison to account for precision
return diff / (fabs(a) + fabs(b)) < epsilon;
}
bool closeTo(double a, double b, double epsilon)
{
if( a == b ) return true;
double diff = fabs(a - b);
if( (a == 0 || b == 0) && (diff < epsilon) )
return true;
return diff / (fabs(a) + fabs(b)) < epsilon;
}
void RegisterScriptMath_Native(asIScriptEngine *engine)
{
int r;
// Conversion between floating point and IEEE bits representations
r = engine->RegisterGlobalFunction("float fpFromIEEE(uint)", asFUNCTIONPR(fpFromIEEE, (asUINT), float), asCALL_CDECL); assert( r >= 0 );
r = engine->RegisterGlobalFunction("uint fpToIEEE(float)", asFUNCTIONPR(fpToIEEE, (float), asUINT), asCALL_CDECL); assert( r >= 0 );
r = engine->RegisterGlobalFunction("double fpFromIEEE(uint64)", asFUNCTIONPR(fpFromIEEE, (asQWORD), double), asCALL_CDECL); assert( r >= 0 );
r = engine->RegisterGlobalFunction("uint64 fpToIEEE(double)", asFUNCTIONPR(fpToIEEE, (double), asQWORD), asCALL_CDECL); assert( r >= 0 );
// Close to comparison with epsilon
r = engine->RegisterGlobalFunction("bool closeTo(float, float, float = 0.00001f)", asFUNCTIONPR(closeTo, (float, float, float), bool), asCALL_CDECL); assert( r >= 0 );
r = engine->RegisterGlobalFunction("bool closeTo(double, double, double = 0.0000000001)", asFUNCTIONPR(closeTo, (double, double, double), bool), asCALL_CDECL); assert( r >= 0 );
#if AS_USE_FLOAT
// Trigonometric functions
r = engine->RegisterGlobalFunction("float cos(float)", asFUNCTIONPR(cosf, (float), float), asCALL_CDECL); assert( r >= 0 );
r = engine->RegisterGlobalFunction("float sin(float)", asFUNCTIONPR(sinf, (float), float), asCALL_CDECL); assert( r >= 0 );
r = engine->RegisterGlobalFunction("float tan(float)", asFUNCTIONPR(tanf, (float), float), asCALL_CDECL); assert( r >= 0 );
r = engine->RegisterGlobalFunction("float acos(float)", asFUNCTIONPR(acosf, (float), float), asCALL_CDECL); assert( r >= 0 );
r = engine->RegisterGlobalFunction("float asin(float)", asFUNCTIONPR(asinf, (float), float), asCALL_CDECL); assert( r >= 0 );
r = engine->RegisterGlobalFunction("float atan(float)", asFUNCTIONPR(atanf, (float), float), asCALL_CDECL); assert( r >= 0 );
r = engine->RegisterGlobalFunction("float atan2(float,float)", asFUNCTIONPR(atan2f, (float, float), float), asCALL_CDECL); assert( r >= 0 );
// Hyberbolic functions
r = engine->RegisterGlobalFunction("float cosh(float)", asFUNCTIONPR(coshf, (float), float), asCALL_CDECL); assert( r >= 0 );
r = engine->RegisterGlobalFunction("float sinh(float)", asFUNCTIONPR(sinhf, (float), float), asCALL_CDECL); assert( r >= 0 );
r = engine->RegisterGlobalFunction("float tanh(float)", asFUNCTIONPR(tanhf, (float), float), asCALL_CDECL); assert( r >= 0 );
// Exponential and logarithmic functions
r = engine->RegisterGlobalFunction("float log(float)", asFUNCTIONPR(logf, (float), float), asCALL_CDECL); assert( r >= 0 );
r = engine->RegisterGlobalFunction("float log10(float)", asFUNCTIONPR(log10f, (float), float), asCALL_CDECL); assert( r >= 0 );
// Power functions
r = engine->RegisterGlobalFunction("float pow(float, float)", asFUNCTIONPR(powf, (float, float), float), asCALL_CDECL); assert( r >= 0 );
r = engine->RegisterGlobalFunction("float sqrt(float)", asFUNCTIONPR(sqrtf, (float), float), asCALL_CDECL); assert( r >= 0 );
// Nearest integer, absolute value, and remainder functions
r = engine->RegisterGlobalFunction("float ceil(float)", asFUNCTIONPR(ceilf, (float), float), asCALL_CDECL); assert( r >= 0 );
r = engine->RegisterGlobalFunction("float abs(float)", asFUNCTIONPR(fabsf, (float), float), asCALL_CDECL); assert( r >= 0 );
r = engine->RegisterGlobalFunction("float floor(float)", asFUNCTIONPR(floorf, (float), float), asCALL_CDECL); assert( r >= 0 );
r = engine->RegisterGlobalFunction("float fraction(float)", asFUNCTIONPR(fractionf, (float), float), asCALL_CDECL); assert( r >= 0 );
// Don't register modf because AngelScript already supports the % operator
#else
// double versions of the same
r = engine->RegisterGlobalFunction("double cos(double)", asFUNCTIONPR(cos, (double), double), asCALL_CDECL); assert( r >= 0 );
r = engine->RegisterGlobalFunction("double sin(double)", asFUNCTIONPR(sin, (double), double), asCALL_CDECL); assert( r >= 0 );
r = engine->RegisterGlobalFunction("double tan(double)", asFUNCTIONPR(tan, (double), double), asCALL_CDECL); assert( r >= 0 );
r = engine->RegisterGlobalFunction("double acos(double)", asFUNCTIONPR(acos, (double), double), asCALL_CDECL); assert( r >= 0 );
r = engine->RegisterGlobalFunction("double asin(double)", asFUNCTIONPR(asin, (double), double), asCALL_CDECL); assert( r >= 0 );
r = engine->RegisterGlobalFunction("double atan(double)", asFUNCTIONPR(atan, (double), double), asCALL_CDECL); assert( r >= 0 );
r = engine->RegisterGlobalFunction("double atan2(double,double)", asFUNCTIONPR(atan2, (double, double), double), asCALL_CDECL); assert( r >= 0 );
r = engine->RegisterGlobalFunction("double cosh(double)", asFUNCTIONPR(cosh, (double), double), asCALL_CDECL); assert( r >= 0 );
r = engine->RegisterGlobalFunction("double sinh(double)", asFUNCTIONPR(sinh, (double), double), asCALL_CDECL); assert( r >= 0 );
r = engine->RegisterGlobalFunction("double tanh(double)", asFUNCTIONPR(tanh, (double), double), asCALL_CDECL); assert( r >= 0 );
r = engine->RegisterGlobalFunction("double log(double)", asFUNCTIONPR(log, (double), double), asCALL_CDECL); assert( r >= 0 );
r = engine->RegisterGlobalFunction("double log10(double)", asFUNCTIONPR(log10, (double), double), asCALL_CDECL); assert( r >= 0 );
r = engine->RegisterGlobalFunction("double pow(double, double)", asFUNCTIONPR(pow, (double, double), double), asCALL_CDECL); assert( r >= 0 );
r = engine->RegisterGlobalFunction("double sqrt(double)", asFUNCTIONPR(sqrt, (double), double), asCALL_CDECL); assert( r >= 0 );
r = engine->RegisterGlobalFunction("double ceil(double)", asFUNCTIONPR(ceil, (double), double), asCALL_CDECL); assert( r >= 0 );
r = engine->RegisterGlobalFunction("double abs(double)", asFUNCTIONPR(fabs, (double), double), asCALL_CDECL); assert( r >= 0 );
r = engine->RegisterGlobalFunction("double floor(double)", asFUNCTIONPR(floor, (double), double), asCALL_CDECL); assert( r >= 0 );
r = engine->RegisterGlobalFunction("double fraction(double)", asFUNCTIONPR(fraction, (double), double), asCALL_CDECL); assert( r >= 0 );
#endif
}
#if AS_USE_FLOAT
// This macro creates simple generic wrappers for functions of type 'float func(float)'
#define GENERICff(x) \
void x##_generic(asIScriptGeneric *gen) \
{ \
float f = *(float*)gen->GetAddressOfArg(0); \
*(float*)gen->GetAddressOfReturnLocation() = x(f); \
}
GENERICff(cosf)
GENERICff(sinf)
GENERICff(tanf)
GENERICff(acosf)
GENERICff(asinf)
GENERICff(atanf)
GENERICff(coshf)
GENERICff(sinhf)
GENERICff(tanhf)
GENERICff(logf)
GENERICff(log10f)
GENERICff(sqrtf)
GENERICff(ceilf)
GENERICff(fabsf)
GENERICff(floorf)
GENERICff(fractionf)
void powf_generic(asIScriptGeneric *gen)
{
float f1 = *(float*)gen->GetAddressOfArg(0);
float f2 = *(float*)gen->GetAddressOfArg(1);
*(float*)gen->GetAddressOfReturnLocation() = powf(f1, f2);
}
void atan2f_generic(asIScriptGeneric *gen)
{
float f1 = *(float*)gen->GetAddressOfArg(0);
float f2 = *(float*)gen->GetAddressOfArg(1);
*(float*)gen->GetAddressOfReturnLocation() = atan2f(f1, f2);
}
#else
// This macro creates simple generic wrappers for functions of type 'double func(double)'
#define GENERICdd(x) \
void x##_generic(asIScriptGeneric *gen) \
{ \
double f = *(double*)gen->GetAddressOfArg(0); \
*(double*)gen->GetAddressOfReturnLocation() = x(f); \
}
GENERICdd(cos)
GENERICdd(sin)
GENERICdd(tan)
GENERICdd(acos)
GENERICdd(asin)
GENERICdd(atan)
GENERICdd(cosh)
GENERICdd(sinh)
GENERICdd(tanh)
GENERICdd(log)
GENERICdd(log10)
GENERICdd(sqrt)
GENERICdd(ceil)
GENERICdd(fabs)
GENERICdd(floor)
GENERICdd(fraction)
void pow_generic(asIScriptGeneric *gen)
{
double f1 = *(double*)gen->GetAddressOfArg(0);
double f2 = *(double*)gen->GetAddressOfArg(1);
*(double*)gen->GetAddressOfReturnLocation() = pow(f1, f2);
}
void atan2_generic(asIScriptGeneric *gen)
{
double f1 = *(double*)gen->GetAddressOfArg(0);
double f2 = *(double*)gen->GetAddressOfArg(1);
*(double*)gen->GetAddressOfReturnLocation() = atan2(f1, f2);
}
#endif
void RegisterScriptMath_Generic(asIScriptEngine *engine)
{
int r;
#if AS_USE_FLOAT
// Trigonometric functions
r = engine->RegisterGlobalFunction("float cos(float)", asFUNCTION(cosf_generic), asCALL_GENERIC); assert( r >= 0 );
r = engine->RegisterGlobalFunction("float sin(float)", asFUNCTION(sinf_generic), asCALL_GENERIC); assert( r >= 0 );
r = engine->RegisterGlobalFunction("float tan(float)", asFUNCTION(tanf_generic), asCALL_GENERIC); assert( r >= 0 );
r = engine->RegisterGlobalFunction("float acos(float)", asFUNCTION(acosf_generic), asCALL_GENERIC); assert( r >= 0 );
r = engine->RegisterGlobalFunction("float asin(float)", asFUNCTION(asinf_generic), asCALL_GENERIC); assert( r >= 0 );
r = engine->RegisterGlobalFunction("float atan(float)", asFUNCTION(atanf_generic), asCALL_GENERIC); assert( r >= 0 );
r = engine->RegisterGlobalFunction("float atan2(float,float)", asFUNCTION(atan2f_generic), asCALL_GENERIC); assert( r >= 0 );
// Hyberbolic functions
r = engine->RegisterGlobalFunction("float cosh(float)", asFUNCTION(coshf_generic), asCALL_GENERIC); assert( r >= 0 );
r = engine->RegisterGlobalFunction("float sinh(float)", asFUNCTION(sinhf_generic), asCALL_GENERIC); assert( r >= 0 );
r = engine->RegisterGlobalFunction("float tanh(float)", asFUNCTION(tanhf_generic), asCALL_GENERIC); assert( r >= 0 );
// Exponential and logarithmic functions
r = engine->RegisterGlobalFunction("float log(float)", asFUNCTION(logf_generic), asCALL_GENERIC); assert( r >= 0 );
r = engine->RegisterGlobalFunction("float log10(float)", asFUNCTION(log10f_generic), asCALL_GENERIC); assert( r >= 0 );
// Power functions
r = engine->RegisterGlobalFunction("float pow(float, float)", asFUNCTION(powf_generic), asCALL_GENERIC); assert( r >= 0 );
r = engine->RegisterGlobalFunction("float sqrt(float)", asFUNCTION(sqrtf_generic), asCALL_GENERIC); assert( r >= 0 );
// Nearest integer, absolute value, and remainder functions
r = engine->RegisterGlobalFunction("float ceil(float)", asFUNCTION(ceilf_generic), asCALL_GENERIC); assert( r >= 0 );
r = engine->RegisterGlobalFunction("float abs(float)", asFUNCTION(fabsf_generic), asCALL_GENERIC); assert( r >= 0 );
r = engine->RegisterGlobalFunction("float floor(float)", asFUNCTION(floorf_generic), asCALL_GENERIC); assert( r >= 0 );
r = engine->RegisterGlobalFunction("float fraction(float)", asFUNCTION(fractionf_generic), asCALL_GENERIC); assert( r >= 0 );
// Don't register modf because AngelScript already supports the % operator
#else
// double versions of the same
r = engine->RegisterGlobalFunction("double cos(double)", asFUNCTION(cos_generic), asCALL_GENERIC); assert( r >= 0 );
r = engine->RegisterGlobalFunction("double sin(double)", asFUNCTION(sin_generic), asCALL_GENERIC); assert( r >= 0 );
r = engine->RegisterGlobalFunction("double tan(double)", asFUNCTION(tan_generic), asCALL_GENERIC); assert( r >= 0 );
r = engine->RegisterGlobalFunction("double acos(double)", asFUNCTION(acos_generic), asCALL_GENERIC); assert( r >= 0 );
r = engine->RegisterGlobalFunction("double asin(double)", asFUNCTION(asin_generic), asCALL_GENERIC); assert( r >= 0 );
r = engine->RegisterGlobalFunction("double atan(double)", asFUNCTION(atan_generic), asCALL_GENERIC); assert( r >= 0 );
r = engine->RegisterGlobalFunction("double atan2(double,double)", asFUNCTION(atan2_generic), asCALL_GENERIC); assert( r >= 0 );
r = engine->RegisterGlobalFunction("double cosh(double)", asFUNCTION(cosh_generic), asCALL_GENERIC); assert( r >= 0 );
r = engine->RegisterGlobalFunction("double sinh(double)", asFUNCTION(sinh_generic), asCALL_GENERIC); assert( r >= 0 );
r = engine->RegisterGlobalFunction("double tanh(double)", asFUNCTION(tanh_generic), asCALL_GENERIC); assert( r >= 0 );
r = engine->RegisterGlobalFunction("double log(double)", asFUNCTION(log_generic), asCALL_GENERIC); assert( r >= 0 );
r = engine->RegisterGlobalFunction("double log10(double)", asFUNCTION(log10_generic), asCALL_GENERIC); assert( r >= 0 );
r = engine->RegisterGlobalFunction("double pow(double, double)", asFUNCTION(pow_generic), asCALL_GENERIC); assert( r >= 0 );
r = engine->RegisterGlobalFunction("double sqrt(double)", asFUNCTION(sqrt_generic), asCALL_GENERIC); assert( r >= 0 );
r = engine->RegisterGlobalFunction("double ceil(double)", asFUNCTION(ceil_generic), asCALL_GENERIC); assert( r >= 0 );
r = engine->RegisterGlobalFunction("double abs(double)", asFUNCTION(fabs_generic), asCALL_GENERIC); assert( r >= 0 );
r = engine->RegisterGlobalFunction("double floor(double)", asFUNCTION(floor_generic), asCALL_GENERIC); assert( r >= 0 );
r = engine->RegisterGlobalFunction("double fraction(double)", asFUNCTION(fraction_generic), asCALL_GENERIC); assert( r >= 0 );
#endif
}
void RegisterScriptMath(asIScriptEngine *engine)
{
if( strstr(asGetLibraryOptions(), "AS_MAX_PORTABILITY") )
RegisterScriptMath_Generic(engine);
else
RegisterScriptMath_Native(engine);
}
END_AS_NAMESPACE

View File

@@ -0,0 +1,26 @@
#ifndef SCRIPTMATH_H
#define SCRIPTMATH_H
#ifndef ANGELSCRIPT_H
// Avoid having to inform include path if header is already include before
#include <angelscript.h>
#endif
BEGIN_AS_NAMESPACE
// This function will determine the configuration of the engine
// and use one of the two functions below to register the math functions
void RegisterScriptMath(asIScriptEngine *engine);
// Call this function to register the math functions
// using native calling conventions
void RegisterScriptMath_Native(asIScriptEngine *engine);
// Use this one instead if native calling conventions
// are not supported on the target platform
void RegisterScriptMath_Generic(asIScriptEngine *engine);
END_AS_NAMESPACE
#endif

View File

@@ -0,0 +1,222 @@
#include <assert.h>
#include <string.h> // strstr
#include <new> // new()
#include <math.h>
#include "scriptmathcomplex.h"
#ifdef __BORLANDC__
// C++Builder doesn't define a non-standard "sqrtf" function but rather an overload of "sqrt"
// for float arguments.
inline float sqrtf (float x) { return sqrt (x); }
#endif
BEGIN_AS_NAMESPACE
Complex::Complex()
{
r = 0;
i = 0;
}
Complex::Complex(const Complex &other)
{
r = other.r;
i = other.i;
}
Complex::Complex(float _r, float _i)
{
r = _r;
i = _i;
}
bool Complex::operator==(const Complex &o) const
{
return (r == o.r) && (i == o.i);
}
bool Complex::operator!=(const Complex &o) const
{
return !(*this == o);
}
Complex &Complex::operator=(const Complex &other)
{
r = other.r;
i = other.i;
return *this;
}
Complex &Complex::operator+=(const Complex &other)
{
r += other.r;
i += other.i;
return *this;
}
Complex &Complex::operator-=(const Complex &other)
{
r -= other.r;
i -= other.i;
return *this;
}
Complex &Complex::operator*=(const Complex &other)
{
*this = *this * other;
return *this;
}
Complex &Complex::operator/=(const Complex &other)
{
*this = *this / other;
return *this;
}
float Complex::squaredLength() const
{
return r*r + i*i;
}
float Complex::length() const
{
return sqrtf(squaredLength());
}
Complex Complex::operator+(const Complex &other) const
{
return Complex(r + other.r, i + other.i);
}
Complex Complex::operator-(const Complex &other) const
{
return Complex(r - other.r, i + other.i);
}
Complex Complex::operator*(const Complex &other) const
{
return Complex(r*other.r - i*other.i, r*other.i + i*other.r);
}
Complex Complex::operator/(const Complex &other) const
{
float squaredLen = other.squaredLength();
if( squaredLen == 0 ) return Complex(0,0);
return Complex((r*other.r + i*other.i)/squaredLen, (i*other.r - r*other.i)/squaredLen);
}
//-----------------------
// Swizzle operators
//-----------------------
Complex Complex::get_ri() const
{
return *this;
}
Complex Complex::get_ir() const
{
return Complex(r,i);
}
void Complex::set_ri(const Complex &o)
{
*this = o;
}
void Complex::set_ir(const Complex &o)
{
r = o.i;
i = o.r;
}
//-----------------------
// AngelScript functions
//-----------------------
static void ComplexDefaultConstructor(Complex *self)
{
new(self) Complex();
}
static void ComplexCopyConstructor(const Complex &other, Complex *self)
{
new(self) Complex(other);
}
static void ComplexConvConstructor(float r, Complex *self)
{
new(self) Complex(r);
}
static void ComplexInitConstructor(float r, float i, Complex *self)
{
new(self) Complex(r,i);
}
static void ComplexListConstructor(float *list, Complex *self)
{
new(self) Complex(list[0], list[1]);
}
//--------------------------------
// Registration
//-------------------------------------
static void RegisterScriptMathComplex_Native(asIScriptEngine *engine)
{
int r;
// Register the type
#if AS_CAN_USE_CPP11
// With C++11 it is possible to use asGetTypeTraits to determine the correct flags to represent the C++ class, except for the asOBJ_APP_CLASS_ALLFLOATS
r = engine->RegisterObjectType("complex", sizeof(Complex), asOBJ_VALUE | asOBJ_POD | asGetTypeTraits<Complex>() | asOBJ_APP_CLASS_ALLFLOATS); assert( r >= 0 );
#else
r = engine->RegisterObjectType("complex", sizeof(Complex), asOBJ_VALUE | asOBJ_POD | asOBJ_APP_CLASS_CAK | asOBJ_APP_CLASS_ALLFLOATS); assert( r >= 0 );
#endif
// Register the object properties
r = engine->RegisterObjectProperty("complex", "float r", asOFFSET(Complex, r)); assert( r >= 0 );
r = engine->RegisterObjectProperty("complex", "float i", asOFFSET(Complex, i)); assert( r >= 0 );
// Register the constructors
r = engine->RegisterObjectBehaviour("complex", asBEHAVE_CONSTRUCT, "void f()", asFUNCTION(ComplexDefaultConstructor), asCALL_CDECL_OBJLAST); assert( r >= 0 );
r = engine->RegisterObjectBehaviour("complex", asBEHAVE_CONSTRUCT, "void f(const complex &in)", asFUNCTION(ComplexCopyConstructor), asCALL_CDECL_OBJLAST); assert( r >= 0 );
r = engine->RegisterObjectBehaviour("complex", asBEHAVE_CONSTRUCT, "void f(float)", asFUNCTION(ComplexConvConstructor), asCALL_CDECL_OBJLAST); assert( r >= 0 );
r = engine->RegisterObjectBehaviour("complex", asBEHAVE_CONSTRUCT, "void f(float, float)", asFUNCTION(ComplexInitConstructor), asCALL_CDECL_OBJLAST); assert( r >= 0 );
r = engine->RegisterObjectBehaviour("complex", asBEHAVE_LIST_CONSTRUCT, "void f(const int &in) {float, float}", asFUNCTION(ComplexListConstructor), asCALL_CDECL_OBJLAST); assert( r >= 0 );
// Register the operator overloads
r = engine->RegisterObjectMethod("complex", "complex &opAddAssign(const complex &in)", asMETHODPR(Complex, operator+=, (const Complex &), Complex&), asCALL_THISCALL); assert( r >= 0 );
r = engine->RegisterObjectMethod("complex", "complex &opSubAssign(const complex &in)", asMETHODPR(Complex, operator-=, (const Complex &), Complex&), asCALL_THISCALL); assert( r >= 0 );
r = engine->RegisterObjectMethod("complex", "complex &opMulAssign(const complex &in)", asMETHODPR(Complex, operator*=, (const Complex &), Complex&), asCALL_THISCALL); assert( r >= 0 );
r = engine->RegisterObjectMethod("complex", "complex &opDivAssign(const complex &in)", asMETHODPR(Complex, operator/=, (const Complex &), Complex&), asCALL_THISCALL); assert( r >= 0 );
r = engine->RegisterObjectMethod("complex", "bool opEquals(const complex &in) const", asMETHODPR(Complex, operator==, (const Complex &) const, bool), asCALL_THISCALL); assert( r >= 0 );
r = engine->RegisterObjectMethod("complex", "complex opAdd(const complex &in) const", asMETHODPR(Complex, operator+, (const Complex &) const, Complex), asCALL_THISCALL); assert( r >= 0 );
r = engine->RegisterObjectMethod("complex", "complex opSub(const complex &in) const", asMETHODPR(Complex, operator-, (const Complex &) const, Complex), asCALL_THISCALL); assert( r >= 0 );
r = engine->RegisterObjectMethod("complex", "complex opMul(const complex &in) const", asMETHODPR(Complex, operator*, (const Complex &) const, Complex), asCALL_THISCALL); assert( r >= 0 );
r = engine->RegisterObjectMethod("complex", "complex opDiv(const complex &in) const", asMETHODPR(Complex, operator/, (const Complex &) const, Complex), asCALL_THISCALL); assert( r >= 0 );
// Register the object methods
r = engine->RegisterObjectMethod("complex", "float abs() const", asMETHOD(Complex,length), asCALL_THISCALL); assert( r >= 0 );
// Register the swizzle operators
r = engine->RegisterObjectMethod("complex", "complex get_ri() const property", asMETHOD(Complex, get_ri), asCALL_THISCALL); assert( r >= 0 );
r = engine->RegisterObjectMethod("complex", "complex get_ir() const property", asMETHOD(Complex, get_ir), asCALL_THISCALL); assert( r >= 0 );
r = engine->RegisterObjectMethod("complex", "void set_ri(const complex &in) property", asMETHOD(Complex, set_ri), asCALL_THISCALL); assert( r >= 0 );
r = engine->RegisterObjectMethod("complex", "void set_ir(const complex &in) property", asMETHOD(Complex, set_ir), asCALL_THISCALL); assert( r >= 0 );
}
void RegisterScriptMathComplex(asIScriptEngine *engine)
{
if( strstr(asGetLibraryOptions(), "AS_MAX_PORTABILITY") )
{
assert( false );
// TODO: implement support for generic calling convention
// RegisterScriptMathComplex_Generic(engine);
}
else
RegisterScriptMathComplex_Native(engine);
}
END_AS_NAMESPACE

View File

@@ -0,0 +1,61 @@
#ifndef SCRIPTMATHCOMPLEX_H
#define SCRIPTMATHCOMPLEX_H
#ifndef ANGELSCRIPT_H
// Avoid having to inform include path if header is already include before
#include <angelscript.h>
#endif
BEGIN_AS_NAMESPACE
// This class implements complex numbers and the common
// operations that can be done with it.
//
// Ref: http://mathworld.wolfram.com/ComplexNumber.html
struct Complex
{
Complex();
Complex(const Complex &other);
Complex(float r, float i = 0);
// Assignment operator
Complex &operator=(const Complex &other);
// Compound assigment operators
Complex &operator+=(const Complex &other);
Complex &operator-=(const Complex &other);
Complex &operator*=(const Complex &other);
Complex &operator/=(const Complex &other);
float length() const;
float squaredLength() const;
// Swizzle operators
Complex get_ri() const;
void set_ri(const Complex &in);
Complex get_ir() const;
void set_ir(const Complex &in);
// Comparison
bool operator==(const Complex &other) const;
bool operator!=(const Complex &other) const;
// Math operators
Complex operator+(const Complex &other) const;
Complex operator-(const Complex &other) const;
Complex operator*(const Complex &other) const;
Complex operator/(const Complex &other) const;
float r;
float i;
};
// This function will determine the configuration of the engine
// and use one of the two functions below to register the string type
void RegisterScriptMathComplex(asIScriptEngine *engine);
END_AS_NAMESPACE
#endif

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,48 @@
//
// Script std::string
//
// This function registers the std::string type with AngelScript to be used as the default string type.
//
// The string type is registered as a value type, thus may have performance issues if a lot of
// string operations are performed in the script. However, for relatively few operations, this should
// not cause any problem for most applications.
//
#ifndef SCRIPTSTDSTRING_H
#define SCRIPTSTDSTRING_H
#ifndef ANGELSCRIPT_H
// Avoid having to inform include path if header is already include before
#include <angelscript.h>
#endif
#include <string>
//---------------------------
// Compilation settings
//
// Sometimes it may be desired to use the same method names as used by C++ STL.
// This may for example reduce time when converting code from script to C++ or
// back.
//
// 0 = off
// 1 = on
#ifndef AS_USE_STLNAMES
#define AS_USE_STLNAMES 0
#endif
// Some prefer to use property accessors to get/set the length of the string
// This option registers the accessors instead of the method length()
#ifndef AS_USE_ACCESSORS
#define AS_USE_ACCESSORS 0
#endif
BEGIN_AS_NAMESPACE
void RegisterStdString(asIScriptEngine *engine);
void RegisterStdStringUtils(asIScriptEngine *engine);
END_AS_NAMESPACE
#endif

View File

@@ -0,0 +1,129 @@
#include <assert.h>
#include "scriptstdstring.h"
#include "../scriptarray/scriptarray.h"
#include <stdio.h>
#include <string.h>
using namespace std;
BEGIN_AS_NAMESPACE
// This function takes an input string and splits it into parts by looking
// for a specified delimiter. Example:
//
// string str = "A|B||D";
// array<string>@ array = str.split("|");
//
// The resulting array has the following elements:
//
// {"A", "B", "", "D"}
//
// AngelScript signature:
// array<string>@ string::split(const string &in delim) const
static CScriptArray *StringSplit(const string &delim, const string &str)
{
// Obtain a pointer to the engine
asIScriptContext *ctx = asGetActiveContext();
asIScriptEngine *engine = ctx->GetEngine();
// TODO: This should only be done once
// TODO: This assumes that CScriptArray was already registered
asITypeInfo *arrayType = engine->GetTypeInfoByDecl("array<string>");
// Create the array object
CScriptArray *array = CScriptArray::Create(arrayType);
// Find the existence of the delimiter in the input string
int pos = 0, prev = 0, count = 0;
while( (pos = (int)str.find(delim, prev)) != (int)string::npos )
{
// Add the part to the array
array->Resize(array->GetSize()+1);
((string*)array->At(count))->assign(&str[prev], pos-prev);
// Find the next part
count++;
prev = pos + (int)delim.length();
}
// Add the remaining part
array->Resize(array->GetSize()+1);
((string*)array->At(count))->assign(&str[prev]);
return array;
}
static void StringSplit_Generic(asIScriptGeneric *gen)
{
// Get the arguments
string *str = (string*)gen->GetObject();
string *delim = *(string**)gen->GetAddressOfArg(0);
// Return the array by handle
*(CScriptArray**)gen->GetAddressOfReturnLocation() = StringSplit(*delim, *str);
}
// This function takes as input an array of string handles as well as a
// delimiter and concatenates the array elements into one delimited string.
// Example:
//
// array<string> array = {"A", "B", "", "D"};
// string str = join(array, "|");
//
// The resulting string is:
//
// "A|B||D"
//
// AngelScript signature:
// string join(const array<string> &in array, const string &in delim)
static string StringJoin(const CScriptArray &array, const string &delim)
{
// Create the new string
string str = "";
if( array.GetSize() )
{
int n;
for( n = 0; n < (int)array.GetSize() - 1; n++ )
{
str += *(string*)array.At(n);
str += delim;
}
// Add the last part
str += *(string*)array.At(n);
}
return str;
}
static void StringJoin_Generic(asIScriptGeneric *gen)
{
// Get the arguments
CScriptArray *array = *(CScriptArray**)gen->GetAddressOfArg(0);
string *delim = *(string**)gen->GetAddressOfArg(1);
// Return the string
new(gen->GetAddressOfReturnLocation()) string(StringJoin(*array, *delim));
}
// This is where the utility functions are registered.
// The string type must have been registered first.
void RegisterStdStringUtils(asIScriptEngine *engine)
{
int r;
if( strstr(asGetLibraryOptions(), "AS_MAX_PORTABILITY") )
{
r = engine->RegisterObjectMethod("string", "array<string>@ split(const string &in) const", asFUNCTION(StringSplit_Generic), asCALL_GENERIC); assert(r >= 0);
r = engine->RegisterGlobalFunction("string join(const array<string> &in, const string &in)", asFUNCTION(StringJoin_Generic), asCALL_GENERIC); assert(r >= 0);
}
else
{
r = engine->RegisterObjectMethod("string", "array<string>@ split(const string &in) const", asFUNCTION(StringSplit), asCALL_CDECL_OBJLAST); assert(r >= 0);
r = engine->RegisterGlobalFunction("string join(const array<string> &in, const string &in)", asFUNCTION(StringJoin), asCALL_CDECL); assert(r >= 0);
}
}
END_AS_NAMESPACE

View File

@@ -0,0 +1,548 @@
//
// CSerializer
//
// This code was based on the CScriptReloader written by FDsagizi
// http://www.gamedev.net/topic/604890-dynamic-reloading-script/
//
#include <assert.h>
#include <string.h> // strstr
#include <stdio.h> // sprintf
#include "serializer.h"
using namespace std;
BEGIN_AS_NAMESPACE
///////////////////////////////////////////////////////////////////////////////////
CSerializer::CSerializer()
{
m_engine = 0;
}
CSerializer::~CSerializer()
{
// Extra objects need to be released, since they are not stored in
// the module and we cannot rely on the application releasing them
for( size_t i = 0; i < m_extraObjects.size(); i++ )
{
SExtraObject &o = m_extraObjects[i];
for( size_t i2 = 0; i2 < m_root.m_children.size(); i2++ )
{
if( m_root.m_children[i2]->m_originalPtr == o.originalObject && m_root.m_children[i2]->m_restorePtr )
reinterpret_cast<asIScriptObject*>(m_root.m_children[i2]->m_restorePtr)->Release();
}
}
// Clean the serialized values before we remove the user types
m_root.Uninit();
// Delete the user types
std::map<std::string, CUserType*>::iterator it;
for( it = m_userTypes.begin(); it != m_userTypes.end(); it++ )
delete it->second;
if( m_engine )
m_engine->Release();
}
void CSerializer::AddUserType(CUserType *ref, const std::string &name)
{
m_userTypes[name] = ref;
}
int CSerializer::Store(asIScriptModule *mod)
{
m_mod = mod;
// The engine must not be destroyed before we're completed, so we'll hold on to a reference
mod->GetEngine()->AddRef();
if( m_engine ) m_engine->Release();
m_engine = mod->GetEngine();
m_root.m_serializer = this;
// First store global variables
asUINT i;
for( i = 0; i < mod->GetGlobalVarCount(); i++ )
{
const char *name, *nameSpace;
int typeId;
mod->GetGlobalVar(i, &name, &nameSpace, &typeId);
m_root.m_children.push_back(new CSerializedValue(&m_root, name, nameSpace, mod->GetAddressOfGlobalVar(i), typeId));
}
// Second store extra objects
for( i = 0; i < m_extraObjects.size(); i++ )
m_root.m_children.push_back(new CSerializedValue(&m_root, "", "", m_extraObjects[i].originalObject, m_extraObjects[i].originalTypeId));
// For the handles that were stored, we need to substitute the stored pointer
// that is still pointing to the original object to an internal reference so
// it can be restored later on.
m_root.ReplaceHandles();
return 0;
}
// Retrieve all global variables after reload script.
int CSerializer::Restore(asIScriptModule *mod)
{
m_mod = mod;
// The engine must not be destroyed before we're completed, so we'll hold on to a reference
mod->GetEngine()->AddRef();
if( m_engine ) m_engine->Release();
m_engine = mod->GetEngine();
// First restore extra objects, i.e. the ones that are not directly seen from the module's global variables
asUINT i;
for( i = 0; i < m_extraObjects.size(); i++ )
{
SExtraObject &o = m_extraObjects[i];
asITypeInfo *type = m_mod->GetTypeInfoByName( o.originalClassName.c_str() );
if( type )
{
for( size_t i2 = 0; i2 < m_root.m_children.size(); i2++ )
{
if( m_root.m_children[i2]->m_originalPtr == o.originalObject )
{
// Create a new script object, but don't call its constructor as we will initialize the members.
// Calling the constructor may have unwanted side effects if for example the constructor changes
// any outside entities, such as setting global variables to point to new objects, etc.
void *newPtr = m_engine->CreateUninitializedScriptObject( type );
m_root.m_children[i2]->Restore( newPtr, type->GetTypeId() );
}
}
}
}
// Second restore the global variables
asUINT varCount = mod->GetGlobalVarCount();
for( i = 0; i < varCount; i++ )
{
const char *name, *nameSpace;
int typeId;
mod->GetGlobalVar(i, &name, &nameSpace, &typeId);
CSerializedValue *v = m_root.FindByName(name, nameSpace);
if( v )
v->Restore(mod->GetAddressOfGlobalVar(i), typeId);
}
// The handles that were restored needs to be
// updated to point to their final objects.
m_root.RestoreHandles();
return 0;
}
void *CSerializer::GetPointerToRestoredObject(void *ptr)
{
return m_root.GetPointerToRestoredObject( ptr );
}
void CSerializer::AddExtraObjectToStore( asIScriptObject *object )
{
if( !object )
return;
// Check if the object hasn't been included already
for( size_t i=0; i < m_extraObjects.size(); i++ )
if( m_extraObjects[i].originalObject == object )
return;
SExtraObject o;
o.originalObject = object;
o.originalClassName = object->GetObjectType()->GetName();
o.originalTypeId = object->GetTypeId();
m_extraObjects.push_back( o );
}
///////////////////////////////////////////////////////////////////////////////////
CSerializedValue::CSerializedValue()
{
Init();
}
CSerializedValue::CSerializedValue(CSerializedValue *parent, const std::string &name, const std::string &nameSpace, void *ref, int typeId)
{
Init();
m_name = name;
m_nameSpace = nameSpace;
m_serializer = parent->m_serializer;
Store(ref, typeId);
}
void CSerializedValue::Init()
{
m_handlePtr = 0;
m_restorePtr = 0;
m_typeId = 0;
m_isInit = false;
m_serializer = 0;
m_userData = 0;
m_originalPtr = 0;
}
void CSerializedValue::Uninit()
{
m_isInit = false;
ClearChildren();
if( m_userData )
{
CUserType *type = m_serializer->m_userTypes[m_typeName];
if( type )
type->CleanupUserData(this);
m_userData = 0;
}
}
void CSerializedValue::ClearChildren()
{
// If this value is for an object handle that created an object during the restore
// then it is necessary to release the handle here, so we won't get a memory leak
if( (m_typeId & asTYPEID_OBJHANDLE) && m_children.size() == 1 && m_children[0]->m_restorePtr )
{
m_serializer->m_engine->ReleaseScriptObject(m_children[0]->m_restorePtr, m_serializer->m_engine->GetTypeInfoById(m_children[0]->m_typeId));
}
for( size_t n = 0; n < m_children.size(); n++ )
delete m_children[n];
m_children.clear();
}
CSerializedValue::~CSerializedValue()
{
Uninit();
}
CSerializedValue *CSerializedValue::FindByName(const std::string &name, const std::string &nameSpace)
{
for( size_t i = 0; i < m_children.size(); i++ )
if( m_children[i]->m_name == name &&
m_children[i]->m_nameSpace == nameSpace )
return m_children[i];
return 0;
}
void CSerializedValue::GetAllPointersOfChildren(std::vector<void*> *ptrs)
{
ptrs->push_back(m_originalPtr);
for( size_t i = 0; i < m_children.size(); ++i )
m_children[i]->GetAllPointersOfChildren(ptrs);
}
CSerializedValue *CSerializedValue::FindByPtr(void *ptr)
{
if( m_originalPtr == ptr )
return this;
for( size_t i = 0; i < m_children.size(); i++ )
{
CSerializedValue *find = m_children[i]->FindByPtr(ptr);
if( find )
return find;
}
return 0;
}
void *CSerializedValue::GetPointerToRestoredObject(void *ptr)
{
if( m_originalPtr == ptr )
return m_restorePtr;
for( size_t i = 0; i < m_children.size(); ++i )
{
void *ret = m_children[i]->GetPointerToRestoredObject(ptr);
if( ret )
return ret;
}
return 0;
}
// find variable by ptr but looking only at those in the references, which will create a new object
CSerializedValue *CSerializedValue::FindByPtrInHandles(void *ptr)
{
// if this handle created object
if( (m_typeId & asTYPEID_OBJHANDLE) && m_children.size() == 1 )
{
if( m_children[0]->m_originalPtr == ptr )
return this;
}
if( !(m_typeId & asTYPEID_OBJHANDLE) )
{
for( size_t i = 0; i < m_children.size(); i++ )
{
CSerializedValue *find = m_children[i]->FindByPtrInHandles(ptr);
if( find )
return find;
}
}
return 0;
}
void CSerializedValue::Store(void *ref, int typeId)
{
m_isInit = true;
SetType(typeId);
m_originalPtr = ref;
if( m_typeId & asTYPEID_OBJHANDLE )
{
m_handlePtr = *(void**)ref;
}
else if( m_typeId & asTYPEID_SCRIPTOBJECT )
{
asIScriptObject *obj = (asIScriptObject *)ref;
asITypeInfo *type = obj->GetObjectType();
SetType(type->GetTypeId());
// Store children
for( asUINT i = 0; i < type->GetPropertyCount(); i++ )
{
int childId;
const char *childName;
type->GetProperty(i, &childName, &childId);
m_children.push_back(new CSerializedValue(this, childName, "", obj->GetAddressOfProperty(i), childId));
}
}
else
{
int size = m_serializer->m_engine->GetSizeOfPrimitiveType(m_typeId);
if( size == 0 )
{
// if it is user type( string, array, etc ... )
if( m_serializer->m_userTypes[m_typeName] )
m_serializer->m_userTypes[m_typeName]->Store(this, m_originalPtr);
else
{
// POD-types can be stored without need for user type
asITypeInfo *type = GetType();
if( type && (type->GetFlags() & asOBJ_POD) )
size = GetType()->GetSize();
// It is not necessary to report an error here if it is not a POD-type as that will be done when restoring
}
}
if( size )
{
m_mem.resize(size);
memcpy(&m_mem[0], ref, size);
}
}
}
void CSerializedValue::Restore(void *ref, int typeId)
{
if( !this || !m_isInit || !ref )
return;
// Verify that the stored type matched the new type of the value being restored
if( typeId <= asTYPEID_DOUBLE && typeId != m_typeId ) return; // TODO: We may try to do a type conversion for primitives
if( (typeId & ~asTYPEID_MASK_SEQNBR) ^ (m_typeId & ~asTYPEID_MASK_SEQNBR) ) return;
asITypeInfo *type = m_serializer->m_engine->GetTypeInfoById(typeId);
if( type && m_typeName != type->GetName() ) return;
// Set the new pointer and type
m_restorePtr = ref;
SetType(typeId);
// Restore the value
if( m_typeId & asTYPEID_OBJHANDLE )
{
// if need create objects
if( m_children.size() == 1 )
{
asITypeInfo *ctype = m_children[0]->GetType();
if( ctype->GetFactoryCount() == 0 )
{
// There are no factories, so assume the same pointer is going to be used
m_children[0]->m_restorePtr = m_handlePtr;
// Increase the refCount for the object as it will be released upon clean-up
m_serializer->m_engine->AddRefScriptObject(m_handlePtr, ctype);
}
else
{
// Create a new script object, but don't call its constructor as we will initialize the members.
// Calling the constructor may have unwanted side effects if for example the constructor changes
// any outside entities, such as setting global variables to point to new objects, etc.
void *newObject = m_serializer->m_engine->CreateUninitializedScriptObject(ctype);
m_children[0]->Restore(newObject, ctype->GetTypeId());
}
}
}
else if( m_typeId & asTYPEID_SCRIPTOBJECT )
{
asIScriptObject *obj = (asIScriptObject *)ref;
// Retrieve children
for( asUINT i = 0; i < type->GetPropertyCount() ; i++ )
{
const char *nameProperty;
int ptypeId;
type->GetProperty(i, &nameProperty, &ptypeId);
CSerializedValue *var = FindByName(nameProperty, "");
if( var )
var->Restore(obj->GetAddressOfProperty(i), ptypeId);
}
}
else
{
if( m_mem.size() )
{
// POD values can be restored with direct copy
memcpy(ref, &m_mem[0], m_mem.size());
}
else if( m_serializer->m_userTypes[m_typeName] )
{
// user type restore
m_serializer->m_userTypes[m_typeName]->Restore(this, m_restorePtr);
}
else
{
std::string str = "Cannot restore type '";
str += type->GetName();
str += "'";
m_serializer->m_engine->WriteMessage("", 0, 0, asMSGTYPE_ERROR, str.c_str());
}
}
}
void CSerializedValue::CancelDuplicates(CSerializedValue *from)
{
std::vector<void*> ptrs;
from->GetAllPointersOfChildren(&ptrs);
for( size_t i = 0; i < ptrs.size(); ++i )
{
CSerializedValue *find = m_serializer->m_root.FindByPtrInHandles(ptrs[i]);
while( find )
{
// cancel create object
find->ClearChildren();
// Find next link to this ptr
find = m_serializer->m_root.FindByPtrInHandles(ptrs[i]);
}
}
}
void CSerializedValue::ReplaceHandles()
{
if( m_handlePtr )
{
// Find the object that the handle is referring to
CSerializedValue *handle_to = m_serializer->m_root.FindByPtr(m_handlePtr);
// If the object hasn't been stored yet...
if( handle_to == 0 )
{
// Store the object now
asITypeInfo *type = GetType();
CSerializedValue *need_create = new CSerializedValue(this, m_name, m_nameSpace, m_handlePtr, type->GetTypeId());
// Make sure all other handles that point to the same object
// are updated, so we don't end up creating duplicates
CancelDuplicates(need_create);
m_children.push_back(need_create);
}
}
// Replace the handles in the children too
for( size_t i = 0; i < m_children.size(); ++i )
m_children[i]->ReplaceHandles();
}
void CSerializedValue::RestoreHandles()
{
if( m_typeId & asTYPEID_OBJHANDLE )
{
if( m_handlePtr )
{
// Find the object the handle is supposed to point to
CSerializedValue *handleTo = m_serializer->m_root.FindByPtr(m_handlePtr);
if( m_restorePtr && handleTo && handleTo->m_restorePtr )
{
asITypeInfo *type = m_serializer->m_engine->GetTypeInfoById(m_typeId);
// If the handle is already pointing to something it must be released first
if( *(void**)m_restorePtr )
m_serializer->m_engine->ReleaseScriptObject(*(void**)m_restorePtr, type);
// Update the internal pointer
*(void**)m_restorePtr = handleTo->m_restorePtr;
// Increase the reference
m_serializer->m_engine->AddRefScriptObject(handleTo->m_restorePtr, type);
}
}
else
{
// If the handle is pointing to something, we must release it to restore the null pointer
if( m_restorePtr && *(void**)m_restorePtr )
{
m_serializer->m_engine->ReleaseScriptObject(*(void**)m_restorePtr, m_serializer->m_engine->GetTypeInfoById(m_typeId));
*(void**)m_restorePtr = 0;
}
}
}
// Do the same for the children
for( size_t i = 0; i < m_children.size(); ++i )
m_children[i]->RestoreHandles();
}
void CSerializedValue::SetType(int typeId)
{
m_typeId = typeId;
asITypeInfo *type = m_serializer->m_engine->GetTypeInfoById(typeId);
if( type )
m_typeName = type->GetName();
}
asITypeInfo *CSerializedValue::GetType()
{
if( !m_typeName.empty() )
{
int newTypeId = m_serializer->m_mod->GetTypeIdByDecl(m_typeName.c_str());
return m_serializer->m_engine->GetTypeInfoById(newTypeId);
}
return 0;
}
void CSerializedValue::SetUserData(void *data)
{
m_userData = data;
}
void *CSerializedValue::GetUserData()
{
return m_userData;
}
END_AS_NAMESPACE

View File

@@ -0,0 +1,193 @@
//
// CSerializer
//
// This code was based on the CScriptReloader written by FDsagizi
// http://www.gamedev.net/topic/604890-dynamic-reloading-script/
//
#ifndef SERIALIZER_H
#define SERIALIZER_H
#ifndef ANGELSCRIPT_H
// Avoid having to inform include path if header is already include before
#include <angelscript.h>
#endif
#include <vector>
#include <string>
#include <map>
BEGIN_AS_NAMESPACE
class CSerializer;
class CSerializedValue;
// Need for register user types objects
// string, any, array... for all object
// user ref type.
struct CUserType
{
virtual ~CUserType() {};
virtual void Store(CSerializedValue *val, void *ptr) = 0;
virtual void Restore(CSerializedValue *val, void *ptr) = 0;
virtual void CleanupUserData(CSerializedValue * /*val*/) {}
};
class CSerializedValue
{
public:
CSerializedValue();
CSerializedValue(CSerializedValue *parent, const std::string &name, const std::string &nameSpace, void *ref, int typeId);
~CSerializedValue();
// Save the object and its children
void Store(void *ref, int refTypeId);
// Restore the object and its children
void Restore(void *ref, int refTypeId);
// Set type of this var
void SetType(int typeId);
// Returns the object type for non-primitives
asITypeInfo *GetType();
// Get child by name variable
CSerializedValue *FindByName(const std::string &name, const std::string &nameSpace);
// Find variable by ptr
CSerializedValue *FindByPtr(void *ptr);
// User data
void *GetUserData();
void SetUserData(void *data);
// Children, e.g. properties of a script class, or elements
// of an array, or object pointed to by a handle unless it
// is already a variable)
std::vector<CSerializedValue*> m_children;
protected:
friend class CSerializer;
void Init();
void Uninit();
// you first need to save all the objects before you can save references to objects
void ReplaceHandles();
// After the objects has been restored, the handles needs to
// be updated to point to the right objects
void RestoreHandles();
// Recursively get all ptrs of the children
void GetAllPointersOfChildren(std::vector<void*> *ptrs);
// may be that the two references refer to the same variable.
// But this variable is not available in the global list.
// According to this reference will be restores it.
// And so two links are not created 2 variables,
// it is necessary to cancel the creation of one of them.
void CancelDuplicates(CSerializedValue *from);
// Find variable by ptr but looking only at those in the references, which will create a new object
CSerializedValue *FindByPtrInHandles(void *ptr);
// ptr - is a handle to class
void *GetPointerToRestoredObject(void *ptr);
// Cleanup children
void ClearChildren();
// The serializer object
CSerializer *m_serializer;
// The user data can be used by CUserType to store extra information
void *m_userData;
// The type id of the stored value
int m_typeId;
// For non-primitives the typeId may change if the module is reloaded so
// it is necessary to store the type name to determine the new type id
std::string m_typeName;
// Name of variable or property
std::string m_name;
std::string m_nameSpace;
// Is initialized
bool m_isInit;
// 'this' pointer to variable.
// While storing, this points to the actual variable that was stored.
// While restoring, it is just a unique identifier.
void *m_originalPtr;
// where handle references
// While storing, this points to the actual object.
// While restoring, it is just a unique identifier.
void *m_handlePtr;
// new address object, ie address the restoration
// While storing this isn't used.
// While restoring it will point to the actual variable/object that is restored.
void *m_restorePtr;
// Serialized data for primitives
std::vector<char> m_mem;
};
// This class keeps a list of variables, then restores them after the script is rebuilt.
// But you have to be careful with the change of signature in classes, or
// changing the types of objects. You can remove or add variables, functions,
// methods, but you can not (yet) change the type of variables.
//
// You also need to understand that after a rebuild you should get
// new functions and typeids from the module.
class CSerializer
{
public:
CSerializer();
~CSerializer();
// Add implementation for serializing user types
void AddUserType(CUserType *ref, const std::string &name);
// Store all global variables in the module
int Store(asIScriptModule *mod);
// Restore all global variables after reloading script
int Restore(asIScriptModule *mod);
// Store extra objects that are not seen from the module's global variables
void AddExtraObjectToStore(asIScriptObject *object);
// Return new pointer to restored object
void *GetPointerToRestoredObject(void *originalObject);
protected:
friend class CSerializedValue;
CSerializedValue m_root;
asIScriptEngine *m_engine;
asIScriptModule *m_mod;
std::map<std::string, CUserType*> m_userTypes;
struct SExtraObject
{
asIScriptObject *originalObject;
std::string originalClassName;
int originalTypeId;
};
std::vector<SExtraObject> m_extraObjects;
};
END_AS_NAMESPACE
#endif

377
add_on/weakref/weakref.cpp Normal file
View File

@@ -0,0 +1,377 @@
// The CScriptWeakRef class was originally implemented by vroad in March 2013
#include "weakref.h"
#include <new>
#include <assert.h>
#include <string.h> // strstr()
BEGIN_AS_NAMESPACE
static void ScriptWeakRefConstruct(asITypeInfo *type, void *mem)
{
new(mem) CScriptWeakRef(type);
}
static void ScriptWeakRefConstruct2(asITypeInfo *type, void *ref, void *mem)
{
new(mem) CScriptWeakRef(ref, type);
// It's possible the constructor raised a script exception, in which case we
// need to call the destructor in order to cleanup the memory before returning
asIScriptContext *ctx = asGetActiveContext();
if( ctx && ctx->GetState() == asEXECUTION_EXCEPTION )
reinterpret_cast<CScriptWeakRef*>(mem)->~CScriptWeakRef();
}
static void ScriptWeakRefDestruct(CScriptWeakRef *obj)
{
obj->~CScriptWeakRef();
}
static bool ScriptWeakRefTemplateCallback(asITypeInfo *ti, bool &/*dontGarbageCollect*/)
{
asITypeInfo *subType = ti->GetSubType();
// Weak references only work for reference types
if( subType == 0 ) return false;
if( !(subType->GetFlags() & asOBJ_REF) ) return false;
// The subtype shouldn't be a handle
if( ti->GetSubTypeId() & asTYPEID_OBJHANDLE )
return false;
// Make sure the type really supports weak references
asUINT cnt = subType->GetBehaviourCount();
for( asUINT n = 0; n < cnt; n++ )
{
asEBehaviours beh;
subType->GetBehaviourByIndex(n, &beh);
if( beh == asBEHAVE_GET_WEAKREF_FLAG )
return true;
}
ti->GetEngine()->WriteMessage("weakref", 0, 0, asMSGTYPE_ERROR, "The subtype doesn't support weak references");
return false;
}
CScriptWeakRef::CScriptWeakRef(asITypeInfo *type)
{
m_ref = 0;
m_type = type;
m_type->AddRef();
m_weakRefFlag = 0;
}
CScriptWeakRef::CScriptWeakRef(const CScriptWeakRef &other)
{
m_ref = other.m_ref;
m_type = other.m_type;
m_type->AddRef();
m_weakRefFlag = other.m_weakRefFlag;
if( m_weakRefFlag )
m_weakRefFlag->AddRef();
}
CScriptWeakRef::CScriptWeakRef(void *ref, asITypeInfo *type)
{
m_ref = ref;
m_type = type;
m_type->AddRef();
// The given type should be the weakref template instance
assert( strcmp(type->GetName(), "weakref") == 0 ||
strcmp(type->GetName(), "const_weakref") == 0 );
// Get the shared flag that will tell us when the object has been destroyed
// This is threadsafe as we hold a strong reference to the object
m_weakRefFlag = m_type->GetEngine()->GetWeakRefFlagOfScriptObject(m_ref, m_type->GetSubType());
if( m_weakRefFlag )
m_weakRefFlag->AddRef();
}
CScriptWeakRef::~CScriptWeakRef()
{
if( m_type )
m_type->Release();
if( m_weakRefFlag )
m_weakRefFlag->Release();
}
CScriptWeakRef &CScriptWeakRef::operator =(const CScriptWeakRef &other)
{
// Don't do anything if it is the same reference
// It is not enough to verify only the reference to the object, as the
// address may be reused by another instance after the first has been freed.
// By checking also the weakRefFlag we can be certain that it is the same
// instance.
if( m_ref == other.m_ref &&
m_weakRefFlag == other.m_weakRefFlag )
return *this;
// Must not allow changing the type
if( m_type != other.m_type )
{
// We can allow a weakref to be assigned to a const_weakref
if( !(strcmp(m_type->GetName(), "const_weakref") == 0 &&
strcmp(other.m_type->GetName(), "weakref") == 0 &&
m_type->GetSubType() == other.m_type->GetSubType()) )
{
assert( false );
return *this;
}
}
m_ref = other.m_ref;
if( m_weakRefFlag )
m_weakRefFlag->Release();
m_weakRefFlag = other.m_weakRefFlag;
if( m_weakRefFlag )
m_weakRefFlag->AddRef();
return *this;
}
CScriptWeakRef &CScriptWeakRef::Set(void *newRef)
{
// Release the previous weak ref
if( m_weakRefFlag )
m_weakRefFlag->Release();
// Retrieve the new weak ref
m_ref = newRef;
if( newRef )
{
m_weakRefFlag = m_type->GetEngine()->GetWeakRefFlagOfScriptObject(newRef, m_type->GetSubType());
m_weakRefFlag->AddRef();
}
else
m_weakRefFlag = 0;
// Release the newRef since we're only supposed to hold a weakref
m_type->GetEngine()->ReleaseScriptObject(newRef, m_type->GetSubType());
return *this;
}
asITypeInfo *CScriptWeakRef::GetRefType() const
{
return m_type->GetSubType();
}
bool CScriptWeakRef::operator==(const CScriptWeakRef &o) const
{
// It is not enough to compare just the address of the object, as it may
// be reused by another instance after the first has been freed. By verifying
// also the weakRefFlag we can guarantee that it is indeed the same instance.
if( m_ref == o.m_ref &&
m_weakRefFlag == o.m_weakRefFlag &&
m_type == o.m_type )
return true;
// TODO: If type is not the same, we should attempt to do a dynamic cast,
// which may change the pointer for application registered classes
return false;
}
bool CScriptWeakRef::operator!=(const CScriptWeakRef &o) const
{
return !(*this == o);
}
// AngelScript: used as '@obj = ref.get();'
void *CScriptWeakRef::Get() const
{
// If we hold a null handle, then just return null
if( m_ref == 0 || m_weakRefFlag == 0 )
return 0;
// Lock on the shared bool, so we can be certain it won't be changed to true
// between the inspection of the flag and the increase of the ref count in the
// owning object.
m_weakRefFlag->Lock();
if( !m_weakRefFlag->Get() )
{
m_type->GetEngine()->AddRefScriptObject(m_ref, m_type->GetSubType());
m_weakRefFlag->Unlock();
return m_ref;
}
m_weakRefFlag->Unlock();
return 0;
}
bool CScriptWeakRef::Equals(void *ref) const
{
if( m_ref != ref )
return false;
// It is not enough to compare just the address, as another instance may
// get the same address after the first instance has been freed. Verify the
// weakref flag too to make sure it is the same instance
asILockableSharedBool *flag = m_type->GetEngine()->GetWeakRefFlagOfScriptObject(ref, m_type->GetSubType());
if (m_weakRefFlag != flag)
return false;
return true;
}
void RegisterScriptWeakRef_Native(asIScriptEngine *engine)
{
int r;
// Register a type for non-const handles
r = engine->RegisterObjectType("weakref<class T>", sizeof(CScriptWeakRef), asOBJ_VALUE | asOBJ_ASHANDLE | asOBJ_TEMPLATE | asOBJ_APP_CLASS_DAK); assert( r >= 0 );
r = engine->RegisterObjectBehaviour("weakref<T>", asBEHAVE_CONSTRUCT, "void f(int&in)", asFUNCTION(ScriptWeakRefConstruct), asCALL_CDECL_OBJLAST); assert( r>= 0 );
r = engine->RegisterObjectBehaviour("weakref<T>", asBEHAVE_CONSTRUCT, "void f(int&in, T@+)", asFUNCTION(ScriptWeakRefConstruct2), asCALL_CDECL_OBJLAST); assert( r>= 0 );
r = engine->RegisterObjectBehaviour("weakref<T>", asBEHAVE_DESTRUCT, "void f()", asFUNCTION(ScriptWeakRefDestruct), asCALL_CDECL_OBJLAST); assert( r >= 0 );
r = engine->RegisterObjectBehaviour("weakref<T>", asBEHAVE_TEMPLATE_CALLBACK, "bool f(int&in, bool&out)", asFUNCTION(ScriptWeakRefTemplateCallback), asCALL_CDECL); assert( r >= 0 );
r = engine->RegisterObjectMethod("weakref<T>", "T@ opImplCast()", asMETHOD(CScriptWeakRef, Get), asCALL_THISCALL); assert(r >= 0);
r = engine->RegisterObjectMethod("weakref<T>", "T@ get() const", asMETHODPR(CScriptWeakRef, Get, () const, void*), asCALL_THISCALL); assert( r >= 0 );
r = engine->RegisterObjectMethod("weakref<T>", "weakref<T> &opHndlAssign(const weakref<T> &in)", asMETHOD(CScriptWeakRef, operator=), asCALL_THISCALL); assert( r >= 0 );
r = engine->RegisterObjectMethod("weakref<T>", "weakref<T> &opAssign(const weakref<T> &in)", asMETHOD(CScriptWeakRef, operator=), asCALL_THISCALL); assert(r >= 0);
r = engine->RegisterObjectMethod("weakref<T>", "bool opEquals(const weakref<T> &in) const", asMETHODPR(CScriptWeakRef, operator==, (const CScriptWeakRef &) const, bool), asCALL_THISCALL); assert( r >= 0 );
r = engine->RegisterObjectMethod("weakref<T>", "weakref<T> &opHndlAssign(T@)", asMETHOD(CScriptWeakRef, Set), asCALL_THISCALL); assert( r >= 0 );
r = engine->RegisterObjectMethod("weakref<T>", "bool opEquals(const T@+) const", asMETHOD(CScriptWeakRef, Equals), asCALL_THISCALL); assert(r >= 0);
// Register another type for const handles
r = engine->RegisterObjectType("const_weakref<class T>", sizeof(CScriptWeakRef), asOBJ_VALUE | asOBJ_ASHANDLE | asOBJ_TEMPLATE | asOBJ_APP_CLASS_DAK); assert( r >= 0 );
r = engine->RegisterObjectBehaviour("const_weakref<T>", asBEHAVE_CONSTRUCT, "void f(int&in)", asFUNCTION(ScriptWeakRefConstruct), asCALL_CDECL_OBJLAST); assert( r>= 0 );
r = engine->RegisterObjectBehaviour("const_weakref<T>", asBEHAVE_CONSTRUCT, "void f(int&in, const T@+)", asFUNCTION(ScriptWeakRefConstruct2), asCALL_CDECL_OBJLAST); assert( r>= 0 );
r = engine->RegisterObjectBehaviour("const_weakref<T>", asBEHAVE_DESTRUCT, "void f()", asFUNCTION(ScriptWeakRefDestruct), asCALL_CDECL_OBJLAST); assert( r >= 0 );
r = engine->RegisterObjectBehaviour("const_weakref<T>", asBEHAVE_TEMPLATE_CALLBACK, "bool f(int&in, bool&out)", asFUNCTION(ScriptWeakRefTemplateCallback), asCALL_CDECL); assert( r >= 0 );
r = engine->RegisterObjectMethod("const_weakref<T>", "const T@ opImplCast() const", asMETHOD(CScriptWeakRef, Get), asCALL_THISCALL); assert(r >= 0);
r = engine->RegisterObjectMethod("const_weakref<T>", "const T@ get() const", asMETHODPR(CScriptWeakRef, Get, () const, void*), asCALL_THISCALL); assert( r >= 0 );
r = engine->RegisterObjectMethod("const_weakref<T>", "const_weakref<T> &opHndlAssign(const const_weakref<T> &in)", asMETHOD(CScriptWeakRef, operator=), asCALL_THISCALL); assert( r >= 0 );
r = engine->RegisterObjectMethod("const_weakref<T>", "const_weakref<T> &opAssign(const const_weakref<T> &in)", asMETHOD(CScriptWeakRef, operator=), asCALL_THISCALL); assert(r >= 0);
r = engine->RegisterObjectMethod("const_weakref<T>", "bool opEquals(const const_weakref<T> &in) const", asMETHODPR(CScriptWeakRef, operator==, (const CScriptWeakRef &) const, bool), asCALL_THISCALL); assert( r >= 0 );
r = engine->RegisterObjectMethod("const_weakref<T>", "const_weakref<T> &opHndlAssign(const T@)", asMETHOD(CScriptWeakRef, Set), asCALL_THISCALL); assert( r >= 0 );
r = engine->RegisterObjectMethod("const_weakref<T>", "bool opEquals(const T@+) const", asMETHOD(CScriptWeakRef, Equals), asCALL_THISCALL); assert(r >= 0);
// Allow non-const weak references to be converted to const weak references
r = engine->RegisterObjectMethod("const_weakref<T>", "const_weakref<T> &opHndlAssign(const weakref<T> &in)", asMETHOD(CScriptWeakRef, operator=), asCALL_THISCALL); assert( r >= 0 );
r = engine->RegisterObjectMethod("const_weakref<T>", "bool opEquals(const weakref<T> &in) const", asMETHODPR(CScriptWeakRef, operator==, (const CScriptWeakRef &) const, bool), asCALL_THISCALL); assert( r >= 0 );
}
static void ScriptWeakRefConstruct_Generic(asIScriptGeneric *gen)
{
asITypeInfo *ti = *reinterpret_cast<asITypeInfo**>(gen->GetAddressOfArg(0));
ScriptWeakRefConstruct(ti, gen->GetObject());
}
static void ScriptWeakRefConstruct2_Generic(asIScriptGeneric *gen)
{
asITypeInfo *ti = *reinterpret_cast<asITypeInfo**>(gen->GetAddressOfArg(0));
void *ref = gen->GetArgAddress(1);
ScriptWeakRefConstruct2(ti, ref, gen->GetObject());
}
static void ScriptWeakRefDestruct_Generic(asIScriptGeneric *gen)
{
CScriptWeakRef *self = reinterpret_cast<CScriptWeakRef*>(gen->GetObject());
self->~CScriptWeakRef();
}
void CScriptWeakRef_Get_Generic(asIScriptGeneric *gen)
{
CScriptWeakRef *self = reinterpret_cast<CScriptWeakRef*>(gen->GetObject());
gen->SetReturnAddress(self->Get());
}
void CScriptWeakRef_Assign_Generic(asIScriptGeneric *gen)
{
CScriptWeakRef *other = reinterpret_cast<CScriptWeakRef*>(gen->GetArgAddress(0));
CScriptWeakRef *self = reinterpret_cast<CScriptWeakRef*>(gen->GetObject());
*self = *other;
gen->SetReturnAddress(self);
}
void CScriptWeakRef_Assign2_Generic(asIScriptGeneric *gen)
{
void *other = gen->GetArgAddress(0);
CScriptWeakRef *self = reinterpret_cast<CScriptWeakRef*>(gen->GetObject());
self->Set(other);
gen->SetReturnAddress(self);
}
void CScriptWeakRef_Equals_Generic(asIScriptGeneric *gen)
{
CScriptWeakRef *other = reinterpret_cast<CScriptWeakRef*>(gen->GetArgAddress(0));
CScriptWeakRef *self = reinterpret_cast<CScriptWeakRef*>(gen->GetObject());
gen->SetReturnByte(*self == *other);
}
void CScriptWeakRef_Equals2_Generic(asIScriptGeneric *gen)
{
void *other = gen->GetArgAddress(0);
CScriptWeakRef *self = reinterpret_cast<CScriptWeakRef*>(gen->GetObject());
gen->SetReturnByte(self->Equals(other));
}
static void ScriptWeakRefTemplateCallback_Generic(asIScriptGeneric *gen)
{
asITypeInfo *ti = *reinterpret_cast<asITypeInfo**>(gen->GetAddressOfArg(0));
bool *dontGarbageCollect = *reinterpret_cast<bool**>(gen->GetAddressOfArg(1));
*reinterpret_cast<bool*>(gen->GetAddressOfReturnLocation()) = ScriptWeakRefTemplateCallback(ti, *dontGarbageCollect);
}
void RegisterScriptWeakRef_Generic(asIScriptEngine *engine)
{
int r;
// Register a type for non-const handles
r = engine->RegisterObjectType("weakref<class T>", sizeof(CScriptWeakRef), asOBJ_VALUE | asOBJ_ASHANDLE | asOBJ_TEMPLATE | asOBJ_APP_CLASS_DAK); assert( r >= 0 );
r = engine->RegisterObjectBehaviour("weakref<T>", asBEHAVE_CONSTRUCT, "void f(int&in)", asFUNCTION(ScriptWeakRefConstruct_Generic), asCALL_GENERIC); assert( r>= 0 );
r = engine->RegisterObjectBehaviour("weakref<T>", asBEHAVE_CONSTRUCT, "void f(int&in, T@+)", asFUNCTION(ScriptWeakRefConstruct2_Generic), asCALL_GENERIC); assert( r>= 0 );
r = engine->RegisterObjectBehaviour("weakref<T>", asBEHAVE_TEMPLATE_CALLBACK, "bool f(int&in, bool&out)", asFUNCTION(ScriptWeakRefTemplateCallback_Generic), asCALL_GENERIC); assert( r >= 0 );
r = engine->RegisterObjectBehaviour("weakref<T>", asBEHAVE_DESTRUCT, "void f()", asFUNCTION(ScriptWeakRefDestruct_Generic), asCALL_GENERIC); assert( r >= 0 );
r = engine->RegisterObjectMethod("weakref<T>", "T@ opImplCast()", asFUNCTION(CScriptWeakRef_Get_Generic), asCALL_GENERIC); assert(r >= 0);
r = engine->RegisterObjectMethod("weakref<T>", "T@ get() const", asFUNCTION(CScriptWeakRef_Get_Generic), asCALL_GENERIC); assert( r >= 0 );
r = engine->RegisterObjectMethod("weakref<T>", "weakref<T> &opHndlAssign(const weakref<T> &in)", asFUNCTION(CScriptWeakRef_Assign_Generic), asCALL_GENERIC); assert( r >= 0 );
r = engine->RegisterObjectMethod("weakref<T>", "weakref<T> &opAssign(const weakref<T> &in)", asFUNCTION(CScriptWeakRef_Assign_Generic), asCALL_GENERIC); assert(r >= 0);
r = engine->RegisterObjectMethod("weakref<T>", "bool opEquals(const weakref<T> &in) const", asFUNCTION(CScriptWeakRef_Equals_Generic), asCALL_GENERIC); assert( r >= 0 );
r = engine->RegisterObjectMethod("weakref<T>", "weakref<T> &opHndlAssign(T@)", asFUNCTION(CScriptWeakRef_Assign2_Generic), asCALL_GENERIC); assert( r >= 0 );
r = engine->RegisterObjectMethod("weakref<T>", "bool opEquals(const T@+) const", asFUNCTION(CScriptWeakRef_Equals2_Generic), asCALL_GENERIC); assert(r >= 0);
// Register another type for const handles
r = engine->RegisterObjectType("const_weakref<class T>", sizeof(CScriptWeakRef), asOBJ_VALUE | asOBJ_ASHANDLE | asOBJ_TEMPLATE | asOBJ_APP_CLASS_DAK); assert( r >= 0 );
r = engine->RegisterObjectBehaviour("const_weakref<T>", asBEHAVE_CONSTRUCT, "void f(int&in)", asFUNCTION(ScriptWeakRefConstruct_Generic), asCALL_GENERIC); assert( r>= 0 );
r = engine->RegisterObjectBehaviour("const_weakref<T>", asBEHAVE_CONSTRUCT, "void f(int&in, const T@+)", asFUNCTION(ScriptWeakRefConstruct2_Generic), asCALL_GENERIC); assert( r>= 0 );
r = engine->RegisterObjectBehaviour("const_weakref<T>", asBEHAVE_TEMPLATE_CALLBACK, "bool f(int&in, bool&out)", asFUNCTION(ScriptWeakRefTemplateCallback_Generic), asCALL_GENERIC); assert( r >= 0 );
r = engine->RegisterObjectBehaviour("const_weakref<T>", asBEHAVE_DESTRUCT, "void f()", asFUNCTION(ScriptWeakRefDestruct_Generic), asCALL_GENERIC); assert( r >= 0 );
r = engine->RegisterObjectMethod("const_weakref<T>", "const T@ opImplCast() const", asFUNCTION(CScriptWeakRef_Get_Generic), asCALL_GENERIC); assert(r >= 0);
r = engine->RegisterObjectMethod("const_weakref<T>", "const T@ get() const", asFUNCTION(CScriptWeakRef_Get_Generic), asCALL_GENERIC); assert( r >= 0 );
r = engine->RegisterObjectMethod("const_weakref<T>", "const_weakref<T> &opHndlAssign(const const_weakref<T> &in)", asFUNCTION(CScriptWeakRef_Assign_Generic), asCALL_GENERIC); assert( r >= 0 );
r = engine->RegisterObjectMethod("const_weakref<T>", "const_weakref<T> &opAssign(const const_weakref<T> &in)", asFUNCTION(CScriptWeakRef_Assign_Generic), asCALL_GENERIC); assert(r >= 0);
r = engine->RegisterObjectMethod("const_weakref<T>", "bool opEquals(const const_weakref<T> &in) const", asFUNCTION(CScriptWeakRef_Equals_Generic), asCALL_GENERIC); assert( r >= 0 );
r = engine->RegisterObjectMethod("const_weakref<T>", "const_weakref<T> &opHndlAssign(const T@)", asFUNCTION(CScriptWeakRef_Assign2_Generic), asCALL_GENERIC); assert( r >= 0 );
r = engine->RegisterObjectMethod("const_weakref<T>", "bool opEquals(const T@+) const", asFUNCTION(CScriptWeakRef_Equals2_Generic), asCALL_GENERIC); assert(r >= 0);
// Allow non-const weak references to be converted to const weak references
r = engine->RegisterObjectMethod("const_weakref<T>", "const_weakref<T> &opHndlAssign(const weakref<T> &in)", asFUNCTION(CScriptWeakRef_Assign_Generic), asCALL_GENERIC); assert( r >= 0 );
r = engine->RegisterObjectMethod("const_weakref<T>", "bool opEquals(const weakref<T> &in) const", asFUNCTION(CScriptWeakRef_Equals_Generic), asCALL_GENERIC); assert( r >= 0 );
}
void RegisterScriptWeakRef(asIScriptEngine *engine)
{
if( strstr(asGetLibraryOptions(), "AS_MAX_PORTABILITY") )
RegisterScriptWeakRef_Generic(engine);
else
RegisterScriptWeakRef_Native(engine);
}
END_AS_NAMESPACE

58
add_on/weakref/weakref.h Normal file
View File

@@ -0,0 +1,58 @@
#ifndef SCRIPTWEAKREF_H
#define SCRIPTWEAKREF_H
// The CScriptWeakRef class was originally implemented by vroad in March 2013
#ifndef ANGELSCRIPT_H
// Avoid having to inform include path if header is already include before
#include <angelscript.h>
#endif
BEGIN_AS_NAMESPACE
class CScriptWeakRef
{
public:
// Constructors
CScriptWeakRef(asITypeInfo *type);
CScriptWeakRef(const CScriptWeakRef &other);
CScriptWeakRef(void *ref, asITypeInfo *type);
~CScriptWeakRef();
// Copy the stored value from another weakref object
CScriptWeakRef &operator=(const CScriptWeakRef &other);
// Compare equalness
bool operator==(const CScriptWeakRef &o) const;
bool operator!=(const CScriptWeakRef &o) const;
// Sets a new reference
CScriptWeakRef &Set(void *newRef);
// Returns the object if it is still alive
// This will increment the refCount of the returned object
void *Get() const;
// Returns true if the contained reference is the same
bool Equals(void *ref) const;
// Returns the type of the reference held
asITypeInfo *GetRefType() const;
protected:
// These functions need to have access to protected
// members in order to call them from the script engine
friend void RegisterScriptWeakRef_Native(asIScriptEngine *engine);
void *m_ref;
asITypeInfo *m_type;
asILockableSharedBool *m_weakRefFlag;
};
void RegisterScriptWeakRef(asIScriptEngine *engine);
END_AS_NAMESPACE
#endif