diff --git a/simgear/nasal/code.c b/simgear/nasal/code.c index 782509da..08d710f8 100644 --- a/simgear/nasal/code.c +++ b/simgear/nasal/code.c @@ -424,23 +424,37 @@ static void setSymbol(struct Frame* f, naRef sym, naRef val) naHash_set(f->locals, sym, val); } +static const char* ghostGetMember(naContext ctx, naRef obj, naRef field, naRef* out) +{ + naGhostType* gtype = PTR(obj).ghost->gtype; + if (!gtype->get_member) return "ghost does not support member access"; + return gtype->get_member(ctx, PTR(obj).ghost->ptr, field, out); +} + // Funky API: returns null to indicate no member, an empty string to // indicate success, or a non-empty error message. Works this way so // we can generate smart error messages without throwing them with a // longjmp -- this gets called under naMember_get() from C code. -static const char* getMember_r(naRef obj, naRef field, naRef* out, int count) +static const char* getMember_r(naContext ctx, naRef obj, naRef field, naRef* out, int count) { int i; naRef p; struct VecRec* pv; if(--count < 0) return "too many parents"; - if(!IS_HASH(obj)) return "non-objects have no members"; - if(naHash_get(obj, field, out)) return ""; - if(!naHash_get(obj, globals->parentsRef, &p)) return 0; + if(!IS_HASH(obj) && !IS_GHOST(obj)) return "non-objects have no members"; + + if (IS_GHOST(obj)) { + if (ghostGetMember(ctx, obj, field, out)) return ""; + if(!ghostGetMember(ctx, obj, globals->parentsRef, &p)) return 0; + } else { + if(naHash_get(obj, field, out)) return ""; + if(!naHash_get(obj, globals->parentsRef, &p)) return 0; + } + if(!IS_VEC(p)) return "object \"parents\" field not vector"; pv = PTR(p).vec->rec; for(i=0; pv && isize; i++) { - const char* err = getMember_r(pv->array[i], field, out, count); + const char* err = getMember_r(ctx, pv->array[i], field, out, count); if(err) return err; /* either an error or success */ } return 0; @@ -449,14 +463,29 @@ static const char* getMember_r(naRef obj, naRef field, naRef* out, int count) static void getMember(naContext ctx, naRef obj, naRef fld, naRef* result, int count) { - const char* err = getMember_r(obj, fld, result, count); + const char* err = getMember_r(ctx, obj, fld, result, count); if(!err) naRuntimeError(ctx, "No such member: %s", naStr_data(fld)); if(err[0]) naRuntimeError(ctx, err); } -int naMember_get(naRef obj, naRef field, naRef* out) +static void setMember(naContext ctx, naRef obj, naRef fld, naRef value) { - const char* err = getMember_r(obj, field, out, 64); + if (IS_GHOST(obj)) { + naGhostType* gtype = PTR(obj).ghost->gtype; + if (!gtype->set_member) ERR(ctx, "ghost does not support member access"); + gtype->set_member(ctx, PTR(obj).ghost->ptr, fld, value); + ctx->opTop -= 2; + return; + } + + if(!IS_HASH(obj)) ERR(ctx, "non-objects have no members"); + naHash_set(obj, fld, value); + ctx->opTop -= 2; +} + +int naMember_get(naContext ctx, naRef obj, naRef field, naRef* out) +{ + const char* err = getMember_r(ctx, obj, field, out, 64); return err && !err[0]; } @@ -619,9 +648,7 @@ static naRef run(naContext ctx) getMember(ctx, STK(1), CONSTARG(), &STK(1), 64); break; case OP_SETMEMBER: - if(!IS_HASH(STK(2))) ERR(ctx, "non-objects have no members"); - naHash_set(STK(2), STK(1), STK(3)); - ctx->opTop -= 2; + setMember(ctx, STK(2), STK(1), STK(3)); break; case OP_INSERT: containerSet(ctx, STK(2), STK(1), STK(3)); diff --git a/simgear/nasal/misc.c b/simgear/nasal/misc.c index 8640aea0..1c3a6195 100644 --- a/simgear/nasal/misc.c +++ b/simgear/nasal/misc.c @@ -121,12 +121,24 @@ naRef naNewFunc(struct Context* c, naRef code) naRef naNewGhost(naContext c, naGhostType* type, void* ptr) { + // ensure 'simple' ghost users don't see garbage for these fields + type->get_member = 0; + type->set_member = 0; + naRef ghost = naNew(c, T_GHOST); PTR(ghost).ghost->gtype = type; PTR(ghost).ghost->ptr = ptr; return ghost; } +naRef naNewGhost2(naContext c, naGhostType* t, void* ptr) +{ + naRef ghost = naNew(c, T_GHOST); + PTR(ghost).ghost->gtype = t; + PTR(ghost).ghost->ptr = ptr; + return ghost; +} + naGhostType* naGhost_type(naRef ghost) { if(!IS_GHOST(ghost)) return 0; diff --git a/simgear/nasal/nasal.h b/simgear/nasal/nasal.h index 6857013a..3c5140e0 100644 --- a/simgear/nasal/nasal.h +++ b/simgear/nasal/nasal.h @@ -99,8 +99,8 @@ void naRethrowError(naContext subc); // Retrieve the specified member from the object, respecting the // "parents" array as for "object.field". Returns zero for missing // fields. -int naMember_get(naRef obj, naRef field, naRef* out); -int naMember_cget(naRef obj, const char* field, naRef* out); +int naMember_get(naContext c, naRef obj, naRef field, naRef* out); +int naMember_cget(naContext c, naRef obj, const char* field, naRef* out); // Returns a hash containing functions from the Nasal standard library // Useful for passing as a namespace to an initial function call @@ -181,8 +181,12 @@ void naHash_keys(naRef dst, naRef hash); typedef struct naGhostType { void(*destroy)(void*); const char* name; + const char*(*get_member)(naContext c, void*, naRef key, naRef* out); + void(*set_member)(naContext c, void*, naRef key, naRef val); } naGhostType; + naRef naNewGhost(naContext c, naGhostType* t, void* ghost); +naRef naNewGhost2(naContext c, naGhostType* t, void* ghost); naGhostType* naGhost_type(naRef ghost); void* naGhost_ptr(naRef ghost); int naIsGhost(naRef r);