Bug 1874684 - Part 29: Update spec fixme notes. r=mgaudet
[gecko.git] / js / src / jit / IonIC.cpp
blob55f3bbea6cd21c77fe988c2db5a5bc743bf2eac9
1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*-
2 * vim: set ts=8 sts=2 et sw=2 tw=80:
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 "jit/IonIC.h"
9 #include "jit/CacheIRCompiler.h"
10 #include "jit/CacheIRGenerator.h"
11 #include "jit/IonScript.h"
12 #include "jit/VMFunctions.h"
13 #include "util/DiagnosticAssertions.h"
14 #include "vm/EqualityOperations.h"
15 #include "vm/Iteration.h"
17 #include "vm/Interpreter-inl.h"
18 #include "vm/JSScript-inl.h"
20 using namespace js;
21 using namespace js::jit;
23 void IonIC::resetCodeRaw(IonScript* ionScript) {
24 codeRaw_ = fallbackAddr(ionScript);
27 uint8_t* IonIC::fallbackAddr(IonScript* ionScript) const {
28 return ionScript->method()->raw() + fallbackOffset_;
31 uint8_t* IonIC::rejoinAddr(IonScript* ionScript) const {
32 return ionScript->method()->raw() + rejoinOffset_;
35 Register IonIC::scratchRegisterForEntryJump() {
36 switch (kind_) {
37 case CacheKind::GetProp:
38 case CacheKind::GetElem:
39 return asGetPropertyIC()->output().scratchReg();
40 case CacheKind::GetPropSuper:
41 case CacheKind::GetElemSuper:
42 return asGetPropSuperIC()->output().scratchReg();
43 case CacheKind::SetProp:
44 case CacheKind::SetElem:
45 return asSetPropertyIC()->temp();
46 case CacheKind::GetName:
47 return asGetNameIC()->temp();
48 case CacheKind::BindName:
49 return asBindNameIC()->temp();
50 case CacheKind::In:
51 return asInIC()->temp();
52 case CacheKind::HasOwn:
53 return asHasOwnIC()->output();
54 case CacheKind::CheckPrivateField:
55 return asCheckPrivateFieldIC()->output();
56 case CacheKind::GetIterator:
57 return asGetIteratorIC()->temp1();
58 case CacheKind::OptimizeSpreadCall:
59 return asOptimizeSpreadCallIC()->temp();
60 case CacheKind::InstanceOf:
61 return asInstanceOfIC()->output();
62 case CacheKind::UnaryArith:
63 return asUnaryArithIC()->output().scratchReg();
64 case CacheKind::ToPropertyKey:
65 return asToPropertyKeyIC()->output().scratchReg();
66 case CacheKind::BinaryArith:
67 return asBinaryArithIC()->output().scratchReg();
68 case CacheKind::Compare:
69 return asCompareIC()->output();
70 case CacheKind::CloseIter:
71 return asCloseIterIC()->temp();
72 case CacheKind::OptimizeGetIterator:
73 return asOptimizeGetIteratorIC()->temp();
74 case CacheKind::Call:
75 case CacheKind::TypeOf:
76 case CacheKind::ToBool:
77 case CacheKind::GetIntrinsic:
78 case CacheKind::NewArray:
79 case CacheKind::NewObject:
80 MOZ_CRASH("Unsupported IC");
83 MOZ_CRASH("Invalid kind");
86 void IonIC::discardStubs(Zone* zone, IonScript* ionScript) {
87 if (firstStub_) {
88 // We are removing edges from IonIC to gcthings. Perform a write barrier to
89 // let the GC know about those edges.
90 PreWriteBarrier(zone, ionScript);
93 #ifdef JS_CRASH_DIAGNOSTICS
94 IonICStub* stub = firstStub_;
95 while (stub) {
96 IonICStub* next = stub->next();
97 stub->poison();
98 stub = next;
100 #endif
102 firstStub_ = nullptr;
103 resetCodeRaw(ionScript);
104 state_.trackUnlinkedAllStubs();
107 void IonIC::reset(Zone* zone, IonScript* ionScript) {
108 discardStubs(zone, ionScript);
109 state_.reset();
112 void IonIC::trace(JSTracer* trc, IonScript* ionScript) {
113 if (script_) {
114 TraceManuallyBarrieredEdge(trc, &script_, "IonIC::script_");
117 uint8_t* nextCodeRaw = codeRaw_;
118 for (IonICStub* stub = firstStub_; stub; stub = stub->next()) {
119 JitCode* code = JitCode::FromExecutable(nextCodeRaw);
120 TraceManuallyBarrieredEdge(trc, &code, "ion-ic-code");
122 TraceCacheIRStub(trc, stub, stub->stubInfo());
124 nextCodeRaw = stub->nextCodeRaw();
127 MOZ_ASSERT(nextCodeRaw == fallbackAddr(ionScript));
130 // This helper handles ICState updates/transitions while attaching CacheIR
131 // stubs.
132 template <typename IRGenerator, typename... Args>
133 static void TryAttachIonStub(JSContext* cx, IonIC* ic, IonScript* ionScript,
134 Args&&... args) {
135 if (ic->state().maybeTransition()) {
136 ic->discardStubs(cx->zone(), ionScript);
139 if (ic->state().canAttachStub()) {
140 RootedScript script(cx, ic->script());
141 bool attached = false;
142 IRGenerator gen(cx, script, ic->pc(), ic->state(),
143 std::forward<Args>(args)...);
144 switch (gen.tryAttachStub()) {
145 case AttachDecision::Attach:
146 ic->attachCacheIRStub(cx, gen.writerRef(), gen.cacheKind(), ionScript,
147 &attached);
148 break;
149 case AttachDecision::NoAction:
150 break;
151 case AttachDecision::TemporarilyUnoptimizable:
152 attached = true;
153 break;
154 case AttachDecision::Deferred:
155 MOZ_ASSERT_UNREACHABLE("Not expected in generic TryAttachIonStub");
156 break;
158 if (!attached) {
159 ic->state().trackNotAttached();
164 /* static */
165 bool IonGetPropertyIC::update(JSContext* cx, HandleScript outerScript,
166 IonGetPropertyIC* ic, HandleValue val,
167 HandleValue idVal, MutableHandleValue res) {
168 IonScript* ionScript = outerScript->ionScript();
170 // Optimized-arguments and other magic values must not escape to Ion ICs.
171 MOZ_ASSERT(!val.isMagic());
173 TryAttachIonStub<GetPropIRGenerator>(cx, ic, ionScript, ic->kind(), val,
174 idVal);
176 if (ic->kind() == CacheKind::GetProp) {
177 Rooted<PropertyName*> name(cx, idVal.toString()->asAtom().asPropertyName());
178 if (!GetProperty(cx, val, name, res)) {
179 return false;
181 } else {
182 MOZ_ASSERT(ic->kind() == CacheKind::GetElem);
183 if (!GetElementOperation(cx, val, idVal, res)) {
184 return false;
188 return true;
191 /* static */
192 bool IonGetPropSuperIC::update(JSContext* cx, HandleScript outerScript,
193 IonGetPropSuperIC* ic, HandleObject obj,
194 HandleValue receiver, HandleValue idVal,
195 MutableHandleValue res) {
196 IonScript* ionScript = outerScript->ionScript();
198 if (ic->state().maybeTransition()) {
199 ic->discardStubs(cx->zone(), ionScript);
202 RootedValue val(cx, ObjectValue(*obj));
204 TryAttachIonStub<GetPropIRGenerator>(cx, ic, ionScript, ic->kind(), val,
205 idVal);
207 if (ic->kind() == CacheKind::GetPropSuper) {
208 Rooted<PropertyName*> name(cx, idVal.toString()->asAtom().asPropertyName());
209 if (!GetProperty(cx, obj, receiver, name, res)) {
210 return false;
212 } else {
213 MOZ_ASSERT(ic->kind() == CacheKind::GetElemSuper);
215 JSOp op = JSOp(*ic->pc());
216 MOZ_ASSERT(op == JSOp::GetElemSuper);
218 if (!GetObjectElementOperation(cx, op, obj, receiver, idVal, res)) {
219 return false;
223 return true;
226 /* static */
227 bool IonSetPropertyIC::update(JSContext* cx, HandleScript outerScript,
228 IonSetPropertyIC* ic, HandleObject obj,
229 HandleValue idVal, HandleValue rhs) {
230 using DeferType = SetPropIRGenerator::DeferType;
232 Rooted<Shape*> oldShape(cx);
233 IonScript* ionScript = outerScript->ionScript();
235 bool attached = false;
236 DeferType deferType = DeferType::None;
238 if (ic->state().maybeTransition()) {
239 ic->discardStubs(cx->zone(), ionScript);
242 if (ic->state().canAttachStub()) {
243 oldShape = obj->shape();
245 RootedValue objv(cx, ObjectValue(*obj));
246 RootedScript script(cx, ic->script());
247 jsbytecode* pc = ic->pc();
249 SetPropIRGenerator gen(cx, script, pc, ic->kind(), ic->state(), objv, idVal,
250 rhs);
251 switch (gen.tryAttachStub()) {
252 case AttachDecision::Attach:
253 ic->attachCacheIRStub(cx, gen.writerRef(), gen.cacheKind(), ionScript,
254 &attached);
255 break;
256 case AttachDecision::NoAction:
257 break;
258 case AttachDecision::TemporarilyUnoptimizable:
259 attached = true;
260 break;
261 case AttachDecision::Deferred:
262 deferType = gen.deferType();
263 MOZ_ASSERT(deferType != DeferType::None);
264 break;
268 jsbytecode* pc = ic->pc();
269 if (ic->kind() == CacheKind::SetElem) {
270 if (JSOp(*pc) == JSOp::InitElemInc) {
271 if (!InitElemIncOperation(cx, obj.as<ArrayObject>(), idVal.toInt32(),
272 rhs)) {
273 return false;
275 } else if (IsPropertyInitOp(JSOp(*pc))) {
276 if (!InitElemOperation(cx, pc, obj, idVal, rhs)) {
277 return false;
279 } else {
280 MOZ_ASSERT(IsPropertySetOp(JSOp(*pc)));
281 if (!SetObjectElement(cx, obj, idVal, rhs, ic->strict())) {
282 return false;
285 } else {
286 MOZ_ASSERT(ic->kind() == CacheKind::SetProp);
288 if (JSOp(*pc) == JSOp::InitGLexical) {
289 RootedScript script(cx, ic->script());
290 MOZ_ASSERT(!script->hasNonSyntacticScope());
291 InitGlobalLexicalOperation(cx, &cx->global()->lexicalEnvironment(),
292 script, pc, rhs);
293 } else if (IsPropertyInitOp(JSOp(*pc))) {
294 Rooted<PropertyName*> name(cx,
295 idVal.toString()->asAtom().asPropertyName());
296 if (!InitPropertyOperation(cx, pc, obj, name, rhs)) {
297 return false;
299 } else {
300 MOZ_ASSERT(IsPropertySetOp(JSOp(*pc)));
301 Rooted<PropertyName*> name(cx,
302 idVal.toString()->asAtom().asPropertyName());
303 if (!SetProperty(cx, obj, name, rhs, ic->strict(), pc)) {
304 return false;
309 if (attached) {
310 return true;
313 // The SetProperty call might have entered this IC recursively, so try
314 // to transition.
315 if (ic->state().maybeTransition()) {
316 ic->discardStubs(cx->zone(), ionScript);
319 bool canAttachStub = ic->state().canAttachStub();
320 if (deferType != DeferType::None && canAttachStub) {
321 RootedValue objv(cx, ObjectValue(*obj));
322 RootedScript script(cx, ic->script());
323 jsbytecode* pc = ic->pc();
324 SetPropIRGenerator gen(cx, script, pc, ic->kind(), ic->state(), objv, idVal,
325 rhs);
326 MOZ_ASSERT(deferType == DeferType::AddSlot);
327 AttachDecision decision = gen.tryAttachAddSlotStub(oldShape);
329 switch (decision) {
330 case AttachDecision::Attach:
331 ic->attachCacheIRStub(cx, gen.writerRef(), gen.cacheKind(), ionScript,
332 &attached);
333 break;
334 case AttachDecision::NoAction:
335 gen.trackAttached(IRGenerator::NotAttached);
336 break;
337 case AttachDecision::TemporarilyUnoptimizable:
338 case AttachDecision::Deferred:
339 MOZ_ASSERT_UNREACHABLE("Invalid attach result");
340 break;
343 if (!attached && canAttachStub) {
344 ic->state().trackNotAttached();
347 return true;
350 /* static */
351 bool IonGetNameIC::update(JSContext* cx, HandleScript outerScript,
352 IonGetNameIC* ic, HandleObject envChain,
353 MutableHandleValue res) {
354 IonScript* ionScript = outerScript->ionScript();
355 jsbytecode* pc = ic->pc();
356 Rooted<PropertyName*> name(cx, ic->script()->getName(pc));
358 TryAttachIonStub<GetNameIRGenerator>(cx, ic, ionScript, envChain, name);
360 RootedObject obj(cx);
361 RootedObject holder(cx);
362 PropertyResult prop;
363 if (!LookupName(cx, name, envChain, &obj, &holder, &prop)) {
364 return false;
367 if (JSOp(*GetNextPc(pc)) == JSOp::Typeof) {
368 return FetchName<GetNameMode::TypeOf>(cx, obj, holder, name, prop, res);
371 return FetchName<GetNameMode::Normal>(cx, obj, holder, name, prop, res);
374 /* static */
375 JSObject* IonBindNameIC::update(JSContext* cx, HandleScript outerScript,
376 IonBindNameIC* ic, HandleObject envChain) {
377 IonScript* ionScript = outerScript->ionScript();
378 jsbytecode* pc = ic->pc();
379 Rooted<PropertyName*> name(cx, ic->script()->getName(pc));
381 TryAttachIonStub<BindNameIRGenerator>(cx, ic, ionScript, envChain, name);
383 RootedObject holder(cx);
384 if (!LookupNameUnqualified(cx, name, envChain, &holder)) {
385 return nullptr;
388 return holder;
391 /* static */
392 JSObject* IonGetIteratorIC::update(JSContext* cx, HandleScript outerScript,
393 IonGetIteratorIC* ic, HandleValue value) {
394 IonScript* ionScript = outerScript->ionScript();
396 TryAttachIonStub<GetIteratorIRGenerator>(cx, ic, ionScript, value);
398 PropertyIteratorObject* iterObj = ValueToIterator(cx, value);
399 if (!iterObj) {
400 return nullptr;
403 return iterObj;
406 /* static */
407 bool IonOptimizeSpreadCallIC::update(JSContext* cx, HandleScript outerScript,
408 IonOptimizeSpreadCallIC* ic,
409 HandleValue value,
410 MutableHandleValue result) {
411 IonScript* ionScript = outerScript->ionScript();
413 TryAttachIonStub<OptimizeSpreadCallIRGenerator>(cx, ic, ionScript, value);
415 return OptimizeSpreadCall(cx, value, result);
418 /* static */
419 bool IonHasOwnIC::update(JSContext* cx, HandleScript outerScript,
420 IonHasOwnIC* ic, HandleValue val, HandleValue idVal,
421 int32_t* res) {
422 IonScript* ionScript = outerScript->ionScript();
424 TryAttachIonStub<HasPropIRGenerator>(cx, ic, ionScript, CacheKind::HasOwn,
425 idVal, val);
427 bool found;
428 if (!HasOwnProperty(cx, val, idVal, &found)) {
429 return false;
432 *res = found;
433 return true;
436 /* static */
437 bool IonCheckPrivateFieldIC::update(JSContext* cx, HandleScript outerScript,
438 IonCheckPrivateFieldIC* ic, HandleValue val,
439 HandleValue idVal, bool* res) {
440 IonScript* ionScript = outerScript->ionScript();
441 jsbytecode* pc = ic->pc();
443 TryAttachIonStub<CheckPrivateFieldIRGenerator>(
444 cx, ic, ionScript, CacheKind::CheckPrivateField, idVal, val);
446 return CheckPrivateFieldOperation(cx, pc, val, idVal, res);
449 /* static */
450 bool IonInIC::update(JSContext* cx, HandleScript outerScript, IonInIC* ic,
451 HandleValue key, HandleObject obj, bool* res) {
452 IonScript* ionScript = outerScript->ionScript();
453 RootedValue objV(cx, ObjectValue(*obj));
455 TryAttachIonStub<HasPropIRGenerator>(cx, ic, ionScript, CacheKind::In, key,
456 objV);
458 return OperatorIn(cx, key, obj, res);
460 /* static */
461 bool IonInstanceOfIC::update(JSContext* cx, HandleScript outerScript,
462 IonInstanceOfIC* ic, HandleValue lhs,
463 HandleObject rhs, bool* res) {
464 IonScript* ionScript = outerScript->ionScript();
466 TryAttachIonStub<InstanceOfIRGenerator>(cx, ic, ionScript, lhs, rhs);
468 return InstanceofOperator(cx, rhs, lhs, res);
471 /* static */
472 bool IonToPropertyKeyIC::update(JSContext* cx, HandleScript outerScript,
473 IonToPropertyKeyIC* ic, HandleValue val,
474 MutableHandleValue res) {
475 IonScript* ionScript = outerScript->ionScript();
477 TryAttachIonStub<ToPropertyKeyIRGenerator>(cx, ic, ionScript, val);
479 return ToPropertyKeyOperation(cx, val, res);
482 /* static */
483 bool IonCloseIterIC::update(JSContext* cx, HandleScript outerScript,
484 IonCloseIterIC* ic, HandleObject iter) {
485 IonScript* ionScript = outerScript->ionScript();
486 CompletionKind kind = ic->completionKind();
488 TryAttachIonStub<CloseIterIRGenerator>(cx, ic, ionScript, iter, kind);
490 return CloseIterOperation(cx, iter, kind);
493 /* static */
494 bool IonOptimizeGetIteratorIC::update(JSContext* cx, HandleScript outerScript,
495 IonOptimizeGetIteratorIC* ic,
496 HandleValue value, bool* result) {
497 IonScript* ionScript = outerScript->ionScript();
499 TryAttachIonStub<OptimizeGetIteratorIRGenerator>(cx, ic, ionScript, value);
501 return OptimizeGetIterator(cx, value, result);
504 /* static */
505 bool IonUnaryArithIC::update(JSContext* cx, HandleScript outerScript,
506 IonUnaryArithIC* ic, HandleValue val,
507 MutableHandleValue res) {
508 IonScript* ionScript = outerScript->ionScript();
509 RootedScript script(cx, ic->script());
510 jsbytecode* pc = ic->pc();
511 JSOp op = JSOp(*pc);
513 switch (op) {
514 case JSOp::BitNot: {
515 res.set(val);
516 if (!BitNot(cx, res, res)) {
517 return false;
519 break;
521 case JSOp::Pos: {
522 res.set(val);
523 if (!ToNumber(cx, res)) {
524 return false;
526 break;
528 case JSOp::Neg: {
529 res.set(val);
530 if (!NegOperation(cx, res, res)) {
531 return false;
533 break;
535 case JSOp::Inc: {
536 if (!IncOperation(cx, val, res)) {
537 return false;
539 break;
541 case JSOp::Dec: {
542 if (!DecOperation(cx, val, res)) {
543 return false;
545 break;
547 case JSOp::ToNumeric: {
548 res.set(val);
549 if (!ToNumeric(cx, res)) {
550 return false;
552 break;
554 default:
555 MOZ_CRASH("Unexpected op");
557 MOZ_ASSERT(res.isNumeric());
559 TryAttachIonStub<UnaryArithIRGenerator>(cx, ic, ionScript, op, val, res);
561 return true;
564 /* static */
565 bool IonBinaryArithIC::update(JSContext* cx, HandleScript outerScript,
566 IonBinaryArithIC* ic, HandleValue lhs,
567 HandleValue rhs, MutableHandleValue ret) {
568 IonScript* ionScript = outerScript->ionScript();
569 RootedScript script(cx, ic->script());
570 jsbytecode* pc = ic->pc();
571 JSOp op = JSOp(*pc);
573 // Don't pass lhs/rhs directly, we need the original values when
574 // generating stubs.
575 RootedValue lhsCopy(cx, lhs);
576 RootedValue rhsCopy(cx, rhs);
578 // Perform the compare operation.
579 switch (op) {
580 case JSOp::Add:
581 // Do an add.
582 if (!AddValues(cx, &lhsCopy, &rhsCopy, ret)) {
583 return false;
585 break;
586 case JSOp::Sub:
587 if (!SubValues(cx, &lhsCopy, &rhsCopy, ret)) {
588 return false;
590 break;
591 case JSOp::Mul:
592 if (!MulValues(cx, &lhsCopy, &rhsCopy, ret)) {
593 return false;
595 break;
596 case JSOp::Div:
597 if (!DivValues(cx, &lhsCopy, &rhsCopy, ret)) {
598 return false;
600 break;
601 case JSOp::Mod:
602 if (!ModValues(cx, &lhsCopy, &rhsCopy, ret)) {
603 return false;
605 break;
606 case JSOp::Pow:
607 if (!PowValues(cx, &lhsCopy, &rhsCopy, ret)) {
608 return false;
610 break;
611 case JSOp::BitOr: {
612 if (!BitOr(cx, &lhsCopy, &rhsCopy, ret)) {
613 return false;
615 break;
617 case JSOp::BitXor: {
618 if (!BitXor(cx, &lhsCopy, &rhsCopy, ret)) {
619 return false;
621 break;
623 case JSOp::BitAnd: {
624 if (!BitAnd(cx, &lhsCopy, &rhsCopy, ret)) {
625 return false;
627 break;
629 case JSOp::Lsh: {
630 if (!BitLsh(cx, &lhsCopy, &rhsCopy, ret)) {
631 return false;
633 break;
635 case JSOp::Rsh: {
636 if (!BitRsh(cx, &lhsCopy, &rhsCopy, ret)) {
637 return false;
639 break;
641 case JSOp::Ursh: {
642 if (!UrshValues(cx, &lhsCopy, &rhsCopy, ret)) {
643 return false;
645 break;
647 default:
648 MOZ_CRASH("Unhandled binary arith op");
651 TryAttachIonStub<BinaryArithIRGenerator>(cx, ic, ionScript, op, lhs, rhs,
652 ret);
654 return true;
657 /* static */
658 bool IonCompareIC::update(JSContext* cx, HandleScript outerScript,
659 IonCompareIC* ic, HandleValue lhs, HandleValue rhs,
660 bool* res) {
661 IonScript* ionScript = outerScript->ionScript();
662 RootedScript script(cx, ic->script());
663 jsbytecode* pc = ic->pc();
664 JSOp op = JSOp(*pc);
666 // Don't pass lhs/rhs directly, we need the original values when
667 // generating stubs.
668 RootedValue lhsCopy(cx, lhs);
669 RootedValue rhsCopy(cx, rhs);
671 // Perform the compare operation.
672 switch (op) {
673 case JSOp::Lt:
674 if (!LessThan(cx, &lhsCopy, &rhsCopy, res)) {
675 return false;
677 break;
678 case JSOp::Le:
679 if (!LessThanOrEqual(cx, &lhsCopy, &rhsCopy, res)) {
680 return false;
682 break;
683 case JSOp::Gt:
684 if (!GreaterThan(cx, &lhsCopy, &rhsCopy, res)) {
685 return false;
687 break;
688 case JSOp::Ge:
689 if (!GreaterThanOrEqual(cx, &lhsCopy, &rhsCopy, res)) {
690 return false;
692 break;
693 case JSOp::Eq:
694 if (!js::LooselyEqual(cx, lhsCopy, rhsCopy, res)) {
695 return false;
697 break;
698 case JSOp::Ne:
699 if (!js::LooselyEqual(cx, lhsCopy, rhsCopy, res)) {
700 return false;
702 *res = !*res;
703 break;
704 case JSOp::StrictEq:
705 if (!js::StrictlyEqual(cx, lhsCopy, rhsCopy, res)) {
706 return false;
708 break;
709 case JSOp::StrictNe:
710 if (!js::StrictlyEqual(cx, lhsCopy, rhsCopy, res)) {
711 return false;
713 *res = !*res;
714 break;
715 default:
716 MOZ_ASSERT_UNREACHABLE("Unhandled ion compare op");
717 return false;
720 TryAttachIonStub<CompareIRGenerator>(cx, ic, ionScript, op, lhs, rhs);
722 return true;
725 uint8_t* IonICStub::stubDataStart() {
726 return reinterpret_cast<uint8_t*>(this) + stubInfo_->stubDataOffset();
729 void IonIC::attachStub(IonICStub* newStub, JitCode* code) {
730 MOZ_ASSERT(newStub);
731 MOZ_ASSERT(code);
733 if (firstStub_) {
734 newStub->setNext(firstStub_, codeRaw_);
736 firstStub_ = newStub;
737 codeRaw_ = code->raw();
739 state_.trackAttached();