First big wave of js1.7 changes (326466, 336376, r=mrbkap).

This commit is contained in:
brendan%mozilla.org 2006-05-20 22:27:28 +00:00
parent 229cb1758f
commit bcf6aea253
39 changed files with 2397 additions and 962 deletions

View File

@ -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 \

View File

@ -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 \

View File

@ -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,

View File

@ -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")

View File

@ -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
}

View File

@ -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)

View File

@ -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);

View File

@ -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[];

View File

@ -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)
{

View File

@ -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.

View File

@ -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

View File

@ -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;
}

View File

@ -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(&notes[totalCount]);
#ifdef DEBUG_brendan
#ifdef DEBUG_notme
{ int bin = JS_CeilingLog2(totalCount);
if (bin >= NBINS)
bin = NBINS - 1;

View File

@ -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.

View File

@ -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);
}
}

View File

@ -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);

View File

@ -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

View File

@ -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

View File

@ -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))

View File

@ -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;

View File

@ -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

View File

@ -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);

View File

@ -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);

View File

@ -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);
}

View File

@ -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 */

View File

@ -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)

View File

@ -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)

View File

@ -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 */

View File

@ -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

View File

@ -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)

View File

@ -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

View File

@ -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);

View File

@ -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) ||

View File

@ -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

View File

@ -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),

View File

@ -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))

View File

@ -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 */

View File

@ -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;