1 |
/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- |
/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- |
2 |
* vim: set ts=8 sw=4 et tw=78: |
* vim: set ts=8 sw=4 et tw=79: |
3 |
* |
* |
4 |
* ***** BEGIN LICENSE BLOCK ***** |
* ***** BEGIN LICENSE BLOCK ***** |
5 |
* Version: MPL 1.1/GPL 2.0/LGPL 2.1 |
* Version: MPL 1.1/GPL 2.0/LGPL 2.1 |
69 |
#include "jsparse.h" |
#include "jsparse.h" |
70 |
#include "jsscope.h" |
#include "jsscope.h" |
71 |
#include "jsscript.h" |
#include "jsscript.h" |
|
#include "jsstr.h" |
|
|
#include "jsdbgapi.h" /* whether or not JS_HAS_OBJ_WATCHPOINT */ |
|
72 |
#include "jsstaticcheck.h" |
#include "jsstaticcheck.h" |
73 |
|
#include "jsstr.h" |
74 |
|
#include "jstracer.h" |
75 |
|
#include "jsdbgapi.h" |
76 |
|
|
77 |
#if JS_HAS_GENERATORS |
#if JS_HAS_GENERATORS |
78 |
#include "jsiter.h" |
#include "jsiter.h" |
102 |
#endif |
#endif |
103 |
|
|
104 |
JS_FRIEND_DATA(JSObjectOps) js_ObjectOps = { |
JS_FRIEND_DATA(JSObjectOps) js_ObjectOps = { |
105 |
js_NewObjectMap, js_DestroyObjectMap, |
NULL, |
106 |
js_LookupProperty, js_DefineProperty, |
js_LookupProperty, js_DefineProperty, |
107 |
js_GetProperty, js_SetProperty, |
js_GetProperty, js_SetProperty, |
108 |
js_GetAttributes, js_SetAttributes, |
js_GetAttributes, js_SetAttributes, |
110 |
js_Enumerate, js_CheckAccess, |
js_Enumerate, js_CheckAccess, |
111 |
NULL, NATIVE_DROP_PROPERTY, |
NULL, NATIVE_DROP_PROPERTY, |
112 |
js_Call, js_Construct, |
js_Call, js_Construct, |
113 |
NULL, js_HasInstance, |
js_HasInstance, js_TraceObject, |
114 |
js_SetProtoOrParent, js_SetProtoOrParent, |
js_Clear, js_GetRequiredSlot, |
115 |
js_TraceObject, js_Clear, |
js_SetRequiredSlot |
|
js_GetRequiredSlot, js_SetRequiredSlot |
|
116 |
}; |
}; |
117 |
|
|
118 |
JSClass js_ObjectClass = { |
JSClass js_ObjectClass = { |
169 |
uintN attrs; |
uintN attrs; |
170 |
JSObject *pobj; |
JSObject *pobj; |
171 |
JSClass *clasp; |
JSClass *clasp; |
|
JSExtendedClass *xclasp; |
|
172 |
|
|
173 |
slot = (uint32) JSVAL_TO_INT(id); |
slot = (uint32) JSVAL_TO_INT(id); |
174 |
if (id == INT_TO_JSVAL(JSSLOT_PROTO)) { |
if (id == INT_TO_JSVAL(JSSLOT_PROTO)) { |
189 |
if (clasp == &js_CallClass || clasp == &js_BlockClass) { |
if (clasp == &js_CallClass || clasp == &js_BlockClass) { |
190 |
/* Censor activations and lexical scopes per ECMA-262. */ |
/* Censor activations and lexical scopes per ECMA-262. */ |
191 |
*vp = JSVAL_NULL; |
*vp = JSVAL_NULL; |
192 |
} else if (clasp->flags & JSCLASS_IS_EXTENDED) { |
} else { |
193 |
xclasp = (JSExtendedClass *) clasp; |
/* |
194 |
if (xclasp->outerObject) { |
* DeclEnv only exists as a parent for a Call object which we |
195 |
pobj = xclasp->outerObject(cx, pobj); |
* censor. So it cannot escape to scripts. |
196 |
|
*/ |
197 |
|
JS_ASSERT(clasp != &js_DeclEnvClass); |
198 |
|
if (pobj->map->ops->thisObject) { |
199 |
|
pobj = pobj->map->ops->thisObject(cx, pobj); |
200 |
if (!pobj) |
if (!pobj) |
201 |
return JS_FALSE; |
return JS_FALSE; |
202 |
*vp = OBJECT_TO_JSVAL(pobj); |
*vp = OBJECT_TO_JSVAL(pobj); |
240 |
return JS_FALSE; |
return JS_FALSE; |
241 |
} |
} |
242 |
|
|
243 |
return js_SetProtoOrParent(cx, obj, slot, pobj); |
return js_SetProtoOrParent(cx, obj, slot, pobj, JS_TRUE); |
244 |
} |
} |
245 |
|
|
246 |
static JSBool |
static JSBool |
279 |
#endif /* !JS_HAS_OBJ_PROTO_PROP */ |
#endif /* !JS_HAS_OBJ_PROTO_PROP */ |
280 |
|
|
281 |
JSBool |
JSBool |
282 |
js_SetProtoOrParent(JSContext *cx, JSObject *obj, uint32 slot, JSObject *pobj) |
js_SetProtoOrParent(JSContext *cx, JSObject *obj, uint32 slot, JSObject *pobj, |
283 |
|
JSBool checkForCycles) |
284 |
{ |
{ |
285 |
JSSetSlotRequest ssr; |
JS_ASSERT(slot == JSSLOT_PARENT || slot == JSSLOT_PROTO); |
286 |
JSRuntime *rt; |
JS_ASSERT_IF(!checkForCycles, obj != pobj); |
287 |
|
|
288 |
/* Optimize the null case to avoid the unnecessary overhead of js_GC. */ |
if (slot == JSSLOT_PROTO) { |
289 |
if (!pobj) { |
if (OBJ_IS_NATIVE(obj)) { |
290 |
JS_LOCK_OBJ(cx, obj); |
JS_LOCK_OBJ(cx, obj); |
291 |
if (slot == JSSLOT_PROTO && !js_GetMutableScope(cx, obj)) { |
bool ok = !!js_GetMutableScope(cx, obj); |
292 |
JS_UNLOCK_OBJ(cx, obj); |
JS_UNLOCK_OBJ(cx, obj); |
293 |
return JS_FALSE; |
if (!ok) |
294 |
|
return JS_FALSE; |
295 |
|
} |
296 |
|
|
297 |
|
/* |
298 |
|
* Regenerate property cache shape ids for all of the scopes along the |
299 |
|
* old prototype chain to invalidate their property cache entries, in |
300 |
|
* case any entries were filled by looking up starting from obj. |
301 |
|
*/ |
302 |
|
JSObject *oldproto = obj; |
303 |
|
while (oldproto && OBJ_IS_NATIVE(oldproto)) { |
304 |
|
JS_LOCK_OBJ(cx, oldproto); |
305 |
|
JSScope *scope = OBJ_SCOPE(oldproto); |
306 |
|
js_MakeScopeShapeUnique(cx, scope); |
307 |
|
JSObject *tmp = STOBJ_GET_PROTO(scope->object); |
308 |
|
JS_UNLOCK_OBJ(cx, oldproto); |
309 |
|
oldproto = tmp; |
310 |
} |
} |
|
LOCKED_OBJ_SET_SLOT(obj, slot, JSVAL_NULL); |
|
|
JS_UNLOCK_OBJ(cx, obj); |
|
|
return JS_TRUE; |
|
311 |
} |
} |
312 |
|
|
313 |
ssr.obj = obj; |
if (!pobj || !checkForCycles) { |
314 |
ssr.pobj = pobj; |
if (slot == JSSLOT_PROTO) |
315 |
ssr.slot = (uint16) slot; |
STOBJ_SET_PROTO(obj, pobj); |
316 |
ssr.errnum = (uint16) JSMSG_NOT_AN_ERROR; |
else |
317 |
|
STOBJ_SET_PARENT(obj, pobj); |
318 |
rt = cx->runtime; |
} else { |
319 |
JS_LOCK_GC(rt); |
/* |
320 |
ssr.next = rt->setSlotRequests; |
* Use the GC machinery to serialize access to all objects on the |
321 |
rt->setSlotRequests = &ssr; |
* prototype or parent chain. |
322 |
for (;;) { |
*/ |
323 |
js_GC(cx, GC_SET_SLOT_REQUEST); |
JSSetSlotRequest ssr; |
324 |
JS_UNLOCK_GC(rt); |
ssr.obj = obj; |
325 |
if (!rt->setSlotRequests) |
ssr.pobj = pobj; |
326 |
break; |
ssr.slot = (uint16) slot; |
327 |
|
ssr.cycle = false; |
328 |
|
|
329 |
|
JSRuntime *rt = cx->runtime; |
330 |
JS_LOCK_GC(rt); |
JS_LOCK_GC(rt); |
331 |
} |
ssr.next = rt->setSlotRequests; |
332 |
|
rt->setSlotRequests = &ssr; |
333 |
|
for (;;) { |
334 |
|
js_GC(cx, GC_SET_SLOT_REQUEST); |
335 |
|
JS_UNLOCK_GC(rt); |
336 |
|
if (!rt->setSlotRequests) |
337 |
|
break; |
338 |
|
JS_LOCK_GC(rt); |
339 |
|
} |
340 |
|
|
341 |
if (ssr.errnum != JSMSG_NOT_AN_ERROR) { |
if (ssr.cycle) { |
342 |
if (ssr.errnum == JSMSG_OUT_OF_MEMORY) { |
JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, |
343 |
JS_ReportOutOfMemory(cx); |
JSMSG_CYCLIC_VALUE, |
|
} else { |
|
|
JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, ssr.errnum, |
|
344 |
#if JS_HAS_OBJ_PROTO_PROP |
#if JS_HAS_OBJ_PROTO_PROP |
345 |
object_props[slot].name |
object_props[slot].name |
346 |
#else |
#else |
348 |
: js_parent_str |
: js_parent_str |
349 |
#endif |
#endif |
350 |
); |
); |
351 |
|
return JS_FALSE; |
352 |
} |
} |
|
return JS_FALSE; |
|
|
} |
|
|
|
|
|
// Maintain the "any Array prototype has indexed properties hazard" flag. |
|
|
if (slot == JSSLOT_PROTO && |
|
|
OBJ_IS_ARRAY(cx, pobj) && |
|
|
pobj->fslots[JSSLOT_ARRAY_LENGTH] != 0) { |
|
|
rt->anyArrayProtoHasElement = JS_TRUE; |
|
353 |
} |
} |
354 |
return JS_TRUE; |
return JS_TRUE; |
355 |
} |
} |
382 |
JS_CHECK_RECURSION(cx, return NULL); |
JS_CHECK_RECURSION(cx, return NULL); |
383 |
|
|
384 |
map = &cx->sharpObjectMap; |
map = &cx->sharpObjectMap; |
385 |
|
JS_ASSERT(map->depth >= 1); |
386 |
table = map->table; |
table = map->table; |
387 |
hash = js_hash_object(obj); |
hash = js_hash_object(obj); |
388 |
hep = JS_HashTableRawLookup(table, hash, obj); |
hep = JS_HashTableRawLookup(table, hash, obj); |
396 |
return NULL; |
return NULL; |
397 |
} |
} |
398 |
|
|
|
/* |
|
|
* Increment map->depth to protect js_EnterSharpObject from reentering |
|
|
* itself badly. Without this fix, if we reenter the basis case where |
|
|
* map->depth == 0, when unwinding the inner call we will destroy the |
|
|
* newly-created hash table and crash. |
|
|
*/ |
|
|
++map->depth; |
|
399 |
ida = JS_Enumerate(cx, obj); |
ida = JS_Enumerate(cx, obj); |
|
--map->depth; |
|
400 |
if (!ida) |
if (!ida) |
401 |
return NULL; |
return NULL; |
402 |
|
|
413 |
if (ok) { |
if (ok) { |
414 |
if (OBJ_IS_NATIVE(obj2) && |
if (OBJ_IS_NATIVE(obj2) && |
415 |
(attrs & (JSPROP_GETTER | JSPROP_SETTER))) { |
(attrs & (JSPROP_GETTER | JSPROP_SETTER))) { |
416 |
|
JSScopeProperty *sprop = (JSScopeProperty *) prop; |
417 |
val = JSVAL_NULL; |
val = JSVAL_NULL; |
418 |
if (attrs & JSPROP_GETTER) |
if (attrs & JSPROP_GETTER) |
419 |
val = (jsval) ((JSScopeProperty*)prop)->getter; |
val = js_CastAsObjectJSVal(sprop->getter); |
420 |
if (attrs & JSPROP_SETTER) { |
if (attrs & JSPROP_SETTER) { |
421 |
if (val != JSVAL_NULL) { |
if (val != JSVAL_NULL) { |
422 |
/* Mark the getter, then set val to setter. */ |
/* Mark the getter, then set val to setter. */ |
424 |
NULL) |
NULL) |
425 |
!= NULL); |
!= NULL); |
426 |
} |
} |
427 |
val = (jsval) ((JSScopeProperty*)prop)->setter; |
val = js_CastAsObjectJSVal(sprop->setter); |
428 |
} |
} |
429 |
} else { |
} else { |
430 |
ok = OBJ_GET_PROPERTY(cx, obj, id, &val); |
ok = OBJ_GET_PROPERTY(cx, obj, id, &val); |
472 |
char buf[20]; |
char buf[20]; |
473 |
size_t len; |
size_t len; |
474 |
|
|
475 |
if (!JS_CHECK_OPERATION_LIMIT(cx, JSOW_ENTER_SHARP)) |
if (!JS_CHECK_OPERATION_LIMIT(cx)) |
476 |
return NULL; |
return NULL; |
477 |
|
|
478 |
/* Set to null in case we return an early error. */ |
/* Set to null in case we return an early error. */ |
493 |
/* From this point the control must flow either through out: or bad:. */ |
/* From this point the control must flow either through out: or bad:. */ |
494 |
ida = NULL; |
ida = NULL; |
495 |
if (map->depth == 0) { |
if (map->depth == 0) { |
496 |
|
/* |
497 |
|
* Although MarkSharpObjects tries to avoid invoking getters, |
498 |
|
* it ends up doing so anyway under some circumstances; for |
499 |
|
* example, if a wrapped object has getters, the wrapper will |
500 |
|
* prevent MarkSharpObjects from recognizing them as such. |
501 |
|
* This could lead to js_LeaveSharpObject being called while |
502 |
|
* MarkSharpObjects is still working. |
503 |
|
* |
504 |
|
* Increment map->depth while we call MarkSharpObjects, to |
505 |
|
* ensure that such a call doesn't free the hash table we're |
506 |
|
* still using. |
507 |
|
*/ |
508 |
|
++map->depth; |
509 |
he = MarkSharpObjects(cx, obj, &ida); |
he = MarkSharpObjects(cx, obj, &ida); |
510 |
|
--map->depth; |
511 |
if (!he) |
if (!he) |
512 |
goto bad; |
goto bad; |
513 |
JS_ASSERT((JS_PTR_TO_UINT32(he->value) & SHARP_BIT) == 0); |
JS_ASSERT((JS_PTR_TO_UINT32(he->value) & SHARP_BIT) == 0); |
779 |
} |
} |
780 |
if (OBJ_IS_NATIVE(obj2) && |
if (OBJ_IS_NATIVE(obj2) && |
781 |
(attrs & (JSPROP_GETTER | JSPROP_SETTER))) { |
(attrs & (JSPROP_GETTER | JSPROP_SETTER))) { |
782 |
|
JSScopeProperty *sprop = (JSScopeProperty *) prop; |
783 |
if (attrs & JSPROP_GETTER) { |
if (attrs & JSPROP_GETTER) { |
784 |
val[valcnt] = (jsval) ((JSScopeProperty *)prop)->getter; |
val[valcnt] = js_CastAsObjectJSVal(sprop->getter); |
785 |
gsopold[valcnt] = |
gsopold[valcnt] = |
786 |
ATOM_TO_STRING(cx->runtime->atomState.getterAtom); |
ATOM_TO_STRING(cx->runtime->atomState.getterAtom); |
787 |
gsop[valcnt] = |
gsop[valcnt] = |
790 |
valcnt++; |
valcnt++; |
791 |
} |
} |
792 |
if (attrs & JSPROP_SETTER) { |
if (attrs & JSPROP_SETTER) { |
793 |
val[valcnt] = (jsval) ((JSScopeProperty *)prop)->setter; |
val[valcnt] = js_CastAsObjectJSVal(sprop->setter); |
794 |
gsopold[valcnt] = |
gsopold[valcnt] = |
795 |
ATOM_TO_STRING(cx->runtime->atomState.setterAtom); |
ATOM_TO_STRING(cx->runtime->atomState.setterAtom); |
796 |
gsop[valcnt] = |
gsop[valcnt] = |
1110 |
return !JSVAL_IS_NULL(*vp); |
return !JSVAL_IS_NULL(*vp); |
1111 |
} |
} |
1112 |
|
|
1113 |
|
#ifdef JS_TRACER |
1114 |
|
static jsval FASTCALL |
1115 |
|
Object_p_valueOf(JSContext* cx, JSObject* obj, JSString *hint) |
1116 |
|
{ |
1117 |
|
return OBJECT_TO_JSVAL(obj); |
1118 |
|
} |
1119 |
|
#endif |
1120 |
|
|
1121 |
/* |
/* |
1122 |
* Check whether principals subsumes scopeobj's principals, and return true |
* Check whether principals subsumes scopeobj's principals, and return true |
1123 |
* if so (or if scopeobj has no principals, for backward compatibility with |
* if so (or if scopeobj has no principals, for backward compatibility with |
1203 |
return principals->codebase; |
return principals->codebase; |
1204 |
} |
} |
1205 |
|
|
1206 |
if (caller->regs && *caller->regs->pc == JSOP_EVAL) { |
jsbytecode *pc = caller->regs->pc; |
1207 |
JS_ASSERT(caller->regs->pc[JSOP_EVAL_LENGTH] == JSOP_LINENO); |
if (caller->regs && js_GetOpcode(cx, caller->script, pc) == JSOP_EVAL) { |
1208 |
*linenop = GET_UINT16(caller->regs->pc + JSOP_EVAL_LENGTH); |
JS_ASSERT(js_GetOpcode(cx, caller->script, pc + JSOP_EVAL_LENGTH) == JSOP_LINENO); |
1209 |
|
*linenop = GET_UINT16(pc + JSOP_EVAL_LENGTH); |
1210 |
} else { |
} else { |
1211 |
*linenop = js_FramePCToLineNumber(cx, caller); |
*linenop = js_FramePCToLineNumber(cx, caller); |
1212 |
} |
} |
1213 |
return caller->script->filename; |
return caller->script->filename; |
1214 |
} |
} |
1215 |
|
|
1216 |
|
#ifndef EVAL_CACHE_CHAIN_LIMIT |
1217 |
|
# define EVAL_CACHE_CHAIN_LIMIT 4 |
1218 |
|
#endif |
1219 |
|
|
1220 |
|
static inline JSScript ** |
1221 |
|
EvalCacheHash(JSContext *cx, JSString *str) |
1222 |
|
{ |
1223 |
|
const jschar *s; |
1224 |
|
size_t n; |
1225 |
|
uint32 h; |
1226 |
|
|
1227 |
|
JSSTRING_CHARS_AND_LENGTH(str, s, n); |
1228 |
|
if (n > 100) |
1229 |
|
n = 100; |
1230 |
|
for (h = 0; n; s++, n--) |
1231 |
|
h = JS_ROTATE_LEFT32(h, 4) ^ *s; |
1232 |
|
|
1233 |
|
h *= JS_GOLDEN_RATIO; |
1234 |
|
h >>= 32 - JS_EVAL_CACHE_SHIFT; |
1235 |
|
return &JS_SCRIPTS_TO_GC(cx)[h]; |
1236 |
|
} |
1237 |
|
|
1238 |
static JSBool |
static JSBool |
1239 |
obj_eval(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) |
obj_eval(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) |
1240 |
{ |
{ |
1241 |
JSStackFrame *fp, *caller; |
JSStackFrame *fp, *caller; |
1242 |
JSBool indirectCall; |
JSBool indirectCall; |
1243 |
JSObject *scopeobj; |
uint32 tcflags; |
1244 |
JSString *str; |
JSPrincipals *principals; |
1245 |
const char *file; |
const char *file; |
1246 |
uintN line; |
uintN line; |
1247 |
JSPrincipals *principals; |
JSString *str; |
|
uint32 tcflags; |
|
1248 |
JSScript *script; |
JSScript *script; |
1249 |
JSBool ok; |
JSBool ok; |
1250 |
|
JSScript **bucket = NULL; /* avoid GCC warning with early decl&init */ |
1251 |
#if JS_HAS_EVAL_THIS_SCOPE |
#if JS_HAS_EVAL_THIS_SCOPE |
1252 |
JSObject *callerScopeChain = NULL, *callerVarObj = NULL; |
JSObject *callerScopeChain = NULL, *callerVarObj = NULL; |
1253 |
JSObject *setCallerScopeChain = NULL; |
JSObject *setCallerScopeChain = NULL; |
1254 |
JSBool setCallerVarObj = JS_FALSE; |
JSBool setCallerVarObj = JS_FALSE; |
1255 |
#endif |
#endif |
1256 |
|
|
1257 |
fp = cx->fp; |
fp = js_GetTopStackFrame(cx); |
1258 |
caller = JS_GetScriptedCaller(cx, fp); |
caller = js_GetScriptedCaller(cx, fp); |
1259 |
indirectCall = (caller && caller->regs && *caller->regs->pc != JSOP_EVAL); |
indirectCall = (caller && caller->regs && *caller->regs->pc != JSOP_EVAL); |
1260 |
|
|
1261 |
/* |
/* |
1262 |
|
* This call to js_GetWrappedObject is safe because of the security checks |
1263 |
|
* we do below. However, the control flow below is confusing, so we double |
1264 |
|
* check. There are two cases: |
1265 |
|
* - Direct call: This object is never used. So unwrapping can't hurt. |
1266 |
|
* - Indirect call: If this object isn't already the scope chain (which |
1267 |
|
* we're guaranteed to be allowed to access) then we do a security |
1268 |
|
* check. |
1269 |
|
*/ |
1270 |
|
obj = js_GetWrappedObject(cx, obj); |
1271 |
|
|
1272 |
|
/* |
1273 |
* Ban all indirect uses of eval (global.foo = eval; global.foo(...)) and |
* Ban all indirect uses of eval (global.foo = eval; global.foo(...)) and |
1274 |
* calls that attempt to use a non-global object as the "with" object in |
* calls that attempt to use a non-global object as the "with" object in |
1275 |
* the former indirect case. |
* the former indirect case. |
1276 |
*/ |
*/ |
1277 |
scopeobj = OBJ_GET_PARENT(cx, obj); |
{ |
1278 |
if (scopeobj) { |
JSObject *parent = OBJ_GET_PARENT(cx, obj); |
1279 |
scopeobj = js_GetWrappedObject(cx, obj); |
if (indirectCall || parent) { |
1280 |
scopeobj = OBJ_GET_PARENT(cx, scopeobj); |
uintN flags = parent |
1281 |
} |
? JSREPORT_ERROR |
1282 |
if (indirectCall || scopeobj) { |
: JSREPORT_STRICT | JSREPORT_WARNING; |
1283 |
uintN flags = scopeobj |
if (!JS_ReportErrorFlagsAndNumber(cx, flags, js_GetErrorMessage, NULL, |
1284 |
? JSREPORT_ERROR |
JSMSG_BAD_INDIRECT_CALL, |
1285 |
: JSREPORT_STRICT | JSREPORT_WARNING; |
js_eval_str)) { |
1286 |
if (!JS_ReportErrorFlagsAndNumber(cx, flags, js_GetErrorMessage, NULL, |
return JS_FALSE; |
1287 |
JSMSG_BAD_INDIRECT_CALL, |
} |
|
js_eval_str)) { |
|
|
return JS_FALSE; |
|
1288 |
} |
} |
1289 |
} |
} |
1290 |
|
|
1298 |
* object, then we need to provide one for the compiler to stick any |
* object, then we need to provide one for the compiler to stick any |
1299 |
* declared (var) variables into. |
* declared (var) variables into. |
1300 |
*/ |
*/ |
1301 |
if (caller && !caller->varobj && !js_GetCallObject(cx, caller, NULL)) |
if (caller && !caller->varobj && !js_GetCallObject(cx, caller)) |
1302 |
return JS_FALSE; |
return JS_FALSE; |
1303 |
|
|
1304 |
/* eval no longer takes an optional trailing argument. */ |
/* Accept an optional trailing argument that overrides the scope object. */ |
1305 |
if (argc >= 2 && |
JSObject *scopeobj = NULL; |
1306 |
!JS_ReportErrorFlagsAndNumber(cx, JSREPORT_WARNING | JSREPORT_STRICT, |
if (argc >= 2) { |
1307 |
js_GetErrorMessage, NULL, |
if (!js_ValueToObject(cx, argv[1], &scopeobj)) |
1308 |
JSMSG_EVAL_ARITY)) { |
return JS_FALSE; |
1309 |
return JS_FALSE; |
argv[1] = OBJECT_TO_JSVAL(scopeobj); |
1310 |
} |
} |
1311 |
|
|
1312 |
/* From here on, control must exit through label out with ok set. */ |
/* From here on, control must exit through label out with ok set. */ |
1327 |
} |
} |
1328 |
if (obj != callerScopeChain) { |
if (obj != callerScopeChain) { |
1329 |
ok = js_CheckPrincipalsAccess(cx, obj, |
ok = js_CheckPrincipalsAccess(cx, obj, |
1330 |
caller->script->principals, |
JS_StackFramePrincipals(cx, caller), |
1331 |
cx->runtime->atomState.evalAtom); |
cx->runtime->atomState.evalAtom); |
1332 |
if (!ok) |
if (!ok) |
1333 |
goto out; |
goto out; |
1367 |
goto out; |
goto out; |
1368 |
} |
} |
1369 |
} |
} |
1370 |
|
} else { |
1371 |
|
scopeobj = js_GetWrappedObject(cx, scopeobj); |
1372 |
|
OBJ_TO_INNER_OBJECT(cx, scopeobj); |
1373 |
|
if (!scopeobj) { |
1374 |
|
ok = JS_FALSE; |
1375 |
|
goto out; |
1376 |
|
} |
1377 |
|
ok = js_CheckPrincipalsAccess(cx, scopeobj, |
1378 |
|
JS_StackFramePrincipals(cx, caller), |
1379 |
|
cx->runtime->atomState.evalAtom); |
1380 |
|
if (!ok) |
1381 |
|
goto out; |
1382 |
|
|
1383 |
|
scopeobj = js_NewWithObject(cx, scopeobj, |
1384 |
|
JS_GetGlobalForObject(cx, scopeobj), -1); |
1385 |
|
if (!scopeobj) { |
1386 |
|
ok = JS_FALSE; |
1387 |
|
goto out; |
1388 |
|
} |
1389 |
|
argv[1] = OBJECT_TO_JSVAL(scopeobj); |
1390 |
} |
} |
1391 |
|
|
1392 |
/* Ensure we compile this eval with the right object in the scope chain. */ |
/* Ensure we compile this eval with the right object in the scope chain. */ |
1396 |
goto out; |
goto out; |
1397 |
} |
} |
1398 |
|
|
1399 |
str = JSVAL_TO_STRING(argv[0]); |
tcflags = TCF_COMPILE_N_GO; |
1400 |
if (caller) { |
if (caller) { |
1401 |
|
tcflags |= TCF_PUT_STATIC_LEVEL(caller->script->staticLevel + 1); |
1402 |
principals = JS_EvalFramePrincipals(cx, fp, caller); |
principals = JS_EvalFramePrincipals(cx, fp, caller); |
1403 |
file = js_ComputeFilename(cx, caller, principals, &line); |
file = js_ComputeFilename(cx, caller, principals, &line); |
1404 |
} else { |
} else { |
1405 |
|
principals = NULL; |
1406 |
file = NULL; |
file = NULL; |
1407 |
line = 0; |
line = 0; |
|
principals = NULL; |
|
1408 |
} |
} |
1409 |
|
|
1410 |
tcflags = TCF_COMPILE_N_GO; |
str = JSVAL_TO_STRING(argv[0]); |
1411 |
if (caller) |
script = NULL; |
1412 |
tcflags |= TCF_PUT_STATIC_DEPTH(caller->script->staticDepth + 1); |
|
1413 |
script = js_CompileScript(cx, scopeobj, caller, principals, tcflags, |
/* Cache local eval scripts indexed by source qualified by scope. */ |
1414 |
JSSTRING_CHARS(str), JSSTRING_LENGTH(str), |
bucket = EvalCacheHash(cx, str); |
1415 |
NULL, file, line); |
if (caller->fun) { |
1416 |
|
uintN count = 0; |
1417 |
|
JSScript **scriptp = bucket; |
1418 |
|
|
1419 |
|
EVAL_CACHE_METER(probe); |
1420 |
|
while ((script = *scriptp) != NULL) { |
1421 |
|
if ((script->flags & JSSF_SAVED_CALLER_FUN) && |
1422 |
|
script->version == cx->version && |
1423 |
|
(script->principals == principals || |
1424 |
|
(principals->subsume(principals, script->principals) && |
1425 |
|
script->principals->subsume(script->principals, principals)))) { |
1426 |
|
/* |
1427 |
|
* Get the prior (cache-filling) eval's saved caller function. |
1428 |
|
* See JSCompiler::compileScript in jsparse.cpp. |
1429 |
|
*/ |
1430 |
|
JSFunction *fun; |
1431 |
|
JS_GET_SCRIPT_FUNCTION(script, 0, fun); |
1432 |
|
|
1433 |
|
if (fun == caller->fun) { |
1434 |
|
/* |
1435 |
|
* Get the source string passed for safekeeping in the |
1436 |
|
* atom map by the prior eval to JSCompiler::compileScript. |
1437 |
|
*/ |
1438 |
|
JSString *src = ATOM_TO_STRING(script->atomMap.vector[0]); |
1439 |
|
|
1440 |
|
if (src == str || js_EqualStrings(src, str)) { |
1441 |
|
/* |
1442 |
|
* Source matches, qualify by comparing scopeobj to the |
1443 |
|
* COMPILE_N_GO-memoized parent of the first literal |
1444 |
|
* function or regexp object if any. If none, then this |
1445 |
|
* script has no compiled-in dependencies on the prior |
1446 |
|
* eval's scopeobj. |
1447 |
|
*/ |
1448 |
|
JSObjectArray *objarray = JS_SCRIPT_OBJECTS(script); |
1449 |
|
int i = 1; |
1450 |
|
if (objarray->length == 1) { |
1451 |
|
if (script->regexpsOffset != 0) { |
1452 |
|
objarray = JS_SCRIPT_REGEXPS(script); |
1453 |
|
i = 0; |
1454 |
|
} else { |
1455 |
|
EVAL_CACHE_METER(noscope); |
1456 |
|
i = -1; |
1457 |
|
} |
1458 |
|
} |
1459 |
|
if (i < 0 || |
1460 |
|
STOBJ_GET_PARENT(objarray->vector[i]) == scopeobj) { |
1461 |
|
EVAL_CACHE_METER(hit); |
1462 |
|
*scriptp = script->u.nextToGC; |
1463 |
|
script->u.nextToGC = NULL; |
1464 |
|
break; |
1465 |
|
} |
1466 |
|
} |
1467 |
|
} |
1468 |
|
} |
1469 |
|
|
1470 |
|
if (++count == EVAL_CACHE_CHAIN_LIMIT) { |
1471 |
|
script = NULL; |
1472 |
|
break; |
1473 |
|
} |
1474 |
|
EVAL_CACHE_METER(step); |
1475 |
|
scriptp = &script->u.nextToGC; |
1476 |
|
} |
1477 |
|
} |
1478 |
|
|
1479 |
if (!script) { |
if (!script) { |
1480 |
ok = JS_FALSE; |
script = JSCompiler::compileScript(cx, scopeobj, caller, principals, tcflags, |
1481 |
goto out; |
JSSTRING_CHARS(str), JSSTRING_LENGTH(str), |
1482 |
|
NULL, file, line, str); |
1483 |
|
if (!script) { |
1484 |
|
ok = JS_FALSE; |
1485 |
|
goto out; |
1486 |
|
} |
1487 |
} |
} |
1488 |
|
|
1489 |
if (argc < 2) { |
if (argc < 2) { |
1501 |
if (ok) |
if (ok) |
1502 |
ok = js_Execute(cx, scopeobj, script, caller, JSFRAME_EVAL, rval); |
ok = js_Execute(cx, scopeobj, script, caller, JSFRAME_EVAL, rval); |
1503 |
|
|
1504 |
script->u.nextToGC = JS_SCRIPTS_TO_GC(cx); |
script->u.nextToGC = *bucket; |
1505 |
JS_SCRIPTS_TO_GC(cx) = script; |
*bucket = script; |
1506 |
#ifdef CHECK_SCRIPT_OWNER |
#ifdef CHECK_SCRIPT_OWNER |
1507 |
script->owner = NULL; |
script->owner = NULL; |
1508 |
#endif |
#endif |
1542 |
callbacks = JS_GetSecurityCallbacks(cx); |
callbacks = JS_GetSecurityCallbacks(cx); |
1543 |
if (callbacks && callbacks->findObjectPrincipals) { |
if (callbacks && callbacks->findObjectPrincipals) { |
1544 |
/* Skip over any obj_watch_* frames between us and the real subject. */ |
/* Skip over any obj_watch_* frames between us and the real subject. */ |
1545 |
caller = JS_GetScriptedCaller(cx, cx->fp); |
caller = js_GetScriptedCaller(cx, NULL); |
1546 |
if (caller) { |
if (caller) { |
1547 |
/* |
/* |
1548 |
* Only call the watch handler if the watcher is allowed to watch |
* Only call the watch handler if the watcher is allowed to watch |
1712 |
} |
} |
1713 |
|
|
1714 |
#ifdef JS_TRACER |
#ifdef JS_TRACER |
1715 |
static int32 FASTCALL |
static JSBool FASTCALL |
1716 |
Object_p_hasOwnProperty(JSContext* cx, JSObject* obj, JSString *str) |
Object_p_hasOwnProperty(JSContext* cx, JSObject* obj, JSString *str) |
1717 |
{ |
{ |
1718 |
jsid id; |
jsid id; |
1719 |
jsval v; |
jsval v; |
1720 |
|
|
1721 |
if (!js_ValueToStringId(cx, STRING_TO_JSVAL(str), &id)) |
if (!js_ValueToStringId(cx, STRING_TO_JSVAL(str), &id) || |
1722 |
return JSVAL_TO_BOOLEAN(JSVAL_VOID); |
!js_HasOwnProperty(cx, obj->map->ops->lookupProperty, obj, id, &v)) { |
1723 |
if (!js_HasOwnProperty(cx, obj->map->ops->lookupProperty, obj, id, &v)) |
js_SetBuiltinError(cx); |
1724 |
return JSVAL_TO_BOOLEAN(JSVAL_VOID); |
return JSVAL_TO_BOOLEAN(JSVAL_VOID); |
1725 |
|
} |
1726 |
|
|
1727 |
JS_ASSERT(JSVAL_IS_BOOLEAN(v)); |
JS_ASSERT(JSVAL_IS_BOOLEAN(v)); |
1728 |
return JSVAL_TO_BOOLEAN(v); |
return JSVAL_TO_BOOLEAN(v); |
1729 |
} |
} |
1758 |
} |
} |
1759 |
|
|
1760 |
#ifdef JS_TRACER |
#ifdef JS_TRACER |
1761 |
static int32 FASTCALL |
static JSBool FASTCALL |
1762 |
Object_p_propertyIsEnumerable(JSContext* cx, JSObject* obj, JSString *str) |
Object_p_propertyIsEnumerable(JSContext* cx, JSObject* obj, JSString *str) |
1763 |
{ |
{ |
1764 |
jsid id = ATOM_TO_JSID(STRING_TO_JSVAL(str)); |
jsid id = ATOM_TO_JSID(STRING_TO_JSVAL(str)); |
1765 |
jsval v; |
jsval v; |
1766 |
if (!js_PropertyIsEnumerable(cx, obj, id, &v)) |
|
1767 |
|
if (!js_PropertyIsEnumerable(cx, obj, id, &v)) { |
1768 |
|
js_SetBuiltinError(cx); |
1769 |
return JSVAL_TO_BOOLEAN(JSVAL_VOID); |
return JSVAL_TO_BOOLEAN(JSVAL_VOID); |
1770 |
|
} |
1771 |
|
|
1772 |
JS_ASSERT(JSVAL_IS_BOOLEAN(v)); |
JS_ASSERT(JSVAL_IS_BOOLEAN(v)); |
1773 |
return JSVAL_TO_BOOLEAN(v); |
return JSVAL_TO_BOOLEAN(v); |
1774 |
} |
} |
1817 |
} |
} |
1818 |
|
|
1819 |
#if JS_HAS_GETTER_SETTER |
#if JS_HAS_GETTER_SETTER |
1820 |
static JSBool |
JS_FRIEND_API(JSBool) |
1821 |
obj_defineGetter(JSContext *cx, uintN argc, jsval *vp) |
js_obj_defineGetter(JSContext *cx, uintN argc, jsval *vp) |
1822 |
{ |
{ |
1823 |
jsval fval, junk; |
jsval fval, junk; |
1824 |
jsid id; |
jsid id; |
1846 |
return JS_FALSE; |
return JS_FALSE; |
1847 |
*vp = JSVAL_VOID; |
*vp = JSVAL_VOID; |
1848 |
return OBJ_DEFINE_PROPERTY(cx, obj, id, JSVAL_VOID, |
return OBJ_DEFINE_PROPERTY(cx, obj, id, JSVAL_VOID, |
1849 |
(JSPropertyOp) JSVAL_TO_OBJECT(fval), |
js_CastAsPropertyOp(JSVAL_TO_OBJECT(fval)), |
1850 |
JS_PropertyStub, |
JS_PropertyStub, |
1851 |
JSPROP_ENUMERATE | JSPROP_GETTER | JSPROP_SHARED, |
JSPROP_ENUMERATE | JSPROP_GETTER | JSPROP_SHARED, |
1852 |
NULL); |
NULL); |
1853 |
} |
} |
1854 |
|
|
1855 |
static JSBool |
JS_FRIEND_API(JSBool) |
1856 |
obj_defineSetter(JSContext *cx, uintN argc, jsval *vp) |
js_obj_defineSetter(JSContext *cx, uintN argc, jsval *vp) |
1857 |
{ |
{ |
1858 |
jsval fval, junk; |
jsval fval, junk; |
1859 |
jsid id; |
jsid id; |
1882 |
*vp = JSVAL_VOID; |
*vp = JSVAL_VOID; |
1883 |
return OBJ_DEFINE_PROPERTY(cx, obj, id, JSVAL_VOID, |
return OBJ_DEFINE_PROPERTY(cx, obj, id, JSVAL_VOID, |
1884 |
JS_PropertyStub, |
JS_PropertyStub, |
1885 |
(JSPropertyOp) JSVAL_TO_OBJECT(fval), |
js_CastAsPropertyOp(JSVAL_TO_OBJECT(fval)), |
1886 |
JSPROP_ENUMERATE | JSPROP_SETTER | JSPROP_SHARED, |
JSPROP_ENUMERATE | JSPROP_SETTER | JSPROP_SHARED, |
1887 |
NULL); |
NULL); |
1888 |
} |
} |
1905 |
if (OBJ_IS_NATIVE(pobj)) { |
if (OBJ_IS_NATIVE(pobj)) { |
1906 |
sprop = (JSScopeProperty *) prop; |
sprop = (JSScopeProperty *) prop; |
1907 |
if (sprop->attrs & JSPROP_GETTER) |
if (sprop->attrs & JSPROP_GETTER) |
1908 |
*vp = OBJECT_TO_JSVAL(sprop->getter); |
*vp = js_CastAsObjectJSVal(sprop->getter); |
1909 |
} |
} |
1910 |
OBJ_DROP_PROPERTY(cx, pobj, prop); |
OBJ_DROP_PROPERTY(cx, pobj, prop); |
1911 |
} |
} |
1930 |
if (OBJ_IS_NATIVE(pobj)) { |
if (OBJ_IS_NATIVE(pobj)) { |
1931 |
sprop = (JSScopeProperty *) prop; |
sprop = (JSScopeProperty *) prop; |
1932 |
if (sprop->attrs & JSPROP_SETTER) |
if (sprop->attrs & JSPROP_SETTER) |
1933 |
*vp = OBJECT_TO_JSVAL(sprop->setter); |
*vp = js_CastAsObjectJSVal(sprop->setter); |
1934 |
} |
} |
1935 |
OBJ_DROP_PROPERTY(cx, pobj, prop); |
OBJ_DROP_PROPERTY(cx, pobj, prop); |
1936 |
} |
} |
1973 |
const char js_lookupSetter_str[] = "__lookupSetter__"; |
const char js_lookupSetter_str[] = "__lookupSetter__"; |
1974 |
#endif |
#endif |
1975 |
|
|
1976 |
|
JS_DEFINE_TRCINFO_1(obj_valueOf, |
1977 |
|
(3, (static, JSVAL, Object_p_valueOf, CONTEXT, THIS, STRING, 0, 0))) |
1978 |
JS_DEFINE_TRCINFO_1(obj_hasOwnProperty, |
JS_DEFINE_TRCINFO_1(obj_hasOwnProperty, |
1979 |
(3, (static, BOOL_FAIL, Object_p_hasOwnProperty, CONTEXT, THIS, STRING, 0, 0))) |
(3, (static, BOOL_FAIL, Object_p_hasOwnProperty, CONTEXT, THIS, STRING, 0, 0))) |
1980 |
JS_DEFINE_TRCINFO_1(obj_propertyIsEnumerable, |
JS_DEFINE_TRCINFO_1(obj_propertyIsEnumerable, |
1981 |
(3, (static, BOOL_FAIL, Object_p_propertyIsEnumerable, CONTEXT, THIS, STRING, 0, 0))) |
(3, (static, BOOL_FAIL, Object_p_propertyIsEnumerable, CONTEXT, THIS, STRING, 0, 0))) |
1982 |
|
|
1983 |
static JSFunctionSpec object_methods[] = { |
static JSFunctionSpec object_methods[] = { |
1984 |
#if JS_HAS_TOSOURCE |
#if JS_HAS_TOSOURCE |
1986 |
#endif |
#endif |
1987 |
JS_FN(js_toString_str, obj_toString, 0,0), |
JS_FN(js_toString_str, obj_toString, 0,0), |
1988 |
JS_FN(js_toLocaleString_str, obj_toLocaleString, 0,0), |
JS_FN(js_toLocaleString_str, obj_toLocaleString, 0,0), |
1989 |
JS_FN(js_valueOf_str, obj_valueOf, 0,0), |
JS_TN(js_valueOf_str, obj_valueOf, 0,0, |
1990 |
|
obj_valueOf_trcinfo), |
1991 |
#if JS_HAS_OBJ_WATCHPOINT |
#if JS_HAS_OBJ_WATCHPOINT |
1992 |
JS_FN(js_watch_str, obj_watch, 2,0), |
JS_FN(js_watch_str, obj_watch, 2,0), |
1993 |
JS_FN(js_unwatch_str, obj_unwatch, 1,0), |
JS_FN(js_unwatch_str, obj_unwatch, 1,0), |
1998 |
JS_TN(js_propertyIsEnumerable_str, obj_propertyIsEnumerable, 1,0, |
JS_TN(js_propertyIsEnumerable_str, obj_propertyIsEnumerable, 1,0, |
1999 |
obj_propertyIsEnumerable_trcinfo), |
obj_propertyIsEnumerable_trcinfo), |
2000 |
#if JS_HAS_GETTER_SETTER |
#if JS_HAS_GETTER_SETTER |
2001 |
JS_FN(js_defineGetter_str, obj_defineGetter, 2,0), |
JS_FN(js_defineGetter_str, js_obj_defineGetter, 2,0), |
2002 |
JS_FN(js_defineSetter_str, obj_defineSetter, 2,0), |
JS_FN(js_defineSetter_str, js_obj_defineSetter, 2,0), |
2003 |
JS_FN(js_lookupGetter_str, obj_lookupGetter, 1,0), |
JS_FN(js_lookupGetter_str, obj_lookupGetter, 1,0), |
2004 |
JS_FN(js_lookupSetter_str, obj_lookupSetter, 1,0), |
JS_FN(js_lookupSetter_str, obj_lookupSetter, 1,0), |
2005 |
#endif |
#endif |
2024 |
} |
} |
2025 |
if (!obj) { |
if (!obj) { |
2026 |
JS_ASSERT(!argc || JSVAL_IS_NULL(argv[0]) || JSVAL_IS_VOID(argv[0])); |
JS_ASSERT(!argc || JSVAL_IS_NULL(argv[0]) || JSVAL_IS_VOID(argv[0])); |
2027 |
if (cx->fp->flags & JSFRAME_CONSTRUCTING) |
if (JS_IsConstructing(cx)) |
2028 |
return JS_TRUE; |
return JS_TRUE; |
2029 |
obj = js_NewObject(cx, &js_ObjectClass, NULL, NULL, 0); |
obj = js_NewObject(cx, &js_ObjectClass, NULL, NULL, 0); |
2030 |
if (!obj) |
if (!obj) |
2034 |
return JS_TRUE; |
return JS_TRUE; |
2035 |
} |
} |
2036 |
|
|
2037 |
|
static inline bool |
2038 |
|
InitScopeForObject(JSContext* cx, JSObject* obj, JSObject* proto, |
2039 |
|
JSObjectOps* ops) |
2040 |
|
{ |
2041 |
|
JS_ASSERT(OPS_IS_NATIVE(ops)); |
2042 |
|
JS_ASSERT(proto == OBJ_GET_PROTO(cx, obj)); |
2043 |
|
|
2044 |
|
JSClass* protoclasp; |
2045 |
|
JSClass* clasp = OBJ_GET_CLASS(cx, obj); |
2046 |
|
|
2047 |
|
/* |
2048 |
|
* Share proto's map only if it has the same JSObjectOps, and only if |
2049 |
|
* proto's class has the same private and reserved slots as obj's map |
2050 |
|
* and class have. We assume that if prototype and object are of the |
2051 |
|
* same class, they always have the same number of computed reserved |
2052 |
|
* slots (returned via clasp->reserveSlots); otherwise, prototype and |
2053 |
|
* object classes must have the same (null or not) reserveSlots hook. |
2054 |
|
*/ |
2055 |
|
if (proto && |
2056 |
|
proto->map->ops == ops && |
2057 |
|
((protoclasp = OBJ_GET_CLASS(cx, proto)) == clasp || |
2058 |
|
(!((protoclasp->flags ^ clasp->flags) & |
2059 |
|
(JSCLASS_HAS_PRIVATE | |
2060 |
|
(JSCLASS_RESERVED_SLOTS_MASK << JSCLASS_RESERVED_SLOTS_SHIFT))) && |
2061 |
|
protoclasp->reserveSlots == clasp->reserveSlots))) |
2062 |
|
{ |
2063 |
|
js_HoldScope(OBJ_SCOPE(proto)); |
2064 |
|
obj->map = proto->map; |
2065 |
|
return true; |
2066 |
|
} |
2067 |
|
|
2068 |
|
JSScope *scope = js_NewScope(cx, ops, clasp, obj); |
2069 |
|
if (!scope) |
2070 |
|
goto bad; |
2071 |
|
|
2072 |
|
/* Let js_NewScope set freeslot so as to reserve slots. */ |
2073 |
|
JS_ASSERT(scope->freeslot >= JSSLOT_PRIVATE); |
2074 |
|
if (scope->freeslot > JS_INITIAL_NSLOTS && |
2075 |
|
!js_ReallocSlots(cx, obj, scope->freeslot, JS_TRUE)) { |
2076 |
|
js_DestroyScope(cx, scope); |
2077 |
|
goto bad; |
2078 |
|
} |
2079 |
|
obj->map = &scope->map; |
2080 |
|
return true; |
2081 |
|
|
2082 |
|
bad: |
2083 |
|
/* Ensure that the map field is initialized for GC. */ |
2084 |
|
obj->map = NULL; |
2085 |
|
return false; |
2086 |
|
} |
2087 |
|
|
2088 |
|
#ifdef JS_TRACER |
2089 |
|
|
2090 |
|
static inline JSObject* |
2091 |
|
NewNativeObject(JSContext* cx, JSClass* clasp, JSObject* proto, JSObject *parent) |
2092 |
|
{ |
2093 |
|
JS_ASSERT(JS_ON_TRACE(cx)); |
2094 |
|
JSObject* obj = (JSObject*) js_NewGCThing(cx, GCX_OBJECT, sizeof(JSObject)); |
2095 |
|
if (!obj) |
2096 |
|
return NULL; |
2097 |
|
|
2098 |
|
obj->classword = jsuword(clasp); |
2099 |
|
obj->fslots[JSSLOT_PROTO] = OBJECT_TO_JSVAL(proto); |
2100 |
|
obj->fslots[JSSLOT_PARENT] = OBJECT_TO_JSVAL(parent); |
2101 |
|
for (unsigned i = JSSLOT_PRIVATE; i < JS_INITIAL_NSLOTS; ++i) |
2102 |
|
obj->fslots[i] = JSVAL_VOID; |
2103 |
|
|
2104 |
|
obj->dslots = NULL; |
2105 |
|
return InitScopeForObject(cx, obj, proto, &js_ObjectOps) ? obj : NULL; |
2106 |
|
} |
2107 |
|
|
2108 |
|
JSObject* FASTCALL |
2109 |
|
js_Object_tn(JSContext* cx, JSObject* proto) |
2110 |
|
{ |
2111 |
|
return NewNativeObject(cx, &js_ObjectClass, proto, JSVAL_TO_OBJECT(proto->fslots[JSSLOT_PARENT])); |
2112 |
|
} |
2113 |
|
|
2114 |
|
JS_DEFINE_TRCINFO_1(js_Object, |
2115 |
|
(2, (extern, CONSTRUCTOR_RETRY, js_Object_tn, CONTEXT, CALLEE_PROTOTYPE, 0, 0))) |
2116 |
|
|
2117 |
|
JSObject* FASTCALL |
2118 |
|
js_NewInstance(JSContext *cx, JSClass *clasp, JSObject *ctor) |
2119 |
|
{ |
2120 |
|
JS_ASSERT(HAS_FUNCTION_CLASS(ctor)); |
2121 |
|
|
2122 |
|
JSAtom *atom = cx->runtime->atomState.classPrototypeAtom; |
2123 |
|
|
2124 |
|
JSScope *scope = OBJ_SCOPE(ctor); |
2125 |
|
#ifdef JS_THREADSAFE |
2126 |
|
if (scope->title.ownercx != cx) |
2127 |
|
return NULL; |
2128 |
|
#endif |
2129 |
|
if (scope->object != ctor) { |
2130 |
|
scope = js_GetMutableScope(cx, ctor); |
2131 |
|
if (!scope) |
2132 |
|
return NULL; |
2133 |
|
} |
2134 |
|
|
2135 |
|
JSScopeProperty *sprop = SCOPE_GET_PROPERTY(scope, ATOM_TO_JSID(atom)); |
2136 |
|
jsval pval = sprop ? STOBJ_GET_SLOT(ctor, sprop->slot) : JSVAL_HOLE; |
2137 |
|
|
2138 |
|
JSObject *proto; |
2139 |
|
if (!JSVAL_IS_PRIMITIVE(pval)) { |
2140 |
|
/* An object in ctor.prototype, let's use it as the new instance's proto. */ |
2141 |
|
proto = JSVAL_TO_OBJECT(pval); |
2142 |
|
} else if (pval == JSVAL_HOLE) { |
2143 |
|
/* No ctor.prototype yet, inline and optimize fun_resolve's prototype code. */ |
2144 |
|
proto = js_NewObject(cx, clasp, NULL, OBJ_GET_PARENT(cx, ctor), 0); |
2145 |
|
if (!proto) |
2146 |
|
return NULL; |
2147 |
|
if (!js_SetClassPrototype(cx, ctor, proto, JSPROP_ENUMERATE | JSPROP_PERMANENT)) |
2148 |
|
return NULL; |
2149 |
|
} else { |
2150 |
|
/* Primitive value in .prototype means we use Object.prototype for proto. */ |
2151 |
|
if (!js_GetClassPrototype(cx, JSVAL_TO_OBJECT(ctor->fslots[JSSLOT_PARENT]), |
2152 |
|
INT_TO_JSID(JSProto_Object), &proto)) { |
2153 |
|
return NULL; |
2154 |
|
} |
2155 |
|
} |
2156 |
|
|
2157 |
|
return NewNativeObject(cx, clasp, proto, JSVAL_TO_OBJECT(ctor->fslots[JSSLOT_PARENT])); |
2158 |
|
} |
2159 |
|
|
2160 |
|
JS_DEFINE_CALLINFO_3(extern, CONSTRUCTOR_RETRY, js_NewInstance, CONTEXT, CLASS, OBJECT, 0, 0) |
2161 |
|
|
2162 |
|
#else /* !JS_TRACER */ |
2163 |
|
|
2164 |
|
# define js_Object_trcinfo NULL |
2165 |
|
|
2166 |
|
#endif /* !JS_TRACER */ |
2167 |
|
|
2168 |
|
/* |
2169 |
|
* Given pc pointing after a property accessing bytecode, return true if the |
2170 |
|
* access is "object-detecting" in the sense used by web scripts, e.g., when |
2171 |
|
* checking whether document.all is defined. |
2172 |
|
*/ |
2173 |
|
static JS_REQUIRES_STACK JSBool |
2174 |
|
Detecting(JSContext *cx, jsbytecode *pc) |
2175 |
|
{ |
2176 |
|
JSScript *script; |
2177 |
|
jsbytecode *endpc; |
2178 |
|
JSOp op; |
2179 |
|
JSAtom *atom; |
2180 |
|
|
2181 |
|
script = cx->fp->script; |
2182 |
|
endpc = script->code + script->length; |
2183 |
|
for (;; pc += js_CodeSpec[op].length) { |
2184 |
|
JS_ASSERT(pc < endpc); |
2185 |
|
|
2186 |
|
/* General case: a branch or equality op follows the access. */ |
2187 |
|
op = js_GetOpcode(cx, script, pc); |
2188 |
|
if (js_CodeSpec[op].format & JOF_DETECTING) |
2189 |
|
return JS_TRUE; |
2190 |
|
|
2191 |
|
switch (op) { |
2192 |
|
case JSOP_NULL: |
2193 |
|
/* |
2194 |
|
* Special case #1: handle (document.all == null). Don't sweat |
2195 |
|
* about JS1.2's revision of the equality operators here. |
2196 |
|
*/ |
2197 |
|
if (++pc < endpc) { |
2198 |
|
op = js_GetOpcode(cx, script, pc); |
2199 |
|
return *pc == JSOP_EQ || *pc == JSOP_NE; |
2200 |
|
} |
2201 |
|
return JS_FALSE; |
2202 |
|
|
2203 |
|
case JSOP_NAME: |
2204 |
|
/* |
2205 |
|
* Special case #2: handle (document.all == undefined). Don't |
2206 |
|
* worry about someone redefining undefined, which was added by |
2207 |
|
* Edition 3, so is read/write for backward compatibility. |
2208 |
|
*/ |
2209 |
|
GET_ATOM_FROM_BYTECODE(script, pc, 0, atom); |
2210 |
|
if (atom == cx->runtime->atomState.typeAtoms[JSTYPE_VOID] && |
2211 |
|
(pc += js_CodeSpec[op].length) < endpc) { |
2212 |
|
op = js_GetOpcode(cx, script, pc); |
2213 |
|
return op == JSOP_EQ || op == JSOP_NE || |
2214 |
|
op == JSOP_STRICTEQ || op == JSOP_STRICTNE; |
2215 |
|
} |
2216 |
|
return JS_FALSE; |
2217 |
|
|
2218 |
|
default: |
2219 |
|
/* |
2220 |
|
* At this point, anything but an extended atom index prefix means |
2221 |
|
* we're not detecting. |
2222 |
|
*/ |
2223 |
|
if (!(js_CodeSpec[op].format & JOF_INDEXBASE)) |
2224 |
|
return JS_FALSE; |
2225 |
|
break; |
2226 |
|
} |
2227 |
|
} |
2228 |
|
} |
2229 |
|
|
2230 |
|
/* |
2231 |
|
* Infer lookup flags from the currently executing bytecode. This does |
2232 |
|
* not attempt to infer JSRESOLVE_WITH, because the current bytecode |
2233 |
|
* does not indicate whether we are in a with statement. Return defaultFlags |
2234 |
|
* if a currently executing bytecode cannot be determined. |
2235 |
|
*/ |
2236 |
|
static uintN |
2237 |
|
InferFlags(JSContext *cx, uintN defaultFlags) |
2238 |
|
{ |
2239 |
|
JSStackFrame *fp; |
2240 |
|
jsbytecode *pc; |
2241 |
|
const JSCodeSpec *cs; |
2242 |
|
uint32 format; |
2243 |
|
uintN flags = 0; |
2244 |
|
|
2245 |
|
fp = js_GetTopStackFrame(cx); |
2246 |
|
if (!fp || !fp->regs) |
2247 |
|
return defaultFlags; |
2248 |
|
pc = fp->regs->pc; |
2249 |
|
cs = &js_CodeSpec[js_GetOpcode(cx, fp->script, pc)]; |
2250 |
|
format = cs->format; |
2251 |
|
if (JOF_MODE(format) != JOF_NAME) |
2252 |
|
flags |= JSRESOLVE_QUALIFIED; |
2253 |
|
if ((format & (JOF_SET | JOF_FOR)) || |
2254 |
|
(fp->flags & JSFRAME_ASSIGNING)) { |
2255 |
|
flags |= JSRESOLVE_ASSIGNING; |
2256 |
|
} else { |
2257 |
|
pc += cs->length; |
2258 |
|
if (pc < cx->fp->script->code + cx->fp->script->length && Detecting(cx, pc)) |
2259 |
|
flags |= JSRESOLVE_DETECTING; |
2260 |
|
} |
2261 |
|
if (format & JOF_DECLARING) |
2262 |
|
flags |= JSRESOLVE_DECLARING; |
2263 |
|
return flags; |
2264 |
|
} |
2265 |
|
|
2266 |
/* |
/* |
2267 |
* ObjectOps and Class for with-statement stack objects. |
* ObjectOps and Class for with-statement stack objects. |
2268 |
*/ |
*/ |
2270 |
with_LookupProperty(JSContext *cx, JSObject *obj, jsid id, JSObject **objp, |
with_LookupProperty(JSContext *cx, JSObject *obj, jsid id, JSObject **objp, |
2271 |
JSProperty **propp) |
JSProperty **propp) |
2272 |
{ |
{ |
2273 |
|
/* Fixes bug 463997 */ |
2274 |
|
uintN flags = cx->resolveFlags; |
2275 |
|
if (flags == JSRESOLVE_INFER) |
2276 |
|
flags = InferFlags(cx, flags); |
2277 |
|
flags |= JSRESOLVE_WITH; |
2278 |
|
JSAutoResolveFlags rf(cx, flags); |
2279 |
JSObject *proto = OBJ_GET_PROTO(cx, obj); |
JSObject *proto = OBJ_GET_PROTO(cx, obj); |
2280 |
if (!proto) |
if (!proto) |
2281 |
return js_LookupProperty(cx, obj, id, objp, propp); |
return js_LookupProperty(cx, obj, id, objp, propp); |
2368 |
} |
} |
2369 |
|
|
2370 |
JS_FRIEND_DATA(JSObjectOps) js_WithObjectOps = { |
JS_FRIEND_DATA(JSObjectOps) js_WithObjectOps = { |
2371 |
js_NewObjectMap, js_DestroyObjectMap, |
NULL, |
2372 |
with_LookupProperty, js_DefineProperty, |
with_LookupProperty, js_DefineProperty, |
2373 |
with_GetProperty, with_SetProperty, |
with_GetProperty, with_SetProperty, |
2374 |
with_GetAttributes, with_SetAttributes, |
with_GetAttributes, with_SetAttributes, |
2376 |
with_Enumerate, with_CheckAccess, |
with_Enumerate, with_CheckAccess, |
2377 |
with_ThisObject, NATIVE_DROP_PROPERTY, |
with_ThisObject, NATIVE_DROP_PROPERTY, |
2378 |
NULL, NULL, |
NULL, NULL, |
2379 |
NULL, NULL, |
NULL, js_TraceObject, |
2380 |
js_SetProtoOrParent, js_SetProtoOrParent, |
js_Clear, NULL, |
2381 |
js_TraceObject, js_Clear, |
NULL |
|
NULL, NULL |
|
2382 |
}; |
}; |
2383 |
|
|
2384 |
static JSObjectOps * |
static JSObjectOps * |
2396 |
0,0,0,0,0,0,0 |
0,0,0,0,0,0,0 |
2397 |
}; |
}; |
2398 |
|
|
2399 |
JSObject * |
JS_REQUIRES_STACK JSObject * |
2400 |
js_NewWithObject(JSContext *cx, JSObject *proto, JSObject *parent, jsint depth) |
js_NewWithObject(JSContext *cx, JSObject *proto, JSObject *parent, jsint depth) |
2401 |
{ |
{ |
2402 |
JSObject *obj; |
JSObject *obj; |
2412 |
JSObject * |
JSObject * |
2413 |
js_NewBlockObject(JSContext *cx) |
js_NewBlockObject(JSContext *cx) |
2414 |
{ |
{ |
|
JSObject *obj; |
|
|
JSBool ok; |
|
|
|
|
2415 |
/* |
/* |
2416 |
* Null obj's proto slot so that Object.prototype.* does not pollute block |
* Null obj's proto slot so that Object.prototype.* does not pollute block |
2417 |
* scopes. Make sure obj has its own scope too, since clearing proto does |
* scopes and to give the block object its own scope. |
|
* not affect OBJ_SCOPE(obj). |
|
2418 |
*/ |
*/ |
2419 |
obj = js_NewObject(cx, &js_BlockClass, NULL, NULL, 0); |
JSObject *blockObj = js_NewObjectWithGivenProto(cx, &js_BlockClass, |
2420 |
if (!obj) |
NULL, NULL, 0); |
2421 |
return NULL; |
JS_ASSERT_IF(blockObj, !OBJ_IS_CLONED_BLOCK(blockObj)); |
2422 |
JS_LOCK_OBJ(cx, obj); |
return blockObj; |
|
ok = js_GetMutableScope(cx, obj) != NULL; |
|
|
JS_UNLOCK_OBJ(cx, obj); |
|
|
if (!ok) |
|
|
return NULL; |
|
|
OBJ_CLEAR_PROTO(cx, obj); |
|
|
return obj; |
|
2423 |
} |
} |
2424 |
|
|
2425 |
JSObject * |
JSObject * |
2437 |
STOBJ_SET_SLOT(clone, JSSLOT_BLOCK_DEPTH, |
STOBJ_SET_SLOT(clone, JSSLOT_BLOCK_DEPTH, |
2438 |
OBJ_GET_SLOT(cx, proto, JSSLOT_BLOCK_DEPTH)); |
OBJ_GET_SLOT(cx, proto, JSSLOT_BLOCK_DEPTH)); |
2439 |
JS_ASSERT(OBJ_IS_CLONED_BLOCK(clone)); |
JS_ASSERT(OBJ_IS_CLONED_BLOCK(clone)); |
2440 |
|
JS_ASSERT(OBJ_SCOPE(clone)->object == proto); |
2441 |
return clone; |
return clone; |
2442 |
} |
} |
2443 |
|
|
2444 |
JSBool |
JS_REQUIRES_STACK JSBool |
2445 |
js_PutBlockObject(JSContext *cx, JSBool normalUnwind) |
js_PutBlockObject(JSContext *cx, JSBool normalUnwind) |
2446 |
{ |
{ |
2447 |
JSStackFrame *fp; |
JSStackFrame *fp; |
2460 |
/* |
/* |
2461 |
* Block objects should never be exposed to scripts. Thus the clone should |
* Block objects should never be exposed to scripts. Thus the clone should |
2462 |
* not own the property map and rather always share it with the prototype |
* not own the property map and rather always share it with the prototype |
2463 |
* object. This allows to skip updating OBJ_SCOPE(obj)->map.freeslot after |
* object. This allows us to skip updating OBJ_SCOPE(obj)->freeslot after |
2464 |
* we copy the stack slots into reserved slots. |
* we copy the stack slots into reserved slots. |
2465 |
*/ |
*/ |
2466 |
JS_ASSERT(OBJ_SCOPE(obj)->object != obj); |
JS_ASSERT(OBJ_SCOPE(obj)->object != obj); |
2565 |
return NO_PARENT_INDEX; |
return NO_PARENT_INDEX; |
2566 |
} |
} |
2567 |
|
|
2568 |
static JSBool |
JSBool |
2569 |
block_xdrObject(JSXDRState *xdr, JSObject **objp) |
js_XDRBlockObject(JSXDRState *xdr, JSObject **objp) |
2570 |
{ |
{ |
2571 |
JSContext *cx; |
JSContext *cx; |
2572 |
uint32 parentId; |
uint32 parentId; |
2667 |
if (xdr->mode == JSXDR_DECODE) { |
if (xdr->mode == JSXDR_DECODE) { |
2668 |
if (!js_DefineNativeProperty(cx, obj, ATOM_TO_JSID(atom), |
if (!js_DefineNativeProperty(cx, obj, ATOM_TO_JSID(atom), |
2669 |
JSVAL_VOID, NULL, NULL, |
JSVAL_VOID, NULL, NULL, |
2670 |
JSPROP_ENUMERATE | JSPROP_PERMANENT, |
JSPROP_ENUMERATE | JSPROP_PERMANENT | |
2671 |
|
JSPROP_SHARED, |
2672 |
SPROP_HAS_SHORTID, shortid, NULL)) { |
SPROP_HAS_SHORTID, shortid, NULL)) { |
2673 |
ok = JS_FALSE; |
ok = JS_FALSE; |
2674 |
break; |
break; |
2680 |
return ok; |
return ok; |
2681 |
} |
} |
2682 |
|
|
|
#else |
|
|
# define block_xdrObject NULL |
|
2683 |
#endif |
#endif |
2684 |
|
|
2685 |
static uint32 |
static uint32 |
2690 |
|
|
2691 |
JSClass js_BlockClass = { |
JSClass js_BlockClass = { |
2692 |
"Block", |
"Block", |
2693 |
JSCLASS_HAS_PRIVATE | JSCLASS_HAS_RESERVED_SLOTS(1) | |
JSCLASS_HAS_PRIVATE | JSCLASS_HAS_RESERVED_SLOTS(1) | JSCLASS_IS_ANONYMOUS, |
|
JSCLASS_IS_ANONYMOUS | JSCLASS_HAS_CACHED_PROTO(JSProto_Block), |
|
2694 |
JS_PropertyStub, JS_PropertyStub, block_getProperty, block_setProperty, |
JS_PropertyStub, JS_PropertyStub, block_getProperty, block_setProperty, |
2695 |
JS_EnumerateStub, JS_ResolveStub, JS_ConvertStub, JS_FinalizeStub, |
JS_EnumerateStub, JS_ResolveStub, JS_ConvertStub, JS_FinalizeStub, |
2696 |
NULL, NULL, NULL, NULL, block_xdrObject, NULL, NULL, block_reserveSlots |
NULL, NULL, NULL, NULL, NULL, NULL, NULL, block_reserveSlots |
2697 |
}; |
}; |
2698 |
|
|
|
JSObject* |
|
|
js_InitBlockClass(JSContext *cx, JSObject* obj) |
|
|
{ |
|
|
JSObject *proto; |
|
|
|
|
|
proto = JS_InitClass(cx, obj, NULL, &js_BlockClass, NULL, 0, NULL, |
|
|
NULL, NULL, NULL); |
|
|
if (!proto) |
|
|
return NULL; |
|
|
|
|
|
OBJ_CLEAR_PROTO(cx, proto); |
|
|
return proto; |
|
|
} |
|
|
|
|
2699 |
JSObject * |
JSObject * |
2700 |
js_InitEval(JSContext *cx, JSObject *obj) |
js_InitEval(JSContext *cx, JSObject *obj) |
2701 |
{ |
{ |
2711 |
JSObject * |
JSObject * |
2712 |
js_InitObjectClass(JSContext *cx, JSObject *obj) |
js_InitObjectClass(JSContext *cx, JSObject *obj) |
2713 |
{ |
{ |
2714 |
return JS_InitClass(cx, obj, NULL, &js_ObjectClass, js_Object, 1, |
return js_InitClass(cx, obj, NULL, &js_ObjectClass, js_Object, 1, |
2715 |
object_props, object_methods, NULL, |
object_props, object_methods, NULL, object_static_methods); |
|
object_static_methods); |
|
2716 |
} |
} |
2717 |
|
|
2718 |
void |
JSObject * |
2719 |
js_InitObjectMap(JSObjectMap *map, jsrefcount nrefs, JSObjectOps *ops, |
js_InitClass(JSContext *cx, JSObject *obj, JSObject *parent_proto, |
2720 |
JSClass *clasp) |
JSClass *clasp, JSNative constructor, uintN nargs, |
2721 |
{ |
JSPropertySpec *ps, JSFunctionSpec *fs, |
2722 |
map->nrefs = nrefs; |
JSPropertySpec *static_ps, JSFunctionSpec *static_fs) |
|
map->ops = ops; |
|
|
map->freeslot = JSSLOT_FREE(clasp); |
|
|
} |
|
|
|
|
|
JSObjectMap * |
|
|
js_NewObjectMap(JSContext *cx, jsrefcount nrefs, JSObjectOps *ops, |
|
|
JSClass *clasp, JSObject *obj) |
|
2723 |
{ |
{ |
2724 |
return (JSObjectMap *) js_NewScope(cx, nrefs, ops, clasp, obj); |
JSAtom *atom; |
2725 |
} |
JSProtoKey key; |
2726 |
|
JSObject *proto, *ctor; |
2727 |
|
JSTempValueRooter tvr; |
2728 |
|
jsval cval, rval; |
2729 |
|
JSBool named; |
2730 |
|
JSFunction *fun; |
2731 |
|
|
2732 |
void |
atom = js_Atomize(cx, clasp->name, strlen(clasp->name), 0); |
2733 |
js_DestroyObjectMap(JSContext *cx, JSObjectMap *map) |
if (!atom) |
2734 |
{ |
return NULL; |
|
js_DestroyScope(cx, (JSScope *)map); |
|
|
} |
|
2735 |
|
|
2736 |
JSObjectMap * |
/* |
2737 |
js_HoldObjectMap(JSContext *cx, JSObjectMap *map) |
* When initializing a standard class, if no parent_proto (grand-proto of |
2738 |
{ |
* instances of the class, parent-proto of the class's prototype object) |
2739 |
JS_ASSERT(map->nrefs >= 0); |
* is given, we must use Object.prototype if it is available. Otherwise, |
2740 |
JS_ATOMIC_INCREMENT(&map->nrefs); |
* we could look up the wrong binding for a class name in obj. Example: |
2741 |
return map; |
* |
2742 |
} |
* String = Array; |
2743 |
|
* print("hi there".join); |
2744 |
|
* |
2745 |
|
* should print undefined, not Array.prototype.join. This is required by |
2746 |
|
* ECMA-262, alas. It might have been better to make String readonly and |
2747 |
|
* permanent in the global object, instead -- but that's too big a change |
2748 |
|
* to swallow at this point. |
2749 |
|
*/ |
2750 |
|
key = JSCLASS_CACHED_PROTO_KEY(clasp); |
2751 |
|
if (key != JSProto_Null && |
2752 |
|
!parent_proto && |
2753 |
|
!js_GetClassPrototype(cx, obj, INT_TO_JSID(JSProto_Object), |
2754 |
|
&parent_proto)) { |
2755 |
|
return NULL; |
2756 |
|
} |
2757 |
|
|
2758 |
JSObjectMap * |
/* Create a prototype object for this class. */ |
2759 |
js_DropObjectMap(JSContext *cx, JSObjectMap *map, JSObject *obj) |
proto = js_NewObject(cx, clasp, parent_proto, obj, 0); |
2760 |
{ |
if (!proto) |
|
JS_ASSERT(map->nrefs > 0); |
|
|
JS_ATOMIC_DECREMENT(&map->nrefs); |
|
|
if (map->nrefs == 0) { |
|
|
map->ops->destroyObjectMap(cx, map); |
|
2761 |
return NULL; |
return NULL; |
2762 |
|
|
2763 |
|
/* After this point, control must exit via label bad or out. */ |
2764 |
|
JS_PUSH_TEMP_ROOT_OBJECT(cx, proto, &tvr); |
2765 |
|
|
2766 |
|
if (!constructor) { |
2767 |
|
/* |
2768 |
|
* Lacking a constructor, name the prototype (e.g., Math) unless this |
2769 |
|
* class (a) is anonymous, i.e. for internal use only; (b) the class |
2770 |
|
* of obj (the global object) is has a reserved slot indexed by key; |
2771 |
|
* and (c) key is not the null key. |
2772 |
|
*/ |
2773 |
|
if ((clasp->flags & JSCLASS_IS_ANONYMOUS) && |
2774 |
|
(OBJ_GET_CLASS(cx, obj)->flags & JSCLASS_IS_GLOBAL) && |
2775 |
|
key != JSProto_Null) { |
2776 |
|
named = JS_FALSE; |
2777 |
|
} else { |
2778 |
|
named = OBJ_DEFINE_PROPERTY(cx, obj, ATOM_TO_JSID(atom), |
2779 |
|
OBJECT_TO_JSVAL(proto), |
2780 |
|
JS_PropertyStub, JS_PropertyStub, |
2781 |
|
(clasp->flags & JSCLASS_IS_ANONYMOUS) |
2782 |
|
? JSPROP_READONLY | JSPROP_PERMANENT |
2783 |
|
: 0, |
2784 |
|
NULL); |
2785 |
|
if (!named) |
2786 |
|
goto bad; |
2787 |
|
} |
2788 |
|
|
2789 |
|
ctor = proto; |
2790 |
|
} else { |
2791 |
|
/* Define the constructor function in obj's scope. */ |
2792 |
|
fun = js_DefineFunction(cx, obj, atom, constructor, nargs, |
2793 |
|
JSFUN_STUB_GSOPS); |
2794 |
|
named = (fun != NULL); |
2795 |
|
if (!fun) |
2796 |
|
goto bad; |
2797 |
|
|
2798 |
|
/* |
2799 |
|
* Remember the class this function is a constructor for so that |
2800 |
|
* we know to create an object of this class when we call the |
2801 |
|
* constructor. |
2802 |
|
*/ |
2803 |
|
FUN_CLASP(fun) = clasp; |
2804 |
|
|
2805 |
|
/* |
2806 |
|
* Optionally construct the prototype object, before the class has |
2807 |
|
* been fully initialized. Allow the ctor to replace proto with a |
2808 |
|
* different object, as is done for operator new -- and as at least |
2809 |
|
* XML support requires. |
2810 |
|
*/ |
2811 |
|
ctor = FUN_OBJECT(fun); |
2812 |
|
if (clasp->flags & JSCLASS_CONSTRUCT_PROTOTYPE) { |
2813 |
|
cval = OBJECT_TO_JSVAL(ctor); |
2814 |
|
if (!js_InternalConstruct(cx, proto, cval, 0, NULL, &rval)) |
2815 |
|
goto bad; |
2816 |
|
if (!JSVAL_IS_PRIMITIVE(rval) && JSVAL_TO_OBJECT(rval) != proto) |
2817 |
|
proto = JSVAL_TO_OBJECT(rval); |
2818 |
|
} |
2819 |
|
|
2820 |
|
/* Connect constructor and prototype by named properties. */ |
2821 |
|
if (!js_SetClassPrototype(cx, ctor, proto, |
2822 |
|
JSPROP_READONLY | JSPROP_PERMANENT)) { |
2823 |
|
goto bad; |
2824 |
|
} |
2825 |
|
|
2826 |
|
/* Bootstrap Function.prototype (see also JS_InitStandardClasses). */ |
2827 |
|
if (OBJ_GET_CLASS(cx, ctor) == clasp) |
2828 |
|
OBJ_SET_PROTO(cx, ctor, proto); |
2829 |
} |
} |
2830 |
if (MAP_IS_NATIVE(map) && ((JSScope *)map)->object == obj) |
|
2831 |
((JSScope *)map)->object = NULL; |
/* Add properties and methods to the prototype and the constructor. */ |
2832 |
return map; |
if ((ps && !JS_DefineProperties(cx, proto, ps)) || |
2833 |
|
(fs && !JS_DefineFunctions(cx, proto, fs)) || |
2834 |
|
(static_ps && !JS_DefineProperties(cx, ctor, static_ps)) || |
2835 |
|
(static_fs && !JS_DefineFunctions(cx, ctor, static_fs))) { |
2836 |
|
goto bad; |
2837 |
|
} |
2838 |
|
|
2839 |
|
/* If this is a standard class, cache its prototype. */ |
2840 |
|
if (key != JSProto_Null && !js_SetClassObject(cx, obj, key, ctor)) |
2841 |
|
goto bad; |
2842 |
|
|
2843 |
|
out: |
2844 |
|
JS_POP_TEMP_ROOT(cx, &tvr); |
2845 |
|
return proto; |
2846 |
|
|
2847 |
|
bad: |
2848 |
|
if (named) |
2849 |
|
(void) OBJ_DELETE_PROPERTY(cx, obj, ATOM_TO_JSID(atom), &rval); |
2850 |
|
proto = NULL; |
2851 |
|
goto out; |
2852 |
} |
} |
2853 |
|
|
2854 |
static void |
static void |
3034 |
js_NewObjectWithGivenProto(JSContext *cx, JSClass *clasp, JSObject *proto, |
js_NewObjectWithGivenProto(JSContext *cx, JSClass *clasp, JSObject *proto, |
3035 |
JSObject *parent, uintN objectSize) |
JSObject *parent, uintN objectSize) |
3036 |
{ |
{ |
|
JSObject *obj; |
|
|
JSObjectOps *ops; |
|
|
JSObjectMap *map; |
|
|
JSClass *protoclasp; |
|
|
uint32 nslots, i; |
|
|
JSTempValueRooter tvr; |
|
|
|
|
3037 |
#ifdef INCLUDE_MOZILLA_DTRACE |
#ifdef INCLUDE_MOZILLA_DTRACE |
3038 |
if (JAVASCRIPT_OBJECT_CREATE_START_ENABLED()) |
if (JAVASCRIPT_OBJECT_CREATE_START_ENABLED()) |
3039 |
jsdtrace_object_create_start(cx->fp, clasp); |
jsdtrace_object_create_start(cx->fp, clasp); |
3050 |
objectSize = sizeof(JSObject); |
objectSize = sizeof(JSObject); |
3051 |
} |
} |
3052 |
|
|
3053 |
|
/* Assert that the class is a proper class. */ |
3054 |
|
JS_ASSERT_IF(clasp->flags & JSCLASS_IS_EXTENDED, |
3055 |
|
((JSExtendedClass *)clasp)->equality); |
3056 |
|
|
3057 |
|
/* Always call the class's getObjectOps hook if it has one. */ |
3058 |
|
JSObjectOps *ops = clasp->getObjectOps |
3059 |
|
? clasp->getObjectOps(cx, clasp) |
3060 |
|
: &js_ObjectOps; |
3061 |
|
|
3062 |
/* |
/* |
3063 |
* Allocate an object from the GC heap and initialize all its fields before |
* Allocate an object from the GC heap and initialize all its fields before |
3064 |
* doing any operation that can potentially trigger GC. |
* doing any operation that can potentially trigger GC. |
3065 |
*/ |
*/ |
3066 |
obj = (JSObject *) js_NewGCThing(cx, GCX_OBJECT, objectSize); |
JSObject *obj = (JSObject *) js_NewGCThing(cx, GCX_OBJECT, objectSize); |
3067 |
if (!obj) |
if (!obj) |
3068 |
goto earlybad; |
goto out; |
|
|
|
|
obj->map = NULL; |
|
|
obj->dslots = NULL; |
|
3069 |
|
|
3070 |
/* |
/* |
3071 |
* Set the class slot with the initial value of the system and delegate |
* Set the class slot with the initial value of the system and delegate |
3076 |
JS_ASSERT(!STOBJ_IS_DELEGATE(obj)); |
JS_ASSERT(!STOBJ_IS_DELEGATE(obj)); |
3077 |
JS_ASSERT(!STOBJ_IS_SYSTEM(obj)); |
JS_ASSERT(!STOBJ_IS_SYSTEM(obj)); |
3078 |
|
|
3079 |
/* Set the proto and parent properties. */ |
obj->fslots[JSSLOT_PROTO] = OBJECT_TO_JSVAL(proto); |
3080 |
STOBJ_SET_PROTO(obj, proto); |
|
3081 |
STOBJ_SET_PARENT(obj, parent); |
/* |
3082 |
|
* Default parent to the parent of the prototype, which was set from |
3083 |
|
* the parent of the prototype's constructor. |
3084 |
|
*/ |
3085 |
|
obj->fslots[JSSLOT_PARENT] = OBJECT_TO_JSVAL((!parent && proto) |
3086 |
|
? OBJ_GET_PARENT(cx, proto) |
3087 |
|
: parent); |
3088 |
|
|
3089 |
/* Initialize the remaining fixed slots. */ |
/* Initialize the remaining fixed slots. */ |
3090 |
for (i = JSSLOT_PRIVATE; i != JS_INITIAL_NSLOTS; ++i) |
for (uint32 i = JSSLOT_PRIVATE; i < JS_INITIAL_NSLOTS; ++i) |
3091 |
obj->fslots[i] = JSVAL_VOID; |
obj->fslots[i] = JSVAL_VOID; |
3092 |
|
|
3093 |
|
obj->dslots = NULL; |
3094 |
|
|
3095 |
|
if (OPS_IS_NATIVE(ops)) { |
3096 |
|
if (!InitScopeForObject(cx, obj, proto, ops)) { |
3097 |
|
obj = NULL; |
3098 |
|
goto out; |
3099 |
|
} |
3100 |
|
} else { |
3101 |
|
JS_ASSERT(ops->objectMap->ops == ops); |
3102 |
|
obj->map = const_cast<JSObjectMap *>(ops->objectMap); |
3103 |
|
} |
3104 |
|
|
3105 |
#ifdef DEBUG |
#ifdef DEBUG |
3106 |
memset((uint8 *) obj + sizeof(JSObject), JS_FREE_PATTERN, |
memset((uint8 *) obj + sizeof(JSObject), JS_FREE_PATTERN, |
3107 |
objectSize - sizeof(JSObject)); |
objectSize - sizeof(JSObject)); |
3108 |
#endif |
#endif |
3109 |
|
|
3110 |
/* |
/* Check that the newborn root still holds the object. */ |
3111 |
* Root obj to prevent it from being collected out from under this call to |
JS_ASSERT_IF(!cx->localRootStack, cx->weakRoots.newborn[GCX_OBJECT] == obj); |
|
* js_NewObject. There's a possibilty of GC under the objectHook call-out |
|
|
* further below. |
|
|
*/ |
|
|
JS_PUSH_TEMP_ROOT_OBJECT(cx, obj, &tvr); |
|
|
|
|
|
/* Always call the class's getObjectOps hook if it has one. */ |
|
|
ops = clasp->getObjectOps |
|
|
? clasp->getObjectOps(cx, clasp) |
|
|
: &js_ObjectOps; |
|
3112 |
|
|
3113 |
/* |
/* |
3114 |
* Default parent to the parent of the prototype, which was set from |
* Do not call debug hooks on trace, because we might be in a non-_FAIL |
3115 |
* the parent of the prototype's constructor. |
* builtin. See bug 481444. |
3116 |
*/ |
*/ |
3117 |
if (proto && !parent) |
if (cx->debugHooks->objectHook && !JS_ON_TRACE(cx)) { |
3118 |
STOBJ_SET_PARENT(obj, OBJ_GET_PARENT(cx, proto)); |
JSAutoTempValueRooter tvr(cx, obj); |
|
|
|
|
/* |
|
|
* Share proto's map only if it has the same JSObjectOps, and only if |
|
|
* proto's class has the same private and reserved slots as obj's map |
|
|
* and class have. We assume that if prototype and object are of the |
|
|
* same class, they always have the same number of computed reserved |
|
|
* slots (returned via clasp->reserveSlots); otherwise, prototype and |
|
|
* object classes must have the same (null or not) reserveSlots hook. |
|
|
*/ |
|
|
if (proto && |
|
|
(map = proto->map)->ops == ops && |
|
|
((protoclasp = OBJ_GET_CLASS(cx, proto)) == clasp || |
|
|
(!((protoclasp->flags ^ clasp->flags) & |
|
|
(JSCLASS_HAS_PRIVATE | |
|
|
(JSCLASS_RESERVED_SLOTS_MASK << JSCLASS_RESERVED_SLOTS_SHIFT))) && |
|
|
protoclasp->reserveSlots == clasp->reserveSlots))) |
|
|
{ |
|
|
/* Share the given prototype's map. */ |
|
|
obj->map = js_HoldObjectMap(cx, map); |
|
|
} else { |
|
|
map = ops->newObjectMap(cx, 1, ops, clasp, obj); |
|
|
if (!map) |
|
|
goto bad; |
|
|
obj->map = map; |
|
|
|
|
|
/* Let ops->newObjectMap set freeslot so as to reserve slots. */ |
|
|
nslots = map->freeslot; |
|
|
JS_ASSERT(nslots >= JSSLOT_PRIVATE); |
|
|
if (nslots > JS_INITIAL_NSLOTS && |
|
|
!js_ReallocSlots(cx, obj, nslots, JS_TRUE)) { |
|
|
js_DropObjectMap(cx, map, obj); |
|
|
obj->map = NULL; |
|
|
goto bad; |
|
|
} |
|
|
} |
|
|
|
|
|
if (cx->debugHooks->objectHook) { |
|
3119 |
JS_KEEP_ATOMS(cx->runtime); |
JS_KEEP_ATOMS(cx->runtime); |
3120 |
cx->debugHooks->objectHook(cx, obj, JS_TRUE, |
cx->debugHooks->objectHook(cx, obj, JS_TRUE, |
3121 |
cx->debugHooks->objectHookData); |
cx->debugHooks->objectHookData); |
3122 |
JS_UNKEEP_ATOMS(cx->runtime); |
JS_UNKEEP_ATOMS(cx->runtime); |
3123 |
|
cx->weakRoots.newborn[GCX_OBJECT] = obj; |
3124 |
} |
} |
3125 |
|
|
3126 |
out: |
out: |
|
JS_POP_TEMP_ROOT(cx, &tvr); |
|
|
cx->weakRoots.newborn[GCX_OBJECT] = obj; |
|
3127 |
#ifdef INCLUDE_MOZILLA_DTRACE |
#ifdef INCLUDE_MOZILLA_DTRACE |
3128 |
if (JAVASCRIPT_OBJECT_CREATE_ENABLED()) |
if (JAVASCRIPT_OBJECT_CREATE_ENABLED()) |
3129 |
jsdtrace_object_create(cx, clasp, obj); |
jsdtrace_object_create(cx, clasp, obj); |
3131 |
jsdtrace_object_create_done(cx->fp, clasp); |
jsdtrace_object_create_done(cx->fp, clasp); |
3132 |
#endif |
#endif |
3133 |
return obj; |
return obj; |
3134 |
|
} |
3135 |
|
|
3136 |
bad: |
JSObject* |
3137 |
obj = NULL; |
js_NewNativeObject(JSContext *cx, JSClass *clasp, JSObject *proto, uint32 slot) |
3138 |
goto out; |
{ |
3139 |
|
JS_ASSERT(!clasp->getObjectOps); |
3140 |
|
JS_ASSERT(proto->map->ops == &js_ObjectOps); |
3141 |
|
JS_ASSERT(OBJ_GET_CLASS(cx, proto) == clasp); |
3142 |
|
|
3143 |
earlybad: |
JSObject* obj = (JSObject*) js_NewGCThing(cx, GCX_OBJECT, sizeof(JSObject)); |
3144 |
#ifdef INCLUDE_MOZILLA_DTRACE |
if (!obj) |
3145 |
if (JAVASCRIPT_OBJECT_CREATE_ENABLED()) |
return NULL; |
3146 |
jsdtrace_object_create(cx, clasp, NULL); |
|
3147 |
if (JAVASCRIPT_OBJECT_CREATE_DONE_ENABLED()) |
js_HoldScope(OBJ_SCOPE(proto)); |
3148 |
jsdtrace_object_create_done(cx->fp, clasp); |
obj->map = proto->map; |
3149 |
#endif |
obj->classword = jsuword(clasp); |
3150 |
return NULL; |
obj->fslots[JSSLOT_PROTO] = OBJECT_TO_JSVAL(proto); |
3151 |
|
obj->fslots[JSSLOT_PARENT] = proto->fslots[JSSLOT_PARENT]; |
3152 |
|
|
3153 |
|
JS_ASSERT(slot > JSSLOT_PARENT); |
3154 |
|
while (slot < JS_INITIAL_NSLOTS) |
3155 |
|
obj->fslots[slot++] = JSVAL_VOID; |
3156 |
|
|
3157 |
|
obj->dslots = NULL; |
3158 |
|
return obj; |
3159 |
} |
} |
3160 |
|
|
3161 |
JS_BEGIN_EXTERN_C |
JS_BEGIN_EXTERN_C |
3247 |
JSBool |
JSBool |
3248 |
js_FindClassObject(JSContext *cx, JSObject *start, jsid id, jsval *vp) |
js_FindClassObject(JSContext *cx, JSObject *start, jsid id, jsval *vp) |
3249 |
{ |
{ |
3250 |
|
JSStackFrame *fp; |
3251 |
JSObject *obj, *cobj, *pobj; |
JSObject *obj, *cobj, *pobj; |
3252 |
JSProtoKey key; |
JSProtoKey key; |
3253 |
JSProperty *prop; |
JSProperty *prop; |
3254 |
jsval v; |
jsval v; |
3255 |
JSScopeProperty *sprop; |
JSScopeProperty *sprop; |
3256 |
|
|
3257 |
if (start || (cx->fp && (start = cx->fp->scopeChain) != NULL)) { |
/* |
3258 |
|
* Find the global object. Use cx->fp directly to avoid falling off |
3259 |
|
* trace; all JIT-elided stack frames have the same global object as |
3260 |
|
* cx->fp. |
3261 |
|
*/ |
3262 |
|
VOUCH_DOES_NOT_REQUIRE_STACK(); |
3263 |
|
if (!start && (fp = cx->fp) != NULL) |
3264 |
|
start = fp->scopeChain; |
3265 |
|
|
3266 |
|
if (start) { |
3267 |
/* Find the topmost object in the scope chain. */ |
/* Find the topmost object in the scope chain. */ |
3268 |
do { |
do { |
3269 |
obj = start; |
obj = start; |
3404 |
void |
void |
3405 |
js_FinalizeObject(JSContext *cx, JSObject *obj) |
js_FinalizeObject(JSContext *cx, JSObject *obj) |
3406 |
{ |
{ |
|
JSObjectMap *map; |
|
|
|
|
3407 |
/* Cope with stillborn objects that have no map. */ |
/* Cope with stillborn objects that have no map. */ |
3408 |
map = obj->map; |
if (!obj->map) |
|
if (!map) |
|
3409 |
return; |
return; |
3410 |
|
|
3411 |
if (cx->debugHooks->objectHook) { |
if (cx->debugHooks->objectHook) { |
3421 |
jsdtrace_object_finalize(obj); |
jsdtrace_object_finalize(obj); |
3422 |
#endif |
#endif |
3423 |
|
|
3424 |
/* Drop map and free slots. */ |
if (OBJ_IS_NATIVE(obj)) |
3425 |
js_DropObjectMap(cx, map, obj); |
js_DropScope(cx, OBJ_SCOPE(obj), obj); |
3426 |
FreeSlots(cx, obj); |
FreeSlots(cx, obj); |
3427 |
} |
} |
3428 |
|
|
3431 |
JSBool |
JSBool |
3432 |
js_AllocSlot(JSContext *cx, JSObject *obj, uint32 *slotp) |
js_AllocSlot(JSContext *cx, JSObject *obj, uint32 *slotp) |
3433 |
{ |
{ |
3434 |
JSObjectMap *map; |
JS_ASSERT(OBJ_IS_NATIVE(obj)); |
|
JSClass *clasp; |
|
3435 |
|
|
3436 |
map = obj->map; |
JSScope *scope = OBJ_SCOPE(obj); |
3437 |
JS_ASSERT(!MAP_IS_NATIVE(map) || ((JSScope *)map)->object == obj); |
JSClass *clasp = LOCKED_OBJ_GET_CLASS(obj); |
3438 |
clasp = LOCKED_OBJ_GET_CLASS(obj); |
if (scope->freeslot == JSSLOT_FREE(clasp) && clasp->reserveSlots) { |
3439 |
if (map->freeslot == JSSLOT_FREE(clasp) && clasp->reserveSlots) { |
/* Adjust scope->freeslot to include computed reserved slots, if any. */ |
3440 |
/* Adjust map->freeslot to include computed reserved slots, if any. */ |
scope->freeslot += clasp->reserveSlots(cx, obj); |
|
map->freeslot += clasp->reserveSlots(cx, obj); |
|
3441 |
} |
} |
3442 |
|
|
3443 |
if (map->freeslot >= STOBJ_NSLOTS(obj) && |
if (scope->freeslot >= STOBJ_NSLOTS(obj) && |
3444 |
!js_ReallocSlots(cx, obj, map->freeslot + 1, JS_FALSE)) { |
!js_ReallocSlots(cx, obj, scope->freeslot + 1, JS_FALSE)) { |
3445 |
return JS_FALSE; |
return JS_FALSE; |
3446 |
} |
} |
3447 |
|
|
3448 |
/* js_ReallocSlots or js_FreeSlot should set the free slots to void. */ |
/* js_ReallocSlots or js_FreeSlot should set the free slots to void. */ |
3449 |
JS_ASSERT(JSVAL_IS_VOID(STOBJ_GET_SLOT(obj, map->freeslot))); |
JS_ASSERT(JSVAL_IS_VOID(STOBJ_GET_SLOT(obj, scope->freeslot))); |
3450 |
*slotp = map->freeslot++; |
*slotp = scope->freeslot++; |
3451 |
return JS_TRUE; |
return JS_TRUE; |
3452 |
} |
} |
3453 |
|
|
3454 |
void |
void |
3455 |
js_FreeSlot(JSContext *cx, JSObject *obj, uint32 slot) |
js_FreeSlot(JSContext *cx, JSObject *obj, uint32 slot) |
3456 |
{ |
{ |
3457 |
JSObjectMap *map; |
JS_ASSERT(OBJ_IS_NATIVE(obj)); |
3458 |
|
|
3459 |
map = obj->map; |
JSScope *scope = OBJ_SCOPE(obj); |
|
JS_ASSERT(!MAP_IS_NATIVE(map) || ((JSScope *)map)->object == obj); |
|
3460 |
LOCKED_OBJ_SET_SLOT(obj, slot, JSVAL_VOID); |
LOCKED_OBJ_SET_SLOT(obj, slot, JSVAL_VOID); |
3461 |
if (map->freeslot == slot + 1) { |
if (scope->freeslot == slot + 1) { |
3462 |
map->freeslot = slot; |
scope->freeslot = slot; |
3463 |
|
|
3464 |
/* When shrinking, js_ReallocSlots always returns true. */ |
/* When shrinking, js_ReallocSlots always returns true. */ |
3465 |
js_ReallocSlots(cx, obj, slot, JS_FALSE); |
js_ReallocSlots(cx, obj, slot, JS_FALSE); |
3482 |
cp++; |
cp++; |
3483 |
} |
} |
3484 |
} |
} |
3485 |
if (cp == end && |
|
3486 |
(oldIndex < (JSVAL_INT_MAX / 10) || |
/* |
3487 |
(oldIndex == (JSVAL_INT_MAX / 10) && |
* Non-integer indexes can't be represented as integers. Also, distinguish |
3488 |
c <= (JSVAL_INT_MAX % 10)))) { |
* index "-0" from "0", because JSVAL_INT cannot. |
3489 |
|
*/ |
3490 |
|
if (cp != end || (negative && index == 0)) |
3491 |
|
return id; |
3492 |
|
if (oldIndex < JSVAL_INT_MAX / 10 || |
3493 |
|
(oldIndex == JSVAL_INT_MAX / 10 && c <= (JSVAL_INT_MAX % 10))) { |
3494 |
if (negative) |
if (negative) |
3495 |
index = 0 - index; |
index = 0 - index; |
3496 |
id = INT_TO_JSID((jsint)index); |
id = INT_TO_JSID((jsint)index); |
3514 |
sprop = SCOPE_GET_PROPERTY(scope, id); |
sprop = SCOPE_GET_PROPERTY(scope, id); |
3515 |
if (sprop) { |
if (sprop) { |
3516 |
PCMETER(JS_PROPERTY_CACHE(cx).pcpurges++); |
PCMETER(JS_PROPERTY_CACHE(cx).pcpurges++); |
3517 |
SCOPE_MAKE_UNIQUE_SHAPE(cx, scope); |
js_MakeScopeShapeUnique(cx, scope); |
3518 |
JS_UNLOCK_SCOPE(cx, scope); |
JS_UNLOCK_SCOPE(cx, scope); |
3519 |
|
|
3520 |
|
if (!STOBJ_GET_PARENT(scope->object)) { |
3521 |
|
/* |
3522 |
|
* All scope chains end in a global object, so this will change |
3523 |
|
* the global shape. jstracer.cpp assumes that the global shape |
3524 |
|
* never changes on trace, so we must deep-bail here. |
3525 |
|
*/ |
3526 |
|
js_LeaveTrace(cx); |
3527 |
|
} |
3528 |
return JS_TRUE; |
return JS_TRUE; |
3529 |
} |
} |
3530 |
obj = LOCKED_OBJ_GET_PROTO(scope->object); |
obj = LOCKED_OBJ_GET_PROTO(scope->object); |
3533 |
return JS_FALSE; |
return JS_FALSE; |
3534 |
} |
} |
3535 |
|
|
3536 |
static void |
void |
3537 |
PurgeScopeChain(JSContext *cx, JSObject *obj, jsid id) |
js_PurgeScopeChainHelper(JSContext *cx, JSObject *obj, jsid id) |
3538 |
{ |
{ |
3539 |
if (!OBJ_IS_DELEGATE(cx, obj)) |
JS_ASSERT(OBJ_IS_DELEGATE(cx, obj)); |
|
return; |
|
|
|
|
3540 |
PurgeProtoChain(cx, OBJ_GET_PROTO(cx, obj), id); |
PurgeProtoChain(cx, OBJ_GET_PROTO(cx, obj), id); |
3541 |
while ((obj = OBJ_GET_PARENT(cx, obj)) != NULL) { |
|
3542 |
if (PurgeProtoChain(cx, obj, id)) |
/* |
3543 |
return; |
* We must purge the scope chain only for Call objects as they are the only |
3544 |
|
* kind of cacheable non-global object that can gain properties after outer |
3545 |
|
* properties with the same names have been cached or traced. Call objects |
3546 |
|
* may gain such properties via eval introducing new vars; see bug 490364. |
3547 |
|
*/ |
3548 |
|
if (STOBJ_GET_CLASS(obj) == &js_CallClass) { |
3549 |
|
while ((obj = OBJ_GET_PARENT(cx, obj)) != NULL) { |
3550 |
|
if (PurgeProtoChain(cx, obj, id)) |
3551 |
|
break; |
3552 |
|
} |
3553 |
} |
} |
3554 |
} |
} |
3555 |
|
|
3566 |
* this optimistically (assuming no failure below) before locking obj, so |
* this optimistically (assuming no failure below) before locking obj, so |
3567 |
* we can lock the shadowed scope. |
* we can lock the shadowed scope. |
3568 |
*/ |
*/ |
3569 |
PurgeScopeChain(cx, obj, id); |
js_PurgeScopeChain(cx, obj, id); |
3570 |
|
|
3571 |
JS_LOCK_OBJ(cx, obj); |
JS_LOCK_OBJ(cx, obj); |
3572 |
scope = js_GetMutableScope(cx, obj); |
scope = js_GetMutableScope(cx, obj); |
3615 |
* nominal initial value of a slot-full property, while GC safety wants that |
* nominal initial value of a slot-full property, while GC safety wants that |
3616 |
* value to be stored before the call-out through the hook. Optimize to do |
* value to be stored before the call-out through the hook. Optimize to do |
3617 |
* both while saving cycles for classes that stub their addProperty hook. |
* both while saving cycles for classes that stub their addProperty hook. |
3618 |
|
* |
3619 |
|
* As in js_SetProtoOrParent (see above), we maintain the "any Array prototype |
3620 |
|
* has indexed properties hazard" flag by conservatively setting it. |
3621 |
*/ |
*/ |
3622 |
#define ADD_PROPERTY_HELPER(cx,clasp,obj,scope,sprop,vp,cleanup) \ |
#define ADD_PROPERTY_HELPER(cx,clasp,obj,scope,sprop,vp,cleanup) \ |
3623 |
JS_BEGIN_MACRO \ |
JS_BEGIN_MACRO \ |
3636 |
JSBool |
JSBool |
3637 |
js_DefineNativeProperty(JSContext *cx, JSObject *obj, jsid id, jsval value, |
js_DefineNativeProperty(JSContext *cx, JSObject *obj, jsid id, jsval value, |
3638 |
JSPropertyOp getter, JSPropertyOp setter, uintN attrs, |
JSPropertyOp getter, JSPropertyOp setter, uintN attrs, |
3639 |
uintN flags, intN shortid, JSProperty **propp) |
uintN flags, intN shortid, JSProperty **propp, |
3640 |
|
uintN defineHow /* = 0 */) |
3641 |
{ |
{ |
3642 |
JSClass *clasp; |
JSClass *clasp; |
3643 |
JSScope *scope; |
JSScope *scope; |
3644 |
JSScopeProperty *sprop; |
JSScopeProperty *sprop; |
3645 |
|
bool added; |
3646 |
|
|
3647 |
|
JS_ASSERT((defineHow & ~(JSDNP_CACHE_RESULT | JSDNP_DONT_PURGE)) == 0); |
3648 |
|
js_LeaveTraceIfGlobalObject(cx, obj); |
3649 |
|
|
3650 |
/* Convert string indices to integers if appropriate. */ |
/* Convert string indices to integers if appropriate. */ |
3651 |
CHECK_FOR_STRING_INDEX(id); |
CHECK_FOR_STRING_INDEX(id); |
3686 |
|
|
3687 |
/* NB: obj == pobj, so we can share unlock code at the bottom. */ |
/* NB: obj == pobj, so we can share unlock code at the bottom. */ |
3688 |
if (!sprop) |
if (!sprop) |
3689 |
goto bad; |
goto error; |
3690 |
} else if (prop) { |
} else if (prop) { |
3691 |
/* NB: call OBJ_DROP_PROPERTY, as pobj might not be native. */ |
/* NB: call OBJ_DROP_PROPERTY, as pobj might not be native. */ |
3692 |
OBJ_DROP_PROPERTY(cx, pobj, prop); |
OBJ_DROP_PROPERTY(cx, pobj, prop); |
3697 |
#endif /* JS_HAS_GETTER_SETTER */ |
#endif /* JS_HAS_GETTER_SETTER */ |
3698 |
|
|
3699 |
/* |
/* |
3700 |
* Purge the property cache of now-shadowed id in obj's scope chain. |
* Purge the property cache of any properties named by id that are about |
3701 |
* Do this early, before locking obj to avoid nesting locks. |
* to be shadowed in obj's scope chain unless it is known a priori that it |
3702 |
|
* is not possible. We do this before locking obj to avoid nesting locks. |
3703 |
|
*/ |
3704 |
|
if (!(defineHow & JSDNP_DONT_PURGE)) |
3705 |
|
js_PurgeScopeChain(cx, obj, id); |
3706 |
|
|
3707 |
|
/* |
3708 |
|
* Check whether a readonly property or setter is being defined on a known |
3709 |
|
* prototype object. See the comment in jscntxt.h before protoHazardShape's |
3710 |
|
* member declaration. |
3711 |
*/ |
*/ |
3712 |
PurgeScopeChain(cx, obj, id); |
if (OBJ_IS_DELEGATE(cx, obj) && (attrs & (JSPROP_READONLY | JSPROP_SETTER))) |
3713 |
|
cx->runtime->protoHazardShape = js_GenerateShape(cx, false); |
3714 |
|
|
3715 |
/* Lock if object locking is required by this implementation. */ |
/* Lock if object locking is required by this implementation. */ |
3716 |
JS_LOCK_OBJ(cx, obj); |
JS_LOCK_OBJ(cx, obj); |
3725 |
/* Get obj's own scope if it has one, or create a new one for obj. */ |
/* Get obj's own scope if it has one, or create a new one for obj. */ |
3726 |
scope = js_GetMutableScope(cx, obj); |
scope = js_GetMutableScope(cx, obj); |
3727 |
if (!scope) |
if (!scope) |
3728 |
goto bad; |
goto error; |
3729 |
|
|
3730 |
|
added = false; |
3731 |
if (!sprop) { |
if (!sprop) { |
3732 |
/* Add a new property, or replace an existing one of the same id. */ |
/* Add a new property, or replace an existing one of the same id. */ |
3733 |
if (clasp->flags & JSCLASS_SHARE_ALL_PROPERTIES) |
if (clasp->flags & JSCLASS_SHARE_ALL_PROPERTIES) |
3736 |
SPROP_INVALID_SLOT, attrs, flags, |
SPROP_INVALID_SLOT, attrs, flags, |
3737 |
shortid); |
shortid); |
3738 |
if (!sprop) |
if (!sprop) |
3739 |
goto bad; |
goto error; |
3740 |
|
added = true; |
3741 |
} |
} |
3742 |
|
|
3743 |
/* Store value before calling addProperty, in case the latter GC's. */ |
/* Store value before calling addProperty, in case the latter GC's. */ |
3747 |
/* XXXbe called with lock held */ |
/* XXXbe called with lock held */ |
3748 |
ADD_PROPERTY_HELPER(cx, clasp, obj, scope, sprop, &value, |
ADD_PROPERTY_HELPER(cx, clasp, obj, scope, sprop, &value, |
3749 |
js_RemoveScopeProperty(cx, scope, id); |
js_RemoveScopeProperty(cx, scope, id); |
3750 |
goto bad); |
goto error); |
3751 |
|
|
3752 |
|
if (defineHow & JSDNP_CACHE_RESULT) { |
3753 |
|
JS_ASSERT_NOT_ON_TRACE(cx); |
3754 |
|
JSPropCacheEntry *entry; |
3755 |
|
entry = js_FillPropertyCache(cx, obj, 0, 0, obj, sprop, added); |
3756 |
|
TRACE_2(SetPropHit, entry, sprop); |
3757 |
|
} |
3758 |
if (propp) |
if (propp) |
3759 |
*propp = (JSProperty *) sprop; |
*propp = (JSProperty *) sprop; |
3760 |
else |
else |
3761 |
JS_UNLOCK_OBJ(cx, obj); |
JS_UNLOCK_OBJ(cx, obj); |
3762 |
return JS_TRUE; |
return JS_TRUE; |
3763 |
|
|
3764 |
bad: |
error: // TRACE_2 jumps here on error, as well. |
3765 |
JS_UNLOCK_OBJ(cx, obj); |
JS_UNLOCK_OBJ(cx, obj); |
3766 |
return JS_FALSE; |
return JS_FALSE; |
3767 |
} |
} |
3768 |
|
|
|
/* |
|
|
* Given pc pointing after a property accessing bytecode, return true if the |
|
|
* access is "object-detecting" in the sense used by web scripts, e.g., when |
|
|
* checking whether document.all is defined. |
|
|
*/ |
|
|
static JSBool |
|
|
Detecting(JSContext *cx, jsbytecode *pc) |
|
|
{ |
|
|
JSScript *script; |
|
|
jsbytecode *endpc; |
|
|
JSOp op; |
|
|
JSAtom *atom; |
|
|
|
|
|
if (!cx->fp) |
|
|
return JS_FALSE; |
|
|
script = cx->fp->script; |
|
|
for (endpc = script->code + script->length; |
|
|
pc < endpc; |
|
|
pc += js_CodeSpec[op].length) { |
|
|
/* General case: a branch or equality op follows the access. */ |
|
|
op = (JSOp) *pc; |
|
|
if (js_CodeSpec[op].format & JOF_DETECTING) |
|
|
return JS_TRUE; |
|
|
|
|
|
switch (op) { |
|
|
case JSOP_NULL: |
|
|
/* |
|
|
* Special case #1: handle (document.all == null). Don't sweat |
|
|
* about JS1.2's revision of the equality operators here. |
|
|
*/ |
|
|
if (++pc < endpc) |
|
|
return *pc == JSOP_EQ || *pc == JSOP_NE; |
|
|
return JS_FALSE; |
|
|
|
|
|
case JSOP_NAME: |
|
|
/* |
|
|
* Special case #2: handle (document.all == undefined). Don't |
|
|
* worry about someone redefining undefined, which was added by |
|
|
* Edition 3, so is read/write for backward compatibility. |
|
|
*/ |
|
|
GET_ATOM_FROM_BYTECODE(script, pc, 0, atom); |
|
|
if (atom == cx->runtime->atomState.typeAtoms[JSTYPE_VOID] && |
|
|
(pc += js_CodeSpec[op].length) < endpc) { |
|
|
op = (JSOp) *pc; |
|
|
return op == JSOP_EQ || op == JSOP_NE || |
|
|
op == JSOP_STRICTEQ || op == JSOP_STRICTNE; |
|
|
} |
|
|
return JS_FALSE; |
|
|
|
|
|
default: |
|
|
/* |
|
|
* At this point, anything but an extended atom index prefix means |
|
|
* we're not detecting. |
|
|
*/ |
|
|
if (!(js_CodeSpec[op].format & JOF_INDEXBASE)) |
|
|
return JS_FALSE; |
|
|
break; |
|
|
} |
|
|
} |
|
|
return JS_FALSE; |
|
|
} |
|
|
|
|
3769 |
JS_FRIEND_API(JSBool) |
JS_FRIEND_API(JSBool) |
3770 |
js_LookupProperty(JSContext *cx, JSObject *obj, jsid id, JSObject **objp, |
js_LookupProperty(JSContext *cx, JSObject *obj, jsid id, JSObject **objp, |
3771 |
JSProperty **propp) |
JSProperty **propp) |
3791 |
JSResolvingEntry *entry; |
JSResolvingEntry *entry; |
3792 |
uint32 generation; |
uint32 generation; |
3793 |
JSNewResolveOp newresolve; |
JSNewResolveOp newresolve; |
|
jsbytecode *pc; |
|
|
const JSCodeSpec *cs; |
|
|
uint32 format; |
|
3794 |
JSBool ok; |
JSBool ok; |
3795 |
|
|
3796 |
/* Convert string indices to integers if appropriate. */ |
/* Convert string indices to integers if appropriate. */ |
3797 |
CHECK_FOR_STRING_INDEX(id); |
CHECK_FOR_STRING_INDEX(id); |
|
JS_COUNT_OPERATION(cx, JSOW_LOOKUP_PROPERTY); |
|
3798 |
|
|
3799 |
/* Search scopes starting with obj and following the prototype link. */ |
/* Search scopes starting with obj and following the prototype link. */ |
3800 |
start = obj; |
start = obj; |
3839 |
|
|
3840 |
if (clasp->flags & JSCLASS_NEW_RESOLVE) { |
if (clasp->flags & JSCLASS_NEW_RESOLVE) { |
3841 |
newresolve = (JSNewResolveOp)resolve; |
newresolve = (JSNewResolveOp)resolve; |
3842 |
if (flags == JSRESOLVE_INFER && cx->fp && cx->fp->regs) { |
if (flags == JSRESOLVE_INFER) |
3843 |
flags = 0; |
flags = InferFlags(cx, flags); |
|
pc = cx->fp->regs->pc; |
|
|
cs = &js_CodeSpec[*pc]; |
|
|
format = cs->format; |
|
|
if (JOF_MODE(format) != JOF_NAME) |
|
|
flags |= JSRESOLVE_QUALIFIED; |
|
|
if ((format & (JOF_SET | JOF_FOR)) || |
|
|
(cx->fp->flags & JSFRAME_ASSIGNING)) { |
|
|
flags |= JSRESOLVE_ASSIGNING; |
|
|
} else { |
|
|
pc += cs->length; |
|
|
if (Detecting(cx, pc)) |
|
|
flags |= JSRESOLVE_DETECTING; |
|
|
} |
|
|
if (format & JOF_DECLARING) |
|
|
flags |= JSRESOLVE_DECLARING; |
|
|
} |
|
3844 |
obj2 = (clasp->flags & JSCLASS_NEW_RESOLVE_GETS_START) |
obj2 = (clasp->flags & JSCLASS_NEW_RESOLVE_GETS_START) |
3845 |
? start |
? start |
3846 |
: NULL; |
: NULL; |
3866 |
proto = OBJ_GET_PROTO(cx, proto)) { |
proto = OBJ_GET_PROTO(cx, proto)) { |
3867 |
protoIndex++; |
protoIndex++; |
3868 |
} |
} |
3869 |
scope = OBJ_SCOPE(obj2); |
if (!OBJ_IS_NATIVE(obj2)) { |
|
if (!MAP_IS_NATIVE(&scope->map)) { |
|
3870 |
/* Whoops, newresolve handed back a foreign obj2. */ |
/* Whoops, newresolve handed back a foreign obj2. */ |
3871 |
JS_ASSERT(obj2 != obj); |
JS_ASSERT(obj2 != obj); |
3872 |
ok = OBJ_LOOKUP_PROPERTY(cx, obj2, id, objp, propp); |
ok = OBJ_LOOKUP_PROPERTY(cx, obj2, id, objp, propp); |
3882 |
* not on obj's proto chain. That last case is a |
* not on obj's proto chain. That last case is a |
3883 |
* "too bad!" case. |
* "too bad!" case. |
3884 |
*/ |
*/ |
3885 |
|
scope = OBJ_SCOPE(obj2); |
3886 |
if (scope->object == obj2) |
if (scope->object == obj2) |
3887 |
sprop = SCOPE_GET_PROPERTY(scope, id); |
sprop = SCOPE_GET_PROPERTY(scope, id); |
3888 |
} |
} |
3905 |
if (!ok) |
if (!ok) |
3906 |
goto cleanup; |
goto cleanup; |
3907 |
JS_LOCK_OBJ(cx, obj); |
JS_LOCK_OBJ(cx, obj); |
3908 |
|
JS_ASSERT(OBJ_IS_NATIVE(obj)); |
3909 |
scope = OBJ_SCOPE(obj); |
scope = OBJ_SCOPE(obj); |
|
JS_ASSERT(MAP_IS_NATIVE(&scope->map)); |
|
3910 |
if (scope->object == obj) |
if (scope->object == obj) |
3911 |
sprop = SCOPE_GET_PROPERTY(scope, id); |
sprop = SCOPE_GET_PROPERTY(scope, id); |
3912 |
} |
} |
3947 |
return protoIndex; |
return protoIndex; |
3948 |
} |
} |
3949 |
|
|
3950 |
int |
/* |
3951 |
js_FindPropertyHelper(JSContext *cx, jsid id, JSObject **objp, |
* We cache name lookup results only for the global object or for native |
3952 |
JSObject **pobjp, JSProperty **propp, |
* non-global objects without prototype or with prototype that never mutates, |
3953 |
JSPropCacheEntry **entryp) |
* see bug 462734 and bug 487039. |
3954 |
|
*/ |
3955 |
|
static inline bool |
3956 |
|
IsCacheableNonGlobalScope(JSObject *obj) |
3957 |
|
{ |
3958 |
|
JS_ASSERT(STOBJ_GET_PARENT(obj)); |
3959 |
|
|
3960 |
|
JSClass *clasp = STOBJ_GET_CLASS(obj); |
3961 |
|
bool cacheable = (clasp == &js_CallClass || |
3962 |
|
clasp == &js_BlockClass || |
3963 |
|
clasp == &js_DeclEnvClass); |
3964 |
|
|
3965 |
|
JS_ASSERT_IF(cacheable, obj->map->ops->lookupProperty == js_LookupProperty); |
3966 |
|
return cacheable; |
3967 |
|
} |
3968 |
|
|
3969 |
|
JSPropCacheEntry * |
3970 |
|
js_FindPropertyHelper(JSContext *cx, jsid id, JSBool cacheResult, |
3971 |
|
JSObject **objp, JSObject **pobjp, JSProperty **propp) |
3972 |
{ |
{ |
3973 |
JSObject *obj, *pobj, *lastobj; |
JSObject *scopeChain, *obj, *parent, *pobj; |
3974 |
uint32 shape; |
JSPropCacheEntry *entry; |
3975 |
int scopeIndex, protoIndex; |
int scopeIndex, protoIndex; |
3976 |
JSProperty *prop; |
JSProperty *prop; |
|
JSScopeProperty *sprop; |
|
3977 |
|
|
3978 |
obj = cx->fp->scopeChain; |
JS_ASSERT_IF(cacheResult, !JS_ON_TRACE(cx)); |
3979 |
shape = OBJ_SHAPE(obj); |
scopeChain = js_GetTopStackFrame(cx)->scopeChain; |
3980 |
for (scopeIndex = 0; ; scopeIndex++) { |
|
3981 |
if (obj->map->ops->lookupProperty == js_LookupProperty) { |
/* Scan entries on the scope chain that we can cache across. */ |
3982 |
protoIndex = |
entry = JS_NO_PROP_CACHE_FILL; |
3983 |
js_LookupPropertyWithFlags(cx, obj, id, cx->resolveFlags, |
obj = scopeChain; |
3984 |
&pobj, &prop); |
parent = OBJ_GET_PARENT(cx, obj); |
3985 |
} else { |
for (scopeIndex = 0; |
3986 |
if (!OBJ_LOOKUP_PROPERTY(cx, obj, id, &pobj, &prop)) |
parent |
3987 |
return -1; |
? IsCacheableNonGlobalScope(obj) |
3988 |
protoIndex = -1; |
: obj->map->ops->lookupProperty == js_LookupProperty; |
3989 |
} |
++scopeIndex) { |
3990 |
|
protoIndex = |
3991 |
|
js_LookupPropertyWithFlags(cx, obj, id, cx->resolveFlags, |
3992 |
|
&pobj, &prop); |
3993 |
|
if (protoIndex < 0) |
3994 |
|
return NULL; |
3995 |
|
|
3996 |
if (prop) { |
if (prop) { |
3997 |
if (entryp) { |
#ifdef DEBUG |
3998 |
if (protoIndex >= 0 && OBJ_IS_NATIVE(pobj)) { |
if (parent) { |
3999 |
sprop = (JSScopeProperty *) prop; |
JSClass *clasp = OBJ_GET_CLASS(cx, obj); |
4000 |
js_FillPropertyCache(cx, cx->fp->scopeChain, shape, |
JS_ASSERT(OBJ_IS_NATIVE(pobj)); |
4001 |
scopeIndex, protoIndex, pobj, sprop, |
JS_ASSERT(OBJ_GET_CLASS(cx, pobj) == clasp); |
4002 |
entryp); |
if (clasp == &js_BlockClass) { |
4003 |
|
/* |
4004 |
|
* Block instances on the scope chain are immutable and |
4005 |
|
* always share their scope with compile-time prototypes. |
4006 |
|
*/ |
4007 |
|
JS_ASSERT(pobj == OBJ_GET_PROTO(cx, obj)); |
4008 |
|
JS_ASSERT(OBJ_SCOPE(obj)->object == pobj); |
4009 |
|
JS_ASSERT(protoIndex == 1); |
4010 |
} else { |
} else { |
4011 |
PCMETER(JS_PROPERTY_CACHE(cx).nofills++); |
/* Call and DeclEnvClass objects have no prototypes. */ |
4012 |
*entryp = NULL; |
JS_ASSERT(!OBJ_GET_PROTO(cx, obj)); |
4013 |
|
JS_ASSERT(protoIndex == 0); |
4014 |
} |
} |
4015 |
} |
} |
4016 |
|
#endif |
4017 |
|
if (cacheResult) { |
4018 |
|
entry = js_FillPropertyCache(cx, scopeChain, |
4019 |
|
scopeIndex, protoIndex, pobj, |
4020 |
|
(JSScopeProperty *) prop, false); |
4021 |
|
} |
4022 |
SCOPE_DEPTH_ACCUM(&rt->scopeSearchDepthStats, scopeIndex); |
SCOPE_DEPTH_ACCUM(&rt->scopeSearchDepthStats, scopeIndex); |
4023 |
*objp = obj; |
goto out; |
|
*pobjp = pobj; |
|
|
*propp = prop; |
|
|
return scopeIndex; |
|
4024 |
} |
} |
4025 |
|
|
4026 |
lastobj = obj; |
if (!parent) { |
4027 |
obj = OBJ_GET_PARENT(cx, obj); |
pobj = NULL; |
4028 |
if (!obj) |
goto out; |
4029 |
|
} |
4030 |
|
obj = parent; |
4031 |
|
parent = OBJ_GET_PARENT(cx, obj); |
4032 |
|
} |
4033 |
|
|
4034 |
|
for (;;) { |
4035 |
|
if (!OBJ_LOOKUP_PROPERTY(cx, obj, id, &pobj, &prop)) |
4036 |
|
return NULL; |
4037 |
|
if (prop) { |
4038 |
|
PCMETER(JS_PROPERTY_CACHE(cx).nofills++); |
4039 |
|
goto out; |
4040 |
|
} |
4041 |
|
|
4042 |
|
/* |
4043 |
|
* We conservatively assume that a resolve hook could mutate the scope |
4044 |
|
* chain during OBJ_LOOKUP_PROPERTY. So we read parent here again. |
4045 |
|
*/ |
4046 |
|
parent = OBJ_GET_PARENT(cx, obj); |
4047 |
|
if (!parent) { |
4048 |
|
pobj = NULL; |
4049 |
break; |
break; |
4050 |
|
} |
4051 |
|
obj = parent; |
4052 |
} |
} |
4053 |
|
|
4054 |
*objp = lastobj; |
out: |
4055 |
*pobjp = NULL; |
JS_ASSERT(!!pobj == !!prop); |
4056 |
*propp = NULL; |
*objp = obj; |
4057 |
return scopeIndex; |
*pobjp = pobj; |
4058 |
|
*propp = prop; |
4059 |
|
return entry; |
4060 |
} |
} |
4061 |
|
|
4062 |
JS_FRIEND_API(JSBool) |
JS_FRIEND_API(JSBool) |
4063 |
js_FindProperty(JSContext *cx, jsid id, JSObject **objp, JSObject **pobjp, |
js_FindProperty(JSContext *cx, jsid id, JSObject **objp, JSObject **pobjp, |
4064 |
JSProperty **propp) |
JSProperty **propp) |
4065 |
{ |
{ |
4066 |
return js_FindPropertyHelper(cx, id, objp, pobjp, propp, NULL) >= 0; |
return !!js_FindPropertyHelper(cx, id, false, objp, pobjp, propp); |
4067 |
} |
} |
4068 |
|
|
4069 |
JSObject * |
JSObject * |
4070 |
js_FindIdentifierBase(JSContext *cx, jsid id, JSPropCacheEntry *entry) |
js_FindIdentifierBase(JSContext *cx, JSObject *scopeChain, jsid id) |
4071 |
{ |
{ |
|
JSObject *obj, *pobj; |
|
|
JSProperty *prop; |
|
|
|
|
4072 |
/* |
/* |
4073 |
* Look for id's property along the "with" statement chain and the |
* This function should not be called for a global object or from the |
4074 |
* statically-linked scope chain. |
* trace and should have a valid cache entry for native scopeChain. |
4075 |
*/ |
*/ |
4076 |
if (js_FindPropertyHelper(cx, id, &obj, &pobj, &prop, &entry) < 0) |
JS_ASSERT(OBJ_GET_PARENT(cx, scopeChain)); |
4077 |
return NULL; |
JS_ASSERT(!JS_ON_TRACE(cx)); |
|
if (prop) { |
|
|
OBJ_DROP_PROPERTY(cx, pobj, prop); |
|
|
return obj; |
|
|
} |
|
4078 |
|
|
4079 |
/* |
JSObject *obj = scopeChain; |
|
* Use the top-level scope from the scope chain, which won't end in the |
|
|
* same scope as cx->globalObject for cross-context function calls. |
|
|
*/ |
|
|
JS_ASSERT(obj); |
|
4080 |
|
|
4081 |
/* |
/* |
4082 |
* Property not found. Give a strict warning if binding an undeclared |
* Loop over cacheable objects on the scope chain until we find a |
4083 |
* top-level variable. |
* property. We also stop when we reach the global object skipping any |
4084 |
|
* farther checks or lookups. For details see the JSOP_BINDNAME case of |
4085 |
|
* js_Interpret. |
4086 |
*/ |
*/ |
4087 |
if (JS_HAS_STRICT_OPTION(cx)) { |
for (int scopeIndex = 0; IsCacheableNonGlobalScope(obj); scopeIndex++) { |
4088 |
JSString *str = JSVAL_TO_STRING(ID_TO_VALUE(id)); |
JSObject *pobj; |
4089 |
const char *bytes = js_GetStringBytes(cx, str); |
JSProperty *prop; |
4090 |
|
int protoIndex = js_LookupPropertyWithFlags(cx, obj, id, |
4091 |
if (!bytes || |
cx->resolveFlags, |
4092 |
!JS_ReportErrorFlagsAndNumber(cx, |
&pobj, &prop); |
4093 |
JSREPORT_WARNING | JSREPORT_STRICT, |
if (protoIndex < 0) |
|
js_GetErrorMessage, NULL, |
|
|
JSMSG_UNDECLARED_VAR, bytes)) { |
|
4094 |
return NULL; |
return NULL; |
4095 |
|
if (prop) { |
4096 |
|
JS_ASSERT(OBJ_IS_NATIVE(pobj)); |
4097 |
|
JS_ASSERT(OBJ_GET_CLASS(cx, pobj) == OBJ_GET_CLASS(cx, obj)); |
4098 |
|
#ifdef DEBUG |
4099 |
|
JSPropCacheEntry *entry = |
4100 |
|
#endif |
4101 |
|
js_FillPropertyCache(cx, scopeChain, |
4102 |
|
scopeIndex, protoIndex, pobj, |
4103 |
|
(JSScopeProperty *) prop, false); |
4104 |
|
JS_ASSERT(entry); |
4105 |
|
JS_UNLOCK_OBJ(cx, pobj); |
4106 |
|
return obj; |
4107 |
} |
} |
4108 |
|
|
4109 |
|
/* Call and other cacheable objects always have a parent. */ |
4110 |
|
obj = OBJ_GET_PARENT(cx, obj); |
4111 |
|
if (!OBJ_GET_PARENT(cx, obj)) |
4112 |
|
return obj; |
4113 |
} |
} |
4114 |
|
|
4115 |
|
/* Loop until we find a property or reach the global object. */ |
4116 |
|
do { |
4117 |
|
JSObject *pobj; |
4118 |
|
JSProperty *prop; |
4119 |
|
if (!OBJ_LOOKUP_PROPERTY(cx, obj, id, &pobj, &prop)) |
4120 |
|
return NULL; |
4121 |
|
if (prop) { |
4122 |
|
OBJ_DROP_PROPERTY(cx, pobj, prop); |
4123 |
|
break; |
4124 |
|
} |
4125 |
|
|
4126 |
|
/* |
4127 |
|
* We conservatively assume that a resolve hook could mutate the scope |
4128 |
|
* chain during OBJ_LOOKUP_PROPERTY. So we must check if parent is not |
4129 |
|
* null here even if it wasn't before the lookup. |
4130 |
|
*/ |
4131 |
|
JSObject *parent = OBJ_GET_PARENT(cx, obj); |
4132 |
|
if (!parent) |
4133 |
|
break; |
4134 |
|
obj = parent; |
4135 |
|
} while (OBJ_GET_PARENT(cx, obj)); |
4136 |
return obj; |
return obj; |
4137 |
} |
} |
4138 |
|
|
4140 |
js_NativeGet(JSContext *cx, JSObject *obj, JSObject *pobj, |
js_NativeGet(JSContext *cx, JSObject *obj, JSObject *pobj, |
4141 |
JSScopeProperty *sprop, jsval *vp) |
JSScopeProperty *sprop, jsval *vp) |
4142 |
{ |
{ |
4143 |
|
js_LeaveTraceIfGlobalObject(cx, pobj); |
4144 |
|
|
4145 |
JSScope *scope; |
JSScope *scope; |
4146 |
uint32 slot; |
uint32 slot; |
4147 |
int32 sample; |
int32 sample; |
4148 |
JSTempValueRooter tvr; |
JSTempValueRooter tvr, tvr2; |
4149 |
JSBool ok; |
JSBool ok; |
4150 |
|
|
4151 |
JS_ASSERT(OBJ_IS_NATIVE(pobj)); |
JS_ASSERT(OBJ_IS_NATIVE(pobj)); |
4163 |
sample = cx->runtime->propertyRemovals; |
sample = cx->runtime->propertyRemovals; |
4164 |
JS_UNLOCK_SCOPE(cx, scope); |
JS_UNLOCK_SCOPE(cx, scope); |
4165 |
JS_PUSH_TEMP_ROOT_SPROP(cx, sprop, &tvr); |
JS_PUSH_TEMP_ROOT_SPROP(cx, sprop, &tvr); |
4166 |
ok = SPROP_GET(cx, sprop, obj, pobj, vp); |
JS_PUSH_TEMP_ROOT_OBJECT(cx, pobj, &tvr2); |
4167 |
|
ok = js_GetSprop(cx, sprop, obj, vp); |
4168 |
|
JS_POP_TEMP_ROOT(cx, &tvr2); |
4169 |
JS_POP_TEMP_ROOT(cx, &tvr); |
JS_POP_TEMP_ROOT(cx, &tvr); |
4170 |
if (!ok) |
if (!ok) |
4171 |
return JS_FALSE; |
return JS_FALSE; |
4184 |
JSBool |
JSBool |
4185 |
js_NativeSet(JSContext *cx, JSObject *obj, JSScopeProperty *sprop, jsval *vp) |
js_NativeSet(JSContext *cx, JSObject *obj, JSScopeProperty *sprop, jsval *vp) |
4186 |
{ |
{ |
4187 |
|
js_LeaveTraceIfGlobalObject(cx, obj); |
4188 |
|
|
4189 |
JSScope *scope; |
JSScope *scope; |
4190 |
uint32 slot; |
uint32 slot; |
4191 |
int32 sample; |
int32 sample; |
4192 |
JSTempValueRooter tvr; |
JSTempValueRooter tvr; |
4193 |
bool ok; |
JSBool ok; |
4194 |
|
|
4195 |
JS_ASSERT(OBJ_IS_NATIVE(obj)); |
JS_ASSERT(OBJ_IS_NATIVE(obj)); |
4196 |
JS_ASSERT(JS_IS_OBJ_LOCKED(cx, obj)); |
JS_ASSERT(JS_IS_OBJ_LOCKED(cx, obj)); |
4197 |
scope = OBJ_SCOPE(obj); |
scope = OBJ_SCOPE(obj); |
4198 |
JS_ASSERT(scope->object == obj); |
JS_ASSERT(scope->object == obj || (sprop->attrs & JSPROP_SHARED)); |
4199 |
|
|
4200 |
slot = sprop->slot; |
slot = sprop->slot; |
4201 |
if (slot != SPROP_INVALID_SLOT) { |
if (slot != SPROP_INVALID_SLOT) { |
4209 |
* Allow API consumers to create shared properties with stub setters. |
* Allow API consumers to create shared properties with stub setters. |
4210 |
* Such properties lack value storage, so setting them is like writing |
* Such properties lack value storage, so setting them is like writing |
4211 |
* to /dev/null. |
* to /dev/null. |
4212 |
|
* |
4213 |
|
* But we can't short-circuit if there's a scripted getter or setter |
4214 |
|
* since we might need to throw. In that case, we let SPROP_SET |
4215 |
|
* decide whether to throw an exception. See bug 478047. |
4216 |
*/ |
*/ |
4217 |
if (SPROP_HAS_STUB_SETTER(sprop)) |
if (!(sprop->attrs & JSPROP_GETTER) && SPROP_HAS_STUB_SETTER(sprop)) { |
4218 |
|
JS_ASSERT(!(sprop->attrs & JSPROP_SETTER)); |
4219 |
return JS_TRUE; |
return JS_TRUE; |
4220 |
|
} |
4221 |
} |
} |
4222 |
|
|
4223 |
sample = cx->runtime->propertyRemovals; |
sample = cx->runtime->propertyRemovals; |
4224 |
JS_UNLOCK_SCOPE(cx, scope); |
JS_UNLOCK_SCOPE(cx, scope); |
4225 |
JS_PUSH_TEMP_ROOT_SPROP(cx, sprop, &tvr); |
JS_PUSH_TEMP_ROOT_SPROP(cx, sprop, &tvr); |
4226 |
ok = SPROP_SET(cx, sprop, obj, obj, vp); |
ok = js_SetSprop(cx, sprop, obj, vp); |
4227 |
JS_POP_TEMP_ROOT(cx, &tvr); |
JS_POP_TEMP_ROOT(cx, &tvr); |
4228 |
if (!ok) |
if (!ok) |
4229 |
return JS_FALSE; |
return JS_FALSE; |
4230 |
|
|
4231 |
JS_LOCK_SCOPE(cx, scope); |
JS_LOCK_SCOPE(cx, scope); |
4232 |
JS_ASSERT(scope->object == obj); |
JS_ASSERT(scope->object == obj || (sprop->attrs & JSPROP_SHARED)); |
4233 |
if (SLOT_IN_SCOPE(slot, scope) && |
if (SLOT_IN_SCOPE(slot, scope) && |
4234 |
(JS_LIKELY(cx->runtime->propertyRemovals == sample) || |
(JS_LIKELY(cx->runtime->propertyRemovals == sample) || |
4235 |
SCOPE_GET_PROPERTY(scope, sprop->id) == sprop)) { |
SCOPE_GET_PROPERTY(scope, sprop->id) == sprop)) { |
4241 |
} |
} |
4242 |
|
|
4243 |
JSBool |
JSBool |
4244 |
js_GetPropertyHelper(JSContext *cx, JSObject *obj, jsid id, jsval *vp, |
js_GetPropertyHelper(JSContext *cx, JSObject *obj, jsid id, JSBool cacheResult, |
4245 |
JSPropCacheEntry **entryp) |
jsval *vp) |
4246 |
{ |
{ |
4247 |
uint32 shape; |
JSObject *aobj, *obj2; |
4248 |
int protoIndex; |
int protoIndex; |
|
JSObject *obj2; |
|
4249 |
JSProperty *prop; |
JSProperty *prop; |
4250 |
JSScopeProperty *sprop; |
JSScopeProperty *sprop; |
4251 |
|
|
4252 |
|
JS_ASSERT_IF(cacheResult, !JS_ON_TRACE(cx)); |
4253 |
/* Convert string indices to integers if appropriate. */ |
/* Convert string indices to integers if appropriate. */ |
4254 |
CHECK_FOR_STRING_INDEX(id); |
CHECK_FOR_STRING_INDEX(id); |
|
JS_COUNT_OPERATION(cx, JSOW_GET_PROPERTY); |
|
4255 |
|
|
4256 |
shape = OBJ_SHAPE(obj); |
aobj = js_GetProtoIfDenseArray(cx, obj); |
4257 |
protoIndex = js_LookupPropertyWithFlags(cx, obj, id, cx->resolveFlags, |
protoIndex = js_LookupPropertyWithFlags(cx, aobj, id, cx->resolveFlags, |
4258 |
&obj2, &prop); |
&obj2, &prop); |
4259 |
if (protoIndex < 0) |
if (protoIndex < 0) |
4260 |
return JS_FALSE; |
return JS_FALSE; |
4261 |
if (!prop) { |
if (!prop) { |
|
jsbytecode *pc; |
|
|
|
|
4262 |
*vp = JSVAL_VOID; |
*vp = JSVAL_VOID; |
4263 |
|
|
4264 |
if (!OBJ_GET_CLASS(cx, obj)->getProperty(cx, obj, ID_TO_VALUE(id), vp)) |
if (!OBJ_GET_CLASS(cx, obj)->getProperty(cx, obj, ID_TO_VALUE(id), vp)) |
4265 |
return JS_FALSE; |
return JS_FALSE; |
4266 |
|
|
4267 |
if (entryp) { |
PCMETER(cacheResult && JS_PROPERTY_CACHE(cx).nofills++); |
|
PCMETER(JS_PROPERTY_CACHE(cx).nofills++); |
|
|
*entryp = NULL; |
|
|
} |
|
4268 |
|
|
4269 |
/* |
/* |
4270 |
* Give a strict warning if foo.bar is evaluated by a script for an |
* Give a strict warning if foo.bar is evaluated by a script for an |
4271 |
* object foo with no property named 'bar'. |
* object foo with no property named 'bar'. |
4272 |
*/ |
*/ |
4273 |
if (JSVAL_IS_VOID(*vp) && cx->fp && cx->fp->regs) { |
jsbytecode *pc; |
4274 |
|
if (JSVAL_IS_VOID(*vp) && ((pc = js_GetCurrentBytecodePC(cx)) != NULL)) { |
4275 |
JSOp op; |
JSOp op; |
4276 |
uintN flags; |
uintN flags; |
4277 |
|
|
|
pc = cx->fp->regs->pc; |
|
4278 |
op = (JSOp) *pc; |
op = (JSOp) *pc; |
4279 |
|
if (op == JSOP_TRAP) { |
4280 |
|
JS_ASSERT_NOT_ON_TRACE(cx); |
4281 |
|
op = JS_GetTrapOpcode(cx, cx->fp->script, pc); |
4282 |
|
} |
4283 |
if (op == JSOP_GETXPROP) { |
if (op == JSOP_GETXPROP) { |
4284 |
flags = JSREPORT_ERROR; |
flags = JSREPORT_ERROR; |
4285 |
} else { |
} else { |
4295 |
if (id == ATOM_TO_JSID(cx->runtime->atomState.iteratorAtom)) |
if (id == ATOM_TO_JSID(cx->runtime->atomState.iteratorAtom)) |
4296 |
return JS_TRUE; |
return JS_TRUE; |
4297 |
|
|
4298 |
/* Kludge to allow (typeof foo == "undefined") tests. */ |
/* Do not warn about tests like (obj[prop] == undefined). */ |
4299 |
JS_ASSERT(cx->fp->script); |
if (cx->resolveFlags == JSRESOLVE_INFER) { |
4300 |
pc += js_CodeSpec[op].length; |
js_LeaveTrace(cx); |
4301 |
if (Detecting(cx, pc)) |
pc += js_CodeSpec[op].length; |
4302 |
|
if (Detecting(cx, pc)) |
4303 |
|
return JS_TRUE; |
4304 |
|
} else if (cx->resolveFlags & JSRESOLVE_DETECTING) { |
4305 |
return JS_TRUE; |
return JS_TRUE; |
4306 |
|
} |
4307 |
|
|
4308 |
flags = JSREPORT_WARNING | JSREPORT_STRICT; |
flags = JSREPORT_WARNING | JSREPORT_STRICT; |
4309 |
} |
} |
4324 |
} |
} |
4325 |
|
|
4326 |
sprop = (JSScopeProperty *) prop; |
sprop = (JSScopeProperty *) prop; |
4327 |
|
|
4328 |
|
if (cacheResult) { |
4329 |
|
JS_ASSERT_NOT_ON_TRACE(cx); |
4330 |
|
js_FillPropertyCache(cx, aobj, 0, protoIndex, obj2, sprop, false); |
4331 |
|
} |
4332 |
|
|
4333 |
if (!js_NativeGet(cx, obj, obj2, sprop, vp)) |
if (!js_NativeGet(cx, obj, obj2, sprop, vp)) |
4334 |
return JS_FALSE; |
return JS_FALSE; |
4335 |
|
|
|
if (entryp) |
|
|
js_FillPropertyCache(cx, obj, shape, 0, protoIndex, obj2, sprop, entryp); |
|
4336 |
JS_UNLOCK_OBJ(cx, obj2); |
JS_UNLOCK_OBJ(cx, obj2); |
4337 |
return JS_TRUE; |
return JS_TRUE; |
4338 |
} |
} |
4340 |
JSBool |
JSBool |
4341 |
js_GetProperty(JSContext *cx, JSObject *obj, jsid id, jsval *vp) |
js_GetProperty(JSContext *cx, JSObject *obj, jsid id, jsval *vp) |
4342 |
{ |
{ |
4343 |
return js_GetPropertyHelper(cx, obj, id, vp, NULL); |
return js_GetPropertyHelper(cx, obj, id, false, vp); |
4344 |
} |
} |
4345 |
|
|
4346 |
JSBool |
JSBool |
4347 |
js_SetPropertyHelper(JSContext *cx, JSObject *obj, jsid id, jsval *vp, |
js_GetMethod(JSContext *cx, JSObject *obj, jsid id, JSBool cacheResult, |
4348 |
JSPropCacheEntry **entryp) |
jsval *vp) |
4349 |
|
{ |
4350 |
|
if (obj->map->ops == &js_ObjectOps || |
4351 |
|
obj->map->ops->getProperty == js_GetProperty) { |
4352 |
|
return js_GetPropertyHelper(cx, obj, id, cacheResult, vp); |
4353 |
|
} |
4354 |
|
JS_ASSERT_IF(cacheResult, OBJ_IS_DENSE_ARRAY(cx, obj)); |
4355 |
|
#if JS_HAS_XML_SUPPORT |
4356 |
|
if (OBJECT_IS_XML(cx, obj)) |
4357 |
|
return js_GetXMLMethod(cx, obj, id, vp); |
4358 |
|
#endif |
4359 |
|
return OBJ_GET_PROPERTY(cx, obj, id, vp); |
4360 |
|
} |
4361 |
|
|
4362 |
|
JS_FRIEND_API(JSBool) |
4363 |
|
js_CheckUndeclaredVarAssignment(JSContext *cx) |
4364 |
|
{ |
4365 |
|
JSStackFrame *fp; |
4366 |
|
if (!JS_HAS_STRICT_OPTION(cx) || |
4367 |
|
!(fp = js_GetTopStackFrame(cx)) || |
4368 |
|
!fp->regs || |
4369 |
|
js_GetOpcode(cx, fp->script, fp->regs->pc) != JSOP_SETNAME) { |
4370 |
|
return JS_TRUE; |
4371 |
|
} |
4372 |
|
|
4373 |
|
JSAtom *atom; |
4374 |
|
GET_ATOM_FROM_BYTECODE(fp->script, fp->regs->pc, 0, atom); |
4375 |
|
|
4376 |
|
const char *bytes = js_AtomToPrintableString(cx, atom); |
4377 |
|
return bytes && |
4378 |
|
JS_ReportErrorFlagsAndNumber(cx, JSREPORT_WARNING | JSREPORT_STRICT, |
4379 |
|
js_GetErrorMessage, NULL, |
4380 |
|
JSMSG_UNDECLARED_VAR, bytes); |
4381 |
|
} |
4382 |
|
|
4383 |
|
/* |
4384 |
|
* Note: all non-error exits in this function must notify the tracer using |
4385 |
|
* SetPropHit when called from the interpreter loop (cacheResult is true). |
4386 |
|
*/ |
4387 |
|
JSBool |
4388 |
|
js_SetPropertyHelper(JSContext *cx, JSObject *obj, jsid id, JSBool cacheResult, |
4389 |
|
jsval *vp) |
4390 |
{ |
{ |
|
uint32 shape; |
|
4391 |
int protoIndex; |
int protoIndex; |
4392 |
JSObject *pobj; |
JSObject *pobj; |
4393 |
JSProperty *prop; |
JSProperty *prop; |
4397 |
intN shortid; |
intN shortid; |
4398 |
JSClass *clasp; |
JSClass *clasp; |
4399 |
JSPropertyOp getter, setter; |
JSPropertyOp getter, setter; |
4400 |
|
bool added; |
4401 |
|
|
4402 |
|
if (cacheResult) |
4403 |
|
JS_ASSERT_NOT_ON_TRACE(cx); |
4404 |
|
|
4405 |
/* Convert string indices to integers if appropriate. */ |
/* Convert string indices to integers if appropriate. */ |
4406 |
CHECK_FOR_STRING_INDEX(id); |
CHECK_FOR_STRING_INDEX(id); |
|
JS_COUNT_OPERATION(cx, JSOW_SET_PROPERTY); |
|
4407 |
|
|
4408 |
shape = OBJ_SHAPE(obj); |
/* |
4409 |
|
* We peek at OBJ_SCOPE(obj) without locking obj. Any race means a failure |
4410 |
|
* to seal before sharing, which is inherently ambiguous. |
4411 |
|
*/ |
4412 |
|
if (SCOPE_IS_SEALED(OBJ_SCOPE(obj)) && OBJ_SCOPE(obj)->object == obj) { |
4413 |
|
flags = JSREPORT_ERROR; |
4414 |
|
goto read_only_error; |
4415 |
|
} |
4416 |
|
|
4417 |
protoIndex = js_LookupPropertyWithFlags(cx, obj, id, cx->resolveFlags, |
protoIndex = js_LookupPropertyWithFlags(cx, obj, id, cx->resolveFlags, |
4418 |
&pobj, &prop); |
&pobj, &prop); |
4419 |
if (protoIndex < 0) |
if (protoIndex < 0) |
4420 |
return JS_FALSE; |
return JS_FALSE; |
4421 |
|
if (prop) { |
4422 |
|
if (!OBJ_IS_NATIVE(pobj)) { |
4423 |
|
OBJ_DROP_PROPERTY(cx, pobj, prop); |
4424 |
|
prop = NULL; |
4425 |
|
} |
4426 |
|
} else { |
4427 |
|
/* We should never add properties to lexical blocks. */ |
4428 |
|
JS_ASSERT(OBJ_GET_CLASS(cx, obj) != &js_BlockClass); |
4429 |
|
|
4430 |
if (prop && !OBJ_IS_NATIVE(pobj)) { |
if (!OBJ_GET_PARENT(cx, obj) && !js_CheckUndeclaredVarAssignment(cx)) |
4431 |
OBJ_DROP_PROPERTY(cx, pobj, prop); |
return JS_FALSE; |
|
prop = NULL; |
|
4432 |
} |
} |
4433 |
sprop = (JSScopeProperty *) prop; |
sprop = (JSScopeProperty *) prop; |
4434 |
|
|
4457 |
|
|
4458 |
attrs = sprop->attrs; |
attrs = sprop->attrs; |
4459 |
if ((attrs & JSPROP_READONLY) || |
if ((attrs & JSPROP_READONLY) || |
4460 |
(SCOPE_IS_SEALED(scope) && pobj == obj)) { |
(SCOPE_IS_SEALED(scope) && (attrs & JSPROP_SHARED))) { |
4461 |
JS_UNLOCK_SCOPE(cx, scope); |
JS_UNLOCK_SCOPE(cx, scope); |
4462 |
|
|
4463 |
/* |
/* |
4472 |
if (attrs & JSPROP_READONLY) { |
if (attrs & JSPROP_READONLY) { |
4473 |
if (!JS_HAS_STRICT_OPTION(cx)) { |
if (!JS_HAS_STRICT_OPTION(cx)) { |
4474 |
/* Just return true per ECMA if not in strict mode. */ |
/* Just return true per ECMA if not in strict mode. */ |
4475 |
PCMETER(!entryp || JS_PROPERTY_CACHE(cx).rofills++); |
PCMETER(cacheResult && JS_PROPERTY_CACHE(cx).rofills++); |
4476 |
|
if (cacheResult) |
4477 |
|
TRACE_2(SetPropHit, JS_NO_PROP_CACHE_FILL, sprop); |
4478 |
return JS_TRUE; |
return JS_TRUE; |
4479 |
|
error: // TRACE_2 jumps here in case of error. |
4480 |
|
return JS_FALSE; |
4481 |
} |
} |
4482 |
|
|
4483 |
/* Strict mode: report a read-only strict warning. */ |
/* Strict mode: report a read-only strict warning. */ |
4496 |
*/ |
*/ |
4497 |
JS_UNLOCK_SCOPE(cx, scope); |
JS_UNLOCK_SCOPE(cx, scope); |
4498 |
|
|
4499 |
/* |
/* Don't clone a shared prototype property. */ |
|
* Don't clone a shared prototype property. Don't fill it in the |
|
|
* property cache either, since the JSOP_SETPROP/JSOP_SETNAME code |
|
|
* in js_Interpret does not handle shared or prototype properties. |
|
|
* Shared prototype properties require more hit qualification than |
|
|
* the fast-path code for those ops, which is targeted on direct, |
|
|
* slot-based properties. |
|
|
*/ |
|
4500 |
if (attrs & JSPROP_SHARED) { |
if (attrs & JSPROP_SHARED) { |
4501 |
if (entryp) { |
if (cacheResult) { |
4502 |
PCMETER(JS_PROPERTY_CACHE(cx).nofills++); |
JSPropCacheEntry *entry; |
4503 |
*entryp = NULL; |
entry = js_FillPropertyCache(cx, obj, 0, protoIndex, pobj, sprop, false); |
4504 |
|
TRACE_2(SetPropHit, entry, sprop); |
4505 |
} |
} |
4506 |
|
|
4507 |
if (SPROP_HAS_STUB_SETTER(sprop) && |
if (SPROP_HAS_STUB_SETTER(sprop) && |
4509 |
return JS_TRUE; |
return JS_TRUE; |
4510 |
} |
} |
4511 |
|
|
4512 |
return !!SPROP_SET(cx, sprop, obj, pobj, vp); |
return js_SetSprop(cx, sprop, obj, vp); |
4513 |
} |
} |
4514 |
|
|
4515 |
/* Restore attrs to the ECMA default for new properties. */ |
/* Restore attrs to the ECMA default for new properties. */ |
4541 |
#endif |
#endif |
4542 |
} |
} |
4543 |
|
|
4544 |
|
added = false; |
4545 |
if (!sprop) { |
if (!sprop) { |
|
if (SCOPE_IS_SEALED(OBJ_SCOPE(obj)) && OBJ_SCOPE(obj)->object == obj) { |
|
|
flags = JSREPORT_ERROR; |
|
|
goto read_only_error; |
|
|
} |
|
|
|
|
4546 |
/* |
/* |
4547 |
* Purge the property cache of now-shadowed id in obj's scope chain. |
* Purge the property cache of now-shadowed id in obj's scope chain. |
4548 |
* Do this early, before locking obj to avoid nesting locks. |
* Do this early, before locking obj to avoid nesting locks. |
4549 |
*/ |
*/ |
4550 |
PurgeScopeChain(cx, obj, id); |
js_PurgeScopeChain(cx, obj, id); |
4551 |
|
|
4552 |
/* Find or make a property descriptor with the right heritage. */ |
/* Find or make a property descriptor with the right heritage. */ |
4553 |
JS_LOCK_OBJ(cx, obj); |
JS_LOCK_OBJ(cx, obj); |
4578 |
js_RemoveScopeProperty(cx, scope, id); |
js_RemoveScopeProperty(cx, scope, id); |
4579 |
JS_UNLOCK_SCOPE(cx, scope); |
JS_UNLOCK_SCOPE(cx, scope); |
4580 |
return JS_FALSE); |
return JS_FALSE); |
4581 |
|
added = true; |
4582 |
|
} |
4583 |
|
|
4584 |
|
if (cacheResult) { |
4585 |
|
JSPropCacheEntry *entry; |
4586 |
|
entry = js_FillPropertyCache(cx, obj, 0, 0, obj, sprop, added); |
4587 |
|
TRACE_2(SetPropHit, entry, sprop); |
4588 |
} |
} |
4589 |
|
|
4590 |
if (!js_NativeSet(cx, obj, sprop, vp)) |
if (!js_NativeSet(cx, obj, sprop, vp)) |
4591 |
return JS_FALSE; |
return NULL; |
4592 |
|
|
|
if (entryp) { |
|
|
if (!(attrs & JSPROP_SHARED)) |
|
|
js_FillPropertyCache(cx, obj, shape, 0, 0, obj, sprop, entryp); |
|
|
else |
|
|
PCMETER(JS_PROPERTY_CACHE(cx).nofills++); |
|
|
} |
|
4593 |
JS_UNLOCK_SCOPE(cx, scope); |
JS_UNLOCK_SCOPE(cx, scope); |
4594 |
return JS_TRUE; |
return JS_TRUE; |
4595 |
|
|
4602 |
JSBool |
JSBool |
4603 |
js_SetProperty(JSContext *cx, JSObject *obj, jsid id, jsval *vp) |
js_SetProperty(JSContext *cx, JSObject *obj, jsid id, jsval *vp) |
4604 |
{ |
{ |
4605 |
return js_SetPropertyHelper(cx, obj, id, vp, NULL); |
return js_SetPropertyHelper(cx, obj, id, false, vp); |
4606 |
} |
} |
4607 |
|
|
4608 |
JSBool |
JSBool |
4673 |
|
|
4674 |
/* Convert string indices to integers if appropriate. */ |
/* Convert string indices to integers if appropriate. */ |
4675 |
CHECK_FOR_STRING_INDEX(id); |
CHECK_FOR_STRING_INDEX(id); |
|
JS_COUNT_OPERATION(cx, JSOW_DELETE_PROPERTY); |
|
4676 |
|
|
4677 |
if (!js_LookupProperty(cx, obj, id, &proto, &prop)) |
if (!js_LookupProperty(cx, obj, id, &proto, &prop)) |
4678 |
return JS_FALSE; |
return JS_FALSE; |
5198 |
if (VALUE_IS_FUNCTION(cx, fval)) { |
if (VALUE_IS_FUNCTION(cx, fval)) { |
5199 |
if (!GetCurrentExecutionContext(cx, obj, &nargv[2])) |
if (!GetCurrentExecutionContext(cx, obj, &nargv[2])) |
5200 |
return JS_FALSE; |
return JS_FALSE; |
5201 |
args = js_GetArgsObject(cx, cx->fp); |
args = js_GetArgsObject(cx, js_GetTopStackFrame(cx)); |
5202 |
if (!args) |
if (!args) |
5203 |
return JS_FALSE; |
return JS_FALSE; |
5204 |
nargv[0] = OBJECT_TO_JSVAL(obj); |
nargv[0] = OBJECT_TO_JSVAL(obj); |
5212 |
return ok; |
return ok; |
5213 |
} |
} |
5214 |
#endif |
#endif |
5215 |
js_ReportIsNotFunction(cx, &argv[-2], cx->fp->flags & JSFRAME_ITERATOR); |
js_ReportIsNotFunction(cx, &argv[-2], js_GetTopStackFrame(cx)->flags & JSFRAME_ITERATOR); |
5216 |
return JS_FALSE; |
return JS_FALSE; |
5217 |
} |
} |
5218 |
return clasp->call(cx, obj, argc, argv, rval); |
return clasp->call(cx, obj, argc, argv, rval); |
5241 |
if (VALUE_IS_FUNCTION(cx, cval)) { |
if (VALUE_IS_FUNCTION(cx, cval)) { |
5242 |
if (!GetCurrentExecutionContext(cx, obj, &nargv[1])) |
if (!GetCurrentExecutionContext(cx, obj, &nargv[1])) |
5243 |
return JS_FALSE; |
return JS_FALSE; |
5244 |
args = js_GetArgsObject(cx, cx->fp); |
args = js_GetArgsObject(cx, js_GetTopStackFrame(cx)); |
5245 |
if (!args) |
if (!args) |
5246 |
return JS_FALSE; |
return JS_FALSE; |
5247 |
nargv[0] = OBJECT_TO_JSVAL(args); |
nargv[0] = OBJECT_TO_JSVAL(args); |
5456 |
obj = JSVAL_TO_OBJECT(v); |
obj = JSVAL_TO_OBJECT(v); |
5457 |
if (!OBJ_DEFAULT_VALUE(cx, obj, JSTYPE_OBJECT, &v)) |
if (!OBJ_DEFAULT_VALUE(cx, obj, JSTYPE_OBJECT, &v)) |
5458 |
return JS_FALSE; |
return JS_FALSE; |
5459 |
if (JSVAL_IS_OBJECT(v)) |
if (!JSVAL_IS_PRIMITIVE(v)) |
5460 |
obj = JSVAL_TO_OBJECT(v); |
obj = JSVAL_TO_OBJECT(v); |
5461 |
} else { |
} else { |
5462 |
if (!js_PrimitiveToObject(cx, &v)) |
if (!js_PrimitiveToObject(cx, &v)) |
5508 |
older = JS_SetErrorReporter(cx, NULL); |
older = JS_SetErrorReporter(cx, NULL); |
5509 |
id = ATOM_TO_JSID(atom); |
id = ATOM_TO_JSID(atom); |
5510 |
fval = JSVAL_VOID; |
fval = JSVAL_VOID; |
5511 |
#if JS_HAS_XML_SUPPORT |
ok = js_GetMethod(cx, obj, id, false, &fval); |
|
if (OBJECT_IS_XML(cx, obj)) { |
|
|
JSXMLObjectOps *ops; |
|
|
|
|
|
ops = (JSXMLObjectOps *) obj->map->ops; |
|
|
obj = ops->getMethod(cx, obj, id, &fval); |
|
|
ok = (obj != NULL); |
|
|
} else |
|
|
#endif |
|
|
{ |
|
|
ok = OBJ_GET_PROPERTY(cx, obj, id, &fval); |
|
|
} |
|
5512 |
if (!ok) |
if (!ok) |
5513 |
JS_ClearPendingException(cx); |
JS_ClearPendingException(cx); |
5514 |
JS_SetErrorReporter(cx, older); |
JS_SetErrorReporter(cx, older); |
5515 |
|
|
5516 |
return JSVAL_IS_PRIMITIVE(fval) || |
if (JSVAL_IS_PRIMITIVE(fval)) |
5517 |
js_InternalCall(cx, obj, fval, argc, argv, rval); |
return JS_TRUE; |
5518 |
|
return js_InternalCall(cx, obj, fval, argc, argv, rval); |
5519 |
} |
} |
5520 |
|
|
5521 |
#if JS_HAS_XDR |
#if JS_HAS_XDR |
5750 |
if (IS_GC_MARKING_TRACER(trc)) { |
if (IS_GC_MARKING_TRACER(trc)) { |
5751 |
uint32 shape, oldshape; |
uint32 shape, oldshape; |
5752 |
|
|
5753 |
shape = ++cx->runtime->shapeGen; |
shape = js_RegenerateShapeForGC(cx); |
|
JS_ASSERT(shape != 0); |
|
|
|
|
5754 |
if (!(sprop->flags & SPROP_MARK)) { |
if (!(sprop->flags & SPROP_MARK)) { |
5755 |
oldshape = sprop->shape; |
oldshape = sprop->shape; |
5756 |
sprop->shape = shape; |
sprop->shape = shape; |
5757 |
sprop->flags |= SPROP_FLAG_SHAPE_REGEN; |
sprop->flags |= SPROP_FLAG_SHAPE_REGEN; |
5758 |
if (scope->shape != oldshape) { |
if (scope->shape != oldshape) |
5759 |
shape = ++cx->runtime->shapeGen; |
shape = js_RegenerateShapeForGC(cx); |
|
JS_ASSERT(shape != 0); |
|
|
} |
|
5760 |
} |
} |
5761 |
|
|
5762 |
scope->shape = shape; |
scope->shape = shape; |
5795 |
* above. |
* above. |
5796 |
*/ |
*/ |
5797 |
nslots = STOBJ_NSLOTS(obj); |
nslots = STOBJ_NSLOTS(obj); |
5798 |
if (scope->object == obj && scope->map.freeslot < nslots) |
if (scope->object == obj && scope->freeslot < nslots) |
5799 |
nslots = scope->map.freeslot; |
nslots = scope->freeslot; |
5800 |
|
|
5801 |
for (i = 0; i != nslots; ++i) { |
for (i = 0; i != nslots; ++i) { |
5802 |
v = STOBJ_GET_SLOT(obj, i); |
v = STOBJ_GET_SLOT(obj, i); |
5830 |
n = JSSLOT_FREE(LOCKED_OBJ_GET_CLASS(obj)); |
n = JSSLOT_FREE(LOCKED_OBJ_GET_CLASS(obj)); |
5831 |
while (--i >= n) |
while (--i >= n) |
5832 |
STOBJ_SET_SLOT(obj, i, JSVAL_VOID); |
STOBJ_SET_SLOT(obj, i, JSVAL_VOID); |
5833 |
scope->map.freeslot = n; |
scope->freeslot = n; |
5834 |
} |
} |
5835 |
JS_UNLOCK_OBJ(cx, obj); |
JS_UNLOCK_OBJ(cx, obj); |
5836 |
} |
} |
5879 |
} |
} |
5880 |
|
|
5881 |
/* Whether or not we grew nslots, we may need to advance freeslot. */ |
/* Whether or not we grew nslots, we may need to advance freeslot. */ |
5882 |
if (scope->object == obj && slot >= scope->map.freeslot) |
if (scope->object == obj && slot >= scope->freeslot) |
5883 |
scope->map.freeslot = slot + 1; |
scope->freeslot = slot + 1; |
5884 |
|
|
5885 |
STOBJ_SET_SLOT(obj, slot, v); |
STOBJ_SET_SLOT(obj, slot, v); |
5886 |
|
GC_POKE(cx, JS_NULL); |
5887 |
JS_UNLOCK_SCOPE(cx, scope); |
JS_UNLOCK_SCOPE(cx, scope); |
5888 |
return JS_TRUE; |
return JS_TRUE; |
5889 |
} |
} |
5905 |
return obj; |
return obj; |
5906 |
} |
} |
5907 |
|
|
5908 |
#if DEBUG |
JSBool |
5909 |
|
js_IsCallable(JSObject *obj, JSContext *cx) |
5910 |
|
{ |
5911 |
|
if (!OBJ_IS_NATIVE(obj)) |
5912 |
|
return obj->map->ops->call != NULL; |
5913 |
|
|
5914 |
|
JS_LOCK_OBJ(cx, obj); |
5915 |
|
JSBool callable = (obj->map->ops == &js_ObjectOps) |
5916 |
|
? HAS_FUNCTION_CLASS(obj) || STOBJ_GET_CLASS(obj)->call |
5917 |
|
: obj->map->ops->call != NULL; |
5918 |
|
JS_UNLOCK_OBJ(cx, obj); |
5919 |
|
return callable; |
5920 |
|
} |
5921 |
|
|
5922 |
|
void |
5923 |
|
js_ReportGetterOnlyAssignment(JSContext *cx) |
5924 |
|
{ |
5925 |
|
JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, |
5926 |
|
JSMSG_GETTER_ONLY, NULL); |
5927 |
|
} |
5928 |
|
|
5929 |
|
|
5930 |
|
JS_FRIEND_API(JSBool) |
5931 |
|
js_GetterOnlyPropertyStub(JSContext *cx, JSObject *obj, jsval id, jsval *vp) |
5932 |
|
{ |
5933 |
|
js_ReportGetterOnlyAssignment(cx); |
5934 |
|
return JS_FALSE; |
5935 |
|
} |
5936 |
|
|
5937 |
|
#ifdef DEBUG |
5938 |
|
|
5939 |
/* |
/* |
5940 |
* Routines to print out values during debugging. These are FRIEND_API to help |
* Routines to print out values during debugging. These are FRIEND_API to help |
6004 |
fprintf(stderr, "null"); |
fprintf(stderr, "null"); |
6005 |
} else if (JSVAL_IS_VOID(val)) { |
} else if (JSVAL_IS_VOID(val)) { |
6006 |
fprintf(stderr, "undefined"); |
fprintf(stderr, "undefined"); |
6007 |
|
} else if (JSVAL_IS_OBJECT(val) && |
6008 |
|
HAS_FUNCTION_CLASS(JSVAL_TO_OBJECT(val))) { |
6009 |
|
JSObject *funobj = JSVAL_TO_OBJECT(val); |
6010 |
|
JSFunction *fun = (JSFunction *) STOBJ_GET_PRIVATE(funobj); |
6011 |
|
fprintf(stderr, "<%s %s at %p (JSFunction at %p)>", |
6012 |
|
fun->atom ? "function" : "unnamed", |
6013 |
|
fun->atom ? JS_GetStringBytes(ATOM_TO_STRING(fun->atom)) : "function", |
6014 |
|
(void *) funobj, |
6015 |
|
(void *) fun); |
6016 |
} else if (JSVAL_IS_OBJECT(val)) { |
} else if (JSVAL_IS_OBJECT(val)) { |
6017 |
JSObject *obj = JSVAL_TO_OBJECT(val); |
JSObject *obj = JSVAL_TO_OBJECT(val); |
6018 |
JSClass *cls = STOBJ_GET_CLASS(obj); |
JSClass *cls = STOBJ_GET_CLASS(obj); |
6019 |
fprintf(stderr, "<%s%s at %p>", |
fprintf(stderr, "<%s%s at %p>", |
6020 |
cls->name, |
cls->name, |
6021 |
cls == &js_ObjectClass ? "" : " object", |
cls == &js_ObjectClass ? "" : " object", |
6022 |
obj); |
(void *) obj); |
6023 |
} else if (JSVAL_IS_INT(val)) { |
} else if (JSVAL_IS_INT(val)) { |
6024 |
fprintf(stderr, "%d", JSVAL_TO_INT(val)); |
fprintf(stderr, "%d", JSVAL_TO_INT(val)); |
6025 |
} else if (JSVAL_IS_STRING(val)) { |
} else if (JSVAL_IS_STRING(val)) { |
6084 |
uint32 i, slots; |
uint32 i, slots; |
6085 |
JSClass *clasp; |
JSClass *clasp; |
6086 |
jsuint reservedEnd; |
jsuint reservedEnd; |
6087 |
JSBool sharesScope = JS_FALSE; |
bool sharesScope = false; |
6088 |
|
|
6089 |
fprintf(stderr, "object %p\n", (void *) obj); |
fprintf(stderr, "object %p\n", (void *) obj); |
6090 |
clasp = STOBJ_GET_CLASS(obj); |
clasp = STOBJ_GET_CLASS(obj); |
6093 |
/* OBJ_IS_DENSE_ARRAY ignores the cx argument. */ |
/* OBJ_IS_DENSE_ARRAY ignores the cx argument. */ |
6094 |
if (OBJ_IS_DENSE_ARRAY(BOGUS_CX, obj)) { |
if (OBJ_IS_DENSE_ARRAY(BOGUS_CX, obj)) { |
6095 |
slots = JS_MIN((jsuint) obj->fslots[JSSLOT_ARRAY_LENGTH], |
slots = JS_MIN((jsuint) obj->fslots[JSSLOT_ARRAY_LENGTH], |
6096 |
ARRAY_DENSE_LENGTH(obj)); |
js_DenseArrayCapacity(obj)); |
6097 |
fprintf(stderr, "elements\n"); |
fprintf(stderr, "elements\n"); |
6098 |
for (i = 0; i < slots; i++) { |
for (i = 0; i < slots; i++) { |
6099 |
fprintf(stderr, " %3d: ", i); |
fprintf(stderr, " %3d: ", i); |
6113 |
|
|
6114 |
sharesScope = (scope->object != obj); |
sharesScope = (scope->object != obj); |
6115 |
if (sharesScope) { |
if (sharesScope) { |
6116 |
fprintf(stderr, "no own properties - see proto (%s at %p)\n", |
if (proto) { |
6117 |
STOBJ_GET_CLASS(proto)->name, proto); |
fprintf(stderr, "no own properties - see proto (%s at %p)\n", |
6118 |
|
STOBJ_GET_CLASS(proto)->name, (void *) proto); |
6119 |
|
} else { |
6120 |
|
fprintf(stderr, "no own properties - null proto\n"); |
6121 |
|
} |
6122 |
} else { |
} else { |
6123 |
fprintf(stderr, "properties:\n"); |
fprintf(stderr, "properties:\n"); |
6124 |
for (JSScopeProperty *sprop = SCOPE_LAST_PROP(scope); sprop; |
for (JSScopeProperty *sprop = SCOPE_LAST_PROP(scope); sprop; |
6139 |
if (clasp->flags & JSCLASS_HAS_PRIVATE) |
if (clasp->flags & JSCLASS_HAS_PRIVATE) |
6140 |
reservedEnd++; |
reservedEnd++; |
6141 |
reservedEnd += JSCLASS_RESERVED_SLOTS(clasp); |
reservedEnd += JSCLASS_RESERVED_SLOTS(clasp); |
6142 |
slots = sharesScope ? reservedEnd : obj->map->freeslot; |
slots = (OBJ_IS_NATIVE(obj) && !sharesScope) |
6143 |
|
? OBJ_SCOPE(obj)->freeslot |
6144 |
|
: STOBJ_NSLOTS(obj); |
6145 |
for (i = 0; i < slots; i++) { |
for (i = 0; i < slots; i++) { |
6146 |
fprintf(stderr, " %3d ", i); |
fprintf(stderr, " %3d ", i); |
6147 |
if (i == JSSLOT_PRIVATE && (clasp->flags & JSCLASS_HAS_PRIVATE)) { |
if (i == JSSLOT_PRIVATE && (clasp->flags & JSCLASS_HAS_PRIVATE)) { |