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