]> git.mxchange.org Git - simgear.git/blobdiff - simgear/nasal/codegen.c
Fix crash in code generator for some bad lvalue expressions
[simgear.git] / simgear / nasal / codegen.c
index c909c92888996436a37e48228799791029265b08..15276e8c96ec4f59c68278c49eb1a45749371e76 100644 (file)
@@ -79,10 +79,11 @@ naRef naInternSymbol(naRef sym)
 
 static int findConstantIndex(struct Parser* p, struct Token* t)
 {
-    naRef c;
+    naRef c, dummy;
     if(t->type == TOK_NIL) c = naNil();
     else if(t->str) {
         c = naStr_fromdata(naNewString(p->context), t->str, t->strlen);
+        naHash_get(globals->symbols, c, &dummy); // noop, make c immutable
         if(t->type == TOK_SYMBOL) c = naInternSymbol(c);
     } else if(t->type == TOK_FUNC) c = newLambda(p, t);
     else if(t->type == TOK_LITERAL) c = naNum(t->num);
@@ -90,42 +91,7 @@ static int findConstantIndex(struct Parser* p, struct Token* t)
     return internConstant(p, c);
 }
 
-static int lastExprInBlock(struct Token* t)
-{
-    if(!t->parent) return 1;
-    if(t->parent->type == TOK_TOP || t->parent->type == TOK_LCURL) return 1;
-    if(t->parent->type == TOK_SEMI)
-        if(!t->next || t->next->type == TOK_EMPTY)
-            return 1;
-    return 0;
-}
-
-// Returns true if the node is in "tail context" -- either a child of
-// a return, the last child of a func block, or else the
-// last child of an if/elsif/if that is itself in tail context.
-static int tailContext(struct Token* t)
-{
-    if(t->parent && t->parent->type == TOK_RETURN)
-        return 1;
-    else if(!lastExprInBlock(t))
-        return 0;
-
-    // Walk up the tree.  It is ok to see semicolons, else's, elsifs
-    // and curlies.  If we reach the top or a func, then we are in
-    // tail context.  If we hit an if, then we are in tail context
-    // only if the "if" node is.
-    while((t = t->parent) != 0)
-        switch(t->type) {
-        case TOK_SEMI: case TOK_LCURL: break;
-        case TOK_ELSE: case TOK_ELSIF: break;
-        case TOK_TOP:  case TOK_FUNC:  return 1;
-        case TOK_IF:                   return tailContext(t);
-        default:                       return 0;
-        }
-    return 0;
-}
-
-static void genScalarConstant(struct Parser* p, struct Token* t)
+static int genScalarConstant(struct Parser* p, struct Token* t)
 {
     // These opcodes are for special-case use in other constructs, but
     // we might as well use them here to save a few bytes in the
@@ -137,26 +103,29 @@ static void genScalarConstant(struct Parser* p, struct Token* t)
     } else {
         int idx = findConstantIndex(p, t);
         emitImmediate(p, OP_PUSHCONST, idx);
+        return idx;
     }
+    return 0;
 }
 
-static int genLValue(struct Parser* p, struct Token* t)
+static int genLValue(struct Parser* p, struct Token* t, int* cidx)
 {
-    if(t->type == TOK_LPAR) {
-        return genLValue(p, LEFT(t)); // Handle stuff like "(a) = 1"
+    if(!t) naParseError(p, "bad lvalue", -1);
+    if(t->type == TOK_LPAR && t->rule != PREC_SUFFIX) {
+        return genLValue(p, LEFT(t), cidx); // Handle stuff like "(a) = 1"
     } else if(t->type == TOK_SYMBOL) {
-        genScalarConstant(p, t);
+        *cidx = genScalarConstant(p, t);
         return OP_SETSYM;
     } else if(t->type == TOK_DOT && RIGHT(t) && RIGHT(t)->type == TOK_SYMBOL) {
         genExpr(p, LEFT(t));
-        genScalarConstant(p, RIGHT(t));
+        *cidx = genScalarConstant(p, RIGHT(t));
         return OP_SETMEMBER;
     } else if(t->type == TOK_LBRA) {
         genExpr(p, LEFT(t));
         genExpr(p, RIGHT(t));
         return OP_INSERT;
-    } else if(t->type == TOK_VAR && RIGHT(t)->type == TOK_SYMBOL) {
-        genScalarConstant(p, RIGHT(t));
+    } else if(t->type == TOK_VAR && RIGHT(t) && RIGHT(t)->type == TOK_SYMBOL) {
+        *cidx = genScalarConstant(p, RIGHT(t));
         return OP_SETLOCAL;
     } else {
         naParseError(p, "bad lvalue", t->line);
@@ -164,9 +133,35 @@ static int genLValue(struct Parser* p, struct Token* t)
     }
 }
 
+static void genEqOp(int op, struct Parser* p, struct Token* t)
+{
+    int cidx, setop = genLValue(p, LEFT(t), &cidx);
+    if(setop == OP_SETMEMBER) {
+        emit(p, OP_DUP2);
+        emit(p, OP_POP);
+        emitImmediate(p, OP_MEMBER, cidx);
+    } else if(setop == OP_INSERT) {
+        emit(p, OP_DUP2);
+        emit(p, OP_EXTRACT);
+    } else // OP_SETSYM, OP_SETLOCAL
+        emitImmediate(p, OP_LOCAL, cidx);
+    genExpr(p, RIGHT(t));
+    emit(p, op);
+    emit(p, setop);
+}
+
 static int defArg(struct Parser* p, struct Token* t)
 {
     if(t->type == TOK_LPAR) return defArg(p, RIGHT(t));
+    if(t->type == TOK_MINUS && RIGHT(t) && 
+       RIGHT(t)->type == TOK_LITERAL && !RIGHT(t)->str)
+    {
+        /* default arguments are constants, but "-1" parses as two
+         * tokens, so we have to subset the expression generator for that
+         * case */
+        RIGHT(t)->num *= -1;
+        return defArg(p, RIGHT(t));
+    }
     return findConstantIndex(p, t);
 }
 
@@ -273,8 +268,6 @@ static void genFuncall(struct Parser* p, struct Token* t)
         genExpr(p, LEFT(t));
     }
     if(RIGHT(t)) nargs = genList(p, RIGHT(t), 0);
-    if(tailContext(t))
-        op = op == OP_FCALL ? OP_FTAIL : OP_MTAIL;
     emitImmediate(p, op, nargs);
 }
 
@@ -314,15 +307,12 @@ static void fixJumpTarget(struct Parser* p, int spot)
 
 static void genShortCircuit(struct Parser* p, struct Token* t)
 {
-    int jumpNext, jumpEnd, isAnd = (t->type == TOK_AND);
+    int end;
     genExpr(p, LEFT(t));
-    if(isAnd) emit(p, OP_NOT);
-    jumpNext = emitJump(p, OP_JIFNOT);
-    emit(p, isAnd ? OP_PUSHNIL : OP_PUSHONE);
-    jumpEnd = emitJump(p, OP_JMP);
-    fixJumpTarget(p, jumpNext);
+    end = emitJump(p, t->type == TOK_AND ? OP_JIFNOT : OP_JIFTRUE);
+    emit(p, OP_POP);
     genExpr(p, RIGHT(t));
-    fixJumpTarget(p, jumpEnd);
+    fixJumpTarget(p, end);
 }
 
 
@@ -330,7 +320,7 @@ static void genIf(struct Parser* p, struct Token* tif, struct Token* telse)
 {
     int jumpNext, jumpEnd;
     genExpr(p, tif->children); // the test
-    jumpNext = emitJump(p, OP_JIFNOT);
+    jumpNext = emitJump(p, OP_JIFNOTPOP);
     genExprList(p, tif->children->next->children); // the body
     jumpEnd = emitJump(p, OP_JMP);
     fixJumpTarget(p, jumpNext);
@@ -354,7 +344,7 @@ static void genQuestion(struct Parser* p, struct Token* t)
     if(!RIGHT(t) || RIGHT(t)->type != TOK_COLON)
         naParseError(p, "invalid ?: expression", t->line);
     genExpr(p, LEFT(t)); // the test
-    jumpNext = emitJump(p, OP_JIFNOT);
+    jumpNext = emitJump(p, OP_JIFNOTPOP);
     genExpr(p, LEFT(RIGHT(t))); // the "if true" expr
     jumpEnd = emitJump(p, OP_JMP);
     fixJumpTarget(p, jumpNext);
@@ -400,7 +390,7 @@ static void genForWhile(struct Parser* p, struct Token* init,
     pushLoop(p, label);
     loopTop = p->cg->codesz;
     genExpr(p, test);
-    jumpEnd = emitJump(p, OP_JIFNOT);
+    jumpEnd = emitJump(p, OP_JIFNOTPOP);
     genLoop(p, body, update, label, loopTop, jumpEnd);
 }
 
@@ -444,7 +434,7 @@ static void genFor(struct Parser* p, struct Token* t)
 
 static void genForEach(struct Parser* p, struct Token* t)
 {
-    int loopTop, jumpEnd, assignOp;
+    int loopTop, jumpEnd, assignOp, dummy;
     struct Token *elem, *body, *vec, *label=0;
     struct Token *h = LEFT(LEFT(t));
     int semis = countSemis(h);
@@ -460,17 +450,19 @@ static void genForEach(struct Parser* p, struct Token* t)
     vec = RIGHT(h);
     body = RIGHT(t)->children;
 
-    pushLoop(p, label);
     genExpr(p, vec);
     emit(p, OP_PUSHZERO);
+    pushLoop(p, label);
     loopTop = p->cg->codesz;
-    emit(p, OP_EACH);
-    jumpEnd = emitJump(p, OP_JIFNIL);
-    assignOp = genLValue(p, elem);
+    emit(p, t->type == TOK_FOREACH ? OP_EACH : OP_INDEX);
+    jumpEnd = emitJump(p, OP_JIFEND);
+    assignOp = genLValue(p, elem, &dummy);
     emit(p, OP_XCHG);
     emit(p, assignOp);
     emit(p, OP_POP);
     genLoop(p, body, 0, label, loopTop, jumpEnd);
+    emit(p, OP_POP); // Pull off the vector and index
+    emit(p, OP_POP);
 }
 
 static int tokMatch(struct Token* a, struct Token* b)
@@ -498,9 +490,9 @@ static void genBreakContinue(struct Parser* p, struct Token* t)
     bp = p->cg->loops[p->cg->loopTop - levels].breakIP;
     cp = p->cg->loops[p->cg->loopTop - levels].contIP;
     for(i=0; i<levels; i++)
-        emit(p, OP_BREAK);
+        emit(p, (i<levels-1) ? OP_BREAK2 : OP_BREAK);
     if(t->type == TOK_BREAK)
-        emit(p, OP_PUSHNIL); // breakIP is always a JIFNOT/JIFNIL!
+        emit(p, OP_PUSHEND); // breakIP is always a JIFNOTPOP/JIFEND!
     emitImmediate(p, OP_JMP, t->type == TOK_BREAK ? bp : cp);
 }
 
@@ -521,7 +513,9 @@ static void newLineEntry(struct Parser* p, int line)
 
 static void genExpr(struct Parser* p, struct Token* t)
 {
-    int i;
+    int i, dummy;
+    if(!t) naParseError(p, "parse error", -1); // throw line -1...
+    p->errLine = t->line;                      // ...to use this one instead
     if(t->line != p->cg->lastLine)
         newLineEntry(p, t->line);
     p->cg->lastLine = t->line;
@@ -539,6 +533,7 @@ static void genExpr(struct Parser* p, struct Token* t)
         genFor(p, t);
         break;
     case TOK_FOREACH:
+    case TOK_FORINDEX:
         genForEach(p, t);
         break;
     case TOK_BREAK: case TOK_CONTINUE:
@@ -567,7 +562,7 @@ static void genExpr(struct Parser* p, struct Token* t)
         genHash(p, LEFT(t));
         break;
     case TOK_ASSIGN:
-        i = genLValue(p, LEFT(t));
+        i = genLValue(p, LEFT(t), &dummy);
         genExpr(p, RIGHT(t));
         emit(p, i); // use the op appropriate to the lvalue
         break;
@@ -590,7 +585,7 @@ static void genExpr(struct Parser* p, struct Token* t)
     case TOK_MINUS:
         if(BINARY(t)) {
             genBinOp(OP_MINUS,  p, t);  // binary subtraction
-        } else if(RIGHT(t)->type == TOK_LITERAL && !RIGHT(t)->str) {
+        } else if(RIGHT(t) && RIGHT(t)->type == TOK_LITERAL && !RIGHT(t)->str) {
             RIGHT(t)->num *= -1;        // Pre-negate constants
             genScalarConstant(p, RIGHT(t));
         } else {
@@ -604,7 +599,7 @@ static void genExpr(struct Parser* p, struct Token* t)
         break;
     case TOK_DOT:
         genExpr(p, LEFT(t));
-        if(RIGHT(t)->type != TOK_SYMBOL)
+        if(!RIGHT(t) || RIGHT(t)->type != TOK_SYMBOL)
             naParseError(p, "object field not symbol", RIGHT(t)->line);
         emitImmediate(p, OP_MEMBER, findConstantIndex(p, RIGHT(t)));
         break;
@@ -623,6 +618,11 @@ static void genExpr(struct Parser* p, struct Token* t)
     case TOK_NEQ:   genBinOp(OP_NEQ,    p, t); break;
     case TOK_GT:    genBinOp(OP_GT,     p, t); break;
     case TOK_GTE:   genBinOp(OP_GTE,    p, t); break;
+    case TOK_PLUSEQ:  genEqOp(OP_PLUS, p, t);  break;
+    case TOK_MINUSEQ: genEqOp(OP_MINUS, p, t); break;
+    case TOK_MULEQ:   genEqOp(OP_MUL, p, t);   break;
+    case TOK_DIVEQ:   genEqOp(OP_DIV, p, t);   break;
+    case TOK_CATEQ:   genEqOp(OP_CAT, p, t);   break;
     default:
         naParseError(p, "parse error", t->line);
     };
@@ -630,7 +630,7 @@ static void genExpr(struct Parser* p, struct Token* t)
 
 static void genExprList(struct Parser* p, struct Token* t)
 {
-    if(t->type == TOK_SEMI) {
+    if(t && t->type == TOK_SEMI) {
         genExpr(p, LEFT(t));
         if(RIGHT(t) && RIGHT(t)->type != TOK_EMPTY) {
             emit(p, OP_POP);
@@ -664,7 +664,7 @@ naRef naCodeGen(struct Parser* p, struct Token* block, struct Token* arglist)
 
     // Now make a code object
     codeObj = naNewCode(p->context);
-    code = codeObj.ref.ptr.code;
+    code = PTR(codeObj).code;
 
     // Parse the argument list, if any
     code->restArgSym = globals->argRef;