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++