зеркало из https://github.com/mozilla/gecko-dev.git
Bug 839727 - Minor cleanups around polymorphic inlining. r=djvj
This commit is contained in:
Родитель
15c33862f4
Коммит
6da51915ea
|
@ -370,30 +370,34 @@ CodeGenerator::visitPolyInlineDispatch(LPolyInlineDispatch *lir)
|
|||
|
||||
InlinePropertyTable *inlinePropTable = mir->inlinePropertyTable();
|
||||
if (inlinePropTable) {
|
||||
// Temporary register is only assigned in the TypeObject case.
|
||||
Register tempReg = ToRegister(lir->temp());
|
||||
|
||||
masm.loadPtr(Address(inputReg, JSObject::offsetOfType()), tempReg);
|
||||
|
||||
// Detect functions by TypeObject.
|
||||
for (size_t i = 0; i < inlinePropTable->numEntries(); i++) {
|
||||
types::TypeObject *typeObj = inlinePropTable->getTypeObject(i);
|
||||
JSFunction *func = inlinePropTable->getFunction(i);
|
||||
LBlock *target = mir->getFunctionBlock(func)->lir();
|
||||
masm.branchPtr(Assembler::Equal, tempReg, ImmGCPtr(typeObj), target->label());
|
||||
}
|
||||
// Jump to fallback block
|
||||
|
||||
// Unknown function: jump to fallback block.
|
||||
LBlock *fallback = mir->fallbackPrepBlock()->lir();
|
||||
masm.jump(fallback->label());
|
||||
} else {
|
||||
for (size_t i = 0; i < mir->numCallees(); i++) {
|
||||
JSFunction *func = mir->getFunction(i);
|
||||
LBlock *target = mir->getFunctionBlock(i)->lir();
|
||||
if (i < mir->numCallees() - 1) {
|
||||
masm.branchPtr(Assembler::Equal, inputReg, ImmGCPtr(func), target->label());
|
||||
} else {
|
||||
// Don't generate guard for final case
|
||||
masm.jump(target->label());
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
// Compare function pointers directly.
|
||||
for (size_t i = 0; i < mir->numCallees() - 1; i++) {
|
||||
JSFunction *func = mir->getFunction(i);
|
||||
LBlock *target = mir->getFunctionBlock(i)->lir();
|
||||
masm.branchPtr(Assembler::Equal, inputReg, ImmGCPtr(func), target->label());
|
||||
}
|
||||
|
||||
// There's no fallback case, so a final guard isn't necessary.
|
||||
LBlock *target = mir->getFunctionBlock(mir->numCallees() - 1)->lir();
|
||||
masm.jump(target->label());
|
||||
return true;
|
||||
}
|
||||
|
||||
|
|
|
@ -2950,49 +2950,56 @@ IonBuilder::inlineScriptedCall(HandleFunction target, CallInfo &callInfo)
|
|||
return true;
|
||||
}
|
||||
|
||||
MDefinition *
|
||||
IonBuilder::patchInlinedReturn(CallInfo &callInfo, MBasicBlock *exit, MBasicBlock *bottom)
|
||||
{
|
||||
// Replaces the MReturn in the exit block with an MGoto.
|
||||
MDefinition *rdef = exit->lastIns()->toReturn()->input();
|
||||
exit->discardLastIns();
|
||||
|
||||
// Constructors must be patched by the caller to always return an object.
|
||||
if (callInfo.constructing()) {
|
||||
if (rdef->type() == MIRType_Value) {
|
||||
// Unknown return: dynamically detect objects.
|
||||
MReturnFromCtor *filter = MReturnFromCtor::New(rdef, callInfo.thisArg());
|
||||
exit->add(filter);
|
||||
rdef = filter;
|
||||
} else if (rdef->type() != MIRType_Object) {
|
||||
// Known non-object return: force |this|.
|
||||
rdef = callInfo.thisArg();
|
||||
}
|
||||
}
|
||||
|
||||
MGoto *replacement = MGoto::New(bottom);
|
||||
exit->end(replacement);
|
||||
if (!bottom->addPredecessorWithoutPhis(exit))
|
||||
return NULL;
|
||||
|
||||
return rdef;
|
||||
}
|
||||
|
||||
MDefinition *
|
||||
IonBuilder::patchInlinedReturns(CallInfo &callInfo, MIRGraphExits &exits, MBasicBlock *bottom)
|
||||
{
|
||||
// Replace all MReturns with MGotos, and remember the MDefinition that
|
||||
// would have been returned.
|
||||
// Replaces MReturns with MGotos, returning the MDefinition
|
||||
// representing the return value, or NULL.
|
||||
JS_ASSERT(exits.length() > 0);
|
||||
|
||||
// In the case of a single return, no phi is necessary.
|
||||
MPhi *phi = NULL;
|
||||
if (exits.length() > 1) {
|
||||
phi = MPhi::New(bottom->stackDepth());
|
||||
phi->initLength(exits.length());
|
||||
bottom->addPhi(phi);
|
||||
}
|
||||
if (exits.length() == 1)
|
||||
return patchInlinedReturn(callInfo, exits[0], bottom);
|
||||
|
||||
// Accumulate multiple returns with a phi.
|
||||
MPhi *phi = MPhi::New(bottom->stackDepth());
|
||||
phi->initLength(exits.length());
|
||||
|
||||
for (size_t i = 0; i < exits.length(); i++) {
|
||||
MBasicBlock *exitBlock = exits[i];
|
||||
|
||||
MDefinition *rval = exitBlock->lastIns()->toReturn()->getOperand(0);
|
||||
exitBlock->discardLastIns();
|
||||
|
||||
// Inlined constructors return |this| unless overridden by another Object.
|
||||
if (callInfo.constructing()) {
|
||||
if (rval->type() == MIRType_Value) {
|
||||
MReturnFromCtor *filter = MReturnFromCtor::New(rval, callInfo.thisArg());
|
||||
exitBlock->add(filter);
|
||||
rval = filter;
|
||||
} else if (rval->type() != MIRType_Object) {
|
||||
rval = callInfo.thisArg();
|
||||
}
|
||||
}
|
||||
|
||||
MGoto *replacement = MGoto::New(bottom);
|
||||
exitBlock->end(replacement);
|
||||
if (!bottom->addPredecessorWithoutPhis(exitBlock))
|
||||
MDefinition *rdef = patchInlinedReturn(callInfo, exits[i], bottom);
|
||||
if (!rdef)
|
||||
return NULL;
|
||||
|
||||
if (exits.length() == 1)
|
||||
return rval;
|
||||
|
||||
phi->setOperand(i, rval);
|
||||
phi->setOperand(i, rdef);
|
||||
}
|
||||
|
||||
bottom->addPhi(phi);
|
||||
return phi;
|
||||
}
|
||||
|
||||
|
@ -3060,33 +3067,13 @@ IonBuilder::jsop_call_inline(HandleFunction callee, CallInfo &callInfo, MBasicBl
|
|||
return false;
|
||||
}
|
||||
|
||||
// Replace all MReturns with MGotos, and accumulate them.
|
||||
MIRGraphExits &exits = *inlineBuilder.graph().exitAccumulator();
|
||||
|
||||
// Replace all MReturns with MGotos, and remember the MDefinition that
|
||||
// would have been returned.
|
||||
for (MBasicBlock **it = exits.begin(), **end = exits.end(); it != end; ++it) {
|
||||
MBasicBlock *exitBlock = *it;
|
||||
|
||||
MDefinition *rval = exitBlock->lastIns()->toReturn()->getOperand(0);
|
||||
exitBlock->discardLastIns();
|
||||
|
||||
// Inlined constructors return |this| unless overridden by another Object.
|
||||
if (callInfo.constructing()) {
|
||||
if (rval->type() == MIRType_Value) {
|
||||
MReturnFromCtor *filter = MReturnFromCtor::New(rval, callInfo.thisArg());
|
||||
exitBlock->add(filter);
|
||||
rval = filter;
|
||||
} else if (rval->type() != MIRType_Object) {
|
||||
rval = callInfo.thisArg();
|
||||
}
|
||||
}
|
||||
|
||||
if (!retvalDefns.append(rval))
|
||||
MDefinition *rdef = patchInlinedReturn(callInfo, *it, bottom);
|
||||
if (!rdef)
|
||||
return false;
|
||||
|
||||
MGoto *replacement = MGoto::New(bottom);
|
||||
exitBlock->end(replacement);
|
||||
if (!bottom->addPredecessorWithoutPhis(exitBlock))
|
||||
if (!retvalDefns.append(rdef))
|
||||
return false;
|
||||
}
|
||||
JS_ASSERT(!retvalDefns.empty());
|
||||
|
@ -3094,7 +3081,7 @@ IonBuilder::jsop_call_inline(HandleFunction callee, CallInfo &callInfo, MBasicBl
|
|||
}
|
||||
|
||||
bool
|
||||
IonBuilder::makeInliningDecision(AutoObjectVector &targets, uint32_t argc)
|
||||
IonBuilder::makeInliningDecision(AutoObjectVector &targets)
|
||||
{
|
||||
AssertCanGC();
|
||||
|
||||
|
@ -3169,85 +3156,70 @@ IonBuilder::makeInliningDecision(AutoObjectVector &targets, uint32_t argc)
|
|||
}
|
||||
|
||||
static bool
|
||||
ValidateInlineableGetPropertyCache(MGetPropertyCache *getPropCache, MDefinition *thisDefn,
|
||||
size_t maxUseCount)
|
||||
CanInlineGetPropertyCache(MGetPropertyCache *cache, MDefinition *thisDef)
|
||||
{
|
||||
JS_ASSERT(getPropCache->object()->type() == MIRType_Object);
|
||||
|
||||
if (getPropCache->useCount() > maxUseCount)
|
||||
JS_ASSERT(cache->object()->type() == MIRType_Object);
|
||||
if (cache->object() != thisDef)
|
||||
return false;
|
||||
|
||||
// Ensure that the input to the GetPropertyCache is the thisDefn for this function.
|
||||
if (getPropCache->object() != thisDefn)
|
||||
InlinePropertyTable *table = cache->inlinePropertyTable();
|
||||
if (!table)
|
||||
return false;
|
||||
|
||||
InlinePropertyTable *propTable = getPropCache->inlinePropertyTable();
|
||||
if (!propTable || propTable->numEntries() == 0)
|
||||
if (table->numEntries() == 0)
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
MGetPropertyCache *
|
||||
IonBuilder::checkInlineableGetPropertyCache(uint32_t argc)
|
||||
IonBuilder::getInlineableGetPropertyCache(CallInfo &callInfo)
|
||||
{
|
||||
// Stack state:
|
||||
// ..., Func, This, Arg1, ..., ArgC
|
||||
// Note: PassArgs have already been eliminated.
|
||||
|
||||
JS_ASSERT(current->stackDepth() >= argc + 2);
|
||||
|
||||
// Ensure that This is object-typed.
|
||||
int thisDefnDepth = -((int) argc + 1);
|
||||
MDefinition *thisDefn = current->peek(thisDefnDepth);
|
||||
if (thisDefn->type() != MIRType_Object)
|
||||
if (callInfo.constructing())
|
||||
return NULL;
|
||||
|
||||
// Ensure that Func is defined by a GetPropertyCache that is then TypeBarriered and then
|
||||
// infallibly Unboxed to an object.
|
||||
int funcDefnDepth = -((int) argc + 2);
|
||||
MDefinition *funcDefn = current->peek(funcDefnDepth);
|
||||
if (funcDefn->type() != MIRType_Object)
|
||||
MDefinition *thisDef = callInfo.thisArg();
|
||||
if (thisDef->type() != MIRType_Object)
|
||||
return NULL;
|
||||
|
||||
// If it's a constant, then ignore it since there's nothing to optimize: any potential
|
||||
// GetProp that led to the funcDefn has already been optimized away.
|
||||
if (funcDefn->isConstant())
|
||||
MDefinition *funcDef = callInfo.fun();
|
||||
if (funcDef->type() != MIRType_Object)
|
||||
return NULL;
|
||||
|
||||
// Match patterns:
|
||||
// 1. MGetPropertyCache
|
||||
// 2. MUnbox[MIRType_Object, Infallible] <- MTypeBarrier <- MGetPropertyCache
|
||||
|
||||
// If it's a GetPropertyCache, return it immediately, but make sure its not used anywhere
|
||||
// else (because otherwise we wouldn't be able to move it).
|
||||
if (funcDefn->isGetPropertyCache()) {
|
||||
MGetPropertyCache *getPropCache = funcDefn->toGetPropertyCache();
|
||||
if (!ValidateInlineableGetPropertyCache(getPropCache, thisDefn, 0))
|
||||
// MGetPropertyCache with no uses may be optimized away.
|
||||
if (funcDef->isGetPropertyCache()) {
|
||||
MGetPropertyCache *cache = funcDef->toGetPropertyCache();
|
||||
if (cache->useCount() > 0)
|
||||
return NULL;
|
||||
|
||||
return getPropCache;
|
||||
if (!CanInlineGetPropertyCache(cache, thisDef))
|
||||
return NULL;
|
||||
return cache;
|
||||
}
|
||||
|
||||
// Check for MUnbox[MIRType_Object, Infallible] <- MTypeBarrier <- MGetPropertyCache
|
||||
if (!funcDefn->isUnbox() || funcDefn->toUnbox()->useCount() > 0)
|
||||
return NULL;
|
||||
// Optimize away the following common pattern:
|
||||
// MUnbox[MIRType_Object, Infallible] <- MTypeBarrier <- MGetPropertyCache
|
||||
if (funcDef->isUnbox()) {
|
||||
MUnbox *unbox = funcDef->toUnbox();
|
||||
if (unbox->mode() != MUnbox::Infallible)
|
||||
return NULL;
|
||||
if (unbox->useCount() > 0)
|
||||
return NULL;
|
||||
if (!unbox->input()->isTypeBarrier())
|
||||
return NULL;
|
||||
|
||||
MUnbox *unbox = current->peek(funcDefnDepth)->toUnbox();
|
||||
if (unbox->mode() != MUnbox::Infallible || !unbox->input()->isTypeBarrier())
|
||||
return NULL;
|
||||
MTypeBarrier *barrier = unbox->input()->toTypeBarrier();
|
||||
if (barrier->useCount() != 1)
|
||||
return NULL;
|
||||
if (!barrier->input()->isGetPropertyCache())
|
||||
return NULL;
|
||||
|
||||
MTypeBarrier *typeBarrier = unbox->input()->toTypeBarrier();
|
||||
if (typeBarrier->useCount() != 1 || !typeBarrier->input()->isGetPropertyCache())
|
||||
return NULL;
|
||||
MGetPropertyCache *cache = barrier->input()->toGetPropertyCache();
|
||||
if (cache->useCount() > 1)
|
||||
return NULL;
|
||||
if (!CanInlineGetPropertyCache(cache, thisDef))
|
||||
return NULL;
|
||||
return cache;
|
||||
}
|
||||
|
||||
MGetPropertyCache *getPropCache = typeBarrier->input()->toGetPropertyCache();
|
||||
JS_ASSERT(getPropCache->object()->type() == MIRType_Object);
|
||||
|
||||
if (!ValidateInlineableGetPropertyCache(getPropCache, thisDefn, 1))
|
||||
return NULL;
|
||||
|
||||
return getPropCache;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
MPolyInlineDispatch *
|
||||
|
@ -3378,9 +3350,7 @@ IonBuilder::inlineScriptedCalls(AutoObjectVector &targets, AutoObjectVector &ori
|
|||
callInfo.unwrapArgs();
|
||||
callInfo.pushFormals(current);
|
||||
|
||||
#ifdef DEBUG
|
||||
uint32_t origStackDepth = current->stackDepth();
|
||||
#endif
|
||||
DebugOnly<uint32_t> origStackDepth = current->stackDepth();
|
||||
|
||||
IonSpew(IonSpew_Inlining, "Inlining %d targets", (int) targets.length());
|
||||
JS_ASSERT(targets.length() > 0);
|
||||
|
@ -3388,29 +3358,14 @@ IonBuilder::inlineScriptedCalls(AutoObjectVector &targets, AutoObjectVector &ori
|
|||
// |top| jumps into the callee subgraph -- save it for later use.
|
||||
MBasicBlock *top = current;
|
||||
|
||||
// Check if the input is a GetPropertyCache that can be eliminated via guards on
|
||||
// the |this| object's typeguards.
|
||||
MGetPropertyCache *getPropCache = NULL;
|
||||
if (!callInfo.constructing()) {
|
||||
getPropCache = checkInlineableGetPropertyCache(callInfo.argc());
|
||||
if (getPropCache) {
|
||||
InlinePropertyTable *inlinePropTable = getPropCache->inlinePropertyTable();
|
||||
// checkInlineableGetPropertyCache should have verified this.
|
||||
JS_ASSERT(inlinePropTable != NULL);
|
||||
|
||||
int numCases = inlinePropTable->numEntries();
|
||||
IonSpew(IonSpew_Inlining, "Got inlineable property cache with %d cases", numCases);
|
||||
|
||||
inlinePropTable->trimToAndMaybePatchTargets(targets, originals);
|
||||
|
||||
// Trim the cases based on those that match the targets at this call site.
|
||||
IonSpew(IonSpew_Inlining, "%d inlineable cases left after trimming to %d targets",
|
||||
(int) inlinePropTable->numEntries(),
|
||||
(int) targets.length());
|
||||
|
||||
if (inlinePropTable->numEntries() == 0)
|
||||
getPropCache = NULL;
|
||||
}
|
||||
// Check for a function MGetPropertyCache that may be eliminated via guards
|
||||
// on the |this| object's typeguards.
|
||||
MGetPropertyCache *getPropCache = getInlineableGetPropertyCache(callInfo);
|
||||
if (getPropCache) {
|
||||
InlinePropertyTable *table = getPropCache->inlinePropertyTable();
|
||||
table->trimToAndMaybePatchTargets(targets, originals);
|
||||
if (table->numEntries() == 0)
|
||||
getPropCache = NULL;
|
||||
}
|
||||
|
||||
// Do the inline build. Return value definitions are stored in retvalDefns.
|
||||
|
@ -4020,7 +3975,7 @@ IonBuilder::jsop_funapplyarguments(uint32_t argc)
|
|||
AutoObjectVector targets(cx);
|
||||
targets.append(target);
|
||||
|
||||
if (makeInliningDecision(targets, argc))
|
||||
if (makeInliningDecision(targets))
|
||||
return inlineScriptedCall(target, callInfo);
|
||||
}
|
||||
|
||||
|
@ -4075,7 +4030,7 @@ IonBuilder::jsop_call(uint32_t argc, bool constructing)
|
|||
}
|
||||
|
||||
// Inline scriped call(s).
|
||||
if (inliningEnabled() && targets.length() > 0 && makeInliningDecision(targets, argc))
|
||||
if (inliningEnabled() && targets.length() > 0 && makeInliningDecision(targets))
|
||||
return inlineScriptedCalls(targets, originals, callInfo);
|
||||
|
||||
// No inline, just make the call.
|
||||
|
|
|
@ -439,7 +439,7 @@ class IonBuilder : public MIRGenerator
|
|||
bool inlineScriptedCalls(AutoObjectVector &targets, AutoObjectVector &originals,
|
||||
CallInfo &callInfo);
|
||||
bool inlineScriptedCall(HandleFunction target, CallInfo &callInfo);
|
||||
bool makeInliningDecision(AutoObjectVector &targets, uint32_t argc);
|
||||
bool makeInliningDecision(AutoObjectVector &targets);
|
||||
|
||||
bool anyFunctionIsCloneAtCallsite(types::StackTypeSet *funTypes);
|
||||
MDefinition *makeCallsiteClone(HandleFunction target, MDefinition *fun);
|
||||
|
@ -450,6 +450,7 @@ class IonBuilder : public MIRGenerator
|
|||
bool makeCall(HandleFunction target, CallInfo &callInfo,
|
||||
types::StackTypeSet *calleeTypes, bool cloneAtCallsite);
|
||||
|
||||
MDefinition *patchInlinedReturn(CallInfo &callInfo, MBasicBlock *exit, MBasicBlock *bottom);
|
||||
MDefinition *patchInlinedReturns(CallInfo &callInfo, MIRGraphExits &exits, MBasicBlock *bottom);
|
||||
|
||||
inline bool TestCommonPropFunc(JSContext *cx, types::StackTypeSet *types,
|
||||
|
@ -460,7 +461,7 @@ class IonBuilder : public MIRGenerator
|
|||
bool annotateGetPropertyCache(JSContext *cx, MDefinition *obj, MGetPropertyCache *getPropCache,
|
||||
types::StackTypeSet *objTypes, types::StackTypeSet *pushedTypes);
|
||||
|
||||
MGetPropertyCache *checkInlineableGetPropertyCache(uint32_t argc);
|
||||
MGetPropertyCache *getInlineableGetPropertyCache(CallInfo &callInfo);
|
||||
|
||||
MPolyInlineDispatch *
|
||||
makePolyInlineDispatch(JSContext *cx, CallInfo &callInfo,
|
||||
|
|
|
@ -1909,3 +1909,33 @@ MLoadSlot::mightAlias(MDefinition *store)
|
|||
return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
void
|
||||
InlinePropertyTable::trimToAndMaybePatchTargets(AutoObjectVector &targets,
|
||||
AutoObjectVector &originals)
|
||||
{
|
||||
IonSpew(IonSpew_Inlining, "Got inlineable property cache with %d cases",
|
||||
(int)numEntries());
|
||||
|
||||
size_t i = 0;
|
||||
while (i < numEntries()) {
|
||||
bool foundFunc = false;
|
||||
// Compare using originals, but if we find a matching function,
|
||||
// patch it to the target, which might be a clone.
|
||||
for (size_t j = 0; j < originals.length(); j++) {
|
||||
if (entries_[i]->func == originals[j]) {
|
||||
if (entries_[i]->func != targets[j])
|
||||
entries_[i] = new Entry(entries_[i]->typeObj, targets[j]->toFunction());
|
||||
foundFunc = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!foundFunc)
|
||||
entries_.erase(&(entries_[i]));
|
||||
else
|
||||
i++;
|
||||
}
|
||||
|
||||
IonSpew(IonSpew_Inlining, "%d inlineable cases left after trimming to %d targets",
|
||||
(int)numEntries(), (int)targets.length());
|
||||
}
|
||||
|
|
|
@ -1051,6 +1051,9 @@ class MReturn
|
|||
return new MReturn(ins);
|
||||
}
|
||||
|
||||
MDefinition *input() const {
|
||||
return getOperand(0);
|
||||
}
|
||||
TypePolicy *typePolicy() {
|
||||
return this;
|
||||
}
|
||||
|
@ -4610,7 +4613,7 @@ class MStoreFixedSlot
|
|||
: MBinaryInstruction(obj, rval),
|
||||
needsBarrier_(barrier),
|
||||
slot_(slot)
|
||||
{}
|
||||
{ }
|
||||
|
||||
public:
|
||||
INSTRUCTION_HEADER(StoreFixedSlot)
|
||||
|
@ -4654,22 +4657,18 @@ class InlinePropertyTable : public TempObject
|
|||
CompilerRootFunction func;
|
||||
|
||||
Entry(types::TypeObject *typeObj, JSFunction *func)
|
||||
: typeObj(typeObj),
|
||||
func(func)
|
||||
{
|
||||
}
|
||||
: typeObj(typeObj), func(func)
|
||||
{ }
|
||||
};
|
||||
|
||||
jsbytecode *pc_;
|
||||
MResumePoint *priorResumePoint_;
|
||||
Vector<Entry *, 4, IonAllocPolicy> entries_;
|
||||
|
||||
public:
|
||||
InlinePropertyTable(jsbytecode *pc)
|
||||
: pc_(pc),
|
||||
priorResumePoint_(NULL),
|
||||
entries_()
|
||||
{
|
||||
}
|
||||
: pc_(pc), priorResumePoint_(NULL), entries_()
|
||||
{ }
|
||||
|
||||
void setPriorResumePoint(MResumePoint *resumePoint) {
|
||||
JS_ASSERT(priorResumePoint_ == NULL);
|
||||
|
@ -4702,26 +4701,7 @@ class InlinePropertyTable : public TempObject
|
|||
return entries_[i]->func;
|
||||
}
|
||||
|
||||
void trimToAndMaybePatchTargets(AutoObjectVector &targets, AutoObjectVector &originals) {
|
||||
size_t i = 0;
|
||||
while (i < numEntries()) {
|
||||
bool foundFunc = false;
|
||||
// Compare using originals, but if we find a matching function,
|
||||
// patch it to the target, which might be a clone.
|
||||
for (size_t j = 0; j < originals.length(); j++) {
|
||||
if (entries_[i]->func == originals[j]) {
|
||||
if (entries_[i]->func != targets[j])
|
||||
entries_[i] = new Entry(entries_[i]->typeObj, targets[j]->toFunction());
|
||||
foundFunc = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!foundFunc)
|
||||
entries_.erase(&(entries_[i]));
|
||||
else
|
||||
i++;
|
||||
}
|
||||
}
|
||||
void trimToAndMaybePatchTargets(AutoObjectVector &targets, AutoObjectVector &originals);
|
||||
};
|
||||
|
||||
class MGetPropertyCache
|
||||
|
|
Загрузка…
Ссылка в новой задаче