зеркало из https://github.com/mozilla/gecko-dev.git
Backed out changeset 0b365c68c8a9 (bug 995336) for Android armv6 crashes.
CLOSED TREE
This commit is contained in:
Родитель
7262b1ae70
Коммит
a3e615580e
|
@ -300,6 +300,7 @@ static const PhaseInfo phases[] = {
|
|||
{ PHASE_DISCARD_TI, "Discard TI", PHASE_DISCARD_ANALYSIS },
|
||||
{ PHASE_FREE_TI_ARENA, "Free TI Arena", PHASE_DISCARD_ANALYSIS },
|
||||
{ PHASE_SWEEP_TYPES, "Sweep Types", PHASE_DISCARD_ANALYSIS },
|
||||
{ PHASE_CLEAR_SCRIPT_ANALYSIS, "Clear Script Analysis", PHASE_DISCARD_ANALYSIS },
|
||||
{ PHASE_SWEEP_OBJECT, "Sweep Object", PHASE_SWEEP },
|
||||
{ PHASE_SWEEP_STRING, "Sweep String", PHASE_SWEEP },
|
||||
{ PHASE_SWEEP_SCRIPT, "Sweep Script", PHASE_SWEEP },
|
||||
|
|
|
@ -52,6 +52,7 @@ enum Phase {
|
|||
PHASE_DISCARD_TI,
|
||||
PHASE_FREE_TI_ARENA,
|
||||
PHASE_SWEEP_TYPES,
|
||||
PHASE_CLEAR_SCRIPT_ANALYSIS,
|
||||
PHASE_SWEEP_OBJECT,
|
||||
PHASE_SWEEP_STRING,
|
||||
PHASE_SWEEP_SCRIPT,
|
||||
|
|
|
@ -10,7 +10,6 @@
|
|||
#include "jit/BaselineIC.h"
|
||||
#include "jit/BaselineJIT.h"
|
||||
#include "jit/FixedList.h"
|
||||
#include "jit/IonAnalysis.h"
|
||||
#include "jit/IonLinker.h"
|
||||
#include "jit/IonSpewer.h"
|
||||
#ifdef JS_ION_PERF
|
||||
|
@ -80,9 +79,16 @@ BaselineCompiler::compile()
|
|||
AutoTraceLog logScript(logger, TraceLogCreateTextId(logger, script));
|
||||
AutoTraceLog logCompile(logger, TraceLogger::BaselineCompilation);
|
||||
|
||||
if (!script->ensureHasTypes(cx) || !script->ensureHasAnalyzedArgsUsage(cx))
|
||||
if (!script->ensureHasTypes(cx))
|
||||
return Method_Error;
|
||||
|
||||
// Only need to analyze scripts which are marked |argumensHasVarBinding|, to
|
||||
// compute |needsArgsObj| flag.
|
||||
if (script->argumentsHasVarBinding()) {
|
||||
if (!script->ensureRanAnalysis(cx))
|
||||
return Method_Error;
|
||||
}
|
||||
|
||||
// Pin analysis info during compilation.
|
||||
types::AutoEnterAnalysis autoEnterAnalysis(cx);
|
||||
|
||||
|
@ -234,7 +240,18 @@ BaselineCompiler::compile()
|
|||
baselineScript->toggleSPS(true);
|
||||
|
||||
uint32_t *bytecodeMap = baselineScript->bytecodeTypeMap();
|
||||
types::FillBytecodeTypeMap(script, bytecodeMap);
|
||||
|
||||
uint32_t added = 0;
|
||||
for (jsbytecode *pc = script->code(); pc < script->codeEnd(); pc += GetBytecodeLength(pc)) {
|
||||
JSOp op = JSOp(*pc);
|
||||
if (js_CodeSpec[op].format & JOF_TYPESET) {
|
||||
bytecodeMap[added++] = script->pcToOffset(pc);
|
||||
if (added == script->nTypeSets())
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
JS_ASSERT(added == script->nTypeSets());
|
||||
|
||||
// The last entry in the last index found, and is used to avoid binary
|
||||
// searches for the sought entry when queries are in linear order.
|
||||
|
|
|
@ -304,9 +304,6 @@ TryToSpecializeBinaryArithOp(ICStub **stubs,
|
|||
MIRType
|
||||
BaselineInspector::expectedBinaryArithSpecialization(jsbytecode *pc)
|
||||
{
|
||||
if (!hasBaselineScript())
|
||||
return MIRType_None;
|
||||
|
||||
MIRType result;
|
||||
ICStub *stubs[2];
|
||||
|
||||
|
@ -452,9 +449,6 @@ BaselineInspector::getTemplateObjectForNative(jsbytecode *pc, Native native)
|
|||
DeclEnvObject *
|
||||
BaselineInspector::templateDeclEnvObject()
|
||||
{
|
||||
if (!hasBaselineScript())
|
||||
return nullptr;
|
||||
|
||||
JSObject *res = &templateCallObject()->as<ScopeObject>().enclosingScope();
|
||||
JS_ASSERT(res);
|
||||
|
||||
|
@ -464,9 +458,6 @@ BaselineInspector::templateDeclEnvObject()
|
|||
CallObject *
|
||||
BaselineInspector::templateCallObject()
|
||||
{
|
||||
if (!hasBaselineScript())
|
||||
return nullptr;
|
||||
|
||||
JSObject *res = baselineScript()->templateScope();
|
||||
JS_ASSERT(res);
|
||||
|
||||
|
@ -476,9 +467,6 @@ BaselineInspector::templateCallObject()
|
|||
JSObject *
|
||||
BaselineInspector::commonGetPropFunction(jsbytecode *pc, Shape **lastProperty, JSFunction **commonGetter)
|
||||
{
|
||||
if (!hasBaselineScript())
|
||||
return nullptr;
|
||||
|
||||
const ICEntry &entry = icEntryFromPC(pc);
|
||||
for (ICStub *stub = entry.firstStub(); stub; stub = stub->next()) {
|
||||
if (stub->isGetProp_CallScripted() ||
|
||||
|
@ -497,9 +485,6 @@ BaselineInspector::commonGetPropFunction(jsbytecode *pc, Shape **lastProperty, J
|
|||
JSObject *
|
||||
BaselineInspector::commonSetPropFunction(jsbytecode *pc, Shape **lastProperty, JSFunction **commonSetter)
|
||||
{
|
||||
if (!hasBaselineScript())
|
||||
return nullptr;
|
||||
|
||||
const ICEntry &entry = icEntryFromPC(pc);
|
||||
for (ICStub *stub = entry.firstStub(); stub; stub = stub->next()) {
|
||||
if (stub->isSetProp_CallScripted() || stub->isSetProp_CallNative()) {
|
||||
|
|
|
@ -297,10 +297,6 @@ class CompileInfo
|
|||
return executionMode_;
|
||||
}
|
||||
|
||||
bool executionModeIsAnalysis() const {
|
||||
return executionMode_ == DefinitePropertiesAnalysis || executionMode_ == ArgumentsUsageAnalysis;
|
||||
}
|
||||
|
||||
bool isParallelExecution() const {
|
||||
return executionMode_ == ParallelExecution;
|
||||
}
|
||||
|
|
|
@ -67,7 +67,6 @@ CanIonCompile(JSScript *script, ExecutionMode cmode)
|
|||
case SequentialExecution: return script->canIonCompile();
|
||||
case ParallelExecution: return script->canParallelIonCompile();
|
||||
case DefinitePropertiesAnalysis: return true;
|
||||
case ArgumentsUsageAnalysis: return true;
|
||||
default:;
|
||||
}
|
||||
MOZ_ASSUME_UNREACHABLE("No such execution mode");
|
||||
|
|
|
@ -1947,6 +1947,11 @@ CheckScript(JSContext *cx, JSScript *script, bool osr)
|
|||
return false;
|
||||
}
|
||||
|
||||
if (!script->analyzedArgsUsage() && !script->ensureRanAnalysis(cx)) {
|
||||
IonSpew(IonSpew_Abort, "OOM under ensureRanAnalysis");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!script->compileAndGo()) {
|
||||
IonSpew(IonSpew_Abort, "not compile-and-go");
|
||||
return false;
|
||||
|
|
|
@ -2241,7 +2241,7 @@ jit::AnalyzeNewScriptProperties(JSContext *cx, JSFunction *fun,
|
|||
IonContext ictx(cx, &temp);
|
||||
|
||||
if (!cx->compartment()->ensureJitCompartmentExists(cx))
|
||||
return false;
|
||||
return Method_Error;
|
||||
|
||||
if (!script->hasBaselineScript()) {
|
||||
MethodStatus status = BaselineCompile(cx, script);
|
||||
|
@ -2301,6 +2301,7 @@ jit::AnalyzeNewScriptProperties(JSContext *cx, JSFunction *fun,
|
|||
// appear in the graph.
|
||||
Vector<MInstruction *> instructions(cx);
|
||||
|
||||
Vector<MDefinition *> useWorklist(cx);
|
||||
for (MUseDefIterator uses(thisValue); uses; uses++) {
|
||||
MDefinition *use = uses.def();
|
||||
|
||||
|
@ -2360,114 +2361,3 @@ jit::AnalyzeNewScriptProperties(JSContext *cx, JSFunction *fun,
|
|||
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool
|
||||
ArgumentsUseCanBeLazy(JSContext *cx, JSScript *script, MInstruction *ins, size_t index)
|
||||
{
|
||||
// We can read the frame's arguments directly for f.apply(x, arguments).
|
||||
if (ins->isCall()) {
|
||||
if (*ins->toCall()->resumePoint()->pc() == JSOP_FUNAPPLY &&
|
||||
ins->toCall()->numActualArgs() == 2 &&
|
||||
index == MCall::IndexOfArgument(1))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
// arguments[i] can read fp->canonicalActualArg(i) directly.
|
||||
if (ins->isCallGetElement() && index == 0)
|
||||
return true;
|
||||
|
||||
// arguments.length length can read fp->numActualArgs() directly.
|
||||
if (ins->isCallGetProperty() && index == 0 && ins->toCallGetProperty()->name() == cx->names().length)
|
||||
return true;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool
|
||||
jit::AnalyzeArgumentsUsage(JSContext *cx, JSScript *scriptArg)
|
||||
{
|
||||
RootedScript script(cx, scriptArg);
|
||||
types::AutoEnterAnalysis enter(cx);
|
||||
|
||||
JS_ASSERT(!script->analyzedArgsUsage());
|
||||
|
||||
// Treat the script as needing an arguments object until we determine it
|
||||
// does not need one. This both allows us to easily see where the arguments
|
||||
// object can escape through assignments to the function's named arguments,
|
||||
// and also simplifies handling of early returns.
|
||||
script->setNeedsArgsObj(true);
|
||||
|
||||
if (!jit::IsIonEnabled(cx) || !script->compileAndGo())
|
||||
return true;
|
||||
|
||||
static const uint32_t MAX_SCRIPT_SIZE = 10000;
|
||||
if (script->length() > MAX_SCRIPT_SIZE)
|
||||
return true;
|
||||
|
||||
if (!script->ensureHasTypes(cx))
|
||||
return false;
|
||||
|
||||
LifoAlloc alloc(types::TypeZone::TYPE_LIFO_ALLOC_PRIMARY_CHUNK_SIZE);
|
||||
|
||||
TempAllocator temp(&alloc);
|
||||
IonContext ictx(cx, &temp);
|
||||
|
||||
if (!cx->compartment()->ensureJitCompartmentExists(cx))
|
||||
return false;
|
||||
|
||||
MIRGraph graph(&temp);
|
||||
CompileInfo info(script, script->functionNonDelazifying(),
|
||||
/* osrPc = */ nullptr, /* constructing = */ false,
|
||||
ArgumentsUsageAnalysis,
|
||||
/* needsArgsObj = */ true);
|
||||
|
||||
AutoTempAllocatorRooter root(cx, &temp);
|
||||
|
||||
const OptimizationInfo *optimizationInfo = js_IonOptimizations.get(Optimization_Normal);
|
||||
|
||||
types::CompilerConstraintList *constraints = types::NewCompilerConstraintList(temp);
|
||||
if (!constraints)
|
||||
return false;
|
||||
|
||||
BaselineInspector inspector(script);
|
||||
const JitCompileOptions options(cx);
|
||||
|
||||
IonBuilder builder(nullptr, CompileCompartment::get(cx->compartment()), options, &temp, &graph, constraints,
|
||||
&inspector, &info, optimizationInfo, /* baselineFrame = */ nullptr);
|
||||
|
||||
if (!builder.build()) {
|
||||
if (builder.abortReason() == AbortReason_Alloc)
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
if (!SplitCriticalEdges(graph))
|
||||
return false;
|
||||
|
||||
if (!RenumberBlocks(graph))
|
||||
return false;
|
||||
|
||||
if (!BuildDominatorTree(graph))
|
||||
return false;
|
||||
|
||||
if (!EliminatePhis(&builder, graph, AggressiveObservability))
|
||||
return false;
|
||||
|
||||
MDefinition *argumentsValue = graph.begin()->getSlot(info.argsObjSlot());
|
||||
|
||||
for (MUseDefIterator uses(argumentsValue); uses; uses++) {
|
||||
MDefinition *use = uses.def();
|
||||
|
||||
// Don't track |arguments| through assignments to phis.
|
||||
if (!use->isInstruction())
|
||||
return true;
|
||||
|
||||
if (!ArgumentsUseCanBeLazy(cx, script, use->toInstruction(), uses.index()))
|
||||
return true;
|
||||
}
|
||||
|
||||
script->setNeedsArgsObj(false);
|
||||
return true;
|
||||
}
|
||||
|
|
|
@ -7,8 +7,6 @@
|
|||
#ifndef jit_IonAnalysis_h
|
||||
#define jit_IonAnalysis_h
|
||||
|
||||
#ifdef JS_ION
|
||||
|
||||
// This file declares various analysis passes that operate on MIR.
|
||||
|
||||
#include "jit/IonAllocPolicy.h"
|
||||
|
@ -138,12 +136,7 @@ AnalyzeNewScriptProperties(JSContext *cx, JSFunction *fun,
|
|||
types::TypeObject *type, HandleObject baseobj,
|
||||
Vector<types::TypeNewScript::Initializer> *initializerList);
|
||||
|
||||
bool
|
||||
AnalyzeArgumentsUsage(JSContext *cx, JSScript *script);
|
||||
|
||||
} // namespace jit
|
||||
} // namespace js
|
||||
|
||||
#endif // JS_ION
|
||||
|
||||
#endif /* jit_IonAnalysis_h */
|
||||
|
|
|
@ -124,7 +124,6 @@ IonBuilder::IonBuilder(JSContext *analysisContext, CompileCompartment *comp,
|
|||
argTypes(nullptr),
|
||||
typeArray(nullptr),
|
||||
typeArrayHint(0),
|
||||
bytecodeTypeMap(nullptr),
|
||||
loopDepth_(loopDepth),
|
||||
callerResumePoint_(nullptr),
|
||||
callerBuilder_(nullptr),
|
||||
|
@ -146,7 +145,7 @@ IonBuilder::IonBuilder(JSContext *analysisContext, CompileCompartment *comp,
|
|||
script_ = info->script();
|
||||
pc = info->startPC();
|
||||
|
||||
JS_ASSERT(script()->hasBaselineScript() == (info->executionMode() != ArgumentsUsageAnalysis));
|
||||
JS_ASSERT(script()->hasBaselineScript());
|
||||
JS_ASSERT(!!analysisContext == (info->executionMode() == DefinitePropertiesAnalysis));
|
||||
}
|
||||
|
||||
|
@ -610,17 +609,6 @@ IonBuilder::init()
|
|||
if (!analysis().init(alloc(), gsn))
|
||||
return false;
|
||||
|
||||
// The baseline script normally has the bytecode type map, but compute
|
||||
// it ourselves if we do not have a baseline script.
|
||||
if (script()->hasBaselineScript()) {
|
||||
bytecodeTypeMap = script()->baselineScript()->bytecodeTypeMap();
|
||||
} else {
|
||||
bytecodeTypeMap = alloc_->lifoAlloc()->newArrayUninitialized<uint32_t>(script()->nTypeSets());
|
||||
if (!bytecodeTypeMap)
|
||||
return false;
|
||||
types::FillBytecodeTypeMap(script(), bytecodeTypeMap);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
@ -998,10 +986,8 @@ IonBuilder::initScopeChain(MDefinition *callee)
|
|||
scope = MFunctionEnvironment::New(alloc(), callee);
|
||||
current->add(scope);
|
||||
|
||||
// This reproduce what is done in CallObject::createForFunction. Skip
|
||||
// this for analyses, as the script might not have a baseline script
|
||||
// with template objects yet.
|
||||
if (fun->isHeavyweight() && !info().executionModeIsAnalysis()) {
|
||||
// This reproduce what is done in CallObject::createForFunction
|
||||
if (fun->isHeavyweight()) {
|
||||
if (fun->isNamedLambda()) {
|
||||
scope = createDeclEnvObject(callee, scope);
|
||||
if (!scope)
|
||||
|
@ -3587,12 +3573,7 @@ IonBuilder::jsop_try()
|
|||
return abort("Has try-finally");
|
||||
|
||||
// Try-catch within inline frames is not yet supported.
|
||||
JS_ASSERT(!isInlineBuilder());
|
||||
|
||||
// Try-catch during the arguments usage analysis is not yet supported. Code
|
||||
// accessing the arguments within the 'catch' block is not accounted for.
|
||||
if (info().executionMode() == ArgumentsUsageAnalysis)
|
||||
return abort("Try-catch during arguments usage analysis");
|
||||
JS_ASSERT(script()->uninlineable() && !isInlineBuilder());
|
||||
|
||||
graph().setHasTryBlock();
|
||||
|
||||
|
@ -4115,10 +4096,6 @@ IonBuilder::makeInliningDecision(JSFunction *target, CallInfo &callInfo)
|
|||
if (target == nullptr)
|
||||
return InliningDecision_DontInline;
|
||||
|
||||
// Never inline during the arguments usage analysis.
|
||||
if (info().executionMode() == ArgumentsUsageAnalysis)
|
||||
return InliningDecision_DontInline;
|
||||
|
||||
// Native functions provide their own detection in inlineNativeCall().
|
||||
if (target->isNative())
|
||||
return InliningDecision_Inline;
|
||||
|
@ -5664,8 +5641,14 @@ IonBuilder::jsop_initprop(PropertyName *name)
|
|||
|
||||
// In parallel execution, we never require write barriers. See
|
||||
// forkjoin.cpp for more information.
|
||||
if (info().executionMode() == ParallelExecution)
|
||||
switch (info().executionMode()) {
|
||||
case SequentialExecution:
|
||||
case DefinitePropertiesAnalysis:
|
||||
break;
|
||||
case ParallelExecution:
|
||||
needsBarrier = false;
|
||||
break;
|
||||
}
|
||||
|
||||
if (templateObject->isFixedSlot(shape->slot())) {
|
||||
MStoreFixedSlot *store = MStoreFixedSlot::New(alloc(), obj, shape->slot(), value);
|
||||
|
@ -6681,21 +6664,6 @@ IonBuilder::jsop_getelem()
|
|||
MDefinition *index = current->pop();
|
||||
MDefinition *obj = current->pop();
|
||||
|
||||
// Always use a call if we are performing analysis and not actually
|
||||
// emitting code, to simplify later analysis.
|
||||
if (info().executionModeIsAnalysis()) {
|
||||
MInstruction *ins = MCallGetElement::New(alloc(), obj, index);
|
||||
|
||||
current->add(ins);
|
||||
current->push(ins);
|
||||
|
||||
if (!resumeAfter(ins))
|
||||
return false;
|
||||
|
||||
types::TemporaryTypeSet *types = bytecodeTypes(pc);
|
||||
return pushTypeBarrier(ins, types, true);
|
||||
}
|
||||
|
||||
bool emitted = false;
|
||||
|
||||
if (!getElemTryTypedObject(&emitted, obj, index) || emitted)
|
||||
|
@ -8568,11 +8536,11 @@ IonBuilder::jsop_getprop(PropertyName *name)
|
|||
bool barrier = PropertyReadNeedsTypeBarrier(analysisContext, constraints(),
|
||||
current->peek(-1), name, types);
|
||||
|
||||
// Always use a call if we are performing analysis and
|
||||
// Always use a call if we are doing the definite properties analysis and
|
||||
// not actually emitting code, to simplify later analysis. Also skip deeper
|
||||
// analysis if there are no known types for this operation, as it will
|
||||
// always invalidate when executing.
|
||||
if (info().executionModeIsAnalysis() || types->empty()) {
|
||||
if (info().executionMode() == DefinitePropertiesAnalysis || types->empty()) {
|
||||
MDefinition *obj = current->peek(-1);
|
||||
MCallGetProperty *call = MCallGetProperty::New(alloc(), obj, name, *pc == JSOP_CALLPROP);
|
||||
current->add(call);
|
||||
|
@ -8581,7 +8549,7 @@ IonBuilder::jsop_getprop(PropertyName *name)
|
|||
// constants read off the prototype chain, to allow inlining later on.
|
||||
// In this case we still need the getprop call so that the later
|
||||
// analysis knows when the |this| value has been read from.
|
||||
if (info().executionModeIsAnalysis()) {
|
||||
if (info().executionMode() == DefinitePropertiesAnalysis) {
|
||||
if (!getPropTryConstant(&emitted, name, types) || emitted)
|
||||
return emitted;
|
||||
}
|
||||
|
@ -9048,7 +9016,7 @@ IonBuilder::jsop_setprop(PropertyName *name)
|
|||
|
||||
// Always use a call if we are doing the definite properties analysis and
|
||||
// not actually emitting code, to simplify later analysis.
|
||||
if (info().executionModeIsAnalysis()) {
|
||||
if (info().executionMode() == DefinitePropertiesAnalysis) {
|
||||
MInstruction *ins = MCallSetProperty::New(alloc(), obj, value, name, script()->strict());
|
||||
current->add(ins);
|
||||
current->push(value);
|
||||
|
@ -9634,10 +9602,11 @@ IonBuilder::jsop_this()
|
|||
return true;
|
||||
}
|
||||
|
||||
// If we are doing an analysis, we might not yet know the type of |this|.
|
||||
// Instead of bailing out just push the |this| slot, as this code won't
|
||||
// actually execute and it does not matter whether |this| is primitive.
|
||||
if (info().executionModeIsAnalysis()) {
|
||||
// If we are doing a definite properties analysis, we don't yet know the
|
||||
// |this| type as its type object is being created right now. Instead of
|
||||
// bailing out just push the |this| slot, as this code won't actually
|
||||
// execute and it does not matter whether |this| is primitive.
|
||||
if (info().executionMode() == DefinitePropertiesAnalysis) {
|
||||
current->pushSlot(info().thisSlot());
|
||||
return true;
|
||||
}
|
||||
|
@ -10038,7 +10007,7 @@ IonBuilder::addShapeGuard(MDefinition *obj, Shape *const shape, BailoutKind bail
|
|||
types::TemporaryTypeSet *
|
||||
IonBuilder::bytecodeTypes(jsbytecode *pc)
|
||||
{
|
||||
return types::TypeScript::BytecodeTypes(script(), pc, bytecodeTypeMap, &typeArrayHint, typeArray);
|
||||
return types::TypeScript::BytecodeTypes(script(), pc, &typeArrayHint, typeArray);
|
||||
}
|
||||
|
||||
TypeDescrSetHash *
|
||||
|
|
|
@ -837,7 +837,6 @@ class IonBuilder : public MIRGenerator
|
|||
|
||||
types::TemporaryTypeSet *thisTypes, *argTypes, *typeArray;
|
||||
uint32_t typeArrayHint;
|
||||
uint32_t *bytecodeTypeMap;
|
||||
|
||||
GSNCache gsn;
|
||||
ScopeCoordinateNameCache scopeCoordinateNameCache;
|
||||
|
|
|
@ -1482,6 +1482,12 @@ IonBuilder::inlineForceSequentialOrInParallelSection(CallInfo &callInfo)
|
|||
|
||||
ExecutionMode executionMode = info().executionMode();
|
||||
switch (executionMode) {
|
||||
case SequentialExecution:
|
||||
case DefinitePropertiesAnalysis:
|
||||
// In sequential mode, leave as is, because we'd have to
|
||||
// access the "in warmup" flag of the runtime.
|
||||
return InliningStatus_NotInlined;
|
||||
|
||||
case ParallelExecution: {
|
||||
// During Parallel Exec, we always force sequential, so
|
||||
// replace with true. This permits UCE to eliminate the
|
||||
|
@ -1492,11 +1498,6 @@ IonBuilder::inlineForceSequentialOrInParallelSection(CallInfo &callInfo)
|
|||
current->push(ins);
|
||||
return InliningStatus_Inlined;
|
||||
}
|
||||
|
||||
default:
|
||||
// In sequential mode, leave as is, because we'd have to
|
||||
// access the "in warmup" flag of the runtime.
|
||||
return InliningStatus_NotInlined;
|
||||
}
|
||||
|
||||
MOZ_ASSUME_UNREACHABLE("Invalid execution mode");
|
||||
|
@ -1521,6 +1522,11 @@ IonBuilder::inlineForkJoinGetSlice(CallInfo &callInfo)
|
|||
callInfo.setImplicitlyUsedUnchecked();
|
||||
|
||||
switch (info().executionMode()) {
|
||||
case SequentialExecution:
|
||||
case DefinitePropertiesAnalysis:
|
||||
// ForkJoinGetSlice acts as identity for sequential execution.
|
||||
current->push(callInfo.getArg(0));
|
||||
return InliningStatus_Inlined;
|
||||
case ParallelExecution:
|
||||
if (LIRGenerator::allowInlineForkJoinGetSlice()) {
|
||||
MForkJoinGetSlice *getSlice = MForkJoinGetSlice::New(alloc(),
|
||||
|
@ -1530,11 +1536,6 @@ IonBuilder::inlineForkJoinGetSlice(CallInfo &callInfo)
|
|||
return InliningStatus_Inlined;
|
||||
}
|
||||
return InliningStatus_NotInlined;
|
||||
|
||||
default:
|
||||
// ForkJoinGetSlice acts as identity for sequential execution.
|
||||
current->push(callInfo.getArg(0));
|
||||
return InliningStatus_Inlined;
|
||||
}
|
||||
|
||||
MOZ_ASSUME_UNREACHABLE("Invalid execution mode");
|
||||
|
@ -1550,10 +1551,11 @@ IonBuilder::inlineNewDenseArray(CallInfo &callInfo)
|
|||
// par. mode we use inlined MIR.
|
||||
ExecutionMode executionMode = info().executionMode();
|
||||
switch (executionMode) {
|
||||
case SequentialExecution:
|
||||
case DefinitePropertiesAnalysis:
|
||||
return inlineNewDenseArrayForSequentialExecution(callInfo);
|
||||
case ParallelExecution:
|
||||
return inlineNewDenseArrayForParallelExecution(callInfo);
|
||||
default:
|
||||
return inlineNewDenseArrayForSequentialExecution(callInfo);
|
||||
}
|
||||
|
||||
MOZ_ASSUME_UNREACHABLE("unknown ExecutionMode");
|
||||
|
|
|
@ -1922,6 +1922,81 @@ TypeCompartment::newTypeObject(ExclusiveContext *cx, const Class *clasp, Handle<
|
|||
return object;
|
||||
}
|
||||
|
||||
static inline jsbytecode *
|
||||
PreviousOpcode(HandleScript script, jsbytecode *pc)
|
||||
{
|
||||
ScriptAnalysis *analysis = script->analysis();
|
||||
JS_ASSERT(analysis->isReachable(pc));
|
||||
|
||||
if (pc == script->code())
|
||||
return nullptr;
|
||||
|
||||
for (pc--;; pc--) {
|
||||
if (analysis->isReachable(pc))
|
||||
break;
|
||||
}
|
||||
|
||||
return pc;
|
||||
}
|
||||
|
||||
/*
|
||||
* If pc is an array initializer within an outer multidimensional array
|
||||
* initializer, find the opcode of the previous newarray. nullptr otherwise.
|
||||
*/
|
||||
static inline jsbytecode *
|
||||
FindPreviousInnerInitializer(HandleScript script, jsbytecode *initpc)
|
||||
{
|
||||
if (!script->hasAnalysis())
|
||||
return nullptr;
|
||||
|
||||
if (!script->analysis()->isReachable(initpc))
|
||||
return nullptr;
|
||||
|
||||
/*
|
||||
* Pattern match the following bytecode, which will appear between
|
||||
* adjacent initializer elements:
|
||||
*
|
||||
* endinit (for previous initializer)
|
||||
* initelem_array (for previous initializer)
|
||||
* newarray
|
||||
*/
|
||||
|
||||
if (*initpc != JSOP_NEWARRAY)
|
||||
return nullptr;
|
||||
|
||||
jsbytecode *last = PreviousOpcode(script, initpc);
|
||||
if (!last || *last != JSOP_INITELEM_ARRAY)
|
||||
return nullptr;
|
||||
|
||||
last = PreviousOpcode(script, last);
|
||||
if (!last || *last != JSOP_ENDINIT)
|
||||
return nullptr;
|
||||
|
||||
/*
|
||||
* Find the start of the previous initializer. Keep track of initializer
|
||||
* depth to skip over inner initializers within the previous one (e.g. for
|
||||
* arrays with three or more dimensions).
|
||||
*/
|
||||
size_t initDepth = 0;
|
||||
jsbytecode *previnit;
|
||||
for (previnit = last; previnit; previnit = PreviousOpcode(script, previnit)) {
|
||||
if (*previnit == JSOP_ENDINIT)
|
||||
initDepth++;
|
||||
if (*previnit == JSOP_NEWINIT ||
|
||||
*previnit == JSOP_NEWARRAY ||
|
||||
*previnit == JSOP_NEWOBJECT)
|
||||
{
|
||||
if (--initDepth == 0)
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!previnit || *previnit != JSOP_NEWARRAY)
|
||||
return nullptr;
|
||||
|
||||
return previnit;
|
||||
}
|
||||
|
||||
TypeObject *
|
||||
TypeCompartment::addAllocationSiteTypeObject(JSContext *cx, AllocationSiteKey key)
|
||||
{
|
||||
|
@ -1940,8 +2015,24 @@ TypeCompartment::addAllocationSiteTypeObject(JSContext *cx, AllocationSiteKey ke
|
|||
|
||||
TypeObject *res = nullptr;
|
||||
|
||||
/*
|
||||
* If this is an array initializer nested in another array initializer,
|
||||
* try to reuse the type objects from earlier elements to avoid
|
||||
* distinguishing elements of the outer array unnecessarily.
|
||||
*/
|
||||
jsbytecode *pc = key.script->offsetToPC(key.offset);
|
||||
RootedScript keyScript(cx, key.script);
|
||||
jsbytecode *prev = FindPreviousInnerInitializer(keyScript, pc);
|
||||
if (prev) {
|
||||
AllocationSiteKey nkey;
|
||||
nkey.script = key.script;
|
||||
nkey.offset = key.script->pcToOffset(prev);
|
||||
nkey.kind = JSProto_Array;
|
||||
|
||||
AllocationSiteTable::Ptr p = cx->compartment()->types.allocationSiteTable->lookup(nkey);
|
||||
if (p)
|
||||
res = p->value();
|
||||
}
|
||||
|
||||
if (!res) {
|
||||
RootedObject proto(cx);
|
||||
|
@ -3397,21 +3488,6 @@ IsAboutToBeFinalized(TypeObjectKey *key)
|
|||
return isAboutToBeFinalized;
|
||||
}
|
||||
|
||||
void
|
||||
types::FillBytecodeTypeMap(JSScript *script, uint32_t *bytecodeMap)
|
||||
{
|
||||
uint32_t added = 0;
|
||||
for (jsbytecode *pc = script->code(); pc < script->codeEnd(); pc += GetBytecodeLength(pc)) {
|
||||
JSOp op = JSOp(*pc);
|
||||
if (js_CodeSpec[op].format & JOF_TYPESET) {
|
||||
bytecodeMap[added++] = script->pcToOffset(pc);
|
||||
if (added == script->nTypeSets())
|
||||
break;
|
||||
}
|
||||
}
|
||||
JS_ASSERT(added == script->nTypeSets());
|
||||
}
|
||||
|
||||
void
|
||||
types::TypeMonitorResult(JSContext *cx, JSScript *script, jsbytecode *pc, const js::Value &rval)
|
||||
{
|
||||
|
@ -3533,6 +3609,28 @@ JSScript::makeTypes(JSContext *cx)
|
|||
}
|
||||
#endif
|
||||
|
||||
return analyzedArgsUsage() || ensureRanAnalysis(cx);
|
||||
}
|
||||
|
||||
bool
|
||||
JSScript::makeAnalysis(JSContext *cx)
|
||||
{
|
||||
JS_ASSERT(types && !types->analysis);
|
||||
|
||||
AutoEnterAnalysis enter(cx);
|
||||
|
||||
types->analysis = cx->typeLifoAlloc().new_<ScriptAnalysis>(this);
|
||||
|
||||
if (!types->analysis)
|
||||
return false;
|
||||
|
||||
RootedScript self(cx, this);
|
||||
|
||||
if (!self->types->analysis->analyzeBytecode(cx)) {
|
||||
self->types->analysis = nullptr;
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
@ -4261,8 +4359,6 @@ TypeZone::sweep(FreeOp *fop, bool releaseTypes, bool *oom)
|
|||
|
||||
/* Sweep and find compressed indexes for each compiler output. */
|
||||
size_t newCompilerOutputCount = 0;
|
||||
|
||||
#ifdef JS_ION
|
||||
if (compilerOutputs) {
|
||||
for (size_t i = 0; i < compilerOutputs->length(); i++) {
|
||||
CompilerOutput &output = (*compilerOutputs)[i];
|
||||
|
@ -4277,7 +4373,6 @@ TypeZone::sweep(FreeOp *fop, bool releaseTypes, bool *oom)
|
|||
}
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
{
|
||||
gcstats::AutoPhase ap2(rt->gcStats, gcstats::PHASE_DISCARD_TI);
|
||||
|
@ -4289,15 +4384,11 @@ TypeZone::sweep(FreeOp *fop, bool releaseTypes, bool *oom)
|
|||
|
||||
if (releaseTypes) {
|
||||
if (script->hasParallelIonScript()) {
|
||||
#ifdef JS_ION
|
||||
// It's possible that we preserved the parallel
|
||||
// IonScript. The heuristic for their preservation is
|
||||
// independent of general JIT code preservation.
|
||||
MOZ_ASSERT(jit::ShouldPreserveParallelJITCode(rt, script));
|
||||
script->parallelIonScript()->recompileInfoRef().shouldSweep(*this);
|
||||
#else
|
||||
MOZ_CRASH();
|
||||
#endif
|
||||
} else {
|
||||
script->types->destroy();
|
||||
script->types = nullptr;
|
||||
|
@ -4349,6 +4440,14 @@ TypeZone::sweep(FreeOp *fop, bool releaseTypes, bool *oom)
|
|||
JS_ALWAYS_TRUE(compilerOutputs->resize(newCompilerOutputCount));
|
||||
}
|
||||
|
||||
{
|
||||
gcstats::AutoPhase ap2(rt->gcStats, gcstats::PHASE_CLEAR_SCRIPT_ANALYSIS);
|
||||
for (CellIterUnderGC i(zone(), FINALIZE_SCRIPT); !i.done(); i.next()) {
|
||||
JSScript *script = i.get<JSScript>();
|
||||
script->clearAnalysis();
|
||||
}
|
||||
}
|
||||
|
||||
{
|
||||
gcstats::AutoPhase ap2(rt->gcStats, gcstats::PHASE_FREE_TI_ARENA);
|
||||
rt->freeLifoAlloc.transferFrom(&oldAlloc);
|
||||
|
|
|
@ -150,13 +150,7 @@ enum ExecutionMode {
|
|||
* MIR analysis performed when invoking 'new' on a script, to determine
|
||||
* definite properties. Used by the optimizing JIT.
|
||||
*/
|
||||
DefinitePropertiesAnalysis,
|
||||
|
||||
/*
|
||||
* MIR analysis performed when executing a script which uses its arguments,
|
||||
* when it is not known whether a lazy arguments value can be used.
|
||||
*/
|
||||
ArgumentsUsageAnalysis
|
||||
DefinitePropertiesAnalysis
|
||||
};
|
||||
|
||||
/*
|
||||
|
@ -192,6 +186,10 @@ namespace jit {
|
|||
class TempAllocator;
|
||||
}
|
||||
|
||||
namespace analyze {
|
||||
class ScriptAnalysis;
|
||||
}
|
||||
|
||||
namespace types {
|
||||
|
||||
class TypeZone;
|
||||
|
@ -1244,6 +1242,9 @@ class TypeScript
|
|||
{
|
||||
friend class ::JSScript;
|
||||
|
||||
/* Analysis information for the script, cleared on each GC. */
|
||||
analyze::ScriptAnalysis *analysis;
|
||||
|
||||
public:
|
||||
/* Array of type type sets for variables and JOF_TYPESET ops. */
|
||||
StackTypeSet *typeArray() const { return (StackTypeSet *) (uintptr_t(this) + sizeof(TypeScript)); }
|
||||
|
@ -1257,7 +1258,7 @@ class TypeScript
|
|||
static inline StackTypeSet *BytecodeTypes(JSScript *script, jsbytecode *pc);
|
||||
|
||||
template <typename TYPESET>
|
||||
static inline TYPESET *BytecodeTypes(JSScript *script, jsbytecode *pc, uint32_t *bytecodeMap,
|
||||
static inline TYPESET *BytecodeTypes(JSScript *script, jsbytecode *pc,
|
||||
uint32_t *hint, TYPESET *typeArray);
|
||||
|
||||
/* Get a type object for an allocation site in this script. */
|
||||
|
@ -1309,9 +1310,6 @@ class TypeScript
|
|||
#endif
|
||||
};
|
||||
|
||||
void
|
||||
FillBytecodeTypeMap(JSScript *script, uint32_t *bytecodeMap);
|
||||
|
||||
class RecompileInfo;
|
||||
|
||||
// Allocate a CompilerOutput for a finished compilation and generate the type
|
||||
|
|
|
@ -567,10 +567,15 @@ TypeScript::ArgTypes(JSScript *script, unsigned i)
|
|||
|
||||
template <typename TYPESET>
|
||||
/* static */ inline TYPESET *
|
||||
TypeScript::BytecodeTypes(JSScript *script, jsbytecode *pc, uint32_t *bytecodeMap,
|
||||
uint32_t *hint, TYPESET *typeArray)
|
||||
TypeScript::BytecodeTypes(JSScript *script, jsbytecode *pc, uint32_t *hint, TYPESET *typeArray)
|
||||
{
|
||||
JS_ASSERT(js_CodeSpec[*pc].format & JOF_TYPESET);
|
||||
#ifdef JS_ION
|
||||
uint32_t *bytecodeMap = script->baselineScript()->bytecodeTypeMap();
|
||||
#else
|
||||
uint32_t *bytecodeMap = nullptr;
|
||||
MOZ_CRASH();
|
||||
#endif
|
||||
uint32_t offset = script->pcToOffset(pc);
|
||||
|
||||
// See if this pc is the next typeset opcode after the last one looked up.
|
||||
|
@ -612,11 +617,11 @@ TypeScript::BytecodeTypes(JSScript *script, jsbytecode *pc)
|
|||
JS_ASSERT(CurrentThreadCanAccessRuntime(script->runtimeFromMainThread()));
|
||||
#ifdef JS_ION
|
||||
uint32_t *hint = script->baselineScript()->bytecodeTypeMap() + script->nTypeSets();
|
||||
return BytecodeTypes(script, pc, script->baselineScript()->bytecodeTypeMap(),
|
||||
hint, script->types->typeArray());
|
||||
#else
|
||||
uint32_t *hint = nullptr;
|
||||
MOZ_CRASH();
|
||||
#endif
|
||||
return BytecodeTypes(script, pc, hint, script->types->typeArray());
|
||||
}
|
||||
|
||||
struct AllocationSiteKey : public DefaultHasher<AllocationSiteKey> {
|
||||
|
@ -1351,6 +1356,39 @@ JSScript::ensureHasTypes(JSContext *cx)
|
|||
return types || makeTypes(cx);
|
||||
}
|
||||
|
||||
inline bool
|
||||
JSScript::ensureRanAnalysis(JSContext *cx)
|
||||
{
|
||||
js::types::AutoEnterAnalysis aea(cx);
|
||||
|
||||
if (!ensureHasTypes(cx))
|
||||
return false;
|
||||
if (!hasAnalysis() && !makeAnalysis(cx))
|
||||
return false;
|
||||
JS_ASSERT(hasAnalysis());
|
||||
return true;
|
||||
}
|
||||
|
||||
inline bool
|
||||
JSScript::hasAnalysis()
|
||||
{
|
||||
return types && types->analysis;
|
||||
}
|
||||
|
||||
inline js::analyze::ScriptAnalysis *
|
||||
JSScript::analysis()
|
||||
{
|
||||
JS_ASSERT(hasAnalysis());
|
||||
return types->analysis;
|
||||
}
|
||||
|
||||
inline void
|
||||
JSScript::clearAnalysis()
|
||||
{
|
||||
if (types)
|
||||
types->analysis = nullptr;
|
||||
}
|
||||
|
||||
namespace js {
|
||||
|
||||
template <>
|
||||
|
|
|
@ -284,6 +284,19 @@ js_DumpPCCounts(JSContext *cx, HandleScript script, js::Sprinter *sp)
|
|||
// Bytecode Parser
|
||||
/////////////////////////////////////////////////////////////////////
|
||||
|
||||
// Ensure that script analysis reports the same stack depth.
|
||||
static void
|
||||
AssertStackDepth(JSScript *script, uint32_t offset, uint32_t stackDepth) {
|
||||
/*
|
||||
* If this assertion fails, run the failing test case under gdb and use the
|
||||
* following gdb command to understand the execution path of this function.
|
||||
*
|
||||
* call js_DumpScriptDepth(cx, script, pc)
|
||||
*/
|
||||
if (script->hasAnalysis())
|
||||
script->analysis()->assertMatchingStackDepthAtOffset(offset, stackDepth);
|
||||
}
|
||||
|
||||
namespace {
|
||||
|
||||
class BytecodeParser
|
||||
|
@ -487,6 +500,7 @@ BytecodeParser::addJump(uint32_t offset, uint32_t *currentOffset,
|
|||
code = alloc().new_<Bytecode>();
|
||||
if (!code)
|
||||
return false;
|
||||
AssertStackDepth(script_, offset, stackDepth);
|
||||
if (!code->captureOffsetStack(alloc(), offsetStack, stackDepth)) {
|
||||
reportOOM();
|
||||
return false;
|
||||
|
@ -639,6 +653,7 @@ BytecodeParser::parse()
|
|||
reportOOM();
|
||||
return false;
|
||||
}
|
||||
AssertStackDepth(script_, successorOffset, stackDepth);
|
||||
if (!nextcode->captureOffsetStack(alloc(), offsetStack, stackDepth)) {
|
||||
reportOOM();
|
||||
return false;
|
||||
|
|
|
@ -3450,17 +3450,13 @@ void
|
|||
JSScript::setArgumentsHasVarBinding()
|
||||
{
|
||||
argsHasVarBinding_ = true;
|
||||
#ifdef JS_ION
|
||||
needsArgsAnalysis_ = true;
|
||||
#else
|
||||
// The arguments analysis is performed by IonBuilder.
|
||||
needsArgsObj_ = true;
|
||||
#endif
|
||||
}
|
||||
|
||||
void
|
||||
JSScript::setNeedsArgsObj(bool needsArgsObj)
|
||||
{
|
||||
JS_ASSERT(!analyzedArgsUsage());
|
||||
JS_ASSERT_IF(needsArgsObj, argumentsHasVarBinding());
|
||||
needsArgsAnalysis_ = false;
|
||||
needsArgsObj_ = needsArgsObj;
|
||||
|
|
|
@ -50,6 +50,10 @@ class Shape;
|
|||
class WatchpointMap;
|
||||
class NestedScopeObject;
|
||||
|
||||
namespace analyze {
|
||||
class ScriptAnalysis;
|
||||
}
|
||||
|
||||
namespace frontend {
|
||||
class BytecodeEmitter;
|
||||
}
|
||||
|
@ -1130,7 +1134,7 @@ class JSScript : public js::gc::BarrieredCell<JSScript>
|
|||
/*
|
||||
* As an optimization, even when argsHasLocalBinding, the function prologue
|
||||
* may not need to create an arguments object. This is determined by
|
||||
* needsArgsObj which is set by AnalyzeArgumentsUsage before running
|
||||
* needsArgsObj which is set by ScriptAnalysis::analyzeSSA before running
|
||||
* the script the first time. When !needsArgsObj, the prologue may simply
|
||||
* write MagicValue(JS_OPTIMIZED_ARGUMENTS) to 'arguments's slot and any
|
||||
* uses of 'arguments' will be guaranteed to handle this magic value.
|
||||
|
@ -1138,7 +1142,6 @@ class JSScript : public js::gc::BarrieredCell<JSScript>
|
|||
* that needsArgsObj is only called after the script has been analyzed.
|
||||
*/
|
||||
bool analyzedArgsUsage() const { return !needsArgsAnalysis_; }
|
||||
inline bool ensureHasAnalyzedArgsUsage(JSContext *cx);
|
||||
bool needsArgsObj() const {
|
||||
JS_ASSERT(analyzedArgsUsage());
|
||||
return needsArgsObj_;
|
||||
|
@ -1314,6 +1317,19 @@ class JSScript : public js::gc::BarrieredCell<JSScript>
|
|||
/* Ensure the script has a TypeScript. */
|
||||
inline bool ensureHasTypes(JSContext *cx);
|
||||
|
||||
/*
|
||||
* Ensure the script has bytecode analysis information. Performed when the
|
||||
* script first runs, or first runs after a TypeScript GC purge.
|
||||
*/
|
||||
inline bool ensureRanAnalysis(JSContext *cx);
|
||||
|
||||
/* Ensure the script has type inference analysis information. */
|
||||
inline bool ensureRanInference(JSContext *cx);
|
||||
|
||||
inline bool hasAnalysis();
|
||||
inline void clearAnalysis();
|
||||
inline js::analyze::ScriptAnalysis *analysis();
|
||||
|
||||
inline js::GlobalObject &global() const;
|
||||
js::GlobalObject &uninlinedGlobal() const;
|
||||
|
||||
|
@ -1326,6 +1342,7 @@ class JSScript : public js::gc::BarrieredCell<JSScript>
|
|||
|
||||
private:
|
||||
bool makeTypes(JSContext *cx);
|
||||
bool makeAnalysis(JSContext *cx);
|
||||
|
||||
public:
|
||||
uint32_t getUseCount() const {
|
||||
|
|
|
@ -11,7 +11,6 @@
|
|||
|
||||
#include "jit/AsmJSLink.h"
|
||||
#include "jit/BaselineJIT.h"
|
||||
#include "jit/IonAnalysis.h"
|
||||
#include "vm/ScopeObject.h"
|
||||
|
||||
#include "jscompartmentinlines.h"
|
||||
|
@ -174,16 +173,4 @@ JSScript::setBaselineScript(JSContext *maybecx, js::jit::BaselineScript *baselin
|
|||
updateBaselineOrIonRaw();
|
||||
}
|
||||
|
||||
inline bool
|
||||
JSScript::ensureHasAnalyzedArgsUsage(JSContext *cx)
|
||||
{
|
||||
if (analyzedArgsUsage())
|
||||
return true;
|
||||
#ifdef JS_ION
|
||||
return js::jit::AnalyzeArgumentsUsage(cx, this);
|
||||
#else
|
||||
MOZ_CRASH();
|
||||
#endif
|
||||
}
|
||||
|
||||
#endif /* jsscriptinlines_h */
|
||||
|
|
|
@ -33,7 +33,6 @@
|
|||
#include "builtin/Eval.h"
|
||||
#include "jit/BaselineJIT.h"
|
||||
#include "jit/Ion.h"
|
||||
#include "jit/IonAnalysis.h"
|
||||
#include "js/OldDebugAPI.h"
|
||||
#include "vm/Debugger.h"
|
||||
#include "vm/Opcodes.h"
|
||||
|
@ -2866,7 +2865,7 @@ CASE(JSOP_TABLESWITCH)
|
|||
|
||||
CASE(JSOP_ARGUMENTS)
|
||||
JS_ASSERT(!REGS.fp()->fun()->hasRest());
|
||||
if (!script->ensureHasAnalyzedArgsUsage(cx))
|
||||
if (!script->analyzedArgsUsage() && !script->ensureRanAnalysis(cx))
|
||||
goto error;
|
||||
if (script->needsArgsObj()) {
|
||||
ArgumentsObject *obj = ArgumentsObject::createExpected(cx, REGS.fp());
|
||||
|
|
|
@ -1150,7 +1150,7 @@ class DebugScopeProxy : public BaseProxyHandler
|
|||
if (scope->is<CallObject>() && !scope->as<CallObject>().isForEval()) {
|
||||
CallObject &callobj = scope->as<CallObject>();
|
||||
RootedScript script(cx, callobj.callee().nonLazyScript());
|
||||
if (!script->ensureHasTypes(cx) || !script->ensureHasAnalyzedArgsUsage(cx))
|
||||
if (!script->ensureHasTypes(cx))
|
||||
return false;
|
||||
|
||||
Bindings &bindings = script->bindings;
|
||||
|
|
Загрузка…
Ссылка в новой задаче