js/src/vm/ArgumentsObject.cpp

Wed, 31 Dec 2014 06:09:35 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Wed, 31 Dec 2014 06:09:35 +0100
changeset 0
6474c204b198
permissions
-rw-r--r--

Cloned upstream origin tor-browser at tor-browser-31.3.0esr-4.5-1-build1
revision ID fc1c9ff7c1b2defdbc039f12214767608f46423f for hacking purpose.

michael@0 1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
michael@0 2 * vim: set ts=8 sts=4 et sw=4 tw=99:
michael@0 3 * This Source Code Form is subject to the terms of the Mozilla Public
michael@0 4 * License, v. 2.0. If a copy of the MPL was not distributed with this
michael@0 5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
michael@0 6
michael@0 7 #include "vm/ArgumentsObject-inl.h"
michael@0 8
michael@0 9 #include "jsinfer.h"
michael@0 10
michael@0 11 #ifdef JS_ION
michael@0 12 #include "jit/IonFrames.h"
michael@0 13 #endif
michael@0 14 #include "vm/GlobalObject.h"
michael@0 15 #include "vm/Stack.h"
michael@0 16
michael@0 17 #include "jsobjinlines.h"
michael@0 18
michael@0 19 #include "vm/Stack-inl.h"
michael@0 20
michael@0 21 using namespace js;
michael@0 22 using namespace js::gc;
michael@0 23
michael@0 24 static void
michael@0 25 CopyStackFrameArguments(const AbstractFramePtr frame, HeapValue *dst, unsigned totalArgs)
michael@0 26 {
michael@0 27 JS_ASSERT_IF(frame.isInterpreterFrame(), !frame.asInterpreterFrame()->runningInJit());
michael@0 28
michael@0 29 JS_ASSERT(Max(frame.numActualArgs(), frame.numFormalArgs()) == totalArgs);
michael@0 30
michael@0 31 /* Copy arguments. */
michael@0 32 Value *src = frame.argv();
michael@0 33 Value *end = src + totalArgs;
michael@0 34 while (src != end)
michael@0 35 (dst++)->init(*src++);
michael@0 36 }
michael@0 37
michael@0 38 /* static */ void
michael@0 39 ArgumentsObject::MaybeForwardToCallObject(AbstractFramePtr frame, JSObject *obj,
michael@0 40 ArgumentsData *data)
michael@0 41 {
michael@0 42 JSScript *script = frame.script();
michael@0 43 if (frame.fun()->isHeavyweight() && script->argsObjAliasesFormals()) {
michael@0 44 obj->initFixedSlot(MAYBE_CALL_SLOT, ObjectValue(frame.callObj()));
michael@0 45 for (AliasedFormalIter fi(script); fi; fi++)
michael@0 46 data->args[fi.frameIndex()] = JS::MagicValueUint32(fi.scopeSlot());
michael@0 47 }
michael@0 48 }
michael@0 49
michael@0 50 #if defined(JS_ION)
michael@0 51 /* static */ void
michael@0 52 ArgumentsObject::MaybeForwardToCallObject(jit::IonJSFrameLayout *frame, HandleObject callObj,
michael@0 53 JSObject *obj, ArgumentsData *data)
michael@0 54 {
michael@0 55 JSFunction *callee = jit::CalleeTokenToFunction(frame->calleeToken());
michael@0 56 JSScript *script = callee->nonLazyScript();
michael@0 57 if (callee->isHeavyweight() && script->argsObjAliasesFormals()) {
michael@0 58 JS_ASSERT(callObj && callObj->is<CallObject>());
michael@0 59 obj->initFixedSlot(MAYBE_CALL_SLOT, ObjectValue(*callObj.get()));
michael@0 60 for (AliasedFormalIter fi(script); fi; fi++)
michael@0 61 data->args[fi.frameIndex()] = JS::MagicValueUint32(fi.scopeSlot());
michael@0 62 }
michael@0 63 }
michael@0 64 #endif
michael@0 65
michael@0 66 struct CopyFrameArgs
michael@0 67 {
michael@0 68 AbstractFramePtr frame_;
michael@0 69
michael@0 70 CopyFrameArgs(AbstractFramePtr frame)
michael@0 71 : frame_(frame)
michael@0 72 { }
michael@0 73
michael@0 74 void copyArgs(JSContext *, HeapValue *dst, unsigned totalArgs) const {
michael@0 75 CopyStackFrameArguments(frame_, dst, totalArgs);
michael@0 76 }
michael@0 77
michael@0 78 /*
michael@0 79 * If a call object exists and the arguments object aliases formals, the
michael@0 80 * call object is the canonical location for formals.
michael@0 81 */
michael@0 82 void maybeForwardToCallObject(JSObject *obj, ArgumentsData *data) {
michael@0 83 ArgumentsObject::MaybeForwardToCallObject(frame_, obj, data);
michael@0 84 }
michael@0 85 };
michael@0 86
michael@0 87 #if defined(JS_ION)
michael@0 88 struct CopyIonJSFrameArgs
michael@0 89 {
michael@0 90 jit::IonJSFrameLayout *frame_;
michael@0 91 HandleObject callObj_;
michael@0 92
michael@0 93 CopyIonJSFrameArgs(jit::IonJSFrameLayout *frame, HandleObject callObj)
michael@0 94 : frame_(frame), callObj_(callObj)
michael@0 95 { }
michael@0 96
michael@0 97 void copyArgs(JSContext *, HeapValue *dstBase, unsigned totalArgs) const {
michael@0 98 unsigned numActuals = frame_->numActualArgs();
michael@0 99 unsigned numFormals = jit::CalleeTokenToFunction(frame_->calleeToken())->nargs();
michael@0 100 JS_ASSERT(numActuals <= totalArgs);
michael@0 101 JS_ASSERT(numFormals <= totalArgs);
michael@0 102 JS_ASSERT(Max(numActuals, numFormals) == totalArgs);
michael@0 103
michael@0 104 /* Copy all arguments. */
michael@0 105 Value *src = frame_->argv() + 1; /* +1 to skip this. */
michael@0 106 Value *end = src + numActuals;
michael@0 107 HeapValue *dst = dstBase;
michael@0 108 while (src != end)
michael@0 109 (dst++)->init(*src++);
michael@0 110
michael@0 111 if (numActuals < numFormals) {
michael@0 112 HeapValue *dstEnd = dstBase + totalArgs;
michael@0 113 while (dst != dstEnd)
michael@0 114 (dst++)->init(UndefinedValue());
michael@0 115 }
michael@0 116 }
michael@0 117
michael@0 118 /*
michael@0 119 * If a call object exists and the arguments object aliases formals, the
michael@0 120 * call object is the canonical location for formals.
michael@0 121 */
michael@0 122 void maybeForwardToCallObject(JSObject *obj, ArgumentsData *data) {
michael@0 123 ArgumentsObject::MaybeForwardToCallObject(frame_, callObj_, obj, data);
michael@0 124 }
michael@0 125 };
michael@0 126 #endif
michael@0 127
michael@0 128 struct CopyScriptFrameIterArgs
michael@0 129 {
michael@0 130 ScriptFrameIter &iter_;
michael@0 131
michael@0 132 CopyScriptFrameIterArgs(ScriptFrameIter &iter)
michael@0 133 : iter_(iter)
michael@0 134 { }
michael@0 135
michael@0 136 void copyArgs(JSContext *cx, HeapValue *dstBase, unsigned totalArgs) const {
michael@0 137 /* Copy actual arguments. */
michael@0 138 iter_.unaliasedForEachActual(cx, CopyToHeap(dstBase));
michael@0 139
michael@0 140 /* Define formals which are not part of the actuals. */
michael@0 141 unsigned numActuals = iter_.numActualArgs();
michael@0 142 unsigned numFormals = iter_.callee()->nargs();
michael@0 143 JS_ASSERT(numActuals <= totalArgs);
michael@0 144 JS_ASSERT(numFormals <= totalArgs);
michael@0 145 JS_ASSERT(Max(numActuals, numFormals) == totalArgs);
michael@0 146
michael@0 147 if (numActuals < numFormals) {
michael@0 148 HeapValue *dst = dstBase + numActuals, *dstEnd = dstBase + totalArgs;
michael@0 149 while (dst != dstEnd)
michael@0 150 (dst++)->init(UndefinedValue());
michael@0 151 }
michael@0 152 }
michael@0 153
michael@0 154 /*
michael@0 155 * Ion frames are copying every argument onto the stack, other locations are
michael@0 156 * invalid.
michael@0 157 */
michael@0 158 void maybeForwardToCallObject(JSObject *obj, ArgumentsData *data) {
michael@0 159 if (!iter_.isJit())
michael@0 160 ArgumentsObject::MaybeForwardToCallObject(iter_.abstractFramePtr(), obj, data);
michael@0 161 }
michael@0 162 };
michael@0 163
michael@0 164 template <typename CopyArgs>
michael@0 165 /* static */ ArgumentsObject *
michael@0 166 ArgumentsObject::create(JSContext *cx, HandleScript script, HandleFunction callee, unsigned numActuals,
michael@0 167 CopyArgs &copy)
michael@0 168 {
michael@0 169 RootedObject proto(cx, callee->global().getOrCreateObjectPrototype(cx));
michael@0 170 if (!proto)
michael@0 171 return nullptr;
michael@0 172
michael@0 173 bool strict = callee->strict();
michael@0 174 const Class *clasp = strict ? &StrictArgumentsObject::class_ : &NormalArgumentsObject::class_;
michael@0 175
michael@0 176 RootedTypeObject type(cx, cx->getNewType(clasp, proto.get()));
michael@0 177 if (!type)
michael@0 178 return nullptr;
michael@0 179
michael@0 180 JSObject *metadata = nullptr;
michael@0 181 if (!NewObjectMetadata(cx, &metadata))
michael@0 182 return nullptr;
michael@0 183
michael@0 184 RootedShape shape(cx, EmptyShape::getInitialShape(cx, clasp, TaggedProto(proto),
michael@0 185 proto->getParent(), metadata, FINALIZE_KIND,
michael@0 186 BaseShape::INDEXED));
michael@0 187 if (!shape)
michael@0 188 return nullptr;
michael@0 189
michael@0 190 unsigned numFormals = callee->nargs();
michael@0 191 unsigned numDeletedWords = NumWordsForBitArrayOfLength(numActuals);
michael@0 192 unsigned numArgs = Max(numActuals, numFormals);
michael@0 193 unsigned numBytes = offsetof(ArgumentsData, args) +
michael@0 194 numDeletedWords * sizeof(size_t) +
michael@0 195 numArgs * sizeof(Value);
michael@0 196
michael@0 197 ArgumentsData *data = (ArgumentsData *)cx->malloc_(numBytes);
michael@0 198 if (!data)
michael@0 199 return nullptr;
michael@0 200
michael@0 201 JSObject *obj = JSObject::create(cx, FINALIZE_KIND, GetInitialHeap(GenericObject, clasp),
michael@0 202 shape, type);
michael@0 203 if (!obj) {
michael@0 204 js_free(data);
michael@0 205 return nullptr;
michael@0 206 }
michael@0 207
michael@0 208 data->numArgs = numArgs;
michael@0 209 data->callee.init(ObjectValue(*callee.get()));
michael@0 210 data->script = script;
michael@0 211
michael@0 212 /* Copy [0, numArgs) into data->slots. */
michael@0 213 HeapValue *dst = data->args, *dstEnd = data->args + numArgs;
michael@0 214 copy.copyArgs(cx, dst, numArgs);
michael@0 215
michael@0 216 data->deletedBits = reinterpret_cast<size_t *>(dstEnd);
michael@0 217 ClearAllBitArrayElements(data->deletedBits, numDeletedWords);
michael@0 218
michael@0 219 obj->initFixedSlot(INITIAL_LENGTH_SLOT, Int32Value(numActuals << PACKED_BITS_COUNT));
michael@0 220 obj->initFixedSlot(DATA_SLOT, PrivateValue(data));
michael@0 221
michael@0 222 copy.maybeForwardToCallObject(obj, data);
michael@0 223
michael@0 224 ArgumentsObject &argsobj = obj->as<ArgumentsObject>();
michael@0 225 JS_ASSERT(argsobj.initialLength() == numActuals);
michael@0 226 JS_ASSERT(!argsobj.hasOverriddenLength());
michael@0 227 return &argsobj;
michael@0 228 }
michael@0 229
michael@0 230 ArgumentsObject *
michael@0 231 ArgumentsObject::createExpected(JSContext *cx, AbstractFramePtr frame)
michael@0 232 {
michael@0 233 JS_ASSERT(frame.script()->needsArgsObj());
michael@0 234 RootedScript script(cx, frame.script());
michael@0 235 RootedFunction callee(cx, frame.callee());
michael@0 236 CopyFrameArgs copy(frame);
michael@0 237 ArgumentsObject *argsobj = create(cx, script, callee, frame.numActualArgs(), copy);
michael@0 238 if (!argsobj)
michael@0 239 return nullptr;
michael@0 240
michael@0 241 frame.initArgsObj(*argsobj);
michael@0 242 return argsobj;
michael@0 243 }
michael@0 244
michael@0 245 ArgumentsObject *
michael@0 246 ArgumentsObject::createUnexpected(JSContext *cx, ScriptFrameIter &iter)
michael@0 247 {
michael@0 248 RootedScript script(cx, iter.script());
michael@0 249 RootedFunction callee(cx, iter.callee());
michael@0 250 CopyScriptFrameIterArgs copy(iter);
michael@0 251 return create(cx, script, callee, iter.numActualArgs(), copy);
michael@0 252 }
michael@0 253
michael@0 254 ArgumentsObject *
michael@0 255 ArgumentsObject::createUnexpected(JSContext *cx, AbstractFramePtr frame)
michael@0 256 {
michael@0 257 RootedScript script(cx, frame.script());
michael@0 258 RootedFunction callee(cx, frame.callee());
michael@0 259 CopyFrameArgs copy(frame);
michael@0 260 return create(cx, script, callee, frame.numActualArgs(), copy);
michael@0 261 }
michael@0 262
michael@0 263 #if defined(JS_ION)
michael@0 264 ArgumentsObject *
michael@0 265 ArgumentsObject::createForIon(JSContext *cx, jit::IonJSFrameLayout *frame, HandleObject scopeChain)
michael@0 266 {
michael@0 267 jit::CalleeToken token = frame->calleeToken();
michael@0 268 JS_ASSERT(jit::CalleeTokenIsFunction(token));
michael@0 269 RootedScript script(cx, jit::ScriptFromCalleeToken(token));
michael@0 270 RootedFunction callee(cx, jit::CalleeTokenToFunction(token));
michael@0 271 RootedObject callObj(cx, scopeChain->is<CallObject>() ? scopeChain.get() : nullptr);
michael@0 272 CopyIonJSFrameArgs copy(frame, callObj);
michael@0 273 return create(cx, script, callee, frame->numActualArgs(), copy);
michael@0 274 }
michael@0 275 #endif
michael@0 276
michael@0 277 static bool
michael@0 278 args_delProperty(JSContext *cx, HandleObject obj, HandleId id, bool *succeeded)
michael@0 279 {
michael@0 280 ArgumentsObject &argsobj = obj->as<ArgumentsObject>();
michael@0 281 if (JSID_IS_INT(id)) {
michael@0 282 unsigned arg = unsigned(JSID_TO_INT(id));
michael@0 283 if (arg < argsobj.initialLength() && !argsobj.isElementDeleted(arg))
michael@0 284 argsobj.markElementDeleted(arg);
michael@0 285 } else if (JSID_IS_ATOM(id, cx->names().length)) {
michael@0 286 argsobj.markLengthOverridden();
michael@0 287 } else if (JSID_IS_ATOM(id, cx->names().callee)) {
michael@0 288 argsobj.as<NormalArgumentsObject>().clearCallee();
michael@0 289 }
michael@0 290 *succeeded = true;
michael@0 291 return true;
michael@0 292 }
michael@0 293
michael@0 294 static bool
michael@0 295 ArgGetter(JSContext *cx, HandleObject obj, HandleId id, MutableHandleValue vp)
michael@0 296 {
michael@0 297 if (!obj->is<NormalArgumentsObject>())
michael@0 298 return true;
michael@0 299
michael@0 300 NormalArgumentsObject &argsobj = obj->as<NormalArgumentsObject>();
michael@0 301 if (JSID_IS_INT(id)) {
michael@0 302 /*
michael@0 303 * arg can exceed the number of arguments if a script changed the
michael@0 304 * prototype to point to another Arguments object with a bigger argc.
michael@0 305 */
michael@0 306 unsigned arg = unsigned(JSID_TO_INT(id));
michael@0 307 if (arg < argsobj.initialLength() && !argsobj.isElementDeleted(arg))
michael@0 308 vp.set(argsobj.element(arg));
michael@0 309 } else if (JSID_IS_ATOM(id, cx->names().length)) {
michael@0 310 if (!argsobj.hasOverriddenLength())
michael@0 311 vp.setInt32(argsobj.initialLength());
michael@0 312 } else {
michael@0 313 JS_ASSERT(JSID_IS_ATOM(id, cx->names().callee));
michael@0 314 if (!argsobj.callee().isMagic(JS_OVERWRITTEN_CALLEE))
michael@0 315 vp.set(argsobj.callee());
michael@0 316 }
michael@0 317 return true;
michael@0 318 }
michael@0 319
michael@0 320 static bool
michael@0 321 ArgSetter(JSContext *cx, HandleObject obj, HandleId id, bool strict, MutableHandleValue vp)
michael@0 322 {
michael@0 323 if (!obj->is<NormalArgumentsObject>())
michael@0 324 return true;
michael@0 325
michael@0 326 unsigned attrs;
michael@0 327 if (!baseops::GetAttributes(cx, obj, id, &attrs))
michael@0 328 return false;
michael@0 329 JS_ASSERT(!(attrs & JSPROP_READONLY));
michael@0 330 attrs &= (JSPROP_ENUMERATE | JSPROP_PERMANENT); /* only valid attributes */
michael@0 331
michael@0 332 NormalArgumentsObject &argsobj = obj->as<NormalArgumentsObject>();
michael@0 333 RootedScript script(cx, argsobj.containingScript());
michael@0 334
michael@0 335 if (JSID_IS_INT(id)) {
michael@0 336 unsigned arg = unsigned(JSID_TO_INT(id));
michael@0 337 if (arg < argsobj.initialLength() && !argsobj.isElementDeleted(arg)) {
michael@0 338 argsobj.setElement(cx, arg, vp);
michael@0 339 if (arg < script->functionNonDelazifying()->nargs())
michael@0 340 types::TypeScript::SetArgument(cx, script, arg, vp);
michael@0 341 return true;
michael@0 342 }
michael@0 343 } else {
michael@0 344 JS_ASSERT(JSID_IS_ATOM(id, cx->names().length) || JSID_IS_ATOM(id, cx->names().callee));
michael@0 345 }
michael@0 346
michael@0 347 /*
michael@0 348 * For simplicity we use delete/define to replace the property with one
michael@0 349 * backed by the default Object getter and setter. Note that we rely on
michael@0 350 * args_delProperty to clear the corresponding reserved slot so the GC can
michael@0 351 * collect its value. Note also that we must define the property instead
michael@0 352 * of setting it in case the user has changed the prototype to an object
michael@0 353 * that has a setter for this id.
michael@0 354 */
michael@0 355 bool succeeded;
michael@0 356 return baseops::DeleteGeneric(cx, obj, id, &succeeded) &&
michael@0 357 baseops::DefineGeneric(cx, obj, id, vp, nullptr, nullptr, attrs);
michael@0 358 }
michael@0 359
michael@0 360 static bool
michael@0 361 args_resolve(JSContext *cx, HandleObject obj, HandleId id, MutableHandleObject objp)
michael@0 362 {
michael@0 363 objp.set(nullptr);
michael@0 364
michael@0 365 Rooted<NormalArgumentsObject*> argsobj(cx, &obj->as<NormalArgumentsObject>());
michael@0 366
michael@0 367 unsigned attrs = JSPROP_SHARED | JSPROP_SHADOWABLE;
michael@0 368 if (JSID_IS_INT(id)) {
michael@0 369 uint32_t arg = uint32_t(JSID_TO_INT(id));
michael@0 370 if (arg >= argsobj->initialLength() || argsobj->isElementDeleted(arg))
michael@0 371 return true;
michael@0 372
michael@0 373 attrs |= JSPROP_ENUMERATE;
michael@0 374 } else if (JSID_IS_ATOM(id, cx->names().length)) {
michael@0 375 if (argsobj->hasOverriddenLength())
michael@0 376 return true;
michael@0 377 } else {
michael@0 378 if (!JSID_IS_ATOM(id, cx->names().callee))
michael@0 379 return true;
michael@0 380
michael@0 381 if (argsobj->callee().isMagic(JS_OVERWRITTEN_CALLEE))
michael@0 382 return true;
michael@0 383 }
michael@0 384
michael@0 385 if (!baseops::DefineGeneric(cx, argsobj, id, UndefinedHandleValue, ArgGetter, ArgSetter, attrs))
michael@0 386 return false;
michael@0 387
michael@0 388 objp.set(argsobj);
michael@0 389 return true;
michael@0 390 }
michael@0 391
michael@0 392 static bool
michael@0 393 args_enumerate(JSContext *cx, HandleObject obj)
michael@0 394 {
michael@0 395 Rooted<NormalArgumentsObject*> argsobj(cx, &obj->as<NormalArgumentsObject>());
michael@0 396 RootedId id(cx);
michael@0 397
michael@0 398 /*
michael@0 399 * Trigger reflection in args_resolve using a series of js_LookupProperty
michael@0 400 * calls.
michael@0 401 */
michael@0 402 int argc = int(argsobj->initialLength());
michael@0 403 for (int i = -2; i != argc; i++) {
michael@0 404 id = (i == -2)
michael@0 405 ? NameToId(cx->names().length)
michael@0 406 : (i == -1)
michael@0 407 ? NameToId(cx->names().callee)
michael@0 408 : INT_TO_JSID(i);
michael@0 409
michael@0 410 RootedObject pobj(cx);
michael@0 411 RootedShape prop(cx);
michael@0 412 if (!baseops::LookupProperty<CanGC>(cx, argsobj, id, &pobj, &prop))
michael@0 413 return false;
michael@0 414 }
michael@0 415 return true;
michael@0 416 }
michael@0 417
michael@0 418 static bool
michael@0 419 StrictArgGetter(JSContext *cx, HandleObject obj, HandleId id, MutableHandleValue vp)
michael@0 420 {
michael@0 421 if (!obj->is<StrictArgumentsObject>())
michael@0 422 return true;
michael@0 423
michael@0 424 StrictArgumentsObject &argsobj = obj->as<StrictArgumentsObject>();
michael@0 425
michael@0 426 if (JSID_IS_INT(id)) {
michael@0 427 /*
michael@0 428 * arg can exceed the number of arguments if a script changed the
michael@0 429 * prototype to point to another Arguments object with a bigger argc.
michael@0 430 */
michael@0 431 unsigned arg = unsigned(JSID_TO_INT(id));
michael@0 432 if (arg < argsobj.initialLength() && !argsobj.isElementDeleted(arg))
michael@0 433 vp.set(argsobj.element(arg));
michael@0 434 } else {
michael@0 435 JS_ASSERT(JSID_IS_ATOM(id, cx->names().length));
michael@0 436 if (!argsobj.hasOverriddenLength())
michael@0 437 vp.setInt32(argsobj.initialLength());
michael@0 438 }
michael@0 439 return true;
michael@0 440 }
michael@0 441
michael@0 442 static bool
michael@0 443 StrictArgSetter(JSContext *cx, HandleObject obj, HandleId id, bool strict, MutableHandleValue vp)
michael@0 444 {
michael@0 445 if (!obj->is<StrictArgumentsObject>())
michael@0 446 return true;
michael@0 447
michael@0 448 unsigned attrs;
michael@0 449 if (!baseops::GetAttributes(cx, obj, id, &attrs))
michael@0 450 return false;
michael@0 451 JS_ASSERT(!(attrs & JSPROP_READONLY));
michael@0 452 attrs &= (JSPROP_ENUMERATE | JSPROP_PERMANENT); /* only valid attributes */
michael@0 453
michael@0 454 Rooted<StrictArgumentsObject*> argsobj(cx, &obj->as<StrictArgumentsObject>());
michael@0 455
michael@0 456 if (JSID_IS_INT(id)) {
michael@0 457 unsigned arg = unsigned(JSID_TO_INT(id));
michael@0 458 if (arg < argsobj->initialLength()) {
michael@0 459 argsobj->setElement(cx, arg, vp);
michael@0 460 return true;
michael@0 461 }
michael@0 462 } else {
michael@0 463 JS_ASSERT(JSID_IS_ATOM(id, cx->names().length));
michael@0 464 }
michael@0 465
michael@0 466 /*
michael@0 467 * For simplicity we use delete/define to replace the property with one
michael@0 468 * backed by the default Object getter and setter. Note that we rely on
michael@0 469 * args_delProperty to clear the corresponding reserved slot so the GC can
michael@0 470 * collect its value.
michael@0 471 */
michael@0 472 bool succeeded;
michael@0 473 return baseops::DeleteGeneric(cx, argsobj, id, &succeeded) &&
michael@0 474 baseops::DefineGeneric(cx, argsobj, id, vp, nullptr, nullptr, attrs);
michael@0 475 }
michael@0 476
michael@0 477 static bool
michael@0 478 strictargs_resolve(JSContext *cx, HandleObject obj, HandleId id, MutableHandleObject objp)
michael@0 479 {
michael@0 480 objp.set(nullptr);
michael@0 481
michael@0 482 Rooted<StrictArgumentsObject*> argsobj(cx, &obj->as<StrictArgumentsObject>());
michael@0 483
michael@0 484 unsigned attrs = JSPROP_SHARED | JSPROP_SHADOWABLE;
michael@0 485 PropertyOp getter = StrictArgGetter;
michael@0 486 StrictPropertyOp setter = StrictArgSetter;
michael@0 487
michael@0 488 if (JSID_IS_INT(id)) {
michael@0 489 uint32_t arg = uint32_t(JSID_TO_INT(id));
michael@0 490 if (arg >= argsobj->initialLength() || argsobj->isElementDeleted(arg))
michael@0 491 return true;
michael@0 492
michael@0 493 attrs |= JSPROP_ENUMERATE;
michael@0 494 } else if (JSID_IS_ATOM(id, cx->names().length)) {
michael@0 495 if (argsobj->hasOverriddenLength())
michael@0 496 return true;
michael@0 497 } else {
michael@0 498 if (!JSID_IS_ATOM(id, cx->names().callee) && !JSID_IS_ATOM(id, cx->names().caller))
michael@0 499 return true;
michael@0 500
michael@0 501 attrs = JSPROP_PERMANENT | JSPROP_GETTER | JSPROP_SETTER | JSPROP_SHARED;
michael@0 502 getter = CastAsPropertyOp(argsobj->global().getThrowTypeError());
michael@0 503 setter = CastAsStrictPropertyOp(argsobj->global().getThrowTypeError());
michael@0 504 }
michael@0 505
michael@0 506 if (!baseops::DefineGeneric(cx, argsobj, id, UndefinedHandleValue, getter, setter, attrs))
michael@0 507 return false;
michael@0 508
michael@0 509 objp.set(argsobj);
michael@0 510 return true;
michael@0 511 }
michael@0 512
michael@0 513 static bool
michael@0 514 strictargs_enumerate(JSContext *cx, HandleObject obj)
michael@0 515 {
michael@0 516 Rooted<StrictArgumentsObject*> argsobj(cx, &obj->as<StrictArgumentsObject>());
michael@0 517
michael@0 518 /*
michael@0 519 * Trigger reflection in strictargs_resolve using a series of
michael@0 520 * js_LookupProperty calls.
michael@0 521 */
michael@0 522 RootedObject pobj(cx);
michael@0 523 RootedShape prop(cx);
michael@0 524 RootedId id(cx);
michael@0 525
michael@0 526 // length
michael@0 527 id = NameToId(cx->names().length);
michael@0 528 if (!baseops::LookupProperty<CanGC>(cx, argsobj, id, &pobj, &prop))
michael@0 529 return false;
michael@0 530
michael@0 531 // callee
michael@0 532 id = NameToId(cx->names().callee);
michael@0 533 if (!baseops::LookupProperty<CanGC>(cx, argsobj, id, &pobj, &prop))
michael@0 534 return false;
michael@0 535
michael@0 536 // caller
michael@0 537 id = NameToId(cx->names().caller);
michael@0 538 if (!baseops::LookupProperty<CanGC>(cx, argsobj, id, &pobj, &prop))
michael@0 539 return false;
michael@0 540
michael@0 541 for (uint32_t i = 0, argc = argsobj->initialLength(); i < argc; i++) {
michael@0 542 id = INT_TO_JSID(i);
michael@0 543 if (!baseops::LookupProperty<CanGC>(cx, argsobj, id, &pobj, &prop))
michael@0 544 return false;
michael@0 545 }
michael@0 546
michael@0 547 return true;
michael@0 548 }
michael@0 549
michael@0 550 void
michael@0 551 ArgumentsObject::finalize(FreeOp *fop, JSObject *obj)
michael@0 552 {
michael@0 553 fop->free_(reinterpret_cast<void *>(obj->as<ArgumentsObject>().data()));
michael@0 554 }
michael@0 555
michael@0 556 void
michael@0 557 ArgumentsObject::trace(JSTracer *trc, JSObject *obj)
michael@0 558 {
michael@0 559 ArgumentsObject &argsobj = obj->as<ArgumentsObject>();
michael@0 560 ArgumentsData *data = argsobj.data();
michael@0 561 MarkValue(trc, &data->callee, js_callee_str);
michael@0 562 MarkValueRange(trc, data->numArgs, data->args, js_arguments_str);
michael@0 563 MarkScriptUnbarriered(trc, &data->script, "script");
michael@0 564 }
michael@0 565
michael@0 566 /*
michael@0 567 * The classes below collaborate to lazily reflect and synchronize actual
michael@0 568 * argument values, argument count, and callee function object stored in a
michael@0 569 * stack frame with their corresponding property values in the frame's
michael@0 570 * arguments object.
michael@0 571 */
michael@0 572 const Class NormalArgumentsObject::class_ = {
michael@0 573 "Arguments",
michael@0 574 JSCLASS_NEW_RESOLVE | JSCLASS_IMPLEMENTS_BARRIERS |
michael@0 575 JSCLASS_HAS_RESERVED_SLOTS(NormalArgumentsObject::RESERVED_SLOTS) |
michael@0 576 JSCLASS_HAS_CACHED_PROTO(JSProto_Object) | JSCLASS_BACKGROUND_FINALIZE,
michael@0 577 JS_PropertyStub, /* addProperty */
michael@0 578 args_delProperty,
michael@0 579 JS_PropertyStub, /* getProperty */
michael@0 580 JS_StrictPropertyStub, /* setProperty */
michael@0 581 args_enumerate,
michael@0 582 reinterpret_cast<JSResolveOp>(args_resolve),
michael@0 583 JS_ConvertStub,
michael@0 584 ArgumentsObject::finalize,
michael@0 585 nullptr, /* call */
michael@0 586 nullptr, /* hasInstance */
michael@0 587 nullptr, /* construct */
michael@0 588 ArgumentsObject::trace
michael@0 589 };
michael@0 590
michael@0 591 /*
michael@0 592 * Strict mode arguments is significantly less magical than non-strict mode
michael@0 593 * arguments, so it is represented by a different class while sharing some
michael@0 594 * functionality.
michael@0 595 */
michael@0 596 const Class StrictArgumentsObject::class_ = {
michael@0 597 "Arguments",
michael@0 598 JSCLASS_NEW_RESOLVE | JSCLASS_IMPLEMENTS_BARRIERS |
michael@0 599 JSCLASS_HAS_RESERVED_SLOTS(StrictArgumentsObject::RESERVED_SLOTS) |
michael@0 600 JSCLASS_HAS_CACHED_PROTO(JSProto_Object) | JSCLASS_BACKGROUND_FINALIZE,
michael@0 601 JS_PropertyStub, /* addProperty */
michael@0 602 args_delProperty,
michael@0 603 JS_PropertyStub, /* getProperty */
michael@0 604 JS_StrictPropertyStub, /* setProperty */
michael@0 605 strictargs_enumerate,
michael@0 606 reinterpret_cast<JSResolveOp>(strictargs_resolve),
michael@0 607 JS_ConvertStub,
michael@0 608 ArgumentsObject::finalize,
michael@0 609 nullptr, /* call */
michael@0 610 nullptr, /* hasInstance */
michael@0 611 nullptr, /* construct */
michael@0 612 ArgumentsObject::trace
michael@0 613 };

mercurial