Sync with Nasal upstream. Mostly fixes to naContinue(), which
FlightGear doesn't use. Also includes a performance fix for the call() builtin that should help Melchior, who was measuring lower performance for the props.Node() interface than the getprop/setprop API.
This commit is contained in:
@@ -807,12 +807,30 @@ naRef naContinue(naContext ctx)
|
||||
{
|
||||
naRef result;
|
||||
if(!ctx->callParent) naModLock();
|
||||
|
||||
ctx->dieArg = naNil();
|
||||
ctx->error[0] = 0;
|
||||
|
||||
if(setjmp(ctx->jumpHandle)) {
|
||||
if(!ctx->callParent) naModUnlock(ctx);
|
||||
else naRethrowError(ctx);
|
||||
return naNil();
|
||||
}
|
||||
|
||||
// Wipe off the old function arguments, and push the expected
|
||||
// result (either the result of our subcontext, or a synthesized
|
||||
// nil if the thrown error was from an extension function or
|
||||
// in-script die() call) before re-running the code from the
|
||||
// instruction following the error.
|
||||
ctx->opTop = ctx->opFrame;
|
||||
PUSH(naNil());
|
||||
PUSH(ctx->callChild ? naContinue(ctx->callChild) : naNil());
|
||||
|
||||
// Getting here means the child completed successfully. But
|
||||
// because its original C stack was longjmp'd out of existence,
|
||||
// there is no one left to free the context, so we have to do it.
|
||||
// This is fragile, but unfortunately required.
|
||||
if(ctx->callChild) naFreeContext(ctx->callChild);
|
||||
|
||||
result = run(ctx);
|
||||
if(!ctx->callParent) naModUnlock();
|
||||
return result;
|
||||
|
||||
@@ -10,9 +10,13 @@
|
||||
#define MAX_MARK_DEPTH 128
|
||||
|
||||
// Number of objects (per pool per thread) asked for using naGC_get().
|
||||
// Testing with fib.nas shows that this gives the best performance,
|
||||
// without too much per-thread overhead.
|
||||
#define OBJ_CACHE_SZ 128
|
||||
// The idea is that contexts can "cache" allocations to prevent thread
|
||||
// contention on the global pools. But in practice this interacts
|
||||
// very badly with small subcontext calls, which grab huge numbers of
|
||||
// cached objects and don't use them, causing far more collections
|
||||
// than necessary. Just leave it at 1 pending a rework of the
|
||||
// collector synchronization.
|
||||
#define OBJ_CACHE_SZ 1
|
||||
|
||||
enum {
|
||||
OP_NOT, OP_MUL, OP_PLUS, OP_MINUS, OP_DIV, OP_NEG,
|
||||
|
||||
@@ -9,7 +9,7 @@
|
||||
// bitmask that sets the top 16 bits. As a double, this is a
|
||||
// signalling NaN that cannot itself be produced by normal numerics
|
||||
// code. The pointer value can be reconstructed if (and only if) we
|
||||
// are guaranteed that all memory that can be poitned to by a naRef
|
||||
// are guaranteed that all memory that can be pointed to by a naRef
|
||||
// (i.e. all memory returned by naAlloc) lives in the bottom 48 bits
|
||||
// of memory. Linux on x86_64, Win64, Solaris and Irix all have such
|
||||
// policies with address spaces:
|
||||
@@ -82,7 +82,7 @@ enum { T_STR, T_VEC, T_HASH, T_CODE, T_FUNC, T_CCODE, T_GHOST,
|
||||
#define MUTABLE(r) (IS_STR(r) && PTR(r).str->hashcode == 0)
|
||||
|
||||
// This is a macro instead of a separate struct to allow compilers to
|
||||
// avoid padding. GCC on x86, at least, will always padd the size of
|
||||
// avoid padding. GCC on x86, at least, will always pad the size of
|
||||
// an embedded struct up to 32 bits. Doing it this way allows the
|
||||
// implementing objects to pack in 16 bits worth of data "for free".
|
||||
#define GC_HEADER \
|
||||
|
||||
@@ -227,29 +227,32 @@ static naRef f_call(naContext c, naRef me, int argc, naRef* args)
|
||||
if(!IS_FUNC(args[0]) || (!IS_NIL(callargs) && !IS_VEC(callargs)))
|
||||
ARGERR();
|
||||
|
||||
// Note that we don't free the subcontext, in case the user
|
||||
// re-throws the same error. That happens at the next OP_RETURN
|
||||
// or naSubContext().
|
||||
subc = naSubContext(c);
|
||||
vr = IS_NIL(callargs) ? 0 : PTR(callargs).vec->rec;
|
||||
result = naCall(subc, args[0], vr ? vr->size : 0, vr ? vr->array : 0,
|
||||
callme, callns);
|
||||
if(naGetError(subc)) {
|
||||
if(argc <= 2 || !IS_VEC(args[argc-1])) {
|
||||
naRethrowError(subc);
|
||||
} else {
|
||||
int i, sd;
|
||||
naRef errv = args[argc-1];
|
||||
if(!IS_NIL(subc->dieArg)) naVec_append(errv, subc->dieArg);
|
||||
else naVec_append(errv, NEWCSTR(subc, naGetError(subc)));
|
||||
sd = naStackDepth(subc);
|
||||
for(i=0; i<sd; i++) {
|
||||
naVec_append(errv, naGetSourceFile(subc, i));
|
||||
naVec_append(errv, naNum(naGetLine(subc, i)));
|
||||
}
|
||||
if(!naGetError(subc)) {
|
||||
naFreeContext(subc);
|
||||
return result;
|
||||
}
|
||||
|
||||
// Error handling. Note that we don't free the subcontext after an
|
||||
// error, in case the user re-throws the same error or calls
|
||||
// naContinue()
|
||||
if(argc <= 2 || !IS_VEC(args[argc-1])) {
|
||||
naRethrowError(subc);
|
||||
} else {
|
||||
int i, sd;
|
||||
naRef errv = args[argc-1];
|
||||
if(!IS_NIL(subc->dieArg)) naVec_append(errv, subc->dieArg);
|
||||
else naVec_append(errv, NEWCSTR(subc, naGetError(subc)));
|
||||
sd = naStackDepth(subc);
|
||||
for(i=0; i<sd; i++) {
|
||||
naVec_append(errv, naGetSourceFile(subc, i));
|
||||
naVec_append(errv, naNum(naGetLine(subc, i)));
|
||||
}
|
||||
}
|
||||
return result;
|
||||
return naNil();
|
||||
}
|
||||
|
||||
static naRef f_die(naContext c, naRef me, int argc, naRef* args)
|
||||
|
||||
@@ -5,17 +5,9 @@
|
||||
#include "nasal.h"
|
||||
#include "code.h"
|
||||
|
||||
static void* chkptr(void* p)
|
||||
{
|
||||
naRef foo;
|
||||
SETPTR(foo, p);
|
||||
if(PTR(foo).obj != p) *(int*)0=0;
|
||||
return p;
|
||||
}
|
||||
|
||||
void naFree(void* m) { free(m); }
|
||||
void* naAlloc(int n) { return chkptr(malloc(n)); }
|
||||
void* naRealloc(void* b, int n) { return chkptr(realloc(b, n)); }
|
||||
void* naAlloc(int n) { return malloc(n); }
|
||||
void* naRealloc(void* b, int n) { return realloc(b, n); }
|
||||
void naBZero(void* m, int n) { memset(m, 0, n); }
|
||||
|
||||
void naTempSave(naContext c, naRef r)
|
||||
|
||||
@@ -6,6 +6,15 @@ extern "C" {
|
||||
|
||||
#include "naref.h"
|
||||
|
||||
#if __GNUC__ > 2
|
||||
/* This marks the function as having no side effects and depending on
|
||||
* nothing but its arguments, which allows the optimizer to avoid
|
||||
* duplicate calls to naNil(). */
|
||||
#define GCC_PURE __attribute__((__pure__))
|
||||
#else
|
||||
#define GCC_PURE
|
||||
#endif
|
||||
|
||||
typedef struct Context* naContext;
|
||||
|
||||
// The function signature for an extension function:
|
||||
@@ -27,7 +36,7 @@ naContext naSubContext(naContext super);
|
||||
// provision for sharing it, nor for validating the source or type of
|
||||
// the pointer returned.
|
||||
void naSetUserData(naContext c, void* p);
|
||||
void* naGetUserData(naContext c);
|
||||
void* naGetUserData(naContext c) GCC_PURE;
|
||||
|
||||
// "Save" this object in the context, preventing it (and objects
|
||||
// referenced by it) from being garbage collected.
|
||||
@@ -117,19 +126,19 @@ naRef naGetSourceFile(naContext ctx, int frame);
|
||||
char* naGetError(naContext ctx);
|
||||
|
||||
// Type predicates
|
||||
int naIsNil(naRef r);
|
||||
int naIsNum(naRef r);
|
||||
int naIsString(naRef r);
|
||||
int naIsScalar(naRef r);
|
||||
int naIsVector(naRef r);
|
||||
int naIsHash(naRef r);
|
||||
int naIsCode(naRef r);
|
||||
int naIsFunc(naRef r);
|
||||
int naIsCCode(naRef r);
|
||||
int naIsNil(naRef r) GCC_PURE;
|
||||
int naIsNum(naRef r) GCC_PURE;
|
||||
int naIsString(naRef r) GCC_PURE;
|
||||
int naIsScalar(naRef r) GCC_PURE;
|
||||
int naIsVector(naRef r) GCC_PURE;
|
||||
int naIsHash(naRef r) GCC_PURE;
|
||||
int naIsCode(naRef r) GCC_PURE;
|
||||
int naIsFunc(naRef r) GCC_PURE;
|
||||
int naIsCCode(naRef r) GCC_PURE;
|
||||
|
||||
// Allocators/generators:
|
||||
naRef naNil();
|
||||
naRef naNum(double num);
|
||||
naRef naNil() GCC_PURE;
|
||||
naRef naNum(double num) GCC_PURE;
|
||||
naRef naNewString(naContext c);
|
||||
naRef naNewVector(naContext c);
|
||||
naRef naNewHash(naContext c);
|
||||
@@ -137,10 +146,10 @@ naRef naNewFunc(naContext c, naRef code);
|
||||
naRef naNewCCode(naContext c, naCFunction fptr);
|
||||
|
||||
// Some useful conversion/comparison routines
|
||||
int naEqual(naRef a, naRef b);
|
||||
int naStrEqual(naRef a, naRef b);
|
||||
int naTrue(naRef b);
|
||||
naRef naNumValue(naRef n);
|
||||
int naEqual(naRef a, naRef b) GCC_PURE;
|
||||
int naStrEqual(naRef a, naRef b) GCC_PURE;
|
||||
int naTrue(naRef b) GCC_PURE;
|
||||
naRef naNumValue(naRef n) GCC_PURE;
|
||||
naRef naStringValue(naContext c, naRef n);
|
||||
|
||||
// String utilities:
|
||||
|
||||
Reference in New Issue
Block a user