1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*-
2 * vim: set ts=4 sw=4 et tw=99:
4 * ***** BEGIN LICENSE BLOCK *****
5 * Version: MPL 1.1/GPL 2.0/LGPL 2.1
7 * The contents of this file are subject to the Mozilla Public License Version
8 * 1.1 (the "License"); you may not use this file except in compliance with
9 * the License. You may obtain a copy of the License at
10 * http://www.mozilla.org/MPL/
12 * Software distributed under the License is distributed on an "AS IS" basis,
13 * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
14 * for the specific language governing rights and limitations under the
17 * The Original Code is Mozilla SpiderMonkey JavaScript 1.9 code, released
20 * The Initial Developer of the Original Code is
21 * Brendan Eich <brendan@mozilla.org>
24 * David Mandelin <dmandelin@mozilla.com>
26 * Alternatively, the contents of this file may be used under the terms of
27 * either of the GNU General Public License Version 2 or later (the "GPL"),
28 * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
29 * in which case the provisions of the GPL or the LGPL are applicable instead
30 * of those above. If you wish to allow use of your version of this file only
31 * under the terms of either the GPL or the LGPL, and not to allow others to
32 * use your version of this file under the terms of the MPL, indicate your
33 * decision by deleting the provisions above and replace them with the notice
34 * and other provisions required by the GPL or the LGPL. If you do not delete
35 * the provisions above, a recipient may use your version of this file under
36 * the terms of any one of the MPL, the GPL or the LGPL.
38 * ***** END LICENSE BLOCK ***** */
40 #include "StubCalls.h"
41 #include "CodeGenIncludes.h"
42 #include "StubCalls-inl.h"
43 #include "assembler/assembler/LinkBuffer.h"
46 #include "jsobjinlines.h"
47 #include "jsscopeinlines.h"
48 #include "jspropertycache.h"
49 #include "jspropertycacheinlines.h"
50 #include "jsautooplen.h"
55 using namespace js::mjit
;
56 using namespace js::mjit::ic
;
58 /* Rough over-estimate of how much memory we need to unprotect. */
59 static const uint32 INLINE_PATH_LENGTH
= 64;
61 typedef JSC::FunctionPtr FunctionPtr
;
62 typedef JSC::RepatchBuffer RepatchBuffer
;
63 typedef JSC::CodeBlock CodeBlock
;
64 typedef JSC::CodeLocationLabel CodeLocationLabel
;
65 typedef JSC::JITCode JITCode
;
66 typedef JSC::MacroAssembler::Jump Jump
;
67 typedef JSC::MacroAssembler::RegisterID RegisterID
;
68 typedef JSC::MacroAssembler::Label Label
;
69 typedef JSC::MacroAssembler::Imm32 Imm32
;
70 typedef JSC::MacroAssembler::ImmPtr ImmPtr
;
71 typedef JSC::MacroAssembler::Address Address
;
72 typedef JSC::ReturnAddressPtr ReturnAddressPtr
;
73 typedef JSC::MacroAssemblerCodePtr MacroAssemblerCodePtr
;
75 class AutoPropertyDropper
82 AutoPropertyDropper(JSContext
*cx
, JSObject
*obj
, JSProperty
*prop
)
83 : cx(cx
), holder(obj
), prop(prop
)
88 ~AutoPropertyDropper()
90 holder
->dropProperty(cx
, prop
);
103 PICStubCompiler(const char *type
, VMFrame
&f
, JSScript
*script
, ic::PICInfo
&pic
)
104 : type(type
), f(f
), script(script
), pic(pic
)
107 bool disable(const char *reason
, VoidStub stub
)
109 return disable(reason
, JS_FUNC_TO_DATA_PTR(void *, stub
));
112 bool disable(const char *reason
, VoidStubUInt32 stub
)
114 return disable(reason
, JS_FUNC_TO_DATA_PTR(void *, stub
));
117 bool disable(const char *reason
, void *stub
)
119 spew("disabled", reason
);
120 JITCode
jitCode(pic
.slowPathStart
.executableAddress(), INLINE_PATH_LENGTH
);
121 CodeBlock
codeBlock(jitCode
);
122 RepatchBuffer
repatcher(&codeBlock
);
123 ReturnAddressPtr
retPtr(pic
.slowPathStart
.callAtOffset(pic
.callReturn
).executableAddress());
124 MacroAssemblerCodePtr
target(stub
);
125 repatcher
.relinkCallerToTrampoline(retPtr
, target
);
129 JSC::ExecutablePool
*getExecPool(size_t size
)
131 mjit::ThreadData
*jd
= &JS_METHODJIT_DATA(f
.cx
);
132 return jd
->execPool
->poolForSize(size
);
136 void spew(const char *event
, const char *op
)
138 JaegerSpew(JSpew_PICs
, "%s %s: %s (%s: %d)\n",
139 type
, event
, op
, script
->filename
,
140 js_FramePCToLineNumber(f
.cx
, f
.fp
));
144 class PICRepatchBuffer
: public JSC::RepatchBuffer
147 JSC::CodeLocationLabel label
;
150 PICRepatchBuffer(ic::PICInfo
&ic
, JSC::CodeLocationLabel path
)
151 : JSC::RepatchBuffer(path
.executableAddress(), INLINE_PATH_LENGTH
),
155 void relink(int32 offset
, JSC::CodeLocationLabel target
) {
156 JSC::RepatchBuffer::relink(label
.jumpAtOffset(offset
), target
);
160 class SetPropCompiler
: public PICStubCompiler
166 static int32
dslotsLoadOffset(ic::PICInfo
&pic
) {
167 #if defined JS_NUNBOX32
168 if (pic
.u
.vr
.isConstant
)
169 return SETPROP_DSLOTS_BEFORE_CONSTANT
;
170 if (pic
.u
.vr
.u
.s
.isTypeKnown
)
171 return SETPROP_DSLOTS_BEFORE_KTYPE
;
172 return SETPROP_DSLOTS_BEFORE_DYNAMIC
;
173 #elif defined JS_PUNBOX64
174 return pic
.labels
.setprop
.dslotsLoadOffset
;
178 #if defined JS_NUNBOX32
179 inline int32
inlineTypeOffset() {
180 if (pic
.u
.vr
.isConstant
)
181 return SETPROP_INLINE_STORE_CONST_TYPE
;
182 if (pic
.u
.vr
.u
.s
.isTypeKnown
)
183 return SETPROP_INLINE_STORE_KTYPE_TYPE
;
184 return SETPROP_INLINE_STORE_DYN_TYPE
;
188 #if defined JS_NUNBOX32
189 inline int32
inlineDataOffset() {
190 if (pic
.u
.vr
.isConstant
)
191 return SETPROP_INLINE_STORE_CONST_DATA
;
192 if (pic
.u
.vr
.u
.s
.isTypeKnown
)
193 return SETPROP_INLINE_STORE_KTYPE_DATA
;
194 return SETPROP_INLINE_STORE_DYN_DATA
;
198 static int32
inlineShapeOffset(ic::PICInfo
&pic
) {
199 #if defined JS_NUNBOX32
200 return SETPROP_INLINE_SHAPE_OFFSET
;
201 #elif defined JS_PUNBOX64
202 return pic
.labels
.setprop
.inlineShapeOffset
;
206 static int32
inlineShapeJump(ic::PICInfo
&pic
) {
207 #if defined JS_NUNBOX32
208 return SETPROP_INLINE_SHAPE_JUMP
;
209 #elif defined JS_PUNBOX64
210 return inlineShapeOffset(pic
) + SETPROP_INLINE_SHAPE_JUMP
;
214 inline int32
dslotsLoadOffset() {
215 return dslotsLoadOffset(pic
);
218 inline int32
inlineShapeOffset() {
219 return inlineShapeOffset(pic
);
222 inline int32
inlineShapeJump() {
223 return inlineShapeJump(pic
);
227 SetPropCompiler(VMFrame
&f
, JSScript
*script
, JSObject
*obj
, ic::PICInfo
&pic
, JSAtom
*atom
,
229 : PICStubCompiler("setprop", f
, script
, pic
), obj(obj
), atom(atom
), stub(stub
)
232 bool disable(const char *reason
)
234 return PICStubCompiler::disable(reason
, stub
);
237 static void reset(ic::PICInfo
&pic
)
239 RepatchBuffer
repatcher(pic
.fastPathStart
.executableAddress(), INLINE_PATH_LENGTH
);
240 repatcher
.repatchLEAToLoadPtr(pic
.storeBack
.instructionAtOffset(dslotsLoadOffset(pic
)));
241 repatcher
.repatch(pic
.fastPathStart
.dataLabel32AtOffset(
242 pic
.shapeGuard
+ inlineShapeOffset(pic
)),
243 int32(JSScope::INVALID_SHAPE
));
244 repatcher
.relink(pic
.fastPathStart
.jumpAtOffset(
245 pic
.shapeGuard
+ inlineShapeJump(pic
)),
248 RepatchBuffer
repatcher2(pic
.slowPathStart
.executableAddress(), INLINE_PATH_LENGTH
);
249 ReturnAddressPtr
retPtr(pic
.slowPathStart
.callAtOffset(pic
.callReturn
).executableAddress());
250 MacroAssemblerCodePtr
target(JS_FUNC_TO_DATA_PTR(void *, ic::SetProp
));
251 repatcher
.relinkCallerToTrampoline(retPtr
, target
);
254 bool patchInline(JSScopeProperty
*sprop
)
256 JS_ASSERT(!pic
.inlinePathPatched
);
257 JaegerSpew(JSpew_PICs
, "patch setprop inline at %p\n", pic
.fastPathStart
.executableAddress());
259 PICRepatchBuffer
repatcher(pic
, pic
.fastPathStart
);
262 if (sprop
->slot
< JS_INITIAL_NSLOTS
) {
263 JSC::CodeLocationInstruction istr
;
264 istr
= pic
.storeBack
.instructionAtOffset(dslotsLoadOffset());
265 repatcher
.repatchLoadPtrToLEA(istr
);
268 // We've patched | mov dslots, [obj + DSLOTS_OFFSET]
269 // To: | lea fslots, [obj + DSLOTS_OFFSET]
271 // Because the offset is wrong, it's necessary to correct it
274 int32 diff
= int32(offsetof(JSObject
, fslots
)) -
275 int32(offsetof(JSObject
, dslots
));
276 JS_ASSERT(diff
!= 0);
277 offset
= (int32(sprop
->slot
) * sizeof(Value
)) + diff
;
279 offset
= (sprop
->slot
- JS_INITIAL_NSLOTS
) * sizeof(Value
);
282 uint32 shapeOffs
= pic
.shapeGuard
+ inlineShapeOffset();
283 repatcher
.repatch(pic
.fastPathStart
.dataLabel32AtOffset(shapeOffs
), obj
->shape());
284 #if defined JS_NUNBOX32
285 repatcher
.repatch(pic
.storeBack
.dataLabel32AtOffset(inlineTypeOffset()), offset
+ 4);
286 repatcher
.repatch(pic
.storeBack
.dataLabel32AtOffset(inlineDataOffset()), offset
);
287 #elif defined JS_PUNBOX64
288 repatcher
.repatch(pic
.storeBack
.dataLabel32AtOffset(SETPROP_INLINE_STORE_VALUE
), offset
);
291 pic
.inlinePathPatched
= true;
296 void patchPreviousToHere(PICRepatchBuffer
&repatcher
, CodeLocationLabel cs
)
298 // Patch either the inline fast path or a generated stub. The stub
299 // omits the prefix of the inline fast path that loads the shape, so
300 // the offsets are different.
301 int shapeGuardJumpOffset
;
302 if (pic
.stubsGenerated
)
303 #if defined JS_NUNBOX32
304 shapeGuardJumpOffset
= SETPROP_STUB_SHAPE_JUMP
;
305 #elif defined JS_PUNBOX64
306 shapeGuardJumpOffset
= pic
.labels
.setprop
.stubShapeJump
;
309 shapeGuardJumpOffset
= pic
.shapeGuard
+ inlineShapeJump();
310 repatcher
.relink(shapeGuardJumpOffset
, cs
);
313 // :TODO: x64 -- implement more efficient version.
314 void emitStore(Assembler
&masm
, Address address
)
316 if (pic
.u
.vr
.isConstant
) {
317 masm
.storeValue(Valueify(pic
.u
.vr
.u
.v
), address
);
319 if (pic
.u
.vr
.u
.s
.isTypeKnown
)
320 masm
.storeTypeTag(ImmType(pic
.u
.vr
.u
.s
.type
.knownType
), address
);
322 masm
.storeTypeTag(pic
.u
.vr
.u
.s
.type
.reg
, address
);
323 masm
.storePayload(pic
.u
.vr
.u
.s
.data
, address
);
327 bool generateStub(JSScopeProperty
*sprop
)
332 if (pic
.shapeNeedsRemat()) {
333 masm
.loadShape(pic
.objReg
, pic
.shapeReg
);
334 pic
.shapeRegHasBaseShape
= true;
337 Label start
= masm
.label();
338 Jump shapeMismatch
= masm
.branch32_force32(Assembler::NotEqual
, pic
.shapeReg
,
339 Imm32(obj
->shape()));
341 #if defined JS_NUNBOX32
342 DBGLABEL(dbgStubShapeJump
);
343 JS_ASSERT(masm
.differenceBetween(start
, dbgStubShapeJump
) == SETPROP_STUB_SHAPE_JUMP
);
344 #elif defined JS_PUNBOX64
345 Label stubShapeJumpLabel
= masm
.label();
348 JSScope
*scope
= obj
->scope();
349 JS_ASSERT_IF(!sprop
->hasDefaultSetter(), obj
->getClass() == &js_CallClass
);
353 if (sprop
->hasDefaultSetter()) {
354 Address
address(pic
.objReg
, offsetof(JSObject
, fslots
) + sprop
->slot
* sizeof(Value
));
355 if (sprop
->slot
>= JS_INITIAL_NSLOTS
) {
356 masm
.loadPtr(Address(pic
.objReg
, offsetof(JSObject
, dslots
)), pic
.objReg
);
357 address
= Address(pic
.objReg
, (sprop
->slot
- JS_INITIAL_NSLOTS
) * sizeof(Value
));
360 // If the scope is branded, or has a method barrier. It's now necessary
361 // to guard that we're not overwriting a function-valued property.
362 if (scope
->brandedOrHasMethodBarrier()) {
363 masm
.loadTypeTag(address
, pic
.shapeReg
);
364 Jump skip
= masm
.testObject(Assembler::NotEqual
, pic
.shapeReg
);
365 masm
.loadPayload(address
, pic
.shapeReg
);
366 rebrand
= masm
.testFunction(Assembler::Equal
, pic
.shapeReg
);
367 skip
.linkTo(masm
.label(), &masm
);
368 pic
.shapeRegHasBaseShape
= false;
371 emitStore(masm
, address
);
373 uint16 slot
= uint16(sprop
->shortid
);
375 /* Guard that the call object has a frame. */
376 masm
.loadFunctionPrivate(pic
.objReg
, pic
.shapeReg
);
377 Jump escapedFrame
= masm
.branchTestPtr(Assembler::Zero
, pic
.shapeReg
, pic
.shapeReg
);
381 if (sprop
->setterOp() == SetCallArg
)
382 masm
.loadPtr(Address(pic
.shapeReg
, offsetof(JSStackFrame
, argv
)), pic
.shapeReg
);
384 bias
= sizeof(JSStackFrame
);
385 Address
address(pic
.shapeReg
, bias
+ slot
* sizeof(Value
));
386 emitStore(masm
, address
);
387 skipOver
= masm
.jump();
390 escapedFrame
.linkTo(masm
.label(), &masm
);
392 if (sprop
->setterOp() == SetCallVar
) {
393 JSFunction
*fun
= js_GetCallObjectFunction(obj
);
396 masm
.loadPtr(Address(pic
.objReg
, offsetof(JSObject
, dslots
)), pic
.objReg
);
398 Address
dslot(pic
.objReg
, slot
* sizeof(Value
));
399 emitStore(masm
, dslot
);
402 pic
.shapeRegHasBaseShape
= false;
404 Jump done
= masm
.jump();
406 JSC::ExecutablePool
*ep
= getExecPool(masm
.size());
407 if (!ep
|| !pic
.execPools
.append(ep
)) {
410 js_ReportOutOfMemory(f
.cx
);
414 JSC::LinkBuffer
buffer(&masm
, ep
);
415 buffer
.link(shapeMismatch
, pic
.slowPathStart
);
416 buffer
.link(done
, pic
.storeBack
);
417 if (sprop
->hasDefaultSetter() && (scope
->brandedOrHasMethodBarrier()))
418 buffer
.link(rebrand
, pic
.slowPathStart
);
419 if (!sprop
->hasDefaultSetter())
420 buffer
.link(skipOver
, pic
.storeBack
);
421 CodeLocationLabel cs
= buffer
.finalizeCodeAddendum();
422 JaegerSpew(JSpew_PICs
, "generate setprop stub %p %d %d at %p\n",
426 cs
.executableAddress());
428 PICRepatchBuffer
repatcher(pic
, pic
.lastPathStart());
430 // This function can patch either the inline fast path for a generated
431 // stub. The stub omits the prefix of the inline fast path that loads
432 // the shape, so the offsets are different.
433 patchPreviousToHere(repatcher
, cs
);
435 pic
.stubsGenerated
++;
436 pic
.lastStubStart
= buffer
.locationOf(start
);
438 #if defined JS_PUNBOX64
439 pic
.labels
.setprop
.stubShapeJump
= masm
.differenceBetween(start
, stubShapeJumpLabel
);
442 if (pic
.stubsGenerated
== MAX_PIC_STUBS
)
443 disable("max stubs reached");
451 spew("first hit", "nop");
456 JSObject
*aobj
= js_GetProtoIfDenseArray(obj
);
457 if (!aobj
->isNative())
458 return disable("non-native");
461 JSProperty
*prop
= NULL
;
462 if (!aobj
->lookupProperty(f
.cx
, ATOM_TO_JSID(atom
), &holder
, &prop
))
465 return disable("property not found");
467 AutoPropertyDropper
dropper(f
.cx
, holder
, prop
);
469 return disable("property not on object");
471 JSScope
*scope
= obj
->scope();
472 JSScopeProperty
*sprop
= (JSScopeProperty
*)prop
;
473 if (!sprop
->writable())
474 return disable("readonly");
475 if (scope
->sealed() && !sprop
->hasSlot())
476 return disable("what does this even mean");
478 if (sprop
->hasDefaultSetter()) {
479 if (!SPROP_HAS_VALID_SLOT(sprop
, scope
))
480 return disable("invalid slot");
482 if (sprop
->hasSetterValue())
483 return disable("scripted setter");
484 if (sprop
->setterOp() != SetCallArg
&&
485 sprop
->setterOp() != SetCallVar
) {
486 return disable("setter");
490 JS_ASSERT(obj
== holder
);
491 if (!pic
.inlinePathPatched
&&
492 !scope
->brandedOrHasMethodBarrier() &&
493 sprop
->hasDefaultSetter() &&
494 !obj
->isDenseArray()) {
495 return patchInline(sprop
);
498 return generateStub(sprop
);
502 class GetPropCompiler
: public PICStubCompiler
507 int lastStubSecondShapeGuard
;
509 static int32
inlineShapeOffset(ic::PICInfo
&pic
) {
510 #if defined JS_NUNBOX32
511 return GETPROP_INLINE_SHAPE_OFFSET
;
512 #elif defined JS_PUNBOX64
513 return pic
.labels
.getprop
.inlineShapeOffset
;
517 inline int32
inlineShapeOffset() {
518 return inlineShapeOffset(pic
);
521 static int32
inlineShapeJump(ic::PICInfo
&pic
) {
522 #if defined JS_NUNBOX32
523 return GETPROP_INLINE_SHAPE_JUMP
;
524 #elif defined JS_PUNBOX64
525 return inlineShapeOffset(pic
) + GETPROP_INLINE_SHAPE_JUMP
;
529 inline int32
inlineShapeJump() {
530 return inlineShapeJump(pic
);
533 static int32
dslotsLoad(ic::PICInfo
&pic
) {
534 #if defined JS_NUNBOX32
535 return GETPROP_DSLOTS_LOAD
;
536 #elif defined JS_PUNBOX64
537 return pic
.labels
.getprop
.dslotsLoadOffset
;
541 inline int32
dslotsLoad() {
542 return dslotsLoad(pic
);
546 GetPropCompiler(VMFrame
&f
, JSScript
*script
, JSObject
*obj
, ic::PICInfo
&pic
, JSAtom
*atom
,
548 : PICStubCompiler("getprop", f
, script
, pic
), obj(obj
), atom(atom
),
549 stub(JS_FUNC_TO_DATA_PTR(void *, stub
)),
550 lastStubSecondShapeGuard(pic
.u
.get
.secondShapeGuard
)
553 GetPropCompiler(VMFrame
&f
, JSScript
*script
, JSObject
*obj
, ic::PICInfo
&pic
, JSAtom
*atom
,
555 : PICStubCompiler("callprop", f
, script
, pic
), obj(obj
), atom(atom
),
556 stub(JS_FUNC_TO_DATA_PTR(void *, stub
)),
557 lastStubSecondShapeGuard(pic
.u
.get
.secondShapeGuard
)
560 static void reset(ic::PICInfo
&pic
)
562 RepatchBuffer
repatcher(pic
.fastPathStart
.executableAddress(), INLINE_PATH_LENGTH
);
563 repatcher
.repatchLEAToLoadPtr(pic
.storeBack
.instructionAtOffset(dslotsLoad(pic
)));
564 repatcher
.repatch(pic
.fastPathStart
.dataLabel32AtOffset(
565 pic
.shapeGuard
+ inlineShapeOffset(pic
)),
566 int32(JSScope::INVALID_SHAPE
));
567 repatcher
.relink(pic
.fastPathStart
.jumpAtOffset(pic
.shapeGuard
+ inlineShapeJump(pic
)),
570 if (pic
.hasTypeCheck()) {
571 repatcher
.relink(pic
.fastPathStart
.jumpAtOffset(GETPROP_INLINE_TYPE_GUARD
),
575 RepatchBuffer
repatcher2(pic
.slowPathStart
.executableAddress(), INLINE_PATH_LENGTH
);
576 ReturnAddressPtr
retPtr(pic
.slowPathStart
.callAtOffset(pic
.callReturn
).executableAddress());
580 case ic::PICInfo::GET
:
583 case ic::PICInfo::CALL
:
587 JS_NOT_REACHED("invalid pic kind for GetPropCompiler::reset");
591 MacroAssemblerCodePtr
target(JS_FUNC_TO_DATA_PTR(void *, stub
));
592 repatcher
.relinkCallerToTrampoline(retPtr
, target
);
595 bool generateArgsLengthStub()
599 Address
clasp(pic
.objReg
, offsetof(JSObject
, clasp
));
600 Jump notArgs
= masm
.branchPtr(Assembler::NotEqual
, clasp
, ImmPtr(&js_SlowArrayClass
));
602 masm
.load32(Address(pic
.objReg
, offsetof(JSObject
, fslots
) +
603 JSObject::JSSLOT_ARGS_LENGTH
* sizeof(Value
)),
605 masm
.move(pic
.objReg
, pic
.shapeReg
);
606 masm
.and32(Imm32(1), pic
.shapeReg
);
607 Jump overridden
= masm
.branchTest32(Assembler::NonZero
, pic
.shapeReg
, pic
.shapeReg
);
609 masm
.move(ImmType(JSVAL_TYPE_INT32
), pic
.shapeReg
);
610 Jump done
= masm
.jump();
612 JSC::ExecutablePool
*ep
= getExecPool(masm
.size());
613 if (!ep
|| !pic
.execPools
.append(ep
)) {
616 js_ReportOutOfMemory(f
.cx
);
620 JSC::LinkBuffer
buffer(&masm
, ep
);
621 buffer
.link(notArgs
, pic
.slowPathStart
);
622 buffer
.link(overridden
, pic
.slowPathStart
);
623 buffer
.link(done
, pic
.storeBack
);
625 CodeLocationLabel start
= buffer
.finalizeCodeAddendum();
626 JaegerSpew(JSpew_PICs
, "generate args length stub at %p\n",
627 start
.executableAddress());
629 PICRepatchBuffer
repatcher(pic
, pic
.lastPathStart());
630 patchPreviousToHere(repatcher
, start
);
632 disable("args length done");
637 bool generateArrayLengthStub()
641 masm
.loadPtr(Address(pic
.objReg
, offsetof(JSObject
, clasp
)), pic
.shapeReg
);
642 Jump isDense
= masm
.branchPtr(Assembler::Equal
, pic
.shapeReg
, ImmPtr(&js_ArrayClass
));
643 Jump notArray
= masm
.branchPtr(Assembler::NotEqual
, pic
.shapeReg
,
644 ImmPtr(&js_SlowArrayClass
));
646 isDense
.linkTo(masm
.label(), &masm
);
647 masm
.load32(Address(pic
.objReg
, offsetof(JSObject
, fslots
) +
648 JSObject::JSSLOT_ARRAY_LENGTH
* sizeof(Value
)),
650 Jump oob
= masm
.branch32(Assembler::Above
, pic
.objReg
, Imm32(JSVAL_INT_MAX
));
651 masm
.move(ImmType(JSVAL_TYPE_INT32
), pic
.shapeReg
);
652 Jump done
= masm
.jump();
654 JSC::ExecutablePool
*ep
= getExecPool(masm
.size());
655 if (!ep
|| !pic
.execPools
.append(ep
)) {
658 js_ReportOutOfMemory(f
.cx
);
662 JSC::LinkBuffer
buffer(&masm
, ep
);
663 buffer
.link(notArray
, pic
.slowPathStart
);
664 buffer
.link(oob
, pic
.slowPathStart
);
665 buffer
.link(done
, pic
.storeBack
);
667 CodeLocationLabel start
= buffer
.finalizeCodeAddendum();
668 JaegerSpew(JSpew_PICs
, "generate array length stub at %p\n",
669 start
.executableAddress());
671 PICRepatchBuffer
repatcher(pic
, pic
.lastPathStart());
672 patchPreviousToHere(repatcher
, start
);
674 disable("array length done");
679 bool generateStringCallStub()
681 JS_ASSERT(pic
.hasTypeCheck());
682 JS_ASSERT(pic
.kind
== ic::PICInfo::CALL
);
684 if (!f
.fp
->script
->compileAndGo
)
685 return disable("String.prototype without compile-and-go");
687 mjit::ThreadData
&jm
= JS_METHODJIT_DATA(f
.cx
);
688 if (!jm
.addScript(script
)) {
689 js_ReportOutOfMemory(f
.cx
);
695 if (!obj
->lookupProperty(f
.cx
, ATOM_TO_JSID(atom
), &holder
, &prop
))
698 return disable("property not found");
700 AutoPropertyDropper
dropper(f
.cx
, holder
, prop
);
701 JSScopeProperty
*sprop
= (JSScopeProperty
*)prop
;
703 return disable("proto walk on String.prototype");
704 if (!sprop
->hasDefaultGetterOrIsMethod())
705 return disable("getter");
706 if (!SPROP_HAS_VALID_SLOT(sprop
, holder
->scope()))
707 return disable("invalid slot");
709 JS_ASSERT(holder
->isNative());
713 /* Only strings are allowed. */
714 Jump notString
= masm
.branchPtr(Assembler::NotEqual
, pic
.typeReg(),
715 ImmType(JSVAL_TYPE_STRING
));
718 * Sink pic.objReg, since we're about to lose it. This is optimistic,
719 * we could reload it from objRemat if we wanted.
721 * Note: This is really hacky, and relies on f.regs.sp being set
722 * correctly in ic::CallProp. Should we just move the store higher
723 * up in the fast path, or put this offset in PICInfo?
725 uint32 thisvOffset
= uint32(f
.regs
.sp
- f
.fp
->slots()) - 1;
726 Address
thisv(JSFrameReg
, sizeof(JSStackFrame
) + thisvOffset
* sizeof(Value
));
727 masm
.storeTypeTag(ImmType(JSVAL_TYPE_STRING
), thisv
);
728 masm
.storePayload(pic
.objReg
, thisv
);
731 * Clobber objReg with String.prototype and do some PIC stuff. Well,
732 * really this is now a MIC, except it won't ever be patched, so we
733 * just disable the PIC at the end. :FIXME:? String.prototype probably
734 * does not get random shape changes.
736 masm
.move(ImmPtr(obj
), pic
.objReg
);
737 masm
.loadShape(pic
.objReg
, pic
.shapeReg
);
738 Jump shapeMismatch
= masm
.branch32(Assembler::NotEqual
, pic
.shapeReg
,
739 Imm32(obj
->shape()));
740 masm
.loadSlot(pic
.objReg
, pic
.objReg
, sprop
->slot
, pic
.shapeReg
, pic
.objReg
);
742 Jump done
= masm
.jump();
744 JSC::ExecutablePool
*ep
= getExecPool(masm
.size());
745 if (!ep
|| !pic
.execPools
.append(ep
)) {
748 js_ReportOutOfMemory(f
.cx
);
752 JSC::LinkBuffer
patchBuffer(&masm
, ep
);
754 patchBuffer
.link(notString
, pic
.slowPathStart
.labelAtOffset(pic
.u
.get
.typeCheckOffset
));
755 patchBuffer
.link(shapeMismatch
, pic
.slowPathStart
);
756 patchBuffer
.link(done
, pic
.storeBack
);
758 CodeLocationLabel cs
= patchBuffer
.finalizeCodeAddendum();
759 JaegerSpew(JSpew_PICs
, "generate string call stub at %p\n",
760 cs
.executableAddress());
762 /* Patch the type check to jump here. */
763 if (pic
.hasTypeCheck()) {
764 RepatchBuffer
repatcher(pic
.fastPathStart
.executableAddress(), INLINE_PATH_LENGTH
);
765 repatcher
.relink(pic
.fastPathStart
.jumpAtOffset(GETPROP_INLINE_TYPE_GUARD
), cs
);
768 /* Disable the PIC so we don't keep generating stubs on the above shape mismatch. */
769 disable("generated string call stub");
774 bool generateStringLengthStub()
776 JS_ASSERT(pic
.hasTypeCheck());
779 Jump notString
= masm
.branchPtr(Assembler::NotEqual
, pic
.typeReg(),
780 ImmType(JSVAL_TYPE_STRING
));
781 masm
.loadPtr(Address(pic
.objReg
, offsetof(JSString
, mLengthAndFlags
)), pic
.objReg
);
782 // String length is guaranteed to be no more than 2**28, so the 32-bit operation is OK.
783 masm
.urshift32(Imm32(JSString::FLAGS_LENGTH_SHIFT
), pic
.objReg
);
784 masm
.move(ImmType(JSVAL_TYPE_INT32
), pic
.shapeReg
);
785 Jump done
= masm
.jump();
787 JSC::ExecutablePool
*ep
= getExecPool(masm
.size());
788 if (!ep
|| !pic
.execPools
.append(ep
)) {
791 js_ReportOutOfMemory(f
.cx
);
795 JSC::LinkBuffer
patchBuffer(&masm
, ep
);
796 patchBuffer
.link(notString
, pic
.slowPathStart
.labelAtOffset(pic
.u
.get
.typeCheckOffset
));
797 patchBuffer
.link(done
, pic
.storeBack
);
799 CodeLocationLabel start
= patchBuffer
.finalizeCodeAddendum();
800 JaegerSpew(JSpew_PICs
, "generate string length stub at %p\n",
801 start
.executableAddress());
803 if (pic
.hasTypeCheck()) {
804 RepatchBuffer
repatcher(pic
.fastPathStart
.executableAddress(), INLINE_PATH_LENGTH
);
805 repatcher
.relink(pic
.fastPathStart
.jumpAtOffset(GETPROP_INLINE_TYPE_GUARD
), start
);
808 disable("generated string length stub");
813 bool patchInline(JSObject
*holder
, JSScopeProperty
*sprop
)
815 spew("patch", "inline");
816 PICRepatchBuffer
repatcher(pic
, pic
.fastPathStart
);
818 mjit::ThreadData
&jm
= JS_METHODJIT_DATA(f
.cx
);
819 if (!jm
.addScript(script
)) {
820 js_ReportOutOfMemory(f
.cx
);
825 if (sprop
->slot
< JS_INITIAL_NSLOTS
) {
826 JSC::CodeLocationInstruction istr
;
827 istr
= pic
.storeBack
.instructionAtOffset(dslotsLoad());
828 repatcher
.repatchLoadPtrToLEA(istr
);
831 // We've patched | mov dslots, [obj + DSLOTS_OFFSET]
832 // To: | lea fslots, [obj + DSLOTS_OFFSET]
834 // Because the offset is wrong, it's necessary to correct it
837 int32 diff
= int32(offsetof(JSObject
, fslots
)) -
838 int32(offsetof(JSObject
, dslots
));
839 JS_ASSERT(diff
!= 0);
840 offset
= (int32(sprop
->slot
) * sizeof(Value
)) + diff
;
842 offset
= (sprop
->slot
- JS_INITIAL_NSLOTS
) * sizeof(Value
);
845 uint32 shapeOffs
= pic
.shapeGuard
+ inlineShapeOffset();
846 repatcher
.repatch(pic
.fastPathStart
.dataLabel32AtOffset(shapeOffs
), obj
->shape());
847 #if defined JS_NUNBOX32
848 repatcher
.repatch(pic
.storeBack
.dataLabel32AtOffset(GETPROP_TYPE_LOAD
), offset
+ 4);
849 repatcher
.repatch(pic
.storeBack
.dataLabel32AtOffset(GETPROP_DATA_LOAD
), offset
);
850 #elif defined JS_PUNBOX64
851 repatcher
.repatch(pic
.storeBack
.dataLabel32AtOffset(pic
.labels
.getprop
.inlineValueOffset
), offset
);
854 pic
.inlinePathPatched
= true;
859 bool generateStub(JSObject
*holder
, JSScopeProperty
*sprop
)
861 Vector
<Jump
, 8> shapeMismatches(f
.cx
);
865 if (pic
.objNeedsRemat()) {
866 if (pic
.objRemat() >= sizeof(JSStackFrame
))
867 masm
.load32(Address(JSFrameReg
, pic
.objRemat()), pic
.objReg
);
869 masm
.move(RegisterID(pic
.objRemat()), pic
.objReg
);
870 pic
.u
.get
.objNeedsRemat
= false;
876 if (obj
->isDenseArray()) {
877 start
= masm
.label();
878 shapeGuard
= masm
.branchPtr(Assembler::NotEqual
,
879 Address(pic
.objReg
, offsetof(JSObject
, clasp
)),
880 ImmPtr(obj
->getClass()));
882 * No need to assert validity of GETPROP_STUB_SHAPE_JUMP in this case:
883 * the IC is disabled after a dense array hit, so no patching can occur.
886 if (pic
.shapeNeedsRemat()) {
887 masm
.loadShape(pic
.objReg
, pic
.shapeReg
);
888 pic
.shapeRegHasBaseShape
= true;
891 start
= masm
.label();
892 shapeGuard
= masm
.branch32_force32(Assembler::NotEqual
, pic
.shapeReg
,
893 Imm32(obj
->shape()));
894 #if defined JS_NUNBOX32
895 JS_ASSERT(masm
.differenceBetween(start
, shapeGuard
) == GETPROP_STUB_SHAPE_JUMP
);
899 #if defined JS_PUNBOX64
900 Label stubShapeJumpLabel
= masm
.label();
904 if (!shapeMismatches
.append(shapeGuard
))
908 // Emit code that walks the prototype chain.
909 JSObject
*tempObj
= obj
;
910 Address
proto(pic
.objReg
, offsetof(JSObject
, proto
));
912 tempObj
= tempObj
->getProto();
913 // FIXME: we should find out why this condition occurs. It is probably
914 // related to PICs on globals.
916 return disable("null object in prototype chain");
920 * If there is a non-native along the prototype chain the shape is technically
923 if (!tempObj
->isNative())
924 return disable("non-JS-native in prototype chain");
926 masm
.loadPtr(proto
, pic
.objReg
);
927 pic
.shapeRegHasBaseShape
= false;
928 pic
.u
.get
.objNeedsRemat
= true;
930 Jump j
= masm
.branchTestPtr(Assembler::Zero
, pic
.objReg
, pic
.objReg
);
931 if (!shapeMismatches
.append(j
))
933 } while (tempObj
!= holder
);
935 // Load the shape out of the holder and check it.
936 masm
.loadShape(pic
.objReg
, pic
.shapeReg
);
937 Jump j
= masm
.branch32_force32(Assembler::NotEqual
, pic
.shapeReg
,
938 Imm32(holder
->shape()));
939 if (!shapeMismatches
.append(j
))
941 pic
.u
.get
.secondShapeGuard
= masm
.distanceOf(masm
.label()) - masm
.distanceOf(start
);
943 JS_ASSERT(holder
->isNative()); /* Precondition: already checked. */
944 pic
.u
.get
.secondShapeGuard
= 0;
947 /* Load the value out of the object. */
948 masm
.loadSlot(pic
.objReg
, pic
.objReg
, sprop
->slot
, pic
.shapeReg
, pic
.objReg
);
949 Jump done
= masm
.jump();
951 JSC::ExecutablePool
*ep
= getExecPool(masm
.size());
953 js_ReportOutOfMemory(f
.cx
);
957 // :TODO: this can OOM
958 JSC::LinkBuffer
buffer(&masm
, ep
);
960 if (!pic
.execPools
.append(ep
)) {
962 js_ReportOutOfMemory(f
.cx
);
966 // The guard exit jumps to the original slow case.
967 for (Jump
*pj
= shapeMismatches
.begin(); pj
!= shapeMismatches
.end(); ++pj
)
968 buffer
.link(*pj
, pic
.slowPathStart
);
970 // The final exit jumps to the store-back in the inline stub.
971 buffer
.link(done
, pic
.storeBack
);
972 CodeLocationLabel cs
= buffer
.finalizeCodeAddendum();
973 JaegerSpew(JSpew_PICs
, "generated %s stub at %p\n", type
, cs
.executableAddress());
975 PICRepatchBuffer
repatcher(pic
, pic
.lastPathStart());
976 patchPreviousToHere(repatcher
, cs
);
978 pic
.stubsGenerated
++;
979 pic
.lastStubStart
= buffer
.locationOf(start
);
981 #if defined JS_PUNBOX64
982 pic
.labels
.getprop
.stubShapeJump
= masm
.differenceBetween(start
, stubShapeJumpLabel
);
985 if (pic
.stubsGenerated
== MAX_PIC_STUBS
)
986 disable("max stubs reached");
987 if (obj
->isDenseArray())
988 disable("dense array");
993 void patchPreviousToHere(PICRepatchBuffer
&repatcher
, CodeLocationLabel cs
)
995 // Patch either the inline fast path or a generated stub. The stub
996 // omits the prefix of the inline fast path that loads the shape, so
997 // the offsets are different.
998 int shapeGuardJumpOffset
;
999 if (pic
.stubsGenerated
)
1000 #if defined JS_NUNBOX32
1001 shapeGuardJumpOffset
= GETPROP_STUB_SHAPE_JUMP
;
1002 #elif defined JS_PUNBOX64
1003 shapeGuardJumpOffset
= pic
.labels
.getprop
.stubShapeJump
;
1006 shapeGuardJumpOffset
= pic
.shapeGuard
+ inlineShapeJump();
1007 repatcher
.relink(shapeGuardJumpOffset
, cs
);
1008 if (lastStubSecondShapeGuard
)
1009 repatcher
.relink(lastStubSecondShapeGuard
, cs
);
1015 spew("first hit", "nop");
1020 JSObject
*aobj
= js_GetProtoIfDenseArray(obj
);
1021 if (!aobj
->isNative())
1022 return disable("non-native");
1026 if (!aobj
->lookupProperty(f
.cx
, ATOM_TO_JSID(atom
), &holder
, &prop
))
1030 return disable("lookup failed");
1032 AutoPropertyDropper
dropper(f
.cx
, holder
, prop
);
1034 if (!holder
->isNative())
1035 return disable("non-native holder");
1037 JSScopeProperty
*sprop
= (JSScopeProperty
*)prop
;
1038 if (!sprop
->hasDefaultGetterOrIsMethod())
1039 return disable("getter");
1040 if (!SPROP_HAS_VALID_SLOT(sprop
, holder
->scope()))
1041 return disable("invalid slot");
1043 if (obj
== holder
&& !pic
.inlinePathPatched
)
1044 return patchInline(holder
, sprop
);
1046 return generateStub(holder
, sprop
);
1049 bool disable(const char *reason
)
1051 return PICStubCompiler::disable(reason
, stub
);
1055 class GetElemCompiler
: public PICStubCompiler
1060 int lastStubSecondShapeGuard
;
1062 static int32
dslotsLoad(ic::PICInfo
&pic
) {
1063 #if defined JS_NUNBOX32
1064 return GETELEM_DSLOTS_LOAD
;
1065 #elif defined JS_PUNBOX64
1066 return pic
.labels
.getelem
.dslotsLoadOffset
;
1070 inline int32
dslotsLoad() {
1071 return dslotsLoad(pic
);
1074 static int32
inlineShapeOffset(ic::PICInfo
&pic
) {
1075 #if defined JS_NUNBOX32
1076 return GETELEM_INLINE_SHAPE_OFFSET
;
1077 #elif defined JS_PUNBOX64
1078 return pic
.labels
.getelem
.inlineShapeOffset
;
1082 inline int32
inlineShapeOffset() {
1083 return inlineShapeOffset(pic
);
1086 static int32
inlineShapeJump(ic::PICInfo
&pic
) {
1087 #if defined JS_NUNBOX32
1088 return GETELEM_INLINE_SHAPE_JUMP
;
1089 #elif defined JS_PUNBOX64
1090 return inlineShapeOffset(pic
) + GETELEM_INLINE_SHAPE_JUMP
;
1094 inline int32
inlineShapeJump() {
1095 return inlineShapeJump(pic
);
1098 static int32
inlineAtomOffset(ic::PICInfo
&pic
) {
1099 #if defined JS_NUNBOX32
1100 return GETELEM_INLINE_ATOM_OFFSET
;
1101 #elif defined JS_PUNBOX64
1102 return pic
.labels
.getelem
.inlineAtomOffset
;
1106 inline int32
inlineAtomOffset() {
1107 return inlineAtomOffset(pic
);
1110 static int32
inlineAtomJump(ic::PICInfo
&pic
) {
1111 #if defined JS_NUNBOX32
1112 return GETELEM_INLINE_ATOM_JUMP
;
1113 #elif defined JS_PUNBOX64
1114 return inlineAtomOffset(pic
) + GETELEM_INLINE_ATOM_JUMP
;
1118 inline int32
inlineAtomJump() {
1119 return inlineAtomJump(pic
);
1123 GetElemCompiler(VMFrame
&f
, JSScript
*script
, JSObject
*obj
, ic::PICInfo
&pic
, JSString
*id
,
1125 : PICStubCompiler("getelem", f
, script
, pic
), obj(obj
), id(id
),
1126 stub(JS_FUNC_TO_DATA_PTR(void *, stub
)),
1127 lastStubSecondShapeGuard(pic
.u
.get
.secondShapeGuard
)
1130 static void reset(ic::PICInfo
&pic
)
1132 JS_ASSERT(pic
.kind
== ic::PICInfo::GETELEM
);
1134 RepatchBuffer
repatcher(pic
.fastPathStart
.executableAddress(), INLINE_PATH_LENGTH
);
1135 repatcher
.repatchLEAToLoadPtr(pic
.storeBack
.instructionAtOffset(dslotsLoad(pic
)));
1137 /* Only the shape needs to be patched to fail -- atom jump will never be taken. */
1138 repatcher
.repatch(pic
.fastPathStart
.dataLabel32AtOffset(
1139 pic
.shapeGuard
+ inlineShapeOffset(pic
)),
1140 int32(JSScope::INVALID_SHAPE
));
1141 repatcher
.relink(pic
.fastPathStart
.jumpAtOffset(pic
.shapeGuard
+ inlineShapeJump(pic
)),
1144 RepatchBuffer
repatcher2(pic
.slowPathStart
.executableAddress(), INLINE_PATH_LENGTH
);
1145 ReturnAddressPtr
retPtr(pic
.slowPathStart
.callAtOffset(pic
.callReturn
).executableAddress());
1147 MacroAssemblerCodePtr
target(JS_FUNC_TO_DATA_PTR(void *, ic::GetElem
));
1148 repatcher
.relinkCallerToTrampoline(retPtr
, target
);
1151 bool patchInline(JSObject
*holder
, JSScopeProperty
*sprop
)
1153 spew("patch", "inline");
1154 PICRepatchBuffer
repatcher(pic
, pic
.fastPathStart
);
1157 if (sprop
->slot
< JS_INITIAL_NSLOTS
) {
1158 JSC::CodeLocationInstruction istr
= pic
.storeBack
.instructionAtOffset(dslotsLoad());
1159 repatcher
.repatchLoadPtrToLEA(istr
);
1162 // We've patched | mov dslots, [obj + DSLOTS_OFFSET]
1163 // To: | lea fslots, [obj + DSLOTS_OFFSET]
1165 // Because the offset is wrong, it's necessary to correct it
1168 int32 diff
= int32(offsetof(JSObject
, fslots
)) - int32(offsetof(JSObject
, dslots
));
1169 JS_ASSERT(diff
!= 0);
1170 offset
= (int32(sprop
->slot
) * sizeof(Value
)) + diff
;
1172 offset
= (sprop
->slot
- JS_INITIAL_NSLOTS
) * sizeof(Value
);
1175 uint32 shapeOffset
= pic
.shapeGuard
+ inlineShapeOffset();
1176 repatcher
.repatch(pic
.fastPathStart
.dataLabel32AtOffset(shapeOffset
), obj
->shape());
1177 uint32 idOffset
= pic
.shapeGuard
+ inlineAtomOffset();
1178 repatcher
.repatch(pic
.fastPathStart
.dataLabelPtrAtOffset(idOffset
), id
);
1179 #if defined JS_NUNBOX32
1180 repatcher
.repatch(pic
.storeBack
.dataLabel32AtOffset(GETELEM_TYPE_LOAD
), offset
+ 4);
1181 repatcher
.repatch(pic
.storeBack
.dataLabel32AtOffset(GETELEM_DATA_LOAD
), offset
);
1182 #elif defined JS_PUNBOX64
1183 repatcher
.repatch(pic
.storeBack
.dataLabel32AtOffset(pic
.labels
.getelem
.inlineValueOffset
), offset
);
1185 pic
.inlinePathPatched
= true;
1190 void patchPreviousToHere(PICRepatchBuffer
&repatcher
, CodeLocationLabel cs
)
1192 // Patch either the inline fast path or a generated stub. The stub
1193 // omits the prefix of the inline fast path that loads the shape, so
1194 // the offsets are different.
1195 int shapeGuardJumpOffset
;
1196 int atomGuardJumpOffset
;
1197 if (pic
.stubsGenerated
) {
1198 #if defined JS_NUNBOX32
1199 shapeGuardJumpOffset
= GETELEM_STUB_SHAPE_JUMP
;
1200 #elif defined JS_PUNBOX64
1201 shapeGuardJumpOffset
= pic
.labels
.getelem
.stubShapeJump
;
1203 atomGuardJumpOffset
= GETELEM_STUB_ATOM_JUMP
;
1205 shapeGuardJumpOffset
= pic
.shapeGuard
+ inlineShapeJump();
1206 atomGuardJumpOffset
= pic
.shapeGuard
+ inlineAtomJump();
1208 repatcher
.relink(shapeGuardJumpOffset
, cs
);
1209 repatcher
.relink(atomGuardJumpOffset
, cs
);
1210 if (lastStubSecondShapeGuard
)
1211 repatcher
.relink(lastStubSecondShapeGuard
, cs
);
1214 bool generateStub(JSObject
*holder
, JSScopeProperty
*sprop
)
1216 JS_ASSERT(pic
.u
.get
.idReg
!= pic
.shapeReg
);
1217 Vector
<Jump
, 8> shapeMismatches(f
.cx
);
1221 if (pic
.objNeedsRemat()) {
1222 if (pic
.objRemat() >= sizeof(JSStackFrame
))
1223 masm
.load32(Address(JSFrameReg
, pic
.objRemat()), pic
.objReg
);
1225 masm
.move(RegisterID(pic
.objRemat()), pic
.objReg
);
1226 pic
.u
.get
.objNeedsRemat
= false;
1229 if (pic
.idNeedsRemat()) {
1230 if (pic
.idRemat() >= sizeof(JSStackFrame
))
1231 masm
.load32(Address(JSFrameReg
, pic
.idRemat()), pic
.u
.get
.idReg
);
1233 masm
.move(RegisterID(pic
.idRemat()), pic
.u
.get
.idReg
);
1234 pic
.u
.get
.idNeedsRemat
= false;
1237 if (pic
.shapeNeedsRemat()) {
1238 masm
.loadShape(pic
.objReg
, pic
.shapeReg
);
1239 pic
.shapeRegHasBaseShape
= true;
1242 Label start
= masm
.label();
1243 Jump atomGuard
= masm
.branchPtr(Assembler::NotEqual
, pic
.u
.get
.idReg
, ImmPtr(id
));
1244 DBGLABEL(dbgStubAtomJump
);
1245 Jump shapeGuard
= masm
.branch32_force32(Assembler::NotEqual
, pic
.shapeReg
,
1246 Imm32(obj
->shape()));
1248 #if (defined JS_NUNBOX32 && defined DEBUG) || defined JS_PUNBOX64
1249 Label stubShapeJump
= masm
.label();
1252 JS_ASSERT(masm
.differenceBetween(start
, dbgStubAtomJump
) == GETELEM_STUB_ATOM_JUMP
);
1253 #if defined JS_NUNBOX32
1254 JS_ASSERT(masm
.differenceBetween(start
, stubShapeJump
) == GETELEM_STUB_SHAPE_JUMP
);
1257 if (!(shapeMismatches
.append(shapeGuard
) && shapeMismatches
.append(atomGuard
)))
1260 if (obj
!= holder
) {
1261 // Emit code that walks the prototype chain.
1262 JSObject
*tempObj
= obj
;
1263 Address
proto(pic
.objReg
, offsetof(JSObject
, proto
));
1265 tempObj
= tempObj
->getProto();
1266 // FIXME: we should find out why this condition occurs. It is probably
1267 // related to PICs on globals.
1269 return disable("null object in prototype chain");
1273 * If there is a non-native along the prototype chain the shape is technically
1276 if (!tempObj
->isNative())
1277 return disable("non-JS-native in prototype chain");
1279 masm
.loadPtr(proto
, pic
.objReg
);
1280 pic
.shapeRegHasBaseShape
= false;
1281 pic
.u
.get
.objNeedsRemat
= true;
1283 Jump j
= masm
.branchTestPtr(Assembler::Zero
, pic
.objReg
, pic
.objReg
);
1284 if (!shapeMismatches
.append(j
))
1286 } while (tempObj
!= holder
);
1288 // Load the shape out of the holder and check it.
1289 masm
.loadShape(pic
.objReg
, pic
.shapeReg
);
1290 Jump j
= masm
.branch32_force32(Assembler::NotEqual
, pic
.shapeReg
,
1291 Imm32(holder
->shape()));
1292 if (!shapeMismatches
.append(j
))
1294 pic
.u
.get
.secondShapeGuard
= masm
.distanceOf(masm
.label()) - masm
.distanceOf(start
);
1296 JS_ASSERT(holder
->isNative()); /* Precondition: already checked. */
1297 pic
.u
.get
.secondShapeGuard
= 0;
1300 /* Load the value out of the object. */
1301 masm
.loadSlot(pic
.objReg
, pic
.objReg
, sprop
->slot
, pic
.shapeReg
, pic
.objReg
);
1302 Jump done
= masm
.jump();
1304 JSC::ExecutablePool
*ep
= getExecPool(masm
.size());
1306 js_ReportOutOfMemory(f
.cx
);
1310 // :TODO: this can OOM
1311 JSC::LinkBuffer
buffer(&masm
, ep
);
1313 if (!pic
.execPools
.append(ep
)) {
1315 js_ReportOutOfMemory(f
.cx
);
1319 // The guard exit jumps to the original slow case.
1320 for (Jump
*pj
= shapeMismatches
.begin(); pj
!= shapeMismatches
.end(); ++pj
)
1321 buffer
.link(*pj
, pic
.slowPathStart
);
1323 // The final exit jumps to the store-back in the inline stub.
1324 buffer
.link(done
, pic
.storeBack
);
1325 CodeLocationLabel cs
= buffer
.finalizeCodeAddendum();
1327 char *chars
= js_DeflateString(f
.cx
, id
->chars(), id
->length());
1328 JaegerSpew(JSpew_PICs
, "generated %s stub at %p for atom 0x%x (\"%s\") shape 0x%x (%s: %d)\n",
1329 type
, cs
.executableAddress(), id
, chars
, holder
->shape(), script
->filename
,
1330 js_FramePCToLineNumber(f
.cx
, f
.fp
));
1334 PICRepatchBuffer
repatcher(pic
, pic
.lastPathStart());
1335 patchPreviousToHere(repatcher
, cs
);
1337 pic
.stubsGenerated
++;
1338 pic
.lastStubStart
= buffer
.locationOf(start
);
1340 #if defined JS_PUNBOX64
1341 pic
.labels
.getelem
.stubShapeJump
= masm
.differenceBetween(start
, stubShapeJump
);
1344 if (pic
.stubsGenerated
== MAX_PIC_STUBS
)
1345 disable("max stubs reached");
1346 if (obj
->isDenseArray())
1347 disable("dense array");
1355 spew("first hit", "nop");
1360 JSAtom
*atom
= js_AtomizeString(f
.cx
, id
, 0);
1365 if (!obj
->lookupProperty(f
.cx
, ATOM_TO_JSID(atom
), &holder
, &prop
))
1369 return disable("lookup failed");
1371 AutoPropertyDropper
dropper(f
.cx
, holder
, prop
);
1373 if (!obj
->isNative())
1374 return disable("non-native obj");
1375 if (!holder
->isNative())
1376 return disable("non-native holder");
1378 JSScopeProperty
*sprop
= (JSScopeProperty
*)prop
;
1379 if (!sprop
->hasDefaultGetterOrIsMethod())
1380 return disable("getter");
1381 if (!SPROP_HAS_VALID_SLOT(sprop
, holder
->scope()))
1382 return disable("invalid slot");
1384 if (obj
== holder
&& !pic
.inlinePathPatched
)
1385 return patchInline(holder
, sprop
);
1387 return generateStub(holder
, sprop
);
1390 bool disable(const char *reason
)
1392 return PICStubCompiler::disable(reason
, stub
);
1396 class ScopeNameCompiler
: public PICStubCompiler
1398 JSObject
*scopeChain
;
1406 JSScopeProperty
*sprop
;
1409 ScopeNameCompiler(VMFrame
&f
, JSScript
*script
, JSObject
*scopeChain
, ic::PICInfo
&pic
,
1410 JSAtom
*atom
, VoidStubUInt32 stub
)
1411 : PICStubCompiler("name", f
, script
, pic
), scopeChain(scopeChain
), atom(atom
),
1412 stub(JS_FUNC_TO_DATA_PTR(void *, stub
)), obj(NULL
), holder(NULL
), prop(NULL
)
1415 bool disable(const char *reason
)
1417 return PICStubCompiler::disable(reason
, stub
);
1420 static void reset(ic::PICInfo
&pic
)
1422 RepatchBuffer
repatcher(pic
.fastPathStart
.executableAddress(), INLINE_PATH_LENGTH
);
1423 repatcher
.relink(pic
.fastPathStart
.jumpAtOffset(SCOPENAME_JUMP_OFFSET
),
1426 RepatchBuffer
repatcher2(pic
.slowPathStart
.executableAddress(), INLINE_PATH_LENGTH
);
1427 ReturnAddressPtr
retPtr(pic
.slowPathStart
.callAtOffset(pic
.callReturn
).executableAddress());
1428 MacroAssemblerCodePtr
target(JS_FUNC_TO_DATA_PTR(void *, ic::Name
));
1429 repatcher
.relinkCallerToTrampoline(retPtr
, target
);
1432 typedef Vector
<Jump
, 8, ContextAllocPolicy
> JumpList
;
1434 bool walkScopeChain(Assembler
&masm
, JumpList
&fails
, bool &found
)
1436 /* Walk the scope chain. */
1437 JSObject
*tobj
= scopeChain
;
1439 while (tobj
&& tobj
!= holder
) {
1440 if (!js_IsCacheableNonGlobalScope(tobj
))
1441 return disable("non-cacheable scope chain object");
1442 JS_ASSERT(tobj
->isNative());
1444 if (tobj
!= scopeChain
) {
1445 /* scopeChain will never be NULL, but parents can be NULL. */
1446 Jump j
= masm
.branchTestPtr(Assembler::Zero
, pic
.objReg
, pic
.objReg
);
1447 if (!fails
.append(j
))
1451 /* Guard on intervening shapes. */
1452 masm
.loadShape(pic
.objReg
, pic
.shapeReg
);
1453 Jump j
= masm
.branch32(Assembler::NotEqual
, pic
.shapeReg
, Imm32(tobj
->shape()));
1454 if (!fails
.append(j
))
1457 /* Load the next link in the scope chain. */
1458 Address
parent(pic
.objReg
, offsetof(JSObject
, parent
));
1459 masm
.loadPtr(parent
, pic
.objReg
);
1461 tobj
= tobj
->getParent();
1464 found
= tobj
== holder
;
1469 bool generateGlobalStub()
1472 JumpList
fails(f
.cx
);
1474 masm
.loadPtr(Address(JSFrameReg
, offsetof(JSStackFrame
, scopeChain
)), pic
.objReg
);
1476 JS_ASSERT(obj
== holder
);
1477 JS_ASSERT(holder
== scopeChain
->getGlobal());
1480 if (!walkScopeChain(masm
, fails
, found
))
1483 return disable("scope chain walk terminated early");
1485 Jump finalNull
= masm
.branchTestPtr(Assembler::Zero
, pic
.objReg
, pic
.objReg
);
1486 masm
.loadShape(pic
.objReg
, pic
.shapeReg
);
1487 Jump finalShape
= masm
.branch32(Assembler::NotEqual
, pic
.shapeReg
, Imm32(holder
->shape()));
1489 masm
.loadSlot(pic
.objReg
, pic
.objReg
, sprop
->slot
, pic
.shapeReg
, pic
.objReg
);
1491 Jump done
= masm
.jump();
1493 // All failures flow to here, so there is a common point to patch.
1494 for (Jump
*pj
= fails
.begin(); pj
!= fails
.end(); ++pj
)
1495 pj
->linkTo(masm
.label(), &masm
);
1496 finalNull
.linkTo(masm
.label(), &masm
);
1497 finalShape
.linkTo(masm
.label(), &masm
);
1498 Label failLabel
= masm
.label();
1499 Jump failJump
= masm
.jump();
1500 DBGLABEL(dbgJumpOffset
);
1502 JS_ASSERT(masm
.differenceBetween(failLabel
, dbgJumpOffset
) == SCOPENAME_JUMP_OFFSET
);
1504 JSC::ExecutablePool
*ep
= getExecPool(masm
.size());
1506 js_ReportOutOfMemory(f
.cx
);
1510 // :TODO: this can OOM
1511 JSC::LinkBuffer
buffer(&masm
, ep
);
1513 if (!pic
.execPools
.append(ep
)) {
1515 js_ReportOutOfMemory(f
.cx
);
1519 buffer
.link(failJump
, pic
.slowPathStart
);
1520 buffer
.link(done
, pic
.storeBack
);
1521 CodeLocationLabel cs
= buffer
.finalizeCodeAddendum();
1522 JaegerSpew(JSpew_PICs
, "generated %s global stub at %p\n", type
, cs
.executableAddress());
1523 spew("NAME stub", "global");
1525 PICRepatchBuffer
repatcher(pic
, pic
.lastPathStart());
1526 repatcher
.relink(SCOPENAME_JUMP_OFFSET
, cs
);
1528 pic
.stubsGenerated
++;
1529 pic
.lastStubStart
= buffer
.locationOf(failLabel
);
1531 if (pic
.stubsGenerated
== MAX_PIC_STUBS
)
1532 disable("max stubs reached");
1537 enum CallObjPropKind
{
1542 bool generateCallStub()
1545 Vector
<Jump
, 8, ContextAllocPolicy
> fails(f
.cx
);
1547 masm
.loadPtr(Address(JSFrameReg
, offsetof(JSStackFrame
, scopeChain
)), pic
.objReg
);
1549 JS_ASSERT(obj
== holder
);
1550 JS_ASSERT(holder
!= scopeChain
->getGlobal());
1552 CallObjPropKind kind
;
1553 if (sprop
->getterOp() == js_GetCallArg
) {
1555 } else if (sprop
->getterOp() == js_GetCallVar
) {
1558 return disable("unhandled callobj sprop getter");
1562 if (!walkScopeChain(masm
, fails
, found
))
1565 return disable("scope chain walk terminated early");
1567 Jump finalNull
= masm
.branchTestPtr(Assembler::Zero
, pic
.objReg
, pic
.objReg
);
1568 masm
.loadShape(pic
.objReg
, pic
.shapeReg
);
1569 Jump finalShape
= masm
.branch32(Assembler::NotEqual
, pic
.shapeReg
, Imm32(holder
->shape()));
1571 /* Get callobj's stack frame. */
1572 masm
.loadFunctionPrivate(pic
.objReg
, pic
.shapeReg
);
1574 uint16 slot
= uint16(sprop
->shortid
);
1577 Jump escapedFrame
= masm
.branchTestPtr(Assembler::Zero
, pic
.shapeReg
, pic
.shapeReg
);
1579 /* Not-escaped case. */
1583 masm
.loadPtr(Address(pic
.shapeReg
, offsetof(JSStackFrame
, argv
)), pic
.shapeReg
);
1585 bias
= sizeof(JSStackFrame
);
1586 Address
addr(pic
.shapeReg
, bias
+ slot
* sizeof(Value
));
1587 masm
.loadPayload(addr
, pic
.objReg
);
1588 masm
.loadTypeTag(addr
, pic
.shapeReg
);
1589 skipOver
= masm
.jump();
1592 escapedFrame
.linkTo(masm
.label(), &masm
);
1595 masm
.loadPtr(Address(pic
.objReg
, offsetof(JSObject
, dslots
)), pic
.objReg
);
1597 JSFunction
*fun
= js_GetCallObjectFunction(holder
);
1600 Address
dslot(pic
.objReg
, slot
* sizeof(Value
));
1601 masm
.loadTypeTag(dslot
, pic
.shapeReg
);
1602 masm
.loadPayload(dslot
, pic
.objReg
);
1605 skipOver
.linkTo(masm
.label(), &masm
);
1606 Jump done
= masm
.jump();
1608 // All failures flow to here, so there is a common point to patch.
1609 for (Jump
*pj
= fails
.begin(); pj
!= fails
.end(); ++pj
)
1610 pj
->linkTo(masm
.label(), &masm
);
1611 finalNull
.linkTo(masm
.label(), &masm
);
1612 finalShape
.linkTo(masm
.label(), &masm
);
1613 Label failLabel
= masm
.label();
1614 Jump failJump
= masm
.jump();
1616 JSC::ExecutablePool
*ep
= getExecPool(masm
.size());
1618 js_ReportOutOfMemory(f
.cx
);
1622 // :TODO: this can OOM
1623 JSC::LinkBuffer
buffer(&masm
, ep
);
1625 if (!pic
.execPools
.append(ep
)) {
1627 js_ReportOutOfMemory(f
.cx
);
1631 buffer
.link(failJump
, pic
.slowPathStart
);
1632 buffer
.link(done
, pic
.storeBack
);
1633 CodeLocationLabel cs
= buffer
.finalizeCodeAddendum();
1634 JaegerSpew(JSpew_PICs
, "generated %s call stub at %p\n", type
, cs
.executableAddress());
1636 PICRepatchBuffer
repatcher(pic
, pic
.lastPathStart());
1637 repatcher
.relink(SCOPENAME_JUMP_OFFSET
, cs
);
1639 pic
.stubsGenerated
++;
1640 pic
.lastStubStart
= buffer
.locationOf(failLabel
);
1642 if (pic
.stubsGenerated
== MAX_PIC_STUBS
)
1643 disable("max stubs reached");
1650 JSContext
*cx
= f
.cx
;
1652 if (!js_FindProperty(cx
, ATOM_TO_JSID(atom
), &obj
, &holder
, &prop
))
1656 spew("first hit", "nop");
1662 return disable("property not found");
1663 if (!obj
->isNative() || !holder
->isNative())
1664 return disable("non-native scope object");
1666 return disable("property is on proto of a scope object");
1668 sprop
= (JSScopeProperty
*)prop
;
1669 if (!sprop
->hasDefaultGetterOrIsMethod())
1670 return disable("getter");
1671 if (!SPROP_HAS_VALID_SLOT(sprop
, holder
->scope()))
1672 return disable("invalid slot");
1674 if (obj
->getClass() == &js_CallClass
)
1675 return generateCallStub();
1677 if (!obj
->getParent())
1678 return generateGlobalStub();
1680 return disable("scope object not handled yet");
1684 class BindNameCompiler
: public PICStubCompiler
1686 JSObject
*scopeChain
;
1690 static int32
inlineJumpOffset(ic::PICInfo
&pic
) {
1691 #if defined JS_NUNBOX32
1692 return BINDNAME_INLINE_JUMP_OFFSET
;
1693 #elif defined JS_PUNBOX64
1694 return pic
.labels
.bindname
.inlineJumpOffset
;
1698 inline int32
inlineJumpOffset() {
1699 return inlineJumpOffset(pic
);
1703 BindNameCompiler(VMFrame
&f
, JSScript
*script
, JSObject
*scopeChain
, ic::PICInfo
&pic
,
1704 JSAtom
*atom
, VoidStubUInt32 stub
)
1705 : PICStubCompiler("bind", f
, script
, pic
), scopeChain(scopeChain
), atom(atom
),
1706 stub(JS_FUNC_TO_DATA_PTR(void *, stub
))
1709 bool disable(const char *reason
)
1711 return PICStubCompiler::disable(reason
, stub
);
1714 static void reset(ic::PICInfo
&pic
)
1716 PICRepatchBuffer
repatcher(pic
, pic
.fastPathStart
);
1717 repatcher
.relink(pic
.shapeGuard
+ inlineJumpOffset(pic
), pic
.slowPathStart
);
1719 RepatchBuffer
repatcher2(pic
.slowPathStart
.executableAddress(), INLINE_PATH_LENGTH
);
1720 ReturnAddressPtr
retPtr(pic
.slowPathStart
.callAtOffset(pic
.callReturn
).executableAddress());
1721 MacroAssemblerCodePtr
target(JS_FUNC_TO_DATA_PTR(void *, ic::BindName
));
1722 repatcher
.relinkCallerToTrampoline(retPtr
, target
);
1725 bool generateStub(JSObject
*obj
)
1728 js::Vector
<Jump
, 8, ContextAllocPolicy
> fails(f
.cx
);
1730 /* Guard on the shape of the scope chain. */
1731 masm
.loadPtr(Address(JSFrameReg
, offsetof(JSStackFrame
, scopeChain
)), pic
.objReg
);
1732 masm
.loadShape(pic
.objReg
, pic
.shapeReg
);
1733 Jump firstShape
= masm
.branch32(Assembler::NotEqual
, pic
.shapeReg
,
1734 Imm32(scopeChain
->shape()));
1736 /* Walk up the scope chain. */
1737 JSObject
*tobj
= scopeChain
;
1738 Address
parent(pic
.objReg
, offsetof(JSObject
, parent
));
1739 while (tobj
&& tobj
!= obj
) {
1740 if (!js_IsCacheableNonGlobalScope(tobj
))
1741 return disable("non-cacheable obj in scope chain");
1742 masm
.loadPtr(parent
, pic
.objReg
);
1743 Jump nullTest
= masm
.branchTestPtr(Assembler::Zero
, pic
.objReg
, pic
.objReg
);
1744 if (!fails
.append(nullTest
))
1746 masm
.loadShape(pic
.objReg
, pic
.shapeReg
);
1747 Jump shapeTest
= masm
.branch32(Assembler::NotEqual
, pic
.shapeReg
,
1748 Imm32(tobj
->shape()));
1749 tobj
= tobj
->getParent();
1752 return disable("indirect hit");
1754 Jump done
= masm
.jump();
1756 // All failures flow to here, so there is a common point to patch.
1757 for (Jump
*pj
= fails
.begin(); pj
!= fails
.end(); ++pj
)
1758 pj
->linkTo(masm
.label(), &masm
);
1759 firstShape
.linkTo(masm
.label(), &masm
);
1760 Label failLabel
= masm
.label();
1761 Jump failJump
= masm
.jump();
1762 DBGLABEL(dbgStubJumpOffset
);
1764 JS_ASSERT(masm
.differenceBetween(failLabel
, dbgStubJumpOffset
) == BINDNAME_STUB_JUMP_OFFSET
);
1766 JSC::ExecutablePool
*ep
= getExecPool(masm
.size());
1768 js_ReportOutOfMemory(f
.cx
);
1772 // :TODO: this can OOM
1773 JSC::LinkBuffer
buffer(&masm
, ep
);
1775 if (!pic
.execPools
.append(ep
)) {
1777 js_ReportOutOfMemory(f
.cx
);
1781 buffer
.link(failJump
, pic
.slowPathStart
);
1782 buffer
.link(done
, pic
.storeBack
);
1783 CodeLocationLabel cs
= buffer
.finalizeCodeAddendum();
1784 JaegerSpew(JSpew_PICs
, "generated %s stub at %p\n", type
, cs
.executableAddress());
1786 PICRepatchBuffer
repatcher(pic
, pic
.lastPathStart());
1787 if (!pic
.stubsGenerated
)
1788 repatcher
.relink(pic
.shapeGuard
+ inlineJumpOffset(), cs
);
1790 repatcher
.relink(BINDNAME_STUB_JUMP_OFFSET
, cs
);
1792 pic
.stubsGenerated
++;
1793 pic
.lastStubStart
= buffer
.locationOf(failLabel
);
1795 if (pic
.stubsGenerated
== MAX_PIC_STUBS
)
1796 disable("max stubs reached");
1803 JS_ASSERT(scopeChain
->getParent());
1805 JSObject
*obj
= js_FindIdentifierBase(f
.cx
, scopeChain
, ATOM_TO_JSID(atom
));
1810 spew("first hit", "nop");
1815 if (!generateStub(obj
))
1823 ic::GetProp(VMFrame
&f
, uint32 index
)
1825 JSScript
*script
= f
.fp
->script
;
1826 PICInfo
&pic
= script
->pics
[index
];
1828 JSAtom
*atom
= pic
.atom
;
1829 if (atom
== f
.cx
->runtime
->atomState
.lengthAtom
) {
1830 if (f
.regs
.sp
[-1].isString()) {
1831 GetPropCompiler
cc(f
, script
, NULL
, pic
, NULL
, stubs::Length
);
1832 if (!cc
.generateStringLengthStub()) {
1833 cc
.disable("error");
1836 JSString
*str
= f
.regs
.sp
[-1].toString();
1837 f
.regs
.sp
[-1].setInt32(str
->length());
1839 } else if (!f
.regs
.sp
[-1].isPrimitive()) {
1840 JSObject
*obj
= &f
.regs
.sp
[-1].toObject();
1841 if (obj
->isArray() || (obj
->isArguments() && !obj
->isArgsLengthOverridden())) {
1842 GetPropCompiler
cc(f
, script
, obj
, pic
, NULL
, stubs::Length
);
1843 if (obj
->isArray()) {
1844 if (!cc
.generateArrayLengthStub()) {
1845 cc
.disable("error");
1848 f
.regs
.sp
[-1].setNumber(obj
->getArrayLength());
1849 } else if (obj
->isArguments()) {
1850 if (!cc
.generateArgsLengthStub()) {
1851 cc
.disable("error");
1854 f
.regs
.sp
[-1].setInt32(int32_t(obj
->getArgsLength()));
1859 atom
= f
.cx
->runtime
->atomState
.lengthAtom
;
1862 JSObject
*obj
= ValueToObject(f
.cx
, &f
.regs
.sp
[-1]);
1866 if (pic
.shouldGenerate()) {
1867 GetPropCompiler
cc(f
, script
, obj
, pic
, atom
, stubs::GetProp
);
1869 cc
.disable("error");
1875 if (!obj
->getProperty(f
.cx
, ATOM_TO_JSID(atom
), &v
))
1881 ic::GetElem(VMFrame
&f
, uint32 picIndex
)
1883 JSScript
*script
= f
.fp
->script
;
1884 PICInfo
&pic
= script
->pics
[picIndex
];
1886 JSObject
*obj
= ValueToObject(f
.cx
, &f
.regs
.sp
[-2]);
1890 Value idval
= f
.regs
.sp
[-1];
1891 JS_ASSERT(idval
.isString());
1892 JSString
*id
= idval
.toString();
1893 if (pic
.shouldGenerate()) {
1894 GetElemCompiler
cc(f
, script
, obj
, pic
, id
, stubs::GetElem
);
1896 cc
.disable("error");
1901 JSAtom
*atom
= js_AtomizeString(f
.cx
, id
, 0);
1905 if (!obj
->getProperty(f
.cx
, ATOM_TO_JSID(atom
), &v
))
1910 static void JS_FASTCALL
1911 SetPropDumb(VMFrame
&f
, uint32 index
)
1913 JSScript
*script
= f
.fp
->script
;
1914 ic::PICInfo
&pic
= script
->pics
[index
];
1915 JS_ASSERT(pic
.kind
== ic::PICInfo::SET
);
1916 JSAtom
*atom
= pic
.atom
;
1918 JSObject
*obj
= ValueToObject(f
.cx
, &f
.regs
.sp
[-2]);
1921 Value rval
= f
.regs
.sp
[-1];
1922 if (!obj
->setProperty(f
.cx
, ATOM_TO_JSID(atom
), &f
.regs
.sp
[-1]))
1924 f
.regs
.sp
[-2] = rval
;
1927 static void JS_FASTCALL
1928 SetPropSlow(VMFrame
&f
, uint32 index
)
1930 JSScript
*script
= f
.fp
->script
;
1931 ic::PICInfo
&pic
= script
->pics
[index
];
1932 JS_ASSERT(pic
.kind
== ic::PICInfo::SET
);
1934 JSAtom
*atom
= pic
.atom
;
1935 stubs::SetName(f
, atom
);
1939 ic::SetProp(VMFrame
&f
, uint32 index
)
1941 JSObject
*obj
= ValueToObject(f
.cx
, &f
.regs
.sp
[-2]);
1945 JSScript
*script
= f
.fp
->script
;
1946 ic::PICInfo
&pic
= script
->pics
[index
];
1947 JSAtom
*atom
= pic
.atom
;
1948 JS_ASSERT(pic
.kind
== ic::PICInfo::SET
);
1951 // Important: We update the PIC before looking up the property so that the
1952 // PIC is updated only if the property already exists. The PIC doesn't try
1953 // to optimize adding new properties; that is for the slow case.
1955 // Also note, we can't use SetName for PROPINC PICs because the property
1956 // cache can't handle a GET and SET from the same scripted PC.
1959 VoidStubUInt32 stub
;
1960 switch (JSOp(*f
.regs
.pc
)) {
1976 SetPropCompiler
cc(f
, script
, obj
, pic
, atom
, stub
);
1978 cc
.disable("error");
1982 Value rval
= f
.regs
.sp
[-1];
1986 static void JS_FASTCALL
1987 CallPropSlow(VMFrame
&f
, uint32 index
)
1989 JSScript
*script
= f
.fp
->script
;
1990 ic::PICInfo
&pic
= script
->pics
[index
];
1991 stubs::CallProp(f
, pic
.atom
);
1995 ic::CallProp(VMFrame
&f
, uint32 index
)
1997 JSContext
*cx
= f
.cx
;
1998 JSFrameRegs
®s
= f
.regs
;
2000 JSScript
*script
= f
.fp
->script
;
2001 ic::PICInfo
&pic
= script
->pics
[index
];
2002 JSAtom
*origAtom
= pic
.atom
;
2008 if (lval
.isObject()) {
2011 JSProtoKey protoKey
;
2012 if (lval
.isString()) {
2013 protoKey
= JSProto_String
;
2014 } else if (lval
.isNumber()) {
2015 protoKey
= JSProto_Number
;
2016 } else if (lval
.isBoolean()) {
2017 protoKey
= JSProto_Boolean
;
2019 JS_ASSERT(lval
.isNull() || lval
.isUndefined());
2020 js_ReportIsNullOrUndefined(cx
, -1, lval
, NULL
);
2024 if (!js_GetClassPrototype(cx
, NULL
, protoKey
, &pobj
))
2026 objv
.setObject(*pobj
);
2029 JSObject
*aobj
= js_GetProtoIfDenseArray(&objv
.toObject());
2034 PropertyCacheEntry
*entry
;
2037 JS_PROPERTY_CACHE(cx
).test(cx
, regs
.pc
, aobj
, obj2
, entry
, atom
);
2039 if (entry
->vword
.isFunObj()) {
2040 rval
.setObject(entry
->vword
.toFunObj());
2041 } else if (entry
->vword
.isSlot()) {
2042 uint32 slot
= entry
->vword
.toSlot();
2043 JS_ASSERT(slot
< obj2
->scope()->freeslot
);
2044 rval
= obj2
->lockedGetSlot(slot
);
2046 JS_ASSERT(entry
->vword
.isSprop());
2047 JSScopeProperty
*sprop
= entry
->vword
.toSprop();
2048 NATIVE_GET(cx
, &objv
.toObject(), obj2
, sprop
, JSGET_NO_METHOD_BARRIER
, &rval
,
2058 * Cache miss: use the immediate atom that was loaded for us under
2059 * PropertyCache::test.
2062 id
= ATOM_TO_JSID(origAtom
);
2065 regs
.sp
[-1].setNull();
2066 if (lval
.isObject()) {
2067 if (!js_GetMethod(cx
, &objv
.toObject(), id
,
2068 JS_LIKELY(!objv
.toObject().getOps()->getProperty
)
2069 ? JSGET_CACHE_RESULT
| JSGET_NO_METHOD_BARRIER
2070 : JSGET_NO_METHOD_BARRIER
,
2077 JS_ASSERT(!objv
.toObject().getOps()->getProperty
);
2078 if (!js_GetPropertyHelper(cx
, &objv
.toObject(), id
,
2079 JSGET_CACHE_RESULT
| JSGET_NO_METHOD_BARRIER
,
2088 /* Wrap primitive lval in object clothing if necessary. */
2089 if (lval
.isPrimitive()) {
2090 /* FIXME: https://bugzilla.mozilla.org/show_bug.cgi?id=412571 */
2092 if (!IsFunctionObject(rval
, &funobj
) ||
2093 !PrimitiveThisTest(GET_FUNCTION_PRIVATE(cx
, funobj
), lval
)) {
2094 if (!js_PrimitiveToObject(cx
, ®s
.sp
[-1]))
2100 GetPropCompiler
cc(f
, script
, &objv
.toObject(), pic
, origAtom
, CallPropSlow
);
2102 if (lval
.isObject()) {
2104 cc
.disable("error");
2107 } else if (lval
.isString()) {
2108 if (!cc
.generateStringCallStub()) {
2109 cc
.disable("error");
2113 cc
.disable("non-string primitive");
2116 cc
.disable("wrapped primitive");
2119 #if JS_HAS_NO_SUCH_METHOD
2120 if (JS_UNLIKELY(rval
.isUndefined())) {
2121 regs
.sp
[-2].setString(ATOM_TO_STRING(origAtom
));
2122 if (!js_OnUnknownMethod(cx
, regs
.sp
- 2))
2128 static void JS_FASTCALL
2129 SlowName(VMFrame
&f
, uint32 index
)
2135 ic::Name(VMFrame
&f
, uint32 index
)
2137 JSScript
*script
= f
.fp
->script
;
2138 ic::PICInfo
&pic
= script
->pics
[index
];
2139 JSAtom
*atom
= pic
.atom
;
2141 ScopeNameCompiler
cc(f
, script
, f
.fp
->scopeChain
, pic
, atom
, SlowName
);
2144 cc
.disable("error");
2149 if (cc
.prop
&& (!cc
.obj
->isNative() || !cc
.holder
->isNative())) {
2150 cc
.holder
->dropProperty(f
.cx
, cc
.prop
);
2151 if (!cc
.obj
->getProperty(f
.cx
, ATOM_TO_JSID(atom
), &rval
))
2155 /* Kludge to allow (typeof foo == "undefined") tests. */
2156 cc
.disable("property not found");
2157 JSOp op2
= js_GetOpcode(f
.cx
, f
.fp
->script
, f
.regs
.pc
+ JSOP_NAME_LENGTH
);
2158 if (op2
== JSOP_TYPEOF
) {
2159 f
.regs
.sp
[0].setUndefined();
2162 ReportAtomNotDefined(f
.cx
, atom
);
2165 JSScopeProperty
*sprop
= (JSScopeProperty
*)cc
.prop
;
2166 JSObject
*normalized
= cc
.obj
;
2167 if (cc
.obj
->getClass() == &js_WithClass
&& !sprop
->hasDefaultGetter())
2168 normalized
= js_UnwrapWithObject(f
.cx
, cc
.obj
);
2169 NATIVE_GET(f
.cx
, normalized
, cc
.holder
, sprop
, JSGET_METHOD_BARRIER
, &rval
,
2171 JS_UNLOCK_OBJ(f
.cx
, cc
.holder
);
2174 f
.regs
.sp
[0] = rval
;
2177 static void JS_FASTCALL
2178 SlowBindName(VMFrame
&f
, uint32 index
)
2184 ic::BindName(VMFrame
&f
, uint32 index
)
2186 JSScript
*script
= f
.fp
->script
;
2187 ic::PICInfo
&pic
= script
->pics
[index
];
2188 JSAtom
*atom
= pic
.atom
;
2190 BindNameCompiler
cc(f
, script
, f
.fp
->scopeChain
, pic
, atom
, SlowBindName
);
2192 JSObject
*obj
= cc
.update();
2194 cc
.disable("error");
2198 f
.regs
.sp
[0].setObject(*obj
);
2202 ic::PurgePICs(JSContext
*cx
, JSScript
*script
)
2204 uint32 npics
= script
->numPICs();
2205 for (uint32 i
= 0; i
< npics
; i
++) {
2206 ic::PICInfo
&pic
= script
->pics
[i
];
2208 case ic::PICInfo::SET
:
2209 SetPropCompiler::reset(pic
);
2211 case ic::PICInfo::NAME
:
2212 ScopeNameCompiler::reset(pic
);
2214 case ic::PICInfo::BIND
:
2215 BindNameCompiler::reset(pic
);
2217 case ic::PICInfo::CALL
: /* fall-through */
2218 case ic::PICInfo::GET
:
2219 GetPropCompiler::reset(pic
);
2221 case ic::PICInfo::GETELEM
:
2222 GetElemCompiler::reset(pic
);
2225 JS_NOT_REACHED("Unhandled PIC kind");
2232 #endif /* JS_POLYIC */