mirror of
https://gitlab.winehq.org/wine/wine-gecko.git
synced 2024-09-13 09:24:08 -07:00
Bug 772509 - Freeze a compilation output instead of a script. r=bhackett,dvander
This commit is contained in:
parent
b8745962e9
commit
7bc01053bb
@ -108,6 +108,25 @@ IonBailoutIterator::dump() const
|
||||
}
|
||||
}
|
||||
|
||||
static JSScript*
|
||||
GetBailedJSScript(JSContext *cx)
|
||||
{
|
||||
// Just after the frame conversion, we can safely interpret the ionTop as JS
|
||||
// frame because it targets the bailed JS frame converted to an exit frame.
|
||||
IonJSFrameLayout *frame = reinterpret_cast<IonJSFrameLayout*>(cx->runtime->ionTop);
|
||||
switch (GetCalleeTokenTag(frame->calleeToken())) {
|
||||
case CalleeToken_Function: {
|
||||
JSFunction *fun = CalleeTokenToFunction(frame->calleeToken());
|
||||
return fun->script();
|
||||
}
|
||||
case CalleeToken_Script:
|
||||
return CalleeTokenToScript(frame->calleeToken());
|
||||
default:
|
||||
JS_NOT_REACHED("unexpected callee token kind");
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
StackFrame::initFromBailout(JSContext *cx, SnapshotIterator &iter)
|
||||
{
|
||||
@ -521,7 +540,7 @@ uint32
|
||||
ion::BoundsCheckFailure()
|
||||
{
|
||||
JSContext *cx = GetIonContext()->cx;
|
||||
JSScript *script = cx->fp()->script();
|
||||
JSScript *script = GetBailedJSScript(cx);
|
||||
|
||||
IonSpew(IonSpew_Bailouts, "Bounds check failure %s:%d", script->filename,
|
||||
script->lineno);
|
||||
@ -530,8 +549,9 @@ ion::BoundsCheckFailure()
|
||||
script->failedBoundsCheck = true;
|
||||
|
||||
// Invalidate the script to force a recompile.
|
||||
Vector<types::RecompileInfo> scripts(cx);
|
||||
if (!scripts.append(types::RecompileInfo(script)))
|
||||
Vector<types::CompilerOutput> scripts(cx);
|
||||
types::CompilerOutput co(script);
|
||||
if (!scripts.append(co))
|
||||
return BAILOUT_RETURN_FATAL_ERROR;
|
||||
|
||||
IonSpew(IonSpew_Invalidate, "Invalidating due to bounds check failure");
|
||||
|
@ -876,7 +876,8 @@ IonCompile(JSContext *cx, JSScript *script, StackFrame *fp, jsbytecode *osrPc)
|
||||
if (!oracle.init(cx, script))
|
||||
return false;
|
||||
|
||||
types::AutoEnterCompilation enterCompiler(cx, script, false, 0);
|
||||
types::AutoEnterCompilation enterCompiler(cx, types::AutoEnterCompilation::Ion);
|
||||
enterCompiler.init(script, false, 0);
|
||||
AutoCompilerRoots roots(script->compartment()->rt);
|
||||
|
||||
IonBuilder builder(cx, temp, graph, &oracle, *info);
|
||||
@ -1309,20 +1310,31 @@ ion::InvalidateAll(FreeOp *fop, JSCompartment *c)
|
||||
}
|
||||
|
||||
void
|
||||
ion::Invalidate(FreeOp *fop, const Vector<types::RecompileInfo> &invalid, bool resetUses)
|
||||
ion::Invalidate(FreeOp *fop, const Vector<types::CompilerOutput> &invalid, bool resetUses)
|
||||
{
|
||||
IonSpew(IonSpew_Invalidate, "Start invalidation.");
|
||||
// Add an invalidation reference to all invalidated IonScripts to indicate
|
||||
// to the traversal which frames have been invalidated.
|
||||
bool anyInvalidation = false;
|
||||
for (size_t i = 0; i < invalid.length(); i++) {
|
||||
JSScript *script = invalid[i].script;
|
||||
if (script->hasIonScript()) {
|
||||
const types::CompilerOutput &co = invalid[i];
|
||||
JS_ASSERT(co.isValid());
|
||||
if (co.isIon()) {
|
||||
JS_ASSERT(co.script->hasIonScript() && co.out.ion == co.script->ionScript());
|
||||
IonSpew(IonSpew_Invalidate, " Invalidate %s:%u, IonScript %p",
|
||||
script->filename, script->lineno, script->ion);
|
||||
script->ion->incref();
|
||||
co.script->filename, co.script->lineno, co.out.ion);
|
||||
|
||||
// Cause the IonScript to be invalidated in InvalidateActivation.
|
||||
co.out.ion->incref();
|
||||
anyInvalidation = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (!anyInvalidation) {
|
||||
IonSpew(IonSpew_Invalidate, " No IonScript invalidation.");
|
||||
return;
|
||||
}
|
||||
|
||||
for (IonActivationIterator iter(fop->runtime()); iter.more(); ++iter)
|
||||
InvalidateActivation(fop, iter.top(), false);
|
||||
|
||||
@ -1361,8 +1373,9 @@ ion::Invalidate(JSContext *cx, JSScript *script, bool resetUses)
|
||||
{
|
||||
JS_ASSERT(script->hasIonScript());
|
||||
|
||||
Vector<types::RecompileInfo> scripts(cx);
|
||||
if (!scripts.append(types::RecompileInfo(script)))
|
||||
Vector<types::CompilerOutput> scripts(cx);
|
||||
types::CompilerOutput co(script);
|
||||
if (!scripts.append(co))
|
||||
return false;
|
||||
|
||||
Invalidate(cx->runtime->defaultFreeOp(), scripts, resetUses);
|
||||
|
@ -251,7 +251,7 @@ IonExecStatus Cannon(JSContext *cx, StackFrame *fp);
|
||||
IonExecStatus SideCannon(JSContext *cx, StackFrame *fp, jsbytecode *pc);
|
||||
|
||||
// Walk the stack and invalidate active Ion frames for the invalid scripts.
|
||||
void Invalidate(FreeOp *fop, const Vector<types::RecompileInfo> &invalid, bool resetUses = true);
|
||||
void Invalidate(FreeOp *fop, const Vector<types::CompilerOutput> &invalid, bool resetUses = true);
|
||||
bool Invalidate(JSContext *cx, JSScript *script, bool resetUses = true);
|
||||
|
||||
void MarkFromIon(JSCompartment *comp, Value *vp);
|
||||
|
@ -71,8 +71,9 @@ InvokeFunction(JSContext *cx, JSFunction *fun, uint32 argc, Value *argv, Value *
|
||||
if (fun->isInterpreted() && !fun->script()->canIonCompile()) {
|
||||
JSScript *script = GetTopIonJSScript(cx);
|
||||
if (script->hasIonScript() && ++script->ion->slowCallCount >= js_IonOptions.slowCallLimit) {
|
||||
Vector<types::RecompileInfo> scripts(cx);
|
||||
if (!scripts.append(types::RecompileInfo(script)))
|
||||
Vector<types::CompilerOutput> scripts(cx);
|
||||
types::CompilerOutput co(script);
|
||||
if (!scripts.append(co))
|
||||
return false;
|
||||
|
||||
Invalidate(cx->runtime->defaultFreeOp(), scripts);
|
||||
|
@ -601,6 +601,11 @@ JSCompartment::sweep(FreeOp *fop, bool releaseTypes)
|
||||
JSScript *script = i.get<JSScript>();
|
||||
script->clearAnalysis();
|
||||
}
|
||||
|
||||
if (types.constrainedOutputs) {
|
||||
fop->delete_(types.constrainedOutputs);
|
||||
types.constrainedOutputs = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
{
|
||||
|
@ -1467,7 +1467,9 @@ TypeSet::getKnownTypeTag(JSContext *cx)
|
||||
bool empty = flags == 0 && baseObjectCount() == 0;
|
||||
JS_ASSERT_IF(empty, type == JSVAL_TYPE_UNKNOWN);
|
||||
|
||||
if (cx->compartment->types.compiledInfo.script && (empty || type != JSVAL_TYPE_UNKNOWN)) {
|
||||
if (cx->compartment->types.compiledInfo.compilerOutput(cx)->script &&
|
||||
(empty || type != JSVAL_TYPE_UNKNOWN))
|
||||
{
|
||||
add(cx, cx->typeLifoAlloc().new_<TypeConstraintFreezeTypeTag>(
|
||||
cx->compartment->types.compiledInfo), false);
|
||||
}
|
||||
@ -1845,6 +1847,8 @@ TypeCompartment::init(JSContext *cx)
|
||||
{
|
||||
PodZero(this);
|
||||
|
||||
compiledInfo.outputIndex = RecompileInfo::NoCompilerRunning;
|
||||
|
||||
if (cx && cx->getRunOptions() & JSOPTION_TYPE_INFERENCE) {
|
||||
#ifdef JS_METHODJIT
|
||||
JSC::MacroAssembler masm;
|
||||
@ -2041,7 +2045,7 @@ void
|
||||
TypeCompartment::processPendingRecompiles(FreeOp *fop)
|
||||
{
|
||||
/* Steal the list of scripts to recompile, else we will try to recursively recompile them. */
|
||||
Vector<RecompileInfo> *pending = pendingRecompiles;
|
||||
Vector<CompilerOutput> *pending = pendingRecompiles;
|
||||
pendingRecompiles = NULL;
|
||||
|
||||
JS_ASSERT(!pending->empty());
|
||||
@ -2051,11 +2055,14 @@ TypeCompartment::processPendingRecompiles(FreeOp *fop)
|
||||
mjit::ExpandInlineFrames(compartment());
|
||||
|
||||
for (unsigned i = 0; i < pending->length(); i++) {
|
||||
const RecompileInfo &info = (*pending)[i];
|
||||
mjit::JITScript *jit = info.script->getJIT(info.constructing, info.barriers);
|
||||
if (jit && jit->chunkDescriptor(info.chunkIndex).chunk) {
|
||||
mjit::Recompiler::clearStackReferences(fop, info.script);
|
||||
jit->destroyChunk(fop, info.chunkIndex);
|
||||
const CompilerOutput &info = (*pending)[i];
|
||||
JS_ASSERT(info.isValid());
|
||||
if (info.isJM()) {
|
||||
mjit::JITScript *jit = info.out.mjit;
|
||||
if (jit && jit->chunkDescriptor(info.chunkIndex).chunk) {
|
||||
mjit::Recompiler::clearStackReferences(fop, info.script);
|
||||
jit->destroyChunk(fop, info.chunkIndex);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -2133,14 +2140,17 @@ TypeCompartment::nukeTypes(FreeOp *fop)
|
||||
}
|
||||
|
||||
void
|
||||
TypeCompartment::addPendingRecompile(JSContext *cx, const RecompileInfo &info)
|
||||
TypeCompartment::addPendingRecompile(JSContext *cx, CompilerOutput &co)
|
||||
{
|
||||
if (!co.isValid())
|
||||
return;
|
||||
|
||||
#ifdef JS_METHODJIT
|
||||
mjit::JITScript *jit = info.script->getJIT(info.constructing, info.barriers);
|
||||
bool hasJITCode = jit && jit->chunkDescriptor(info.chunkIndex).chunk;
|
||||
|
||||
mjit::JITScript *jit = co.script->getJIT(co.constructing, co.barriers);
|
||||
bool hasJITCode = jit && jit->chunkDescriptor(co.chunkIndex).chunk;
|
||||
|
||||
# if defined(JS_ION)
|
||||
hasJITCode |= !!info.script->hasIonScript();
|
||||
hasJITCode |= !!co.script->hasIonScript();
|
||||
# endif
|
||||
|
||||
if (!hasJITCode) {
|
||||
@ -2149,31 +2159,35 @@ TypeCompartment::addPendingRecompile(JSContext *cx, const RecompileInfo &info)
|
||||
}
|
||||
|
||||
if (!pendingRecompiles) {
|
||||
pendingRecompiles = cx->new_< Vector<RecompileInfo> >(cx);
|
||||
pendingRecompiles = cx->new_< Vector<CompilerOutput> >(cx);
|
||||
if (!pendingRecompiles) {
|
||||
cx->compartment->types.setPendingNukeTypes(cx);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
for (unsigned i = 0; i < pendingRecompiles->length(); i++) {
|
||||
if (info == (*pendingRecompiles)[i])
|
||||
return;
|
||||
}
|
||||
|
||||
if (!pendingRecompiles->append(info)) {
|
||||
if (!pendingRecompiles->append(co)) {
|
||||
cx->compartment->types.setPendingNukeTypes(cx);
|
||||
return;
|
||||
}
|
||||
|
||||
co.invalidate();
|
||||
#endif
|
||||
}
|
||||
|
||||
void
|
||||
TypeCompartment::addPendingRecompile(JSContext *cx, const RecompileInfo &info)
|
||||
{
|
||||
addPendingRecompile(cx, (*constrainedOutputs)[info.outputIndex]);
|
||||
}
|
||||
|
||||
void
|
||||
TypeCompartment::addPendingRecompile(JSContext *cx, JSScript *script, jsbytecode *pc)
|
||||
{
|
||||
#ifdef JS_METHODJIT
|
||||
RecompileInfo info;
|
||||
CompilerOutput info;
|
||||
info.script = script;
|
||||
info.isIonFlag = false;
|
||||
|
||||
for (int constructing = 0; constructing <= 1; constructing++) {
|
||||
for (int barriers = 0; barriers <= 1; barriers++) {
|
||||
@ -2181,13 +2195,16 @@ TypeCompartment::addPendingRecompile(JSContext *cx, JSScript *script, jsbytecode
|
||||
info.constructing = constructing;
|
||||
info.barriers = barriers;
|
||||
info.chunkIndex = jit->chunkIndex(pc);
|
||||
info.out.mjit = jit;
|
||||
addPendingRecompile(cx, info);
|
||||
}
|
||||
}
|
||||
}
|
||||
# ifdef JS_ION
|
||||
if (script->hasIonScript())
|
||||
addPendingRecompile(cx, RecompileInfo(script));
|
||||
if (script->hasIonScript()) {
|
||||
info = CompilerOutput(script);
|
||||
addPendingRecompile(cx, info);
|
||||
}
|
||||
# endif
|
||||
#endif
|
||||
}
|
||||
|
@ -28,6 +28,14 @@ namespace js {
|
||||
|
||||
class CallObject;
|
||||
|
||||
namespace mjit {
|
||||
struct JITScript;
|
||||
}
|
||||
|
||||
namespace ion {
|
||||
struct IonScript;
|
||||
}
|
||||
|
||||
namespace types {
|
||||
|
||||
/* Type set entry for either a JSObject with singleton type or a non-singleton TypeObject. */
|
||||
@ -1003,31 +1011,47 @@ typedef HashMap<ObjectTableKey,ObjectTableEntry,ObjectTableKey,SystemAllocPolicy
|
||||
struct AllocationSiteKey;
|
||||
typedef HashMap<AllocationSiteKey,ReadBarriered<TypeObject>,AllocationSiteKey,SystemAllocPolicy> AllocationSiteTable;
|
||||
|
||||
struct RecompileInfo
|
||||
/*
|
||||
* Information about the result of the compilation of a script. This structure
|
||||
* stored in the TypeCompartment is indexed by the RecompileInfo. This
|
||||
* indirection enable the invalidation of all constraints related to the same
|
||||
* compilation. The compiler output is built by the AutoEnterCompilation.
|
||||
*/
|
||||
struct CompilerOutput
|
||||
{
|
||||
JSScript *script;
|
||||
bool isIonFlag : 1;
|
||||
bool constructing : 1;
|
||||
bool barriers : 1;
|
||||
uint32_t chunkIndex:30;
|
||||
uint32_t chunkIndex:29;
|
||||
|
||||
/* Result of the compilation */
|
||||
union {
|
||||
mjit::JITScript *mjit;
|
||||
ion::IonScript *ion;
|
||||
} out;
|
||||
|
||||
CompilerOutput();
|
||||
explicit CompilerOutput(JSScript *script, bool isIonFlag = true);
|
||||
|
||||
bool isJM() const { return !isIonFlag; }
|
||||
bool isIon() const { return isIonFlag; }
|
||||
bool isValid() const;
|
||||
|
||||
void invalidate() {
|
||||
out.ion = NULL;
|
||||
}
|
||||
};
|
||||
|
||||
struct RecompileInfo
|
||||
{
|
||||
static const uint32_t NoCompilerRunning = uint32_t(-1);
|
||||
uint32_t outputIndex;
|
||||
|
||||
bool operator == (const RecompileInfo &o) const {
|
||||
return script == o.script
|
||||
&& constructing == o.constructing
|
||||
&& barriers == o.barriers
|
||||
&& chunkIndex == o.chunkIndex;
|
||||
}
|
||||
|
||||
RecompileInfo()
|
||||
{
|
||||
}
|
||||
|
||||
explicit RecompileInfo(JSScript *script)
|
||||
: script(script),
|
||||
constructing(false),
|
||||
barriers(false),
|
||||
chunkIndex(0)
|
||||
{
|
||||
return outputIndex == o.outputIndex;
|
||||
}
|
||||
CompilerOutput *compilerOutput(JSContext *cx) const;
|
||||
};
|
||||
|
||||
/* Type information for a compartment. */
|
||||
@ -1064,8 +1088,11 @@ struct TypeCompartment
|
||||
/* Number of scripts in this compartment. */
|
||||
unsigned scriptCount;
|
||||
|
||||
/* Valid & Invalid script referenced by type constraints. */
|
||||
Vector<CompilerOutput> *constrainedOutputs;
|
||||
|
||||
/* Pending recompilations to perform before execution of JIT code can resume. */
|
||||
Vector<RecompileInfo> *pendingRecompiles;
|
||||
Vector<CompilerOutput> *pendingRecompiles;
|
||||
|
||||
/*
|
||||
* Number of recompilation events and inline frame expansions that have
|
||||
@ -1136,6 +1163,7 @@ struct TypeCompartment
|
||||
void setPendingNukeTypesNoReport();
|
||||
|
||||
/* Mark a script as needing recompilation once inference has finished. */
|
||||
void addPendingRecompile(JSContext *cx, CompilerOutput &co);
|
||||
void addPendingRecompile(JSContext *cx, const RecompileInfo &info);
|
||||
void addPendingRecompile(JSContext *cx, JSScript *script, jsbytecode *pc);
|
||||
|
||||
|
@ -21,13 +21,63 @@
|
||||
#ifndef jsinferinlines_h___
|
||||
#define jsinferinlines_h___
|
||||
|
||||
namespace js {
|
||||
namespace types {
|
||||
|
||||
/////////////////////////////////////////////////////////////////////
|
||||
// CompilerOutput & RecompileInfo
|
||||
/////////////////////////////////////////////////////////////////////
|
||||
|
||||
inline
|
||||
CompilerOutput::CompilerOutput()
|
||||
: script(script),
|
||||
isIonFlag(false),
|
||||
constructing(false),
|
||||
barriers(false),
|
||||
chunkIndex(false)
|
||||
{
|
||||
out.mjit = NULL;
|
||||
}
|
||||
|
||||
inline
|
||||
CompilerOutput::CompilerOutput(JSScript *script, bool isIonFlag)
|
||||
: script(script),
|
||||
isIonFlag(isIonFlag),
|
||||
constructing(false),
|
||||
barriers(false),
|
||||
chunkIndex(false)
|
||||
{
|
||||
out.mjit = NULL;
|
||||
#ifdef JS_METHODJIT
|
||||
if (isJM() && script->hasJITInfo())
|
||||
out.mjit = script->getJIT(constructing, barriers);
|
||||
#endif
|
||||
if (isIon() && script->hasIonScript())
|
||||
out.ion = script->ionScript();
|
||||
}
|
||||
|
||||
inline bool
|
||||
CompilerOutput::isValid() const
|
||||
{
|
||||
#ifdef JS_METHODJIT
|
||||
if (isJM() && out.mjit != NULL && script->getJIT(constructing, barriers) == out.mjit)
|
||||
return true;
|
||||
#endif
|
||||
if (isIon() && script->hasIonScript() && script->ionScript() == out.ion)
|
||||
return true;
|
||||
return false;
|
||||
}
|
||||
|
||||
inline CompilerOutput*
|
||||
RecompileInfo::compilerOutput(JSContext *cx) const
|
||||
{
|
||||
return &(*cx->compartment->types.constrainedOutputs)[outputIndex];
|
||||
}
|
||||
|
||||
/////////////////////////////////////////////////////////////////////
|
||||
// Types
|
||||
/////////////////////////////////////////////////////////////////////
|
||||
|
||||
namespace js {
|
||||
namespace types {
|
||||
|
||||
/* static */ inline Type
|
||||
Type::ObjectType(JSObject *obj)
|
||||
{
|
||||
@ -243,25 +293,62 @@ struct AutoEnterTypeInference
|
||||
*/
|
||||
struct AutoEnterCompilation
|
||||
{
|
||||
JSContext *cx;
|
||||
RecompileInfo &info;
|
||||
enum Compiler { JM, Ion };
|
||||
Compiler mode;
|
||||
|
||||
AutoEnterCompilation(JSContext *cx, JSScript *script, bool constructing, unsigned chunkIndex)
|
||||
: info(cx->compartment->types.compiledInfo)
|
||||
AutoEnterCompilation(JSContext *cx, Compiler mode)
|
||||
: cx(cx),
|
||||
info(cx->compartment->types.compiledInfo),
|
||||
mode(mode)
|
||||
{
|
||||
JS_ASSERT(!info.script);
|
||||
info.script = script;
|
||||
info.constructing = constructing;
|
||||
info.barriers = cx->compartment->needsBarrier();
|
||||
info.chunkIndex = chunkIndex;
|
||||
JS_ASSERT(info.outputIndex == RecompileInfo::NoCompilerRunning);
|
||||
}
|
||||
|
||||
bool init(JSScript *script, bool constructing, unsigned chunkIndex)
|
||||
{
|
||||
CompilerOutput co;
|
||||
co.script = script;
|
||||
co.constructing = constructing;
|
||||
co.barriers = cx->compartment->needsBarrier();
|
||||
co.chunkIndex = chunkIndex;
|
||||
|
||||
TypeCompartment &types = cx->compartment->types;
|
||||
if (!types.constrainedOutputs) {
|
||||
types.constrainedOutputs = cx->new_< Vector<CompilerOutput> >(cx);
|
||||
if (!types.constrainedOutputs) {
|
||||
types.setPendingNukeTypes(cx);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
info.outputIndex = cx->compartment->types.constrainedOutputs->length();
|
||||
// I hope we GC before we reach 64k of compilation attempts.
|
||||
if (info.outputIndex >= RecompileInfo::NoCompilerRunning)
|
||||
return false;
|
||||
|
||||
if (!cx->compartment->types.constrainedOutputs->append(co))
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
~AutoEnterCompilation()
|
||||
{
|
||||
JS_ASSERT(info.script);
|
||||
info.script = NULL;
|
||||
info.constructing = false;
|
||||
info.barriers = false;
|
||||
info.chunkIndex = 0;
|
||||
CompilerOutput *co = info.compilerOutput(cx);
|
||||
#ifdef JS_METHODJIT
|
||||
if (mode == JM) {
|
||||
co->isIonFlag = false;
|
||||
if (co->script->hasJITInfo())
|
||||
co->out.mjit = co->script->getJIT(co->constructing, co->barriers);
|
||||
}
|
||||
#endif
|
||||
if (mode == Ion) {
|
||||
co->isIonFlag = true;
|
||||
if (co->script->hasIonScript())
|
||||
co->out.ion = co->script->ionScript();
|
||||
}
|
||||
info.outputIndex = RecompileInfo::NoCompilerRunning;
|
||||
}
|
||||
};
|
||||
|
||||
|
@ -506,7 +506,11 @@ mjit::Compiler::performCompilation()
|
||||
JS_ASSERT(cx->compartment->activeInference);
|
||||
|
||||
{
|
||||
types::AutoEnterCompilation enter(cx, outerScript, isConstructing, chunkIndex);
|
||||
types::AutoEnterCompilation enter(cx, types::AutoEnterCompilation::JM);
|
||||
if (!enter.init(outerScript, isConstructing, chunkIndex)) {
|
||||
js_ReportOutOfMemory(cx);
|
||||
return Compile_Error;
|
||||
}
|
||||
|
||||
CHECK_STATUS(checkAnalysis(outerScript));
|
||||
if (inlining())
|
||||
|
Loading…
Reference in New Issue
Block a user