mirror of
https://github.com/mozilla/gecko-dev.git
synced 2025-01-26 23:23:33 +00:00
First big wave of js1.7 changes (326466, 336376, r=mrbkap).
This commit is contained in:
parent
229cb1758f
commit
bcf6aea253
@ -82,6 +82,7 @@ CSRCS = \
|
||||
jsgc.c \
|
||||
jshash.c \
|
||||
jsinterp.c \
|
||||
jsiter.c \
|
||||
jslock.c \
|
||||
jslog2.c \
|
||||
jslong.c \
|
||||
@ -124,6 +125,7 @@ EXPORTS = \
|
||||
jsgc.h \
|
||||
jshash.h \
|
||||
jsinterp.h \
|
||||
jsiter.h \
|
||||
jslock.h \
|
||||
jslong.h \
|
||||
jsmath.h \
|
||||
|
@ -158,6 +158,7 @@ JS_HFILES = \
|
||||
jsfun.h \
|
||||
jsgc.h \
|
||||
jsinterp.h \
|
||||
jsiter.h \
|
||||
jslibmath.h \
|
||||
jslock.h \
|
||||
jsmath.h \
|
||||
@ -198,6 +199,7 @@ OTHER_HFILES = \
|
||||
prmjtime.h \
|
||||
resource.h \
|
||||
jsopcode.tbl \
|
||||
jsproto.tbl \
|
||||
js.msg \
|
||||
jsshell.msg \
|
||||
jskeyword.tbl \
|
||||
@ -227,6 +229,7 @@ JS_CFILES = \
|
||||
jsgc.c \
|
||||
jshash.c \
|
||||
jsinterp.c \
|
||||
jsiter.c \
|
||||
jslock.c \
|
||||
jslog2.c \
|
||||
jslong.c \
|
||||
|
@ -2299,7 +2299,7 @@ global_resolve(JSContext *cx, JSObject *obj, jsval id, uintN flags,
|
||||
}
|
||||
|
||||
JSClass global_class = {
|
||||
"global", JSCLASS_NEW_RESOLVE,
|
||||
"global", JSCLASS_NEW_RESOLVE | JSCLASS_GLOBAL_FLAGS,
|
||||
JS_PropertyStub, JS_PropertyStub,
|
||||
JS_PropertyStub, JS_PropertyStub,
|
||||
global_enumerate, (JSResolveOp) global_resolve,
|
||||
|
@ -81,7 +81,7 @@
|
||||
|
||||
MSG_DEF(JSMSG_NOT_AN_ERROR, 0, 0, JSEXN_NONE, "<Error #0 is reserved>")
|
||||
MSG_DEF(JSMSG_NOT_DEFINED, 1, 1, JSEXN_REFERENCEERR, "{0} is not defined")
|
||||
MSG_DEF(JSMSG_NO_REG_EXPS, 2, 1, JSEXN_INTERNALERR, "sorry, regular expression are not supported")
|
||||
MSG_DEF(JSMSG_INACTIVE, 2, 0, JSEXN_INTERNALERR, "nothing active on context")
|
||||
MSG_DEF(JSMSG_MORE_ARGS_NEEDED, 3, 3, JSEXN_TYPEERR, "{0} requires more than {1} argument{2}")
|
||||
MSG_DEF(JSMSG_BAD_CHAR, 4, 1, JSEXN_INTERNALERR, "invalid format character {0}")
|
||||
MSG_DEF(JSMSG_BAD_TYPE, 5, 1, JSEXN_TYPEERR, "unknown type {0}")
|
||||
@ -100,7 +100,7 @@ MSG_DEF(JSMSG_NEED_DIET, 17, 1, JSEXN_INTERNALERR, "{0} too large"
|
||||
MSG_DEF(JSMSG_TOO_MANY_LOCAL_ROOTS, 18, 0, JSEXN_ERR, "out of local root space")
|
||||
MSG_DEF(JSMSG_READ_ONLY, 19, 1, JSEXN_ERR, "{0} is read-only")
|
||||
MSG_DEF(JSMSG_BAD_FORMAL, 20, 0, JSEXN_SYNTAXERR, "malformed formal parameter")
|
||||
MSG_DEF(JSMSG_SAME_FORMAL, 21, 1, JSEXN_SYNTAXERR, "duplicate formal argument {0}")
|
||||
MSG_DEF(JSMSG_BAD_ITERATOR, 21, 3, JSEXN_TYPEERR, "{0} has invalid {1} value {2}")
|
||||
MSG_DEF(JSMSG_NOT_FUNCTION, 22, 1, JSEXN_TYPEERR, "{0} is not a function")
|
||||
MSG_DEF(JSMSG_NOT_CONSTRUCTOR, 23, 1, JSEXN_TYPEERR, "{0} is not a constructor")
|
||||
MSG_DEF(JSMSG_STACK_OVERFLOW, 24, 1, JSEXN_INTERNALERR, "stack overflow in {0}")
|
||||
@ -113,7 +113,7 @@ MSG_DEF(JSMSG_BAD_SHARP_USE, 30, 1, JSEXN_ERR, "invalid sharp variable
|
||||
MSG_DEF(JSMSG_BAD_INSTANCEOF_RHS, 31, 1, JSEXN_TYPEERR, "invalid 'instanceof' operand {0}")
|
||||
MSG_DEF(JSMSG_BAD_BYTECODE, 32, 1, JSEXN_INTERNALERR, "unimplemented JavaScript bytecode {0}")
|
||||
MSG_DEF(JSMSG_BAD_RADIX, 33, 1, JSEXN_ERR, "illegal radix {0}")
|
||||
MSG_DEF(JSMSG_NAN, 34, 1, JSEXN_ERR, "{0} is not a number")
|
||||
MSG_DEF(JSMSG_UNUSED34, 34, 0, JSEXN_NONE, "")
|
||||
MSG_DEF(JSMSG_CANT_CONVERT, 35, 1, JSEXN_ERR, "can't convert {0} to an integer")
|
||||
MSG_DEF(JSMSG_CYCLIC_VALUE, 36, 1, JSEXN_ERR, "cyclic {0} value")
|
||||
MSG_DEF(JSMSG_PERMANENT, 37, 1, JSEXN_ERR, "{0} is permanent")
|
||||
@ -129,10 +129,10 @@ MSG_DEF(JSMSG_BAD_QUANTIFIER, 46, 1, JSEXN_SYNTAXERR, "invalid quantifi
|
||||
MSG_DEF(JSMSG_MIN_TOO_BIG, 47, 1, JSEXN_SYNTAXERR, "overlarge minimum {0}")
|
||||
MSG_DEF(JSMSG_MAX_TOO_BIG, 48, 1, JSEXN_SYNTAXERR, "overlarge maximum {0}")
|
||||
MSG_DEF(JSMSG_OUT_OF_ORDER, 49, 1, JSEXN_SYNTAXERR, "maximum {0} less than minimum")
|
||||
MSG_DEF(JSMSG_ZERO_QUANTIFIER, 50, 1, JSEXN_SYNTAXERR, "zero quantifier {0}")
|
||||
MSG_DEF(JSMSG_UNTERM_QUANTIFIER, 51, 1, JSEXN_SYNTAXERR, "unterminated quantifier {0}")
|
||||
MSG_DEF(JSMSG_EMPTY_BEFORE_STAR, 52, 0, JSEXN_SYNTAXERR, "regular expression before * could be empty")
|
||||
MSG_DEF(JSMSG_EMPTY_BEFORE_PLUS, 53, 0, JSEXN_SYNTAXERR, "regular expression before + could be empty")
|
||||
MSG_DEF(JSMSG_UNUSED50, 50, 0, JSEXN_NONE, "")
|
||||
MSG_DEF(JSMSG_UNUSED51, 51, 0, JSEXN_NONE, "")
|
||||
MSG_DEF(JSMSG_UNUSED52, 52, 0, JSEXN_NONE, "")
|
||||
MSG_DEF(JSMSG_UNUSED53, 53, 0, JSEXN_NONE, "")
|
||||
MSG_DEF(JSMSG_MISSING_PAREN, 54, 0, JSEXN_SYNTAXERR, "unterminated parenthetical")
|
||||
MSG_DEF(JSMSG_UNTERM_CLASS, 55, 1, JSEXN_SYNTAXERR, "unterminated character class {0}")
|
||||
MSG_DEF(JSMSG_TRAILING_SLASH, 56, 0, JSEXN_SYNTAXERR, "trailing \\ in regular expression")
|
||||
@ -203,7 +203,7 @@ MSG_DEF(JSMSG_FINALLY_WITHOUT_TRY, 120, 0, JSEXN_SYNTAXERR, "finally without
|
||||
MSG_DEF(JSMSG_LABEL_NOT_FOUND, 121, 0, JSEXN_SYNTAXERR, "label not found")
|
||||
MSG_DEF(JSMSG_TOUGH_BREAK, 122, 0, JSEXN_SYNTAXERR, "invalid break")
|
||||
MSG_DEF(JSMSG_BAD_CONTINUE, 123, 0, JSEXN_SYNTAXERR, "invalid continue")
|
||||
MSG_DEF(JSMSG_BAD_RETURN, 124, 0, JSEXN_SYNTAXERR, "invalid return")
|
||||
MSG_DEF(JSMSG_BAD_RETURN_OR_YIELD, 124, 1, JSEXN_SYNTAXERR, "{0} not in function")
|
||||
MSG_DEF(JSMSG_BAD_LABEL, 125, 0, JSEXN_SYNTAXERR, "invalid label")
|
||||
MSG_DEF(JSMSG_DUPLICATE_LABEL, 126, 0, JSEXN_SYNTAXERR, "duplicate label")
|
||||
MSG_DEF(JSMSG_VAR_HIDES_ARG, 127, 1, JSEXN_TYPEERR, "variable {0} hides argument")
|
||||
@ -248,20 +248,20 @@ MSG_DEF(JSMSG_NEWREGEXP_FLAGGED, 165, 0, JSEXN_TYPEERR, "can't supply flags
|
||||
MSG_DEF(JSMSG_RESERVED_SLOT_RANGE, 166, 0, JSEXN_RANGEERR, "reserved slot index out of range")
|
||||
MSG_DEF(JSMSG_CANT_DECODE_PRINCIPALS, 167, 0, JSEXN_INTERNALERR, "can't decode JSPrincipals")
|
||||
MSG_DEF(JSMSG_CANT_SEAL_OBJECT, 168, 1, JSEXN_ERR, "can't seal {0} objects")
|
||||
MSG_DEF(JSMSG_CANT_UNSEAL_OBJECT, 169, 1, JSEXN_ERR, "can't unseal {0} objects")
|
||||
MSG_DEF(JSMSG_UNUSED169, 169, 0, JSEXN_NONE, "")
|
||||
MSG_DEF(JSMSG_BAD_XML_MARKUP, 170, 0, JSEXN_SYNTAXERR, "invalid XML markup")
|
||||
MSG_DEF(JSMSG_BAD_XML_CHARACTER, 171, 0, JSEXN_SYNTAXERR, "illegal XML character")
|
||||
MSG_DEF(JSMSG_BAD_DEFAULT_XML_NAMESPACE,172,0,JSEXN_SYNTAXERR, "invalid default XML namespace")
|
||||
MSG_DEF(JSMSG_BAD_XML_NAME_SYNTAX, 173, 0, JSEXN_SYNTAXERR, "invalid XML name")
|
||||
MSG_DEF(JSMSG_BRACKET_AFTER_ATTR_EXPR,174, 0, JSEXN_SYNTAXERR, "missing ] after attribute expression")
|
||||
MSG_DEF(JSMSG_NAME_AFTER_DBLDOT, 175, 0, JSEXN_SYNTAXERR, "missing name after .. operator")
|
||||
MSG_DEF(JSMSG_UNUSED175, 175, 0, JSEXN_NONE, "")
|
||||
MSG_DEF(JSMSG_CURLY_IN_XML_EXPR, 176, 0, JSEXN_SYNTAXERR, "missing } in XML expression")
|
||||
MSG_DEF(JSMSG_BAD_XML_NAMESPACE, 177, 1, JSEXN_TYPEERR, "invalid XML namespace {0}")
|
||||
MSG_DEF(JSMSG_BAD_XML_ATTR_NAME, 178, 1, JSEXN_TYPEERR, "invalid XML attribute name {0}")
|
||||
MSG_DEF(JSMSG_BAD_XML_NAME, 179, 1, JSEXN_TYPEERR, "invalid XML name {0}")
|
||||
MSG_DEF(JSMSG_BAD_XML_CONVERSION, 180, 1, JSEXN_TYPEERR, "can't convert {0} to XML")
|
||||
MSG_DEF(JSMSG_BAD_XMLLIST_CONVERSION, 181, 1, JSEXN_TYPEERR, "can't convert {0} to XMLList")
|
||||
MSG_DEF(JSMSG_IS_NOT_XML_OBJECT, 182, 1, JSEXN_TYPEERR, "{0} is not an XML object")
|
||||
MSG_DEF(JSMSG_UNUSED182, 182, 0, JSEXN_NONE, "")
|
||||
MSG_DEF(JSMSG_NO_ASSIGN_IN_XML_ATTR, 183, 0, JSEXN_SYNTAXERR, "missing = in XML attribute")
|
||||
MSG_DEF(JSMSG_BAD_XML_ATTR_VALUE, 184, 0, JSEXN_SYNTAXERR, "invalid XML attribute value")
|
||||
MSG_DEF(JSMSG_XML_TAG_NAME_MISMATCH, 185, 0, JSEXN_SYNTAXERR, "XML tag name mismatch")
|
||||
@ -286,4 +286,10 @@ MSG_DEF(JSMSG_BAD_SURROGATE_CHAR, 203, 1, JSEXN_TYPEERR, "bad surrogate char
|
||||
MSG_DEF(JSMSG_UTF8_CHAR_TOO_LARGE, 204, 1, JSEXN_TYPEERR, "UTF-8 character {0} too large")
|
||||
MSG_DEF(JSMSG_MALFORMED_UTF8_CHAR, 205, 1, JSEXN_TYPEERR, "malformed UTF-8 character sequence at offset {0}")
|
||||
MSG_DEF(JSMSG_USER_DEFINED_ERROR, 206, 0, JSEXN_ERR, "JS_ReportError was called")
|
||||
MSG_DEF(JSMSG_WRONG_CONSTRUCTOR, 207, 1, JSEXN_TYPEERR, "wrong construtor called for {0}")
|
||||
MSG_DEF(JSMSG_WRONG_CONSTRUCTOR, 207, 1, JSEXN_TYPEERR, "wrong constructor called for {0}")
|
||||
MSG_DEF(JSMSG_BAD_GENERATOR_RETURN, 208, 1, JSEXN_TYPEERR, "generator function {0} returns a value")
|
||||
MSG_DEF(JSMSG_BAD_ANON_GENERATOR_RETURN, 209, 1, JSEXN_TYPEERR, "anonymous generator function returns a value")
|
||||
MSG_DEF(JSMSG_NAME_AFTER_FOR_PAREN, 210, 0, JSEXN_SYNTAXERR, "missing name after for (")
|
||||
MSG_DEF(JSMSG_IN_AFTER_FOR_NAME, 211, 0, JSEXN_SYNTAXERR, "missing in after for")
|
||||
MSG_DEF(JSMSG_BAD_ITERATOR_RETURN, 212, 2, JSEXN_TYPEERR, "{0}.{1} returned a primitive value")
|
||||
MSG_DEF(JSMSG_KEYWORD_NOT_NS, 213, 0, JSEXN_SYNTAXERR, "keyword is used as namespace")
|
||||
|
126
js/src/jsapi.c
126
js/src/jsapi.c
@ -86,6 +86,10 @@
|
||||
#include "jsxml.h"
|
||||
#endif
|
||||
|
||||
#if JS_HAS_GENERATORS
|
||||
#include "jsiter.h"
|
||||
#endif
|
||||
|
||||
#ifdef HAVE_VA_LIST_AS_ARRAY
|
||||
#define JS_ADDRESSOF_VA_LIST(ap) ((va_list *)(ap))
|
||||
#else
|
||||
@ -1084,67 +1088,9 @@ JS_GetGlobalObject(JSContext *cx)
|
||||
return cx->globalObject;
|
||||
}
|
||||
|
||||
static JSBool
|
||||
AlreadyHasOwnProperty(JSContext *cx, JSObject *obj, JSAtom *atom, jsval *vp)
|
||||
{
|
||||
JSScopeProperty *sprop;
|
||||
JSScope *scope;
|
||||
|
||||
JS_ASSERT(OBJ_IS_NATIVE(obj));
|
||||
JS_LOCK_OBJ(cx, obj);
|
||||
scope = OBJ_SCOPE(obj);
|
||||
sprop = SCOPE_GET_PROPERTY(scope, ATOM_TO_JSID(atom));
|
||||
if (vp) {
|
||||
*vp = (sprop && SPROP_HAS_VALID_SLOT(sprop, scope))
|
||||
? LOCKED_OBJ_GET_SLOT(obj, sprop->slot)
|
||||
: JSVAL_VOID;
|
||||
}
|
||||
JS_UNLOCK_SCOPE(cx, scope);
|
||||
return sprop != NULL;
|
||||
}
|
||||
|
||||
JS_PUBLIC_API(void)
|
||||
JS_SetGlobalObject(JSContext *cx, JSObject *obj)
|
||||
{
|
||||
JSContext *ocx;
|
||||
JSAtom **classAtoms;
|
||||
JSProtoKey key;
|
||||
jsval v;
|
||||
|
||||
if (!obj) {
|
||||
/* Clearing cx->globalObject: clear cached class object refs too. */
|
||||
memset(cx->classObjects, 0, sizeof cx->classObjects);
|
||||
} else {
|
||||
/*
|
||||
* In case someone initialized obj's standard classes on another
|
||||
* context, then handed obj off to cx, try to find that other context
|
||||
* and copy its class objects into cx's.
|
||||
*/
|
||||
ocx = js_FindContextForGlobal(cx, obj);
|
||||
if (ocx) {
|
||||
memcpy(cx->classObjects, ocx->classObjects,
|
||||
sizeof cx->classObjects);
|
||||
} else {
|
||||
/*
|
||||
* Darn, can't find another context in which obj's standard classes,
|
||||
* or at least some of them, were initialized. Try to make obj and
|
||||
* cx agree on the state of the standard classes.
|
||||
*/
|
||||
memset(cx->classObjects, 0, sizeof cx->classObjects);
|
||||
classAtoms = cx->runtime->atomState.classAtoms;
|
||||
for (key = JSProto_Null; key < JSProto_LIMIT; key++) {
|
||||
if (AlreadyHasOwnProperty(cx, obj, classAtoms[key], &v) &&
|
||||
!JSVAL_IS_PRIMITIVE(v)) {
|
||||
cx->classObjects[key] = JSVAL_TO_OBJECT(v);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Do this after js_FindContextForGlobal, so it can assert that obj is not
|
||||
* yet cx->globalObject.
|
||||
*/
|
||||
cx->globalObject = obj;
|
||||
|
||||
#if JS_HAS_XML_SUPPORT
|
||||
@ -1269,6 +1215,9 @@ JS_InitStandardClasses(JSContext *cx, JSObject *obj)
|
||||
#endif
|
||||
#if JS_HAS_FILE_OBJECT
|
||||
js_InitFileClass(cx, obj) &&
|
||||
#endif
|
||||
#if JS_HAS_GENERATORS
|
||||
js_InitIteratorClasses(cx, obj) &&
|
||||
#endif
|
||||
js_InitDateClass(cx, obj);
|
||||
}
|
||||
@ -1306,6 +1255,9 @@ static struct {
|
||||
#endif
|
||||
#if JS_HAS_FILE_OBJECT
|
||||
{js_InitFileClass, CLASS_ATOM_OFFSET(File)},
|
||||
#endif
|
||||
#if JS_HAS_GENERATORS
|
||||
{js_InitIteratorClasses, CLASS_ATOM_OFFSET(StopIteration)},
|
||||
#endif
|
||||
{NULL, 0}
|
||||
};
|
||||
@ -1384,6 +1336,10 @@ static JSStdName standard_class_names[] = {
|
||||
{js_InitXMLClass, LAZILY_PINNED_ATOM(isXMLName)},
|
||||
#endif
|
||||
|
||||
#if JS_HAS_GENERATORS
|
||||
{js_InitIteratorClasses, EAGERLY_PINNED_CLASS_ATOM(Iterator)},
|
||||
#endif
|
||||
|
||||
{NULL, 0, NULL}
|
||||
};
|
||||
|
||||
@ -1492,6 +1448,20 @@ JS_ResolveStandardClass(JSContext *cx, JSObject *obj, jsval id,
|
||||
return JS_TRUE;
|
||||
}
|
||||
|
||||
static JSBool
|
||||
AlreadyHasOwnProperty(JSContext *cx, JSObject *obj, JSAtom *atom)
|
||||
{
|
||||
JSScopeProperty *sprop;
|
||||
JSScope *scope;
|
||||
|
||||
JS_ASSERT(OBJ_IS_NATIVE(obj));
|
||||
JS_LOCK_OBJ(cx, obj);
|
||||
scope = OBJ_SCOPE(obj);
|
||||
sprop = SCOPE_GET_PROPERTY(scope, ATOM_TO_JSID(atom));
|
||||
JS_UNLOCK_SCOPE(cx, scope);
|
||||
return sprop != NULL;
|
||||
}
|
||||
|
||||
JS_PUBLIC_API(JSBool)
|
||||
JS_EnumerateStandardClasses(JSContext *cx, JSObject *obj)
|
||||
{
|
||||
@ -1504,7 +1474,7 @@ JS_EnumerateStandardClasses(JSContext *cx, JSObject *obj)
|
||||
|
||||
/* Check whether we need to bind 'undefined' and define it if so. */
|
||||
atom = rt->atomState.typeAtoms[JSTYPE_VOID];
|
||||
if (!AlreadyHasOwnProperty(cx, obj, atom, NULL) &&
|
||||
if (!AlreadyHasOwnProperty(cx, obj, atom) &&
|
||||
!OBJ_DEFINE_PROPERTY(cx, obj, ATOM_TO_JSID(atom), JSVAL_VOID,
|
||||
NULL, NULL, JSPROP_PERMANENT, NULL)) {
|
||||
return JS_FALSE;
|
||||
@ -1513,7 +1483,7 @@ JS_EnumerateStandardClasses(JSContext *cx, JSObject *obj)
|
||||
/* Initialize any classes that have not been resolved yet. */
|
||||
for (i = 0; standard_class_atoms[i].init; i++) {
|
||||
atom = OFFSET_TO_ATOM(rt, standard_class_atoms[i].atomOffset);
|
||||
if (!AlreadyHasOwnProperty(cx, obj, atom, NULL) &&
|
||||
if (!AlreadyHasOwnProperty(cx, obj, atom) &&
|
||||
!standard_class_atoms[i].init(cx, obj)) {
|
||||
return JS_FALSE;
|
||||
}
|
||||
@ -1544,7 +1514,7 @@ static JSIdArray *
|
||||
EnumerateIfResolved(JSContext *cx, JSObject *obj, JSAtom *atom, JSIdArray *ida,
|
||||
jsint *ip, JSBool *foundp)
|
||||
{
|
||||
*foundp = AlreadyHasOwnProperty(cx, obj, atom, NULL);
|
||||
*foundp = AlreadyHasOwnProperty(cx, obj, atom);
|
||||
if (*foundp)
|
||||
ida = AddAtomToArray(cx, atom, ida, ip);
|
||||
return ida;
|
||||
@ -1625,7 +1595,14 @@ JS_GetClassObject(JSContext *cx, JSObject *obj, JSProtoKey key,
|
||||
JS_PUBLIC_API(JSObject *)
|
||||
JS_GetScopeChain(JSContext *cx)
|
||||
{
|
||||
return cx->fp ? cx->fp->scopeChain : NULL;
|
||||
JSStackFrame *fp;
|
||||
|
||||
fp = cx->fp;
|
||||
if (!fp) {
|
||||
JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_INACTIVE);
|
||||
return NULL;
|
||||
}
|
||||
return js_GetScopeChain(cx, fp);
|
||||
}
|
||||
|
||||
JS_PUBLIC_API(void *)
|
||||
@ -2171,22 +2148,31 @@ JS_InitClass(JSContext *cx, JSObject *obj, JSObject *parent_proto,
|
||||
return NULL;
|
||||
|
||||
/* After this point, control must exit via label bad or out. */
|
||||
JS_PUSH_SINGLE_TEMP_ROOT(cx, OBJECT_TO_JSVAL(proto), &tvr);
|
||||
JS_PUSH_SINGLE_TEMP_ROOT(cx, proto, &tvr);
|
||||
|
||||
if (!constructor) {
|
||||
/*
|
||||
* Lacking a constructor, name the prototype (e.g., Math) unless this
|
||||
* class is anonymous, i.e. for internal use only.
|
||||
* class (a) is anonymous, i.e. for internal use only; (b) the class
|
||||
* of obj (the global object) is has a reserved slot indexed by key;
|
||||
* and (c) key is not the null key.
|
||||
*/
|
||||
if (clasp->flags & JSCLASS_IS_ANONYMOUS) {
|
||||
if ((clasp->flags & JSCLASS_IS_ANONYMOUS) &&
|
||||
(OBJ_GET_CLASS(cx, obj)->flags & JSCLASS_IS_GLOBAL) &&
|
||||
key != JSProto_Null) {
|
||||
named = JS_FALSE;
|
||||
} else {
|
||||
named = OBJ_DEFINE_PROPERTY(cx, obj, ATOM_TO_JSID(atom),
|
||||
OBJECT_TO_JSVAL(proto),
|
||||
NULL, NULL, 0, NULL);
|
||||
NULL, NULL,
|
||||
(clasp->flags & JSCLASS_IS_ANONYMOUS)
|
||||
? JSPROP_READONLY | JSPROP_PERMANENT
|
||||
: 0,
|
||||
NULL);
|
||||
if (!named)
|
||||
goto bad;
|
||||
}
|
||||
|
||||
ctor = proto;
|
||||
} else {
|
||||
/* Define the constructor function in obj's scope. */
|
||||
@ -2239,8 +2225,8 @@ JS_InitClass(JSContext *cx, JSObject *obj, JSObject *parent_proto,
|
||||
}
|
||||
|
||||
/* If this is a standard class, cache its prototype. */
|
||||
if (key != JSProto_Null)
|
||||
js_SetClassObject(cx, obj, key, ctor);
|
||||
if (key != JSProto_Null && !js_SetClassObject(cx, obj, key, ctor))
|
||||
goto bad;
|
||||
|
||||
out:
|
||||
JS_POP_TEMP_ROOT(cx, &tvr);
|
||||
@ -3210,8 +3196,6 @@ JS_ClearScope(JSContext *cx, JSObject *obj)
|
||||
|
||||
if (obj->map->ops->clear)
|
||||
obj->map->ops->clear(cx, obj);
|
||||
if (cx->globalObject == obj)
|
||||
memset(cx->classObjects, 0, sizeof cx->classObjects);
|
||||
}
|
||||
|
||||
JS_PUBLIC_API(JSIdArray *)
|
||||
@ -4371,7 +4355,7 @@ JS_SetCallReturnValue2(JSContext *cx, jsval v)
|
||||
{
|
||||
#if JS_HAS_LVALUE_RETURN
|
||||
cx->rval2 = v;
|
||||
cx->rval2set = JS_TRUE;
|
||||
cx->rval2set = JS_RVAL2_VALUE;
|
||||
#endif
|
||||
}
|
||||
|
||||
|
@ -963,6 +963,23 @@ struct JSExtendedClass {
|
||||
/* True if JSClass is really a JSExtendedClass. */
|
||||
#define JSCLASS_IS_EXTENDED (1<<(JSCLASS_HIGH_FLAGS_SHIFT+0))
|
||||
#define JSCLASS_IS_ANONYMOUS (1<<(JSCLASS_HIGH_FLAGS_SHIFT+1))
|
||||
#define JSCLASS_IS_GLOBAL (1<<(JSCLASS_HIGH_FLAGS_SHIFT+2))
|
||||
|
||||
/*
|
||||
* ECMA-262 requires that most constructors used internally create objects
|
||||
* with "the original Foo.prototype value" as their [[Prototype]] (__proto__)
|
||||
* member initial value. The "original ... value" verbiage is there because
|
||||
* in ECMA-262, global properties naming class objects are read/write and
|
||||
* deleteable, for the most part.
|
||||
*
|
||||
* Implementing this efficiently requires that global objects have classes
|
||||
* with the following flags. Failure to use JSCLASS_GLOBAL_FLAGS won't break
|
||||
* anything except the ECMA-262 "original prototype value" behavior, which was
|
||||
* broken for years in SpiderMonkey. In other words, without these flags you
|
||||
* get backward compatibility.
|
||||
*/
|
||||
#define JSCLASS_GLOBAL_FLAGS \
|
||||
(JSCLASS_IS_GLOBAL | JSCLASS_HAS_RESERVED_SLOTS(JSProto_LIMIT))
|
||||
|
||||
/* Fast access to the original value of each standard class's prototype. */
|
||||
#define JSCLASS_CACHED_PROTO_SHIFT (JSCLASS_HIGH_FLAGS_SHIFT + 8)
|
||||
|
@ -86,12 +86,12 @@ const char *js_boolean_strs[] = {
|
||||
js_true_str
|
||||
};
|
||||
|
||||
#define JS_PROTO(name,init) const char js_##name##_str[] = #name;
|
||||
#define JS_PROTO(name,code,init) const char js_##name##_str[] = #name;
|
||||
#include "jsproto.tbl"
|
||||
#undef JS_PROTO
|
||||
|
||||
const char *js_proto_strs[JSProto_LIMIT] = {
|
||||
#define JS_PROTO(name,init) js_##name##_str,
|
||||
#define JS_PROTO(name,code,init) js_##name##_str,
|
||||
#include "jsproto.tbl"
|
||||
#undef JS_PROTO
|
||||
};
|
||||
@ -110,8 +110,10 @@ const char js_get_str[] = "get";
|
||||
const char js_getter_str[] = "getter";
|
||||
const char js_index_str[] = "index";
|
||||
const char js_input_str[] = "input";
|
||||
const char js_iterator_str[] = "__iterator__";
|
||||
const char js_length_str[] = "length";
|
||||
const char js_name_str[] = "name";
|
||||
const char js_next_str[] = "next";
|
||||
const char js_noSuchMethod_str[] = "__noSuchMethod__";
|
||||
const char js_object_str[] = "object";
|
||||
const char js_parent_str[] = "__parent__";
|
||||
@ -310,8 +312,10 @@ js_InitPinnedAtoms(JSContext *cx, JSAtomState *state)
|
||||
FROB(getterAtom, js_getter_str);
|
||||
FROB(indexAtom, js_index_str);
|
||||
FROB(inputAtom, js_input_str);
|
||||
FROB(iteratorAtom, js_iterator_str);
|
||||
FROB(lengthAtom, js_length_str);
|
||||
FROB(nameAtom, js_name_str);
|
||||
FROB(nextAtom, js_next_str);
|
||||
FROB(noSuchMethodAtom, js_noSuchMethod_str);
|
||||
FROB(parentAtom, js_parent_str);
|
||||
FROB(protoAtom, js_proto_str);
|
||||
|
@ -179,9 +179,11 @@ struct JSAtomState {
|
||||
JSAtom *getterAtom;
|
||||
JSAtom *indexAtom;
|
||||
JSAtom *inputAtom;
|
||||
JSAtom *iteratorAtom;
|
||||
JSAtom *lengthAtom;
|
||||
JSAtom *nameAtom;
|
||||
JSAtom *namespaceAtom;
|
||||
JSAtom *nextAtom;
|
||||
JSAtom *noSuchMethodAtom;
|
||||
JSAtom *parentAtom;
|
||||
JSAtom *protoAtom;
|
||||
@ -250,26 +252,10 @@ extern const char *js_type_strs[];
|
||||
extern const char *js_boolean_strs[];
|
||||
extern const char *js_proto_strs[];
|
||||
|
||||
#define JS_PROTO(name,init) extern const char js_##name##_str[];
|
||||
#define JS_PROTO(name,code,init) extern const char js_##name##_str[];
|
||||
#include "jsproto.tbl"
|
||||
#undef JS_PROTO
|
||||
|
||||
extern const char js_Arguments_str[];
|
||||
extern const char js_Array_str[];
|
||||
extern const char js_Boolean_str[];
|
||||
extern const char js_Call_str[];
|
||||
extern const char js_Date_str[];
|
||||
extern const char js_Function_str[];
|
||||
extern const char js_Math_str[];
|
||||
extern const char js_Namespace_str[];
|
||||
extern const char js_Number_str[];
|
||||
extern const char js_Object_str[];
|
||||
extern const char js_QName_str[];
|
||||
extern const char js_RegExp_str[];
|
||||
extern const char js_Script_str[];
|
||||
extern const char js_String_str[];
|
||||
extern const char js_XML_str[];
|
||||
extern const char js_File_str[];
|
||||
extern const char js_anonymous_str[];
|
||||
extern const char js_arguments_str[];
|
||||
extern const char js_arity_str[];
|
||||
@ -285,9 +271,11 @@ extern const char js_get_str[];
|
||||
extern const char js_getter_str[];
|
||||
extern const char js_index_str[];
|
||||
extern const char js_input_str[];
|
||||
extern const char js_iterator_str[];
|
||||
extern const char js_length_str[];
|
||||
extern const char js_name_str[];
|
||||
extern const char js_namespace_str[];
|
||||
extern const char js_next_str[];
|
||||
extern const char js_noSuchMethod_str[];
|
||||
extern const char js_object_str[];
|
||||
extern const char js_parent_str[];
|
||||
|
123
js/src/jscntxt.c
123
js/src/jscntxt.c
@ -767,129 +767,6 @@ js_MarkLocalRoots(JSContext *cx, JSLocalRootStack *lrs)
|
||||
JS_ASSERT(!lrc);
|
||||
}
|
||||
|
||||
JS_STATIC_DLL_CALLBACK(JSObject *)
|
||||
js_InitNullClass(JSContext *cx, JSObject *obj)
|
||||
{
|
||||
JS_ASSERT(0);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
#define JS_PROTO(name,init) extern JSObject *init(JSContext *, JSObject *);
|
||||
#include "jsproto.tbl"
|
||||
#undef JS_PROTO
|
||||
|
||||
static JSObjectOp lazy_prototype_init[JSProto_LIMIT] = {
|
||||
#define JS_PROTO(name,init) init,
|
||||
#include "jsproto.tbl"
|
||||
#undef JS_PROTO
|
||||
};
|
||||
|
||||
/*
|
||||
* We optimize prototype caching by storing strong references to standard
|
||||
* object prototypes in each cx whose globalObject is populated (eagerly or
|
||||
* lazily) with the standard class constructors and global functions.
|
||||
*
|
||||
* But since in anything like a browser embedding, an object statically
|
||||
* scoped by one global object may be accessed by script running on another
|
||||
* global object's context, we must not dynamically scope cached prototypes.
|
||||
* This may require searching all contexts for a given thread, in the event
|
||||
* that a given execution context does not have a global object equal to the
|
||||
* global of a given target object.
|
||||
*/
|
||||
JSContext *
|
||||
js_FindContextForGlobal(JSContext *cx, JSObject *obj)
|
||||
{
|
||||
JSCList *head, *link;
|
||||
JSContext *ocx;
|
||||
|
||||
JS_ASSERT(cx->globalObject != obj);
|
||||
#ifdef JS_THREADSAFE
|
||||
head = &cx->thread->contextList;
|
||||
#else
|
||||
head = &cx->runtime->contextList;
|
||||
#endif
|
||||
for (link = head->next; link != head; link = link->next) {
|
||||
#ifdef JS_THREADSAFE
|
||||
ocx = CX_FROM_THREAD_LINKS(link);
|
||||
JS_ASSERT(ocx->thread == cx->thread);
|
||||
#else
|
||||
ocx = (JSContext *) link;
|
||||
#endif
|
||||
if (ocx != cx && ocx->globalObject == obj)
|
||||
return ocx;
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
JSBool
|
||||
js_GetClassObject(JSContext *cx, JSObject *obj, JSProtoKey key,
|
||||
JSObject **objp)
|
||||
{
|
||||
JSBool ok;
|
||||
JSResolvingKey rkey;
|
||||
JSResolvingEntry *rentry;
|
||||
uint32 generation;
|
||||
JSObject *tmp, *cobj;
|
||||
JSContext *ocx;
|
||||
JSObjectOp init;
|
||||
|
||||
rkey.obj = obj;
|
||||
rkey.id = ATOM_TO_JSID(cx->runtime->atomState.classAtoms[key]);
|
||||
ok = js_StartResolving(cx, &rkey, JSRESFLAG_LOOKUP, &rentry);
|
||||
if (!ok)
|
||||
return JS_FALSE;
|
||||
if (!rentry) {
|
||||
/* Already caching key in obj -- suppress recursion. */
|
||||
*objp = NULL;
|
||||
return JS_TRUE;
|
||||
}
|
||||
generation = cx->resolvingTable->generation;
|
||||
|
||||
while ((tmp = OBJ_GET_PARENT(cx, obj)) != NULL)
|
||||
obj = tmp;
|
||||
if (obj == cx->globalObject) {
|
||||
ocx = cx;
|
||||
} else {
|
||||
ocx = js_FindContextForGlobal(cx, obj);
|
||||
if (!ocx) {
|
||||
cobj = NULL;
|
||||
goto out;
|
||||
}
|
||||
}
|
||||
|
||||
cobj = ocx->classObjects[key];
|
||||
if (!cobj) {
|
||||
init = lazy_prototype_init[key];
|
||||
if (init) {
|
||||
if (!init(cx, obj))
|
||||
ok = JS_FALSE;
|
||||
cobj = ocx->classObjects[key];
|
||||
}
|
||||
}
|
||||
|
||||
out:
|
||||
*objp = cobj;
|
||||
js_StopResolving(cx, &rkey, JSRESFLAG_LOOKUP, rentry, generation);
|
||||
return ok;
|
||||
}
|
||||
|
||||
void
|
||||
js_SetClassObject(JSContext *cx, JSObject *obj, JSProtoKey key,
|
||||
JSObject *value)
|
||||
{
|
||||
JSContext *ocx;
|
||||
|
||||
JS_ASSERT(!OBJ_GET_PARENT(cx, obj));
|
||||
if (obj == cx->globalObject) {
|
||||
ocx = cx;
|
||||
} else {
|
||||
ocx = js_FindContextForGlobal(cx, obj);
|
||||
if (!ocx)
|
||||
return;
|
||||
}
|
||||
ocx->classObjects[key] = value;
|
||||
}
|
||||
|
||||
static void
|
||||
ReportError(JSContext *cx, const char *message, JSErrorReport *reportp)
|
||||
{
|
||||
|
@ -351,6 +351,20 @@ struct JSRuntime {
|
||||
#define JS_KEEP_ATOMS(rt) JS_ATOMIC_INCREMENT(&(rt)->gcKeepAtoms);
|
||||
#define JS_UNKEEP_ATOMS(rt) JS_ATOMIC_DECREMENT(&(rt)->gcKeepAtoms);
|
||||
|
||||
#if JS_HAS_LVALUE_RETURN
|
||||
/*
|
||||
* Values for the cx->rval2set flag byte. This flag tells whether cx->rval2
|
||||
* is unset (CLEAR), set to a jsval (VALUE) naming a property in the object
|
||||
* referenced by cx->fp->rval, or set to a jsid (ITERKEY) result of a native
|
||||
* iterator's it.next() call (where the return value of it.next() is the next
|
||||
* value in the iteration).
|
||||
*
|
||||
* The ITERKEY case is just an optimization for native iterators, as general
|
||||
* iterators can return an array of length 2 to return a [key, value] pair.
|
||||
*/
|
||||
enum { JS_RVAL2_CLEAR, JS_RVAL2_VALUE, JS_RVAL2_ITERKEY };
|
||||
#endif
|
||||
|
||||
#ifdef JS_ARGUMENT_FORMATTER_DEFINED
|
||||
/*
|
||||
* Linked list mapping format strings for JS_{Convert,Push}Arguments{,VA} to
|
||||
@ -415,11 +429,22 @@ typedef struct JSLocalRootStack {
|
||||
typedef struct JSTempValueRooter JSTempValueRooter;
|
||||
|
||||
/*
|
||||
* Context-linked stack of temporary GC roots.
|
||||
*
|
||||
* If count is -1, then u.value contains the single value to root. Otherwise
|
||||
* u.array points to a stack-allocated vector of jsvals. Note that the vector
|
||||
* may have length 0 or 1 for full generality, so we need -1 to discriminate
|
||||
* the union.
|
||||
*
|
||||
* To root a single GC-thing pointer, which need not be tagged and stored as a
|
||||
* jsval, use JS_PUSH_SINGLE_TEMP_ROOT. The (jsval)(val) cast works because a
|
||||
* GC-thing is aligned on a 0 mod 8 boundary, and object has the 0 jsval tag.
|
||||
* So any GC-thing may be tagged as if it were an object and untagged, if it's
|
||||
* then used only as an opaque pointer until discriminated by other means than
|
||||
* tag bits (this is how the GC mark function uses its |thing| parameter -- it
|
||||
* consults GC-thing flags stored separately from the thing to decide the type
|
||||
* of thing).
|
||||
*
|
||||
* If you need to protect a result value that flows out of a C function across
|
||||
* several layers of other functions, use the js_LeaveLocalRootScopeWithResult
|
||||
* internal API (see further below) instead.
|
||||
@ -444,7 +469,7 @@ struct JSTempValueRooter {
|
||||
JS_BEGIN_MACRO \
|
||||
JS_PUSH_TEMP_ROOT_COMMON(cx, tvr); \
|
||||
(tvr)->count = -1; \
|
||||
(tvr)->u.value = (val); \
|
||||
(tvr)->u.value = (jsval)(val); \
|
||||
JS_END_MACRO
|
||||
|
||||
#define JS_PUSH_TEMP_ROOT(cx,cnt,arr,tvr) \
|
||||
@ -549,7 +574,7 @@ struct JSContext {
|
||||
* jsval by calling JS_SetCallReturnValue2(cx, idval).
|
||||
*/
|
||||
jsval rval2;
|
||||
JSPackedBool rval2set;
|
||||
uint8 rval2set;
|
||||
#endif
|
||||
|
||||
#if JS_HAS_XML_SUPPORT
|
||||
@ -601,13 +626,13 @@ struct JSContext {
|
||||
/* Stack of thread-stack-allocated temporary GC roots. */
|
||||
JSTempValueRooter *tempValueRooters;
|
||||
|
||||
/* Roots for the standard class objects (Object, Function, etc.) */
|
||||
JSObject *classObjects[JSProto_LIMIT];
|
||||
/* Iterator cache to speed up native default for-in loop case. */
|
||||
JSObject *cachedIterObj;
|
||||
|
||||
#ifdef GC_MARK_DEBUG
|
||||
/* Top of the GC mark stack. */
|
||||
void *gcCurrentMarkNode;
|
||||
#endif
|
||||
#endif
|
||||
};
|
||||
|
||||
#define JS_THREAD_ID(cx) ((cx)->thread ? (cx)->thread->id : 0)
|
||||
@ -736,20 +761,6 @@ js_PushLocalRoot(JSContext *cx, JSLocalRootStack *lrs, jsval v);
|
||||
extern void
|
||||
js_MarkLocalRoots(JSContext *cx, JSLocalRootStack *lrs);
|
||||
|
||||
/*
|
||||
* Fast access to immutable standard objects (constructors and prototypes).
|
||||
*/
|
||||
extern JSContext *
|
||||
js_FindContextForGlobal(JSContext *cx, JSObject *obj);
|
||||
|
||||
extern JSBool
|
||||
js_GetClassObject(JSContext *cx, JSObject *obj, JSProtoKey key,
|
||||
JSObject **objp);
|
||||
|
||||
extern void
|
||||
js_SetClassObject(JSContext *cx, JSObject *obj, JSProtoKey key,
|
||||
JSObject *value);
|
||||
|
||||
/*
|
||||
* Report an exception, which is currently realized as a printf-style format
|
||||
* string and its arguments.
|
||||
|
@ -41,7 +41,7 @@
|
||||
* JS configuration macros.
|
||||
*/
|
||||
#ifndef JS_VERSION
|
||||
#define JS_VERSION 160
|
||||
#define JS_VERSION 170
|
||||
#endif
|
||||
|
||||
/*
|
||||
@ -104,6 +104,8 @@
|
||||
#define JS_HAS_NO_SUCH_METHOD 0 /* has o.__noSuchMethod__ handler */
|
||||
#define JS_HAS_XML_SUPPORT 0 /* has ECMAScript for XML support */
|
||||
#define JS_HAS_ARRAY_EXTRAS 0 /* has indexOf and Lispy extras */
|
||||
#define JS_HAS_GENERATORS 0 /* has yield in generator function */
|
||||
#define JS_HAS_BLOCK_SCOPE 0 /* has block scope via let/arraycomp */
|
||||
|
||||
#elif JS_VERSION < 150
|
||||
|
||||
@ -133,6 +135,8 @@
|
||||
#define JS_HAS_NO_SUCH_METHOD 1 /* has o.__noSuchMethod__ handler */
|
||||
#define JS_HAS_XML_SUPPORT 0 /* has ECMAScript for XML support */
|
||||
#define JS_HAS_ARRAY_EXTRAS 0 /* has indexOf and Lispy extras */
|
||||
#define JS_HAS_GENERATORS 0 /* has yield in generator function */
|
||||
#define JS_HAS_BLOCK_SCOPE 0 /* has block scope via let/arraycomp */
|
||||
|
||||
#elif JS_VERSION == 160
|
||||
|
||||
@ -158,6 +162,35 @@
|
||||
#define JS_HAS_NO_SUCH_METHOD 1 /* has o.__noSuchMethod__ handler */
|
||||
#define JS_HAS_XML_SUPPORT 1 /* has ECMAScript for XML support */
|
||||
#define JS_HAS_ARRAY_EXTRAS 1 /* has indexOf and Lispy extras */
|
||||
#define JS_HAS_GENERATORS 0 /* has yield in generator function */
|
||||
#define JS_HAS_BLOCK_SCOPE 0 /* has block scope via let/arraycomp */
|
||||
|
||||
#elif JS_VERSION == 170
|
||||
|
||||
#define JS_HAS_STR_HTML_HELPERS 1 /* has str.anchor, str.bold, etc. */
|
||||
#define JS_HAS_PERL_SUBSTR 1 /* has str.substr */
|
||||
#define JS_HAS_OBJ_PROTO_PROP 1 /* has o.__proto__ etc. */
|
||||
#define JS_HAS_OBJ_WATCHPOINT 1 /* has o.watch and o.unwatch */
|
||||
#define JS_HAS_EXPORT_IMPORT 1 /* has export fun; import obj.fun */
|
||||
#define JS_HAS_EVAL_THIS_SCOPE 1 /* Math.eval is same as with (Math) */
|
||||
#define JS_HAS_SHARP_VARS 1 /* has #n=, #n# for object literals */
|
||||
#define JS_HAS_SCRIPT_OBJECT 1 /* has (new Script("x++")).exec() */
|
||||
#define JS_HAS_XDR 1 /* has XDR API and internal support */
|
||||
#define JS_HAS_XDR_FREEZE_THAW 0 /* has XDR freeze/thaw script methods */
|
||||
#define JS_HAS_TOSOURCE 1 /* has Object/Array toSource method */
|
||||
#define JS_HAS_DEBUGGER_KEYWORD 1 /* has hook for debugger keyword */
|
||||
#define JS_HAS_CATCH_GUARD 1 /* has exception handling catch guard */
|
||||
#define JS_HAS_SPARSE_ARRAYS 0 /* array methods preserve empty elems */
|
||||
#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 */
|
||||
#define JS_HAS_LVALUE_RETURN 1 /* has o.item(i) = j; for native item */
|
||||
#define JS_HAS_NO_SUCH_METHOD 1 /* has o.__noSuchMethod__ handler */
|
||||
#define JS_HAS_XML_SUPPORT 1 /* has ECMAScript for XML support */
|
||||
#define JS_HAS_ARRAY_EXTRAS 1 /* has indexOf and Lispy extras */
|
||||
#define JS_HAS_GENERATORS 1 /* has yield in generator function */
|
||||
#define JS_HAS_BLOCK_SCOPE 1 /* has block scope via let/arraycomp */
|
||||
|
||||
#else
|
||||
|
||||
|
@ -782,7 +782,7 @@ JS_GetFrameScopeChain(JSContext *cx, JSStackFrame *fp)
|
||||
{
|
||||
/* Force creation of argument and call objects if not yet created */
|
||||
(void) JS_GetFrameCallObject(cx, fp);
|
||||
return fp->scopeChain;
|
||||
return js_GetScopeChain(cx, fp);
|
||||
}
|
||||
|
||||
JS_PUBLIC_API(JSObject *)
|
||||
@ -901,10 +901,15 @@ JS_EvaluateUCInStackFrame(JSContext *cx, JSStackFrame *fp,
|
||||
const char *filename, uintN lineno,
|
||||
jsval *rval)
|
||||
{
|
||||
JSObject *scobj;
|
||||
uint32 flags, options;
|
||||
JSScript *script;
|
||||
JSBool ok;
|
||||
|
||||
scobj = js_GetScopeChain(cx, fp);
|
||||
if (!scobj)
|
||||
return JS_FALSE;
|
||||
|
||||
/*
|
||||
* XXX Hack around ancient compiler API to propagate the JSFRAME_SPECIAL
|
||||
* flags to the code generator (see js_EmitTree's TOK_SEMI case).
|
||||
@ -913,7 +918,7 @@ JS_EvaluateUCInStackFrame(JSContext *cx, JSStackFrame *fp,
|
||||
fp->flags |= JSFRAME_DEBUGGER | JSFRAME_EVAL;
|
||||
options = cx->options;
|
||||
cx->options = options | JSOPTION_COMPILE_N_GO;
|
||||
script = JS_CompileUCScriptForPrincipals(cx, fp->scopeChain,
|
||||
script = JS_CompileUCScriptForPrincipals(cx, scobj,
|
||||
JS_StackFramePrincipals(cx, fp),
|
||||
chars, length, filename, lineno);
|
||||
fp->flags = flags;
|
||||
@ -921,8 +926,8 @@ JS_EvaluateUCInStackFrame(JSContext *cx, JSStackFrame *fp,
|
||||
if (!script)
|
||||
return JS_FALSE;
|
||||
|
||||
ok = js_Execute(cx, fp->scopeChain, script, fp,
|
||||
JSFRAME_DEBUGGER | JSFRAME_EVAL, rval);
|
||||
ok = js_Execute(cx, scobj, script, fp, JSFRAME_DEBUGGER | JSFRAME_EVAL,
|
||||
rval);
|
||||
js_DestroyScript(cx, script);
|
||||
return ok;
|
||||
}
|
||||
|
345
js/src/jsemit.c
345
js/src/jsemit.c
@ -233,6 +233,7 @@ js_EmitN(JSContext *cx, JSCodeGenerator *cg, JSOp op, size_t extra)
|
||||
|
||||
/* XXX too many "... statement" L10N gaffes below -- fix via js.msg! */
|
||||
const char js_with_statement_str[] = "with statement";
|
||||
const char js_finally_block_str[] = "finally block";
|
||||
const char js_script_str[] = "script";
|
||||
|
||||
static const char *statementName[] = {
|
||||
@ -242,9 +243,11 @@ static const char *statementName[] = {
|
||||
"else statement", /* ELSE */
|
||||
"switch statement", /* SWITCH */
|
||||
js_with_statement_str, /* WITH */
|
||||
"try statement", /* TRY */
|
||||
"block scope", /* BLOCK_SCOPE */
|
||||
"catch block", /* CATCH */
|
||||
"finally statement", /* FINALLY */
|
||||
"try block", /* TRY */
|
||||
js_finally_block_str, /* FINALLY */
|
||||
js_finally_block_str, /* SUBROUTINE */
|
||||
"do loop", /* DO_LOOP */
|
||||
"for loop", /* FOR_LOOP */
|
||||
"for/in loop", /* FOR_IN_LOOP */
|
||||
@ -1226,6 +1229,22 @@ js_PushStatement(JSTreeContext *tc, JSStmtInfo *stmt, JSStmtType type,
|
||||
stmt->label = NULL;
|
||||
stmt->down = tc->topStmt;
|
||||
tc->topStmt = stmt;
|
||||
if (STMT_TYPE_IS_SCOPE(type)) {
|
||||
stmt->downScope = tc->topScopeStmt;
|
||||
tc->topScopeStmt = stmt;
|
||||
} else {
|
||||
stmt->downScope = NULL;
|
||||
}
|
||||
stmt->blockObj = NULL;
|
||||
}
|
||||
|
||||
void
|
||||
js_PushBlockScope(JSTreeContext *tc, JSStmtInfo *stmt, JSObject *blockObj,
|
||||
ptrdiff_t top)
|
||||
{
|
||||
js_PushStatement(tc, stmt, STMT_BLOCK_SCOPE, top);
|
||||
blockObj->slots[JSSLOT_PARENT] = OBJECT_TO_JSVAL(tc->blockChain);
|
||||
tc->blockChain = stmt->blockObj = blockObj;
|
||||
}
|
||||
|
||||
/*
|
||||
@ -1244,6 +1263,18 @@ EmitBackPatchOp(JSContext *cx, JSCodeGenerator *cg, JSOp op, ptrdiff_t *lastp)
|
||||
return EmitJump(cx, cg, op, delta);
|
||||
}
|
||||
|
||||
/*
|
||||
* Macro to emit a bytecode followed by a uint16 immediate operand stored in
|
||||
* big-endian order, used for arg and var numbers as well as for atomIndexes.
|
||||
* NB: We use cx and cg from our caller's lexical environment, and return
|
||||
* false on error.
|
||||
*/
|
||||
#define EMIT_UINT16_IMM_OP(op, i) \
|
||||
JS_BEGIN_MACRO \
|
||||
if (js_Emit3(cx, cg, op, UINT16_HI(i), UINT16_LO(i)) < 0) \
|
||||
return JS_FALSE; \
|
||||
JS_END_MACRO
|
||||
|
||||
/* Emit additional bytecode(s) for non-local jumps. */
|
||||
static JSBool
|
||||
EmitNonLocalJumpFixup(JSContext *cx, JSCodeGenerator *cg, JSStmtInfo *toStmt,
|
||||
@ -1318,9 +1349,10 @@ EmitNonLocalJumpFixup(JSContext *cx, JSCodeGenerator *cg, JSStmtInfo *toStmt,
|
||||
case STMT_FOR_IN_LOOP:
|
||||
/*
|
||||
* The iterator and the object being iterated need to be popped.
|
||||
* JSOP_POP2 isn't decompiled, so it doesn't need to be HIDDEN.
|
||||
*/
|
||||
if (js_Emit1(cx, cg, JSOP_POP2) < 0)
|
||||
if (js_NewSrcNote(cx, cg, SRC_HIDDEN) < 0)
|
||||
return JS_FALSE;
|
||||
if (js_Emit1(cx, cg, JSOP_ENDITER) < 0)
|
||||
return JS_FALSE;
|
||||
break;
|
||||
|
||||
@ -1332,6 +1364,20 @@ EmitNonLocalJumpFixup(JSContext *cx, JSCodeGenerator *cg, JSStmtInfo *toStmt,
|
||||
return JS_FALSE;
|
||||
break;
|
||||
|
||||
#if JS_HAS_BLOCK_SCOPE
|
||||
case STMT_BLOCK_SCOPE:
|
||||
{
|
||||
uintN i;
|
||||
|
||||
/* There is a Block object with locals on the stack to pop. */
|
||||
if (js_NewSrcNote(cx, cg, SRC_HIDDEN) < 0)
|
||||
return JS_FALSE;
|
||||
i = OBJ_BLOCK_COUNT(cx, stmt->blockObj);
|
||||
EMIT_UINT16_IMM_OP(JSOP_LEAVEBLOCK, i);
|
||||
break;
|
||||
}
|
||||
#endif
|
||||
|
||||
default:;
|
||||
}
|
||||
}
|
||||
@ -1393,7 +1439,19 @@ BackPatch(JSContext *cx, JSCodeGenerator *cg, ptrdiff_t last,
|
||||
void
|
||||
js_PopStatement(JSTreeContext *tc)
|
||||
{
|
||||
tc->topStmt = tc->topStmt->down;
|
||||
JSStmtInfo *stmt;
|
||||
JSStmtType type;
|
||||
|
||||
stmt = tc->topStmt;
|
||||
tc->topStmt = stmt->down;
|
||||
type = stmt->type;
|
||||
if (STMT_TYPE_IS_SCOPE(type)) {
|
||||
tc->topScopeStmt = stmt->downScope;
|
||||
if (type == STMT_BLOCK_SCOPE) {
|
||||
tc->blockChain =
|
||||
JSVAL_TO_OBJECT(stmt->blockObj->slots[JSSLOT_PARENT]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
JSBool
|
||||
@ -1436,12 +1494,59 @@ js_DefineCompileTimeConstant(JSContext *cx, JSCodeGenerator *cg, JSAtom *atom,
|
||||
return JS_TRUE;
|
||||
}
|
||||
|
||||
/*
|
||||
* Find a lexically scoped variable (one declared by let, catch, or an array
|
||||
* comprehension) named by atom, looking in tc's compile-time scopes.
|
||||
*
|
||||
* Return null on error. If atom is found, return the statement info record
|
||||
* in which it was found directly, and set *slotp to its stack slot (if any).
|
||||
* If atom is not found, return &LL_NOT_FOUND.
|
||||
*/
|
||||
static JSStmtInfo LL_NOT_FOUND;
|
||||
|
||||
static JSStmtInfo *
|
||||
LexicalLookup(JSContext *cx, JSTreeContext *tc, JSAtom *atom, jsint *slotp)
|
||||
{
|
||||
JSStmtInfo *stmt;
|
||||
JSObject *obj, *pobj;
|
||||
JSProperty *prop;
|
||||
JSScopeProperty *sprop;
|
||||
|
||||
*slotp = -1;
|
||||
for (stmt = tc->topScopeStmt; stmt; stmt = stmt->downScope) {
|
||||
if (stmt->type == STMT_WITH)
|
||||
return stmt;
|
||||
if (stmt->type == STMT_CATCH && stmt->label == atom)
|
||||
return stmt;
|
||||
|
||||
JS_ASSERT(stmt->type == STMT_BLOCK_SCOPE);
|
||||
obj = stmt->blockObj;
|
||||
if (!js_LookupProperty(cx, obj, ATOM_TO_JSID(atom), &pobj, &prop))
|
||||
return NULL;
|
||||
if (prop) {
|
||||
if (pobj != obj) {
|
||||
stmt = &LL_NOT_FOUND;
|
||||
} else {
|
||||
sprop = (JSScopeProperty *) prop;
|
||||
JS_ASSERT(sprop->flags & SPROP_HAS_SHORTID);
|
||||
*slotp = OBJ_BLOCK_DEPTH(cx, obj) + sprop->shortid;
|
||||
}
|
||||
OBJ_DROP_PROPERTY(cx, pobj, prop);
|
||||
return stmt;
|
||||
}
|
||||
}
|
||||
|
||||
return &LL_NOT_FOUND;
|
||||
}
|
||||
|
||||
JSBool
|
||||
js_LookupCompileTimeConstant(JSContext *cx, JSCodeGenerator *cg, JSAtom *atom,
|
||||
jsval *vp)
|
||||
{
|
||||
JSBool ok;
|
||||
JSStackFrame *fp;
|
||||
JSStmtInfo *stmt;
|
||||
jsint slot;
|
||||
JSAtomListElement *ale;
|
||||
JSObject *obj, *pobj;
|
||||
JSProperty *prop;
|
||||
@ -1461,9 +1566,16 @@ js_LookupCompileTimeConstant(JSContext *cx, JSCodeGenerator *cg, JSAtom *atom,
|
||||
JS_ASSERT(fp->flags & JSFRAME_COMPILING);
|
||||
|
||||
obj = fp->varobj;
|
||||
if (obj == fp->scopeChain &&
|
||||
!js_InWithStatement(&cg->treeContext) &&
|
||||
!js_InCatchBlock(&cg->treeContext, atom)) {
|
||||
if (obj == fp->scopeChain) {
|
||||
/* XXX this will need revising when 'let const' is added. */
|
||||
stmt = LexicalLookup(cx, &cg->treeContext, atom, &slot);
|
||||
if (!stmt)
|
||||
return JS_FALSE;
|
||||
if (stmt != &LL_NOT_FOUND) {
|
||||
fp = fp->down;
|
||||
continue;
|
||||
}
|
||||
|
||||
ATOM_LIST_SEARCH(ale, &cg->constList, atom);
|
||||
if (ale) {
|
||||
*vp = ALE_VALUE(ale);
|
||||
@ -1606,18 +1718,6 @@ IndexRegExpClone(JSContext *cx, JSParseNode *pn, JSAtomListElement *ale,
|
||||
return JS_TRUE;
|
||||
}
|
||||
|
||||
/*
|
||||
* Macro to emit a bytecode followed by a uint16 immediate operand stored in
|
||||
* big-endian order, used for arg and var numbers as well as for atomIndexes.
|
||||
* NB: We use cx and cg from our caller's lexical environment, and return
|
||||
* false on error.
|
||||
*/
|
||||
#define EMIT_UINT16_IMM_OP(op, i) \
|
||||
JS_BEGIN_MACRO \
|
||||
if (js_Emit3(cx, cg, op, ATOM_INDEX_HI(i), ATOM_INDEX_LO(i)) < 0) \
|
||||
return JS_FALSE; \
|
||||
JS_END_MACRO
|
||||
|
||||
/*
|
||||
* Emit a bytecode and its 2-byte constant (atom) index immediate operand.
|
||||
* If the atomIndex requires more than 2 bytes, emit a prefix op whose 24-bit
|
||||
@ -1721,7 +1821,7 @@ EmitAtomOp(JSContext *cx, JSParseNode *pn, JSOp op, JSCodeGenerator *cg)
|
||||
* error, true on success.
|
||||
*
|
||||
* The caller can inspect pn->pn_slot for a non-negative slot number to tell
|
||||
* whether optimization occurred, in which case LookupArgOrVar also updated
|
||||
* whether optimization occurred, in which case BindNameToSlot also updated
|
||||
* pn->pn_op. If pn->pn_slot is still -1 on return, pn->pn_op nevertheless
|
||||
* may have been optimized, e.g., from JSOP_NAME to JSOP_ARGUMENTS. Whether
|
||||
* or not pn->pn_op was modified, if this function finds an argument or local
|
||||
@ -1729,20 +1829,21 @@ EmitAtomOp(JSContext *cx, JSParseNode *pn, JSOp op, JSCodeGenerator *cg)
|
||||
* successful return.
|
||||
*/
|
||||
static JSBool
|
||||
LookupArgOrVar(JSContext *cx, JSTreeContext *tc, JSParseNode *pn)
|
||||
BindNameToSlot(JSContext *cx, JSTreeContext *tc, JSParseNode *pn)
|
||||
{
|
||||
JSAtom *atom;
|
||||
JSStmtInfo *stmt;
|
||||
jsint slot;
|
||||
JSOp op;
|
||||
JSStackFrame *fp;
|
||||
JSObject *obj, *pobj;
|
||||
JSClass *clasp;
|
||||
JSBool optimizeGlobals;
|
||||
JSAtom *atom;
|
||||
JSProperty *prop;
|
||||
JSScopeProperty *sprop;
|
||||
JSOp op;
|
||||
JSPropertyOp getter;
|
||||
uintN attrs;
|
||||
jsint slot;
|
||||
JSAtomListElement *ale;
|
||||
JSProperty *prop;
|
||||
JSScopeProperty *sprop;
|
||||
|
||||
JS_ASSERT(pn->pn_type == TOK_NAME);
|
||||
if (pn->pn_slot >= 0 || pn->pn_op == JSOP_ARGUMENTS)
|
||||
@ -1752,6 +1853,46 @@ LookupArgOrVar(JSContext *cx, JSTreeContext *tc, JSParseNode *pn)
|
||||
if (pn->pn_op == JSOP_QNAMEPART)
|
||||
return JS_TRUE;
|
||||
|
||||
/*
|
||||
* We can't optimize if we are compiling a with statement and its body,
|
||||
* or we're in a catch block whose exception variable has the same name
|
||||
* as this node. FIXME: we should be able to optimize catch vars to be
|
||||
* block-locals.
|
||||
*/
|
||||
atom = pn->pn_atom;
|
||||
stmt = LexicalLookup(cx, tc, atom, &slot);
|
||||
if (!stmt)
|
||||
return JS_FALSE;
|
||||
|
||||
if (stmt != &LL_NOT_FOUND) {
|
||||
if (stmt->type == STMT_WITH)
|
||||
return JS_TRUE;
|
||||
if (stmt->type == STMT_CATCH) {
|
||||
JS_ASSERT(stmt->label == atom);
|
||||
return JS_TRUE;
|
||||
}
|
||||
|
||||
JS_ASSERT(stmt->type == STMT_BLOCK_SCOPE);
|
||||
JS_ASSERT(slot >= 0);
|
||||
op = pn->pn_op;
|
||||
switch (op) {
|
||||
case JSOP_NAME: op = JSOP_GETLOCAL; break;
|
||||
case JSOP_SETNAME: op = JSOP_SETLOCAL; break;
|
||||
case JSOP_INCNAME: op = JSOP_INCLOCAL; break;
|
||||
case JSOP_NAMEINC: op = JSOP_LOCALINC; break;
|
||||
case JSOP_DECNAME: op = JSOP_DECLOCAL; break;
|
||||
case JSOP_NAMEDEC: op = JSOP_LOCALDEC; break;
|
||||
case JSOP_FORNAME: op = JSOP_FORLOCAL; break;
|
||||
case JSOP_DELNAME: op = JSOP_FALSE; break;
|
||||
default: JS_ASSERT(0);
|
||||
}
|
||||
if (op != pn->pn_op) {
|
||||
pn->pn_op = op;
|
||||
pn->pn_slot = slot;
|
||||
}
|
||||
return JS_TRUE;
|
||||
}
|
||||
|
||||
/*
|
||||
* A Script object can be used to split an eval into a compile step done
|
||||
* at construction time, and an execute step done separately, possibly in
|
||||
@ -1760,7 +1901,8 @@ LookupArgOrVar(JSContext *cx, JSTreeContext *tc, JSParseNode *pn)
|
||||
* ensures that its caller's frame has a Call object, so arg and var name
|
||||
* lookups will succeed.
|
||||
*/
|
||||
if (cx->fp->flags & JSFRAME_SCRIPT_OBJECT)
|
||||
fp = cx->fp;
|
||||
if (fp->flags & JSFRAME_SCRIPT_OBJECT)
|
||||
return JS_TRUE;
|
||||
|
||||
/*
|
||||
@ -1775,7 +1917,6 @@ LookupArgOrVar(JSContext *cx, JSTreeContext *tc, JSParseNode *pn)
|
||||
* We can't optimize if we're not compiling a function body, whether via
|
||||
* eval, or directly when compiling a function statement or expression.
|
||||
*/
|
||||
fp = cx->fp;
|
||||
obj = fp->varobj;
|
||||
clasp = OBJ_GET_CLASS(cx, obj);
|
||||
if (clasp != &js_FunctionClass && clasp != &js_CallClass) {
|
||||
@ -1798,16 +1939,10 @@ LookupArgOrVar(JSContext *cx, JSTreeContext *tc, JSParseNode *pn)
|
||||
}
|
||||
|
||||
/*
|
||||
* We can't optimize if we're in an eval called inside a with statement,
|
||||
* or we're compiling a with statement and its body, or we're in a catch
|
||||
* block whose exception variable has the same name as pn.
|
||||
* We can't optimize if we are in an eval called inside a with statement.
|
||||
*/
|
||||
atom = pn->pn_atom;
|
||||
if (fp->scopeChain != obj ||
|
||||
js_InWithStatement(tc) ||
|
||||
js_InCatchBlock(tc, atom)) {
|
||||
if (fp->scopeChain != obj)
|
||||
return JS_TRUE;
|
||||
}
|
||||
|
||||
op = pn->pn_op;
|
||||
getter = NULL;
|
||||
@ -2011,7 +2146,7 @@ CheckSideEffects(JSContext *cx, JSTreeContext *tc, JSParseNode *pn,
|
||||
} else {
|
||||
if (pn->pn_type == TOK_LB) {
|
||||
pn2 = pn->pn_left;
|
||||
if (pn2->pn_type == TOK_NAME && !LookupArgOrVar(cx, tc, pn2))
|
||||
if (pn2->pn_type == TOK_NAME && !BindNameToSlot(cx, tc, pn2))
|
||||
return JS_FALSE;
|
||||
if (pn2->pn_op != JSOP_ARGUMENTS) {
|
||||
/*
|
||||
@ -2041,7 +2176,7 @@ CheckSideEffects(JSContext *cx, JSTreeContext *tc, JSParseNode *pn,
|
||||
|
||||
case PN_NAME:
|
||||
if (pn->pn_type == TOK_NAME) {
|
||||
if (!LookupArgOrVar(cx, tc, pn))
|
||||
if (!BindNameToSlot(cx, tc, pn))
|
||||
return JS_FALSE;
|
||||
if (pn->pn_slot < 0 && pn->pn_op != JSOP_ARGUMENTS) {
|
||||
/*
|
||||
@ -2053,7 +2188,7 @@ CheckSideEffects(JSContext *cx, JSTreeContext *tc, JSParseNode *pn,
|
||||
}
|
||||
pn2 = pn->pn_expr;
|
||||
if (pn->pn_type == TOK_DOT) {
|
||||
if (pn2->pn_type == TOK_NAME && !LookupArgOrVar(cx, tc, pn2))
|
||||
if (pn2->pn_type == TOK_NAME && !BindNameToSlot(cx, tc, pn2))
|
||||
return JS_FALSE;
|
||||
if (!(pn2->pn_op == JSOP_ARGUMENTS &&
|
||||
pn->pn_atom == cx->runtime->atomState.lengthAtom)) {
|
||||
@ -2105,7 +2240,7 @@ EmitPropOp(JSContext *cx, JSParseNode *pn, JSOp op, JSCodeGenerator *cg)
|
||||
pn->pn_type == TOK_DOT &&
|
||||
pn2->pn_type == TOK_NAME) {
|
||||
/* Try to optimize arguments.length into JSOP_ARGCNT. */
|
||||
if (!LookupArgOrVar(cx, &cg->treeContext, pn2))
|
||||
if (!BindNameToSlot(cx, &cg->treeContext, pn2))
|
||||
return JS_FALSE;
|
||||
if (pn2->pn_op == JSOP_ARGUMENTS &&
|
||||
pn->pn_atom == cx->runtime->atomState.lengthAtom) {
|
||||
@ -2193,7 +2328,7 @@ EmitElemOp(JSContext *cx, JSParseNode *pn, JSOp op, JSCodeGenerator *cg)
|
||||
* one or more index expression and JSOP_GETELEM op pairs.
|
||||
*/
|
||||
if (left->pn_type == TOK_NAME && next->pn_type == TOK_NUMBER) {
|
||||
if (!LookupArgOrVar(cx, &cg->treeContext, left))
|
||||
if (!BindNameToSlot(cx, &cg->treeContext, left))
|
||||
return JS_FALSE;
|
||||
if (left->pn_op == JSOP_ARGUMENTS &&
|
||||
JSDOUBLE_IS_INT(next->pn_dval, slot) &&
|
||||
@ -2248,7 +2383,7 @@ EmitElemOp(JSContext *cx, JSParseNode *pn, JSOp op, JSCodeGenerator *cg)
|
||||
if (op == JSOP_GETELEM &&
|
||||
left->pn_type == TOK_NAME &&
|
||||
right->pn_type == TOK_NUMBER) {
|
||||
if (!LookupArgOrVar(cx, &cg->treeContext, left))
|
||||
if (!BindNameToSlot(cx, &cg->treeContext, left))
|
||||
return JS_FALSE;
|
||||
if (left->pn_op == JSOP_ARGUMENTS &&
|
||||
JSDOUBLE_IS_INT(right->pn_dval, slot) &&
|
||||
@ -2786,7 +2921,10 @@ js_EmitFunctionBody(JSContext *cx, JSCodeGenerator *cg, JSParseNode *body,
|
||||
? JSFRAME_COMPILING | JSFRAME_COMPILE_N_GO
|
||||
: JSFRAME_COMPILING;
|
||||
cx->fp = &frame;
|
||||
ok = js_EmitTree(cx, cg, body) && js_Emit1(cx, cg, JSOP_STOP) >= 0;
|
||||
ok = (!(cg->treeContext.flags & TCF_FUN_IS_GENERATOR) ||
|
||||
js_Emit1(cx, cg, JSOP_GENERATOR) >= 0) &&
|
||||
js_EmitTree(cx, cg, body) &&
|
||||
js_Emit1(cx, cg, JSOP_STOP) >= 0;
|
||||
cx->fp = fp;
|
||||
if (!ok)
|
||||
return JS_FALSE;
|
||||
@ -3197,7 +3335,7 @@ js_EmitTree(JSContext *cx, JSCodeGenerator *cg, JSParseNode *pn)
|
||||
return JS_FALSE;
|
||||
|
||||
/* Emit a push to allocate the iterator. */
|
||||
if (js_Emit1(cx, cg, JSOP_PUSH) < 0)
|
||||
if (js_Emit1(cx, cg, JSOP_STARTITER) < 0)
|
||||
return JS_FALSE;
|
||||
|
||||
/* Compile the object expression to the right of 'in'. */
|
||||
@ -3228,23 +3366,27 @@ js_EmitTree(JSContext *cx, JSCodeGenerator *cg, JSParseNode *pn)
|
||||
if (pn3->pn_slot >= 0) {
|
||||
op = pn3->pn_op;
|
||||
switch (op) {
|
||||
case JSOP_GETARG: /* FALL THROUGH */
|
||||
case JSOP_SETARG: op = JSOP_FORARG; break;
|
||||
case JSOP_GETVAR: /* FALL THROUGH */
|
||||
case JSOP_SETVAR: op = JSOP_FORVAR; break;
|
||||
case JSOP_GETGVAR:
|
||||
case JSOP_SETGVAR: op = JSOP_FORNAME; break;
|
||||
default: JS_ASSERT(0);
|
||||
case JSOP_GETARG: /* FALL THROUGH */
|
||||
case JSOP_SETARG: op = JSOP_FORARG; break;
|
||||
case JSOP_GETVAR: /* FALL THROUGH */
|
||||
case JSOP_SETVAR: op = JSOP_FORVAR; break;
|
||||
case JSOP_GETGVAR: /* FALL THROUGH */
|
||||
case JSOP_SETGVAR: op = JSOP_FORNAME; break;
|
||||
case JSOP_GETLOCAL: /* FALL THROUGH */
|
||||
case JSOP_SETLOCAL: op = JSOP_FORLOCAL; break;
|
||||
default: JS_ASSERT(0);
|
||||
}
|
||||
} else {
|
||||
pn3->pn_op = JSOP_FORNAME;
|
||||
if (!LookupArgOrVar(cx, &cg->treeContext, pn3))
|
||||
if (!BindNameToSlot(cx, &cg->treeContext, pn3))
|
||||
return JS_FALSE;
|
||||
op = pn3->pn_op;
|
||||
}
|
||||
if (pn3->pn_slot >= 0) {
|
||||
if (pn3->pn_attrs & JSPROP_READONLY)
|
||||
if (pn3->pn_attrs & JSPROP_READONLY) {
|
||||
JS_ASSERT(op == JSOP_FORVAR);
|
||||
op = JSOP_GETVAR;
|
||||
}
|
||||
atomIndex = (jsatomid) pn3->pn_slot;
|
||||
EMIT_UINT16_IMM_OP(op, atomIndex);
|
||||
} else {
|
||||
@ -3429,12 +3571,12 @@ js_EmitTree(JSContext *cx, JSCodeGenerator *cg, JSParseNode *pn)
|
||||
return JS_FALSE;
|
||||
}
|
||||
|
||||
/* Now fixup all breaks and continues (before for/in's final POP2). */
|
||||
/* Now fixup all breaks and continues (before for/in's JSOP_ENDITER). */
|
||||
if (!js_PopStatementCG(cx, cg))
|
||||
return JS_FALSE;
|
||||
|
||||
if (pn2->pn_type == TOK_IN) {
|
||||
if (js_Emit1(cx, cg, JSOP_POP2) < 0)
|
||||
if (js_Emit1(cx, cg, JSOP_ENDITER) < 0)
|
||||
return JS_FALSE;
|
||||
}
|
||||
break;
|
||||
@ -3804,7 +3946,7 @@ js_EmitTree(JSContext *cx, JSCodeGenerator *cg, JSParseNode *pn)
|
||||
off = noteIndex = -1;
|
||||
for (pn2 = pn->pn_head; ; pn2 = pn2->pn_next) {
|
||||
JS_ASSERT(pn2->pn_type == TOK_NAME);
|
||||
if (!LookupArgOrVar(cx, &cg->treeContext, pn2))
|
||||
if (!BindNameToSlot(cx, &cg->treeContext, pn2))
|
||||
return JS_FALSE;
|
||||
op = pn2->pn_op;
|
||||
if (op == JSOP_ARGUMENTS) {
|
||||
@ -3938,6 +4080,15 @@ js_EmitTree(JSContext *cx, JSCodeGenerator *cg, JSParseNode *pn)
|
||||
return JS_FALSE;
|
||||
break;
|
||||
|
||||
#if JS_HAS_GENERATORS
|
||||
case TOK_YIELD:
|
||||
if (!js_EmitTree(cx, cg, pn->pn_kid))
|
||||
return JS_FALSE;
|
||||
if (js_Emit1(cx, cg, JSOP_YIELD) < 0)
|
||||
return JS_FALSE;
|
||||
break;
|
||||
#endif
|
||||
|
||||
case TOK_LC:
|
||||
#if JS_HAS_XML_SUPPORT
|
||||
if (pn->pn_arity == PN_UNARY) {
|
||||
@ -4064,7 +4215,7 @@ js_EmitTree(JSContext *cx, JSCodeGenerator *cg, JSParseNode *pn)
|
||||
atomIndex = (jsatomid) -1; /* Suppress warning. */
|
||||
switch (pn2->pn_type) {
|
||||
case TOK_NAME:
|
||||
if (!LookupArgOrVar(cx, &cg->treeContext, pn2))
|
||||
if (!BindNameToSlot(cx, &cg->treeContext, pn2))
|
||||
return JS_FALSE;
|
||||
if (pn2->pn_slot >= 0) {
|
||||
atomIndex = (jsatomid) pn2->pn_slot;
|
||||
@ -4369,7 +4520,7 @@ js_EmitTree(JSContext *cx, JSCodeGenerator *cg, JSParseNode *pn)
|
||||
switch (pn2->pn_type) {
|
||||
case TOK_NAME:
|
||||
pn2->pn_op = op;
|
||||
if (!LookupArgOrVar(cx, &cg->treeContext, pn2))
|
||||
if (!BindNameToSlot(cx, &cg->treeContext, pn2))
|
||||
return JS_FALSE;
|
||||
op = pn2->pn_op;
|
||||
if (pn2->pn_slot >= 0) {
|
||||
@ -4444,7 +4595,7 @@ js_EmitTree(JSContext *cx, JSCodeGenerator *cg, JSParseNode *pn)
|
||||
switch (pn2->pn_type) {
|
||||
case TOK_NAME:
|
||||
pn2->pn_op = JSOP_DELNAME;
|
||||
if (!LookupArgOrVar(cx, &cg->treeContext, pn2))
|
||||
if (!BindNameToSlot(cx, &cg->treeContext, pn2))
|
||||
return JS_FALSE;
|
||||
op = pn2->pn_op;
|
||||
if (op == JSOP_FALSE) {
|
||||
@ -4573,7 +4724,56 @@ js_EmitTree(JSContext *cx, JSCodeGenerator *cg, JSParseNode *pn)
|
||||
return JS_FALSE;
|
||||
break;
|
||||
|
||||
#if JS_HAS_BLOCK_SCOPE
|
||||
case TOK_LEXICALSCOPE:
|
||||
{
|
||||
JSObject *obj;
|
||||
jsint count;
|
||||
|
||||
atom = pn->pn_atom;
|
||||
obj = ATOM_TO_OBJECT(atom);
|
||||
js_PushBlockScope(&cg->treeContext, &stmtInfo, obj, CG_OFFSET(cg));
|
||||
|
||||
OBJ_SET_BLOCK_DEPTH(cx, obj, cg->stackDepth);
|
||||
count = OBJ_BLOCK_COUNT(cx, obj);
|
||||
cg->stackDepth += count;
|
||||
if ((uintN)cg->stackDepth > cg->maxStackDepth)
|
||||
cg->maxStackDepth = cg->stackDepth;
|
||||
|
||||
ale = js_IndexAtom(cx, atom, &cg->atomList);
|
||||
if (!ale)
|
||||
return JS_FALSE;
|
||||
EMIT_ATOM_INDEX_OP(JSOP_ENTERBLOCK, ALE_INDEX(ale));
|
||||
|
||||
if (!js_EmitTree(cx, cg, pn->pn_expr))
|
||||
return JS_FALSE;
|
||||
|
||||
EMIT_UINT16_IMM_OP(JSOP_LEAVEBLOCK, count);
|
||||
cg->stackDepth -= count;
|
||||
|
||||
if (!js_PopStatementCG(cx, cg))
|
||||
return JS_FALSE;
|
||||
break;
|
||||
}
|
||||
#endif
|
||||
|
||||
#if JS_HAS_GENERATORS
|
||||
case TOK_ARRAYPUSH:
|
||||
/*
|
||||
* Pick up the array's stack index from pn->pn_array, which points up
|
||||
* the tree to our TOK_ARRAYCOMP ancestor. See below under the array
|
||||
* initialiser code generator for array comprehension special casing.
|
||||
*/
|
||||
if (!js_EmitTree(cx, cg, pn->pn_kid))
|
||||
return JS_FALSE;
|
||||
EMIT_UINT16_IMM_OP(pn->pn_op, pn->pn_array->pn_extra);
|
||||
break;
|
||||
#endif
|
||||
|
||||
case TOK_RB:
|
||||
#if JS_HAS_GENERATORS
|
||||
case TOK_ARRAYCOMP:
|
||||
#endif
|
||||
/*
|
||||
* Emit code for [a, b, c] of the form:
|
||||
* t = new Array; t[0] = a; t[1] = b; t[2] = c; t;
|
||||
@ -4597,6 +4797,25 @@ js_EmitTree(JSContext *cx, JSCodeGenerator *cg, JSParseNode *pn)
|
||||
}
|
||||
#endif
|
||||
|
||||
#if JS_HAS_GENERATORS
|
||||
if (pn->pn_type == TOK_ARRAYCOMP) {
|
||||
/*
|
||||
* Pass the new array's stack index to the TOK_ARRAYPUSH case by
|
||||
* storing it in pn->pn_extra, then simply traverse the TOK_FOR
|
||||
* node and its kids under pn2 to generate this comprehension.
|
||||
*/
|
||||
JS_ASSERT(cg->stackDepth > 0);
|
||||
pn->pn_extra = (uint32) (cg->stackDepth - 1);
|
||||
if (!js_EmitTree(cx, cg, pn2))
|
||||
return JS_FALSE;
|
||||
|
||||
/* Emit the usual op needed for decompilation. */
|
||||
if (js_Emit1(cx, cg, JSOP_ENDINIT) < 0)
|
||||
return JS_FALSE;
|
||||
break;
|
||||
}
|
||||
#endif /* JS_HAS_GENERATORS */
|
||||
|
||||
for (atomIndex = 0; pn2; atomIndex++, pn2 = pn2->pn_next) {
|
||||
if (!EmitNumberOp(cx, atomIndex, cg))
|
||||
return JS_FALSE;
|
||||
@ -4720,7 +4939,7 @@ js_EmitTree(JSContext *cx, JSCodeGenerator *cg, JSParseNode *pn)
|
||||
break;
|
||||
|
||||
case TOK_NAME:
|
||||
if (!LookupArgOrVar(cx, &cg->treeContext, pn))
|
||||
if (!BindNameToSlot(cx, &cg->treeContext, pn))
|
||||
return JS_FALSE;
|
||||
op = pn->pn_op;
|
||||
if (op == JSOP_ARGUMENTS) {
|
||||
@ -5202,7 +5421,7 @@ js_SetSrcNoteOffset(JSContext *cx, JSCodeGenerator *cg, uintN index,
|
||||
return JS_TRUE;
|
||||
}
|
||||
|
||||
#ifdef DEBUG_brendan
|
||||
#ifdef DEBUG_notme
|
||||
#define DEBUG_srcnotesize
|
||||
#endif
|
||||
|
||||
@ -5291,7 +5510,7 @@ js_FinishTakingSrcNotes(JSContext *cx, JSCodeGenerator *cg, jssrcnote *notes)
|
||||
memcpy(notes + prologCount, cg->main.notes, SRCNOTE_SIZE(mainCount));
|
||||
SN_MAKE_TERMINATOR(¬es[totalCount]);
|
||||
|
||||
#ifdef DEBUG_brendan
|
||||
#ifdef DEBUG_notme
|
||||
{ int bin = JS_CeilingLog2(totalCount);
|
||||
if (bin >= NBINS)
|
||||
bin = NBINS - 1;
|
||||
|
@ -53,8 +53,12 @@
|
||||
JS_BEGIN_EXTERN_C
|
||||
|
||||
/*
|
||||
* NB: If you add non-loop STMT_* enumerators, do so before STMT_DO_LOOP or
|
||||
* you will break the STMT_IS_LOOP macro, just below this enum.
|
||||
* NB: If you add enumerators for scope statements, add them between STMT_WITH
|
||||
* and STMT_CATCH, or you will break the STMT_TYPE_IS_SCOPE macro. If you add
|
||||
* non-looping statement enumerators, add them before STMT_DO_LOOP or you will
|
||||
* break the STMT_TYPE_IS_LOOP macro.
|
||||
*
|
||||
* Also remember to keep the statementName array in jsemit.c in sync.
|
||||
*/
|
||||
typedef enum JSStmtType {
|
||||
STMT_BLOCK = 0, /* compound statement: { s1[;... sN] } */
|
||||
@ -63,17 +67,23 @@ typedef enum JSStmtType {
|
||||
STMT_ELSE = 3, /* else clause of if statement */
|
||||
STMT_SWITCH = 4, /* switch statement */
|
||||
STMT_WITH = 5, /* with statement */
|
||||
STMT_TRY = 6, /* try statement */
|
||||
STMT_BLOCK_SCOPE = 6, /* let block/expr or array comprehension */
|
||||
STMT_CATCH = 7, /* catch block */
|
||||
STMT_FINALLY = 8, /* finally statement */
|
||||
STMT_SUBROUTINE = 9, /* gosub-target subroutine body */
|
||||
STMT_DO_LOOP = 10, /* do/while loop statement */
|
||||
STMT_FOR_LOOP = 11, /* for loop statement */
|
||||
STMT_FOR_IN_LOOP = 12, /* for/in loop statement */
|
||||
STMT_WHILE_LOOP = 13 /* while loop statement */
|
||||
STMT_TRY = 8, /* try block */
|
||||
STMT_FINALLY = 9, /* finally block */
|
||||
STMT_SUBROUTINE = 10, /* gosub-target subroutine body */
|
||||
STMT_DO_LOOP = 11, /* do/while loop statement */
|
||||
STMT_FOR_LOOP = 12, /* for loop statement */
|
||||
STMT_FOR_IN_LOOP = 13, /* for/in loop statement */
|
||||
STMT_WHILE_LOOP = 14 /* while loop statement */
|
||||
} JSStmtType;
|
||||
|
||||
#define STMT_IS_LOOP(stmt) ((stmt)->type >= STMT_DO_LOOP)
|
||||
#define STMT_TYPE_IS_SCOPE(type) \
|
||||
((uintN)((type) - STMT_WITH) < (uintN)(STMT_CATCH - STMT_WITH))
|
||||
#define STMT_TYPE_IS_LOOP(type) ((type) >= STMT_DO_LOOP)
|
||||
|
||||
#define STMT_IS_SCOPE(stmt) STMT_TYPE_IS_SCOPE((stmt)->type)
|
||||
#define STMT_IS_LOOP(stmt) STMT_TYPE_IS_LOOP((stmt)->type)
|
||||
|
||||
typedef struct JSStmtInfo JSStmtInfo;
|
||||
|
||||
@ -86,6 +96,8 @@ struct JSStmtInfo {
|
||||
ptrdiff_t catchJump; /* offset of last end-of-catch jump */
|
||||
JSAtom *label; /* name of LABEL or CATCH var */
|
||||
JSStmtInfo *down; /* info for enclosing statement */
|
||||
JSStmtInfo *downScope; /* next enclosing lexical scope */
|
||||
JSObject *blockObj; /* block object if BLOCK_SCOPE */
|
||||
};
|
||||
|
||||
#define SET_STATEMENT_TOP(stmt, top) \
|
||||
@ -99,6 +111,8 @@ struct JSTreeContext { /* tree context for semantic checks */
|
||||
uint32 globalUses; /* optimizable global var uses in total */
|
||||
uint32 loopyGlobalUses;/* optimizable global var uses in loops */
|
||||
JSStmtInfo *topStmt; /* top of statement info stack */
|
||||
JSStmtInfo *topScopeStmt; /* top lexical scope statement */
|
||||
JSObject *blockChain; /* compile time block scope chain */
|
||||
JSAtomList decls; /* function, const, and var declarations */
|
||||
JSParseNode *nodeList; /* list of recyclable parse-node structs */
|
||||
};
|
||||
@ -111,13 +125,16 @@ struct JSTreeContext { /* tree context for semantic checks */
|
||||
#define TCF_FUN_CLOSURE_VS_VAR 0x20 /* function and var with same name */
|
||||
#define TCF_FUN_USES_NONLOCALS 0x40 /* function refers to non-local names */
|
||||
#define TCF_FUN_HEAVYWEIGHT 0x80 /* function needs Call object per call */
|
||||
#define TCF_FUN_FLAGS 0xE0 /* flags to propagate from FunctionBody */
|
||||
#define TCF_HAS_DEFXMLNS 0x100 /* default xml namespace = ...; parsed */
|
||||
#define TCF_FUN_IS_GENERATOR 0x100 /* parsed yield statement in function */
|
||||
#define TCF_FUN_FLAGS 0x1E0 /* flags to propagate from FunctionBody */
|
||||
#define TCF_HAS_DEFXMLNS 0x200 /* default xml namespace = ...; parsed */
|
||||
|
||||
#define TREE_CONTEXT_INIT(tc) \
|
||||
((tc)->flags = (tc)->numGlobalVars = 0, \
|
||||
(tc)->tryCount = (tc)->globalUses = (tc)->loopyGlobalUses = 0, \
|
||||
(tc)->topStmt = NULL, ATOM_LIST_INIT(&(tc)->decls), \
|
||||
(tc)->topStmt = (tc)->topScopeStmt = NULL, \
|
||||
(tc)->blockChain = NULL, \
|
||||
ATOM_LIST_INIT(&(tc)->decls), \
|
||||
(tc)->nodeList = NULL)
|
||||
|
||||
#define TREE_CONTEXT_FINISH(tc) \
|
||||
@ -332,6 +349,15 @@ extern void
|
||||
js_PushStatement(JSTreeContext *tc, JSStmtInfo *stmt, JSStmtType type,
|
||||
ptrdiff_t top);
|
||||
|
||||
/*
|
||||
* Push a block scope statement and link blockObj into tc->blockChain. To pop
|
||||
* this statement info record, use js_PopStatement as usual, or if appropriate
|
||||
* (if generating code), js_PopStatementCG.
|
||||
*/
|
||||
extern void
|
||||
js_PushBlockScope(JSTreeContext *tc, JSStmtInfo *stmt, JSObject *blockObj,
|
||||
ptrdiff_t top);
|
||||
|
||||
/*
|
||||
* Pop tc->topStmt. If the top JSStmtInfo struct is not stack-allocated, it
|
||||
* is up to the caller to free it.
|
||||
|
@ -579,6 +579,8 @@ js_PutCallObject(JSContext *cx, JSStackFrame *fp)
|
||||
{
|
||||
JSObject *callobj;
|
||||
JSBool ok;
|
||||
JSObject *obj;
|
||||
JSScopeProperty *sprop;
|
||||
jsid argsid;
|
||||
jsval aval;
|
||||
|
||||
@ -591,6 +593,17 @@ js_PutCallObject(JSContext *cx, JSStackFrame *fp)
|
||||
return JS_TRUE;
|
||||
ok = call_enumerate(cx, callobj);
|
||||
|
||||
/*
|
||||
* Walk the scope chain looking for block scopes whose locals need to be
|
||||
* copied from stack slots into object slots before fp goes away.
|
||||
*/
|
||||
for (obj = fp->scopeChain; obj; obj = OBJ_GET_PARENT(cx, obj)) {
|
||||
if (OBJ_GET_CLASS(cx, obj) == &js_BlockClass) {
|
||||
for (sprop = OBJ_SCOPE(obj)->lastProp; sprop; sprop = sprop->parent)
|
||||
ok &= OBJ_GET_PROPERTY(cx, obj, sprop->id, &aval);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Get the arguments object to snapshot fp's actual argument values.
|
||||
*/
|
||||
@ -1211,7 +1224,7 @@ fun_xdrObject(JSXDRState *xdr, JSObject **objp)
|
||||
}
|
||||
|
||||
/* From here on, control flow must flow through label out. */
|
||||
JS_PUSH_SINGLE_TEMP_ROOT(cx, OBJECT_TO_JSVAL(fun->object), &tvr);
|
||||
JS_PUSH_SINGLE_TEMP_ROOT(cx, fun->object, &tvr);
|
||||
ok = JS_TRUE;
|
||||
|
||||
if (!JS_XDRUint32(xdr, &nullAtom))
|
||||
@ -2230,6 +2243,8 @@ js_ReportIsNotFunction(JSContext *cx, jsval *vp, uintN flags)
|
||||
JSType type;
|
||||
JSString *fallback;
|
||||
JSString *str;
|
||||
JSTempValueRooter tvr;
|
||||
const char *bytes;
|
||||
|
||||
/*
|
||||
* We provide the typename as the fallback to handle the case when
|
||||
@ -2248,10 +2263,20 @@ js_ReportIsNotFunction(JSContext *cx, jsval *vp, uintN flags)
|
||||
*vp,
|
||||
fallback);
|
||||
if (str) {
|
||||
JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL,
|
||||
(uintN)((flags & JSV2F_CONSTRUCT)
|
||||
? JSMSG_NOT_CONSTRUCTOR
|
||||
: JSMSG_NOT_FUNCTION),
|
||||
JS_GetStringBytes(str));
|
||||
JS_PUSH_SINGLE_TEMP_ROOT(cx, str, &tvr);
|
||||
bytes = JS_GetStringBytes(str);
|
||||
if (flags & JSV2F_ITERATOR) {
|
||||
JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL,
|
||||
JSMSG_BAD_ITERATOR,
|
||||
bytes, js_iterator_str,
|
||||
js_ValueToPrintableSource(cx, *vp));
|
||||
} else {
|
||||
JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL,
|
||||
(uintN)((flags & JSV2F_CONSTRUCT)
|
||||
? JSMSG_NOT_CONSTRUCTOR
|
||||
: JSMSG_NOT_FUNCTION),
|
||||
bytes);
|
||||
}
|
||||
JS_POP_TEMP_ROOT(cx, &tvr);
|
||||
}
|
||||
}
|
||||
|
@ -122,7 +122,8 @@ js_DefineFunction(JSContext *cx, JSObject *obj, JSAtom *atom, JSNative native,
|
||||
* with #if/#error in jsfun.c.
|
||||
*/
|
||||
#define JSV2F_CONSTRUCT JSINVOKE_CONSTRUCT
|
||||
#define JSV2F_SEARCH_STACK 2
|
||||
#define JSV2F_ITERATOR JSINVOKE_ITERATOR
|
||||
#define JSV2F_SEARCH_STACK 0x10000
|
||||
|
||||
extern JSFunction *
|
||||
js_ValueToFunction(JSContext *cx, jsval *vp, uintN flags);
|
||||
|
@ -2178,7 +2178,7 @@ restart:
|
||||
if (acx->throwing && JSVAL_IS_GCTHING(acx->exception))
|
||||
GC_MARK(cx, JSVAL_TO_GCTHING(acx->exception), "exception");
|
||||
#if JS_HAS_LVALUE_RETURN
|
||||
if (acx->rval2set && JSVAL_IS_GCTHING(acx->rval2))
|
||||
if (acx->rval2set == JS_RVAL2_VALUE && JSVAL_IS_GCTHING(acx->rval2))
|
||||
GC_MARK(cx, JSVAL_TO_GCTHING(acx->rval2), "rval2");
|
||||
#endif
|
||||
|
||||
@ -2202,8 +2202,7 @@ restart:
|
||||
}
|
||||
}
|
||||
|
||||
for (i = 0; i < JSProto_LIMIT; i++)
|
||||
GC_MARK(cx, acx->classObjects[i], "classObjects[i]");
|
||||
acx->cachedIterObj = NULL;
|
||||
}
|
||||
|
||||
#ifdef DUMP_CALL_TABLE
|
||||
|
File diff suppressed because it is too large
Load Diff
@ -51,6 +51,11 @@ JS_BEGIN_EXTERN_C
|
||||
* JS stack frame, may be allocated on the C stack by native callers. Always
|
||||
* allocated on cx->stackPool for calls from the interpreter to an interpreted
|
||||
* function.
|
||||
*
|
||||
* NB: This struct is manually initialized in jsinterp.c and jsiter.c. If you
|
||||
* add new members, update both files. But first, try to remove members. The
|
||||
* sharp* and xml* members should be moved onto the stack as local variables
|
||||
* with well-known slots, if possible.
|
||||
*/
|
||||
struct JSStackFrame {
|
||||
JSObject *callobj; /* lazily created Call object */
|
||||
@ -75,6 +80,7 @@ struct JSStackFrame {
|
||||
uint32 flags; /* frame flags -- see below */
|
||||
JSStackFrame *dormantNext; /* next dormant frame chain */
|
||||
JSObject *xmlNamespace; /* null or default xml namespace in E4X */
|
||||
JSObject *blockChain; /* active compile-time block scopes */
|
||||
};
|
||||
|
||||
typedef struct JSInlineFrame {
|
||||
@ -99,6 +105,9 @@ typedef struct JSInlineFrame {
|
||||
#define JSFRAME_COMPILE_N_GO 0x80 /* compiler-and-go mode, can optimize name
|
||||
references based on scope chain */
|
||||
#define JSFRAME_SCRIPT_OBJECT 0x100 /* compiling source for a Script object */
|
||||
#define JSFRAME_YIELDING 0x200 /* js_Interpret dispatched JSOP_YIELD */
|
||||
#define JSFRAME_FILTERING 0x400 /* XML filtering predicate expression */
|
||||
#define JSFRAME_ITERATOR 0x800 /* trying to get an iterator for for-in */
|
||||
|
||||
#define JSFRAME_OVERRIDE_SHIFT 24 /* override bit-set params; see jsfun.c */
|
||||
#define JSFRAME_OVERRIDE_BITS 8
|
||||
@ -259,6 +268,16 @@ extern size_t js_LogCallToSourceLimit;
|
||||
extern void js_DumpCallTable(JSContext *cx);
|
||||
#endif
|
||||
|
||||
/*
|
||||
* Refresh and return fp->scopeChain. It may be stale if block scopes are
|
||||
* active but not yet reflected by objects in the scope chain. If a block
|
||||
* scope contains a with, eval, XML filtering predicate, or similar such
|
||||
* dynamically scoped construct, then compile-time block scope at fp->blocks
|
||||
* must reflect at runtime.
|
||||
*/
|
||||
extern JSObject *
|
||||
js_GetScopeChain(JSContext *cx, JSStackFrame *fp);
|
||||
|
||||
/*
|
||||
* Compute the 'this' parameter for a call with nominal 'this' given by thisp
|
||||
* and arguments including argv[-1] (nominal 'this') and argv[-2] (callee).
|
||||
@ -278,11 +297,27 @@ extern JS_FRIEND_API(JSBool)
|
||||
js_Invoke(JSContext *cx, uintN argc, uintN flags);
|
||||
|
||||
/*
|
||||
* Consolidated js_Invoke flags simply rename the low JSFRAME_* flags.
|
||||
* Consolidated js_Invoke flags simply rename certain JSFRAME_* flags, so that
|
||||
* we can share bits stored in JSStackFrame.flags and passed to:
|
||||
*
|
||||
* js_Invoke
|
||||
* js_InternalInvoke
|
||||
* js_ValueToFunction
|
||||
* js_ValueToFunctionObject
|
||||
* js_ValueToCallableObject
|
||||
* js_ReportIsNotFunction
|
||||
*
|
||||
* See jsfun.h for the latter four and flag renaming macros.
|
||||
*/
|
||||
#define JSINVOKE_CONSTRUCT JSFRAME_CONSTRUCTING
|
||||
#define JSINVOKE_INTERNAL JSFRAME_INTERNAL
|
||||
#define JSINVOKE_SKIP_CALLER JSFRAME_SKIP_CALLER
|
||||
#define JSINVOKE_ITERATOR JSFRAME_ITERATOR
|
||||
|
||||
/*
|
||||
* Mask to isolate construct and iterator flags for use with jsfun.h functions.
|
||||
*/
|
||||
#define JSINVOKE_FUNFLAGS (JSINVOKE_CONSTRUCT | JSINVOKE_ITERATOR)
|
||||
|
||||
/*
|
||||
* "Internal" calls may come from C or C++ code using a JSContext on which no
|
||||
|
@ -57,6 +57,7 @@
|
||||
#include "jsgc.h"
|
||||
#include "jsinterp.h"
|
||||
#include "jsiter.h"
|
||||
#include "jslock.h"
|
||||
#include "jsobj.h"
|
||||
#include "jsopcode.h"
|
||||
#include "jsscope.h"
|
||||
@ -96,7 +97,7 @@ iterator_finalize(JSContext *cx, JSObject *obj)
|
||||
NULL, NULL);
|
||||
} else
|
||||
#endif
|
||||
OBJ_ENUMERATE(cx, iterable, JSENUMERATE_DESTROY, &state, NULL);
|
||||
OBJ_ENUMERATE(cx, iterable, JSENUMERATE_DESTROY, &state, NULL);
|
||||
}
|
||||
|
||||
js_RemoveRoot(cx->runtime, &obj->slots[JSSLOT_PARENT]);
|
||||
@ -332,6 +333,14 @@ js_FinishNativeIterator(JSContext *cx, JSObject *iterobj)
|
||||
if (!(flags & JSITER_HIDDEN))
|
||||
return;
|
||||
|
||||
/*
|
||||
* Clear the cached iterator object member of cx. Normally the GC clears
|
||||
* all contexts' cachedIterObj members, but JSOP_ENDITER calls us eagerly
|
||||
* to finalize iterobj.
|
||||
*/
|
||||
if (iterobj == cx->cachedIterObj)
|
||||
cx->cachedIterObj = NULL;
|
||||
|
||||
/* Finalize iterobj, then mark its ITER_STATE slot to flag it as dead. */
|
||||
iterator_finalize(cx, iterobj);
|
||||
iterobj->slots[JSSLOT_ITER_STATE] = JSVAL_VOID;
|
||||
@ -341,17 +350,17 @@ JSBool
|
||||
js_DefaultIterator(JSContext *cx, JSObject *obj, uintN argc, jsval *argv,
|
||||
jsval *rval)
|
||||
{
|
||||
JSBool foreach;
|
||||
JSBool keyonly;
|
||||
|
||||
if (OBJ_GET_CLASS(cx, obj) == &js_IteratorClass) {
|
||||
*rval = OBJECT_TO_JSVAL(obj);
|
||||
return JS_TRUE;
|
||||
}
|
||||
|
||||
foreach = JS_FALSE;
|
||||
if (argc != 0 && !js_ValueToBoolean(cx, argv[0], &foreach))
|
||||
keyonly = JS_FALSE;
|
||||
if (argc != 0 && !js_ValueToBoolean(cx, argv[0], &keyonly))
|
||||
return JS_FALSE;
|
||||
return js_NewNativeIterator(cx, obj, foreach ? JSITER_FOREACH : 0, rval);
|
||||
return js_NewNativeIterator(cx, obj, keyonly ? 0 : JSITER_FOREACH, rval);
|
||||
}
|
||||
|
||||
JSObject *
|
||||
@ -376,7 +385,7 @@ js_ValueToIterator(JSContext *cx, jsval v, uintN flags)
|
||||
if (!JS_GetMethodById(cx, obj, ATOM_TO_JSID(atom), &obj, &fval))
|
||||
goto bad;
|
||||
|
||||
arg = BOOLEAN_TO_JSVAL((flags & JSITER_FOREACH) != 0);
|
||||
arg = BOOLEAN_TO_JSVAL((flags & JSITER_FOREACH) == 0);
|
||||
if (!js_InternalInvoke(cx, obj, fval, JSINVOKE_ITERATOR, 1, &arg, &rval))
|
||||
goto bad;
|
||||
|
||||
@ -400,7 +409,7 @@ js_ValueToIterator(JSContext *cx, jsval v, uintN flags)
|
||||
* code -- the js_FinishNativeIteration early-finalization optimization
|
||||
* based on it will break badly if script can reach iterobj.
|
||||
*/
|
||||
if (JS_InstanceOf(cx, iterobj, &js_IteratorClass, NULL) &&
|
||||
if (OBJ_GET_CLASS(cx, iterobj) == &js_IteratorClass &&
|
||||
VALUE_IS_FUNCTION(cx, fval)) {
|
||||
fun = (JSFunction *) JS_GetPrivate(cx, JSVAL_TO_OBJECT(fval));
|
||||
if (!FUN_INTERPRETED(fun) && fun->u.n.native == js_DefaultIterator)
|
||||
@ -416,11 +425,74 @@ bad:
|
||||
}
|
||||
|
||||
JSBool
|
||||
js_CallIteratorNext(JSContext *cx, JSObject *iterobj, jsid *idp, jsval *rval)
|
||||
js_CallIteratorNext(JSContext *cx, JSObject *iterobj, uintN flags,
|
||||
jsid *idp, jsval *rval)
|
||||
{
|
||||
JSBool unlock;
|
||||
JSObject *obj;
|
||||
JSScope *scope;
|
||||
JSScopeProperty *sprop;
|
||||
jsval fval;
|
||||
JSFunction *fun;
|
||||
const jsid id = ATOM_TO_JSID(cx->runtime->atomState.nextAtom);
|
||||
|
||||
/* Fastest path for repeated call from for-in loop bytecode. */
|
||||
if (iterobj == cx->cachedIterObj) {
|
||||
JS_ASSERT(OBJ_GET_CLASS(cx, iterobj) == &js_IteratorClass);
|
||||
JS_ASSERT(flags & JSITER_HIDDEN);
|
||||
if (!iterator_next(cx, iterobj, 0, NULL, rval) ||
|
||||
!CheckKeyValueReturn(cx, idp, rval)) {
|
||||
cx->cachedIterObj = NULL;
|
||||
return JS_FALSE;
|
||||
}
|
||||
return JS_TRUE;
|
||||
}
|
||||
|
||||
/* Fast path for native iterator with unoverridden .next() method. */
|
||||
unlock = JS_TRUE;
|
||||
obj = iterobj;
|
||||
JS_LOCK_OBJ(cx, obj);
|
||||
scope = OBJ_SCOPE(obj);
|
||||
sprop = NULL;
|
||||
|
||||
while (LOCKED_OBJ_GET_CLASS(obj) == &js_IteratorClass) {
|
||||
obj = scope->object;
|
||||
sprop = SCOPE_GET_PROPERTY(scope, id);
|
||||
if (sprop)
|
||||
break;
|
||||
obj = LOCKED_OBJ_GET_PROTO(obj);
|
||||
if (!obj)
|
||||
break;
|
||||
JS_UNLOCK_SCOPE(cx, scope);
|
||||
scope = OBJ_SCOPE(obj);
|
||||
JS_LOCK_SCOPE(cx, scope);
|
||||
}
|
||||
|
||||
if (sprop && SPROP_HAS_VALID_SLOT(sprop, scope)) {
|
||||
/*
|
||||
* Unlock scope as soon as we fetch fval, and clear the unlock flag in
|
||||
* case we do not return early after setting cx->cachedIterObj.
|
||||
*/
|
||||
fval = LOCKED_OBJ_GET_SLOT(obj, sprop->slot);
|
||||
JS_UNLOCK_SCOPE(cx, scope);
|
||||
unlock = JS_FALSE;
|
||||
if (VALUE_IS_FUNCTION(cx, fval)) {
|
||||
fun = (JSFunction *) JS_GetPrivate(cx, JSVAL_TO_OBJECT(fval));
|
||||
if (!FUN_INTERPRETED(fun) && fun->u.n.native == iterator_next) {
|
||||
if (!iterator_next(cx, iterobj, 0, NULL, rval) ||
|
||||
!CheckKeyValueReturn(cx, idp, rval)) {
|
||||
return JS_FALSE;
|
||||
}
|
||||
if (flags & JSITER_HIDDEN)
|
||||
cx->cachedIterObj = iterobj;
|
||||
return JS_TRUE;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (unlock)
|
||||
JS_UNLOCK_SCOPE(cx, scope);
|
||||
|
||||
return JS_GetMethodById(cx, iterobj, id, &iterobj, &fval) &&
|
||||
js_InternalCall(cx, iterobj, fval, 0, NULL, rval) &&
|
||||
CheckKeyValueReturn(cx, idp, rval);
|
||||
@ -442,7 +514,8 @@ static JSFunctionSpec stopiter_methods[] = {
|
||||
static JSBool
|
||||
stopiter_hasInstance(JSContext *cx, JSObject *obj, jsval v, JSBool *bp)
|
||||
{
|
||||
*bp = !JSVAL_IS_PRIMITIVE(v) && JSVAL_TO_OBJECT(v) == obj;
|
||||
*bp = !JSVAL_IS_PRIMITIVE(v) &&
|
||||
OBJ_GET_CLASS(cx, JSVAL_TO_OBJECT(v)) == &js_StopIterationClass;
|
||||
return JS_TRUE;
|
||||
}
|
||||
|
||||
@ -462,11 +535,11 @@ JSClass js_StopIterationClass = {
|
||||
JSBool
|
||||
js_ThrowStopIteration(JSContext *cx, JSObject *obj)
|
||||
{
|
||||
JSObject *stop;
|
||||
jsval v;
|
||||
|
||||
JS_ASSERT(!JS_IsExceptionPending(cx));
|
||||
if (js_GetClassObject(cx, obj, JSProto_StopIteration, &stop))
|
||||
JS_SetPendingException(cx, OBJECT_TO_JSVAL(stop));
|
||||
if (js_FindClassObject(cx, obj, INT_TO_JSID(JSProto_StopIteration), &v))
|
||||
JS_SetPendingException(cx, v);
|
||||
return JS_FALSE;
|
||||
}
|
||||
|
||||
@ -634,7 +707,7 @@ static JSFunctionSpec generator_methods[] = {
|
||||
JSObject *
|
||||
js_InitIteratorClasses(JSContext *cx, JSObject *obj)
|
||||
{
|
||||
JSObject *stop, *proto;
|
||||
JSObject *proto, *stop;
|
||||
|
||||
/* Idempotency required: we initialize several things, possibly lazily. */
|
||||
if (!js_GetClassObject(cx, obj, JSProto_StopIteration, &stop))
|
||||
|
@ -81,7 +81,8 @@ js_ValueToIterator(JSContext *cx, jsval v, uintN flags);
|
||||
* pass its return value back unchanged.
|
||||
*/
|
||||
extern JSBool
|
||||
js_CallIteratorNext(JSContext *cx, JSObject *iterobj, jsid *idp, jsval *rval);
|
||||
js_CallIteratorNext(JSContext *cx, JSObject *iterobj, uintN flags,
|
||||
jsid *idp, jsval *rval);
|
||||
|
||||
extern JSClass js_StopIterationClass;
|
||||
|
||||
|
@ -114,3 +114,7 @@ JS_KEYWORD(debugger, TOK_DEBUGGER, JSOP_NOP, JSVERSION_DEFAULT)
|
||||
#elif JS_HAS_RESERVED_ECMA_KEYWORDS
|
||||
JS_KEYWORD(debugger, TOK_RESERVED, JSOP_NOP, JSVERSION_DEFAULT)
|
||||
#endif
|
||||
|
||||
#if JS_HAS_GENERATORS
|
||||
JS_KEYWORD(yield, TOK_YIELD, JSOP_NOP, JSVERSION_DEFAULT)
|
||||
#endif
|
||||
|
216
js/src/jsobj.c
216
js/src/jsobj.c
@ -69,6 +69,10 @@
|
||||
|
||||
#include "jsdbgapi.h" /* whether or not JS_HAS_OBJ_WATCHPOINT */
|
||||
|
||||
#if JS_HAS_GENERATORS
|
||||
#include "jsiter.h"
|
||||
#endif
|
||||
|
||||
#if JS_HAS_XML_SUPPORT
|
||||
#include "jsxml.h"
|
||||
#endif
|
||||
@ -1181,7 +1185,9 @@ obj_eval(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
|
||||
#if JS_HAS_EVAL_THIS_SCOPE
|
||||
/* If obj.eval(str), emulate 'with (obj) eval(str)' in the caller. */
|
||||
if (indirectCall) {
|
||||
callerScopeChain = caller->scopeChain;
|
||||
callerScopeChain = js_GetScopeChain(cx, caller);
|
||||
if (!callerScopeChain)
|
||||
return JS_FALSE;
|
||||
OBJ_TO_INNER_OBJECT(cx, obj);
|
||||
if (!obj)
|
||||
return JS_FALSE;
|
||||
@ -1193,8 +1199,7 @@ obj_eval(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
|
||||
return JS_FALSE;
|
||||
}
|
||||
|
||||
scopeobj = js_NewObject(cx, &js_WithClass, obj,
|
||||
callerScopeChain);
|
||||
scopeobj = js_NewWithObject(cx, obj, callerScopeChain, -1);
|
||||
if (!scopeobj)
|
||||
return JS_FALSE;
|
||||
|
||||
@ -1214,8 +1219,13 @@ obj_eval(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
|
||||
#endif
|
||||
|
||||
/* Compile using caller's current scope object. */
|
||||
if (caller)
|
||||
scopeobj = caller->scopeChain;
|
||||
if (caller) {
|
||||
scopeobj = js_GetScopeChain(cx, caller);
|
||||
if (!scopeobj) {
|
||||
ok = JS_FALSE;
|
||||
goto out;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* Ensure we compile this eval with the right object in the scope chain. */
|
||||
@ -1627,6 +1637,9 @@ static JSFunctionSpec object_methods[] = {
|
||||
{js_defineSetter_str, obj_defineSetter, 2,0,0},
|
||||
{js_lookupGetter_str, obj_lookupGetter, 1,0,0},
|
||||
{js_lookupSetter_str, obj_lookupSetter, 1,0,0},
|
||||
#endif
|
||||
#if JS_HAS_GENERATORS
|
||||
{js_iterator_str, js_DefaultIterator, 0,0,0},
|
||||
#endif
|
||||
{0,0,0,0,0}
|
||||
};
|
||||
@ -1775,13 +1788,105 @@ with_getObjectOps(JSContext *cx, JSClass *clasp)
|
||||
|
||||
JSClass js_WithClass = {
|
||||
"With",
|
||||
JSCLASS_HAS_PRIVATE,
|
||||
JSCLASS_HAS_PRIVATE | JSCLASS_HAS_RESERVED_SLOTS(1) | JSCLASS_IS_ANONYMOUS,
|
||||
JS_PropertyStub, JS_PropertyStub, JS_PropertyStub, JS_PropertyStub,
|
||||
JS_EnumerateStub, JS_ResolveStub, JS_ConvertStub, JS_FinalizeStub,
|
||||
with_getObjectOps,
|
||||
0,0,0,0,0,0,0
|
||||
};
|
||||
|
||||
JSObject *
|
||||
js_NewWithObject(JSContext *cx, JSObject *proto, JSObject *parent, jsint depth)
|
||||
{
|
||||
JSObject *obj;
|
||||
|
||||
obj = js_NewObject(cx, &js_WithClass, proto, parent);
|
||||
if (!obj)
|
||||
return NULL;
|
||||
OBJ_SET_BLOCK_DEPTH(cx, obj, depth);
|
||||
return obj;
|
||||
}
|
||||
|
||||
JSObject *
|
||||
js_NewBlockObject(JSContext *cx)
|
||||
{
|
||||
JSObject *obj;
|
||||
|
||||
/*
|
||||
* Null obj's proto slot so that Object.prototype.* does not pollute block
|
||||
* scopes. Make sure obj has its own scope too, since clearing proto does
|
||||
* not affect OBJ_SCOPE(obj).
|
||||
*/
|
||||
obj = js_NewObject(cx, &js_BlockClass, NULL, NULL);
|
||||
if (!obj || !js_GetMutableScope(cx, obj))
|
||||
return NULL;
|
||||
OBJ_SET_PROTO(cx, obj, NULL);
|
||||
return obj;
|
||||
}
|
||||
|
||||
JSObject *
|
||||
js_CloneBlockObject(JSContext *cx, JSObject *proto, JSObject *parent,
|
||||
JSStackFrame *fp)
|
||||
{
|
||||
JSObject *clone;
|
||||
|
||||
clone = js_NewObject(cx, &js_BlockClass, proto, parent);
|
||||
if (!clone)
|
||||
return NULL;
|
||||
clone->slots[JSSLOT_PRIVATE] = PRIVATE_TO_JSVAL(fp);
|
||||
clone->slots[JSSLOT_BLOCK_DEPTH] =
|
||||
OBJ_GET_SLOT(cx, proto, JSSLOT_BLOCK_DEPTH);
|
||||
return clone;
|
||||
}
|
||||
|
||||
static JSBool
|
||||
block_getProperty(JSContext *cx, JSObject *obj, jsval id, jsval *vp)
|
||||
{
|
||||
JSStackFrame *fp;
|
||||
jsint slot;
|
||||
|
||||
JS_ASSERT(JS_InstanceOf(cx, obj, &js_BlockClass, NULL));
|
||||
if (!JSVAL_IS_INT(id))
|
||||
return JS_TRUE;
|
||||
|
||||
fp = (JSStackFrame *) JS_GetPrivate(cx, obj);
|
||||
if (!fp)
|
||||
return JS_TRUE;
|
||||
|
||||
slot = OBJ_BLOCK_DEPTH(cx, obj) + (uint16) JSVAL_TO_INT(id);
|
||||
JS_ASSERT((uintN)slot < fp->script->depth);
|
||||
*vp = fp->spbase[slot];
|
||||
return JS_TRUE;
|
||||
}
|
||||
|
||||
static JSBool
|
||||
block_setProperty(JSContext *cx, JSObject *obj, jsval id, jsval *vp)
|
||||
{
|
||||
JSStackFrame *fp;
|
||||
jsint slot;
|
||||
|
||||
JS_ASSERT(JS_InstanceOf(cx, obj, &js_BlockClass, NULL));
|
||||
if (!JSVAL_IS_INT(id))
|
||||
return JS_TRUE;
|
||||
|
||||
fp = (JSStackFrame *) JS_GetPrivate(cx, obj);
|
||||
if (!fp)
|
||||
return JS_TRUE;
|
||||
|
||||
slot = OBJ_BLOCK_DEPTH(cx, obj) + (uint16) JSVAL_TO_INT(id);
|
||||
JS_ASSERT((uintN)slot < fp->script->depth);
|
||||
fp->spbase[slot] = *vp;
|
||||
return JS_TRUE;
|
||||
}
|
||||
|
||||
JSClass js_BlockClass = {
|
||||
"Block",
|
||||
JSCLASS_HAS_PRIVATE | JSCLASS_HAS_RESERVED_SLOTS(1) | JSCLASS_IS_ANONYMOUS,
|
||||
JS_PropertyStub, JS_PropertyStub, block_getProperty, block_setProperty,
|
||||
JS_EnumerateStub, JS_ResolveStub, JS_ConvertStub, JS_FinalizeStub,
|
||||
JSCLASS_NO_OPTIONAL_MEMBERS
|
||||
};
|
||||
|
||||
JSObject *
|
||||
js_InitObjectClass(JSContext *cx, JSObject *obj)
|
||||
{
|
||||
@ -1926,6 +2031,8 @@ js_GetClassId(JSContext *cx, JSClass *clasp, jsid *idp)
|
||||
key = JSCLASS_CACHED_PROTO_KEY(clasp);
|
||||
if (key != JSProto_Null) {
|
||||
*idp = INT_TO_JSID(key);
|
||||
} else if (clasp->flags & JSCLASS_IS_ANONYMOUS) {
|
||||
*idp = INT_TO_JSID(JSProto_Object);
|
||||
} else {
|
||||
atom = js_Atomize(cx, clasp->name, strlen(clasp->name), 0);
|
||||
if (!atom)
|
||||
@ -1980,7 +2087,7 @@ js_NewObject(JSContext *cx, JSClass *clasp, JSObject *proto, JSObject *parent)
|
||||
* GC calling JS_ClearNewbornRoots. There's also the possibilty of things
|
||||
* happening under the objectHook call-out further below.
|
||||
*/
|
||||
JS_PUSH_SINGLE_TEMP_ROOT(cx, OBJECT_TO_JSVAL(obj), &tvr);
|
||||
JS_PUSH_SINGLE_TEMP_ROOT(cx, obj, &tvr);
|
||||
|
||||
/*
|
||||
* Share proto's map only if it has the same JSObjectOps, and only if
|
||||
@ -2057,6 +2164,88 @@ bad:
|
||||
goto out;
|
||||
}
|
||||
|
||||
JS_STATIC_DLL_CALLBACK(JSObject *)
|
||||
js_InitNullClass(JSContext *cx, JSObject *obj)
|
||||
{
|
||||
JS_ASSERT(0);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
#define JS_PROTO(name,code,init) extern JSObject *init(JSContext *, JSObject *);
|
||||
#include "jsproto.tbl"
|
||||
#undef JS_PROTO
|
||||
|
||||
static JSObjectOp lazy_prototype_init[JSProto_LIMIT] = {
|
||||
#define JS_PROTO(name,code,init) init,
|
||||
#include "jsproto.tbl"
|
||||
#undef JS_PROTO
|
||||
};
|
||||
|
||||
JSBool
|
||||
js_GetClassObject(JSContext *cx, JSObject *obj, JSProtoKey key,
|
||||
JSObject **objp)
|
||||
{
|
||||
JSBool ok;
|
||||
JSObject *tmp, *cobj;
|
||||
JSResolvingKey rkey;
|
||||
JSResolvingEntry *rentry;
|
||||
uint32 generation;
|
||||
JSObjectOp init;
|
||||
jsval v;
|
||||
|
||||
while ((tmp = OBJ_GET_PARENT(cx, obj)) != NULL)
|
||||
obj = tmp;
|
||||
if (!(OBJ_GET_CLASS(cx, obj)->flags & JSCLASS_IS_GLOBAL)) {
|
||||
*objp = NULL;
|
||||
return JS_TRUE;
|
||||
}
|
||||
|
||||
ok = JS_GetReservedSlot(cx, obj, key, &v);
|
||||
if (!ok)
|
||||
return JS_FALSE;
|
||||
if (!JSVAL_IS_PRIMITIVE(v)) {
|
||||
*objp = JSVAL_TO_OBJECT(v);
|
||||
return JS_TRUE;
|
||||
}
|
||||
|
||||
rkey.obj = obj;
|
||||
rkey.id = ATOM_TO_JSID(cx->runtime->atomState.classAtoms[key]);
|
||||
if (!js_StartResolving(cx, &rkey, JSRESFLAG_LOOKUP, &rentry))
|
||||
return JS_FALSE;
|
||||
if (!rentry) {
|
||||
/* Already caching key in obj -- suppress recursion. */
|
||||
*objp = NULL;
|
||||
return JS_TRUE;
|
||||
}
|
||||
generation = cx->resolvingTable->generation;
|
||||
|
||||
cobj = NULL;
|
||||
init = lazy_prototype_init[key];
|
||||
if (init) {
|
||||
if (!init(cx, obj)) {
|
||||
ok = JS_FALSE;
|
||||
} else {
|
||||
ok = JS_GetReservedSlot(cx, obj, key, &v);
|
||||
if (ok && !JSVAL_IS_PRIMITIVE(v))
|
||||
cobj = JSVAL_TO_OBJECT(v);
|
||||
}
|
||||
}
|
||||
|
||||
js_StopResolving(cx, &rkey, JSRESFLAG_LOOKUP, rentry, generation);
|
||||
*objp = cobj;
|
||||
return ok;
|
||||
}
|
||||
|
||||
JSBool
|
||||
js_SetClassObject(JSContext *cx, JSObject *obj, JSProtoKey key, JSObject *cobj)
|
||||
{
|
||||
JS_ASSERT(!OBJ_GET_PARENT(cx, obj));
|
||||
if (!(OBJ_GET_CLASS(cx, obj)->flags & JSCLASS_IS_GLOBAL))
|
||||
return JS_TRUE;
|
||||
|
||||
return JS_SetReservedSlot(cx, obj, key, OBJECT_TO_JSVAL(cobj));
|
||||
}
|
||||
|
||||
JSBool
|
||||
js_FindClassObject(JSContext *cx, JSObject *start, jsid id, jsval *vp)
|
||||
{
|
||||
@ -2169,10 +2358,11 @@ js_ConstructObject(JSContext *cx, JSClass *clasp, JSObject *proto,
|
||||
obj = JSVAL_TO_OBJECT(rval);
|
||||
|
||||
/*
|
||||
* If the given class has both the JSCLASS_HAS_PRIVATE and the
|
||||
* JSCLASS_CONSTRUCT_PROTOTYPE flags, then the class should have its private
|
||||
* data set. If it doesn't, then it means the constructor was replaced, and
|
||||
* we should throw a typerr.
|
||||
* If the instance's class differs from what was requested, throw a type
|
||||
* error. If the given class has both the JSCLASS_HAS_PRIVATE and the
|
||||
* JSCLASS_CONSTRUCT_PROTOTYPE flags, and the instance does not have its
|
||||
* private data set at this point, then the constructor was replaced and
|
||||
* we should throw a type error.
|
||||
*/
|
||||
if (OBJ_GET_CLASS(cx, obj) != clasp ||
|
||||
(!(~clasp->flags & (JSCLASS_HAS_PRIVATE |
|
||||
@ -2471,7 +2661,6 @@ js_DefineNativeProperty(JSContext *cx, JSObject *obj, jsid id, jsval value,
|
||||
{
|
||||
JSClass *clasp;
|
||||
JSScope *scope;
|
||||
JSProperty *prop;
|
||||
JSScopeProperty *sprop;
|
||||
|
||||
/*
|
||||
@ -2488,6 +2677,7 @@ js_DefineNativeProperty(JSContext *cx, JSObject *obj, jsid id, jsval value,
|
||||
*/
|
||||
if (attrs & (JSPROP_GETTER | JSPROP_SETTER)) {
|
||||
JSObject *pobj;
|
||||
JSProperty *prop;
|
||||
|
||||
/*
|
||||
* If JS_THREADSAFE and id is found, js_LookupProperty returns with
|
||||
@ -3734,7 +3924,7 @@ js_Call(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
|
||||
return ok;
|
||||
}
|
||||
#endif
|
||||
ReportIsNotFunction(cx, &argv[-2], 0);
|
||||
ReportIsNotFunction(cx, &argv[-2], cx->fp->flags & JSFRAME_ITERATOR);
|
||||
return JS_FALSE;
|
||||
}
|
||||
return clasp->call(cx, obj, argc, argv, rval);
|
||||
|
@ -236,6 +236,51 @@ extern JS_FRIEND_DATA(JSObjectOps) js_ObjectOps;
|
||||
extern JS_FRIEND_DATA(JSObjectOps) js_WithObjectOps;
|
||||
extern JSClass js_ObjectClass;
|
||||
extern JSClass js_WithClass;
|
||||
extern JSClass js_BlockClass;
|
||||
|
||||
/*
|
||||
* Block scope object macros. The slots reserved by js_BlockClass are:
|
||||
*
|
||||
* JSSLOT_PRIVATE JSStackFrame * active frame pointer or null
|
||||
* JSSLOT_BLOCK_DEPTH int depth of block slots in frame
|
||||
*
|
||||
* After JSSLOT_BLOCK_DEPTH come one or more slots for the block locals.
|
||||
* OBJ_BLOCK_COUNT depends on this arrangement.
|
||||
*
|
||||
* A With object is like a Block object, in that both have one reserved slot
|
||||
* telling the stack depth of the relevant slots (the slot whose value is the
|
||||
* object named in the with statement; the slots containing the block's local
|
||||
* variables).
|
||||
*/
|
||||
#define JSSLOT_BLOCK_DEPTH (JSSLOT_PRIVATE + 1)
|
||||
|
||||
#define OBJ_BLOCK_COUNT(cx,obj) \
|
||||
((obj)->map->freeslot - (JSSLOT_BLOCK_DEPTH + 1))
|
||||
#define OBJ_BLOCK_DEPTH(cx,obj) \
|
||||
JSVAL_TO_INT(OBJ_GET_SLOT(cx, obj, JSSLOT_BLOCK_DEPTH))
|
||||
#define OBJ_SET_BLOCK_DEPTH(cx,obj,depth) \
|
||||
OBJ_SET_SLOT(cx, obj, JSSLOT_BLOCK_DEPTH, INT_TO_JSVAL(depth))
|
||||
|
||||
/*
|
||||
* To make sure this slot is well-defined, always call js_NewWithObject to
|
||||
* create a With object, don't call js_NewObject directly. When creating a
|
||||
* With object that does not correspond to a stack slot, pass -1 for depth.
|
||||
*/
|
||||
extern JSObject *
|
||||
js_NewWithObject(JSContext *cx, JSObject *proto, JSObject *parent, jsint depth);
|
||||
|
||||
/*
|
||||
* Create a new block scope object not linked to any proto or parent object.
|
||||
* Blocks are created by the compiler to reify let blocks and comprehensions.
|
||||
* Only when dynamic scope is captured do they need to be cloned and spliced
|
||||
* into an active scope chain.
|
||||
*/
|
||||
extern JSObject *
|
||||
js_NewBlockObject(JSContext *cx);
|
||||
|
||||
extern JSObject *
|
||||
js_CloneBlockObject(JSContext *cx, JSObject *proto, JSObject *parent,
|
||||
JSStackFrame *fp);
|
||||
|
||||
struct JSSharpObjectMap {
|
||||
jsrefcount depth;
|
||||
@ -308,6 +353,16 @@ js_GetClassId(JSContext *cx, JSClass *clasp, jsid *idp);
|
||||
extern JSObject *
|
||||
js_NewObject(JSContext *cx, JSClass *clasp, JSObject *proto, JSObject *parent);
|
||||
|
||||
/*
|
||||
* Fast access to immutable standard objects (constructors and prototypes).
|
||||
*/
|
||||
extern JSBool
|
||||
js_GetClassObject(JSContext *cx, JSObject *obj, JSProtoKey key,
|
||||
JSObject **objp);
|
||||
|
||||
extern JSBool
|
||||
js_SetClassObject(JSContext *cx, JSObject *obj, JSProtoKey key, JSObject *cobj);
|
||||
|
||||
extern JSBool
|
||||
js_FindClassObject(JSContext *cx, JSObject *start, jsid id, jsval *vp);
|
||||
|
||||
|
@ -128,6 +128,41 @@ js_Disassemble(JSContext *cx, JSScript *script, JSBool lines, FILE *fp)
|
||||
return JS_TRUE;
|
||||
}
|
||||
|
||||
const char *
|
||||
ToDisassemblySource(JSContext *cx, jsval v)
|
||||
{
|
||||
JSObject *obj;
|
||||
JSScopeProperty *sprop;
|
||||
char *source;
|
||||
const char *bytes;
|
||||
JSString *str;
|
||||
|
||||
if (!JSVAL_IS_PRIMITIVE(v)) {
|
||||
obj = JSVAL_TO_OBJECT(v);
|
||||
if (OBJ_GET_CLASS(cx, obj) == &js_BlockClass) {
|
||||
source = JS_sprintf_append(NULL, "depth %d {",
|
||||
OBJ_BLOCK_DEPTH(cx, obj));
|
||||
for (sprop = OBJ_SCOPE(obj)->lastProp; sprop;
|
||||
sprop = sprop->parent) {
|
||||
bytes = js_AtomToPrintableString(cx, JSID_TO_ATOM(sprop->id));
|
||||
if (!bytes)
|
||||
return NULL;
|
||||
source = JS_sprintf_append(source, "%s: %d%s",
|
||||
bytes, sprop->shortid,
|
||||
sprop->parent ? ", " : "");
|
||||
}
|
||||
source = JS_sprintf_append(source, "}");
|
||||
if (!source)
|
||||
return NULL;
|
||||
str = JS_NewString(cx, source, strlen(source));
|
||||
if (!str)
|
||||
return NULL;
|
||||
return JS_GetStringBytes(str);
|
||||
}
|
||||
}
|
||||
return js_ValueToPrintableSource(cx, v);
|
||||
}
|
||||
|
||||
JS_FRIEND_API(uintN)
|
||||
js_Disassemble1(JSContext *cx, JSScript *script, jsbytecode *pc, uintN loc,
|
||||
JSBool lines, FILE *fp)
|
||||
@ -137,7 +172,7 @@ js_Disassemble1(JSContext *cx, JSScript *script, jsbytecode *pc, uintN loc,
|
||||
ptrdiff_t len, off, jmplen;
|
||||
uint32 type;
|
||||
JSAtom *atom;
|
||||
JSString *str;
|
||||
const char *bytes;
|
||||
|
||||
op = (JSOp)*pc;
|
||||
if (op >= JSOP_LIMIT) {
|
||||
@ -173,14 +208,17 @@ js_Disassemble1(JSContext *cx, JSScript *script, jsbytecode *pc, uintN loc,
|
||||
|
||||
case JOF_CONST:
|
||||
atom = GET_ATOM(cx, script, pc);
|
||||
str = js_ValueToSource(cx, ATOM_KEY(atom));
|
||||
if (!str)
|
||||
bytes = ToDisassemblySource(cx, ATOM_KEY(atom));
|
||||
if (!bytes)
|
||||
return 0;
|
||||
fprintf(fp, " %s", JS_GetStringBytes(str));
|
||||
fprintf(fp, " %s", bytes);
|
||||
break;
|
||||
|
||||
case JOF_UINT16:
|
||||
fprintf(fp, " %u", GET_ARGC(pc));
|
||||
#if JS_HAS_BLOCK_SCOPE
|
||||
case JOF_LOCAL:
|
||||
#endif
|
||||
fprintf(fp, " %u", GET_UINT16(pc));
|
||||
break;
|
||||
|
||||
case JOF_TABLESWITCH:
|
||||
@ -228,10 +266,10 @@ js_Disassemble1(JSContext *cx, JSScript *script, jsbytecode *pc, uintN loc,
|
||||
off = GetJumpOffset(pc, pc2);
|
||||
pc2 += jmplen;
|
||||
|
||||
str = js_ValueToSource(cx, ATOM_KEY(atom));
|
||||
if (!str)
|
||||
bytes = ToDisassemblySource(cx, ATOM_KEY(atom));
|
||||
if (!bytes)
|
||||
return 0;
|
||||
fprintf(fp, "\n\t%s: %d", JS_GetStringBytes(str), off);
|
||||
fprintf(fp, "\n\t%s: %d", bytes, off);
|
||||
npairs--;
|
||||
}
|
||||
len = 1 + pc2 - pc;
|
||||
@ -250,20 +288,20 @@ js_Disassemble1(JSContext *cx, JSScript *script, jsbytecode *pc, uintN loc,
|
||||
fprintf(fp, " %u", GET_VARNO(pc));
|
||||
pc += VARNO_LEN;
|
||||
atom = GET_ATOM(cx, script, pc);
|
||||
str = js_ValueToSource(cx, ATOM_KEY(atom));
|
||||
if (!str)
|
||||
bytes = ToDisassemblySource(cx, ATOM_KEY(atom));
|
||||
if (!bytes)
|
||||
return 0;
|
||||
fprintf(fp, " %s", JS_GetStringBytes(str));
|
||||
fprintf(fp, " %s", bytes);
|
||||
break;
|
||||
|
||||
case JOF_UINT24:
|
||||
if (op == JSOP_FINDNAME) {
|
||||
/* Special case to avoid a JOF_FINDNAME just for this op. */
|
||||
atom = js_GetAtom(cx, &script->atomMap, GET_LITERAL_INDEX(pc));
|
||||
str = js_ValueToSource(cx, ATOM_KEY(atom));
|
||||
if (!str)
|
||||
bytes = ToDisassemblySource(cx, ATOM_KEY(atom));
|
||||
if (!bytes)
|
||||
return 0;
|
||||
fprintf(fp, " %s", JS_GetStringBytes(str));
|
||||
fprintf(fp, " %s", bytes);
|
||||
break;
|
||||
}
|
||||
|
||||
@ -273,8 +311,8 @@ js_Disassemble1(JSContext *cx, JSScript *script, jsbytecode *pc, uintN loc,
|
||||
|
||||
case JOF_LITOPX:
|
||||
atom = js_GetAtom(cx, &script->atomMap, GET_LITERAL_INDEX(pc));
|
||||
str = js_ValueToSource(cx, ATOM_KEY(atom));
|
||||
if (!str)
|
||||
bytes = ToDisassemblySource(cx, ATOM_KEY(atom));
|
||||
if (!bytes)
|
||||
return 0;
|
||||
|
||||
/*
|
||||
@ -284,7 +322,7 @@ js_Disassemble1(JSContext *cx, JSScript *script, jsbytecode *pc, uintN loc,
|
||||
pc += 1 + LITERAL_INDEX_LEN;
|
||||
op = *pc;
|
||||
cs = &js_CodeSpec[op];
|
||||
fprintf(fp, " %s op %s", JS_GetStringBytes(str), cs->name);
|
||||
fprintf(fp, " %s op %s", bytes, cs->name);
|
||||
if ((cs->format & JOF_TYPEMASK) == JOF_INDEXCONST)
|
||||
fprintf(fp, " %u", GET_VARNO(pc));
|
||||
|
||||
@ -367,6 +405,12 @@ SprintPut(Sprinter *sp, const char *s, size_t len)
|
||||
return offset;
|
||||
}
|
||||
|
||||
static ptrdiff_t
|
||||
SprintCString(Sprinter *sp, const char *s)
|
||||
{
|
||||
return SprintPut(sp, s, strlen(s));
|
||||
}
|
||||
|
||||
static ptrdiff_t
|
||||
Sprint(Sprinter *sp, const char *format, ...)
|
||||
{
|
||||
@ -381,7 +425,7 @@ Sprint(Sprinter *sp, const char *format, ...)
|
||||
JS_ReportOutOfMemory(sp->context);
|
||||
return -1;
|
||||
}
|
||||
offset = SprintPut(sp, bp, strlen(bp));
|
||||
offset = SprintCString(sp, bp);
|
||||
free(bp);
|
||||
return offset;
|
||||
}
|
||||
@ -613,7 +657,7 @@ js_printf(JSPrinter *jp, const char *format, ...)
|
||||
JSBool
|
||||
js_puts(JSPrinter *jp, const char *s)
|
||||
{
|
||||
return SprintPut(&jp->sprinter, s, strlen(s)) >= 0;
|
||||
return SprintCString(&jp->sprinter, s) >= 0;
|
||||
}
|
||||
|
||||
/************************************************************************/
|
||||
@ -623,6 +667,7 @@ typedef struct SprintStack {
|
||||
ptrdiff_t *offsets; /* stack of postfix string offsets */
|
||||
jsbytecode *opcodes; /* parallel stack of JS opcodes */
|
||||
uintN top; /* top of stack index */
|
||||
uintN inArrayInit; /* array initialiser/comprehension level */
|
||||
JSPrinter *printer; /* permanent output goes here */
|
||||
} SprintStack;
|
||||
|
||||
@ -870,7 +915,7 @@ Decompile(SprintStack *ss, jsbytecode *pc, intN nb)
|
||||
{
|
||||
JSContext *cx;
|
||||
JSPrinter *jp, *jp2;
|
||||
jsbytecode *endpc, *done, *forelem_tail, *forelem_done;
|
||||
jsbytecode *endpc, *pc2, *done, *forelem_tail, *forelem_done;
|
||||
ptrdiff_t tail, todo, len, oplen, cond, next;
|
||||
JSOp op, lastop, saveop;
|
||||
const JSCodeSpec *cs, *topcs;
|
||||
@ -892,6 +937,8 @@ Decompile(SprintStack *ss, jsbytecode *pc, intN nb)
|
||||
jsval val;
|
||||
|
||||
static const char catch_cookie[] = "/*CATCH*/";
|
||||
static const char finally_cookie[] = "/*FINALLY*/";
|
||||
static const char iter_cookie[] = "/*ITER*/";
|
||||
static const char with_cookie[] = "/*WITH*/";
|
||||
static const char dot_format[] = "%s.%s";
|
||||
static const char index_format[] = "%s[%s]";
|
||||
@ -906,7 +953,10 @@ Decompile(SprintStack *ss, jsbytecode *pc, intN nb)
|
||||
*/
|
||||
#define DECOMPILE_CODE(pc,nb) if (!Decompile(ss, pc, nb)) return JS_FALSE
|
||||
#define POP_STR() OFF2STR(&ss->sprinter, PopOff(ss, op))
|
||||
#define LOCAL_ASSERT(expr) JS_ASSERT(expr); if (!(expr)) return JS_FALSE
|
||||
#define LOCAL_ASSERT(expr) JS_BEGIN_MACRO \
|
||||
JS_ASSERT(expr); \
|
||||
if (!(expr)) return JS_FALSE; \
|
||||
JS_END_MACRO
|
||||
|
||||
/*
|
||||
* Callers know that ATOM_IS_STRING(atom), and we leave it to the optimizer to
|
||||
@ -982,7 +1032,7 @@ Decompile(SprintStack *ss, jsbytecode *pc, intN nb)
|
||||
sn = js_GetSrcNote(jp->script, pc);
|
||||
if (sn && SN_TYPE(sn) == SRC_ASSIGNOP) {
|
||||
/* Print only the right operand of the assignment-op. */
|
||||
todo = SprintPut(&ss->sprinter, rval, strlen(rval));
|
||||
todo = SprintCString(&ss->sprinter, rval);
|
||||
} else if (!inXML) {
|
||||
todo = Sprint(&ss->sprinter, "%s %s %s",
|
||||
lval, cs->token, rval);
|
||||
@ -999,7 +1049,7 @@ Decompile(SprintStack *ss, jsbytecode *pc, intN nb)
|
||||
break;
|
||||
|
||||
case 0:
|
||||
todo = SprintPut(&ss->sprinter, cs->token, strlen(cs->token));
|
||||
todo = SprintCString(&ss->sprinter, cs->token);
|
||||
break;
|
||||
|
||||
default:
|
||||
@ -1197,6 +1247,18 @@ Decompile(SprintStack *ss, jsbytecode *pc, intN nb)
|
||||
}
|
||||
break;
|
||||
|
||||
case JSOP_STARTITER:
|
||||
if (ss->inArrayInit) {
|
||||
ss->offsets[ss->top++] = ss->sprinter.offset;
|
||||
ss->offsets[ss->top++] = ss->sprinter.offset;
|
||||
ss->opcodes[ss->top-1] = ss->opcodes[ss->top-2] = op;
|
||||
break;
|
||||
}
|
||||
todo = Sprint(&ss->sprinter, iter_cookie);
|
||||
if (todo < 0 || !PushOff(ss, todo, op))
|
||||
return JS_FALSE;
|
||||
/* FALL THROUGH */
|
||||
|
||||
case JSOP_PUSH:
|
||||
case JSOP_PUSHOBJ:
|
||||
case JSOP_BINDNAME:
|
||||
@ -1210,9 +1272,6 @@ Decompile(SprintStack *ss, jsbytecode *pc, intN nb)
|
||||
todo = -2;
|
||||
break;
|
||||
|
||||
{
|
||||
static const char finally_cookie[] = "/*FINALLY*/";
|
||||
|
||||
case JSOP_FINALLY:
|
||||
jp->indent -= 4;
|
||||
js_printf(jp, "\t} finally {\n");
|
||||
@ -1231,7 +1290,6 @@ Decompile(SprintStack *ss, jsbytecode *pc, intN nb)
|
||||
LOCAL_ASSERT(strcmp(rval, finally_cookie) == 0);
|
||||
todo = -2;
|
||||
break;
|
||||
}
|
||||
|
||||
case JSOP_SWAP:
|
||||
/*
|
||||
@ -1323,9 +1381,21 @@ Decompile(SprintStack *ss, jsbytecode *pc, intN nb)
|
||||
break;
|
||||
|
||||
case JSOP_POP2:
|
||||
(void) PopOff(ss, op);
|
||||
(void) PopOff(ss, op);
|
||||
case JSOP_ENDITER:
|
||||
sn = js_GetSrcNote(jp->script, pc);
|
||||
todo = -2;
|
||||
if (sn && SN_TYPE(sn) == SRC_HIDDEN)
|
||||
break;
|
||||
(void) PopOff(ss, op);
|
||||
if (ss->inArrayInit) {
|
||||
ss->top -= 2;
|
||||
break;
|
||||
}
|
||||
(void) PopOff(ss, op);
|
||||
if (op == JSOP_ENDITER) {
|
||||
rval = POP_STR();
|
||||
JS_ASSERT(!strcmp(rval, iter_cookie));
|
||||
}
|
||||
break;
|
||||
|
||||
case JSOP_ENTERWITH:
|
||||
@ -1352,9 +1422,102 @@ Decompile(SprintStack *ss, jsbytecode *pc, intN nb)
|
||||
js_printf(jp, "\t}\n");
|
||||
break;
|
||||
|
||||
#if JS_HAS_BLOCK_SCOPE
|
||||
case JSOP_ENTERBLOCK:
|
||||
{
|
||||
JSAtom **atomv, *smallv[5];
|
||||
JSScopeProperty *sprop;
|
||||
|
||||
atom = GET_ATOM(cx, jp->script, pc);
|
||||
obj = ATOM_TO_OBJECT(atom);
|
||||
argc = OBJ_BLOCK_COUNT(cx, obj);
|
||||
if ((size_t)argc <= sizeof smallv / sizeof smallv[0]) {
|
||||
atomv = smallv;
|
||||
} else {
|
||||
atomv = (JSAtom **) JS_malloc(cx, argc * sizeof(JSAtom *));
|
||||
if (!atomv)
|
||||
return JS_FALSE;
|
||||
}
|
||||
|
||||
for (i = argc, sprop = OBJ_SCOPE(obj)->lastProp; --i >= 0;
|
||||
sprop = sprop->parent) {
|
||||
atomv[i] = JSID_TO_ATOM(sprop->id);
|
||||
}
|
||||
ok = JS_TRUE;
|
||||
for (i = 0; i < argc; i++) {
|
||||
atom = atomv[i];
|
||||
rval = QuoteString(&ss->sprinter, ATOM_TO_STRING(atom), 0);
|
||||
if (!rval ||
|
||||
!PushOff(ss, STR2OFF(&ss->sprinter, rval), op)) {
|
||||
ok = JS_FALSE;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (atomv != smallv)
|
||||
JS_free(cx, atomv);
|
||||
if (!ok)
|
||||
return JS_FALSE;
|
||||
todo = -2;
|
||||
break;
|
||||
}
|
||||
|
||||
case JSOP_LEAVEBLOCK:
|
||||
{
|
||||
uintN top, depth;
|
||||
|
||||
sn = js_GetSrcNote(jp->script, pc);
|
||||
todo = -2;
|
||||
if (sn && SN_TYPE(sn) == SRC_HIDDEN)
|
||||
break;
|
||||
top = ss->top;
|
||||
depth = GET_UINT16(pc);
|
||||
JS_ASSERT(top >= depth);
|
||||
top -= depth;
|
||||
ss->top = top;
|
||||
ss->sprinter.offset = ss->offsets[top];
|
||||
break;
|
||||
}
|
||||
|
||||
case JSOP_GETLOCAL:
|
||||
i = GET_UINT16(pc);
|
||||
rval = OFF2STR(&ss->sprinter, ss->offsets[i]);
|
||||
todo = SprintCString(&ss->sprinter, rval);
|
||||
break;
|
||||
|
||||
case JSOP_SETLOCAL:
|
||||
i = GET_UINT16(pc);
|
||||
lval = OFF2STR(&ss->sprinter, ss->offsets[i]);
|
||||
rval = POP_STR();
|
||||
goto do_setlval;
|
||||
|
||||
case JSOP_INCLOCAL:
|
||||
case JSOP_DECLOCAL:
|
||||
i = GET_UINT16(pc);
|
||||
lval = OFF2STR(&ss->sprinter, ss->offsets[i]);
|
||||
goto do_inclval;
|
||||
|
||||
case JSOP_LOCALINC:
|
||||
case JSOP_LOCALDEC:
|
||||
i = GET_UINT16(pc);
|
||||
lval = OFF2STR(&ss->sprinter, ss->offsets[i]);
|
||||
goto do_lvalinc;
|
||||
|
||||
case JSOP_FORLOCAL:
|
||||
i = GET_UINT16(pc);
|
||||
lval = OFF2STR(&ss->sprinter, ss->offsets[i]);
|
||||
atom = NULL;
|
||||
goto do_forlvalinloop;
|
||||
#endif
|
||||
|
||||
case JSOP_SETRVAL:
|
||||
op = JSOP_RETURN;
|
||||
/* FALL THROUGH */
|
||||
case JSOP_RETURN:
|
||||
lval = js_CodeSpec[JSOP_RETURN].name;
|
||||
#if JS_HAS_GENERATORS
|
||||
case JSOP_YIELD:
|
||||
#endif
|
||||
lval = js_CodeSpec[op].name;
|
||||
rval = POP_STR();
|
||||
if (*rval != '\0')
|
||||
js_printf(jp, "\t%s %s;\n", lval, rval);
|
||||
@ -1363,6 +1526,43 @@ Decompile(SprintStack *ss, jsbytecode *pc, intN nb)
|
||||
todo = -2;
|
||||
break;
|
||||
|
||||
#if JS_HAS_GENERATORS
|
||||
case JSOP_ARRAYPUSH:
|
||||
{
|
||||
uintN pos, blockpos, startpos;
|
||||
ptrdiff_t start;
|
||||
|
||||
rval = POP_STR();
|
||||
pos = ss->top;
|
||||
while (ss->opcodes[--pos] != JSOP_ENTERBLOCK)
|
||||
LOCAL_ASSERT(pos != 0);
|
||||
blockpos = pos;
|
||||
while (ss->opcodes[--pos] == JSOP_ENTERBLOCK) {
|
||||
if (pos == 0)
|
||||
break;
|
||||
}
|
||||
LOCAL_ASSERT(ss->opcodes[pos] == JSOP_NEWINIT);
|
||||
startpos = pos;
|
||||
start = ss->offsets[pos];
|
||||
LOCAL_ASSERT(ss->sprinter.base[start] == '[' ||
|
||||
ss->sprinter.base[start] == '#');
|
||||
pos = blockpos;
|
||||
while (ss->opcodes[++pos] == JSOP_STARTITER)
|
||||
LOCAL_ASSERT(pos < ss->top);
|
||||
LOCAL_ASSERT(pos < ss->top);
|
||||
xval = OFF2STR(&ss->sprinter, ss->offsets[pos]);
|
||||
lval = OFF2STR(&ss->sprinter, start);
|
||||
RETRACT(&ss->sprinter, xval);
|
||||
todo = Sprint(&ss->sprinter, "%s%s%.*s",
|
||||
lval, rval, rval - xval, xval);
|
||||
if (todo < 0)
|
||||
return JS_FALSE;
|
||||
ss->offsets[startpos] = todo;
|
||||
todo = -2;
|
||||
break;
|
||||
}
|
||||
#endif
|
||||
|
||||
case JSOP_THROW:
|
||||
sn = js_GetSrcNote(jp->script, pc);
|
||||
todo = -2;
|
||||
@ -1415,11 +1615,19 @@ Decompile(SprintStack *ss, jsbytecode *pc, intN nb)
|
||||
case SRC_IF:
|
||||
case SRC_IF_ELSE:
|
||||
rval = POP_STR();
|
||||
js_printf(jp, "\tif (%s) {\n", rval);
|
||||
jp->indent += 4;
|
||||
if (ss->inArrayInit) {
|
||||
LOCAL_ASSERT(SN_TYPE(sn) == SRC_IF);
|
||||
if (Sprint(&ss->sprinter, " if (%s)", rval) < 0)
|
||||
return JS_FALSE;
|
||||
} else {
|
||||
js_printf(jp, "\tif (%s) {\n", rval);
|
||||
jp->indent += 4;
|
||||
}
|
||||
|
||||
if (SN_TYPE(sn) == SRC_IF) {
|
||||
DECOMPILE_CODE(pc + oplen, len - oplen);
|
||||
} else {
|
||||
LOCAL_ASSERT(!ss->inArrayInit);
|
||||
len = js_GetSrcNoteOffset(sn, 0);
|
||||
DECOMPILE_CODE(pc + oplen, len - oplen);
|
||||
jp->indent -= 4;
|
||||
@ -1431,8 +1639,11 @@ Decompile(SprintStack *ss, jsbytecode *pc, intN nb)
|
||||
jp->indent += 4;
|
||||
DECOMPILE_CODE(pc + oplen, len - oplen);
|
||||
}
|
||||
jp->indent -= 4;
|
||||
js_printf(jp, "\t}\n");
|
||||
|
||||
if (!ss->inArrayInit) {
|
||||
jp->indent -= 4;
|
||||
js_printf(jp, "\t}\n");
|
||||
}
|
||||
todo = -2;
|
||||
break;
|
||||
|
||||
@ -1535,9 +1746,10 @@ Decompile(SprintStack *ss, jsbytecode *pc, intN nb)
|
||||
atom = GET_ATOM(cx, jp->script, pc);
|
||||
|
||||
do_fornameinloop:
|
||||
lval = "";
|
||||
do_forlvalinloop:
|
||||
sn = js_GetSrcNote(jp->script, pc);
|
||||
xval = NULL;
|
||||
lval = "";
|
||||
goto do_forinloop;
|
||||
|
||||
case JSOP_FORPROP:
|
||||
@ -1561,34 +1773,47 @@ Decompile(SprintStack *ss, jsbytecode *pc, intN nb)
|
||||
sn2 = js_GetSrcNote(jp->script, pc);
|
||||
tail = js_GetSrcNoteOffset(sn2, 0);
|
||||
|
||||
do_forinbody:
|
||||
do_forinhead:
|
||||
#if JS_HAS_XML_SUPPORT
|
||||
if (foreach) {
|
||||
foreach = JS_FALSE;
|
||||
js_printf(jp, "\tfor %s (%s%s",
|
||||
js_each_str, VarPrefix(sn), lval);
|
||||
todo = Sprint(&ss->sprinter, "for %s (%s%s",
|
||||
js_each_str, VarPrefix(sn), lval);
|
||||
} else
|
||||
#endif
|
||||
js_printf(jp, "\tfor (%s%s", VarPrefix(sn), lval);
|
||||
{
|
||||
todo = Sprint(&ss->sprinter, "for (%s%s",
|
||||
VarPrefix(sn), lval);
|
||||
}
|
||||
if (atom) {
|
||||
if (*lval && SprintPut(&ss->sprinter, ".", 1) < 0)
|
||||
return JS_FALSE;
|
||||
xval = QuoteString(&ss->sprinter, ATOM_TO_STRING(atom), 0);
|
||||
if (!xval)
|
||||
return JS_FALSE;
|
||||
RETRACT(&ss->sprinter, xval);
|
||||
js_printf(jp, *lval ? ".%s" : "%s", xval);
|
||||
} else if (xval && *xval) {
|
||||
js_printf(jp,
|
||||
(js_CodeSpec[lastop].format & JOF_XMLNAME)
|
||||
? ".%s"
|
||||
: "[%s]",
|
||||
xval);
|
||||
Sprint(&ss->sprinter,
|
||||
(js_CodeSpec[lastop].format & JOF_XMLNAME)
|
||||
? ".%s"
|
||||
: "[%s]",
|
||||
xval);
|
||||
}
|
||||
lval = OFF2STR(&ss->sprinter, todo);
|
||||
rval = OFF2STR(&ss->sprinter, ss->offsets[ss->top-1]);
|
||||
js_printf(jp, " in %s) {\n", rval);
|
||||
jp->indent += 4;
|
||||
DECOMPILE_CODE(pc + oplen, tail - oplen);
|
||||
jp->indent -= 4;
|
||||
js_printf(jp, "\t}\n");
|
||||
RETRACT(&ss->sprinter, rval);
|
||||
if (ss->inArrayInit) {
|
||||
todo = Sprint(&ss->sprinter, " %s in %s)", lval, rval);
|
||||
if (todo < 0)
|
||||
return JS_FALSE;
|
||||
ss->offsets[ss->top-1] = todo;
|
||||
DECOMPILE_CODE(pc + oplen, tail - oplen);
|
||||
} else {
|
||||
js_printf(jp, "\t%s in %s) {\n", lval, rval);
|
||||
jp->indent += 4;
|
||||
DECOMPILE_CODE(pc + oplen, tail - oplen);
|
||||
jp->indent -= 4;
|
||||
js_printf(jp, "\t}\n");
|
||||
}
|
||||
todo = -2;
|
||||
break;
|
||||
|
||||
@ -1599,7 +1824,7 @@ Decompile(SprintStack *ss, jsbytecode *pc, intN nb)
|
||||
|
||||
/*
|
||||
* Arrange for the JSOP_ENUMELEM case to set tail for use by
|
||||
* do_forinbody: code that uses on it to find the loop-closing
|
||||
* do_forinhead: code that uses on it to find the loop-closing
|
||||
* jump (whatever its format, normal or extended), in order to
|
||||
* bound the recursively decompiled loop body.
|
||||
*/
|
||||
@ -1617,7 +1842,7 @@ Decompile(SprintStack *ss, jsbytecode *pc, intN nb)
|
||||
* Since a for..in loop can't nest in the head of another for
|
||||
* loop, we can use forelem_{tail,done} singletons to remember
|
||||
* state from JSOP_FORELEM to JSOP_ENUMELEM, thence (via goto)
|
||||
* to label do_forinbody.
|
||||
* to label do_forinhead.
|
||||
*/
|
||||
JS_ASSERT(!forelem_done);
|
||||
forelem_done = pc + GetJumpOffset(pc, pc);
|
||||
@ -1641,7 +1866,7 @@ Decompile(SprintStack *ss, jsbytecode *pc, intN nb)
|
||||
JS_ASSERT(forelem_done > pc);
|
||||
len = forelem_done - pc;
|
||||
forelem_done = NULL;
|
||||
goto do_forinbody;
|
||||
goto do_forinhead;
|
||||
|
||||
#if JS_HAS_GETTER_SETTER
|
||||
case JSOP_GETTER:
|
||||
@ -1652,7 +1877,7 @@ Decompile(SprintStack *ss, jsbytecode *pc, intN nb)
|
||||
|
||||
case JSOP_DUP2:
|
||||
rval = OFF2STR(&ss->sprinter, ss->offsets[ss->top-2]);
|
||||
todo = SprintPut(&ss->sprinter, rval, strlen(rval));
|
||||
todo = SprintCString(&ss->sprinter, rval);
|
||||
if (todo < 0 || !PushOff(ss, todo, ss->opcodes[ss->top-2]))
|
||||
return JS_FALSE;
|
||||
/* FALL THROUGH */
|
||||
@ -1660,7 +1885,7 @@ Decompile(SprintStack *ss, jsbytecode *pc, intN nb)
|
||||
case JSOP_DUP:
|
||||
rval = OFF2STR(&ss->sprinter, ss->offsets[ss->top-1]);
|
||||
op = ss->opcodes[ss->top-1];
|
||||
todo = SprintPut(&ss->sprinter, rval, strlen(rval));
|
||||
todo = SprintCString(&ss->sprinter, rval);
|
||||
break;
|
||||
|
||||
case JSOP_SETARG:
|
||||
@ -1835,6 +2060,7 @@ Decompile(SprintStack *ss, jsbytecode *pc, intN nb)
|
||||
if (!lval)
|
||||
return JS_FALSE;
|
||||
RETRACT(&ss->sprinter, lval);
|
||||
do_inclval:
|
||||
todo = Sprint(&ss->sprinter, ss_format,
|
||||
js_incop_strs[!(cs->format & JOF_INC)], lval);
|
||||
break;
|
||||
@ -1893,6 +2119,7 @@ Decompile(SprintStack *ss, jsbytecode *pc, intN nb)
|
||||
lval = QuoteString(&ss->sprinter, ATOM_TO_STRING(atom), 0);
|
||||
if (!lval)
|
||||
return JS_FALSE;
|
||||
do_lvalinc:
|
||||
todo = STR2OFF(&ss->sprinter, lval);
|
||||
SprintPut(&ss->sprinter,
|
||||
js_incop_strs[!(cs->format & JOF_INC)],
|
||||
@ -2089,7 +2316,11 @@ Decompile(SprintStack *ss, jsbytecode *pc, intN nb)
|
||||
|
||||
case JSOP_LITOPX:
|
||||
atomIndex = GET_LITERAL_INDEX(pc);
|
||||
op = pc[1 + LITERAL_INDEX_LEN];
|
||||
pc2 = pc + 1 + LITERAL_INDEX_LEN;
|
||||
op = *pc2;
|
||||
pc += len - (1 + ATOM_INDEX_LEN);
|
||||
cs = &js_CodeSpec[op];
|
||||
len = cs->length;
|
||||
switch (op) {
|
||||
case JSOP_ANONFUNOBJ: goto do_JSOP_ANONFUNOBJ;
|
||||
case JSOP_BINDNAME: goto do_JSOP_BINDNAME;
|
||||
@ -2185,7 +2416,6 @@ Decompile(SprintStack *ss, jsbytecode *pc, intN nb)
|
||||
case JSOP_TABLESWITCH:
|
||||
case JSOP_TABLESWITCHX:
|
||||
{
|
||||
jsbytecode *pc2;
|
||||
ptrdiff_t jmplen, off, off2;
|
||||
jsint j, n, low, high;
|
||||
TableEntry *table, pivot;
|
||||
@ -2247,7 +2477,6 @@ Decompile(SprintStack *ss, jsbytecode *pc, intN nb)
|
||||
case JSOP_LOOKUPSWITCH:
|
||||
case JSOP_LOOKUPSWITCHX:
|
||||
{
|
||||
jsbytecode *pc2;
|
||||
ptrdiff_t jmplen, off, off2;
|
||||
jsatomid npairs, k;
|
||||
TableEntry *table;
|
||||
@ -2296,7 +2525,6 @@ Decompile(SprintStack *ss, jsbytecode *pc, intN nb)
|
||||
|
||||
case JSOP_CONDSWITCH:
|
||||
{
|
||||
jsbytecode *pc2;
|
||||
ptrdiff_t off, off2, caseOff;
|
||||
jsint ncases;
|
||||
TableEntry *table;
|
||||
@ -2451,6 +2679,7 @@ Decompile(SprintStack *ss, jsbytecode *pc, intN nb)
|
||||
LOCAL_ASSERT(ss->top >= 2);
|
||||
(void) PopOff(ss, op);
|
||||
lval = POP_STR();
|
||||
todo = ss->sprinter.offset;
|
||||
#if JS_HAS_SHARP_VARS
|
||||
op = (JSOp)pc[len];
|
||||
if (op == JSOP_DEFSHARP) {
|
||||
@ -2458,23 +2687,30 @@ Decompile(SprintStack *ss, jsbytecode *pc, intN nb)
|
||||
cs = &js_CodeSpec[op];
|
||||
len = cs->length;
|
||||
i = (jsint) GET_ATOM_INDEX(pc);
|
||||
todo = Sprint(&ss->sprinter, "#%u=%c",
|
||||
(unsigned) i,
|
||||
(*lval == 'O') ? '{' : '[');
|
||||
} else
|
||||
#endif /* JS_HAS_SHARP_VARS */
|
||||
{
|
||||
todo = Sprint(&ss->sprinter, (*lval == 'O') ? "{" : "[");
|
||||
if (Sprint(&ss->sprinter, "#%u=", (unsigned) i) < 0)
|
||||
return JS_FALSE;
|
||||
}
|
||||
#endif /* JS_HAS_SHARP_VARS */
|
||||
if (*lval == 'A') {
|
||||
++ss->inArrayInit;
|
||||
if (SprintCString(&ss->sprinter, "[") < 0)
|
||||
return JS_FALSE;
|
||||
} else {
|
||||
if (SprintCString(&ss->sprinter, "{") < 0)
|
||||
return JS_FALSE;
|
||||
}
|
||||
op = JSOP_NEWINIT; /* mark the stack with this op */
|
||||
break;
|
||||
|
||||
case JSOP_ENDINIT:
|
||||
rval = POP_STR();
|
||||
sn = js_GetSrcNote(jp->script, pc);
|
||||
if (*rval == '[')
|
||||
--ss->inArrayInit;
|
||||
todo = Sprint(&ss->sprinter, "%s%s%c",
|
||||
rval,
|
||||
(sn && SN_TYPE(sn) == SRC_CONTINUE) ? ", " : "",
|
||||
(*rval == '{') ? '}' : ']');
|
||||
(*rval == '[') ? ']' : '}');
|
||||
break;
|
||||
|
||||
case JSOP_INITPROP:
|
||||
@ -2694,7 +2930,7 @@ Decompile(SprintStack *ss, jsbytecode *pc, intN nb)
|
||||
todo = SprintPut(&ss->sprinter, "<?", 2);
|
||||
ok = QuoteString(&ss->sprinter, ATOM_TO_STRING(atom), 0) &&
|
||||
SprintPut(&ss->sprinter, " ", 1) >= 0 &&
|
||||
SprintPut(&ss->sprinter, rval, strlen(rval));
|
||||
SprintCString(&ss->sprinter, rval);
|
||||
JS_free(cx, (char *)rval);
|
||||
if (!ok)
|
||||
return JS_FALSE;
|
||||
@ -2768,7 +3004,7 @@ js_DecompileCode(JSPrinter *jp, JSScript *script, jsbytecode *pc, uintN len)
|
||||
}
|
||||
ss.offsets = (ptrdiff_t *) space;
|
||||
ss.opcodes = (jsbytecode *) ((char *)space + offsetsz);
|
||||
ss.top = 0;
|
||||
ss.top = ss.inArrayInit = 0;
|
||||
|
||||
/* Call recursive subroutine to do the hard work. */
|
||||
oldscript = jp->script;
|
||||
@ -3121,9 +3357,9 @@ js_DecompileValueGenerator(JSContext *cx, intN spindex, jsval v,
|
||||
name = NULL;
|
||||
jp = js_NewPrinter(cx, "js_DecompileValueGenerator", 0, JS_FALSE);
|
||||
if (jp) {
|
||||
if (fp->fun && fp->fun->object) {
|
||||
JS_ASSERT(OBJ_IS_NATIVE(fp->fun->object));
|
||||
jp->scope = OBJ_SCOPE(fp->fun->object);
|
||||
if (fp->fun && fp->fun->object) {
|
||||
JS_ASSERT(OBJ_IS_NATIVE(fp->fun->object));
|
||||
jp->scope = OBJ_SCOPE(fp->fun->object);
|
||||
}
|
||||
if (js_DecompileCode(jp, script, begin, len))
|
||||
name = js_GetPrinterOutput(jp);
|
||||
@ -3134,5 +3370,5 @@ js_DecompileValueGenerator(JSContext *cx, intN spindex, jsval v,
|
||||
return name;
|
||||
|
||||
do_fallback:
|
||||
return fallback ? fallback : js_ValueToString(cx, v);
|
||||
return fallback ? fallback : js_ValueToSource(cx, v);
|
||||
}
|
||||
|
@ -72,7 +72,7 @@ typedef enum JSOp {
|
||||
#define JOF_LOOKUPSWITCH 5 /* lookup switch */
|
||||
#define JOF_QARG 6 /* quickened get/set function argument ops */
|
||||
#define JOF_QVAR 7 /* quickened get/set local variable ops */
|
||||
#define JOF_INDEXCONST 8 /* arg or var index + constant pool index */
|
||||
#define JOF_INDEXCONST 8 /* uint16 slot index + constant pool index */
|
||||
#define JOF_JUMPX 9 /* signed 32-bit jump offset immediate */
|
||||
#define JOF_TABLESWITCHX 10 /* extended (32-bit offset) table switch */
|
||||
#define JOF_LOOKUPSWITCHX 11 /* extended (32-bit offset) lookup switch */
|
||||
@ -80,6 +80,7 @@ typedef enum JSOp {
|
||||
#define JOF_LITOPX 13 /* JOF_UINT24 followed by op being extended,
|
||||
where op if JOF_CONST has no unsigned 16-
|
||||
bit immediate operand */
|
||||
#define JOF_LOCAL 14 /* block-local operand stack variable */
|
||||
#define JOF_TYPEMASK 0x000f /* mask for above immediate types */
|
||||
#define JOF_NAME 0x0010 /* name operation */
|
||||
#define JOF_PROP 0x0020 /* obj.prop operation */
|
||||
@ -187,19 +188,29 @@ JS_STATIC_ASSERT(sizeof(jsatomid) * JS_BITS_PER_BYTE >=
|
||||
ATOM_INDEX_LIMIT_LOG2 + 1);
|
||||
#endif
|
||||
|
||||
/* Common uint16 immediate format helpers. */
|
||||
#define UINT16_HI(i) ((jsbytecode)((i) >> 8))
|
||||
#define UINT16_LO(i) ((jsbytecode)(i))
|
||||
#define GET_UINT16(pc) ((uintN)(((pc)[1] << 8) | (pc)[2]))
|
||||
#define SET_UINT16(pc,i) ((pc)[1] = UINT16_HI(i), (pc)[2] = UINT16_LO(i))
|
||||
#define UINT16_LIMIT ((uintN)1 << 16)
|
||||
|
||||
/* Actual argument count operand format helpers. */
|
||||
#define ARGC_HI(argc) ((jsbytecode)((argc) >> 8))
|
||||
#define ARGC_LO(argc) ((jsbytecode)(argc))
|
||||
#define GET_ARGC(pc) ((uintN)(((pc)[1] << 8) | (pc)[2]))
|
||||
#define ARGC_LIMIT ((uint32)1 << 16)
|
||||
#define ARGC_HI(argc) UINT16_HI(argc)
|
||||
#define ARGC_LO(argc) UINT16_LO(argc)
|
||||
#define GET_ARGC(pc) GET_UINT16(pc)
|
||||
#define ARGC_LIMIT UINT16_LIMIT
|
||||
|
||||
/* Synonyms for quick JOF_QARG and JOF_QVAR bytecodes. */
|
||||
#define GET_ARGNO(pc) GET_ARGC(pc)
|
||||
#define SET_ARGNO(pc,argno) SET_JUMP_OFFSET(pc,argno)
|
||||
#define ARGNO_LEN JUMP_OFFSET_LEN
|
||||
#define GET_VARNO(pc) GET_ARGC(pc)
|
||||
#define SET_VARNO(pc,varno) SET_JUMP_OFFSET(pc,varno)
|
||||
#define VARNO_LEN JUMP_OFFSET_LEN
|
||||
#define GET_ARGNO(pc) GET_UINT16(pc)
|
||||
#define SET_ARGNO(pc,argno) SET_UINT16(pc,argno)
|
||||
#define ARGNO_LEN 2
|
||||
#define ARGNO_LIMIT UINT16_LIMIT
|
||||
|
||||
#define GET_VARNO(pc) GET_UINT16(pc)
|
||||
#define SET_VARNO(pc,varno) SET_UINT16(pc,varno)
|
||||
#define VARNO_LEN 2
|
||||
#define VARNO_LIMIT UINT16_LIMIT
|
||||
|
||||
struct JSCodeSpec {
|
||||
const char *name; /* JS bytecode name */
|
||||
|
@ -1,4 +1,5 @@
|
||||
/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
|
||||
* vim: set ts=8 sw=4 et tw=128 ft=C:
|
||||
*
|
||||
* ***** BEGIN LICENSE BLOCK *****
|
||||
* Version: MPL 1.1/GPL 2.0/LGPL 2.1
|
||||
@ -293,9 +294,7 @@ OPDEF(JSOP_FINALLY, 134,"finally", NULL, 1, 0, 0, 0, JOF_BYTE)
|
||||
|
||||
/*
|
||||
* Swap the top two stack elements.
|
||||
* N.B. JSOP_SWAP doesn't swap the corresponding pc stack generating pcs, as
|
||||
* they're not needed for the current use of preserving the top-of-stack return
|
||||
* value when popping scopes while returning from catch blocks.
|
||||
* XXX JSOP_SWAP doesn't swap the corresponding pc stack generating pcs.
|
||||
*/
|
||||
OPDEF(JSOP_SWAP, 135,"swap", NULL, 1, 2, 2, 0, JOF_BYTE)
|
||||
|
||||
@ -405,4 +404,30 @@ OPDEF(JSOP_STOP, 195,"stop", NULL, 1, 0, 0, 0, JOF_BYTE)
|
||||
*/
|
||||
OPDEF(JSOP_GETXPROP, 196,"getxprop", NULL, 3, 1, 1, 12, JOF_CONST|JOF_PROP)
|
||||
OPDEF(JSOP_GETXELEM, 197,"getxelem", NULL, 1, 2, 1, 12, JOF_BYTE |JOF_ELEM|JOF_LEFTASSOC)
|
||||
OPDEF(JSOP_TYPEOFEXPR, 198, js_typeof_str,NULL, 1, 1, 1, 10, JOF_BYTE|JOF_DETECTING)
|
||||
|
||||
/*
|
||||
* Specialized JSOP_TYPEOF to avoid reporting undefined for typeof(0, undef).
|
||||
*/
|
||||
OPDEF(JSOP_TYPEOFEXPR, 198,js_typeof_str, NULL, 1, 1, 1, 10, JOF_BYTE|JOF_DETECTING)
|
||||
|
||||
/*
|
||||
* Block-local scope support.
|
||||
*/
|
||||
OPDEF(JSOP_ENTERBLOCK, 199,"enterblock", NULL, 3, 0, 0, 0, JOF_CONST)
|
||||
OPDEF(JSOP_LEAVEBLOCK, 200,"leaveblock", NULL, 3, 0, 0, 0, JOF_UINT16)
|
||||
OPDEF(JSOP_GETLOCAL, 201,"getlocal", NULL, 3, 0, 1, 13, JOF_LOCAL)
|
||||
OPDEF(JSOP_SETLOCAL, 202,"setlocal", NULL, 3, 1, 1, 1, JOF_LOCAL|JOF_SET|JOF_ASSIGNING|JOF_DETECTING)
|
||||
OPDEF(JSOP_INCLOCAL, 203,"inclocal", NULL, 3, 0, 1, 10, JOF_LOCAL|JOF_INC)
|
||||
OPDEF(JSOP_DECLOCAL, 204,"declocal", NULL, 3, 0, 1, 10, JOF_LOCAL|JOF_DEC)
|
||||
OPDEF(JSOP_LOCALINC, 205,"localinc", NULL, 3, 0, 1, 10, JOF_LOCAL|JOF_INC|JOF_POST)
|
||||
OPDEF(JSOP_LOCALDEC, 206,"localdec", NULL, 3, 0, 1, 10, JOF_LOCAL|JOF_DEC|JOF_POST)
|
||||
OPDEF(JSOP_FORLOCAL, 207,"forlocal", NULL, 3, 0, 1, 0, JOF_LOCAL|JOF_NAME|JOF_FOR)
|
||||
|
||||
/*
|
||||
* Iterator, generator, and array comprehension support.
|
||||
*/
|
||||
OPDEF(JSOP_STARTITER, 208,"startiter", NULL, 1, 0, 2, 0, JOF_BYTE)
|
||||
OPDEF(JSOP_ENDITER, 209,"enditer", NULL, 1, 3, 0, 0, JOF_BYTE)
|
||||
OPDEF(JSOP_GENERATOR, 210,"generator", NULL, 1, 0, 0, 0, JOF_BYTE)
|
||||
OPDEF(JSOP_YIELD, 211,"yield", NULL, 1, 1, 0, 0, JOF_BYTE)
|
||||
OPDEF(JSOP_ARRAYPUSH, 212,"arraypush", NULL, 3, 1, 0, 0, JOF_LOCAL)
|
||||
|
@ -62,7 +62,10 @@
|
||||
/*
|
||||
* Get OS specific header information.
|
||||
*/
|
||||
#if defined(AIXV3) || defined(AIX)
|
||||
#if defined(XP_MACOSX) || defined(DARWIN)
|
||||
#define JS_HAVE_LONG_LONG
|
||||
|
||||
#elif defined(AIXV3) || defined(AIX)
|
||||
#define JS_HAVE_LONG_LONG
|
||||
|
||||
#elif defined(BSDI)
|
||||
|
605
js/src/jsparse.c
605
js/src/jsparse.c
@ -93,6 +93,10 @@ typedef JSParseNode *
|
||||
JSMemberParser(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc,
|
||||
JSBool allowCallSyntax);
|
||||
|
||||
typedef JSParseNode *
|
||||
JSPrimaryParser(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc,
|
||||
JSTokenType tt, JSBool afterDot);
|
||||
|
||||
static JSParser FunctionStmt;
|
||||
static JSParser FunctionExpr;
|
||||
static JSParser Statements;
|
||||
@ -113,7 +117,7 @@ static JSParser AddExpr;
|
||||
static JSParser MulExpr;
|
||||
static JSParser UnaryExpr;
|
||||
static JSMemberParser MemberExpr;
|
||||
static JSParser PrimaryExpr;
|
||||
static JSPrimaryParser PrimaryExpr;
|
||||
|
||||
/*
|
||||
* Insist that the next token be of type tt, or report errno and return null.
|
||||
@ -535,9 +539,10 @@ js_CompileTokenStream(JSContext *cx, JSObject *chain, JSTokenStream *ts,
|
||||
}
|
||||
|
||||
/*
|
||||
* Insist on a final return before control flows out of pn, but don't be too
|
||||
* smart about loops (do {...; return e2;} while(0) at the end of a function
|
||||
* that contains an early return e1 will get a strict-option-only warning).
|
||||
* Insist on a final return before control flows out of pn. Try to be a bit
|
||||
* smart about loops: do {...; return e2;} while(0) at the end of a function
|
||||
* that contains an early return e1 will get a strict warning. Similarly for
|
||||
* iloops: while (true){...} is treated as though ... returns.
|
||||
*/
|
||||
#define ENDS_IN_OTHER 0
|
||||
#define ENDS_IN_RETURN 1
|
||||
@ -560,6 +565,35 @@ HasFinalReturn(JSParseNode *pn)
|
||||
return ENDS_IN_OTHER;
|
||||
return HasFinalReturn(pn->pn_kid2) & HasFinalReturn(pn->pn_kid3);
|
||||
|
||||
case TOK_WHILE:
|
||||
pn2 = pn->pn_left;
|
||||
if (pn2->pn_type == TOK_PRIMARY && pn2->pn_op == JSOP_TRUE)
|
||||
return ENDS_IN_RETURN;
|
||||
if (pn2->pn_type == TOK_NUMBER && pn2->pn_dval)
|
||||
return ENDS_IN_RETURN;
|
||||
return ENDS_IN_OTHER;
|
||||
|
||||
case TOK_DO:
|
||||
pn2 = pn->pn_right;
|
||||
if (pn2->pn_type == TOK_PRIMARY) {
|
||||
if (pn2->pn_op == JSOP_FALSE)
|
||||
return HasFinalReturn(pn->pn_left);
|
||||
if (pn2->pn_op == JSOP_TRUE)
|
||||
return ENDS_IN_RETURN;
|
||||
}
|
||||
if (pn2->pn_type == TOK_NUMBER) {
|
||||
if (pn2->pn_dval == 0)
|
||||
return HasFinalReturn(pn->pn_left);
|
||||
return ENDS_IN_RETURN;
|
||||
}
|
||||
return ENDS_IN_OTHER;
|
||||
|
||||
case TOK_FOR:
|
||||
pn2 = pn->pn_left;
|
||||
if (pn2->pn_arity == PN_TERNARY && !pn2->pn_kid2)
|
||||
return ENDS_IN_RETURN;
|
||||
return ENDS_IN_OTHER;
|
||||
|
||||
case TOK_SWITCH:
|
||||
rv = ENDS_IN_RETURN;
|
||||
hasDefault = ENDS_IN_OTHER;
|
||||
@ -622,33 +656,29 @@ HasFinalReturn(JSParseNode *pn)
|
||||
}
|
||||
|
||||
static JSBool
|
||||
ReportNoReturnValue(JSContext *cx, JSTokenStream *ts)
|
||||
ReportBadReturn(JSContext *cx, JSTokenStream *ts, uintN flags, uintN errnum,
|
||||
uintN anonerrnum)
|
||||
{
|
||||
JSFunction *fun;
|
||||
JSBool ok;
|
||||
const char *name;
|
||||
|
||||
fun = cx->fp->fun;
|
||||
if (fun->atom) {
|
||||
char *name = js_GetStringBytes(cx->runtime, ATOM_TO_STRING(fun->atom));
|
||||
ok = js_ReportCompileErrorNumber(cx, ts,
|
||||
JSREPORT_TS |
|
||||
JSREPORT_WARNING |
|
||||
JSREPORT_STRICT,
|
||||
JSMSG_NO_RETURN_VALUE, name);
|
||||
name = js_AtomToPrintableString(cx, fun->atom);
|
||||
} else {
|
||||
ok = js_ReportCompileErrorNumber(cx, ts,
|
||||
JSREPORT_TS |
|
||||
JSREPORT_WARNING |
|
||||
JSREPORT_STRICT,
|
||||
JSMSG_ANON_NO_RETURN_VALUE);
|
||||
errnum = anonerrnum;
|
||||
name = NULL;
|
||||
}
|
||||
return ok;
|
||||
return js_ReportCompileErrorNumber(cx, ts, JSREPORT_TS | flags, errnum,
|
||||
name);
|
||||
}
|
||||
|
||||
static JSBool
|
||||
CheckFinalReturn(JSContext *cx, JSTokenStream *ts, JSParseNode *pn)
|
||||
{
|
||||
return HasFinalReturn(pn) == ENDS_IN_RETURN || ReportNoReturnValue(cx, ts);
|
||||
return HasFinalReturn(pn) == ENDS_IN_RETURN ||
|
||||
ReportBadReturn(cx, ts, JSREPORT_WARNING | JSREPORT_STRICT,
|
||||
JSMSG_NO_RETURN_VALUE, JSMSG_ANON_NO_RETURN_VALUE);
|
||||
}
|
||||
|
||||
static JSParseNode *
|
||||
@ -1220,6 +1250,7 @@ ImportExpr(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc)
|
||||
pn2 = NewParseNode(cx, ts, PN_NAME, tc);
|
||||
if (!pn2)
|
||||
return NULL;
|
||||
ts->flags |= TSF_KEYWORD_IS_NAME;
|
||||
if (js_MatchToken(cx, ts, TOK_STAR)) {
|
||||
pn2->pn_op = JSOP_IMPORTALL;
|
||||
pn2->pn_atom = NULL;
|
||||
@ -1230,6 +1261,7 @@ ImportExpr(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc)
|
||||
pn2->pn_slot = -1;
|
||||
pn2->pn_attrs = 0;
|
||||
}
|
||||
ts->flags &= ~TSF_KEYWORD_IS_NAME;
|
||||
pn2->pn_expr = pn;
|
||||
pn2->pn_pos.begin = pn->pn_pos.begin;
|
||||
pn2->pn_pos.end = CURRENT_TOKEN(ts).pos.end;
|
||||
@ -1273,6 +1305,51 @@ ImportExpr(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc)
|
||||
|
||||
extern const char js_with_statement_str[];
|
||||
|
||||
static JSParseNode *
|
||||
ContainsStmt(JSParseNode *pn, JSTokenType tt)
|
||||
{
|
||||
JSParseNode *pn2, *pnt;
|
||||
|
||||
if (!pn)
|
||||
return NULL;
|
||||
if (pn->pn_type == tt)
|
||||
return pn;
|
||||
switch (pn->pn_arity) {
|
||||
case PN_LIST:
|
||||
for (pn2 = pn->pn_head; pn2; pn2 = pn2->pn_next) {
|
||||
pnt = ContainsStmt(pn2, tt);
|
||||
if (pnt)
|
||||
return pnt;
|
||||
}
|
||||
break;
|
||||
case PN_TERNARY:
|
||||
pnt = ContainsStmt(pn->pn_kid1, tt);
|
||||
if (pnt)
|
||||
return pnt;
|
||||
pnt = ContainsStmt(pn->pn_kid2, tt);
|
||||
if (pnt)
|
||||
return pnt;
|
||||
return ContainsStmt(pn->pn_kid3, tt);
|
||||
case PN_BINARY:
|
||||
/*
|
||||
* Limit recursion if pn is a binary expression, which can't contain a
|
||||
* var statement.
|
||||
*/
|
||||
if (pn->pn_op != JSOP_NOP)
|
||||
return NULL;
|
||||
pnt = ContainsStmt(pn->pn_left, tt);
|
||||
if (pnt)
|
||||
return pnt;
|
||||
return ContainsStmt(pn->pn_right, tt);
|
||||
case PN_UNARY:
|
||||
if (pn->pn_op != JSOP_NOP)
|
||||
return NULL;
|
||||
return ContainsStmt(pn->pn_kid, tt);
|
||||
default:;
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static JSParseNode *
|
||||
Statement(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc)
|
||||
{
|
||||
@ -1798,6 +1875,16 @@ Statement(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc)
|
||||
catchtail->pn_kid2 = NULL;
|
||||
|
||||
if (js_MatchToken(cx, ts, TOK_FINALLY)) {
|
||||
#if JS_HAS_GENERATORS
|
||||
/* As in Python (see PEP-255), disallow yield from try-finally. */
|
||||
pn1 = ContainsStmt(pn->pn_kid1, TOK_YIELD);
|
||||
if (pn1) {
|
||||
js_ReportCompileErrorNumber(cx, pn1,
|
||||
JSREPORT_PN | JSREPORT_ERROR,
|
||||
JSMSG_BAD_RETURN_OR_YIELD,
|
||||
js_yield_str);
|
||||
}
|
||||
#endif
|
||||
tc->tryCount++;
|
||||
MUST_MATCH_TOKEN(TOK_LC, JSMSG_CURLY_BEFORE_FINALLY);
|
||||
js_PushStatement(tc, &stmtInfo, STMT_FINALLY, -1);
|
||||
@ -1968,27 +2055,44 @@ Statement(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc)
|
||||
break;
|
||||
|
||||
case TOK_RETURN:
|
||||
#if JS_HAS_GENERATORS
|
||||
case TOK_YIELD:
|
||||
#endif
|
||||
if (!(tc->flags & TCF_IN_FUNCTION)) {
|
||||
js_ReportCompileErrorNumber(cx, ts, JSREPORT_TS | JSREPORT_ERROR,
|
||||
JSMSG_BAD_RETURN);
|
||||
JSMSG_BAD_RETURN_OR_YIELD,
|
||||
#if JS_HAS_GENERATORS
|
||||
(tt == TOK_YIELD) ? js_yield_str :
|
||||
#endif
|
||||
js_return_str);
|
||||
return NULL;
|
||||
}
|
||||
pn = NewParseNode(cx, ts, PN_UNARY, tc);
|
||||
if (!pn)
|
||||
return NULL;
|
||||
|
||||
/* This is ugly, but we don't want to require a semicolon. */
|
||||
ts->flags |= TSF_OPERAND;
|
||||
tt = js_PeekTokenSameLine(cx, ts);
|
||||
ts->flags &= ~TSF_OPERAND;
|
||||
if (tt == TOK_ERROR)
|
||||
return NULL;
|
||||
#if JS_HAS_GENERATORS
|
||||
if (tt == TOK_YIELD) {
|
||||
tc->flags |= TCF_FUN_IS_GENERATOR;
|
||||
} else
|
||||
#endif
|
||||
{
|
||||
/* This is ugly, but we don't want to require a semicolon. */
|
||||
ts->flags |= TSF_OPERAND;
|
||||
tt = js_PeekTokenSameLine(cx, ts);
|
||||
ts->flags &= ~TSF_OPERAND;
|
||||
if (tt == TOK_ERROR)
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (tt != TOK_EOF && tt != TOK_EOL && tt != TOK_SEMI && tt != TOK_RC) {
|
||||
pn2 = Expr(cx, ts, tc);
|
||||
if (!pn2)
|
||||
return NULL;
|
||||
tc->flags |= TCF_RETURN_EXPR;
|
||||
#if JS_HAS_GENERATORS
|
||||
if (pn->pn_type == TOK_RETURN)
|
||||
#endif
|
||||
tc->flags |= TCF_RETURN_EXPR;
|
||||
pn->pn_pos.end = pn2->pn_pos.end;
|
||||
pn->pn_kid = pn2;
|
||||
} else {
|
||||
@ -1996,14 +2100,20 @@ Statement(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc)
|
||||
pn->pn_kid = NULL;
|
||||
}
|
||||
|
||||
if ((~tc->flags & (TCF_RETURN_EXPR | TCF_FUN_IS_GENERATOR)) == 0) {
|
||||
/* As in Python (see PEP-255), disallow return v; in generators. */
|
||||
ReportBadReturn(cx, ts, JSREPORT_ERROR,
|
||||
JSMSG_BAD_GENERATOR_RETURN,
|
||||
JSMSG_BAD_ANON_GENERATOR_RETURN);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (JS_HAS_STRICT_OPTION(cx) &&
|
||||
(~tc->flags & (TCF_RETURN_EXPR | TCF_RETURN_VOID)) == 0) {
|
||||
/*
|
||||
* We must be in a frame with a non-native function, because
|
||||
* we're compiling one.
|
||||
*/
|
||||
if (!ReportNoReturnValue(cx, ts))
|
||||
return NULL;
|
||||
(~tc->flags & (TCF_RETURN_EXPR | TCF_RETURN_VOID)) == 0 &&
|
||||
!ReportBadReturn(cx, ts, JSREPORT_WARNING | JSREPORT_STRICT,
|
||||
JSMSG_NO_RETURN_VALUE,
|
||||
JSMSG_ANON_NO_RETURN_VALUE)) {
|
||||
return NULL;
|
||||
}
|
||||
break;
|
||||
|
||||
@ -2677,7 +2787,7 @@ SetLvalKid(JSContext *cx, JSTokenStream *ts, JSParseNode *pn, JSParseNode *kid,
|
||||
return kid;
|
||||
}
|
||||
|
||||
static const char *incop_name_str[] = {"increment", "decrement"};
|
||||
static const char incop_name_str[][10] = {"increment", "decrement"};
|
||||
|
||||
static JSBool
|
||||
SetIncOpKid(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc,
|
||||
@ -2854,11 +2964,9 @@ MemberExpr(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc,
|
||||
|
||||
/* Check for new expression first. */
|
||||
ts->flags |= TSF_OPERAND;
|
||||
tt = js_PeekToken(cx, ts);
|
||||
tt = js_GetToken(cx, ts);
|
||||
ts->flags &= ~TSF_OPERAND;
|
||||
if (tt == TOK_NEW) {
|
||||
(void) js_GetToken(cx, ts);
|
||||
|
||||
pn = NewParseNode(cx, ts, PN_LIST, tc);
|
||||
if (!pn)
|
||||
return NULL;
|
||||
@ -2878,7 +2986,7 @@ MemberExpr(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc,
|
||||
}
|
||||
pn->pn_pos.end = PN_LAST(pn)->pn_pos.end;
|
||||
} else {
|
||||
pn = PrimaryExpr(cx, ts, tc);
|
||||
pn = PrimaryExpr(cx, ts, tc, tt, JS_FALSE);
|
||||
if (!pn)
|
||||
return NULL;
|
||||
|
||||
@ -2904,7 +3012,10 @@ MemberExpr(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc,
|
||||
if (!pn2)
|
||||
return NULL;
|
||||
#if JS_HAS_XML_SUPPORT
|
||||
pn3 = PrimaryExpr(cx, ts, tc);
|
||||
ts->flags |= TSF_OPERAND | TSF_KEYWORD_IS_NAME;
|
||||
tt = js_GetToken(cx, ts);
|
||||
ts->flags &= ~(TSF_OPERAND | TSF_KEYWORD_IS_NAME);
|
||||
pn3 = PrimaryExpr(cx, ts, tc, tt, JS_TRUE);
|
||||
if (!pn3)
|
||||
return NULL;
|
||||
tt = pn3->pn_type;
|
||||
@ -2940,7 +3051,9 @@ MemberExpr(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc,
|
||||
pn2->pn_right = pn3;
|
||||
}
|
||||
#else
|
||||
ts->flags |= TSF_KEYWORD_IS_NAME;
|
||||
MUST_MATCH_TOKEN(TOK_NAME, JSMSG_NAME_AFTER_DOT);
|
||||
ts->flags &= ~TSF_KEYWORD_IS_NAME;
|
||||
pn2->pn_op = JSOP_GETPROP;
|
||||
pn2->pn_expr = pn;
|
||||
pn2->pn_atom = CURRENT_TOKEN(ts).t_atom;
|
||||
@ -2952,7 +3065,10 @@ MemberExpr(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc,
|
||||
pn2 = NewParseNode(cx, ts, PN_BINARY, tc);
|
||||
if (!pn2)
|
||||
return NULL;
|
||||
pn3 = PrimaryExpr(cx, ts, tc);
|
||||
ts->flags |= TSF_OPERAND | TSF_KEYWORD_IS_NAME;
|
||||
tt = js_GetToken(cx, ts);
|
||||
ts->flags &= ~(TSF_OPERAND | TSF_KEYWORD_IS_NAME);
|
||||
pn3 = PrimaryExpr(cx, ts, tc, tt, JS_TRUE);
|
||||
if (!pn3)
|
||||
return NULL;
|
||||
tt = pn3->pn_type;
|
||||
@ -3156,7 +3272,9 @@ QualifiedSuffix(JSContext *cx, JSTokenStream *ts, JSParseNode *pn,
|
||||
if (pn->pn_op == JSOP_QNAMEPART)
|
||||
pn->pn_op = JSOP_NAME;
|
||||
|
||||
ts->flags |= TSF_KEYWORD_IS_NAME;
|
||||
tt = js_GetToken(cx, ts);
|
||||
ts->flags &= ~TSF_KEYWORD_IS_NAME;
|
||||
if (tt == TOK_STAR || tt == TOK_NAME) {
|
||||
/* Inline and specialize PropertySelector for JSOP_QNAMECONST. */
|
||||
pn2->pn_op = JSOP_QNAMECONST;
|
||||
@ -3709,14 +3827,12 @@ js_ParseXMLTokenStream(JSContext *cx, JSObject *chain, JSTokenStream *ts,
|
||||
#endif /* JS_HAS_XMLSUPPORT */
|
||||
|
||||
static JSParseNode *
|
||||
PrimaryExpr(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc)
|
||||
PrimaryExpr(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc,
|
||||
JSTokenType tt, JSBool afterDot)
|
||||
{
|
||||
JSTokenType tt;
|
||||
JSParseNode *pn, *pn2, *pn3;
|
||||
#if JS_HAS_GETTER_SETTER
|
||||
JSAtom *atom;
|
||||
JSRuntime *rt;
|
||||
#endif
|
||||
JSBool afterComma;
|
||||
JSOp op;
|
||||
|
||||
#if JS_HAS_SHARP_VARS
|
||||
JSParseNode *defsharp;
|
||||
@ -3734,10 +3850,6 @@ PrimaryExpr(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc)
|
||||
|
||||
CHECK_RECURSION();
|
||||
|
||||
ts->flags |= TSF_OPERAND;
|
||||
tt = js_GetToken(cx, ts);
|
||||
ts->flags &= ~TSF_OPERAND;
|
||||
|
||||
#if JS_HAS_GETTER_SETTER
|
||||
if (tt == TOK_NAME) {
|
||||
tt = CheckGetterOrSetter(cx, ts, TOK_FUNCTION);
|
||||
@ -3748,6 +3860,7 @@ PrimaryExpr(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc)
|
||||
|
||||
switch (tt) {
|
||||
case TOK_FUNCTION:
|
||||
#if JS_HAS_XML_SUPPORT
|
||||
if (js_MatchToken(cx, ts, TOK_DBLCOLON)) {
|
||||
pn2 = NewParseNode(cx, ts, PN_NULLARY, tc);
|
||||
if (!pn2)
|
||||
@ -3758,6 +3871,7 @@ PrimaryExpr(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc)
|
||||
return NULL;
|
||||
break;
|
||||
}
|
||||
#endif
|
||||
pn = FunctionExpr(cx, ts, tc);
|
||||
if (!pn)
|
||||
return NULL;
|
||||
@ -3819,6 +3933,197 @@ PrimaryExpr(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc)
|
||||
}
|
||||
}
|
||||
|
||||
#if JS_HAS_GENERATORS
|
||||
/*
|
||||
* At this point, atomIndex == 0 and pn->pn_count != 0 implies
|
||||
* one element initialiser was parsed (possibly with a defsharp
|
||||
* before the left bracket).
|
||||
*
|
||||
* An array comprehension of the form:
|
||||
*
|
||||
* [i * j for (i in o) for (j in p) if (i != j)]
|
||||
*
|
||||
* translates to roughly the following let expression:
|
||||
*
|
||||
* let (array = new Array, i, j) {
|
||||
* for (i in o) let {
|
||||
* for (j in p)
|
||||
* if (i != j)
|
||||
* array.push(i * j)
|
||||
* }
|
||||
* array
|
||||
* }
|
||||
*
|
||||
* where array is a nameless block-local variable. The "roughly"
|
||||
* means that an implementation may optimize away the array.push.
|
||||
* An array comprehension opens exactly one block scope, no matter
|
||||
* how many for heads it contains.
|
||||
*
|
||||
* Each let () {...} or for (let ...) ... compiles to:
|
||||
*
|
||||
* JSOP_ENTERBLOCK <o> ... JSOP_LEAVEBLOCK <n>
|
||||
*
|
||||
* where <o> is a literal object representing the block scope,
|
||||
* with <n> properties, naming each var declared in the block.
|
||||
*
|
||||
* Each var declaration in a let-block binds a name in <o> at
|
||||
* compile time, and allocates a slot on the operand stack at
|
||||
* runtime via JSOP_ENTERBLOCK. A block-local var is accessed
|
||||
* by the JSOP_GETLOCAL and JSOP_SETLOCAL ops, and iterated with
|
||||
* JSOP_FORLOCAL. These ops all have an immediate operand, the
|
||||
* local slot's stack index from fp->spbase.
|
||||
*
|
||||
* The array comprehension iteration step, array.push(i * j) in
|
||||
* the example above, is done by <i * j>; JSOP_ARRAYCOMP <array>,
|
||||
* where <array> is the index of array's stack slot.
|
||||
*/
|
||||
if (atomIndex == 0 &&
|
||||
pn->pn_count != 0 &&
|
||||
js_MatchToken(cx, ts, TOK_FOR)) {
|
||||
JSParseNode **pnp, *pnexp, *pntop, *pnlet;
|
||||
JSObject *obj;
|
||||
JSScope *scope;
|
||||
JSStmtInfo stmtInfo;
|
||||
JSAtom *atom;
|
||||
JSScopeProperty *sprop;
|
||||
|
||||
|
||||
/* Relabel pn as an array comprehension node. */
|
||||
pn->pn_type = TOK_ARRAYCOMP;
|
||||
|
||||
/*
|
||||
* Remove the comprehension expression from pn's linked list
|
||||
* and save it via pnexp. We'll re-install it underneath the
|
||||
* ARRAYPUSH nodeafter we parse the rest of the comprehension.
|
||||
*/
|
||||
pnexp = PN_LAST(pn);
|
||||
JS_ASSERT(pn->pn_count == 1 || pn->pn_count == 2);
|
||||
pn->pn_tail = (--pn->pn_count == 1)
|
||||
? &pn->pn_head->pn_next
|
||||
: &pn->pn_head;
|
||||
*pn->pn_tail = NULL;
|
||||
|
||||
/*
|
||||
* Make a parse-node and literal object representing the array
|
||||
* comprehension's block scope.
|
||||
*/
|
||||
pntop = NewParseNode(cx, ts, PN_NAME, tc);
|
||||
if (!pntop)
|
||||
return NULL;
|
||||
|
||||
obj = js_NewBlockObject(cx);
|
||||
if (!obj)
|
||||
return NULL;
|
||||
scope = OBJ_SCOPE(obj);
|
||||
js_PushBlockScope(tc, &stmtInfo, obj, -1);
|
||||
|
||||
atom = js_AtomizeObject(cx, obj, 0);
|
||||
if (!atom)
|
||||
return NULL;
|
||||
pntop->pn_type = TOK_LEXICALSCOPE;
|
||||
pntop->pn_atom = atom;
|
||||
pntop->pn_expr = NULL;
|
||||
pnp = &pntop->pn_expr;
|
||||
|
||||
do {
|
||||
/*
|
||||
* FOR node is binary, left is control and right is body.
|
||||
* Use atomIndex to count each block-local let-variable on
|
||||
* the left-hand side of IN.
|
||||
*/
|
||||
pn2 = NewParseNode(cx, ts, PN_BINARY, tc);
|
||||
if (!pn2)
|
||||
return NULL;
|
||||
|
||||
MUST_MATCH_TOKEN(TOK_LP, JSMSG_PAREN_AFTER_FOR);
|
||||
MUST_MATCH_TOKEN(TOK_NAME, JSMSG_NAME_AFTER_FOR_PAREN);
|
||||
atom = CURRENT_TOKEN(ts).t_atom;
|
||||
|
||||
/*
|
||||
* Look for the loop variable in case it was defined by an
|
||||
* outer 'for' in this comprehension.
|
||||
*/
|
||||
sprop = SCOPE_GET_PROPERTY(scope, ATOM_TO_JSID(atom));
|
||||
if (sprop) {
|
||||
JS_ASSERT(sprop->flags & SPROP_HAS_SHORTID);
|
||||
JS_ASSERT((uint16)sprop->shortid < atomIndex);
|
||||
OBJ_DROP_PROPERTY(cx, obj, (JSProperty *) sprop);
|
||||
} else {
|
||||
if (atomIndex == JS_BIT(16)) {
|
||||
js_ReportCompileErrorNumber(cx, ts,
|
||||
JSREPORT_TS | JSREPORT_ERROR,
|
||||
JSMSG_ARRAY_INIT_TOO_BIG);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* Use JSPROP_ENUMERATE to aid the disassembler. */
|
||||
if (!js_DefineNativeProperty(cx, obj,
|
||||
ATOM_TO_JSID(atom),
|
||||
JSVAL_VOID, NULL, NULL,
|
||||
JSPROP_ENUMERATE |
|
||||
JSPROP_PERMANENT,
|
||||
SPROP_HAS_SHORTID,
|
||||
(intN)atomIndex++,
|
||||
NULL)) {
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Create a name node with op JSOP_NAME. We can't set op
|
||||
* JSOP_GETLOCAL here, because we don't yet know the block
|
||||
* depth in the operand stack frame. The code generator
|
||||
* computes that, and it tries to bind all names to slots,
|
||||
* so we must let it do this optimization.
|
||||
*/
|
||||
pnlet = NewParseNode(cx, ts, PN_NAME, tc);
|
||||
if (!pnlet)
|
||||
return NULL;
|
||||
pnlet->pn_op = JSOP_NAME;
|
||||
pnlet->pn_atom = atom;
|
||||
pnlet->pn_expr = NULL;
|
||||
pnlet->pn_slot = -1;
|
||||
pnlet->pn_attrs = 0;
|
||||
|
||||
MUST_MATCH_TOKEN(TOK_IN, JSMSG_IN_AFTER_FOR_NAME);
|
||||
pn3 = NewBinary(cx, TOK_IN, JSOP_NOP, pnlet,
|
||||
Expr(cx, ts, tc), tc);
|
||||
if (!pn3)
|
||||
return NULL;
|
||||
|
||||
MUST_MATCH_TOKEN(TOK_RP, JSMSG_PAREN_AFTER_FOR_CTRL);
|
||||
pn2->pn_left = pn3;
|
||||
*pnp = pn2;
|
||||
pnp = &pn2->pn_right;
|
||||
} while (js_MatchToken(cx, ts, TOK_FOR));
|
||||
|
||||
if (js_MatchToken(cx, ts, TOK_IF)) {
|
||||
pn2 = NewParseNode(cx, ts, PN_TERNARY, tc);
|
||||
if (!pn2)
|
||||
return NULL;
|
||||
pn2->pn_kid1 = Condition(cx, ts, tc);
|
||||
if (!pn2->pn_kid1)
|
||||
return NULL;
|
||||
pn2->pn_kid2 = NULL;
|
||||
pn2->pn_kid3 = NULL;
|
||||
*pnp = pn2;
|
||||
pnp = &pn2->pn_kid2;
|
||||
}
|
||||
|
||||
pn2 = NewParseNode(cx, ts, PN_UNARY, tc);
|
||||
if (!pn2)
|
||||
return NULL;
|
||||
pn2->pn_type = TOK_ARRAYPUSH;
|
||||
pn2->pn_op = JSOP_ARRAYPUSH;
|
||||
pn2->pn_kid = pnexp;
|
||||
pn2->pn_array = pn;
|
||||
*pnp = pn2;
|
||||
PN_APPEND(pn, pntop);
|
||||
|
||||
js_PopStatement(tc);
|
||||
}
|
||||
#endif /* JS_HAS_GENERATORS */
|
||||
|
||||
MUST_MATCH_TOKEN(TOK_RB, JSMSG_BRACKET_AFTER_LIST);
|
||||
}
|
||||
pn->pn_pos.end = CURRENT_TOKEN(ts).pos.end;
|
||||
@ -3839,33 +4144,37 @@ PrimaryExpr(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc)
|
||||
#endif
|
||||
PN_INIT_LIST(pn);
|
||||
|
||||
if (!js_MatchToken(cx, ts, TOK_RC)) {
|
||||
do {
|
||||
JSOp op;
|
||||
|
||||
tt = js_GetToken(cx, ts);
|
||||
switch (tt) {
|
||||
case TOK_NUMBER:
|
||||
pn3 = NewParseNode(cx, ts, PN_NULLARY, tc);
|
||||
if (pn3)
|
||||
pn3->pn_dval = CURRENT_TOKEN(ts).t_dval;
|
||||
break;
|
||||
case TOK_NAME:
|
||||
afterComma = JS_FALSE;
|
||||
for (;;) {
|
||||
ts->flags |= TSF_KEYWORD_IS_NAME;
|
||||
tt = js_GetToken(cx, ts);
|
||||
ts->flags &= ~TSF_KEYWORD_IS_NAME;
|
||||
switch (tt) {
|
||||
case TOK_NUMBER:
|
||||
pn3 = NewParseNode(cx, ts, PN_NULLARY, tc);
|
||||
if (pn3)
|
||||
pn3->pn_dval = CURRENT_TOKEN(ts).t_dval;
|
||||
break;
|
||||
case TOK_NAME:
|
||||
#if JS_HAS_GETTER_SETTER
|
||||
{
|
||||
JSAtom *atom;
|
||||
JSRuntime *rt;
|
||||
|
||||
atom = CURRENT_TOKEN(ts).t_atom;
|
||||
rt = cx->runtime;
|
||||
if (atom == rt->atomState.getAtom ||
|
||||
atom == rt->atomState.setAtom) {
|
||||
op = (atom == rt->atomState.getAtom)
|
||||
? JSOP_GETTER
|
||||
: JSOP_SETTER;
|
||||
? JSOP_GETTER
|
||||
: JSOP_SETTER;
|
||||
if (js_MatchToken(cx, ts, TOK_NAME)) {
|
||||
pn3 = NewParseNode(cx, ts, PN_NAME, tc);
|
||||
if (!pn3)
|
||||
return NULL;
|
||||
pn3->pn_atom = CURRENT_TOKEN(ts).t_atom;
|
||||
pn3->pn_expr = NULL;
|
||||
|
||||
|
||||
/* We have to fake a 'function' token here. */
|
||||
CURRENT_TOKEN(ts).t_op = JSOP_NOP;
|
||||
CURRENT_TOKEN(ts).type = TOK_FUNCTION;
|
||||
@ -3875,56 +4184,66 @@ PrimaryExpr(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc)
|
||||
}
|
||||
}
|
||||
/* else fall thru ... */
|
||||
#endif
|
||||
case TOK_STRING:
|
||||
pn3 = NewParseNode(cx, ts, PN_NULLARY, tc);
|
||||
if (pn3)
|
||||
pn3->pn_atom = CURRENT_TOKEN(ts).t_atom;
|
||||
break;
|
||||
case TOK_RC:
|
||||
if (!js_ReportCompileErrorNumber(cx, ts,
|
||||
JSREPORT_TS |
|
||||
JSREPORT_WARNING |
|
||||
JSREPORT_STRICT,
|
||||
JSMSG_TRAILING_COMMA)) {
|
||||
return NULL;
|
||||
}
|
||||
goto end_obj_init;
|
||||
default:
|
||||
js_ReportCompileErrorNumber(cx, ts,
|
||||
JSREPORT_TS | JSREPORT_ERROR,
|
||||
JSMSG_BAD_PROP_ID);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
tt = js_GetToken(cx, ts);
|
||||
#if JS_HAS_GETTER_SETTER
|
||||
if (tt == TOK_NAME) {
|
||||
tt = CheckGetterOrSetter(cx, ts, TOK_COLON);
|
||||
if (tt == TOK_ERROR)
|
||||
#endif
|
||||
case TOK_STRING:
|
||||
pn3 = NewParseNode(cx, ts, PN_NULLARY, tc);
|
||||
if (pn3)
|
||||
pn3->pn_atom = CURRENT_TOKEN(ts).t_atom;
|
||||
break;
|
||||
case TOK_RC:
|
||||
if (afterComma &&
|
||||
!js_ReportCompileErrorNumber(cx, ts,
|
||||
JSREPORT_TS |
|
||||
JSREPORT_WARNING |
|
||||
JSREPORT_STRICT,
|
||||
JSMSG_TRAILING_COMMA)) {
|
||||
return NULL;
|
||||
}
|
||||
#endif
|
||||
if (tt != TOK_COLON) {
|
||||
js_ReportCompileErrorNumber(cx, ts,
|
||||
JSREPORT_TS | JSREPORT_ERROR,
|
||||
JSMSG_COLON_AFTER_ID);
|
||||
return NULL;
|
||||
}
|
||||
op = CURRENT_TOKEN(ts).t_op;
|
||||
pn2 = NewBinary(cx, TOK_COLON, op, pn3, AssignExpr(cx, ts, tc),
|
||||
tc);
|
||||
#if JS_HAS_GETTER_SETTER
|
||||
skip:
|
||||
#endif
|
||||
if (!pn2)
|
||||
return NULL;
|
||||
PN_APPEND(pn, pn2);
|
||||
} while (js_MatchToken(cx, ts, TOK_COMMA));
|
||||
goto end_obj_init;
|
||||
default:
|
||||
js_ReportCompileErrorNumber(cx, ts,
|
||||
JSREPORT_TS | JSREPORT_ERROR,
|
||||
JSMSG_BAD_PROP_ID);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
MUST_MATCH_TOKEN(TOK_RC, JSMSG_CURLY_AFTER_LIST);
|
||||
tt = js_GetToken(cx, ts);
|
||||
#if JS_HAS_GETTER_SETTER
|
||||
if (tt == TOK_NAME) {
|
||||
tt = CheckGetterOrSetter(cx, ts, TOK_COLON);
|
||||
if (tt == TOK_ERROR)
|
||||
return NULL;
|
||||
}
|
||||
#endif
|
||||
if (tt != TOK_COLON) {
|
||||
js_ReportCompileErrorNumber(cx, ts,
|
||||
JSREPORT_TS | JSREPORT_ERROR,
|
||||
JSMSG_COLON_AFTER_ID);
|
||||
return NULL;
|
||||
}
|
||||
op = CURRENT_TOKEN(ts).t_op;
|
||||
pn2 = NewBinary(cx, TOK_COLON, op, pn3, AssignExpr(cx, ts, tc),
|
||||
tc);
|
||||
#if JS_HAS_GETTER_SETTER
|
||||
skip:
|
||||
#endif
|
||||
if (!pn2)
|
||||
return NULL;
|
||||
PN_APPEND(pn, pn2);
|
||||
|
||||
tt = js_GetToken(cx, ts);
|
||||
if (tt == TOK_RC)
|
||||
goto end_obj_init;
|
||||
if (tt != TOK_COMMA) {
|
||||
js_ReportCompileErrorNumber(cx, ts,
|
||||
JSREPORT_TS | JSREPORT_ERROR,
|
||||
JSMSG_CURLY_AFTER_LIST);
|
||||
return NULL;
|
||||
}
|
||||
afterComma = JS_TRUE;
|
||||
}
|
||||
end_obj_init:
|
||||
end_obj_init:
|
||||
pn->pn_pos.end = CURRENT_TOKEN(ts).pos.end;
|
||||
return pn;
|
||||
|
||||
@ -3937,6 +4256,9 @@ PrimaryExpr(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc)
|
||||
return NULL;
|
||||
defsharp->pn_kid = NULL;
|
||||
defsharp->pn_num = (jsint) CURRENT_TOKEN(ts).t_dval;
|
||||
ts->flags |= TSF_OPERAND;
|
||||
tt = js_GetToken(cx, ts);
|
||||
ts->flags &= ~TSF_OPERAND;
|
||||
goto again;
|
||||
|
||||
case TOK_USESHARP:
|
||||
@ -4017,6 +4339,28 @@ PrimaryExpr(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc)
|
||||
|
||||
#if JS_HAS_XML_SUPPORT
|
||||
if (js_MatchToken(cx, ts, TOK_DBLCOLON)) {
|
||||
if (afterDot) {
|
||||
JSString *str;
|
||||
|
||||
/*
|
||||
* Here PrimaryExpr is called after '.' or '..' and we
|
||||
* just scanned .name:: or ..name:: . This is the only
|
||||
* case where a keyword after '.' or '..' is not
|
||||
* treated as a property name.
|
||||
*/
|
||||
str = ATOM_TO_STRING(pn->pn_atom);
|
||||
tt = js_CheckKeyword(JSSTRING_CHARS(str),
|
||||
JSSTRING_LENGTH(str));
|
||||
if (tt == TOK_FUNCTION) {
|
||||
pn->pn_arity = PN_NULLARY;
|
||||
pn->pn_type = TOK_FUNCTION;
|
||||
} else if (tt != TOK_EOF) {
|
||||
js_ReportCompileErrorNumber(
|
||||
cx, ts, JSREPORT_TS | JSREPORT_ERROR,
|
||||
JSMSG_KEYWORD_NOT_NS);
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
pn = QualifiedSuffix(cx, ts, pn, tc);
|
||||
if (!pn)
|
||||
return NULL;
|
||||
@ -4101,43 +4445,6 @@ PrimaryExpr(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc)
|
||||
return pn;
|
||||
}
|
||||
|
||||
static JSBool
|
||||
ContainsVarStmt(JSParseNode *pn)
|
||||
{
|
||||
JSParseNode *pn2;
|
||||
|
||||
if (!pn)
|
||||
return JS_FALSE;
|
||||
switch (pn->pn_arity) {
|
||||
case PN_LIST:
|
||||
if (pn->pn_type == TOK_VAR)
|
||||
return JS_TRUE;
|
||||
for (pn2 = pn->pn_head; pn2; pn2 = pn2->pn_next) {
|
||||
if (ContainsVarStmt(pn2))
|
||||
return JS_TRUE;
|
||||
}
|
||||
break;
|
||||
case PN_TERNARY:
|
||||
return ContainsVarStmt(pn->pn_kid1) ||
|
||||
ContainsVarStmt(pn->pn_kid2) ||
|
||||
ContainsVarStmt(pn->pn_kid3);
|
||||
case PN_BINARY:
|
||||
/*
|
||||
* Limit recursion if pn is a binary expression, which can't contain a
|
||||
* var statement.
|
||||
*/
|
||||
if (pn->pn_op != JSOP_NOP)
|
||||
return JS_FALSE;
|
||||
return ContainsVarStmt(pn->pn_left) || ContainsVarStmt(pn->pn_right);
|
||||
case PN_UNARY:
|
||||
if (pn->pn_op != JSOP_NOP)
|
||||
return JS_FALSE;
|
||||
return ContainsVarStmt(pn->pn_kid);
|
||||
default:;
|
||||
}
|
||||
return JS_FALSE;
|
||||
}
|
||||
|
||||
/*
|
||||
* Fold from one constant type to another.
|
||||
* XXX handles only strings and numbers for now
|
||||
@ -4544,7 +4851,7 @@ js_FoldConstants(JSContext *cx, JSParseNode *pn, JSTreeContext *tc)
|
||||
|
||||
switch (pn->pn_type) {
|
||||
case TOK_IF:
|
||||
if (ContainsVarStmt(pn2) || ContainsVarStmt(pn3))
|
||||
if (ContainsStmt(pn2, TOK_VAR) || ContainsStmt(pn3, TOK_VAR))
|
||||
break;
|
||||
/* FALL THROUGH */
|
||||
|
||||
|
@ -183,6 +183,8 @@ JS_BEGIN_EXTERN_C
|
||||
* with pn_slot >= 0 and pn_attrs telling const-ness
|
||||
* TOK_NUMBER dval pn_dval: double value of numeric literal
|
||||
* TOK_PRIMARY nullary pn_op: JSOp bytecode
|
||||
*
|
||||
* <E4X node descriptions>
|
||||
* TOK_ANYNAME nullary pn_op: JSOP_ANYNAME
|
||||
* pn_atom: cx->runtime->atomState.starAtom
|
||||
* TOK_AT unary pn_op: JSOP_TOATTRNAME; pn_kid attribute id/expr
|
||||
@ -235,6 +237,21 @@ JS_BEGIN_EXTERN_C
|
||||
* translates to:
|
||||
*
|
||||
* ((a x {x}) 'Hi there!' ((b y {y}) 'How are you?') ((answer) {x + y}))
|
||||
*
|
||||
* <Non-E4X node descriptions, continued>
|
||||
*
|
||||
* Label Variant Members
|
||||
* ----- ------- -------
|
||||
* TOK_LEXICALSCOPE name pn_atom: block object
|
||||
* pn_expr: block body
|
||||
* TOK_ARRAYCOMP list pn_head: list of pn_count (1 or 2) elements
|
||||
* if pn_count is 2, first element is #n=[...]
|
||||
* last element is block enclosing for loop(s)
|
||||
* and optionally if-guarded TOK_ARRAYPUSH
|
||||
* pn_extra: stack slot, used during code gen
|
||||
* TOK_ARRAYPUSH unary pn_op: JSOP_ARRAYCOMP
|
||||
* pn_kid: array comprehension expression
|
||||
* pn_array: link to TOK_ARRAYCOMP
|
||||
*/
|
||||
typedef enum JSParseNodeArity {
|
||||
PN_FUNC = -3,
|
||||
@ -278,6 +295,7 @@ struct JSParseNode {
|
||||
struct { /* one kid if unary */
|
||||
JSParseNode *kid;
|
||||
jsint num; /* -1 or sharp variable number */
|
||||
JSParseNode *array; /* cyclic link to array comprehension */
|
||||
} unary;
|
||||
struct { /* name, labeled statement, etc. */
|
||||
JSAtom *atom; /* name or label atom, null if slot */
|
||||
@ -313,6 +331,7 @@ struct JSParseNode {
|
||||
#define pn_val pn_u.binary.val
|
||||
#define pn_kid pn_u.unary.kid
|
||||
#define pn_num pn_u.unary.num
|
||||
#define pn_array pn_u.unary.array
|
||||
#define pn_atom pn_u.name.atom
|
||||
#define pn_expr pn_u.name.expr
|
||||
#define pn_slot pn_u.name.slot
|
||||
|
@ -36,28 +36,43 @@
|
||||
*
|
||||
* ***** END LICENSE BLOCK ***** */
|
||||
|
||||
JS_PROTO(Null, js_InitNullClass)
|
||||
JS_PROTO(Object, js_InitFunctionAndObjectClasses)
|
||||
JS_PROTO(Function, js_InitFunctionAndObjectClasses)
|
||||
JS_PROTO(Array, js_InitArrayClass)
|
||||
JS_PROTO(Boolean, js_InitBooleanClass)
|
||||
JS_PROTO(Call, js_InitCallClass)
|
||||
JS_PROTO(Date, js_InitDateClass)
|
||||
JS_PROTO(Math, js_InitMathClass)
|
||||
JS_PROTO(Number, js_InitNumberClass)
|
||||
JS_PROTO(String, js_InitStringClass)
|
||||
JS_PROTO(RegExp, js_InitRegExpClass)
|
||||
JS_PROTO(Script, js_InitScriptClass)
|
||||
JS_PROTO(XML, js_InitXMLClass)
|
||||
JS_PROTO(Namespace, js_InitNamespaceClass)
|
||||
JS_PROTO(QName, js_InitQNameClass)
|
||||
JS_PROTO(AnyName, js_InitAnyNameClass)
|
||||
JS_PROTO(AttributeName, js_InitAttributeNameClass)
|
||||
JS_PROTO(Error, js_InitExceptionClasses)
|
||||
JS_PROTO(InternalError, js_InitExceptionClasses)
|
||||
JS_PROTO(EvalError, js_InitExceptionClasses)
|
||||
JS_PROTO(RangeError, js_InitExceptionClasses)
|
||||
JS_PROTO(ReferenceError, js_InitExceptionClasses)
|
||||
JS_PROTO(SyntaxError, js_InitExceptionClasses)
|
||||
JS_PROTO(TypeError, js_InitExceptionClasses)
|
||||
JS_PROTO(URIError, js_InitExceptionClasses)
|
||||
#include "jsconfig.h"
|
||||
|
||||
/*
|
||||
* Enumerator codes in the second column must not change -- they are part of
|
||||
* the JS XDR API.
|
||||
*/
|
||||
JS_PROTO(Null, 0, js_InitNullClass)
|
||||
JS_PROTO(Object, 1, js_InitFunctionAndObjectClasses)
|
||||
JS_PROTO(Function, 2, js_InitFunctionAndObjectClasses)
|
||||
JS_PROTO(Array, 3, js_InitArrayClass)
|
||||
JS_PROTO(Boolean, 4, js_InitBooleanClass)
|
||||
JS_PROTO(Call, 5, js_InitCallClass)
|
||||
JS_PROTO(Date, 6, js_InitDateClass)
|
||||
JS_PROTO(Math, 7, js_InitMathClass)
|
||||
JS_PROTO(Number, 8, js_InitNumberClass)
|
||||
JS_PROTO(String, 9, js_InitStringClass)
|
||||
JS_PROTO(RegExp, 10, js_InitRegExpClass)
|
||||
#if JS_HAS_SCRIPT_OBJECT
|
||||
JS_PROTO(Script, 11, js_InitScriptClass)
|
||||
#endif
|
||||
#if JS_HAS_XML_SUPPORT
|
||||
JS_PROTO(XML, 12, js_InitXMLClass)
|
||||
JS_PROTO(Namespace, 13, js_InitNamespaceClass)
|
||||
JS_PROTO(QName, 14, js_InitQNameClass)
|
||||
JS_PROTO(AnyName, 15, js_InitAnyNameClass)
|
||||
JS_PROTO(AttributeName, 16, js_InitAttributeNameClass)
|
||||
#endif
|
||||
JS_PROTO(Error, 17, js_InitExceptionClasses)
|
||||
JS_PROTO(InternalError, 18, js_InitExceptionClasses)
|
||||
JS_PROTO(EvalError, 19, js_InitExceptionClasses)
|
||||
JS_PROTO(RangeError, 20, js_InitExceptionClasses)
|
||||
JS_PROTO(ReferenceError, 21, js_InitExceptionClasses)
|
||||
JS_PROTO(SyntaxError, 22, js_InitExceptionClasses)
|
||||
JS_PROTO(TypeError, 23, js_InitExceptionClasses)
|
||||
JS_PROTO(URIError, 24, js_InitExceptionClasses)
|
||||
#if JS_HAS_GENERATORS
|
||||
JS_PROTO(Generator, 25, js_InitIteratorClasses)
|
||||
#endif
|
||||
JS_PROTO(Iterator, 26, js_InitIteratorClasses)
|
||||
JS_PROTO(StopIteration, 27, js_InitIteratorClasses)
|
||||
|
@ -92,7 +92,7 @@ typedef enum JSType {
|
||||
|
||||
/* Dense index into cached prototypes and class atoms for standard objects. */
|
||||
typedef enum JSProtoKey {
|
||||
#define JS_PROTO(name,init) JSProto_##name,
|
||||
#define JS_PROTO(name,code,init) JSProto_##name = code,
|
||||
#include "jsproto.tbl"
|
||||
#undef JS_PROTO
|
||||
JSProto_LIMIT
|
||||
|
@ -4109,7 +4109,7 @@ js_NewRegExpObject(JSContext *cx, JSTokenStream *ts,
|
||||
re = js_NewRegExp(cx, ts, str, flags, JS_FALSE);
|
||||
if (!re)
|
||||
return NULL;
|
||||
JS_PUSH_SINGLE_TEMP_ROOT(cx, STRING_TO_JSVAL(str), &tvr);
|
||||
JS_PUSH_SINGLE_TEMP_ROOT(cx, str, &tvr);
|
||||
obj = js_NewObject(cx, &js_RegExpClass, NULL, NULL);
|
||||
if (!obj || !JS_SetPrivate(cx, obj, re)) {
|
||||
js_DestroyRegExp(cx, re);
|
||||
|
@ -74,8 +74,6 @@
|
||||
#include "jsxml.h"
|
||||
#endif
|
||||
|
||||
#define MAX_KEYWORD_LENGTH 12
|
||||
|
||||
#define JS_KEYWORD(keyword, type, op, version) \
|
||||
const char js_##keyword##_str[] = #keyword;
|
||||
#include "jskeyword.tbl"
|
||||
@ -134,11 +132,14 @@ FindKeyword(const jschar *s, size_t length)
|
||||
return NULL;
|
||||
}
|
||||
|
||||
JSBool
|
||||
js_IsKeyword(const jschar *str, size_t length)
|
||||
JSTokenType
|
||||
js_CheckKeyword(const jschar *str, size_t length)
|
||||
{
|
||||
const struct keyword *kw;
|
||||
|
||||
JS_ASSERT(length != 0);
|
||||
return FindKeyword(str, length) != NULL;
|
||||
kw = FindKeyword(str, length);
|
||||
return kw ? kw->tokentype : TOK_EOF;
|
||||
}
|
||||
|
||||
JS_FRIEND_API(void)
|
||||
@ -583,8 +584,8 @@ ReportCompileErrorNumber(JSContext *cx, void *handle, uintN flags,
|
||||
jschar),
|
||||
0);
|
||||
report->linebuf = linestr
|
||||
? JS_GetStringBytes(linestr)
|
||||
: NULL;
|
||||
? JS_GetStringBytes(linestr)
|
||||
: NULL;
|
||||
tp = &ts->tokens[(ts->cursor+ts->lookahead) & NTOKENS_MASK].pos;
|
||||
#if JS_HAS_XML_SUPPORT
|
||||
if (pn)
|
||||
@ -1282,23 +1283,20 @@ retry:
|
||||
}
|
||||
UngetChar(ts, c);
|
||||
|
||||
/*
|
||||
* Check for keywords unless we saw Unicode escape or parser asks
|
||||
* to ignore keywords.
|
||||
*/
|
||||
if (!hadUnicodeEscape &&
|
||||
!(ts->flags & TSF_KEYWORD_IS_NAME) &&
|
||||
(kw = FindKeyword(TOKENBUF_BASE(), TOKENBUF_LENGTH()))) {
|
||||
if (kw->tokentype == TOK_RESERVED) {
|
||||
char buf[MAX_KEYWORD_LENGTH + 1];
|
||||
size_t buflen = sizeof(buf) - 1;
|
||||
if (!js_DeflateStringToBuffer(cx,
|
||||
TOKENBUF_BASE(),
|
||||
TOKENBUF_LENGTH(),
|
||||
buf, &buflen)) {
|
||||
goto error;
|
||||
}
|
||||
buf [buflen] = 0;
|
||||
if (!js_ReportCompileErrorNumber(cx, ts,
|
||||
JSREPORT_TS |
|
||||
JSREPORT_WARNING |
|
||||
JSREPORT_STRICT,
|
||||
JSMSG_RESERVED_ID, buf)) {
|
||||
JSMSG_RESERVED_ID,
|
||||
kw->chars)) {
|
||||
goto error;
|
||||
}
|
||||
} else if (JS_VERSION_IS_ECMA(cx) ||
|
||||
|
@ -50,6 +50,11 @@
|
||||
|
||||
JS_BEGIN_EXTERN_C
|
||||
|
||||
#define JS_KEYWORD(keyword, type, op, version) \
|
||||
extern const char js_##keyword##_str[];
|
||||
#include "jskeyword.tbl"
|
||||
#undef JS_KEYWORD
|
||||
|
||||
typedef enum JSTokenType {
|
||||
TOK_ERROR = -1, /* well-known as the only code < EOF */
|
||||
TOK_EOF = 0, /* end of file */
|
||||
@ -125,6 +130,10 @@ typedef enum JSTokenType {
|
||||
TOK_FILTER = 76, /* XML filtering predicate op (.()) */
|
||||
TOK_XMLELEM = 77, /* XML element node type (no token) */
|
||||
TOK_XMLLIST = 78, /* XML list node type (no token) */
|
||||
TOK_YIELD = 79, /* yield from generator function */
|
||||
TOK_ARRAYCOMP = 80, /* array comprehension initialiser */
|
||||
TOK_ARRAYPUSH = 81, /* array push within comprehension */
|
||||
TOK_LEXICALSCOPE = 82, /* block scope AST node label */
|
||||
TOK_RESERVED, /* reserved keywords */
|
||||
TOK_LIMIT /* domain size */
|
||||
} JSTokenType;
|
||||
@ -272,6 +281,9 @@ struct JSTokenStream {
|
||||
*/
|
||||
#define TSF_IN_HTML_COMMENT 0x2000
|
||||
|
||||
/* Ignore keywords and return TOK_NAME instead to the parser. */
|
||||
#define TSF_KEYWORD_IS_NAME 0x4000
|
||||
|
||||
/* Unicode separators that are treated as line terminators, in addition to \n, \r */
|
||||
#define LINE_SEPARATOR 0x2028
|
||||
#define PARA_SEPARATOR 0x2029
|
||||
@ -301,10 +313,14 @@ extern JS_FRIEND_API(int)
|
||||
js_fgets(char *buf, int size, FILE *file);
|
||||
|
||||
/*
|
||||
* Return true if the given char array forms JavaScript keyword.
|
||||
* If the given char array forms JavaScript keyword, return corresponding
|
||||
* token. Otherwise return TOK_EOF.
|
||||
*/
|
||||
extern JSBool
|
||||
js_IsKeyword(const jschar *str, size_t length);
|
||||
extern JSTokenType
|
||||
js_CheckKeyword(const jschar *chars, size_t length);
|
||||
|
||||
#define js_IsKeyword(chars, length) \
|
||||
(js_CheckKeyword(chars, length) != TOK_EOF)
|
||||
|
||||
/*
|
||||
* Friend-exported API entry point to call a mapping function on each reserved
|
||||
|
@ -194,8 +194,12 @@ script_compile(JSContext *cx, JSObject *obj, uintN argc, jsval *argv,
|
||||
argv[1] = OBJECT_TO_JSVAL(scopeobj);
|
||||
}
|
||||
if (caller) {
|
||||
if (!scopeobj)
|
||||
scopeobj = caller->scopeChain;
|
||||
if (!scopeobj) {
|
||||
scopeobj = js_GetScopeChain(cx, caller);
|
||||
if (!scopeobj)
|
||||
return JS_FALSE;
|
||||
fp->scopeChain = scopeobj; /* for the compiler's benefit */
|
||||
}
|
||||
|
||||
file = caller->script->filename;
|
||||
line = js_PCToLineNumber(cx, caller->script, caller->pc);
|
||||
@ -295,7 +299,9 @@ script_exec(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
|
||||
* Load caller->scopeChain after the conditional js_GetCallObject
|
||||
* call above, which resets scopeChain as well as varobj.
|
||||
*/
|
||||
scopeobj = caller->scopeChain;
|
||||
scopeobj = js_GetScopeChain(cx, caller);
|
||||
if (!scopeobj)
|
||||
return JS_FALSE;
|
||||
} else {
|
||||
/*
|
||||
* Called from native code, so we don't know what scope object to
|
||||
@ -847,6 +853,12 @@ script_mark(JSContext *cx, JSObject *obj, void *arg)
|
||||
return 0;
|
||||
}
|
||||
|
||||
#if !JS_HAS_SCRIPT_OBJECT
|
||||
const char js_Script_str[] = "Script";
|
||||
|
||||
#define JSProto_Script JSProto_Object
|
||||
#endif
|
||||
|
||||
JS_FRIEND_DATA(JSClass) js_ScriptClass = {
|
||||
js_Script_str,
|
||||
JSCLASS_HAS_PRIVATE | JSCLASS_HAS_CACHED_PROTO(JSProto_Script),
|
||||
|
@ -2646,12 +2646,12 @@ js_StringToObject(JSContext *cx, JSString *str)
|
||||
}
|
||||
|
||||
JS_FRIEND_API(const char *)
|
||||
js_ValueToPrintableString(JSContext *cx, jsval v)
|
||||
js_ValueToPrintable(JSContext *cx, jsval v, JSValueToStringFun v2sfun)
|
||||
{
|
||||
JSString *str;
|
||||
const char *bytes;
|
||||
|
||||
str = js_ValueToString(cx, v);
|
||||
str = v2sfun(cx, v);
|
||||
if (!str)
|
||||
return NULL;
|
||||
str = js_QuoteString(cx, str, 0);
|
||||
@ -2663,7 +2663,7 @@ js_ValueToPrintableString(JSContext *cx, jsval v)
|
||||
return bytes;
|
||||
}
|
||||
|
||||
JSString *
|
||||
JS_FRIEND_API(JSString *)
|
||||
js_ValueToString(JSContext *cx, jsval v)
|
||||
{
|
||||
JSObject *obj;
|
||||
@ -2690,7 +2690,7 @@ js_ValueToString(JSContext *cx, jsval v)
|
||||
return str;
|
||||
}
|
||||
|
||||
JSString *
|
||||
JS_FRIEND_API(JSString *)
|
||||
js_ValueToSource(JSContext *cx, jsval v)
|
||||
{
|
||||
if (JSVAL_IS_STRING(v))
|
||||
|
@ -345,21 +345,29 @@ js_StringToObject(JSContext *cx, JSString *str);
|
||||
/*
|
||||
* Convert a value to a printable C string.
|
||||
*/
|
||||
typedef JSString *(*JSValueToStringFun)(JSContext *cx, jsval v);
|
||||
|
||||
extern JS_FRIEND_API(const char *)
|
||||
js_ValueToPrintableString(JSContext *cx, jsval v);
|
||||
js_ValueToPrintable(JSContext *cx, jsval v, JSValueToStringFun v2sfun);
|
||||
|
||||
#define js_ValueToPrintableString(cx,v) \
|
||||
js_ValueToPrintable(cx, v, js_ValueToString)
|
||||
|
||||
#define js_ValueToPrintableSource(cx,v) \
|
||||
js_ValueToPrintable(cx, v, js_ValueToSource)
|
||||
|
||||
/*
|
||||
* Convert a value to a string, returning null after reporting an error,
|
||||
* otherwise returning a new string reference.
|
||||
*/
|
||||
extern JSString *
|
||||
extern JS_FRIEND_API(JSString *)
|
||||
js_ValueToString(JSContext *cx, jsval v);
|
||||
|
||||
/*
|
||||
* Convert a value to its source expression, returning null after reporting
|
||||
* an error, otherwise returning a new string reference.
|
||||
*/
|
||||
extern JSString *
|
||||
extern JS_FRIEND_API(JSString *)
|
||||
js_ValueToSource(JSContext *cx, jsval v);
|
||||
|
||||
#ifdef HT_ENUMERATE_NEXT /* XXX don't require jshash.h */
|
||||
|
@ -3012,12 +3012,7 @@ ToAttributeName(JSContext *cx, jsval v)
|
||||
if (!qn)
|
||||
return NULL;
|
||||
|
||||
/*
|
||||
* Temp and local root scope APIs take GC-thing pointers tagged as jsvals
|
||||
* and blindly untag. Since qn is a GC-thing pointer, we can treat it as
|
||||
* an object pointer.
|
||||
*/
|
||||
JS_PUSH_SINGLE_TEMP_ROOT(cx, OBJECT_TO_JSVAL(qn), &tvr);
|
||||
JS_PUSH_SINGLE_TEMP_ROOT(cx, qn, &tvr);
|
||||
obj = js_GetAttributeNameObject(cx, qn);
|
||||
JS_POP_TEMP_ROOT(cx, &tvr);
|
||||
if (!obj)
|
||||
@ -7341,7 +7336,7 @@ js_NewXMLObject(JSContext *cx, JSXMLClass xml_class)
|
||||
xml = js_NewXML(cx, xml_class);
|
||||
if (!xml)
|
||||
return NULL;
|
||||
JS_PUSH_SINGLE_TEMP_ROOT(cx, OBJECT_TO_JSVAL(xml), &tvr);
|
||||
JS_PUSH_SINGLE_TEMP_ROOT(cx, xml, &tvr);
|
||||
obj = js_GetXMLObject(cx, xml);
|
||||
JS_POP_TEMP_ROOT(cx, &tvr);
|
||||
return obj;
|
||||
@ -7940,7 +7935,10 @@ js_FilterXMLList(JSContext *cx, JSObject *obj, jsbytecode *pc, jsval *vp)
|
||||
/* All control flow after this point must exit via label out or bad. */
|
||||
*vp = JSVAL_NULL;
|
||||
fp = cx->fp;
|
||||
scobj = fp->scopeChain;
|
||||
fp->flags |= JSFRAME_FILTERING;
|
||||
scobj = js_GetScopeChain(cx, fp);
|
||||
if (!scobj)
|
||||
goto bad;
|
||||
xml = GetPrivate(cx, obj, "filtering predicate operator");
|
||||
if (!xml)
|
||||
goto bad;
|
||||
@ -7963,7 +7961,7 @@ js_FilterXMLList(JSContext *cx, JSObject *obj, jsbytecode *pc, jsval *vp)
|
||||
result = (JSXML *) JS_GetPrivate(cx, resobj);
|
||||
|
||||
/* Hoist the scope chain update out of the loop over kids. */
|
||||
withobj = js_NewObject(cx, &js_WithClass, NULL, scobj);
|
||||
withobj = js_NewWithObject(cx, NULL, scobj, -1);
|
||||
if (!withobj)
|
||||
goto bad;
|
||||
fp->scopeChain = withobj;
|
||||
@ -7987,6 +7985,7 @@ js_FilterXMLList(JSContext *cx, JSObject *obj, jsbytecode *pc, jsval *vp)
|
||||
*vp = OBJECT_TO_JSVAL(resobj);
|
||||
|
||||
out:
|
||||
fp->flags &= ~JSFRAME_FILTERING;
|
||||
fp->scopeChain = scobj;
|
||||
js_LeaveLocalRootScopeWithResult(cx, *vp);
|
||||
return ok;
|
||||
|
Loading…
x
Reference in New Issue
Block a user