- Add JS_HAS_FUN_EXPR_STMT jsconfig.h macro and use it to ifdef a special case explicitly specified by ECMA ed. 3 to be an error: a function expression that's a statement (not at top-level). This allows one to wrap functions in if and else statements and conditionally define them.

(More work is needed to conform to ECMA ed. 3 by removing Closure objects; also we want more efficient closure calling, soon.)

- Move mislocated call to js_FoldConstants from jsemit.c's js_EmitTree, the TOK_FUNCTION case, back to jsparse.c.  This avoids redundant fold-walks over non-top-level functions.  Folding should be done at tree-gen time, not at code-gen time.

- Eliminate dead code in if-else and ?: when folding constants.

- Release tempPool arena space before early return on error in js_FoldConstants, just to be nice (all arena space gets released eventually, when the compiler finishes).
This commit is contained in:
brendan%mozilla.org 1999-11-18 20:19:56 +00:00
Родитель d502ce91a5
Коммит 0d66307ac4
4 изменённых файлов: 81 добавлений и 22 удалений

Просмотреть файл

@ -93,6 +93,7 @@
#define JS_HAS_GETTER_SETTER 0 /* has JS2 getter/setter functions */
#define JS_HAS_UNEVAL 0 /* has uneval() top-level function */
#define JS_HAS_CONST 0 /* has JS2 const as alternative var */
#define JS_HAS_FUN_EXPR_STMT 0 /* has function expression statement */
#elif JS_VERSION == 110
@ -148,6 +149,7 @@
#define JS_HAS_GETTER_SETTER 0 /* has JS2 getter/setter functions */
#define JS_HAS_UNEVAL 0 /* has uneval() top-level function */
#define JS_HAS_CONST 0 /* has JS2 const as alternative var */
#define JS_HAS_FUN_EXPR_STMT 0 /* has function expression statement */
#elif JS_VERSION == 120
@ -203,6 +205,7 @@
#define JS_HAS_GETTER_SETTER 0 /* has JS2 getter/setter functions */
#define JS_HAS_UNEVAL 0 /* has uneval() top-level function */
#define JS_HAS_CONST 0 /* has JS2 const as alternative var */
#define JS_HAS_FUN_EXPR_STMT 0 /* has function expression statement */
#elif JS_VERSION == 130
@ -258,6 +261,7 @@
#define JS_HAS_GETTER_SETTER 0 /* has JS2 getter/setter functions */
#define JS_HAS_UNEVAL 0 /* has uneval() top-level function */
#define JS_HAS_CONST 0 /* has JS2 const as alternative var */
#define JS_HAS_FUN_EXPR_STMT 0 /* has function expression statement */
#elif JS_VERSION == 140
@ -313,6 +317,7 @@
#define JS_HAS_GETTER_SETTER 0 /* has JS2 getter/setter functions */
#define JS_HAS_UNEVAL 0 /* has uneval() top-level function */
#define JS_HAS_CONST 0 /* has JS2 const as alternative var */
#define JS_HAS_FUN_EXPR_STMT 0 /* has function expression statement */
#elif JS_VERSION == 150
@ -368,6 +373,7 @@
#define JS_HAS_GETTER_SETTER 1 /* has JS2 getter/setter functions */
#define JS_HAS_UNEVAL 1 /* has uneval() top-level function */
#define JS_HAS_CONST 1 /* has JS2 const as alternative var */
#define JS_HAS_FUN_EXPR_STMT 1 /* has function expression statement */
#else

Просмотреть файл

@ -585,10 +585,8 @@ js_EmitTree(JSContext *cx, JSCodeGenerator *cg, JSParseNode *pn)
{
JSFunction *fun;
/* Fold constants and generate code for the function's body. */
/* Generate code for the function's body. */
pn2 = pn->pn_body;
if (!js_FoldConstants(cx, pn2))
return JS_FALSE;
if (!js_InitCodeGenerator(cx, &cg2, cg->filename,
pn->pn_pos.begin.lineno,
cg->principals)) {

Просмотреть файл

@ -276,28 +276,26 @@ js_CompileTokenStream(JSContext *cx, JSObject *chain, JSTokenStream *ts,
if (pn && pn->pn_pos.end.lineno == ts->lineno) {
ok = WellTerminated(cx, ts, TOK_FUNCTION);
if (!ok)
goto out;
break;
}
} else {
js_UngetToken(ts);
pn = Statement(cx, ts, &cg->treeContext);
if (pn) {
ok = js_FoldConstants(cx, pn);
if (!ok)
goto out;
}
}
if (pn) {
ok = js_AllocTryNotes(cx, cg);
if (ok)
ok = js_EmitTree(cx, cg, pn);
} else {
if (!pn) {
ok = JS_FALSE;
break;
}
ok = js_FoldConstants(cx, pn);
if (!ok)
break;
ok = js_AllocTryNotes(cx, cg);
if (!ok)
break;
ok = js_EmitTree(cx, cg, pn);
} while (ok);
out:
JS_ENABLE_GC(cx->runtime);
ts->flags &= ~TSF_ERROR;
cx->fp = fp;
@ -409,9 +407,8 @@ js_CompileFunctionBody(JSContext *cx, JSTokenStream *ts, JSFunction *fun)
if (!pn) {
ok = JS_FALSE;
} else {
ok = js_FoldConstants(cx, pn);
if (ok)
ok = js_EmitFunctionBody(cx, &funcg, pn, fun);
ok = js_FoldConstants(cx, pn) &&
js_EmitFunctionBody(cx, &funcg, pn, fun);
}
JS_ENABLE_GC(cx->runtime);
@ -547,7 +544,7 @@ FunctionDef(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc,
* where s contains a function definition, or if in a with statement being
* compiled from the same source that contains this function. Otherwise,
* if in a lambda expression, generate a function object reference. Else,
* generate an annotated NOP if a top-level function.
* generate an annotated NOP for a top-level function.
*/
if (outerFun || cx->fp->scopeChain != parent || InWithStatement(tc))
pn->pn_op = JSOP_CLOSURE;
@ -1449,11 +1446,18 @@ Statement(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc)
return pn2;
}
/* Check explicity against (multi-line) function statement */
/* Check termination of (possibly multi-line) function expression. */
if (pn2->pn_pos.end.lineno == ts->lineno &&
!WellTerminated(cx, ts, lastExprType)) {
return NULL;
}
#if JS_HAS_FUN_EXPR_STMT
/* ECMA ed. 3 extension: function expression statement is a closure. */
if (pn2->pn_type == TOK_FUNCTION)
pn2->pn_op = JSOP_CLOSURE;
#endif
pn = NewParseNode(cx, &CURRENT_TOKEN(ts), PN_UNARY);
if (!pn)
return NULL;
@ -2579,7 +2583,7 @@ PrimaryExpr(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc)
JSBool
js_FoldConstants(JSContext *cx, JSParseNode *pn)
{
JSParseNode *pn1=NULL, *pn2=NULL, *pn3=NULL;
JSParseNode *pn1 = NULL, *pn2 = NULL, *pn3 = NULL;
switch (pn->pn_arity) {
case PN_FUNC:
@ -2635,6 +2639,42 @@ js_FoldConstants(JSContext *cx, JSParseNode *pn)
}
switch (pn->pn_type) {
case TOK_IF:
case TOK_HOOK:
/* Reduce 'if (C) T; else E' into T for true C, E for false. */
switch (pn1->pn_type) {
case TOK_NUMBER:
if (pn1->pn_dval == 0)
pn2 = pn3;
break;
case TOK_STRING:
if (ATOM_TO_STRING(pn1->pn_atom)->length == 0)
pn2 = pn3;
break;
case TOK_PRIMARY:
if (pn1->pn_op == JSOP_TRUE)
break;
if (pn1->pn_op == JSOP_FALSE || pn1->pn_op == JSOP_NULL) {
pn2 = pn3;
break;
}
/* FALL THROUGH */
default:
/* Early return to dodge common code that copies pn2 to pn. */
return JS_TRUE;
}
if (pn2) {
/* pn2 is the then- or else-statement subtree to compile. */
PN_COPY_OVER(pn, pn2);
} else {
/* False condition and no else: make pn an empty statement. */
pn->pn_type = TOK_SEMI;
pn->pn_arity = PN_UNARY;
pn->pn_kid = NULL;
}
break;
case TOK_PLUS:
if (pn1->pn_type == TOK_STRING && pn2->pn_type == TOK_STRING) {
JSString *str1, *str2;
@ -2659,9 +2699,9 @@ js_FoldConstants(JSContext *cx, JSParseNode *pn)
js_strncpy(chars + length1, str2->chars, length2);
chars[length] = 0;
pn->pn_atom = js_AtomizeChars(cx, chars, length, 0);
JS_ARENA_RELEASE(&cx->tempPool, mark);
if (!pn->pn_atom)
return JS_FALSE;
JS_ARENA_RELEASE(&cx->tempPool, mark);
pn->pn_type = TOK_STRING;
pn->pn_op = JSOP_STRING;
pn->pn_arity = PN_NULLARY;

Просмотреть файл

@ -227,6 +227,21 @@ struct JSParseNode {
#define pn_attrs pn_u.name.attrs
#define pn_dval pn_u.dval
/* Copy pn2 over pn, preserving pn->pn_pos and pn->pn_offset. */
#define PN_COPY_OVER(pn, pn2) \
JS_BEGIN_MACRO \
(pn)->pn_type = (pn2)->pn_type; \
(pn)->pn_op = (pn2)->pn_op; \
(pn)->pn_arity = (pn2)->pn_arity; \
(pn)->pn_u = (pn2)->pn_u; \
JS_END_MACRO
/* True if pn is a parsenode representing a literal constant. */
#define PN_IS_CONSTANT(pn) \
((pn)->pn_type == TOK_NUMBER || \
(pn)->pn_type == TOK_STRING || \
((pn)->pn_type == TOK_PRIMARY && (pn)->pn_op != JSOP_THIS))
/*
* Compute a pointer to the last JSParseNode element in a singly-linked list.
* NB: list must be non-empty for correct PN_LAST usage!