mirror of
https://github.com/mozilla/gecko-dev.git
synced 2024-10-30 21:55:31 +00:00
merge mozilla-inbound to mozilla-central a=merge
This commit is contained in:
commit
f8ac7262e0
@ -223,7 +223,7 @@ let OutputGenerator = {
|
||||
},
|
||||
|
||||
_getOutputName: function _getOutputName(aName) {
|
||||
return aName.replace(' ', '');
|
||||
return aName.replace(/\s/g, '');
|
||||
},
|
||||
|
||||
roleRuleMap: {
|
||||
|
@ -848,7 +848,8 @@ PivotContext.prototype = {
|
||||
hints.push(hint);
|
||||
} else if (aAccessible.actionCount > 0) {
|
||||
hints.push({
|
||||
string: Utils.AccRetrieval.getStringRole(aAccessible.role) + '-hint'
|
||||
string: Utils.AccRetrieval.getStringRole(
|
||||
aAccessible.role).replace(/\s/g, '') + '-hint'
|
||||
});
|
||||
}
|
||||
});
|
||||
|
@ -43,6 +43,9 @@
|
||||
accOrElmOrID: 'nested_link3',
|
||||
expectedHints: [{string: 'link-hint'}, {string: 'pushbutton-hint'},
|
||||
"Double tap and hold to activate"]
|
||||
}, {
|
||||
accOrElmOrID: 'menuitemradio',
|
||||
expectedHints: [{string: 'radiomenuitem-hint'}]
|
||||
}];
|
||||
|
||||
// Test hints.
|
||||
@ -80,6 +83,7 @@
|
||||
<a href="#" id="link_with_hint_override" aria-moz-hint="Tap and hold to get to menu">I am a special link</a>
|
||||
<button id="button_with_default_hint">Toggle</button>
|
||||
<button id="button_with_hint_override" aria-moz-hint="Tap and hold to activate">Special</button>
|
||||
<span id="menuitemradio" role="menuitemradio">Item 1</span>
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
|
@ -36,6 +36,7 @@ tier_UPDATE_PACKAGING = update-packaging
|
||||
tier_PRETTY_UPDATE_PACKAGING = pretty-update-packaging
|
||||
tier_UPLOAD_SYMBOLS = uploadsymbols
|
||||
tier_UPLOAD = upload
|
||||
tier_SDK = sdk
|
||||
|
||||
# Automation build steps. Everything in MOZ_AUTOMATION_TIERS also gets used in
|
||||
# TIERS for mach display. As such, the MOZ_AUTOMATION_TIERS are roughly sorted
|
||||
@ -55,6 +56,7 @@ moz_automation_symbols = \
|
||||
L10N_CHECK \
|
||||
PRETTY_L10N_CHECK \
|
||||
UPLOAD \
|
||||
SDK \
|
||||
$(NULL)
|
||||
MOZ_AUTOMATION_TIERS := $(foreach sym,$(moz_automation_symbols),$(if $(filter 1,$(MOZ_AUTOMATION_$(sym))),$(tier_$(sym))))
|
||||
|
||||
@ -76,15 +78,17 @@ automation/upload: automation/package
|
||||
automation/upload: automation/package-tests
|
||||
automation/upload: automation/buildsymbols
|
||||
automation/upload: automation/update-packaging
|
||||
automation/upload: automation/sdk
|
||||
|
||||
# automation/{pretty-}package should depend on build (which is implicit due to
|
||||
# the way client.mk invokes automation/build), but buildsymbols changes the
|
||||
# binaries/libs, and that's what we package/test.
|
||||
automation/pretty-package: automation/buildsymbols
|
||||
|
||||
# The installer and packager both run stage-package, and may conflict
|
||||
# The installer, sdk and packager all run stage-package, and may conflict
|
||||
# with each other.
|
||||
automation/installer: automation/package
|
||||
automation/sdk: automation/installer automation/package
|
||||
|
||||
# The 'pretty' versions of targets run before the regular ones to avoid
|
||||
# conflicts in writing to the same files.
|
||||
|
@ -17,6 +17,7 @@ mk_add_options "export MOZ_AUTOMATION_INSTALLER=${MOZ_AUTOMATION_INSTALLER-0}"
|
||||
mk_add_options "export MOZ_AUTOMATION_UPDATE_PACKAGING=${MOZ_AUTOMATION_UPDATE_PACKAGING-0}"
|
||||
mk_add_options "export MOZ_AUTOMATION_UPLOAD=${MOZ_AUTOMATION_UPLOAD-1}"
|
||||
mk_add_options "export MOZ_AUTOMATION_UPLOAD_SYMBOLS=${MOZ_AUTOMATION_UPLOAD_SYMBOLS-0}"
|
||||
mk_add_options "export MOZ_AUTOMATION_SDK=${MOZ_AUTOMATION_SDK-0}"
|
||||
|
||||
# If we are also building with MOZ_PKG_PRETTYNAMES, set the corresponding
|
||||
# stages.
|
||||
|
@ -252,16 +252,16 @@ SourceBuffer::Remove(double aStart, double aEnd, ErrorResult& aRv)
|
||||
aRv.Throw(NS_ERROR_DOM_INVALID_STATE_ERR);
|
||||
return;
|
||||
}
|
||||
if (mUpdating) {
|
||||
aRv.Throw(NS_ERROR_DOM_INVALID_STATE_ERR);
|
||||
return;
|
||||
}
|
||||
if (IsNaN(mMediaSource->Duration()) ||
|
||||
aStart < 0 || aStart > mMediaSource->Duration() ||
|
||||
aEnd <= aStart || IsNaN(aEnd)) {
|
||||
aRv.Throw(NS_ERROR_DOM_INVALID_ACCESS_ERR);
|
||||
return;
|
||||
}
|
||||
if (mUpdating) {
|
||||
aRv.Throw(NS_ERROR_DOM_INVALID_STATE_ERR);
|
||||
return;
|
||||
}
|
||||
if (mMediaSource->ReadyState() == MediaSourceReadyState::Ended) {
|
||||
mMediaSource->SetReadyState(MediaSourceReadyState::Open);
|
||||
}
|
||||
|
@ -40,5 +40,5 @@ interface SourceBuffer : EventTarget {
|
||||
[Throws]
|
||||
void abort();
|
||||
[Throws]
|
||||
void remove(double start, double end);
|
||||
void remove(double start, unrestricted double end);
|
||||
};
|
||||
|
@ -138,14 +138,18 @@ public:
|
||||
sink->Close();
|
||||
|
||||
RefPtr<ID2D1BitmapBrush> brush;
|
||||
rt->CreateBitmapBrush(mOldSurfBitmap, D2D1::BitmapBrushProperties(), D2D1::BrushProperties(), byRef(brush));
|
||||
HRESULT hr = rt->CreateBitmapBrush(mOldSurfBitmap, D2D1::BitmapBrushProperties(), D2D1::BrushProperties(), byRef(brush));
|
||||
if (FAILED(hr)) {
|
||||
gfxCriticalError(CriticalLog::DefaultOptions(false)) << "[D2D] CreateBitmapBrush failure " << hexa(hr);
|
||||
return;
|
||||
}
|
||||
|
||||
rt->FillGeometry(invClippedArea, brush);
|
||||
}
|
||||
|
||||
private:
|
||||
|
||||
DrawTargetD2D *mDT;
|
||||
DrawTargetD2D *mDT;
|
||||
|
||||
// If we have an operator unbound by the source, this will contain a bitmap
|
||||
// with the old dest surface data.
|
||||
@ -308,7 +312,12 @@ DrawTargetD2D::GetBitmapForSurface(SourceSurface *aSurface,
|
||||
|
||||
D2D1_BITMAP_PROPERTIES props =
|
||||
D2D1::BitmapProperties(D2DPixelFormat(srcSurf->GetFormat()));
|
||||
mRT->CreateBitmap(D2D1::SizeU(UINT32(sourceRect.width), UINT32(sourceRect.height)), data, stride, props, byRef(bitmap));
|
||||
HRESULT hr = mRT->CreateBitmap(D2D1::SizeU(UINT32(sourceRect.width), UINT32(sourceRect.height)), data, stride, props, byRef(bitmap));
|
||||
if (FAILED(hr)) {
|
||||
IntSize size(sourceRect.width, sourceRect.height);
|
||||
gfxCriticalError(CriticalLog::DefaultOptions(Factory::ReasonableSurfaceSize(size))) << "[D2D] 1CreateBitmap failure " << size << " Code: " << hexa(hr);
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
// subtract the integer part leaving the fractional part
|
||||
aSource.x -= (uint32_t)aSource.x;
|
||||
@ -2415,14 +2424,17 @@ DrawTargetD2D::CreateBrushForPattern(const Pattern &aPattern, Float aAlpha)
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
mRT->CreateBitmapBrush(bitmap,
|
||||
|
||||
HRESULT hr = mRT->CreateBitmapBrush(bitmap,
|
||||
D2D1::BitmapBrushProperties(D2DExtend(pat->mExtendMode),
|
||||
D2DExtend(pat->mExtendMode),
|
||||
D2DFilter(pat->mFilter)),
|
||||
D2D1::BrushProperties(aAlpha, D2DMatrix(mat)),
|
||||
byRef(bmBrush));
|
||||
|
||||
if (FAILED(hr)) {
|
||||
gfxCriticalError() << "[D2D] 1CreateBitmapBrush failure: " << hexa(hr);
|
||||
return nullptr;
|
||||
}
|
||||
return bmBrush.forget();
|
||||
}
|
||||
|
||||
|
@ -827,7 +827,7 @@ DrawTargetD2D1::Init(const IntSize &aSize, SurfaceFormat aFormat)
|
||||
hr = mDC->CreateBitmap(D2DIntSize(aSize), nullptr, 0, props, (ID2D1Bitmap1**)byRef(mTempBitmap));
|
||||
|
||||
if (FAILED(hr)) {
|
||||
gfxCriticalError() << "[D2D1.1] failed to create new TempBitmap " << aSize << " Code: " << hexa(hr);
|
||||
gfxCriticalError(CriticalLog::DefaultOptions(Factory::ReasonableSurfaceSize(aSize))) << "[D2D1.1] failed to create new TempBitmap " << aSize << " Code: " << hexa(hr);
|
||||
return false;
|
||||
}
|
||||
|
||||
@ -965,7 +965,13 @@ DrawTargetD2D1::FinalizeDrawing(CompositionOp aOp, const Pattern &aPattern)
|
||||
}
|
||||
|
||||
RefPtr<ID2D1Bitmap> tmpBitmap;
|
||||
mDC->CreateBitmap(D2DIntSize(mSize), D2D1::BitmapProperties(D2DPixelFormat(mFormat)), byRef(tmpBitmap));
|
||||
HRESULT hr = mDC->CreateBitmap(D2DIntSize(mSize), D2D1::BitmapProperties(D2DPixelFormat(mFormat)), byRef(tmpBitmap));
|
||||
if (FAILED(hr)) {
|
||||
gfxCriticalError(CriticalLog::DefaultOptions(Factory::ReasonableSurfaceSize(mSize))) << "[D2D1.1] 5CreateBitmap failure " << mSize << " Code: " << hexa(hr);
|
||||
// For now, crash in this scenario; this should happen because tmpBitmap is
|
||||
// null and CopyFromBitmap call below dereferences it.
|
||||
// return;
|
||||
}
|
||||
|
||||
// This flush is important since the copy method will not know about the context drawing to the surface.
|
||||
// We also need to pop all the clips to make sure any drawn content will have made it to the final bitmap.
|
||||
|
@ -34,6 +34,7 @@ namespace JS {
|
||||
class JS_PUBLIC_API(ProfilingFrameIterator)
|
||||
{
|
||||
JSRuntime *rt_;
|
||||
uint32_t sampleBufferGen_;
|
||||
js::Activation *activation_;
|
||||
|
||||
// When moving past a JitActivation, we need to save the prevJitTop
|
||||
@ -68,6 +69,10 @@ class JS_PUBLIC_API(ProfilingFrameIterator)
|
||||
|
||||
void settle();
|
||||
|
||||
bool hasSampleBufferGen() const {
|
||||
return sampleBufferGen_ != UINT32_MAX;
|
||||
}
|
||||
|
||||
public:
|
||||
struct RegisterState
|
||||
{
|
||||
@ -77,7 +82,8 @@ class JS_PUBLIC_API(ProfilingFrameIterator)
|
||||
void *lr;
|
||||
};
|
||||
|
||||
ProfilingFrameIterator(JSRuntime *rt, const RegisterState &state);
|
||||
ProfilingFrameIterator(JSRuntime *rt, const RegisterState &state,
|
||||
uint32_t sampleBufferGen = UINT32_MAX);
|
||||
~ProfilingFrameIterator();
|
||||
void operator++();
|
||||
bool done() const { return !activation_; }
|
||||
@ -117,6 +123,18 @@ class JS_PUBLIC_API(ProfilingFrameIterator)
|
||||
bool isJit() const;
|
||||
};
|
||||
|
||||
/**
|
||||
* After each sample run, this method should be called with the latest sample
|
||||
* buffer generation, and the lapCount. It will update corresponding fields on
|
||||
* JSRuntime.
|
||||
*
|
||||
* See fields |profilerSampleBufferGen|, |profilerSampleBufferLapCount| on
|
||||
* JSRuntime for documentation about what these values are used for.
|
||||
*/
|
||||
JS_FRIEND_API(void)
|
||||
UpdateJSRuntimeProfilerSampleBufferGen(JSRuntime *runtime, uint32_t generation,
|
||||
uint32_t lapCount);
|
||||
|
||||
} // namespace JS
|
||||
|
||||
#endif /* js_ProfilingFrameIterator_h */
|
||||
|
@ -2203,7 +2203,7 @@ js::LookupAsmJSModuleInCache(ExclusiveContext *cx,
|
||||
|
||||
uint32_t srcStart = parser.pc->maybeFunction->pn_body->pn_pos.begin;
|
||||
uint32_t srcBodyStart = parser.tokenStream.currentToken().pos.end;
|
||||
bool strict = parser.pc->sc->strict && !parser.pc->sc->hasExplicitUseStrict();
|
||||
bool strict = parser.pc->sc->strict() && !parser.pc->sc->hasExplicitUseStrict();
|
||||
|
||||
// usesSignalHandlers will be clobbered when deserializing
|
||||
ScopedJSDeletePtr<AsmJSModule> module(
|
||||
|
@ -1548,7 +1548,7 @@ class MOZ_STACK_CLASS ModuleCompiler
|
||||
// "use strict" should be added to the source if we are in an implicit
|
||||
// strict context, see also comment above addUseStrict in
|
||||
// js::FunctionToString.
|
||||
bool strict = parser_.pc->sc->strict && !parser_.pc->sc->hasExplicitUseStrict();
|
||||
bool strict = parser_.pc->sc->strict() && !parser_.pc->sc->hasExplicitUseStrict();
|
||||
module_ = cx_->new_<AsmJSModule>(parser_.ss, srcStart, srcBodyStart, strict,
|
||||
cx_->canUseSignalHandlers());
|
||||
if (!module_)
|
||||
|
@ -65,6 +65,14 @@ class SplayTree
|
||||
return !root;
|
||||
}
|
||||
|
||||
T *maybeLookup(const T &v)
|
||||
{
|
||||
if (!root)
|
||||
return nullptr;
|
||||
Node *last = lookup(v);
|
||||
return (C::compare(v, last->item) == 0) ? &(last->item) : nullptr;
|
||||
}
|
||||
|
||||
bool contains(const T &v, T *res)
|
||||
{
|
||||
if (!root)
|
||||
|
@ -247,9 +247,9 @@ UpdateDepth(ExclusiveContext *cx, BytecodeEmitter *bce, ptrdiff_t target)
|
||||
static bool
|
||||
CheckStrictOrSloppy(BytecodeEmitter *bce, JSOp op)
|
||||
{
|
||||
if (IsCheckStrictOp(op) && !bce->sc->strict)
|
||||
if (IsCheckStrictOp(op) && !bce->sc->strict())
|
||||
return false;
|
||||
if (IsCheckSloppyOp(op) && bce->sc->strict)
|
||||
if (IsCheckSloppyOp(op) && bce->sc->strict())
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
@ -1527,11 +1527,11 @@ StrictifySetNameOp(JSOp op, BytecodeEmitter *bce)
|
||||
{
|
||||
switch (op) {
|
||||
case JSOP_SETNAME:
|
||||
if (bce->sc->strict)
|
||||
if (bce->sc->strict())
|
||||
op = JSOP_STRICTSETNAME;
|
||||
break;
|
||||
case JSOP_SETGNAME:
|
||||
if (bce->sc->strict)
|
||||
if (bce->sc->strict())
|
||||
op = JSOP_STRICTSETGNAME;
|
||||
break;
|
||||
default:;
|
||||
@ -1664,7 +1664,7 @@ TryConvertFreeName(BytecodeEmitter *bce, ParseNode *pn)
|
||||
// worth the trouble for doubly-nested eval code. So we conservatively
|
||||
// approximate. If the outer eval code is strict, then this eval code will
|
||||
// be: thus, don't optimize if we're compiling strict code inside an eval.
|
||||
if (bce->insideEval && bce->sc->strict)
|
||||
if (bce->insideEval && bce->sc->strict())
|
||||
return false;
|
||||
|
||||
JSOp op;
|
||||
@ -2233,7 +2233,7 @@ BytecodeEmitter::reportStrictModeError(ParseNode *pn, unsigned errorNumber, ...)
|
||||
|
||||
va_list args;
|
||||
va_start(args, errorNumber);
|
||||
bool result = tokenStream()->reportStrictModeErrorNumberVA(pos.begin, sc->strict,
|
||||
bool result = tokenStream()->reportStrictModeErrorNumberVA(pos.begin, sc->strict(),
|
||||
errorNumber, args);
|
||||
va_end(args);
|
||||
return result;
|
||||
@ -2458,7 +2458,7 @@ EmitPropIncDec(ExclusiveContext *cx, ParseNode *pn, BytecodeEmitter *bce)
|
||||
return false;
|
||||
}
|
||||
|
||||
JSOp setOp = bce->sc->strict ? JSOP_STRICTSETPROP : JSOP_SETPROP;
|
||||
JSOp setOp = bce->sc->strict() ? JSOP_STRICTSETPROP : JSOP_SETPROP;
|
||||
if (!EmitAtomOp(cx, pn->pn_kid, setOp, bce)) // N? N+1
|
||||
return false;
|
||||
if (post && Emit1(cx, bce, JSOP_POP) < 0) // RESULT
|
||||
@ -2581,7 +2581,7 @@ EmitElemIncDec(ExclusiveContext *cx, ParseNode *pn, BytecodeEmitter *bce)
|
||||
return false;
|
||||
}
|
||||
|
||||
JSOp setOp = bce->sc->strict ? JSOP_STRICTSETELEM : JSOP_SETELEM;
|
||||
JSOp setOp = bce->sc->strict() ? JSOP_STRICTSETELEM : JSOP_SETELEM;
|
||||
if (!EmitElemOpBase(cx, bce, setOp)) // N? N+1
|
||||
return false;
|
||||
if (post && Emit1(cx, bce, JSOP_POP) < 0) // RESULT
|
||||
@ -3443,7 +3443,7 @@ EmitDestructuringLHS(ExclusiveContext *cx, BytecodeEmitter *bce, ParseNode *targ
|
||||
return false;
|
||||
if (Emit1(cx, bce, JSOP_SWAP) < 0)
|
||||
return false;
|
||||
JSOp setOp = bce->sc->strict ? JSOP_STRICTSETPROP : JSOP_SETPROP;
|
||||
JSOp setOp = bce->sc->strict() ? JSOP_STRICTSETPROP : JSOP_SETPROP;
|
||||
if (!EmitAtomOp(cx, target, setOp, bce))
|
||||
return false;
|
||||
break;
|
||||
@ -3454,7 +3454,7 @@ EmitDestructuringLHS(ExclusiveContext *cx, BytecodeEmitter *bce, ParseNode *targ
|
||||
// See the comment at `case PNK_DOT:` above. This case,
|
||||
// `[a[x]] = [b]`, is handled much the same way. The JSOP_SWAP
|
||||
// is emitted by EmitElemOperands.
|
||||
JSOp setOp = bce->sc->strict ? JSOP_STRICTSETELEM : JSOP_SETELEM;
|
||||
JSOp setOp = bce->sc->strict() ? JSOP_STRICTSETELEM : JSOP_SETELEM;
|
||||
if (!EmitElemOp(cx, target, setOp, bce))
|
||||
return false;
|
||||
break;
|
||||
@ -4215,7 +4215,7 @@ EmitAssignment(ExclusiveContext *cx, BytecodeEmitter *bce, ParseNode *lhs, JSOp
|
||||
break;
|
||||
case PNK_DOT:
|
||||
{
|
||||
JSOp setOp = bce->sc->strict ? JSOP_STRICTSETPROP : JSOP_SETPROP;
|
||||
JSOp setOp = bce->sc->strict() ? JSOP_STRICTSETPROP : JSOP_SETPROP;
|
||||
if (!EmitIndexOp(cx, setOp, atomIndex, bce))
|
||||
return false;
|
||||
break;
|
||||
@ -4226,7 +4226,7 @@ EmitAssignment(ExclusiveContext *cx, BytecodeEmitter *bce, ParseNode *lhs, JSOp
|
||||
break;
|
||||
case PNK_ELEM:
|
||||
{
|
||||
JSOp setOp = bce->sc->strict ? JSOP_STRICTSETELEM : JSOP_SETELEM;
|
||||
JSOp setOp = bce->sc->strict() ? JSOP_STRICTSETELEM : JSOP_SETELEM;
|
||||
if (Emit1(cx, bce, setOp) < 0)
|
||||
return false;
|
||||
break;
|
||||
@ -5408,7 +5408,7 @@ EmitFunc(ExclusiveContext *cx, BytecodeEmitter *bce, ParseNode *pn)
|
||||
|
||||
if (outersc->isFunctionBox() && outersc->asFunctionBox()->mightAliasLocals())
|
||||
funbox->setMightAliasLocals(); // inherit mightAliasLocals from parent
|
||||
MOZ_ASSERT_IF(outersc->strict, funbox->strict);
|
||||
MOZ_ASSERT_IF(outersc->strict(), funbox->strictScript);
|
||||
|
||||
// Inherit most things (principals, version, etc) from the parent.
|
||||
Rooted<JSScript*> parent(cx, bce->script);
|
||||
@ -6034,7 +6034,7 @@ EmitStatement(ExclusiveContext *cx, BytecodeEmitter *bce, ParseNode *pn)
|
||||
// later in the script, because such statements are misleading.
|
||||
const char *directive = nullptr;
|
||||
if (atom == cx->names().useStrict) {
|
||||
if (!bce->sc->strict)
|
||||
if (!bce->sc->strictScript)
|
||||
directive = js_useStrict_str;
|
||||
} else if (atom == cx->names().useAsm) {
|
||||
if (bce->sc->isFunctionBox()) {
|
||||
@ -6076,14 +6076,14 @@ EmitDelete(ExclusiveContext *cx, BytecodeEmitter *bce, ParseNode *pn)
|
||||
break;
|
||||
case PNK_DOT:
|
||||
{
|
||||
JSOp delOp = bce->sc->strict ? JSOP_STRICTDELPROP : JSOP_DELPROP;
|
||||
JSOp delOp = bce->sc->strict() ? JSOP_STRICTDELPROP : JSOP_DELPROP;
|
||||
if (!EmitPropOp(cx, pn2, delOp, bce))
|
||||
return false;
|
||||
break;
|
||||
}
|
||||
case PNK_ELEM:
|
||||
{
|
||||
JSOp delOp = bce->sc->strict ? JSOP_STRICTDELELEM : JSOP_DELELEM;
|
||||
JSOp delOp = bce->sc->strict() ? JSOP_STRICTDELELEM : JSOP_DELELEM;
|
||||
if (!EmitElemOp(cx, pn2, delOp, bce))
|
||||
return false;
|
||||
break;
|
||||
@ -6559,6 +6559,103 @@ EmitConditionalExpression(ExclusiveContext *cx, BytecodeEmitter *bce, Conditiona
|
||||
return SetSrcNoteOffset(cx, bce, noteIndex, 0, jmp - beq);
|
||||
}
|
||||
|
||||
static bool
|
||||
EmitPropertyList(ExclusiveContext *cx, BytecodeEmitter *bce, ParseNode *pn,
|
||||
MutableHandlePlainObject objp, PropListType type)
|
||||
{
|
||||
for (ParseNode *propdef = pn->pn_head; propdef; propdef = propdef->pn_next) {
|
||||
if (!UpdateSourceCoordNotes(cx, bce, propdef->pn_pos.begin))
|
||||
return false;
|
||||
|
||||
// Handle __proto__: v specially because *only* this form, and no other
|
||||
// involving "__proto__", performs [[Prototype]] mutation.
|
||||
if (propdef->isKind(PNK_MUTATEPROTO)) {
|
||||
MOZ_ASSERT(type == ObjectLiteral);
|
||||
if (!EmitTree(cx, bce, propdef->pn_kid))
|
||||
return false;
|
||||
objp.set(nullptr);
|
||||
if (!Emit1(cx, bce, JSOP_MUTATEPROTO))
|
||||
return false;
|
||||
continue;
|
||||
}
|
||||
|
||||
/* Emit an index for t[2] for later consumption by JSOP_INITELEM. */
|
||||
ParseNode *key = propdef->pn_left;
|
||||
bool isIndex = false;
|
||||
if (key->isKind(PNK_NUMBER)) {
|
||||
if (!EmitNumberOp(cx, key->pn_dval, bce))
|
||||
return false;
|
||||
isIndex = true;
|
||||
} else if (key->isKind(PNK_OBJECT_PROPERTY_NAME) || key->isKind(PNK_STRING)) {
|
||||
// EmitClass took care of constructor already.
|
||||
if (type == ClassBody && key->pn_atom == cx->names().constructor)
|
||||
continue;
|
||||
|
||||
// The parser already checked for atoms representing indexes and
|
||||
// used PNK_NUMBER instead, but also watch for ids which TI treats
|
||||
// as indexes for simpliciation of downstream analysis.
|
||||
jsid id = NameToId(key->pn_atom->asPropertyName());
|
||||
if (id != IdToTypeId(id)) {
|
||||
if (!EmitTree(cx, bce, key))
|
||||
return false;
|
||||
isIndex = true;
|
||||
}
|
||||
} else {
|
||||
MOZ_ASSERT(key->isKind(PNK_COMPUTED_NAME));
|
||||
if (!EmitTree(cx, bce, key->pn_kid))
|
||||
return false;
|
||||
isIndex = true;
|
||||
}
|
||||
|
||||
/* Emit code for the property initializer. */
|
||||
if (!EmitTree(cx, bce, propdef->pn_right))
|
||||
return false;
|
||||
|
||||
JSOp op = propdef->getOp();
|
||||
MOZ_ASSERT(op == JSOP_INITPROP ||
|
||||
op == JSOP_INITPROP_GETTER ||
|
||||
op == JSOP_INITPROP_SETTER);
|
||||
|
||||
if (op == JSOP_INITPROP_GETTER || op == JSOP_INITPROP_SETTER)
|
||||
objp.set(nullptr);
|
||||
|
||||
if (isIndex) {
|
||||
objp.set(nullptr);
|
||||
switch (op) {
|
||||
case JSOP_INITPROP: op = JSOP_INITELEM; break;
|
||||
case JSOP_INITPROP_GETTER: op = JSOP_INITELEM_GETTER; break;
|
||||
case JSOP_INITPROP_SETTER: op = JSOP_INITELEM_SETTER; break;
|
||||
default: MOZ_CRASH("Invalid op");
|
||||
}
|
||||
if (Emit1(cx, bce, op) < 0)
|
||||
return false;
|
||||
} else {
|
||||
MOZ_ASSERT(key->isKind(PNK_OBJECT_PROPERTY_NAME) || key->isKind(PNK_STRING));
|
||||
|
||||
jsatomid index;
|
||||
if (!bce->makeAtomIndex(key->pn_atom, &index))
|
||||
return false;
|
||||
|
||||
if (objp) {
|
||||
MOZ_ASSERT(!objp->inDictionaryMode());
|
||||
Rooted<jsid> id(cx, AtomToId(key->pn_atom));
|
||||
RootedValue undefinedValue(cx, UndefinedValue());
|
||||
if (!NativeDefineProperty(cx, objp, id, undefinedValue, nullptr, nullptr,
|
||||
JSPROP_ENUMERATE))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
if (objp->inDictionaryMode())
|
||||
objp.set(nullptr);
|
||||
}
|
||||
|
||||
if (!EmitIndex32(cx, op, index, bce))
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/*
|
||||
* Using MOZ_NEVER_INLINE in here is a workaround for llvm.org/pr14047. See
|
||||
* the comment on EmitSwitch.
|
||||
@ -6590,91 +6687,8 @@ EmitObject(ExclusiveContext *cx, BytecodeEmitter *bce, ParseNode *pn)
|
||||
return false;
|
||||
}
|
||||
|
||||
for (ParseNode *propdef = pn->pn_head; propdef; propdef = propdef->pn_next) {
|
||||
if (!UpdateSourceCoordNotes(cx, bce, propdef->pn_pos.begin))
|
||||
return false;
|
||||
|
||||
// Handle __proto__: v specially because *only* this form, and no other
|
||||
// involving "__proto__", performs [[Prototype]] mutation.
|
||||
if (propdef->isKind(PNK_MUTATEPROTO)) {
|
||||
if (!EmitTree(cx, bce, propdef->pn_kid))
|
||||
return false;
|
||||
obj = nullptr;
|
||||
if (!Emit1(cx, bce, JSOP_MUTATEPROTO))
|
||||
return false;
|
||||
continue;
|
||||
}
|
||||
|
||||
/* Emit an index for t[2] for later consumption by JSOP_INITELEM. */
|
||||
ParseNode *key = propdef->pn_left;
|
||||
bool isIndex = false;
|
||||
if (key->isKind(PNK_NUMBER)) {
|
||||
if (!EmitNumberOp(cx, key->pn_dval, bce))
|
||||
return false;
|
||||
isIndex = true;
|
||||
} else if (key->isKind(PNK_OBJECT_PROPERTY_NAME) || key->isKind(PNK_STRING)) {
|
||||
// The parser already checked for atoms representing indexes and
|
||||
// used PNK_NUMBER instead, but also watch for ids which TI treats
|
||||
// as indexes for simpliciation of downstream analysis.
|
||||
jsid id = NameToId(key->pn_atom->asPropertyName());
|
||||
if (id != IdToTypeId(id)) {
|
||||
if (!EmitTree(cx, bce, key))
|
||||
return false;
|
||||
isIndex = true;
|
||||
}
|
||||
} else {
|
||||
MOZ_ASSERT(key->isKind(PNK_COMPUTED_NAME));
|
||||
if (!EmitTree(cx, bce, key->pn_kid))
|
||||
return false;
|
||||
isIndex = true;
|
||||
}
|
||||
|
||||
/* Emit code for the property initializer. */
|
||||
if (!EmitTree(cx, bce, propdef->pn_right))
|
||||
return false;
|
||||
|
||||
JSOp op = propdef->getOp();
|
||||
MOZ_ASSERT(op == JSOP_INITPROP ||
|
||||
op == JSOP_INITPROP_GETTER ||
|
||||
op == JSOP_INITPROP_SETTER);
|
||||
|
||||
if (op == JSOP_INITPROP_GETTER || op == JSOP_INITPROP_SETTER)
|
||||
obj = nullptr;
|
||||
|
||||
if (isIndex) {
|
||||
obj = nullptr;
|
||||
switch (op) {
|
||||
case JSOP_INITPROP: op = JSOP_INITELEM; break;
|
||||
case JSOP_INITPROP_GETTER: op = JSOP_INITELEM_GETTER; break;
|
||||
case JSOP_INITPROP_SETTER: op = JSOP_INITELEM_SETTER; break;
|
||||
default: MOZ_CRASH("Invalid op");
|
||||
}
|
||||
if (Emit1(cx, bce, op) < 0)
|
||||
return false;
|
||||
} else {
|
||||
MOZ_ASSERT(key->isKind(PNK_OBJECT_PROPERTY_NAME) || key->isKind(PNK_STRING));
|
||||
|
||||
jsatomid index;
|
||||
if (!bce->makeAtomIndex(key->pn_atom, &index))
|
||||
return false;
|
||||
|
||||
if (obj) {
|
||||
MOZ_ASSERT(!obj->inDictionaryMode());
|
||||
Rooted<jsid> id(cx, AtomToId(key->pn_atom));
|
||||
RootedValue undefinedValue(cx, UndefinedValue());
|
||||
if (!NativeDefineProperty(cx, obj, id, undefinedValue, nullptr, nullptr,
|
||||
JSPROP_ENUMERATE))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
if (obj->inDictionaryMode())
|
||||
obj = nullptr;
|
||||
}
|
||||
|
||||
if (!EmitIndex32(cx, op, index, bce))
|
||||
return false;
|
||||
}
|
||||
}
|
||||
if (!EmitPropertyList(cx, bce, pn, &obj, ObjectLiteral))
|
||||
return false;
|
||||
|
||||
if (obj) {
|
||||
/*
|
||||
@ -6865,6 +6879,120 @@ EmitDefaults(ExclusiveContext *cx, BytecodeEmitter *bce, ParseNode *pn)
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
static bool
|
||||
EmitLexicalInitialization(ExclusiveContext *cx, BytecodeEmitter *bce, ParseNode *pn,
|
||||
JSOp globalDefOp)
|
||||
{
|
||||
/*
|
||||
* This function is significantly more complicated than it needs to be.
|
||||
* In fact, it shouldn't exist at all. This should all be a
|
||||
* JSOP_INITLEXIAL. Unfortunately, toplevel lexicals are broken, and
|
||||
* are emitted as vars :(. As such, we have to do these ministrations to
|
||||
* to make sure that all works properly.
|
||||
*/
|
||||
MOZ_ASSERT(pn->isKind(PNK_NAME));
|
||||
|
||||
if (!BindNameToSlot(cx, bce, pn))
|
||||
return false;
|
||||
|
||||
jsatomid atomIndex;
|
||||
if (!MaybeEmitVarDecl(cx, bce, globalDefOp, pn, &atomIndex))
|
||||
return false;
|
||||
|
||||
if (pn->getOp() != JSOP_INITLEXICAL) {
|
||||
bool global = js_CodeSpec[pn->getOp()].format & JOF_GNAME;
|
||||
if (!EmitIndex32(cx, global ? JSOP_BINDGNAME : JSOP_BINDNAME, atomIndex, bce))
|
||||
return false;
|
||||
if (Emit1(cx, bce, JSOP_SWAP) < 0)
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!pn->pn_cookie.isFree()) {
|
||||
if (!EmitVarOp(cx, pn, pn->getOp(), bce))
|
||||
return false;
|
||||
} else {
|
||||
if (!EmitIndexOp(cx, pn->getOp(), atomIndex, bce))
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
// This follows ES6 14.5.14 (ClassDefinitionEvaluation) and ES6 14.5.15
|
||||
// (BindingClassDeclarationEvaluation).
|
||||
static bool
|
||||
EmitClass(ExclusiveContext *cx, BytecodeEmitter *bce, ParseNode *pn)
|
||||
{
|
||||
ClassNode &classNode = pn->as<ClassNode>();
|
||||
|
||||
ClassNames *names = classNode.names();
|
||||
|
||||
MOZ_ASSERT(!classNode.heritage(), "For now, no heritage expressions");
|
||||
LexicalScopeNode *innerBlock = classNode.scope();
|
||||
|
||||
ParseNode *classMethods = innerBlock->pn_expr;
|
||||
MOZ_ASSERT(classMethods->isKind(PNK_CLASSMETHODLIST));
|
||||
|
||||
ParseNode *constructor = nullptr;
|
||||
for (ParseNode *mn = classMethods->pn_head; mn; mn = mn->pn_next) {
|
||||
ClassMethod &method = mn->as<ClassMethod>();
|
||||
ParseNode &methodName = method.name();
|
||||
if (methodName.isKind(PNK_OBJECT_PROPERTY_NAME) &&
|
||||
methodName.pn_atom == cx->names().constructor)
|
||||
{
|
||||
constructor = &method.method();
|
||||
break;
|
||||
}
|
||||
}
|
||||
MOZ_ASSERT(constructor, "For now, no default constructors");
|
||||
|
||||
bool savedStrictness = bce->sc->setLocalStrictMode(true);
|
||||
|
||||
StmtInfoBCE stmtInfo(cx);
|
||||
if (!EnterBlockScope(cx, bce, &stmtInfo, innerBlock->pn_objbox, JSOP_UNINITIALIZED))
|
||||
return false;
|
||||
|
||||
if (!EmitFunc(cx, bce, constructor))
|
||||
return false;
|
||||
|
||||
if (!EmitNewInit(cx, bce, JSProto_Object))
|
||||
return false;
|
||||
|
||||
if (Emit1(cx, bce, JSOP_DUP2) < 0)
|
||||
return false;
|
||||
if (!EmitAtomOp(cx, cx->names().prototype, JSOP_INITLOCKEDPROP, bce))
|
||||
return false;
|
||||
if (!EmitAtomOp(cx, cx->names().constructor, JSOP_INITHIDDENPROP, bce))
|
||||
return false;
|
||||
|
||||
RootedPlainObject obj(cx);
|
||||
if (!EmitPropertyList(cx, bce, classMethods, &obj, ClassBody))
|
||||
return false;
|
||||
|
||||
if (Emit1(cx, bce, JSOP_POP) < 0)
|
||||
return false;
|
||||
|
||||
// That DEFCONST is never gonna be used, but use it here for logical consistency.
|
||||
ParseNode *innerName = names->innerBinding();
|
||||
if (!EmitLexicalInitialization(cx, bce, innerName, JSOP_DEFCONST))
|
||||
return false;
|
||||
|
||||
if (!LeaveNestedScope(cx, bce, &stmtInfo))
|
||||
return false;
|
||||
|
||||
ParseNode *outerName = names->outerBinding();
|
||||
if (!EmitLexicalInitialization(cx, bce, outerName, JSOP_DEFVAR))
|
||||
return false;
|
||||
|
||||
if (Emit1(cx, bce, JSOP_POP) < 0)
|
||||
return false;
|
||||
|
||||
MOZ_ALWAYS_TRUE(bce->sc->setLocalStrictMode(savedStrictness));
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool
|
||||
frontend::EmitTree(ExclusiveContext *cx, BytecodeEmitter *bce, ParseNode *pn)
|
||||
{
|
||||
@ -7312,6 +7440,10 @@ frontend::EmitTree(ExclusiveContext *cx, BytecodeEmitter *bce, ParseNode *pn)
|
||||
MOZ_ASSERT(pn->getArity() == PN_NULLARY);
|
||||
break;
|
||||
|
||||
case PNK_CLASS:
|
||||
ok = EmitClass(cx, bce, pn);
|
||||
break;
|
||||
|
||||
default:
|
||||
MOZ_ASSERT(0);
|
||||
}
|
||||
|
@ -80,6 +80,13 @@ ContainsHoistedDeclaration(ExclusiveContext *cx, ParseNode *node, bool *result)
|
||||
*result = false;
|
||||
return true;
|
||||
|
||||
// Similarly to the lexical declarations above, classes cannot add hoisted
|
||||
// declarations
|
||||
case PNK_CLASS:
|
||||
MOZ_ASSERT(node->isArity(PN_TERNARY));
|
||||
*result = false;
|
||||
return true;
|
||||
|
||||
// ContainsHoistedDeclaration is only called on nested nodes, so any
|
||||
// instance of this can't be function statements at body level. In
|
||||
// SpiderMonkey, a binding induced by a function statement is added when
|
||||
@ -405,6 +412,9 @@ ContainsHoistedDeclaration(ExclusiveContext *cx, ParseNode *node, bool *result)
|
||||
case PNK_FORIN:
|
||||
case PNK_FOROF:
|
||||
case PNK_FORHEAD:
|
||||
case PNK_CLASSMETHOD:
|
||||
case PNK_CLASSMETHODLIST:
|
||||
case PNK_CLASSNAMES:
|
||||
MOZ_CRASH("ContainsHoistedDeclaration should have indicated false on "
|
||||
"some parent node without recurring to test this node");
|
||||
|
||||
|
@ -281,6 +281,16 @@ class FullParseHandler
|
||||
return literal;
|
||||
}
|
||||
|
||||
ParseNode *newClass(ParseNode *name, ParseNode *heritage, ParseNode *methodBlock) {
|
||||
return new_<ClassNode>(name, heritage, methodBlock);
|
||||
}
|
||||
ParseNode *newClassMethodList(uint32_t begin) {
|
||||
return new_<ListNode>(PNK_CLASSMETHODLIST, TokenPos(begin, begin + 1));
|
||||
}
|
||||
ParseNode *newClassNames(ParseNode *outer, ParseNode *inner, const TokenPos &pos) {
|
||||
return new_<ClassNames>(outer, inner, pos);
|
||||
}
|
||||
|
||||
bool addPrototypeMutation(ParseNode *literal, uint32_t begin, ParseNode *expr) {
|
||||
// Object literals with mutated [[Prototype]] are non-constant so that
|
||||
// singleton objects will have Object.prototype as their [[Prototype]].
|
||||
@ -323,7 +333,7 @@ class FullParseHandler
|
||||
return true;
|
||||
}
|
||||
|
||||
bool addMethodDefinition(ParseNode *literal, ParseNode *key, ParseNode *fn, JSOp op)
|
||||
bool addObjectMethodDefinition(ParseNode *literal, ParseNode *key, ParseNode *fn, JSOp op)
|
||||
{
|
||||
MOZ_ASSERT(literal->isArity(PN_LIST));
|
||||
MOZ_ASSERT(key->isKind(PNK_NUMBER) ||
|
||||
@ -339,6 +349,22 @@ class FullParseHandler
|
||||
return true;
|
||||
}
|
||||
|
||||
bool addClassMethodDefinition(ParseNode *methodList, ParseNode *key, ParseNode *fn, JSOp op)
|
||||
{
|
||||
MOZ_ASSERT(methodList->isKind(PNK_CLASSMETHODLIST));
|
||||
MOZ_ASSERT(key->isKind(PNK_NUMBER) ||
|
||||
key->isKind(PNK_OBJECT_PROPERTY_NAME) ||
|
||||
key->isKind(PNK_STRING) ||
|
||||
key->isKind(PNK_COMPUTED_NAME));
|
||||
|
||||
// For now, there's no such thing as static methods.
|
||||
ParseNode *classMethod = new_<ClassMethod>(key, fn, op, false);
|
||||
if (!classMethod)
|
||||
return false;
|
||||
methodList->append(classMethod);
|
||||
return true;
|
||||
}
|
||||
|
||||
ParseNode *newYieldExpression(uint32_t begin, ParseNode *value, ParseNode *gen,
|
||||
JSOp op = JSOP_YIELD) {
|
||||
TokenPos pos(begin, value ? value->pn_pos.end : begin + 1);
|
||||
|
@ -268,6 +268,8 @@ PushNodeChildren(ParseNode *pn, NodeStack *stack)
|
||||
case PNK_WHILE:
|
||||
case PNK_SWITCH:
|
||||
case PNK_LETBLOCK:
|
||||
case PNK_CLASSNAMES:
|
||||
case PNK_CLASSMETHOD:
|
||||
case PNK_FOR: {
|
||||
MOZ_ASSERT(pn->isArity(PN_BINARY));
|
||||
stack->push(pn->pn_left);
|
||||
@ -381,6 +383,16 @@ PushNodeChildren(ParseNode *pn, NodeStack *stack)
|
||||
return PushResult::Recyclable;
|
||||
}
|
||||
|
||||
// classes might have an optinal node for the heritage
|
||||
case PNK_CLASS: {
|
||||
MOZ_ASSERT(pn->isArity(PN_TERNARY));
|
||||
stack->push(pn->pn_kid1);
|
||||
if (pn->pn_kid2)
|
||||
stack->push(pn->pn_kid2);
|
||||
stack->push(pn->pn_kid3);
|
||||
return PushResult::Recyclable;
|
||||
}
|
||||
|
||||
// if-statement nodes have condition and consequent children and a
|
||||
// possibly-null alternative.
|
||||
case PNK_IF: {
|
||||
@ -461,6 +473,7 @@ PushNodeChildren(ParseNode *pn, NodeStack *stack)
|
||||
case PNK_EXPORT_SPEC_LIST:
|
||||
case PNK_SEQ:
|
||||
case PNK_ARGSBODY:
|
||||
case PNK_CLASSMETHODLIST:
|
||||
return PushListNodeChildren(pn, stack);
|
||||
|
||||
// Array comprehension nodes are lists with a single child -- PNK_FOR for
|
||||
@ -630,7 +643,7 @@ Parser<FullParseHandler>::cloneParseTree(ParseNode *opn)
|
||||
|
||||
case PN_CODE:
|
||||
NULLCHECK(pn->pn_funbox = newFunctionBox(pn, opn->pn_funbox->function(), pc,
|
||||
Directives(/* strict = */ opn->pn_funbox->strict),
|
||||
Directives(/* strict = */ opn->pn_funbox->strict()),
|
||||
opn->pn_funbox->generatorKind()));
|
||||
NULLCHECK(pn->pn_body = cloneParseTree(opn->pn_body));
|
||||
pn->pn_cookie = opn->pn_cookie;
|
||||
|
@ -149,6 +149,10 @@ class UpvarCookie
|
||||
F(ARGSBODY) \
|
||||
F(SPREAD) \
|
||||
F(MUTATEPROTO) \
|
||||
F(CLASS) \
|
||||
F(CLASSMETHOD) \
|
||||
F(CLASSMETHODLIST) \
|
||||
F(CLASSNAMES) \
|
||||
\
|
||||
/* Unary operators. */ \
|
||||
F(TYPEOF) \
|
||||
@ -571,6 +575,7 @@ class ParseNode
|
||||
union {
|
||||
unsigned iflags; /* JSITER_* flags for PNK_FOR node */
|
||||
ObjectBox *objbox; /* Only for PN_BINARY_OBJ */
|
||||
bool isStatic; /* Only for PNK_CLASSMETHOD */
|
||||
};
|
||||
} binary;
|
||||
struct { /* one kid if unary */
|
||||
@ -1102,6 +1107,10 @@ struct LexicalScopeNode : public ParseNode
|
||||
pn_expr = blockNode;
|
||||
pn_blockid = blockNode->pn_blockid;
|
||||
}
|
||||
|
||||
static bool test(const ParseNode &node) {
|
||||
return node.isKind(PNK_LEXICALSCOPE);
|
||||
}
|
||||
};
|
||||
|
||||
class LabeledStatement : public ParseNode
|
||||
@ -1319,6 +1328,91 @@ struct CallSiteNode : public ListNode {
|
||||
}
|
||||
};
|
||||
|
||||
struct ClassMethod : public BinaryNode {
|
||||
/*
|
||||
* Method defintions often keep a name and function body that overlap,
|
||||
* so explicitly define the beginning and end here.
|
||||
*/
|
||||
ClassMethod(ParseNode *name, ParseNode *body, JSOp op, bool isStatic)
|
||||
: BinaryNode(PNK_CLASSMETHOD, op, TokenPos(name->pn_pos.begin, body->pn_pos.end), name, body)
|
||||
{
|
||||
pn_u.binary.isStatic = isStatic;
|
||||
}
|
||||
|
||||
static bool test(const ParseNode &node) {
|
||||
bool match = node.isKind(PNK_CLASSMETHOD);
|
||||
MOZ_ASSERT_IF(match, node.isArity(PN_BINARY));
|
||||
return match;
|
||||
}
|
||||
|
||||
ParseNode &name() const {
|
||||
return *pn_u.binary.left;
|
||||
}
|
||||
ParseNode &method() const {
|
||||
return *pn_u.binary.right;
|
||||
}
|
||||
bool isStatic() const {
|
||||
return pn_u.binary.isStatic;
|
||||
}
|
||||
};
|
||||
|
||||
struct ClassNames : public BinaryNode {
|
||||
ClassNames(ParseNode *outerBinding, ParseNode *innerBinding, const TokenPos &pos)
|
||||
: BinaryNode(PNK_CLASSNAMES, JSOP_NOP, pos, outerBinding, innerBinding)
|
||||
{
|
||||
MOZ_ASSERT(outerBinding->isKind(PNK_NAME));
|
||||
MOZ_ASSERT(innerBinding->isKind(PNK_NAME));
|
||||
MOZ_ASSERT(innerBinding->pn_atom == outerBinding->pn_atom);
|
||||
}
|
||||
|
||||
static bool test(const ParseNode &node) {
|
||||
bool match = node.isKind(PNK_CLASSNAMES);
|
||||
MOZ_ASSERT_IF(match, node.isArity(PN_BINARY));
|
||||
return match;
|
||||
}
|
||||
|
||||
/*
|
||||
* Classes require two definitions: The first "outer" binding binds the
|
||||
* class into the scope in which it was declared. the outer binding is a
|
||||
* mutable lexial binding. The second "inner" binding binds the class by
|
||||
* name inside a block in which the methods are evaulated. It is immutable,
|
||||
* giving the methods access to the static members of the class even if
|
||||
* the outer binding has been overwritten.
|
||||
*/
|
||||
ParseNode *outerBinding() const {
|
||||
return pn_u.binary.left;
|
||||
}
|
||||
ParseNode *innerBinding() const {
|
||||
return pn_u.binary.right;
|
||||
}
|
||||
};
|
||||
|
||||
struct ClassNode : public TernaryNode {
|
||||
ClassNode(ParseNode *names, ParseNode *heritage, ParseNode *methodBlock)
|
||||
: TernaryNode(PNK_CLASS, JSOP_NOP, names, heritage, methodBlock)
|
||||
{
|
||||
MOZ_ASSERT(names->is<ClassNames>());
|
||||
MOZ_ASSERT(methodBlock->is<LexicalScopeNode>());
|
||||
}
|
||||
|
||||
static bool test(const ParseNode &node) {
|
||||
bool match = node.isKind(PNK_CLASS);
|
||||
MOZ_ASSERT_IF(match, node.isArity(PN_TERNARY));
|
||||
return match;
|
||||
}
|
||||
|
||||
ClassNames *names() const {
|
||||
return &pn_kid1->as<ClassNames>();
|
||||
}
|
||||
ParseNode *heritage() const {
|
||||
return pn_kid2;
|
||||
}
|
||||
LexicalScopeNode *scope() const {
|
||||
return &pn_kid3->as<LexicalScopeNode>();
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
#ifdef DEBUG
|
||||
void DumpParseTree(ParseNode *pn, int indent = 0);
|
||||
#endif
|
||||
@ -1553,7 +1647,7 @@ enum ParseReportKind
|
||||
ParseStrictError
|
||||
};
|
||||
|
||||
enum FunctionSyntaxKind { Expression, Statement, Arrow, Method };
|
||||
enum FunctionSyntaxKind { Expression, Statement, Arrow, Method, Lazy };
|
||||
|
||||
static inline ParseNode *
|
||||
FunctionArgsList(ParseNode *fn, unsigned *numFormals)
|
||||
|
@ -754,7 +754,7 @@ Parser<ParseHandler>::reportBadReturn(Node pn, ParseReportKind kind,
|
||||
} else {
|
||||
errnum = anonerrnum;
|
||||
}
|
||||
return report(kind, pc->sc->strict, pn, errnum, name.ptr());
|
||||
return report(kind, pc->sc->strict(), pn, errnum, name.ptr());
|
||||
}
|
||||
|
||||
/*
|
||||
@ -777,7 +777,7 @@ Parser<ParseHandler>::checkStrictAssignment(Node lhs)
|
||||
if (!AtomToPrintableString(context, atom, &name))
|
||||
return false;
|
||||
|
||||
if (!report(ParseStrictError, pc->sc->strict, lhs, JSMSG_BAD_STRICT_ASSIGN, name.ptr()))
|
||||
if (!report(ParseStrictError, pc->sc->strict(), lhs, JSMSG_BAD_STRICT_ASSIGN, name.ptr()))
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
@ -800,7 +800,7 @@ Parser<ParseHandler>::checkStrictBinding(PropertyName *name, Node pn)
|
||||
JSAutoByteString bytes;
|
||||
if (!AtomToPrintableString(context, name, &bytes))
|
||||
return false;
|
||||
return report(ParseStrictError, pc->sc->strict, pn,
|
||||
return report(ParseStrictError, pc->sc->strict(), pn,
|
||||
JSMSG_BAD_BINDING, bytes.ptr());
|
||||
}
|
||||
|
||||
@ -1505,7 +1505,7 @@ Parser<ParseHandler>::defineArg(Node funcpn, HandlePropertyName name,
|
||||
JSAutoByteString bytes;
|
||||
if (!AtomToPrintableString(context, name, &bytes))
|
||||
return false;
|
||||
if (!report(ParseStrictError, pc->sc->strict, pn,
|
||||
if (!report(ParseStrictError, pc->sc->strict(), pn,
|
||||
JSMSG_DUPLICATE_FORMAL, bytes.ptr()))
|
||||
{
|
||||
return false;
|
||||
@ -1892,7 +1892,7 @@ Parser<FullParseHandler>::checkFunctionDefinition(HandlePropertyName funName,
|
||||
* statements (e.g., functions in an "if" or "while" block) are
|
||||
* dynamically bound when control flow reaches the statement.
|
||||
*/
|
||||
MOZ_ASSERT(!pc->sc->strict);
|
||||
MOZ_ASSERT(!pc->sc->strict());
|
||||
MOZ_ASSERT(pn->pn_cookie.isFree());
|
||||
if (pc->sc->isFunctionBox()) {
|
||||
FunctionBox *funbox = pc->sc->asFunctionBox();
|
||||
@ -2278,7 +2278,7 @@ Parser<SyntaxParseHandler>::finishFunctionDefinition(Node pn, FunctionBox *funbo
|
||||
for (size_t i = 0; i < numInnerFunctions; i++)
|
||||
innerFunctions[i].init(pc->innerFunctions[i]);
|
||||
|
||||
if (pc->sc->strict)
|
||||
if (pc->sc->strict())
|
||||
lazy->setStrict();
|
||||
lazy->setGeneratorKind(funbox->generatorKind());
|
||||
if (funbox->usesArguments && funbox->usesApply && funbox->usesThis)
|
||||
@ -2470,7 +2470,7 @@ Parser<FullParseHandler>::standaloneLazyFunction(HandleFunction fun, unsigned st
|
||||
if (!funpc.init(tokenStream))
|
||||
return null();
|
||||
|
||||
if (!functionArgsAndBodyGeneric(pn, fun, Normal, Statement)) {
|
||||
if (!functionArgsAndBodyGeneric(pn, fun, Normal, Lazy)) {
|
||||
MOZ_ASSERT(directives == newDirectives);
|
||||
return null();
|
||||
}
|
||||
@ -2555,8 +2555,11 @@ Parser<ParseHandler>::functionArgsAndBodyGeneric(Node pn, HandleFunction fun, Fu
|
||||
if (!body)
|
||||
return false;
|
||||
|
||||
if (fun->name() && !checkStrictBinding(fun->name(), pn))
|
||||
if (kind != Method && kind != Lazy &&
|
||||
fun->name() && !checkStrictBinding(fun->name(), pn))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if (bodyType == StatementListBody) {
|
||||
bool matched;
|
||||
@ -2574,7 +2577,7 @@ Parser<ParseHandler>::functionArgsAndBodyGeneric(Node pn, HandleFunction fun, Fu
|
||||
if (tokenStream.hadError())
|
||||
return false;
|
||||
funbox->bufEnd = pos().end;
|
||||
if (kind == Statement && !MatchOrInsertSemicolon(tokenStream))
|
||||
if ((kind == Statement || kind == Lazy) && !MatchOrInsertSemicolon(tokenStream))
|
||||
return false;
|
||||
}
|
||||
|
||||
@ -2587,7 +2590,7 @@ Parser<ParseHandler>::checkYieldNameValidity()
|
||||
{
|
||||
// In star generators and in JS >= 1.7, yield is a keyword. Otherwise in
|
||||
// strict mode, yield is a future reserved word.
|
||||
if (pc->isStarGenerator() || versionNumber() >= JSVERSION_1_7 || pc->sc->strict) {
|
||||
if (pc->isStarGenerator() || versionNumber() >= JSVERSION_1_7 || pc->sc->strict()) {
|
||||
report(ParseError, false, null(), JSMSG_RESERVED_ID, "yield");
|
||||
return false;
|
||||
}
|
||||
@ -2626,7 +2629,7 @@ Parser<ParseHandler>::functionStmt()
|
||||
|
||||
/* We forbid function statements in strict mode code. */
|
||||
if (!pc->atBodyLevel() && pc->sc->needStrictChecks() &&
|
||||
!report(ParseStrictError, pc->sc->strict, null(), JSMSG_STRICT_FUNCTION_STATEMENT))
|
||||
!report(ParseStrictError, pc->sc->strict(), null(), JSMSG_STRICT_FUNCTION_STATEMENT))
|
||||
return null();
|
||||
|
||||
return functionDef(name, Normal, Statement, generatorKind);
|
||||
@ -2779,7 +2782,7 @@ Parser<ParseHandler>::maybeParseDirective(Node list, Node pn, bool *cont)
|
||||
// We're going to be in strict mode. Note that this scope explicitly
|
||||
// had "use strict";
|
||||
pc->sc->setExplicitUseStrict();
|
||||
if (!pc->sc->strict) {
|
||||
if (!pc->sc->strict()) {
|
||||
if (pc->sc->isFunctionBox()) {
|
||||
// Request that this function be reparsed as strict.
|
||||
pc->newDirectives->setStrict();
|
||||
@ -2792,7 +2795,7 @@ Parser<ParseHandler>::maybeParseDirective(Node list, Node pn, bool *cont)
|
||||
report(ParseError, false, null(), JSMSG_DEPRECATED_OCTAL);
|
||||
return false;
|
||||
}
|
||||
pc->sc->strict = true;
|
||||
pc->sc->strictScript = true;
|
||||
}
|
||||
}
|
||||
} else if (directive == context->names().useAsm) {
|
||||
@ -3263,7 +3266,7 @@ Parser<FullParseHandler>::makeSetCall(ParseNode *pn, unsigned msg)
|
||||
pn->isOp(JSOP_SPREADEVAL) || pn->isOp(JSOP_STRICTSPREADEVAL) ||
|
||||
pn->isOp(JSOP_FUNCALL) || pn->isOp(JSOP_FUNAPPLY));
|
||||
|
||||
if (!report(ParseStrictError, pc->sc->strict, pn, msg))
|
||||
if (!report(ParseStrictError, pc->sc->strict(), pn, msg))
|
||||
return false;
|
||||
handler.markAsSetCall(pn);
|
||||
return true;
|
||||
@ -3322,7 +3325,7 @@ Parser<ParseHandler>::noteNameUse(HandlePropertyName name, Node pn)
|
||||
|
||||
template <>
|
||||
bool
|
||||
Parser<FullParseHandler>::bindDestructuringVar(BindData<FullParseHandler> *data, ParseNode *pn)
|
||||
Parser<FullParseHandler>::bindInitialized(BindData<FullParseHandler> *data, ParseNode *pn)
|
||||
{
|
||||
MOZ_ASSERT(pn->isKind(PNK_NAME));
|
||||
|
||||
@ -3382,7 +3385,7 @@ Parser<FullParseHandler>::checkDestructuringObject(BindData<FullParseHandler> *d
|
||||
report(ParseError, false, expr, JSMSG_NO_VARIABLE_NAME);
|
||||
return false;
|
||||
}
|
||||
ok = bindDestructuringVar(data, expr);
|
||||
ok = bindInitialized(data, expr);
|
||||
} else {
|
||||
ok = checkAndMarkAsAssignmentLhs(expr, KeyedDestructuringAssignment);
|
||||
}
|
||||
@ -3430,7 +3433,7 @@ Parser<FullParseHandler>::checkDestructuringArray(BindData<FullParseHandler> *da
|
||||
report(ParseError, false, target, JSMSG_NO_VARIABLE_NAME);
|
||||
return false;
|
||||
}
|
||||
ok = bindDestructuringVar(data, target);
|
||||
ok = bindInitialized(data, target);
|
||||
} else {
|
||||
ok = checkAndMarkAsAssignmentLhs(target, KeyedDestructuringAssignment);
|
||||
}
|
||||
@ -3660,7 +3663,7 @@ Parser<ParseHandler>::deprecatedLetBlockOrExpression(LetContext letContext)
|
||||
*
|
||||
* See bug 569464.
|
||||
*/
|
||||
if (!reportWithOffset(ParseStrictError, pc->sc->strict, begin,
|
||||
if (!reportWithOffset(ParseStrictError, pc->sc->strict(), begin,
|
||||
JSMSG_STRICT_CODE_LET_EXPR_STMT))
|
||||
{
|
||||
return null();
|
||||
@ -3684,7 +3687,7 @@ Parser<ParseHandler>::deprecatedLetBlockOrExpression(LetContext letContext)
|
||||
MUST_MATCH_TOKEN(TOK_RC, JSMSG_CURLY_AFTER_LET);
|
||||
|
||||
addTelemetry(JSCompartment::DeprecatedLetBlock);
|
||||
if (!report(ParseWarning, pc->sc->strict, expr, JSMSG_DEPRECATED_LET_BLOCK))
|
||||
if (!report(ParseWarning, pc->sc->strict(), expr, JSMSG_DEPRECATED_LET_BLOCK))
|
||||
return null();
|
||||
} else {
|
||||
MOZ_ASSERT(letContext == LetExpression);
|
||||
@ -3693,7 +3696,7 @@ Parser<ParseHandler>::deprecatedLetBlockOrExpression(LetContext letContext)
|
||||
return null();
|
||||
|
||||
addTelemetry(JSCompartment::DeprecatedLetExpression);
|
||||
if (!report(ParseWarning, pc->sc->strict, expr, JSMSG_DEPRECATED_LET_EXPRESSION))
|
||||
if (!report(ParseWarning, pc->sc->strict(), expr, JSMSG_DEPRECATED_LET_EXPRESSION))
|
||||
return null();
|
||||
}
|
||||
handler.setLexicalScopeBody(block, expr);
|
||||
@ -3950,136 +3953,169 @@ Parser<ParseHandler>::variables(ParseNodeKind kind, bool *psimple,
|
||||
return pn;
|
||||
}
|
||||
|
||||
template <>
|
||||
bool
|
||||
Parser<FullParseHandler>::checkAndPrepareLexical(bool isConst, const TokenPos &errorPos)
|
||||
{
|
||||
/*
|
||||
* This is a lexical declaration. We must be directly under a block per the
|
||||
* proposed ES4 specs, but not an implicit block created due to
|
||||
* 'for (let ...)'. If we pass this error test, make the enclosing
|
||||
* StmtInfoPC be our scope. Further let declarations in this block will
|
||||
* find this scope statement and use the same block object.
|
||||
*
|
||||
* If we are the first let declaration in this block (i.e., when the
|
||||
* enclosing maybe-scope StmtInfoPC isn't yet a scope statement) then
|
||||
* we also need to set pc->blockNode to be our PNK_LEXICALSCOPE.
|
||||
*/
|
||||
StmtInfoPC *stmt = pc->topStmt;
|
||||
if (stmt && (!stmt->maybeScope() || stmt->isForLetBlock)) {
|
||||
reportWithOffset(ParseError, false, errorPos.begin, JSMSG_LEXICAL_DECL_NOT_IN_BLOCK,
|
||||
isConst ? "const" : "lexical");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (stmt && stmt->isBlockScope) {
|
||||
MOZ_ASSERT(pc->staticScope == stmt->staticScope);
|
||||
} else {
|
||||
if (pc->atBodyLevel()) {
|
||||
/*
|
||||
* When bug 589199 is fixed, let variables will be stored in
|
||||
* the slots of a new scope chain object, encountered just
|
||||
* before the global object in the overall chain. This extra
|
||||
* object is present in the scope chain for all code in that
|
||||
* global, including self-hosted code. But self-hosted code
|
||||
* must be usable against *any* global object, including ones
|
||||
* with other let variables -- variables possibly placed in
|
||||
* conflicting slots. Forbid top-level let declarations to
|
||||
* prevent such conflicts from ever occurring.
|
||||
*/
|
||||
bool isGlobal = !pc->sc->isFunctionBox() && stmt == pc->topScopeStmt;
|
||||
if (options().selfHostingMode && isGlobal) {
|
||||
report(ParseError, false, null(), JSMSG_SELFHOSTED_TOP_LEVEL_LEXICAL,
|
||||
isConst ? "'const'" : "'let'");
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/*
|
||||
* Some obvious assertions here, but they may help clarify the
|
||||
* situation. This stmt is not yet a scope, so it must not be a
|
||||
* catch block (catch is a lexical scope by definition).
|
||||
*/
|
||||
MOZ_ASSERT(!stmt->isBlockScope);
|
||||
MOZ_ASSERT(stmt != pc->topScopeStmt);
|
||||
MOZ_ASSERT(stmt->type == STMT_BLOCK ||
|
||||
stmt->type == STMT_SWITCH ||
|
||||
stmt->type == STMT_TRY ||
|
||||
stmt->type == STMT_FINALLY);
|
||||
MOZ_ASSERT(!stmt->downScope);
|
||||
|
||||
/* Convert the block statement into a scope statement. */
|
||||
StaticBlockObject *blockObj = StaticBlockObject::create(context);
|
||||
if (!blockObj)
|
||||
return false;
|
||||
|
||||
ObjectBox *blockbox = newObjectBox(blockObj);
|
||||
if (!blockbox)
|
||||
return false;
|
||||
|
||||
/*
|
||||
* Insert stmt on the pc->topScopeStmt/stmtInfo.downScope linked
|
||||
* list stack, if it isn't already there. If it is there, but it
|
||||
* lacks the SIF_SCOPE flag, it must be a try, catch, or finally
|
||||
* block.
|
||||
*/
|
||||
stmt->isBlockScope = stmt->isNestedScope = true;
|
||||
stmt->downScope = pc->topScopeStmt;
|
||||
pc->topScopeStmt = stmt;
|
||||
|
||||
blockObj->initEnclosingNestedScopeFromParser(pc->staticScope);
|
||||
pc->staticScope = blockObj;
|
||||
stmt->staticScope = blockObj;
|
||||
|
||||
#ifdef DEBUG
|
||||
ParseNode *tmp = pc->blockNode;
|
||||
MOZ_ASSERT(!tmp || !tmp->isKind(PNK_LEXICALSCOPE));
|
||||
#endif
|
||||
|
||||
/* Create a new lexical scope node for these statements. */
|
||||
ParseNode *pn1 = handler.new_<LexicalScopeNode>(blockbox, pc->blockNode);
|
||||
if (!pn1)
|
||||
return false;;
|
||||
pc->blockNode = pn1;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
static StaticBlockObject *
|
||||
CurrentLexicalStaticBlock(ParseContext<FullParseHandler> *pc)
|
||||
{
|
||||
return pc->atBodyLevel() ? nullptr :
|
||||
&pc->staticScope->as<StaticBlockObject>();
|
||||
}
|
||||
|
||||
template <>
|
||||
ParseNode *
|
||||
Parser<FullParseHandler>::makeInitializedLexicalBinding(HandlePropertyName name, bool isConst,
|
||||
const TokenPos &pos)
|
||||
{
|
||||
// Handle the silliness of global and body level lexical decls.
|
||||
BindData<FullParseHandler> data(context);
|
||||
if (pc->atGlobalLevel()) {
|
||||
data.initVarOrGlobalConst(isConst ? JSOP_DEFCONST : JSOP_DEFVAR);
|
||||
} else {
|
||||
if (!checkAndPrepareLexical(isConst, pos))
|
||||
return null();
|
||||
data.initLexical(HoistVars, CurrentLexicalStaticBlock(pc), JSMSG_TOO_MANY_LOCALS, isConst);
|
||||
}
|
||||
ParseNode *dn = newBindingNode(name, pc->atGlobalLevel());
|
||||
if (!dn)
|
||||
return null();
|
||||
handler.setPosition(dn, pos);
|
||||
|
||||
if (!bindInitialized(&data, dn))
|
||||
return null();
|
||||
|
||||
return dn;
|
||||
}
|
||||
|
||||
template <>
|
||||
ParseNode *
|
||||
Parser<FullParseHandler>::lexicalDeclaration(bool isConst)
|
||||
{
|
||||
handler.disableSyntaxParser();
|
||||
|
||||
ParseNode *pn;
|
||||
if (!checkAndPrepareLexical(isConst, pos()))
|
||||
return null();
|
||||
|
||||
do {
|
||||
/*
|
||||
* This is a let declaration. We must be directly under a block per the
|
||||
* proposed ES4 specs, but not an implicit block created due to
|
||||
* 'for (let ...)'. If we pass this error test, make the enclosing
|
||||
* StmtInfoPC be our scope. Further let declarations in this block will
|
||||
* find this scope statement and use the same block object.
|
||||
*
|
||||
* If we are the first let declaration in this block (i.e., when the
|
||||
* enclosing maybe-scope StmtInfoPC isn't yet a scope statement) then
|
||||
* we also need to set pc->blockNode to be our PNK_LEXICALSCOPE.
|
||||
*/
|
||||
StmtInfoPC *stmt = pc->topStmt;
|
||||
if (stmt && (!stmt->maybeScope() || stmt->isForLetBlock)) {
|
||||
report(ParseError, false, null(), JSMSG_LEXICAL_DECL_NOT_IN_BLOCK,
|
||||
isConst ? "const" : "let");
|
||||
return null();
|
||||
}
|
||||
|
||||
if (stmt && stmt->isBlockScope) {
|
||||
MOZ_ASSERT(pc->staticScope == stmt->staticScope);
|
||||
} else {
|
||||
if (pc->atBodyLevel()) {
|
||||
/*
|
||||
* When bug 589199 is fixed, let variables will be stored in
|
||||
* the slots of a new scope chain object, encountered just
|
||||
* before the global object in the overall chain. This extra
|
||||
* object is present in the scope chain for all code in that
|
||||
* global, including self-hosted code. But self-hosted code
|
||||
* must be usable against *any* global object, including ones
|
||||
* with other let variables -- variables possibly placed in
|
||||
* conflicting slots. Forbid top-level let declarations to
|
||||
* prevent such conflicts from ever occurring.
|
||||
*/
|
||||
bool isGlobal = !pc->sc->isFunctionBox() && stmt == pc->topScopeStmt;
|
||||
if (options().selfHostingMode && isGlobal) {
|
||||
report(ParseError, false, null(), JSMSG_SELFHOSTED_TOP_LEVEL_LEXICAL,
|
||||
isConst ? "'const'" : "'let'");
|
||||
return null();
|
||||
}
|
||||
|
||||
/*
|
||||
* Parse body-level lets without a new block object. ES6 specs
|
||||
* that an execution environment's initial lexical environment
|
||||
* is the VariableEnvironment, i.e., body-level lets are in
|
||||
* the same environment record as vars.
|
||||
*
|
||||
* However, they cannot be parsed exactly as vars, as ES6
|
||||
* requires that uninitialized lets throw ReferenceError on use.
|
||||
*
|
||||
* See 8.1.1.1.6 and the note in 13.2.1.
|
||||
*
|
||||
* FIXME global-level lets are still considered vars until
|
||||
* other bugs are fixed.
|
||||
*/
|
||||
ParseNodeKind kind = PNK_LET;
|
||||
if (isGlobal)
|
||||
kind = isConst ? PNK_GLOBALCONST : PNK_VAR;
|
||||
else if (isConst)
|
||||
kind = PNK_CONST;
|
||||
pn = variables(kind);
|
||||
if (!pn)
|
||||
return null();
|
||||
pn->pn_xflags |= PNX_POPVAR;
|
||||
break;
|
||||
}
|
||||
|
||||
/*
|
||||
* Some obvious assertions here, but they may help clarify the
|
||||
* situation. This stmt is not yet a scope, so it must not be a
|
||||
* catch block (catch is a lexical scope by definition).
|
||||
*/
|
||||
MOZ_ASSERT(!stmt->isBlockScope);
|
||||
MOZ_ASSERT(stmt != pc->topScopeStmt);
|
||||
MOZ_ASSERT(stmt->type == STMT_BLOCK ||
|
||||
stmt->type == STMT_SWITCH ||
|
||||
stmt->type == STMT_TRY ||
|
||||
stmt->type == STMT_FINALLY);
|
||||
MOZ_ASSERT(!stmt->downScope);
|
||||
|
||||
/* Convert the block statement into a scope statement. */
|
||||
StaticBlockObject *blockObj = StaticBlockObject::create(context);
|
||||
if (!blockObj)
|
||||
return null();
|
||||
|
||||
ObjectBox *blockbox = newObjectBox(blockObj);
|
||||
if (!blockbox)
|
||||
return null();
|
||||
|
||||
/*
|
||||
* Insert stmt on the pc->topScopeStmt/stmtInfo.downScope linked
|
||||
* list stack, if it isn't already there. If it is there, but it
|
||||
* lacks the SIF_SCOPE flag, it must be a try, catch, or finally
|
||||
* block.
|
||||
*/
|
||||
stmt->isBlockScope = stmt->isNestedScope = true;
|
||||
stmt->downScope = pc->topScopeStmt;
|
||||
pc->topScopeStmt = stmt;
|
||||
|
||||
blockObj->initEnclosingNestedScopeFromParser(pc->staticScope);
|
||||
pc->staticScope = blockObj;
|
||||
stmt->staticScope = blockObj;
|
||||
|
||||
#ifdef DEBUG
|
||||
ParseNode *tmp = pc->blockNode;
|
||||
MOZ_ASSERT(!tmp || !tmp->isKind(PNK_LEXICALSCOPE));
|
||||
#endif
|
||||
|
||||
/* Create a new lexical scope node for these statements. */
|
||||
ParseNode *pn1 = handler.new_<LexicalScopeNode>(blockbox, pc->blockNode);
|
||||
if (!pn1)
|
||||
return null();
|
||||
pc->blockNode = pn1;
|
||||
}
|
||||
|
||||
pn = variables(isConst ? PNK_CONST : PNK_LET, nullptr,
|
||||
&pc->staticScope->as<StaticBlockObject>(), HoistVars);
|
||||
if (!pn)
|
||||
return null();
|
||||
pn->pn_xflags = PNX_POPVAR;
|
||||
} while (0);
|
||||
/*
|
||||
* Parse body-level lets without a new block object. ES6 specs
|
||||
* that an execution environment's initial lexical environment
|
||||
* is the VariableEnvironment, i.e., body-level lets are in
|
||||
* the same environment record as vars.
|
||||
*
|
||||
* However, they cannot be parsed exactly as vars, as ES6
|
||||
* requires that uninitialized lets throw ReferenceError on use.
|
||||
*
|
||||
* See 8.1.1.1.6 and the note in 13.2.1.
|
||||
*
|
||||
* FIXME global-level lets are still considered vars until
|
||||
* other bugs are fixed.
|
||||
*/
|
||||
ParseNodeKind kind = PNK_LET;
|
||||
if (pc->atGlobalLevel())
|
||||
kind = isConst ? PNK_GLOBALCONST : PNK_VAR;
|
||||
else if (isConst)
|
||||
kind = PNK_CONST;
|
||||
|
||||
ParseNode *pn = variables(kind, nullptr,
|
||||
CurrentLexicalStaticBlock(pc),
|
||||
HoistVars);
|
||||
if (!pn)
|
||||
return null();
|
||||
pn->pn_xflags = PNX_POPVAR;
|
||||
return MatchOrInsertSemicolon(tokenStream) ? pn : nullptr;
|
||||
}
|
||||
|
||||
@ -4616,7 +4652,7 @@ Parser<FullParseHandler>::forStatement()
|
||||
isForEach = true;
|
||||
addTelemetry(JSCompartment::DeprecatedForEach);
|
||||
if (versionNumber() < JSVERSION_LATEST) {
|
||||
if (!report(ParseWarning, pc->sc->strict, null(), JSMSG_DEPRECATED_FOR_EACH))
|
||||
if (!report(ParseWarning, pc->sc->strict(), null(), JSMSG_DEPRECATED_FOR_EACH))
|
||||
return null();
|
||||
}
|
||||
}
|
||||
@ -5484,7 +5520,7 @@ Parser<FullParseHandler>::withStatement()
|
||||
// construct that is forbidden in strict mode code, but doesn't even merit a
|
||||
// warning under JSOPTION_EXTRA_WARNINGS. See
|
||||
// https://bugzilla.mozilla.org/show_bug.cgi?id=514576#c1.
|
||||
if (pc->sc->strict && !report(ParseStrictError, true, null(), JSMSG_STRICT_CODE_WITH))
|
||||
if (pc->sc->strict() && !report(ParseStrictError, true, null(), JSMSG_STRICT_CODE_WITH))
|
||||
return null();
|
||||
|
||||
MUST_MATCH_TOKEN(TOK_LP, JSMSG_PAREN_BEFORE_WITH);
|
||||
@ -5787,6 +5823,80 @@ Parser<ParseHandler>::debuggerStatement()
|
||||
return handler.newDebuggerStatement(p);
|
||||
}
|
||||
|
||||
template <>
|
||||
ParseNode *
|
||||
Parser<FullParseHandler>::classStatement()
|
||||
{
|
||||
MOZ_ASSERT(tokenStream.isCurrentTokenType(TOK_CLASS));
|
||||
|
||||
TokenKind tt;
|
||||
if (!tokenStream.getToken(&tt))
|
||||
return null();
|
||||
|
||||
RootedPropertyName name(context);
|
||||
if (tt == TOK_NAME) {
|
||||
name = tokenStream.currentName();
|
||||
} else if (tt == TOK_YIELD) {
|
||||
if (!checkYieldNameValidity())
|
||||
return null();
|
||||
name = tokenStream.currentName();
|
||||
} else {
|
||||
// Class statements must have a bound name
|
||||
report(ParseError, false, null(), JSMSG_UNNAMED_CLASS_STMT);
|
||||
return null();
|
||||
}
|
||||
|
||||
if (name == context->names().let) {
|
||||
report(ParseError, false, null(), JSMSG_LET_CLASS_BINDING);
|
||||
return null();
|
||||
}
|
||||
|
||||
// Because the binding definitions keep track of their blockId, we need to
|
||||
// create at least the inner binding later. Keep track of the name's position
|
||||
// in order to provide it for the nodes created later.
|
||||
TokenPos namePos = pos();
|
||||
|
||||
MUST_MATCH_TOKEN(TOK_LC, JSMSG_CURLY_BEFORE_CLASS);
|
||||
|
||||
bool savedStrictness = setLocalStrictMode(true);
|
||||
|
||||
StmtInfoPC classStmt(context);
|
||||
ParseNode *classBlock = pushLexicalScope(&classStmt);
|
||||
if (!classBlock)
|
||||
return null();
|
||||
|
||||
ParseNode *classMethods = propertyList(ClassBody);
|
||||
if (!classMethods)
|
||||
return null();
|
||||
handler.setLexicalScopeBody(classBlock, classMethods);
|
||||
|
||||
ParseNode *innerBinding = makeInitializedLexicalBinding(name, true, namePos);
|
||||
if (!innerBinding)
|
||||
return null();
|
||||
|
||||
PopStatementPC(tokenStream, pc);
|
||||
|
||||
ParseNode *outerBinding = makeInitializedLexicalBinding(name, false, namePos);
|
||||
if (!outerBinding)
|
||||
return null();
|
||||
|
||||
ParseNode *nameNode = handler.newClassNames(outerBinding, innerBinding, namePos);
|
||||
if (!nameNode)
|
||||
return null();
|
||||
|
||||
MOZ_ALWAYS_TRUE(setLocalStrictMode(savedStrictness));
|
||||
|
||||
return handler.newClass(nameNode, null(), classBlock);
|
||||
}
|
||||
|
||||
template <>
|
||||
SyntaxParseHandler::Node
|
||||
Parser<SyntaxParseHandler>::classStatement()
|
||||
{
|
||||
JS_ALWAYS_FALSE(abortIfSyntaxParser());
|
||||
return SyntaxParseHandler::NodeFailure;
|
||||
}
|
||||
|
||||
template <typename ParseHandler>
|
||||
typename ParseHandler::Node
|
||||
Parser<ParseHandler>::statement(bool canHaveDirectives)
|
||||
@ -5854,6 +5964,11 @@ Parser<ParseHandler>::statement(bool canHaveDirectives)
|
||||
return functionStmt();
|
||||
case TOK_DEBUGGER:
|
||||
return debuggerStatement();
|
||||
case TOK_CLASS:
|
||||
if (!abortIfSyntaxParser())
|
||||
return null();
|
||||
return classStatement();
|
||||
|
||||
|
||||
/* TOK_CATCH and TOK_FINALLY are both handled in the TOK_TRY case */
|
||||
case TOK_CATCH:
|
||||
@ -6408,7 +6523,7 @@ Parser<ParseHandler>::unaryExpr(InvokedPrediction invoked)
|
||||
// Per spec, deleting any unary expression is valid -- it simply
|
||||
// returns true -- except for one case that is illegal in strict mode.
|
||||
if (handler.isName(expr)) {
|
||||
if (!report(ParseStrictError, pc->sc->strict, expr, JSMSG_DEPRECATED_DELETE_OPERAND))
|
||||
if (!report(ParseStrictError, pc->sc->strict(), expr, JSMSG_DEPRECATED_DELETE_OPERAND))
|
||||
return null();
|
||||
pc->sc->setBindingsAccessedDynamically();
|
||||
}
|
||||
@ -6817,7 +6932,7 @@ Parser<FullParseHandler>::legacyComprehensionTail(ParseNode *bodyExpr, unsigned
|
||||
pn2->pn_iflags |= JSITER_FOREACH;
|
||||
addTelemetry(JSCompartment::DeprecatedForEach);
|
||||
if (versionNumber() < JSVERSION_LATEST) {
|
||||
if (!report(ParseWarning, pc->sc->strict, pn2, JSMSG_DEPRECATED_FOR_EACH))
|
||||
if (!report(ParseWarning, pc->sc->strict(), pn2, JSMSG_DEPRECATED_FOR_EACH))
|
||||
return null();
|
||||
}
|
||||
}
|
||||
@ -7073,7 +7188,7 @@ Parser<ParseHandler>::generatorComprehensionLambda(GeneratorKind comprehensionKi
|
||||
return null();
|
||||
|
||||
// Create box for fun->object early to root it.
|
||||
Directives directives(/* strict = */ outerpc->sc->strict);
|
||||
Directives directives(/* strict = */ outerpc->sc->strict());
|
||||
FunctionBox *genFunbox = newFunctionBox(genfn, fun, outerpc, directives, comprehensionKind);
|
||||
if (!genFunbox)
|
||||
return null();
|
||||
@ -7591,7 +7706,7 @@ Parser<ParseHandler>::memberExpr(TokenKind tt, bool allowCallSyntax, InvokedPred
|
||||
if (JSAtom *atom = handler.isName(lhs)) {
|
||||
if (tt == TOK_LP && atom == context->names().eval) {
|
||||
/* Select JSOP_EVAL and flag pc as heavyweight. */
|
||||
op = pc->sc->strict ? JSOP_STRICTEVAL : JSOP_EVAL;
|
||||
op = pc->sc->strict() ? JSOP_STRICTEVAL : JSOP_EVAL;
|
||||
pc->sc->setBindingsAccessedDynamically();
|
||||
pc->sc->setHasDirectEval();
|
||||
|
||||
@ -7599,7 +7714,7 @@ Parser<ParseHandler>::memberExpr(TokenKind tt, bool allowCallSyntax, InvokedPred
|
||||
* In non-strict mode code, direct calls to eval can add
|
||||
* variables to the call object.
|
||||
*/
|
||||
if (pc->sc->isFunctionBox() && !pc->sc->strict)
|
||||
if (pc->sc->isFunctionBox() && !pc->sc->strict())
|
||||
pc->sc->asFunctionBox()->setHasExtensibleScope();
|
||||
}
|
||||
} else if (JSAtom *atom = handler.isGetProp(lhs)) {
|
||||
@ -7885,15 +8000,27 @@ Parser<ParseHandler>::computedPropertyName(Node literal)
|
||||
|
||||
template <typename ParseHandler>
|
||||
typename ParseHandler::Node
|
||||
Parser<ParseHandler>::objectLiteral()
|
||||
Parser<ParseHandler>::newPropertyListNode(PropListType type)
|
||||
{
|
||||
if (type == ClassBody)
|
||||
return handler.newClassMethodList(pos().begin);
|
||||
|
||||
MOZ_ASSERT(type == ObjectLiteral);
|
||||
return handler.newObjectLiteral(pos().begin);
|
||||
}
|
||||
|
||||
template <typename ParseHandler>
|
||||
typename ParseHandler::Node
|
||||
Parser<ParseHandler>::propertyList(PropListType type)
|
||||
{
|
||||
MOZ_ASSERT(tokenStream.isCurrentTokenType(TOK_LC));
|
||||
|
||||
Node literal = handler.newObjectLiteral(pos().begin);
|
||||
if (!literal)
|
||||
Node propList = newPropertyListNode(type);
|
||||
if (!propList)
|
||||
return null();
|
||||
|
||||
bool seenPrototypeMutation = false;
|
||||
bool seenConstructor = false;
|
||||
RootedAtom atom(context);
|
||||
for (;;) {
|
||||
TokenKind ltok;
|
||||
@ -7902,6 +8029,9 @@ Parser<ParseHandler>::objectLiteral()
|
||||
if (ltok == TOK_RC)
|
||||
break;
|
||||
|
||||
if (type == ClassBody && ltok == TOK_SEMI)
|
||||
continue;
|
||||
|
||||
bool isGenerator = false;
|
||||
if (ltok == TOK_MUL) {
|
||||
isGenerator = true;
|
||||
@ -7924,7 +8054,7 @@ Parser<ParseHandler>::objectLiteral()
|
||||
break;
|
||||
|
||||
case TOK_LB: {
|
||||
propname = computedPropertyName(literal);
|
||||
propname = computedPropertyName(propList);
|
||||
if (!propname)
|
||||
return null();
|
||||
break;
|
||||
@ -7980,7 +8110,7 @@ Parser<ParseHandler>::objectLiteral()
|
||||
if (!propname)
|
||||
return null();
|
||||
} else if (tt == TOK_LB) {
|
||||
propname = computedPropertyName(literal);
|
||||
propname = computedPropertyName(propList);
|
||||
if (!propname)
|
||||
return null();
|
||||
} else {
|
||||
@ -8017,12 +8147,30 @@ Parser<ParseHandler>::objectLiteral()
|
||||
return null();
|
||||
}
|
||||
|
||||
if (type == ClassBody) {
|
||||
if (atom == context->names().constructor) {
|
||||
if (isGenerator || op != JSOP_INITPROP) {
|
||||
report(ParseError, false, propname, JSMSG_BAD_METHOD_DEF);
|
||||
return null();
|
||||
}
|
||||
if (seenConstructor) {
|
||||
report(ParseError, false, propname, JSMSG_DUPLICATE_PROPERTY, "constructor");
|
||||
return null();
|
||||
}
|
||||
seenConstructor = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (op == JSOP_INITPROP) {
|
||||
TokenKind tt;
|
||||
if (!tokenStream.getToken(&tt))
|
||||
return null();
|
||||
|
||||
if (tt == TOK_COLON) {
|
||||
if (type == ClassBody) {
|
||||
report(ParseError, false, null(), JSMSG_BAD_METHOD_DEF);
|
||||
return null();
|
||||
}
|
||||
if (isGenerator) {
|
||||
report(ParseError, false, null(), JSMSG_BAD_PROP_ID);
|
||||
return null();
|
||||
@ -8047,13 +8195,13 @@ Parser<ParseHandler>::objectLiteral()
|
||||
// method/generator definitions, computed property name
|
||||
// versions of all of these, and shorthands do not.
|
||||
uint32_t begin = handler.getPosition(propname).begin;
|
||||
if (!handler.addPrototypeMutation(literal, begin, propexpr))
|
||||
if (!handler.addPrototypeMutation(propList, begin, propexpr))
|
||||
return null();
|
||||
} else {
|
||||
if (!handler.isConstant(propexpr))
|
||||
handler.setListFlag(literal, PNX_NONCONST);
|
||||
handler.setListFlag(propList, PNX_NONCONST);
|
||||
|
||||
if (!handler.addPropertyDefinition(literal, propname, propexpr))
|
||||
if (!handler.addPropertyDefinition(propList, propname, propexpr))
|
||||
return null();
|
||||
}
|
||||
} else if (ltok == TOK_NAME && (tt == TOK_COMMA || tt == TOK_RC)) {
|
||||
@ -8061,6 +8209,10 @@ Parser<ParseHandler>::objectLiteral()
|
||||
* Support, e.g., |var {x, y} = o| as destructuring shorthand
|
||||
* for |var {x: x, y: y} = o|, per proposed JS2/ES4 for JS1.8.
|
||||
*/
|
||||
if (type == ClassBody) {
|
||||
report(ParseError, false, null(), JSMSG_BAD_METHOD_DEF);
|
||||
return null();
|
||||
}
|
||||
if (isGenerator) {
|
||||
report(ParseError, false, null(), JSMSG_BAD_PROP_ID);
|
||||
return null();
|
||||
@ -8074,11 +8226,11 @@ Parser<ParseHandler>::objectLiteral()
|
||||
if (!nameExpr)
|
||||
return null();
|
||||
|
||||
if (!handler.addShorthand(literal, propname, nameExpr))
|
||||
if (!handler.addShorthand(propList, propname, nameExpr))
|
||||
return null();
|
||||
} else if (tt == TOK_LP) {
|
||||
tokenStream.ungetToken();
|
||||
if (!methodDefinition(literal, propname, Normal, Method,
|
||||
if (!methodDefinition(type, propList, propname, Normal, Method,
|
||||
isGenerator ? StarGenerator : NotGenerator, op)) {
|
||||
return null();
|
||||
}
|
||||
@ -8088,32 +8240,40 @@ Parser<ParseHandler>::objectLiteral()
|
||||
}
|
||||
} else {
|
||||
/* NB: Getter function in { get x(){} } is unnamed. */
|
||||
if (!methodDefinition(literal, propname, op == JSOP_INITPROP_GETTER ? Getter : Setter,
|
||||
if (!methodDefinition(type, propList, propname, op == JSOP_INITPROP_GETTER ? Getter : Setter,
|
||||
Expression, NotGenerator, op)) {
|
||||
return null();
|
||||
}
|
||||
}
|
||||
|
||||
TokenKind tt;
|
||||
if (!tokenStream.getToken(&tt))
|
||||
return null();
|
||||
if (tt == TOK_RC)
|
||||
break;
|
||||
if (tt != TOK_COMMA) {
|
||||
report(ParseError, false, null(), JSMSG_CURLY_AFTER_LIST);
|
||||
return null();
|
||||
if (type == ObjectLiteral) {
|
||||
TokenKind tt;
|
||||
if (!tokenStream.getToken(&tt))
|
||||
return null();
|
||||
if (tt == TOK_RC)
|
||||
break;
|
||||
if (tt != TOK_COMMA) {
|
||||
report(ParseError, false, null(), JSMSG_CURLY_AFTER_LIST);
|
||||
return null();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
handler.setEndPosition(literal, pos().end);
|
||||
return literal;
|
||||
// Default constructors not yet implemented. See bug 1105463
|
||||
if (type == ClassBody && !seenConstructor) {
|
||||
report(ParseError, false, null(), JSMSG_NO_CLASS_CONSTRUCTOR);
|
||||
return null();
|
||||
}
|
||||
|
||||
handler.setEndPosition(propList, pos().end);
|
||||
return propList;
|
||||
}
|
||||
|
||||
template <typename ParseHandler>
|
||||
bool
|
||||
Parser<ParseHandler>::methodDefinition(Node literal, Node propname, FunctionType type,
|
||||
FunctionSyntaxKind kind, GeneratorKind generatorKind,
|
||||
JSOp op)
|
||||
Parser<ParseHandler>::methodDefinition(PropListType listType, Node propList, Node propname,
|
||||
FunctionType type, FunctionSyntaxKind kind,
|
||||
GeneratorKind generatorKind, JSOp op)
|
||||
{
|
||||
RootedPropertyName funName(context);
|
||||
if (kind == Method && tokenStream.isCurrentTokenType(TOK_NAME))
|
||||
@ -8124,9 +8284,12 @@ Parser<ParseHandler>::methodDefinition(Node literal, Node propname, FunctionType
|
||||
Node fn = functionDef(funName, type, kind, generatorKind);
|
||||
if (!fn)
|
||||
return false;
|
||||
if (!handler.addMethodDefinition(literal, propname, fn, op))
|
||||
return false;
|
||||
return true;
|
||||
|
||||
if (listType == ClassBody)
|
||||
return handler.addClassMethodDefinition(propList, propname, fn, op);
|
||||
|
||||
MOZ_ASSERT(listType == ObjectLiteral);
|
||||
return handler.addObjectMethodDefinition(propList, propname, fn, op);
|
||||
}
|
||||
|
||||
template <typename ParseHandler>
|
||||
@ -8144,7 +8307,7 @@ Parser<ParseHandler>::primaryExpr(TokenKind tt, InvokedPrediction invoked)
|
||||
return arrayInitializer();
|
||||
|
||||
case TOK_LC:
|
||||
return objectLiteral();
|
||||
return propertyList(ObjectLiteral);
|
||||
|
||||
case TOK_LET:
|
||||
return deprecatedLetBlockOrExpression(LetExpression);
|
||||
|
@ -301,6 +301,7 @@ struct ParseContext : public GenericParseContext
|
||||
// if (cond) { function f3() { if (cond) { function f4() { } } } }
|
||||
//
|
||||
bool atBodyLevel() { return !topStmt; }
|
||||
bool atGlobalLevel() { return atBodyLevel() && !sc->isFunctionBox() && (topStmt == topScopeStmt); }
|
||||
|
||||
// True if this is the ParseContext for the body of a function created by
|
||||
// the Function constructor.
|
||||
@ -316,7 +317,7 @@ struct ParseContext : public GenericParseContext
|
||||
template <typename ParseHandler>
|
||||
inline
|
||||
Directives::Directives(ParseContext<ParseHandler> *parent)
|
||||
: strict_(parent->sc->strict),
|
||||
: strict_(parent->sc->strict()),
|
||||
asmJS_(parent->useAsmOrInsideUseAsm())
|
||||
{}
|
||||
|
||||
@ -328,6 +329,7 @@ class CompExprTransplanter;
|
||||
enum LetContext { LetExpression, LetStatement };
|
||||
enum VarContext { HoistVars, DontHoistVars };
|
||||
enum FunctionType { Getter, Setter, Normal };
|
||||
enum PropListType { ObjectLiteral, ClassBody };
|
||||
|
||||
template <typename ParseHandler>
|
||||
class Parser : private JS::AutoGCRooter, public StrictModeGetter
|
||||
@ -503,7 +505,11 @@ class Parser : private JS::AutoGCRooter, public StrictModeGetter
|
||||
return versionNumber() >= JSVERSION_1_7 || pc->isGenerator();
|
||||
}
|
||||
|
||||
virtual bool strictMode() { return pc->sc->strict; }
|
||||
virtual bool strictMode() { return pc->sc->strict(); }
|
||||
bool setLocalStrictMode(bool strict) {
|
||||
MOZ_ASSERT(tokenStream.debugHasNoLookahead());
|
||||
return pc->sc->setLocalStrictMode(strict);
|
||||
}
|
||||
|
||||
const ReadOnlyCompileOptions &options() const {
|
||||
return tokenStream.options();
|
||||
@ -547,6 +553,7 @@ class Parser : private JS::AutoGCRooter, public StrictModeGetter
|
||||
Node throwStatement();
|
||||
Node tryStatement();
|
||||
Node debuggerStatement();
|
||||
Node classStatement();
|
||||
|
||||
Node lexicalDeclaration(bool isConst);
|
||||
Node letDeclarationOrBlock();
|
||||
@ -569,8 +576,8 @@ class Parser : private JS::AutoGCRooter, public StrictModeGetter
|
||||
Node parenExprOrGeneratorComprehension();
|
||||
Node exprInParens();
|
||||
|
||||
bool methodDefinition(Node literal, Node propname, FunctionType type, FunctionSyntaxKind kind,
|
||||
GeneratorKind generatorKind, JSOp Op);
|
||||
bool methodDefinition(PropListType listType, Node propList, Node propname, FunctionType type,
|
||||
FunctionSyntaxKind kind, GeneratorKind generatorKind, JSOp Op);
|
||||
|
||||
/*
|
||||
* Additional JS parsers.
|
||||
@ -651,16 +658,21 @@ class Parser : private JS::AutoGCRooter, public StrictModeGetter
|
||||
Node pushLexicalScope(Handle<StaticBlockObject*> blockObj, StmtInfoPC *stmt);
|
||||
Node pushLetScope(Handle<StaticBlockObject*> blockObj, StmtInfoPC *stmt);
|
||||
bool noteNameUse(HandlePropertyName name, Node pn);
|
||||
Node objectLiteral();
|
||||
Node computedPropertyName(Node literal);
|
||||
Node arrayInitializer();
|
||||
Node newRegExp();
|
||||
|
||||
Node propertyList(PropListType type);
|
||||
Node newPropertyListNode(PropListType type);
|
||||
|
||||
bool checkAndPrepareLexical(bool isConst, const TokenPos &errorPos);
|
||||
Node makeInitializedLexicalBinding(HandlePropertyName name, bool isConst, const TokenPos &pos);
|
||||
|
||||
Node newBindingNode(PropertyName *name, bool functionScope, VarContext varContext = HoistVars);
|
||||
bool checkDestructuring(BindData<ParseHandler> *data, Node left);
|
||||
bool checkDestructuringObject(BindData<ParseHandler> *data, Node objectPattern);
|
||||
bool checkDestructuringArray(BindData<ParseHandler> *data, Node arrayPattern);
|
||||
bool bindDestructuringVar(BindData<ParseHandler> *data, Node pn);
|
||||
bool bindInitialized(BindData<ParseHandler> *data, Node pn);
|
||||
bool bindDestructuringLHS(Node pn);
|
||||
bool makeSetCall(Node pn, unsigned msg);
|
||||
Node cloneDestructuringDefault(Node opn);
|
||||
|
@ -178,7 +178,8 @@ class SharedContext
|
||||
public:
|
||||
ExclusiveContext *const context;
|
||||
AnyContextFlags anyCxFlags;
|
||||
bool strict;
|
||||
bool strictScript;
|
||||
bool localStrict;
|
||||
bool extraWarnings;
|
||||
|
||||
// If it's function code, funbox must be non-nullptr and scopeChain must be
|
||||
@ -186,7 +187,8 @@ class SharedContext
|
||||
SharedContext(ExclusiveContext *cx, Directives directives, bool extraWarnings)
|
||||
: context(cx),
|
||||
anyCxFlags(),
|
||||
strict(directives.strict()),
|
||||
strictScript(directives.strict()),
|
||||
localStrict(false),
|
||||
extraWarnings(extraWarnings)
|
||||
{}
|
||||
|
||||
@ -208,9 +210,18 @@ class SharedContext
|
||||
|
||||
inline bool allLocalsAliased();
|
||||
|
||||
bool strict() {
|
||||
return strictScript || localStrict;
|
||||
}
|
||||
bool setLocalStrictMode(bool strict) {
|
||||
bool retVal = localStrict;
|
||||
localStrict = strict;
|
||||
return retVal;
|
||||
}
|
||||
|
||||
// JSOPTION_EXTRA_WARNINGS warnings or strict mode errors.
|
||||
bool needStrictChecks() {
|
||||
return strict || extraWarnings;
|
||||
return strict() || extraWarnings;
|
||||
}
|
||||
|
||||
bool isDotVariable(JSAtom *atom) const {
|
||||
|
@ -186,10 +186,12 @@ class SyntaxParseHandler
|
||||
void addArrayElement(Node literal, Node element) { }
|
||||
|
||||
Node newObjectLiteral(uint32_t begin) { return NodeGeneric; }
|
||||
Node newClassMethodList(uint32_t begin) { return NodeGeneric; }
|
||||
bool addPrototypeMutation(Node literal, uint32_t begin, Node expr) { return true; }
|
||||
bool addPropertyDefinition(Node literal, Node name, Node expr) { return true; }
|
||||
bool addShorthand(Node literal, Node name, Node expr) { return true; }
|
||||
bool addMethodDefinition(Node literal, Node name, Node fn, JSOp op) { return true; }
|
||||
bool addObjectMethodDefinition(Node literal, Node name, Node fn, JSOp op) { return true; }
|
||||
bool addClassMethodDefinition(Node literal, Node name, Node fn, JSOp op) { return true; }
|
||||
Node newYieldExpression(uint32_t begin, Node value, Node gen) { return NodeUnparenthesizedYieldExpr; }
|
||||
Node newYieldStarExpression(uint32_t begin, Node value, Node gen) { return NodeGeneric; }
|
||||
|
||||
|
@ -110,6 +110,7 @@
|
||||
macro(LET, "keyword 'let'") \
|
||||
macro(EXPORT, "keyword 'export'") \
|
||||
macro(IMPORT, "keyword 'import'") \
|
||||
macro(CLASS, "keyword 'class'") \
|
||||
macro(RESERVED, "reserved keyword") \
|
||||
/* reserved keywords in strict mode */ \
|
||||
macro(STRICT_RESERVED, "reserved keyword") \
|
||||
|
@ -976,8 +976,14 @@ TokenStream::putIdentInTokenbuf(const char16_t *identStart)
|
||||
bool
|
||||
TokenStream::checkForKeyword(const KeywordInfo *kw, TokenKind *ttp)
|
||||
{
|
||||
if (kw->tokentype == TOK_RESERVED)
|
||||
if (kw->tokentype == TOK_RESERVED
|
||||
#ifndef JS_HAS_CLASSES
|
||||
|| kw->tokentype == TOK_CLASS
|
||||
#endif
|
||||
)
|
||||
{
|
||||
return reportError(JSMSG_RESERVED_ID, kw->chars);
|
||||
}
|
||||
|
||||
if (kw->tokentype != TOK_STRICT_RESERVED) {
|
||||
if (kw->version <= versionNumber()) {
|
||||
|
@ -531,6 +531,11 @@ class MOZ_STACK_CLASS TokenStream
|
||||
void tell(Position *);
|
||||
void seek(const Position &pos);
|
||||
bool seek(const Position &pos, const TokenStream &other);
|
||||
#ifdef DEBUG
|
||||
inline bool debugHasNoLookahead() const {
|
||||
return lookahead == 0;
|
||||
}
|
||||
#endif
|
||||
|
||||
const char16_t *rawCharPtrAt(size_t offset) const {
|
||||
return userbuf.rawCharPtrAt(offset);
|
||||
|
@ -802,11 +802,11 @@ TypeSet::MarkTypeRoot(JSTracer *trc, TypeSet::Type *v, const char *name)
|
||||
{
|
||||
JS_ROOT_MARKING_ASSERT(trc);
|
||||
trc->setTracingName(name);
|
||||
if (v->isSingleton()) {
|
||||
if (v->isSingletonUnchecked()) {
|
||||
JSObject *obj = v->singleton();
|
||||
MarkInternal(trc, &obj);
|
||||
*v = TypeSet::ObjectType(obj);
|
||||
} else if (v->isGroup()) {
|
||||
} else if (v->isGroupUnchecked()) {
|
||||
ObjectGroup *group = v->group();
|
||||
MarkInternal(trc, &group);
|
||||
*v = TypeSet::ObjectType(group);
|
||||
|
@ -38,6 +38,7 @@ typedef JS::Handle<ScriptSourceObject*> HandleScriptSource;
|
||||
typedef JS::MutableHandle<Shape*> MutableHandleShape;
|
||||
typedef JS::MutableHandle<JSAtom*> MutableHandleAtom;
|
||||
typedef JS::MutableHandle<NativeObject*> MutableHandleNativeObject;
|
||||
typedef JS::MutableHandle<PlainObject*> MutableHandlePlainObject;
|
||||
|
||||
typedef JS::Rooted<NativeObject*> RootedNativeObject;
|
||||
typedef JS::Rooted<Shape*> RootedShape;
|
||||
|
24
js/src/jit-test/tests/debug/execution-observability-06.js
Normal file
24
js/src/jit-test/tests/debug/execution-observability-06.js
Normal file
@ -0,0 +1,24 @@
|
||||
// Test that OSR respect debuggeeness.
|
||||
|
||||
var g = newGlobal();
|
||||
var dbg = new Debugger(g);
|
||||
|
||||
g.eval("" + function f(c) {
|
||||
if (c == 0)
|
||||
return;
|
||||
if (c == 2)
|
||||
debugger;
|
||||
f(c-1);
|
||||
acc = 0;
|
||||
for (var i = 0; i < 100; i++)
|
||||
acc += i;
|
||||
});
|
||||
|
||||
var log = "";
|
||||
dbg.onDebuggerStatement = function (frame) {
|
||||
frame.onPop = function f() { log += "p"; }
|
||||
};
|
||||
|
||||
g.eval("f(2)");
|
||||
|
||||
assertEq(log, "p");
|
@ -277,7 +277,7 @@ BaselineCompiler::compile()
|
||||
return Method_Error;
|
||||
|
||||
JitcodeGlobalEntry::BaselineEntry entry;
|
||||
entry.init(code->raw(), code->rawEnd(), script, str);
|
||||
entry.init(code, code->raw(), code->rawEnd(), script, str);
|
||||
|
||||
JitcodeGlobalTable *globalTable = cx->runtime()->jitRuntime()->getJitcodeGlobalTable();
|
||||
if (!globalTable->addEntry(entry, cx->runtime())) {
|
||||
@ -1974,6 +1974,18 @@ BaselineCompiler::emit_JSOP_INITPROP()
|
||||
return emitOpIC(compiler.getStub(&stubSpace_));
|
||||
}
|
||||
|
||||
bool
|
||||
BaselineCompiler::emit_JSOP_INITLOCKEDPROP()
|
||||
{
|
||||
return emit_JSOP_INITPROP();
|
||||
}
|
||||
|
||||
bool
|
||||
BaselineCompiler::emit_JSOP_INITHIDDENPROP()
|
||||
{
|
||||
return emit_JSOP_INITPROP();
|
||||
}
|
||||
|
||||
typedef bool (*NewbornArrayPushFn)(JSContext *, HandleObject, const Value &);
|
||||
static const VMFunction NewbornArrayPushInfo = FunctionInfo<NewbornArrayPushFn>(NewbornArrayPush);
|
||||
|
||||
|
@ -103,6 +103,8 @@ namespace jit {
|
||||
_(JSOP_INITELEM_INC) \
|
||||
_(JSOP_MUTATEPROTO) \
|
||||
_(JSOP_INITPROP) \
|
||||
_(JSOP_INITLOCKEDPROP) \
|
||||
_(JSOP_INITHIDDENPROP) \
|
||||
_(JSOP_INITPROP_GETTER) \
|
||||
_(JSOP_INITPROP_SETTER) \
|
||||
_(JSOP_ARRAYPUSH) \
|
||||
|
@ -8246,6 +8246,8 @@ DoSetPropFallback(JSContext *cx, BaselineFrame *frame, ICSetProp_Fallback *stub_
|
||||
op == JSOP_SETGNAME ||
|
||||
op == JSOP_STRICTSETGNAME ||
|
||||
op == JSOP_INITPROP ||
|
||||
op == JSOP_INITLOCKEDPROP ||
|
||||
op == JSOP_INITHIDDENPROP ||
|
||||
op == JSOP_SETALIASEDVAR ||
|
||||
op == JSOP_INITALIASEDLEXICAL);
|
||||
|
||||
@ -8279,10 +8281,14 @@ DoSetPropFallback(JSContext *cx, BaselineFrame *frame, ICSetProp_Fallback *stub_
|
||||
return false;
|
||||
}
|
||||
|
||||
if (op == JSOP_INITPROP) {
|
||||
MOZ_ASSERT(obj->is<PlainObject>());
|
||||
if (!NativeDefineProperty(cx, obj.as<PlainObject>(), id, rhs,
|
||||
nullptr, nullptr, JSPROP_ENUMERATE))
|
||||
if (op == JSOP_INITPROP ||
|
||||
op == JSOP_INITLOCKEDPROP ||
|
||||
op == JSOP_INITHIDDENPROP)
|
||||
{
|
||||
MOZ_ASSERT(obj->is<JSFunction>() || obj->is<PlainObject>());
|
||||
unsigned propAttrs = GetInitDataPropAttrs(op);
|
||||
if (!NativeDefineProperty(cx, obj.as<NativeObject>(), id, rhs,
|
||||
nullptr, nullptr, propAttrs))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
@ -172,7 +172,7 @@ jit::EnterBaselineAtBranch(JSContext *cx, InterpreterFrame *fp, jsbytecode *pc)
|
||||
// Skip debug breakpoint/trap handler, the interpreter already handled it
|
||||
// for the current op.
|
||||
if (fp->isDebuggee()) {
|
||||
MOZ_ASSERT(baseline->hasDebugInstrumentation());
|
||||
MOZ_RELEASE_ASSERT(baseline->hasDebugInstrumentation());
|
||||
data.jitcode += MacroAssembler::ToggledCallSize(data.jitcode);
|
||||
}
|
||||
|
||||
@ -294,6 +294,29 @@ jit::CanEnterBaselineAtBranch(JSContext *cx, InterpreterFrame *fp, bool newType)
|
||||
if (!CheckFrame(fp))
|
||||
return Method_CantCompile;
|
||||
|
||||
// This check is needed in the following corner case. Consider a function h,
|
||||
//
|
||||
// function h(x) {
|
||||
// h(false);
|
||||
// if (!x)
|
||||
// return;
|
||||
// for (var i = 0; i < N; i++)
|
||||
// /* do stuff */
|
||||
// }
|
||||
//
|
||||
// Suppose h is not yet compiled in baseline and is executing in the
|
||||
// interpreter. Let this interpreter frame be f_older. The debugger marks
|
||||
// f_older as isDebuggee. At the point of the recursive call h(false), h is
|
||||
// compiled in baseline without debug instrumentation, pushing a baseline
|
||||
// frame f_newer. The debugger never flags f_newer as isDebuggee, and never
|
||||
// recompiles h. When the recursive call returns and execution proceeds to
|
||||
// the loop, the interpreter attempts to OSR into baseline. Since h is
|
||||
// already compiled in baseline, execution jumps directly into baseline
|
||||
// code. This is incorrect as h's baseline script does not have debug
|
||||
// instrumentation.
|
||||
if (fp->isDebuggee() && !Debugger::ensureExecutionObservabilityOfOsrFrame(cx, fp))
|
||||
return Method_Error;
|
||||
|
||||
RootedScript script(cx, fp->script());
|
||||
return CanEnterBaselineJIT(cx, script, fp);
|
||||
}
|
||||
|
@ -4635,15 +4635,15 @@ CodeGenerator::visitMutateProto(LMutateProto *lir)
|
||||
}
|
||||
|
||||
typedef bool(*InitPropFn)(JSContext *cx, HandleNativeObject obj,
|
||||
HandlePropertyName name, HandleValue value);
|
||||
static const VMFunction InitPropInfo =
|
||||
FunctionInfo<InitPropFn>(InitProp);
|
||||
HandlePropertyName name, HandleValue value, jsbytecode *pc);
|
||||
static const VMFunction InitPropInfo = FunctionInfo<InitPropFn>(InitProp);
|
||||
|
||||
void
|
||||
CodeGenerator::visitInitProp(LInitProp *lir)
|
||||
{
|
||||
Register objReg = ToRegister(lir->getObject());
|
||||
|
||||
pushArg(ImmPtr(lir->mir()->resumePoint()->pc()));
|
||||
pushArg(ToValue(lir, LInitProp::ValueIndex));
|
||||
pushArg(ImmGCPtr(lir->mir()->propertyName()));
|
||||
pushArg(objReg);
|
||||
@ -7497,7 +7497,7 @@ CodeGenerator::link(JSContext *cx, CompilerConstraintList *constraints)
|
||||
} else {
|
||||
// Add a dumy jitcodeGlobalTable entry.
|
||||
JitcodeGlobalEntry::DummyEntry entry;
|
||||
entry.init(code->raw(), code->rawEnd());
|
||||
entry.init(code, code->raw(), code->rawEnd());
|
||||
|
||||
// Add entry to the global table.
|
||||
JitcodeGlobalTable *globalTable = cx->runtime()->jitRuntime()->getJitcodeGlobalTable();
|
||||
|
@ -495,6 +495,13 @@ JitRuntime::Mark(JSTracer *trc)
|
||||
JitCode *code = i.get<JitCode>();
|
||||
MarkJitCodeRoot(trc, &code, "wrapper");
|
||||
}
|
||||
|
||||
// Mark all heap the jitcode global table map.
|
||||
if (trc->runtime()->hasJitRuntime() &&
|
||||
trc->runtime()->jitRuntime()->hasJitcodeGlobalTable())
|
||||
{
|
||||
trc->runtime()->jitRuntime()->getJitcodeGlobalTable()->mark(trc);
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
@ -660,7 +667,7 @@ JitCode::finalize(FreeOp *fop)
|
||||
// If this jitcode has a bytecode map, de-register it.
|
||||
if (hasBytecodeMap_) {
|
||||
MOZ_ASSERT(rt->jitRuntime()->hasJitcodeGlobalTable());
|
||||
rt->jitRuntime()->getJitcodeGlobalTable()->removeEntry(raw(), rt);
|
||||
rt->jitRuntime()->getJitcodeGlobalTable()->releaseEntry(raw(), rt);
|
||||
}
|
||||
|
||||
// Buffer can be freed at any time hereafter. Catch use-after-free bugs.
|
||||
|
@ -1730,6 +1730,8 @@ IonBuilder::inspectOpcode(JSOp op)
|
||||
return jsop_initelem_array();
|
||||
|
||||
case JSOP_INITPROP:
|
||||
case JSOP_INITLOCKEDPROP:
|
||||
case JSOP_INITHIDDENPROP:
|
||||
{
|
||||
PropertyName *name = info().getAtom(pc)->asPropertyName();
|
||||
return jsop_initprop(name);
|
||||
@ -6381,7 +6383,7 @@ IonBuilder::jsop_initprop(PropertyName *name)
|
||||
|
||||
bool useSlowPath = false;
|
||||
|
||||
if (obj->isUnknownValue()) {
|
||||
if (obj->isUnknownValue() || obj->isLambda()) {
|
||||
useSlowPath = true;
|
||||
} else {
|
||||
templateObject = obj->toNewObject()->templateObject();
|
||||
|
@ -431,7 +431,7 @@ IonCache::linkAndAttachStub(JSContext *cx, MacroAssembler &masm, StubAttacher &a
|
||||
// Add entry to native => bytecode mapping for this stub if needed.
|
||||
if (cx->runtime()->jitRuntime()->isProfilerInstrumentationEnabled(cx->runtime())) {
|
||||
JitcodeGlobalEntry::IonCacheEntry entry;
|
||||
entry.init(code->raw(), code->rawEnd(), rejoinAddress());
|
||||
entry.init(code, code->raw(), code->rawEnd(), rejoinAddress());
|
||||
|
||||
// Add entry to the global table.
|
||||
JitcodeGlobalTable *globalTable = cx->runtime()->jitRuntime()->getJitcodeGlobalTable();
|
||||
@ -444,7 +444,7 @@ IonCache::linkAndAttachStub(JSContext *cx, MacroAssembler &masm, StubAttacher &a
|
||||
code->setHasBytecodeMap();
|
||||
} else {
|
||||
JitcodeGlobalEntry::DummyEntry entry;
|
||||
entry.init(code->raw(), code->rawEnd());
|
||||
entry.init(code, code->raw(), code->rawEnd());
|
||||
|
||||
// Add entry to the global table.
|
||||
JitcodeGlobalTable *globalTable = cx->runtime()->jitRuntime()->getJitcodeGlobalTable();
|
||||
|
@ -2856,7 +2856,6 @@ GetPreviousRawFrame(FrameType *frame)
|
||||
|
||||
JitProfilingFrameIterator::JitProfilingFrameIterator(void *exitFrame)
|
||||
{
|
||||
// Exit frame was en
|
||||
ExitFrameLayout *frame = (ExitFrameLayout *) exitFrame;
|
||||
FrameType prevType = frame->prevType();
|
||||
|
||||
|
@ -7,8 +7,10 @@
|
||||
#include "jit/JitcodeMap.h"
|
||||
|
||||
#include "mozilla/DebugOnly.h"
|
||||
#include "mozilla/MathAlgorithms.h"
|
||||
#include "mozilla/UniquePtr.h"
|
||||
#include "jsprf.h"
|
||||
#include "gc/Marking.h"
|
||||
|
||||
#include "jit/BaselineJIT.h"
|
||||
#include "jit/JitSpewer.h"
|
||||
@ -375,45 +377,364 @@ JitcodeGlobalTable::lookup(void *ptr, JitcodeGlobalEntry *result, JSRuntime *rt)
|
||||
{
|
||||
MOZ_ASSERT(result);
|
||||
|
||||
// Construct a JitcodeGlobalEntry::Query to do the lookup
|
||||
JitcodeGlobalEntry query = JitcodeGlobalEntry::MakeQuery(ptr);
|
||||
JitcodeGlobalEntry *entry = lookupInternal(ptr);
|
||||
if (!entry)
|
||||
return false;
|
||||
|
||||
// Lookups on tree does mutation. Suppress sampling when this is happening.
|
||||
AutoSuppressProfilerSampling suppressSampling(rt);
|
||||
return tree_.contains(query, result);
|
||||
*result = *entry;
|
||||
return true;
|
||||
}
|
||||
|
||||
void
|
||||
JitcodeGlobalTable::lookupInfallible(void *ptr, JitcodeGlobalEntry *result, JSRuntime *rt)
|
||||
bool
|
||||
JitcodeGlobalTable::lookupForSampler(void *ptr, JitcodeGlobalEntry *result, JSRuntime *rt,
|
||||
uint32_t sampleBufferGen)
|
||||
{
|
||||
mozilla::DebugOnly<bool> success = lookup(ptr, result, rt);
|
||||
MOZ_ASSERT(success);
|
||||
MOZ_ASSERT(result);
|
||||
|
||||
JitcodeGlobalEntry *entry = lookupInternal(ptr);
|
||||
if (!entry)
|
||||
return false;
|
||||
|
||||
entry->setGeneration(sampleBufferGen);
|
||||
|
||||
*result = *entry;
|
||||
return true;
|
||||
}
|
||||
|
||||
JitcodeGlobalEntry *
|
||||
JitcodeGlobalTable::lookupInternal(void *ptr)
|
||||
{
|
||||
JitcodeGlobalEntry query = JitcodeGlobalEntry::MakeQuery(ptr);
|
||||
JitcodeGlobalEntry *searchTower[JitcodeSkiplistTower::MAX_HEIGHT];
|
||||
searchInternal(query, searchTower);
|
||||
|
||||
if (searchTower[0] == nullptr) {
|
||||
// Check startTower
|
||||
if (startTower_[0] == nullptr)
|
||||
return nullptr;
|
||||
|
||||
MOZ_ASSERT(startTower_[0]->compareTo(query) >= 0);
|
||||
int cmp = startTower_[0]->compareTo(query);
|
||||
MOZ_ASSERT(cmp >= 0);
|
||||
return (cmp == 0) ? startTower_[0] : nullptr;
|
||||
}
|
||||
|
||||
JitcodeGlobalEntry *bottom = searchTower[0];
|
||||
MOZ_ASSERT(bottom->compareTo(query) < 0);
|
||||
|
||||
JitcodeGlobalEntry *bottomNext = bottom->tower_->next(0);
|
||||
if (bottomNext == nullptr)
|
||||
return nullptr;
|
||||
|
||||
int cmp = bottomNext->compareTo(query);
|
||||
MOZ_ASSERT(cmp >= 0);
|
||||
return (cmp == 0) ? bottomNext : nullptr;
|
||||
}
|
||||
|
||||
bool
|
||||
JitcodeGlobalTable::addEntry(const JitcodeGlobalEntry &entry, JSRuntime *rt)
|
||||
{
|
||||
// Suppress profiler sampling while table is being mutated.
|
||||
MOZ_ASSERT(entry.isIon() || entry.isBaseline() || entry.isIonCache() || entry.isDummy());
|
||||
|
||||
JitcodeGlobalEntry *searchTower[JitcodeSkiplistTower::MAX_HEIGHT];
|
||||
searchInternal(entry, searchTower);
|
||||
|
||||
// Allocate a new entry and tower.
|
||||
JitcodeSkiplistTower *newTower = allocateTower(generateTowerHeight());
|
||||
if (!newTower)
|
||||
return false;
|
||||
|
||||
JitcodeGlobalEntry *newEntry = allocateEntry();
|
||||
if (!newEntry)
|
||||
return false;
|
||||
|
||||
*newEntry = entry;
|
||||
newEntry->tower_ = newTower;
|
||||
|
||||
// Suppress profiler sampling while skiplist is being mutated.
|
||||
AutoSuppressProfilerSampling suppressSampling(rt);
|
||||
|
||||
MOZ_ASSERT(entry.isIon() || entry.isBaseline() || entry.isIonCache() || entry.isDummy());
|
||||
return tree_.insert(entry);
|
||||
// Link up entry with forward entries taken from tower.
|
||||
for (int level = newTower->height() - 1; level >= 0; level--) {
|
||||
JitcodeGlobalEntry *searchTowerEntry = searchTower[level];
|
||||
if (searchTowerEntry) {
|
||||
MOZ_ASSERT(searchTowerEntry->compareTo(*newEntry) < 0);
|
||||
JitcodeGlobalEntry *searchTowerNextEntry = searchTowerEntry->tower_->next(level);
|
||||
|
||||
MOZ_ASSERT_IF(searchTowerNextEntry, searchTowerNextEntry->compareTo(*newEntry) > 0);
|
||||
|
||||
newTower->setNext(level, searchTowerNextEntry);
|
||||
searchTowerEntry->tower_->setNext(level, newEntry);
|
||||
} else {
|
||||
newTower->setNext(level, startTower_[level]);
|
||||
startTower_[level] = newEntry;
|
||||
}
|
||||
}
|
||||
skiplistSize_++;
|
||||
// verifySkiplist(); - disabled for release.
|
||||
return true;
|
||||
}
|
||||
|
||||
void
|
||||
JitcodeGlobalTable::removeEntry(void *startAddr, JSRuntime *rt)
|
||||
{
|
||||
// Suppress profiler sampling while table is being mutated.
|
||||
AutoSuppressProfilerSampling suppressSampling(rt);
|
||||
|
||||
JitcodeGlobalEntry query = JitcodeGlobalEntry::MakeQuery(startAddr);
|
||||
JitcodeGlobalEntry result;
|
||||
mozilla::DebugOnly<bool> success = tree_.contains(query, &result);
|
||||
MOZ_ASSERT(success);
|
||||
JitcodeGlobalEntry *searchTower[JitcodeSkiplistTower::MAX_HEIGHT];
|
||||
searchInternal(query, searchTower);
|
||||
|
||||
// Destroy entry before removing it from tree.
|
||||
result.destroy();
|
||||
tree_.remove(query);
|
||||
JitcodeGlobalEntry *queryEntry;
|
||||
if (searchTower[0]) {
|
||||
MOZ_ASSERT(searchTower[0]->compareTo(query) < 0);
|
||||
queryEntry = searchTower[0]->tower_->next(0);
|
||||
} else {
|
||||
MOZ_ASSERT(startTower_[0]);
|
||||
queryEntry = startTower_[0];
|
||||
}
|
||||
MOZ_ASSERT(queryEntry->compareTo(query) == 0);
|
||||
|
||||
{
|
||||
// Suppress profiler sampling while table is being mutated.
|
||||
AutoSuppressProfilerSampling suppressSampling(rt);
|
||||
|
||||
// Unlink query entry.
|
||||
for (int level = queryEntry->tower_->height() - 1; level >= 0; level--) {
|
||||
JitcodeGlobalEntry *searchTowerEntry = searchTower[level];
|
||||
if (searchTowerEntry) {
|
||||
MOZ_ASSERT(searchTowerEntry);
|
||||
searchTowerEntry->tower_->setNext(level, queryEntry->tower_->next(level));
|
||||
} else {
|
||||
startTower_[level] = queryEntry->tower_->next(level);
|
||||
}
|
||||
}
|
||||
skiplistSize_--;
|
||||
// verifySkiplist(); - disabled for release.
|
||||
}
|
||||
|
||||
// Entry has been unlinked.
|
||||
queryEntry->destroy();
|
||||
queryEntry->tower_->addToFreeList(&(freeTowers_[queryEntry->tower_->height() - 1]));
|
||||
queryEntry->tower_ = nullptr;
|
||||
*queryEntry = JitcodeGlobalEntry();
|
||||
queryEntry->addToFreeList(&freeEntries_);
|
||||
}
|
||||
|
||||
void
|
||||
JitcodeGlobalTable::releaseEntry(void *startAddr, JSRuntime *rt)
|
||||
{
|
||||
mozilla::DebugOnly<JitcodeGlobalEntry *> entry = lookupInternal(startAddr);
|
||||
mozilla::DebugOnly<uint32_t> gen = rt->profilerSampleBufferGen();
|
||||
mozilla::DebugOnly<uint32_t> lapCount = rt->profilerSampleBufferLapCount();
|
||||
MOZ_ASSERT(entry);
|
||||
MOZ_ASSERT_IF(gen != UINT32_MAX, !entry->isSampled(gen, lapCount));
|
||||
removeEntry(startAddr, rt);
|
||||
}
|
||||
|
||||
void
|
||||
JitcodeGlobalTable::searchInternal(const JitcodeGlobalEntry &query, JitcodeGlobalEntry **towerOut)
|
||||
{
|
||||
JitcodeGlobalEntry *cur = nullptr;
|
||||
for (int level = JitcodeSkiplistTower::MAX_HEIGHT - 1; level >= 0; level--) {
|
||||
JitcodeGlobalEntry *entry = searchAtHeight(level, cur, query);
|
||||
MOZ_ASSERT_IF(entry == nullptr, cur == nullptr);
|
||||
towerOut[level] = entry;
|
||||
cur = entry;
|
||||
}
|
||||
|
||||
// Validate the resulting tower.
|
||||
#ifdef DEBUG
|
||||
for (int level = JitcodeSkiplistTower::MAX_HEIGHT - 1; level >= 0; level--) {
|
||||
if (towerOut[level] == nullptr) {
|
||||
// If we got NULL for a given level, then we should have gotten NULL
|
||||
// for the level above as well.
|
||||
MOZ_ASSERT_IF(unsigned(level) < (JitcodeSkiplistTower::MAX_HEIGHT - 1),
|
||||
towerOut[level + 1] == nullptr);
|
||||
continue;
|
||||
}
|
||||
|
||||
JitcodeGlobalEntry *cur = towerOut[level];
|
||||
|
||||
// Non-null result at a given level must sort < query.
|
||||
MOZ_ASSERT(cur->compareTo(query) < 0);
|
||||
|
||||
// The entry must have a tower height that accomodates level.
|
||||
if (!cur->tower_->next(level))
|
||||
continue;
|
||||
|
||||
JitcodeGlobalEntry *next = cur->tower_->next(level);
|
||||
|
||||
// Next entry must have tower height that accomodates level.
|
||||
MOZ_ASSERT(unsigned(level) < next->tower_->height());
|
||||
|
||||
// Next entry must sort >= query.
|
||||
MOZ_ASSERT(next->compareTo(query) >= 0);
|
||||
}
|
||||
#endif // DEBUG
|
||||
}
|
||||
|
||||
JitcodeGlobalEntry *
|
||||
JitcodeGlobalTable::searchAtHeight(unsigned level, JitcodeGlobalEntry *start,
|
||||
const JitcodeGlobalEntry &query)
|
||||
{
|
||||
JitcodeGlobalEntry *cur = start;
|
||||
|
||||
// If starting with nullptr, use the start tower.
|
||||
if (start == nullptr) {
|
||||
cur = startTower_[level];
|
||||
if (cur == nullptr || cur->compareTo(query) >= 0)
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
// Keep skipping at |level| until we reach an entry < query whose
|
||||
// successor is an entry >= query.
|
||||
for (;;) {
|
||||
JitcodeGlobalEntry *next = cur->tower_->next(level);
|
||||
if (next == nullptr || next->compareTo(query) >= 0)
|
||||
return cur;
|
||||
|
||||
cur = next;
|
||||
}
|
||||
}
|
||||
|
||||
unsigned
|
||||
JitcodeGlobalTable::generateTowerHeight()
|
||||
{
|
||||
// Implementation taken from Hars L. and Pteruska G.,
|
||||
// "Pseudorandom Recursions: Small and fast Pseudorandom number generators for
|
||||
// embedded applications."
|
||||
rand_ ^= mozilla::RotateLeft(rand_, 5) ^ mozilla::RotateLeft(rand_, 24);
|
||||
rand_ += 0x37798849;
|
||||
|
||||
// Return number of lowbit zeros in new randval.
|
||||
unsigned result = 0;
|
||||
for (unsigned i = 0; i < 32; i++) {
|
||||
if ((rand_ >> i) & 0x1)
|
||||
break;
|
||||
result++;
|
||||
}
|
||||
return result + 1;
|
||||
}
|
||||
|
||||
JitcodeSkiplistTower *
|
||||
JitcodeGlobalTable::allocateTower(unsigned height)
|
||||
{
|
||||
MOZ_ASSERT(height >= 1);
|
||||
JitcodeSkiplistTower *tower = JitcodeSkiplistTower::PopFromFreeList(&freeTowers_[height - 1]);
|
||||
if (tower)
|
||||
return tower;
|
||||
|
||||
size_t size = JitcodeSkiplistTower::CalculateSize(height);
|
||||
tower = (JitcodeSkiplistTower *) alloc_.alloc(size);
|
||||
if (!tower)
|
||||
return nullptr;
|
||||
|
||||
return new (tower) JitcodeSkiplistTower(height);
|
||||
}
|
||||
|
||||
JitcodeGlobalEntry *
|
||||
JitcodeGlobalTable::allocateEntry()
|
||||
{
|
||||
JitcodeGlobalEntry *entry = JitcodeGlobalEntry::PopFromFreeList(&freeEntries_);
|
||||
if (entry)
|
||||
return entry;
|
||||
|
||||
return alloc_.new_<JitcodeGlobalEntry>();
|
||||
}
|
||||
|
||||
#ifdef DEBUG
|
||||
void
|
||||
JitcodeGlobalTable::verifySkiplist()
|
||||
{
|
||||
JitcodeGlobalEntry *curTower[JitcodeSkiplistTower::MAX_HEIGHT];
|
||||
for (unsigned i = 0; i < JitcodeSkiplistTower::MAX_HEIGHT; i++)
|
||||
curTower[i] = startTower_[i];
|
||||
|
||||
uint32_t count = 0;
|
||||
JitcodeGlobalEntry *curEntry = startTower_[0];
|
||||
while (curEntry) {
|
||||
count++;
|
||||
unsigned curHeight = curEntry->tower_->height();
|
||||
MOZ_ASSERT(curHeight >= 1);
|
||||
|
||||
for (unsigned i = 0; i < JitcodeSkiplistTower::MAX_HEIGHT; i++) {
|
||||
if (i < curHeight) {
|
||||
MOZ_ASSERT(curTower[i] == curEntry);
|
||||
JitcodeGlobalEntry *nextEntry = curEntry->tower_->next(i);
|
||||
MOZ_ASSERT_IF(nextEntry, curEntry->compareTo(*nextEntry) < 0);
|
||||
curTower[i] = nextEntry;
|
||||
} else {
|
||||
MOZ_ASSERT_IF(curTower[i], curTower[i]->compareTo(*curEntry) > 0);
|
||||
}
|
||||
}
|
||||
curEntry = curEntry->tower_->next(0);
|
||||
}
|
||||
|
||||
MOZ_ASSERT(count == skiplistSize_);
|
||||
}
|
||||
#endif // DEBUG
|
||||
|
||||
struct JitcodeMapEntryTraceCallback
|
||||
{
|
||||
JSTracer *trc;
|
||||
uint32_t gen;
|
||||
uint32_t lapCount;
|
||||
|
||||
explicit JitcodeMapEntryTraceCallback(JSTracer *trc)
|
||||
: trc(trc),
|
||||
gen(trc->runtime()->profilerSampleBufferGen()),
|
||||
lapCount(trc->runtime()->profilerSampleBufferLapCount())
|
||||
{
|
||||
if (!trc->runtime()->spsProfiler.enabled())
|
||||
gen = UINT32_MAX;
|
||||
}
|
||||
|
||||
void operator()(JitcodeGlobalEntry &entry) {
|
||||
// If an entry is not sampled, reset its generation to
|
||||
// the invalid generation, and skip it.
|
||||
if (!entry.isSampled(gen, lapCount)) {
|
||||
entry.setGeneration(UINT32_MAX);
|
||||
return;
|
||||
}
|
||||
|
||||
// Mark jitcode pointed to by this entry.
|
||||
entry.baseEntry().markJitcode(trc);
|
||||
|
||||
// Mark ion entry if necessary.
|
||||
if (entry.isIon())
|
||||
entry.ionEntry().mark(trc);
|
||||
}
|
||||
};
|
||||
|
||||
void
|
||||
JitcodeGlobalTable::mark(JSTracer *trc)
|
||||
{
|
||||
AutoSuppressProfilerSampling suppressSampling(trc->runtime());
|
||||
JitcodeMapEntryTraceCallback traceCallback(trc);
|
||||
|
||||
// Find start entry.
|
||||
JitcodeGlobalEntry *entry = startTower_[0];
|
||||
while (entry != nullptr) {
|
||||
traceCallback(*entry);
|
||||
entry = entry->tower_->next(0);
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
JitcodeGlobalEntry::BaseEntry::markJitcode(JSTracer *trc)
|
||||
{
|
||||
MarkJitCodeRoot(trc, &jitcode_, "jitcodglobaltable-baseentry-jitcode");
|
||||
}
|
||||
|
||||
void
|
||||
JitcodeGlobalEntry::IonEntry::mark(JSTracer *trc)
|
||||
{
|
||||
if (!optsAllTypes_)
|
||||
return;
|
||||
|
||||
for (IonTrackedTypeWithAddendum *iter = optsAllTypes_->begin();
|
||||
iter != optsAllTypes_->end(); iter++)
|
||||
{
|
||||
TypeSet::MarkTypeRoot(trc, &(iter->type), "jitcodeglobaltable-ionentry-type");
|
||||
}
|
||||
}
|
||||
|
||||
/* static */ void
|
||||
@ -845,7 +1166,7 @@ JitcodeIonTable::makeIonEntry(JSContext *cx, JitCode *code,
|
||||
|
||||
SizedScriptList *scriptList = new (mem) SizedScriptList(numScripts, scripts,
|
||||
&profilingStrings[0]);
|
||||
out.init(code->raw(), code->rawEnd(), scriptList, this);
|
||||
out.init(code, code->raw(), code->rawEnd(), scriptList, this);
|
||||
return true;
|
||||
}
|
||||
|
||||
|
@ -7,9 +7,9 @@
|
||||
#ifndef jit_JitcodeMap_h
|
||||
#define jit_JitcodeMap_h
|
||||
|
||||
#include "ds/SplayTree.h"
|
||||
#include "jit/CompactBuffer.h"
|
||||
#include "jit/CompileInfo.h"
|
||||
#include "jit/ExecutableAllocator.h"
|
||||
#include "jit/OptimizationTracking.h"
|
||||
#include "jit/shared/CodeGenerator-shared.h"
|
||||
|
||||
@ -31,11 +31,97 @@ namespace jit {
|
||||
* distinguished by the kind field.
|
||||
*/
|
||||
|
||||
class JitcodeGlobalTable;
|
||||
class JitcodeIonTable;
|
||||
class JitcodeRegionEntry;
|
||||
|
||||
class JitcodeGlobalEntry;
|
||||
|
||||
class JitcodeSkiplistTower
|
||||
{
|
||||
public:
|
||||
static const unsigned MAX_HEIGHT = 32;
|
||||
|
||||
private:
|
||||
uint8_t height_;
|
||||
bool isFree_;
|
||||
JitcodeGlobalEntry *ptrs_[1];
|
||||
|
||||
public:
|
||||
explicit JitcodeSkiplistTower(unsigned height)
|
||||
: height_(height),
|
||||
isFree_(false)
|
||||
{
|
||||
MOZ_ASSERT(height >= 1 && height <= MAX_HEIGHT);
|
||||
clearPtrs();
|
||||
}
|
||||
|
||||
unsigned height() const {
|
||||
return height_;
|
||||
}
|
||||
|
||||
JitcodeGlobalEntry **ptrs(unsigned level) {
|
||||
return ptrs_;
|
||||
}
|
||||
|
||||
JitcodeGlobalEntry *next(unsigned level) const {
|
||||
MOZ_ASSERT(!isFree_);
|
||||
MOZ_ASSERT(level < height());
|
||||
return ptrs_[level];
|
||||
}
|
||||
void setNext(unsigned level, JitcodeGlobalEntry *entry) {
|
||||
MOZ_ASSERT(!isFree_);
|
||||
MOZ_ASSERT(level < height());
|
||||
ptrs_[level] = entry;
|
||||
}
|
||||
|
||||
//
|
||||
// When stored in a free-list, towers use 'ptrs_[0]' to store a
|
||||
// pointer to the next tower. In this context only, 'ptrs_[0]'
|
||||
// may refer to a |JitcodeSkiplistTower *| instead of a
|
||||
// |JitcodeGlobalEntry *|.
|
||||
//
|
||||
|
||||
void addToFreeList(JitcodeSkiplistTower **freeList) {
|
||||
JitcodeSkiplistTower *nextFreeTower = *freeList;
|
||||
MOZ_ASSERT_IF(nextFreeTower, nextFreeTower->isFree_ &&
|
||||
nextFreeTower->height() == height_);
|
||||
ptrs_[0] = (JitcodeGlobalEntry *) nextFreeTower;
|
||||
isFree_ = true;
|
||||
*freeList = this;
|
||||
}
|
||||
|
||||
static JitcodeSkiplistTower *PopFromFreeList(JitcodeSkiplistTower **freeList) {
|
||||
if (!*freeList)
|
||||
return nullptr;
|
||||
|
||||
JitcodeSkiplistTower *tower = *freeList;
|
||||
MOZ_ASSERT(tower->isFree_);
|
||||
JitcodeSkiplistTower *nextFreeTower = (JitcodeSkiplistTower *) tower->ptrs_[0];
|
||||
tower->clearPtrs();
|
||||
tower->isFree_ = false;
|
||||
*freeList = nextFreeTower;
|
||||
return tower;
|
||||
}
|
||||
|
||||
static size_t CalculateSize(unsigned height) {
|
||||
MOZ_ASSERT(height >= 1);
|
||||
return sizeof(JitcodeSkiplistTower) +
|
||||
(sizeof(JitcodeGlobalEntry *) * (height - 1));
|
||||
}
|
||||
|
||||
private:
|
||||
void clearPtrs() {
|
||||
for (unsigned i = 0; i < height_; i++)
|
||||
ptrs_[0] = nullptr;
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
class JitcodeGlobalEntry
|
||||
{
|
||||
friend class JitcodeGlobalTable;
|
||||
|
||||
public:
|
||||
enum Kind {
|
||||
INVALID = 0,
|
||||
@ -58,28 +144,53 @@ class JitcodeGlobalEntry
|
||||
|
||||
struct BaseEntry
|
||||
{
|
||||
JitCode *jitcode_;
|
||||
void *nativeStartAddr_;
|
||||
void *nativeEndAddr_;
|
||||
Kind kind_;
|
||||
uint32_t gen_;
|
||||
Kind kind_ : 7;
|
||||
|
||||
void init() {
|
||||
jitcode_ = nullptr;
|
||||
nativeStartAddr_ = nullptr;
|
||||
nativeEndAddr_ = nullptr;
|
||||
gen_ = UINT32_MAX;
|
||||
kind_ = INVALID;
|
||||
}
|
||||
|
||||
void init(Kind kind, void *nativeStartAddr, void *nativeEndAddr) {
|
||||
void init(Kind kind, JitCode *code,
|
||||
void *nativeStartAddr, void *nativeEndAddr)
|
||||
{
|
||||
MOZ_ASSERT_IF(kind != Query, code);
|
||||
MOZ_ASSERT(nativeStartAddr);
|
||||
MOZ_ASSERT(nativeEndAddr);
|
||||
MOZ_ASSERT(kind > INVALID && kind < LIMIT);
|
||||
jitcode_ = code;
|
||||
nativeStartAddr_ = nativeStartAddr;
|
||||
nativeEndAddr_ = nativeEndAddr;
|
||||
gen_ = UINT32_MAX;
|
||||
kind_ = kind;
|
||||
}
|
||||
|
||||
uint32_t generation() const {
|
||||
return gen_;
|
||||
}
|
||||
void setGeneration(uint32_t gen) {
|
||||
gen_ = gen;
|
||||
}
|
||||
bool isSampled(uint32_t currentGen, uint32_t lapCount) {
|
||||
if (gen_ == UINT32_MAX || currentGen == UINT32_MAX)
|
||||
return false;
|
||||
MOZ_ASSERT(currentGen >= gen_);
|
||||
return (currentGen - gen_) <= lapCount;
|
||||
}
|
||||
|
||||
Kind kind() const {
|
||||
return kind_;
|
||||
}
|
||||
JitCode *jitcode() const {
|
||||
return jitcode_;
|
||||
}
|
||||
void *nativeStartAddr() const {
|
||||
return nativeStartAddr_;
|
||||
}
|
||||
@ -96,6 +207,8 @@ class JitcodeGlobalEntry
|
||||
bool containsPointer(void *ptr) const {
|
||||
return startsBelowPointer(ptr) && endsAbovePointer(ptr);
|
||||
}
|
||||
|
||||
void markJitcode(JSTracer *trc);
|
||||
};
|
||||
|
||||
struct IonEntry : public BaseEntry
|
||||
@ -146,12 +259,12 @@ class JitcodeGlobalEntry
|
||||
|
||||
SizedScriptList *scriptList_;
|
||||
|
||||
void init(void *nativeStartAddr, void *nativeEndAddr,
|
||||
void init(JitCode *code, void *nativeStartAddr, void *nativeEndAddr,
|
||||
SizedScriptList *scriptList, JitcodeIonTable *regionTable)
|
||||
{
|
||||
MOZ_ASSERT(scriptList);
|
||||
MOZ_ASSERT(regionTable);
|
||||
BaseEntry::init(Ion, nativeStartAddr, nativeEndAddr);
|
||||
BaseEntry::init(Ion, code, nativeStartAddr, nativeEndAddr);
|
||||
regionTable_ = regionTable;
|
||||
scriptList_ = scriptList;
|
||||
optsRegionTable_ = nullptr;
|
||||
@ -243,6 +356,8 @@ class JitcodeGlobalEntry
|
||||
}
|
||||
|
||||
mozilla::Maybe<uint8_t> trackedOptimizationIndexAtAddr(void *ptr);
|
||||
|
||||
void mark(JSTracer *trc);
|
||||
};
|
||||
|
||||
struct BaselineEntry : public BaseEntry
|
||||
@ -256,10 +371,11 @@ class JitcodeGlobalEntry
|
||||
jsbytecode *ionAbortPc_;
|
||||
const char *ionAbortMessage_;
|
||||
|
||||
void init(void *nativeStartAddr, void *nativeEndAddr, JSScript *script, const char *str)
|
||||
void init(JitCode *code, void *nativeStartAddr, void *nativeEndAddr,
|
||||
JSScript *script, const char *str)
|
||||
{
|
||||
MOZ_ASSERT(script != nullptr);
|
||||
BaseEntry::init(Baseline, nativeStartAddr, nativeEndAddr);
|
||||
BaseEntry::init(Baseline, code, nativeStartAddr, nativeEndAddr);
|
||||
script_ = script;
|
||||
str_ = str;
|
||||
}
|
||||
@ -300,10 +416,11 @@ class JitcodeGlobalEntry
|
||||
{
|
||||
void *rejoinAddr_;
|
||||
|
||||
void init(void *nativeStartAddr, void *nativeEndAddr, void *rejoinAddr)
|
||||
void init(JitCode *code, void *nativeStartAddr, void *nativeEndAddr,
|
||||
void *rejoinAddr)
|
||||
{
|
||||
MOZ_ASSERT(rejoinAddr != nullptr);
|
||||
BaseEntry::init(IonCache, nativeStartAddr, nativeEndAddr);
|
||||
BaseEntry::init(IonCache, code, nativeStartAddr, nativeEndAddr);
|
||||
rejoinAddr_ = rejoinAddr;
|
||||
}
|
||||
|
||||
@ -328,8 +445,8 @@ class JitcodeGlobalEntry
|
||||
// stack when profiling is enabled.
|
||||
struct DummyEntry : public BaseEntry
|
||||
{
|
||||
void init(void *nativeStartAddr, void *nativeEndAddr) {
|
||||
BaseEntry::init(Dummy, nativeStartAddr, nativeEndAddr);
|
||||
void init(JitCode *code, void *nativeStartAddr, void *nativeEndAddr) {
|
||||
BaseEntry::init(Dummy, code, nativeStartAddr, nativeEndAddr);
|
||||
}
|
||||
|
||||
void destroy() {}
|
||||
@ -360,7 +477,7 @@ class JitcodeGlobalEntry
|
||||
struct QueryEntry : public BaseEntry
|
||||
{
|
||||
void init(void *addr) {
|
||||
BaseEntry::init(Query, addr, addr);
|
||||
BaseEntry::init(Query, nullptr, addr, addr);
|
||||
}
|
||||
uint8_t *addr() const {
|
||||
return reinterpret_cast<uint8_t *>(nativeStartAddr());
|
||||
@ -369,6 +486,8 @@ class JitcodeGlobalEntry
|
||||
};
|
||||
|
||||
private:
|
||||
JitcodeSkiplistTower *tower_;
|
||||
|
||||
union {
|
||||
// Shadowing BaseEntry instance to allow access to base fields
|
||||
// and type extraction.
|
||||
@ -393,27 +512,39 @@ class JitcodeGlobalEntry
|
||||
};
|
||||
|
||||
public:
|
||||
JitcodeGlobalEntry() {
|
||||
JitcodeGlobalEntry()
|
||||
: tower_(nullptr)
|
||||
{
|
||||
base_.init();
|
||||
}
|
||||
|
||||
explicit JitcodeGlobalEntry(const IonEntry &ion) {
|
||||
explicit JitcodeGlobalEntry(const IonEntry &ion)
|
||||
: tower_(nullptr)
|
||||
{
|
||||
ion_ = ion;
|
||||
}
|
||||
|
||||
explicit JitcodeGlobalEntry(const BaselineEntry &baseline) {
|
||||
explicit JitcodeGlobalEntry(const BaselineEntry &baseline)
|
||||
: tower_(nullptr)
|
||||
{
|
||||
baseline_ = baseline;
|
||||
}
|
||||
|
||||
explicit JitcodeGlobalEntry(const IonCacheEntry &ionCache) {
|
||||
explicit JitcodeGlobalEntry(const IonCacheEntry &ionCache)
|
||||
: tower_(nullptr)
|
||||
{
|
||||
ionCache_ = ionCache;
|
||||
}
|
||||
|
||||
explicit JitcodeGlobalEntry(const DummyEntry &dummy) {
|
||||
explicit JitcodeGlobalEntry(const DummyEntry &dummy)
|
||||
: tower_(nullptr)
|
||||
{
|
||||
dummy_ = dummy;
|
||||
}
|
||||
|
||||
explicit JitcodeGlobalEntry(const QueryEntry &query) {
|
||||
explicit JitcodeGlobalEntry(const QueryEntry &query)
|
||||
: tower_(nullptr)
|
||||
{
|
||||
query_ = query;
|
||||
}
|
||||
|
||||
@ -445,6 +576,9 @@ class JitcodeGlobalEntry
|
||||
}
|
||||
}
|
||||
|
||||
JitCode *jitcode() const {
|
||||
return baseEntry().jitcode();
|
||||
}
|
||||
void *nativeStartAddr() const {
|
||||
return base_.nativeStartAddr();
|
||||
}
|
||||
@ -452,6 +586,16 @@ class JitcodeGlobalEntry
|
||||
return base_.nativeEndAddr();
|
||||
}
|
||||
|
||||
uint32_t generation() const {
|
||||
return baseEntry().generation();
|
||||
}
|
||||
void setGeneration(uint32_t gen) {
|
||||
baseEntry().setGeneration(gen);
|
||||
}
|
||||
bool isSampled(uint32_t currentGen, uint32_t lapCount) {
|
||||
return baseEntry().isSampled(currentGen, lapCount);
|
||||
}
|
||||
|
||||
bool startsBelowPointer(void *ptr) const {
|
||||
return base_.startsBelowPointer(ptr);
|
||||
}
|
||||
@ -478,6 +622,9 @@ class JitcodeGlobalEntry
|
||||
return base_.kind();
|
||||
}
|
||||
|
||||
bool isValid() const {
|
||||
return (kind() > INVALID) && (kind() < LIMIT);
|
||||
}
|
||||
bool isIon() const {
|
||||
return kind() == Ion;
|
||||
}
|
||||
@ -494,6 +641,10 @@ class JitcodeGlobalEntry
|
||||
return kind() == Query;
|
||||
}
|
||||
|
||||
BaseEntry &baseEntry() {
|
||||
MOZ_ASSERT(isValid());
|
||||
return base_;
|
||||
}
|
||||
IonEntry &ionEntry() {
|
||||
MOZ_ASSERT(isIon());
|
||||
return ion_;
|
||||
@ -515,6 +666,10 @@ class JitcodeGlobalEntry
|
||||
return query_;
|
||||
}
|
||||
|
||||
const BaseEntry &baseEntry() const {
|
||||
MOZ_ASSERT(isValid());
|
||||
return base_;
|
||||
}
|
||||
const IonEntry &ionEntry() const {
|
||||
MOZ_ASSERT(isIon());
|
||||
return ion_;
|
||||
@ -600,6 +755,9 @@ class JitcodeGlobalEntry
|
||||
|
||||
// Compare two global entries.
|
||||
static int compare(const JitcodeGlobalEntry &ent1, const JitcodeGlobalEntry &ent2);
|
||||
int compareTo(const JitcodeGlobalEntry &other) {
|
||||
return compare(*this, other);
|
||||
}
|
||||
|
||||
// Compute a profiling string for a given script.
|
||||
static char *createScriptString(JSContext *cx, JSScript *script, size_t *length=nullptr);
|
||||
@ -643,6 +801,35 @@ class JitcodeGlobalEntry
|
||||
const IonTrackedTypeVector *allTrackedTypes() {
|
||||
return ionEntry().allTrackedTypes();
|
||||
}
|
||||
|
||||
//
|
||||
// When stored in a free-list, entries use 'tower_' to store a
|
||||
// pointer to the next entry. In this context only, 'tower_'
|
||||
// may refer to a |JitcodeGlobalEntry *| instead of a
|
||||
// |JitcodeSkiplistTower *|.
|
||||
//
|
||||
|
||||
void addToFreeList(JitcodeGlobalEntry **freeList) {
|
||||
MOZ_ASSERT(!isValid());
|
||||
|
||||
JitcodeGlobalEntry *nextFreeEntry = *freeList;
|
||||
MOZ_ASSERT_IF(nextFreeEntry, !nextFreeEntry->isValid());
|
||||
|
||||
tower_ = (JitcodeSkiplistTower *) nextFreeEntry;
|
||||
*freeList = this;
|
||||
}
|
||||
|
||||
static JitcodeGlobalEntry *PopFromFreeList(JitcodeGlobalEntry **freeList) {
|
||||
if (!*freeList)
|
||||
return nullptr;
|
||||
|
||||
JitcodeGlobalEntry *entry = *freeList;
|
||||
MOZ_ASSERT(!entry->isValid());
|
||||
JitcodeGlobalEntry *nextFreeEntry = (JitcodeGlobalEntry *) entry->tower_;
|
||||
entry->tower_ = nullptr;
|
||||
*freeList = nextFreeEntry;
|
||||
return entry;
|
||||
}
|
||||
};
|
||||
|
||||
/*
|
||||
@ -650,31 +837,40 @@ class JitcodeGlobalEntry
|
||||
*/
|
||||
class JitcodeGlobalTable
|
||||
{
|
||||
public:
|
||||
typedef SplayTree<JitcodeGlobalEntry, JitcodeGlobalEntry> EntryTree;
|
||||
|
||||
typedef Vector<JitcodeGlobalEntry, 0, SystemAllocPolicy> EntryVector;
|
||||
|
||||
private:
|
||||
static const size_t LIFO_CHUNK_SIZE = 16 * 1024;
|
||||
LifoAlloc treeAlloc_;
|
||||
EntryTree tree_;
|
||||
EntryVector entries_;
|
||||
|
||||
LifoAlloc alloc_;
|
||||
JitcodeGlobalEntry *freeEntries_;
|
||||
uint32_t rand_;
|
||||
uint32_t skiplistSize_;
|
||||
|
||||
JitcodeGlobalEntry *startTower_[JitcodeSkiplistTower::MAX_HEIGHT];
|
||||
JitcodeSkiplistTower *freeTowers_[JitcodeSkiplistTower::MAX_HEIGHT];
|
||||
|
||||
public:
|
||||
JitcodeGlobalTable() : treeAlloc_(LIFO_CHUNK_SIZE), tree_(&treeAlloc_), entries_() {
|
||||
// Always checking coherency in DEBUG builds may cause tests to time
|
||||
// out under --baseline-eager or --ion-eager.
|
||||
tree_.disableCheckCoherency();
|
||||
JitcodeGlobalTable()
|
||||
: alloc_(LIFO_CHUNK_SIZE), freeEntries_(nullptr), rand_(0), skiplistSize_(0)
|
||||
{
|
||||
for (unsigned i = 0; i < JitcodeSkiplistTower::MAX_HEIGHT; i++)
|
||||
startTower_[i] = nullptr;
|
||||
for (unsigned i = 0; i < JitcodeSkiplistTower::MAX_HEIGHT; i++)
|
||||
freeTowers_[i] = nullptr;
|
||||
}
|
||||
~JitcodeGlobalTable() {}
|
||||
|
||||
bool empty() const {
|
||||
return tree_.empty();
|
||||
return skiplistSize_ == 0;
|
||||
}
|
||||
|
||||
bool lookup(void *ptr, JitcodeGlobalEntry *result, JSRuntime *rt);
|
||||
void lookupInfallible(void *ptr, JitcodeGlobalEntry *result, JSRuntime *rt);
|
||||
bool lookupForSampler(void *ptr, JitcodeGlobalEntry *result, JSRuntime *rt,
|
||||
uint32_t sampleBufferGen);
|
||||
|
||||
void lookupInfallible(void *ptr, JitcodeGlobalEntry *result, JSRuntime *rt) {
|
||||
mozilla::DebugOnly<bool> success = lookup(ptr, result, rt);
|
||||
MOZ_ASSERT(success);
|
||||
}
|
||||
|
||||
bool addEntry(const JitcodeGlobalEntry::IonEntry &entry, JSRuntime *rt) {
|
||||
return addEntry(JitcodeGlobalEntry(entry), rt);
|
||||
@ -690,9 +886,37 @@ class JitcodeGlobalTable
|
||||
}
|
||||
|
||||
void removeEntry(void *startAddr, JSRuntime *rt);
|
||||
void releaseEntry(void *startAddr, JSRuntime *rt);
|
||||
|
||||
void mark(JSTracer *trc);
|
||||
|
||||
private:
|
||||
bool addEntry(const JitcodeGlobalEntry &entry, JSRuntime *rt);
|
||||
|
||||
JitcodeGlobalEntry *lookupInternal(void *ptr);
|
||||
|
||||
// Initialize towerOut such that towerOut[i] (for i in [0, MAX_HEIGHT-1])
|
||||
// is a JitcodeGlobalEntry that is sorted to be <query, whose successor at
|
||||
// level i is either null, or sorted to be >= query.
|
||||
//
|
||||
// If entry with the given properties does not exist for level i, then
|
||||
// towerOut[i] is initialized to nullptr.
|
||||
void searchInternal(const JitcodeGlobalEntry &query, JitcodeGlobalEntry **towerOut);
|
||||
|
||||
JitcodeGlobalEntry *searchAtHeight(unsigned level, JitcodeGlobalEntry *start,
|
||||
const JitcodeGlobalEntry &query);
|
||||
|
||||
// Calculate next random tower height.
|
||||
unsigned generateTowerHeight();
|
||||
|
||||
JitcodeSkiplistTower *allocateTower(unsigned height);
|
||||
JitcodeGlobalEntry *allocateEntry();
|
||||
|
||||
#ifdef DEBUG
|
||||
void verifySkiplist();
|
||||
#else
|
||||
void verifySkiplist() {}
|
||||
#endif
|
||||
};
|
||||
|
||||
|
||||
|
@ -3283,7 +3283,6 @@ class MInitProp
|
||||
: public MAryInstruction<2>,
|
||||
public MixPolicy<ObjectPolicy<0>, BoxPolicy<1> >::Data
|
||||
{
|
||||
public:
|
||||
AlwaysTenuredPropertyName name_;
|
||||
|
||||
protected:
|
||||
@ -3314,6 +3313,7 @@ class MInitProp
|
||||
PropertyName *propertyName() const {
|
||||
return name_;
|
||||
}
|
||||
|
||||
bool possiblyCalls() const MOZ_OVERRIDE {
|
||||
return true;
|
||||
}
|
||||
|
@ -192,10 +192,12 @@ MutatePrototype(JSContext *cx, HandlePlainObject obj, HandleValue value)
|
||||
}
|
||||
|
||||
bool
|
||||
InitProp(JSContext *cx, HandleNativeObject obj, HandlePropertyName name, HandleValue value)
|
||||
InitProp(JSContext *cx, HandleNativeObject obj, HandlePropertyName name, HandleValue value,
|
||||
jsbytecode *pc)
|
||||
{
|
||||
RootedId id(cx, NameToId(name));
|
||||
return NativeDefineProperty(cx, obj, id, value, nullptr, nullptr, JSPROP_ENUMERATE);
|
||||
unsigned propAttrs = GetInitDataPropAttrs(JSOp(*pc));
|
||||
return NativeDefineProperty(cx, obj, id, value, nullptr, nullptr, propAttrs);
|
||||
}
|
||||
|
||||
template<bool Equal>
|
||||
|
@ -647,7 +647,9 @@ bool CheckOverRecursedWithExtra(JSContext *cx, BaselineFrame *frame,
|
||||
bool DefVarOrConst(JSContext *cx, HandlePropertyName dn, unsigned attrs, HandleObject scopeChain);
|
||||
bool SetConst(JSContext *cx, HandlePropertyName name, HandleObject scopeChain, HandleValue rval);
|
||||
bool MutatePrototype(JSContext *cx, HandlePlainObject obj, HandleValue value);
|
||||
bool InitProp(JSContext *cx, HandleNativeObject obj, HandlePropertyName name, HandleValue value);
|
||||
|
||||
bool InitProp(JSContext *cx, HandleNativeObject obj, HandlePropertyName name, HandleValue value,
|
||||
jsbytecode *pc);
|
||||
|
||||
template<bool Equal>
|
||||
bool LooselyEqual(JSContext *cx, MutableHandleValue lhs, MutableHandleValue rhs, bool *res);
|
||||
|
@ -193,6 +193,7 @@ MSG_DEF(JSMSG_BAD_GENERATOR_RETURN, 1, JSEXN_TYPEERR, "generator function {0}
|
||||
MSG_DEF(JSMSG_BAD_GENERATOR_SYNTAX, 1, JSEXN_SYNTAXERR, "{0} expression must be parenthesized")
|
||||
MSG_DEF(JSMSG_BAD_GENEXP_BODY, 1, JSEXN_SYNTAXERR, "illegal use of {0} in generator expression")
|
||||
MSG_DEF(JSMSG_BAD_INCOP_OPERAND, 0, JSEXN_REFERENCEERR, "invalid increment/decrement operand")
|
||||
MSG_DEF(JSMSG_BAD_METHOD_DEF, 0, JSEXN_SYNTAXERR, "bad method definition")
|
||||
MSG_DEF(JSMSG_BAD_OCTAL, 1, JSEXN_SYNTAXERR, "{0} is not a legal ECMA-262 octal constant")
|
||||
MSG_DEF(JSMSG_BAD_OPERAND, 1, JSEXN_SYNTAXERR, "invalid {0} operand")
|
||||
MSG_DEF(JSMSG_BAD_PROP_ID, 0, JSEXN_SYNTAXERR, "invalid property id")
|
||||
@ -219,6 +220,7 @@ MSG_DEF(JSMSG_CURLY_AFTER_LIST, 0, JSEXN_SYNTAXERR, "missing } after prop
|
||||
MSG_DEF(JSMSG_CURLY_AFTER_TRY, 0, JSEXN_SYNTAXERR, "missing } after try block")
|
||||
MSG_DEF(JSMSG_CURLY_BEFORE_BODY, 0, JSEXN_SYNTAXERR, "missing { before function body")
|
||||
MSG_DEF(JSMSG_CURLY_BEFORE_CATCH, 0, JSEXN_SYNTAXERR, "missing { before catch block")
|
||||
MSG_DEF(JSMSG_CURLY_BEFORE_CLASS, 0, JSEXN_SYNTAXERR, "missing { before class body")
|
||||
MSG_DEF(JSMSG_CURLY_BEFORE_FINALLY, 0, JSEXN_SYNTAXERR, "missing { before finally block")
|
||||
MSG_DEF(JSMSG_CURLY_BEFORE_SWITCH, 0, JSEXN_SYNTAXERR, "missing { before switch body")
|
||||
MSG_DEF(JSMSG_CURLY_BEFORE_TRY, 0, JSEXN_SYNTAXERR, "missing { before try block")
|
||||
@ -247,6 +249,7 @@ MSG_DEF(JSMSG_INVALID_FOR_IN_INIT, 0, JSEXN_SYNTAXERR, "for-in loop let decl
|
||||
MSG_DEF(JSMSG_INVALID_FOR_OF_INIT, 0, JSEXN_SYNTAXERR, "for-of loop variable declaration may not have an initializer")
|
||||
MSG_DEF(JSMSG_IN_AFTER_FOR_NAME, 0, JSEXN_SYNTAXERR, "missing 'in' or 'of' after for")
|
||||
MSG_DEF(JSMSG_LABEL_NOT_FOUND, 0, JSEXN_SYNTAXERR, "label not found")
|
||||
MSG_DEF(JSMSG_LET_CLASS_BINDING, 0, JSEXN_SYNTAXERR, "'let' is not a valid name for a class")
|
||||
MSG_DEF(JSMSG_LET_COMP_BINDING, 0, JSEXN_SYNTAXERR, "'let' is not a valid name for a comprehension variable")
|
||||
MSG_DEF(JSMSG_LEXICAL_DECL_NOT_IN_BLOCK, 1, JSEXN_SYNTAXERR, "{0} declaration not directly within block")
|
||||
MSG_DEF(JSMSG_LINE_BREAK_AFTER_THROW, 0, JSEXN_SYNTAXERR, "no line break is allowed between 'throw' and its expression")
|
||||
@ -262,6 +265,7 @@ MSG_DEF(JSMSG_MODULE_SPEC_AFTER_FROM, 0, JSEXN_SYNTAXERR, "missing module speci
|
||||
MSG_DEF(JSMSG_NAME_AFTER_DOT, 0, JSEXN_SYNTAXERR, "missing name after . operator")
|
||||
MSG_DEF(JSMSG_NONDEFAULT_FORMAL_AFTER_DEFAULT, 0, JSEXN_SYNTAXERR, "parameter(s) with default followed by parameter without default")
|
||||
MSG_DEF(JSMSG_NO_BINDING_NAME, 0, JSEXN_SYNTAXERR, "missing binding name")
|
||||
MSG_DEF(JSMSG_NO_CLASS_CONSTRUCTOR, 0, JSEXN_TYPEERR, "class default constructors not yet implemented")
|
||||
MSG_DEF(JSMSG_NO_EXPORT_NAME, 0, JSEXN_SYNTAXERR, "missing export name")
|
||||
MSG_DEF(JSMSG_NO_IMPORT_NAME, 0, JSEXN_SYNTAXERR, "missing import name")
|
||||
MSG_DEF(JSMSG_NO_VARIABLE_NAME, 0, JSEXN_SYNTAXERR, "missing variable name")
|
||||
@ -309,6 +313,7 @@ MSG_DEF(JSMSG_TOO_MANY_LOCALS, 0, JSEXN_SYNTAXERR, "too many local varia
|
||||
MSG_DEF(JSMSG_TOO_MANY_YIELDS, 0, JSEXN_SYNTAXERR, "too many yield expressions")
|
||||
MSG_DEF(JSMSG_TOUGH_BREAK, 0, JSEXN_SYNTAXERR, "unlabeled break must be inside loop or switch")
|
||||
MSG_DEF(JSMSG_UNEXPECTED_TOKEN, 2, JSEXN_SYNTAXERR, "expected {0}, got {1}")
|
||||
MSG_DEF(JSMSG_UNNAMED_CLASS_STMT, 0, JSEXN_SYNTAXERR, "class statement requires a name")
|
||||
MSG_DEF(JSMSG_UNNAMED_FUNCTION_STMT, 0, JSEXN_SYNTAXERR, "function statement requires a name")
|
||||
MSG_DEF(JSMSG_UNTERMINATED_COMMENT, 0, JSEXN_SYNTAXERR, "unterminated comment")
|
||||
MSG_DEF(JSMSG_UNTERMINATED_REGEXP, 0, JSEXN_SYNTAXERR, "unterminated regular expression literal")
|
||||
|
@ -22,3 +22,45 @@ BEGIN_TEST(testSavedStacks_withNoStack)
|
||||
return true;
|
||||
}
|
||||
END_TEST(testSavedStacks_withNoStack)
|
||||
|
||||
BEGIN_TEST(testSavedStacks_ApiDefaultValues)
|
||||
{
|
||||
js::RootedSavedFrame savedFrame(cx, nullptr);
|
||||
|
||||
// Source
|
||||
JS::RootedString str(cx);
|
||||
JS::SavedFrameResult result = JS::GetSavedFrameSource(cx, savedFrame, &str);
|
||||
CHECK(result == JS::SavedFrameResult::AccessDenied);
|
||||
CHECK(str.get() == cx->runtime()->emptyString);
|
||||
|
||||
// Line
|
||||
uint32_t line = 123;
|
||||
result = JS::GetSavedFrameLine(cx, savedFrame, &line);
|
||||
CHECK(result == JS::SavedFrameResult::AccessDenied);
|
||||
CHECK(line == 0);
|
||||
|
||||
// Column
|
||||
uint32_t column = 123;
|
||||
result = JS::GetSavedFrameColumn(cx, savedFrame, &column);
|
||||
CHECK(result == JS::SavedFrameResult::AccessDenied);
|
||||
CHECK(column == 0);
|
||||
|
||||
// Function display name
|
||||
result = JS::GetSavedFrameFunctionDisplayName(cx, savedFrame, &str);
|
||||
CHECK(result == JS::SavedFrameResult::AccessDenied);
|
||||
CHECK(str.get() == nullptr);
|
||||
|
||||
// Parent
|
||||
JS::RootedObject parent(cx);
|
||||
result = JS::GetSavedFrameParent(cx, savedFrame, &parent);
|
||||
CHECK(result == JS::SavedFrameResult::AccessDenied);
|
||||
CHECK(parent.get() == nullptr);
|
||||
|
||||
// Stack string
|
||||
result = JS::GetSavedFrameSource(cx, savedFrame, &str);
|
||||
CHECK(result == JS::SavedFrameResult::AccessDenied);
|
||||
CHECK(str.get() == cx->runtime()->emptyString);
|
||||
|
||||
return true;
|
||||
}
|
||||
END_TEST(testSavedStacks_ApiDefaultValues)
|
||||
|
@ -5045,13 +5045,87 @@ SetOutOfMemoryCallback(JSRuntime *rt, OutOfMemoryCallback cb, void *data);
|
||||
|
||||
|
||||
/*
|
||||
* Capture the current call stack as a chain of SavedFrame objects, and set
|
||||
* |stackp| to the SavedFrame for the newest stack frame. If |maxFrameCount| is
|
||||
* non-zero, capture at most the youngest |maxFrameCount| frames.
|
||||
* Capture the current call stack as a chain of SavedFrame JSObjects, and set
|
||||
* |stackp| to the SavedFrame for the youngest stack frame, or nullptr if there
|
||||
* are no JS frames on the stack. If |maxFrameCount| is non-zero, capture at
|
||||
* most the youngest |maxFrameCount| frames.
|
||||
*/
|
||||
extern JS_PUBLIC_API(bool)
|
||||
CaptureCurrentStack(JSContext *cx, MutableHandleObject stackp, unsigned maxFrameCount = 0);
|
||||
|
||||
/*
|
||||
* Accessors for working with SavedFrame JSObjects
|
||||
*
|
||||
* Each of these functions assert that if their `HandleObject savedFrame`
|
||||
* argument is non-null, its JSClass is the SavedFrame class (or it is a
|
||||
* cross-compartment or Xray wrapper around an object with the SavedFrame class)
|
||||
* and the object is not the SavedFrame.prototype object.
|
||||
*
|
||||
* Each of these functions will find the first SavedFrame object in the chain
|
||||
* whose underlying stack frame principals are subsumed by the cx's current
|
||||
* compartment's principals, and operate on that SavedFrame object. This
|
||||
* prevents leaking information about privileged frames to un-privileged
|
||||
* callers. As a result, the SavedFrame in parameters do _NOT_ need to be in the
|
||||
* same compartment as the cx, and the various out parameters are _NOT_
|
||||
* guaranteed to be in the same compartment as cx.
|
||||
*
|
||||
* Additionally, it may be the case that there is no such SavedFrame object
|
||||
* whose captured frame's principals are subsumed by the caller's compartment's
|
||||
* principals! If the `HandleObject savedFrame` argument is null, or the
|
||||
* caller's principals do not subsume any of the chained SavedFrame object's
|
||||
* principals, `SavedFrameResult::AccessDenied` is returned and a (hopefully)
|
||||
* sane default value is chosen for the out param.
|
||||
*/
|
||||
|
||||
enum class SavedFrameResult {
|
||||
Ok,
|
||||
AccessDenied
|
||||
};
|
||||
|
||||
/*
|
||||
* Given a SavedFrame JSObject, get its source property. Defaults to the empty
|
||||
* string.
|
||||
*/
|
||||
extern JS_PUBLIC_API(SavedFrameResult)
|
||||
GetSavedFrameSource(JSContext *cx, HandleObject savedFrame, MutableHandleString sourcep);
|
||||
|
||||
/*
|
||||
* Given a SavedFrame JSObject, get its line property. Defaults to 0.
|
||||
*/
|
||||
extern JS_PUBLIC_API(SavedFrameResult)
|
||||
GetSavedFrameLine(JSContext *cx, HandleObject savedFrame, uint32_t *linep);
|
||||
|
||||
/*
|
||||
* Given a SavedFrame JSObject, get its column property. Defaults to 0.
|
||||
*/
|
||||
extern JS_PUBLIC_API(SavedFrameResult)
|
||||
GetSavedFrameColumn(JSContext *cx, HandleObject savedFrame, uint32_t *columnp);
|
||||
|
||||
/*
|
||||
* Given a SavedFrame JSObject, get its functionDisplayName string, or nullptr
|
||||
* if SpiderMonkey was unable to infer a name for the captured frame's
|
||||
* function. Defaults to nullptr.
|
||||
*/
|
||||
extern JS_PUBLIC_API(SavedFrameResult)
|
||||
GetSavedFrameFunctionDisplayName(JSContext *cx, HandleObject savedFrame, MutableHandleString namep);
|
||||
|
||||
/*
|
||||
* Given a SavedFrame JSObject, get its parent SavedFrame object or nullptr if
|
||||
* it is the oldest frame in the stack. The `parentp` out parameter is _NOT_
|
||||
* guaranteed to be in the cx's compartment. Defaults to nullptr.
|
||||
*/
|
||||
extern JS_PUBLIC_API(SavedFrameResult)
|
||||
GetSavedFrameParent(JSContext *cx, HandleObject savedFrame, MutableHandleObject parentp);
|
||||
|
||||
/*
|
||||
* Given a SavedFrame JSObject stack, stringify it in the same format as
|
||||
* Error.prototype.stack. The stringified stack out parameter is placed in the
|
||||
* cx's compartment. Defaults to the empty string.
|
||||
*/
|
||||
extern JS_PUBLIC_API(bool)
|
||||
StringifySavedFrameStack(JSContext *cx, HandleObject stack, MutableHandleString stringp);
|
||||
|
||||
|
||||
} /* namespace JS */
|
||||
|
||||
#endif /* jsapi_h */
|
||||
|
@ -77,4 +77,6 @@ ASTDEF(AST_TAGGED_TEMPLATE, "TaggedTemplate", "taggedTempl
|
||||
ASTDEF(AST_CALL_SITE_OBJ, "CallSiteObject", "callSiteObject")
|
||||
ASTDEF(AST_COMPUTED_NAME, "ComputedName", "computedName")
|
||||
|
||||
ASTDEF(AST_CLASS_STMT, "ClassStatement", "classStatement")
|
||||
ASTDEF(AST_CLASS_METHOD, "ClassMethod", "classMethod")
|
||||
/* AST_LIMIT = last + 1 */
|
||||
|
@ -219,6 +219,7 @@
|
||||
#include "gc/Memory.h"
|
||||
#include "jit/BaselineJIT.h"
|
||||
#include "jit/IonCode.h"
|
||||
#include "jit/JitcodeMap.h"
|
||||
#include "js/SliceBudget.h"
|
||||
#include "proxy/DeadObjectProxy.h"
|
||||
#include "vm/Debugger.h"
|
||||
@ -5178,6 +5179,7 @@ GCRuntime::beginSweepPhase(bool lastGC)
|
||||
#endif
|
||||
|
||||
DropStringWrappers(rt);
|
||||
|
||||
findZoneGroups();
|
||||
endMarkingZoneGroup();
|
||||
beginSweepingZoneGroup();
|
||||
|
@ -616,6 +616,10 @@ class NodeBuilder
|
||||
|
||||
bool exportBatchSpecifier(TokenPos *pos, MutableHandleValue dst);
|
||||
|
||||
bool classStatement(HandleValue name, HandleValue heritage, HandleValue block, TokenPos *pos, MutableHandleValue dst);
|
||||
bool classMethods(NodeVector &methods, MutableHandleValue dst);
|
||||
bool classMethod(HandleValue name, HandleValue body, PropKind kind, bool isStatic, TokenPos *pos, MutableHandleValue dst);
|
||||
|
||||
/*
|
||||
* expressions
|
||||
*/
|
||||
@ -1714,6 +1718,53 @@ NodeBuilder::function(ASTType type, TokenPos *pos,
|
||||
dst);
|
||||
}
|
||||
|
||||
bool
|
||||
NodeBuilder::classMethod(HandleValue name, HandleValue body, PropKind kind, bool isStatic,
|
||||
TokenPos *pos, MutableHandleValue dst)
|
||||
{
|
||||
RootedValue kindName(cx);
|
||||
if (!atomValue(kind == PROP_INIT
|
||||
? "method"
|
||||
: kind == PROP_GETTER
|
||||
? "get"
|
||||
: "set", &kindName)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
RootedValue isStaticVal(cx, BooleanValue(isStatic));
|
||||
RootedValue cb(cx, callbacks[AST_CLASS_METHOD]);
|
||||
if (!cb.isNull())
|
||||
return callback(cb, kindName, name, body, isStaticVal, pos, dst);
|
||||
|
||||
return newNode(AST_CLASS_METHOD, pos,
|
||||
"name", name,
|
||||
"body", body,
|
||||
"kind", kindName,
|
||||
"static", isStaticVal,
|
||||
dst);
|
||||
}
|
||||
|
||||
bool
|
||||
NodeBuilder::classMethods(NodeVector &methods, MutableHandleValue dst)
|
||||
{
|
||||
return newArray(methods, dst);
|
||||
}
|
||||
|
||||
bool
|
||||
NodeBuilder::classStatement(HandleValue name, HandleValue heritage, HandleValue block,
|
||||
TokenPos *pos, MutableHandleValue dst)
|
||||
{
|
||||
RootedValue cb(cx, callbacks[AST_CLASS_STMT]);
|
||||
if (!cb.isNull())
|
||||
return callback(cb, name, heritage, block, pos, dst);
|
||||
|
||||
return newNode(AST_CLASS_STMT, pos,
|
||||
"name", name,
|
||||
"heritage", heritage,
|
||||
"body", block,
|
||||
dst);
|
||||
}
|
||||
|
||||
namespace {
|
||||
|
||||
/*
|
||||
@ -1786,6 +1837,8 @@ class ASTSerializer
|
||||
bool propertyName(ParseNode *pn, MutableHandleValue dst);
|
||||
bool property(ParseNode *pn, MutableHandleValue dst);
|
||||
|
||||
bool classMethod(ParseNode *pn, MutableHandleValue dst);
|
||||
|
||||
bool optIdentifier(HandleAtom atom, TokenPos *pos, MutableHandleValue dst) {
|
||||
if (!atom) {
|
||||
dst.setMagic(JS_SERIALIZE_NO_NODE);
|
||||
@ -2560,6 +2613,35 @@ ASTSerializer::statement(ParseNode *pn, MutableHandleValue dst)
|
||||
case PNK_DEBUGGER:
|
||||
return builder.debuggerStatement(&pn->pn_pos, dst);
|
||||
|
||||
case PNK_CLASS:
|
||||
{
|
||||
RootedValue className(cx);
|
||||
RootedValue heritage(cx);
|
||||
RootedValue classBody(cx);
|
||||
return identifier(pn->pn_kid1->as<ClassNames>().innerBinding(), &className) &&
|
||||
optExpression(pn->pn_kid2, &heritage) &&
|
||||
statement(pn->pn_kid3, &classBody) &&
|
||||
builder.classStatement(className, heritage, classBody, &pn->pn_pos, dst);
|
||||
}
|
||||
|
||||
case PNK_CLASSMETHODLIST:
|
||||
{
|
||||
NodeVector methods(cx);
|
||||
if (!methods.reserve(pn->pn_count))
|
||||
return false;
|
||||
|
||||
for (ParseNode *next = pn->pn_head; next; next = next->pn_next) {
|
||||
MOZ_ASSERT(pn->pn_pos.encloses(next->pn_pos));
|
||||
|
||||
RootedValue prop(cx);
|
||||
if (!classMethod(next, &prop))
|
||||
return false;
|
||||
methods.infallibleAppend(prop);
|
||||
}
|
||||
|
||||
return builder.classMethods(methods, dst);
|
||||
}
|
||||
|
||||
case PNK_NOP:
|
||||
return builder.emptyStatement(&pn->pn_pos, dst);
|
||||
|
||||
@ -2568,6 +2650,34 @@ ASTSerializer::statement(ParseNode *pn, MutableHandleValue dst)
|
||||
}
|
||||
}
|
||||
|
||||
bool
|
||||
ASTSerializer::classMethod(ParseNode *pn, MutableHandleValue dst)
|
||||
{
|
||||
PropKind kind;
|
||||
switch (pn->getOp()) {
|
||||
case JSOP_INITPROP:
|
||||
kind = PROP_INIT;
|
||||
break;
|
||||
|
||||
case JSOP_INITPROP_GETTER:
|
||||
kind = PROP_GETTER;
|
||||
break;
|
||||
|
||||
case JSOP_INITPROP_SETTER:
|
||||
kind = PROP_SETTER;
|
||||
break;
|
||||
|
||||
default:
|
||||
LOCAL_NOT_REACHED("unexpected object-literal property");
|
||||
}
|
||||
|
||||
RootedValue key(cx), val(cx);
|
||||
bool isStatic = pn->as<ClassMethod>().isStatic();
|
||||
return propertyName(pn->pn_left, &key) &&
|
||||
expression(pn->pn_right, &val) &&
|
||||
builder.classMethod(key, val, kind, isStatic, &pn->pn_pos, dst);
|
||||
}
|
||||
|
||||
bool
|
||||
ASTSerializer::leftAssociate(ParseNode *pn, MutableHandleValue dst)
|
||||
{
|
||||
|
@ -2603,7 +2603,7 @@ JSScript::fullyInitFromEmitter(ExclusiveContext *cx, HandleScript script, Byteco
|
||||
bce->tryNoteList.finish(script->trynotes());
|
||||
if (bce->blockScopeList.length() != 0)
|
||||
bce->blockScopeList.finish(script->blockScopes());
|
||||
script->strict_ = bce->sc->strict;
|
||||
script->strict_ = bce->sc->strict();
|
||||
script->explicitUseStrict_ = bce->sc->hasExplicitUseStrict();
|
||||
script->bindingsAccessedDynamically_ = bce->sc->bindingsAccessedDynamically();
|
||||
script->funHasExtensibleScope_ = funbox ? funbox->hasExtensibleScope() : false;
|
||||
|
@ -41,4 +41,9 @@
|
||||
*/
|
||||
#define JS_OLD_GETTER_SETTER_METHODS 1
|
||||
|
||||
/* Support for ES6 Classes. */
|
||||
#ifdef NIGHTLY_BUILD
|
||||
#define JS_HAS_CLASSES 1
|
||||
#endif
|
||||
|
||||
#endif /* jsversion_h */
|
||||
|
@ -332,6 +332,10 @@ function jsTestDriverBrowserInit()
|
||||
{
|
||||
properties.version = '1.8';
|
||||
}
|
||||
else if (properties.test.match(/^ecma_6\/Class/))
|
||||
{
|
||||
properties.version = '1.8';
|
||||
}
|
||||
}
|
||||
|
||||
// default to language=type;text/javascript. required for
|
||||
|
27
js/src/tests/ecma_6/Class/classPrototype.js
Normal file
27
js/src/tests/ecma_6/Class/classPrototype.js
Normal file
@ -0,0 +1,27 @@
|
||||
var test = `
|
||||
|
||||
// The prototype of a class is a non-writable, non-configurable, non-enumerable data property.
|
||||
class a { constructor() { } }
|
||||
var protoDesc = Object.getOwnPropertyDescriptor(a, "prototype");
|
||||
assertEq(protoDesc.writable, false);
|
||||
assertEq(protoDesc.configurable, false);
|
||||
assertEq(protoDesc.enumerable, false);
|
||||
|
||||
var prototype = protoDesc.value;
|
||||
assertEq(typeof prototype, "object");
|
||||
assertEq(Object.getPrototypeOf(prototype), Object.prototype);
|
||||
assertEq(Object.isExtensible(prototype), true);
|
||||
|
||||
var desiredPrototype = {};
|
||||
Object.defineProperty(desiredPrototype, "constructor", { writable: true,
|
||||
configurable: true,
|
||||
enumerable: false,
|
||||
value: a });
|
||||
assertDeepEq(prototype, desiredPrototype);
|
||||
`;
|
||||
|
||||
if (classesEnabled())
|
||||
eval(test);
|
||||
|
||||
if (typeof reportCompare === "function")
|
||||
reportCompare(0, 0, "OK");
|
31
js/src/tests/ecma_6/Class/constructorCalled.js
Normal file
31
js/src/tests/ecma_6/Class/constructorCalled.js
Normal file
@ -0,0 +1,31 @@
|
||||
// The constructor specified should get called, regardless of order, or
|
||||
// other distractions
|
||||
|
||||
var test = `
|
||||
|
||||
var called = false;
|
||||
class a { constructor(x) { assertEq(x, 4); called = true } }
|
||||
new a(4);
|
||||
assertEq(called, true);
|
||||
|
||||
called = false;
|
||||
class b { constructor() { called = true } method() { } }
|
||||
new b();
|
||||
assertEq(called, true);
|
||||
|
||||
called = false;
|
||||
class c { method() { } constructor() { called = true; } }
|
||||
new c();
|
||||
assertEq(called, true);
|
||||
|
||||
called = false;
|
||||
class d { [\"constructor\"]() { throw new Error(\"NO\"); } constructor() { called = true; } }
|
||||
new d();
|
||||
assertEq(called, true);
|
||||
`;
|
||||
|
||||
if (classesEnabled())
|
||||
eval(test);
|
||||
|
||||
if (typeof reportCompare === "function")
|
||||
reportCompare(0, 0, "OK");
|
78
js/src/tests/ecma_6/Class/innerBinding.js
Normal file
78
js/src/tests/ecma_6/Class/innerBinding.js
Normal file
@ -0,0 +1,78 @@
|
||||
// Class statements should create an immutable inner binding. Since all code in
|
||||
// classes is in strict mode, attempts to mutate it should throw.
|
||||
|
||||
if (classesEnabled()) {
|
||||
|
||||
// XXXefaust Because we currently try to do assignment to const as an early error,
|
||||
// this is a syntax error. It is specced to be a TypeError
|
||||
|
||||
function syntaxWrapper() {
|
||||
eval("class Foo { constructor() { } tryBreak() { Foo = 4; } }");
|
||||
}
|
||||
assertThrowsInstanceOf(syntaxWrapper, SyntaxError);
|
||||
/*
|
||||
var test = `
|
||||
class Foo { constructor() { }; tryBreak() { Foo = 4; } }
|
||||
assertThrowsInstanceOf(() => new Foo().tryBreak(), TypeError);
|
||||
|
||||
{
|
||||
class foo { constructor() { }; tryBreak() { foo = 4; } }
|
||||
assertThrowsInstanceOf(() => new foo().tryBreak(), TypeError);
|
||||
}
|
||||
`;
|
||||
*/
|
||||
|
||||
var test = `
|
||||
|
||||
// TDZ applies to inner bindings
|
||||
assertThrowsInstanceOf(()=>eval(\`class Bar {
|
||||
constructor() { };
|
||||
[Bar] () { };
|
||||
}\`), ReferenceError);
|
||||
|
||||
// There's no magic "inner binding" global
|
||||
{
|
||||
class Foo {
|
||||
constructor() { };
|
||||
test() {
|
||||
class Bar {
|
||||
constructor() { }
|
||||
test() { return Foo === Bar }
|
||||
}
|
||||
return new Bar().test();
|
||||
}
|
||||
}
|
||||
assertEq(new Foo().test(), false);
|
||||
}
|
||||
|
||||
// Inner bindings are shadowable
|
||||
{
|
||||
class Foo {
|
||||
constructor() { }
|
||||
test(Foo) { return Foo; }
|
||||
}
|
||||
assertEq(new Foo().test(4), 4);
|
||||
}
|
||||
|
||||
// The outer binding is distinct from the inner one
|
||||
{
|
||||
let orig_X;
|
||||
|
||||
class X {
|
||||
constructor() { }
|
||||
f() { assertEq(X, orig_X); }
|
||||
}
|
||||
|
||||
orig_X = X;
|
||||
X = 13;
|
||||
assertEq(X, 13);
|
||||
new orig_X().f();
|
||||
}
|
||||
`;
|
||||
|
||||
eval(test);
|
||||
|
||||
}
|
||||
|
||||
if (typeof reportCompare === "function")
|
||||
reportCompare(0, 0, "OK");
|
@ -165,6 +165,30 @@ assertEq(a.b(1), 1);
|
||||
a = {["b"](c){"use strict";return c;}};
|
||||
assertEq(a.b(1), 1);
|
||||
|
||||
// Allow strict-reserved names as methods in objects.
|
||||
// (Bug 1124362)
|
||||
a = { static() { return 4; } };
|
||||
assertEq(a.static(), 4);
|
||||
|
||||
a = { get static() { return 4; } };
|
||||
assertEq(a.static, 4);
|
||||
|
||||
a = { set static(x) { assertEq(x, 4); } };
|
||||
a.static = 4;
|
||||
|
||||
function testStrictMode() {
|
||||
"use strict";
|
||||
var obj = { static() { return 4; } };
|
||||
assertEq(obj.static(), 4);
|
||||
|
||||
obj = { get static() { return 4; } };
|
||||
assertEq(obj.static, 4);
|
||||
|
||||
obj = { set static(x) { assertEq(x, 4); } };
|
||||
obj.static = 4;
|
||||
}
|
||||
testStrictMode();
|
||||
|
||||
// Tests provided by benvie in the bug to distinguish from ES5 desugar.
|
||||
assertEq(({ method() {} }).method.name, "method");
|
||||
assertThrowsInstanceOf(function() {({ method() { method() } }).method() }, ReferenceError);
|
||||
|
52
js/src/tests/ecma_6/Class/methodInstallation.js
Normal file
52
js/src/tests/ecma_6/Class/methodInstallation.js
Normal file
@ -0,0 +1,52 @@
|
||||
// Do the things we write in classes actually appear as they are supposed to?
|
||||
|
||||
var test= `
|
||||
|
||||
var methodCalled = false;
|
||||
var getterCalled = false;
|
||||
var setterCalled = false;
|
||||
var constructorCalled = false;
|
||||
class a {
|
||||
constructor() { constructorCalled = true; }
|
||||
__proto__() { methodCalled = true }
|
||||
get getter() { getterCalled = true; }
|
||||
set setter(x) { setterCalled = true; }
|
||||
*[Symbol.iterator]() { yield "cow"; yield "pig"; }
|
||||
}
|
||||
var aConstDesc = Object.getOwnPropertyDescriptor(a.prototype, \"constructor\");
|
||||
assertEq(aConstDesc.writable, true);
|
||||
assertEq(aConstDesc.configurable, true);
|
||||
assertEq(aConstDesc.enumerable, false);
|
||||
aConstDesc.value();
|
||||
assertEq(constructorCalled, true);
|
||||
|
||||
// __proto__ is just an identifier for classes. No prototype changes are made.
|
||||
assertEq(Object.getPrototypeOf(a.prototype), Object.prototype);
|
||||
var aMethDesc = Object.getOwnPropertyDescriptor(a.prototype, \"__proto__\");
|
||||
assertEq(aMethDesc.writable, true);
|
||||
assertEq(aMethDesc.configurable, true);
|
||||
assertEq(aMethDesc.enumerable, true);
|
||||
aMethDesc.value();
|
||||
assertEq(methodCalled, true);
|
||||
|
||||
var aGetDesc = Object.getOwnPropertyDescriptor(a.prototype, \"getter\");
|
||||
assertEq(aGetDesc.configurable, true);
|
||||
assertEq(aGetDesc.enumerable, true);
|
||||
aGetDesc.get();
|
||||
assertEq(getterCalled, true);
|
||||
|
||||
var aSetDesc = Object.getOwnPropertyDescriptor(a.prototype, \"setter\");
|
||||
assertEq(aSetDesc.configurable, true);
|
||||
assertEq(aSetDesc.enumerable, true);
|
||||
aSetDesc.set();
|
||||
assertEq(setterCalled, true);
|
||||
assertDeepEq(aSetDesc, Object.getOwnPropertyDescriptor(a.prototype, \"setter\"));
|
||||
|
||||
assertEq([...new a()].join(), "cow,pig");
|
||||
`;
|
||||
|
||||
if (classesEnabled())
|
||||
eval(test);
|
||||
|
||||
if (typeof reportCompare === "function")
|
||||
reportCompare(0, 0, "OK");
|
45
js/src/tests/ecma_6/Class/methodOverwrites.js
Normal file
45
js/src/tests/ecma_6/Class/methodOverwrites.js
Normal file
@ -0,0 +1,45 @@
|
||||
// Ensure that we can overwrite methods when more tha one is present.
|
||||
|
||||
var test = `
|
||||
var result = 0;
|
||||
// Regardless of order, the constructor is overridden by any CPN, because it's
|
||||
// processed seperately.
|
||||
class a { [\"constructor\"]() { result += 1; }; constructor() { result += 2; } }
|
||||
var aInst = new a();
|
||||
assertEq(result, 2);
|
||||
aInst.constructor();
|
||||
assertEq(result, 3);
|
||||
|
||||
class b { constructor() { result += 2; } [\"constructor\"]() { result += 1; }; }
|
||||
var bInst = new b();
|
||||
assertEq(result, 5);
|
||||
bInst.constructor();
|
||||
assertEq(result, 6);
|
||||
|
||||
class c { constructor() { } method() { result += 1 } get method() { result += 2 } }
|
||||
new c().method;
|
||||
assertEq(result, 8);
|
||||
|
||||
class d { constructor() { } get method() { result += 1 } method() { result += 2 } }
|
||||
new d().method();
|
||||
assertEq(result, 10);
|
||||
|
||||
// getters and setter should not overwrite each other, but merge cleanly.
|
||||
class e { constructor() { } get method() { result += 1 } set method(x) { } }
|
||||
new e().method;
|
||||
assertEq(result, 11);
|
||||
|
||||
class f { constructor() { }
|
||||
set method(x) { throw "NO"; }
|
||||
method() { throw "NO" }
|
||||
get method() { return new Function("result += 1"); }
|
||||
}
|
||||
new f().method();
|
||||
assertEq(result, 12);
|
||||
`;
|
||||
|
||||
if (classesEnabled())
|
||||
eval(test);
|
||||
|
||||
if (typeof reportCompare === "function")
|
||||
reportCompare(0, 0, "OK");
|
63
js/src/tests/ecma_6/Class/outerBinding.js
Normal file
63
js/src/tests/ecma_6/Class/outerBinding.js
Normal file
@ -0,0 +1,63 @@
|
||||
// A class creates a mutable lexical outer binding.
|
||||
|
||||
var test = `
|
||||
class Foo { constructor() { } }
|
||||
assertEq(typeof Foo, \"function\");
|
||||
Foo = 5;
|
||||
assertEq(Foo, 5);
|
||||
|
||||
{
|
||||
class foo { constructor() { } }
|
||||
assertEq(typeof foo, \"function\");
|
||||
foo = 4;
|
||||
assertEq(foo, 4);
|
||||
}
|
||||
|
||||
var ieval = eval;
|
||||
|
||||
{
|
||||
class PermanentBinding { constructor() { } }
|
||||
delete PermanentBinding;
|
||||
// That...didn't actually work, right?
|
||||
assertEq(typeof PermanentBinding, "function");
|
||||
}
|
||||
|
||||
{
|
||||
try {
|
||||
ieval(\`class x { constructor () { } }
|
||||
throw new Error("FAIL");
|
||||
class y { constructor () { } }
|
||||
\`);
|
||||
} catch (e if e instanceof Error) { }
|
||||
assertEq(typeof x, "function");
|
||||
assertEq(y, undefined, "Congrats, you fixed top-level lexical scoping! " +
|
||||
"Please uncomment the tests below for the real test.");
|
||||
// assertThrowsInstanceOf(() => y, ReferenceError);
|
||||
}
|
||||
|
||||
/*
|
||||
===== UNCOMMENT ME WHEN ENABLING THE TEST ABOVE. =====
|
||||
const globalConstant = 0;
|
||||
var earlyError = true;
|
||||
try {
|
||||
ieval("earlyError = false; class globalConstant { constructor() { } }");
|
||||
} catch (e if e instanceof TypeError) { }
|
||||
assertEq(earlyError, true);
|
||||
*/
|
||||
|
||||
function strictEvalShadows() {
|
||||
"use strict";
|
||||
let x = 4;
|
||||
eval(\`class x { constructor() { } }
|
||||
assertEq(typeof x, "function");
|
||||
\`);
|
||||
assertEq(x, 4);
|
||||
}
|
||||
strictEvalShadows()
|
||||
`;
|
||||
|
||||
if (classesEnabled())
|
||||
eval(test);
|
||||
|
||||
if (typeof reportCompare === "function")
|
||||
reportCompare(0, 0, "OK");
|
@ -0,0 +1,12 @@
|
||||
// Enable "let" in shell builds. So silly.
|
||||
if (typeof version != 'undefined')
|
||||
version(185);
|
||||
|
||||
function classesEnabled() {
|
||||
try {
|
||||
new Function("class Foo { constructor() { } }");
|
||||
return true;
|
||||
} catch (e if e instanceof SyntaxError) {
|
||||
return false;
|
||||
}
|
||||
}
|
20
js/src/tests/ecma_6/Class/strictExecution.js
Normal file
20
js/src/tests/ecma_6/Class/strictExecution.js
Normal file
@ -0,0 +1,20 @@
|
||||
// Classes are always strict mode. Check computed property names as well.
|
||||
|
||||
var test = `
|
||||
class a { constructor() { Object.preventExtensions({}).prop = 0; } }
|
||||
assertThrowsInstanceOf(() => new a(), TypeError);
|
||||
|
||||
function shouldThrow() {
|
||||
class b {
|
||||
[Object.preventExtensions({}).prop = 4]() { }
|
||||
constructor() { }
|
||||
}
|
||||
}
|
||||
assertThrowsInstanceOf(shouldThrow, TypeError);
|
||||
`;
|
||||
|
||||
if (classesEnabled())
|
||||
eval(test);
|
||||
|
||||
if (typeof reportCompare === "function")
|
||||
reportCompare(0, 0, "OK");
|
@ -67,6 +67,18 @@ function defaultClause(stmts) Pattern({ type: "SwitchCase", test: null, conseque
|
||||
function catchClause(id, guard, body) Pattern({ type: "CatchClause", param: id, guard: guard, body: body })
|
||||
function tryStmt(body, guarded, unguarded, fin) Pattern({ type: "TryStatement", block: body, guardedHandlers: guarded, handler: unguarded, finalizer: fin })
|
||||
function letStmt(head, body) Pattern({ type: "LetStatement", head: head, body: body })
|
||||
|
||||
function classStmt(id, heritage, body) Pattern({ type: "ClassStatement",
|
||||
name: id,
|
||||
heritage: heritage,
|
||||
body: body})
|
||||
function classMethod(id, body, kind, static) Pattern({ type: "ClassMethod",
|
||||
name: id,
|
||||
body: body,
|
||||
kind: kind,
|
||||
static: static })
|
||||
|
||||
|
||||
function funExpr(id, args, body, gen) Pattern({ type: "FunctionExpression",
|
||||
id: id,
|
||||
params: args,
|
||||
@ -1134,6 +1146,149 @@ try {
|
||||
} catch (e) { }
|
||||
|
||||
|
||||
// Classes
|
||||
function classesEnabled() {
|
||||
try {
|
||||
Reflect.parse("class foo { constructor() { } }");
|
||||
return true;
|
||||
} catch (e) {
|
||||
assertEq(e instanceof SyntaxError, true);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
function testClasses() {
|
||||
// No unnamed class statements.
|
||||
assertError("class { constructor() { } }", SyntaxError);
|
||||
|
||||
function simpleMethod(id, kind, generator, args=[]) {
|
||||
assertEq(generator && kind === "method", generator);
|
||||
let idN = ident(id);
|
||||
let methodMaker = generator ? genFunExpr : funExpr;
|
||||
let methodName = kind !== "method" ? null : idN;
|
||||
let methodFun = methodMaker(methodName, args.map(ident), blockStmt([]));
|
||||
|
||||
return classMethod(idN, methodFun, kind, false);
|
||||
}
|
||||
function setClassMethods(class_, methods) {
|
||||
class_.template.body = methods;
|
||||
}
|
||||
|
||||
let simpleConstructor = simpleMethod("constructor", "method", false);
|
||||
let emptyFooClass = classStmt(ident("Foo"), null, [simpleConstructor]);
|
||||
|
||||
/* Trivial classes */
|
||||
assertStmt("class Foo { constructor() { } }", emptyFooClass);
|
||||
|
||||
// Allow methods and accessors
|
||||
let stmt = classStmt(ident("Foo"), null,
|
||||
[simpleConstructor, simpleMethod("method", "method", false)]);
|
||||
assertStmt("class Foo { constructor() { } method() { } }", stmt);
|
||||
|
||||
setClassMethods(stmt, [simpleConstructor, simpleMethod("method", "get", false)]);
|
||||
assertStmt("class Foo { constructor() { } get method() { } }", stmt);
|
||||
|
||||
setClassMethods(stmt, [simpleConstructor, simpleMethod("method", "set", false, ["x"])]);
|
||||
assertStmt("class Foo { constructor() { } set method(x) { } }", stmt);
|
||||
|
||||
/* Constructor */
|
||||
// Currently, we do not allow default constructors
|
||||
assertError("class Foo { }", TypeError);
|
||||
|
||||
// It is an error to have two methods named constructor, but not other
|
||||
// names, regardless if one is an accessor or a generator.
|
||||
assertError("class Foo { constructor() { } constructor(a) { } }", SyntaxError);
|
||||
let methods = [["method() { }", simpleMethod("method", "method", false)],
|
||||
["*method() { }", simpleMethod("method", "method", true)],
|
||||
["get method() { }", simpleMethod("method", "get", false)],
|
||||
["set method(x) { }", simpleMethod("method", "set", false, ["x"])]];
|
||||
let i,j;
|
||||
for (i=0; i < methods.length; i++) {
|
||||
for (j=0; j < methods.length; j++) {
|
||||
setClassMethods(stmt,
|
||||
[simpleConstructor,
|
||||
methods[i][1],
|
||||
methods[j][1]]);
|
||||
let str = "class Foo { constructor() { } " +
|
||||
methods[i][0] + " " + methods[j][0] +
|
||||
" }";
|
||||
assertStmt(str, stmt);
|
||||
}
|
||||
}
|
||||
|
||||
// It is, however, not an error to have a constructor, and a method with a
|
||||
// computed property name 'constructor'
|
||||
assertStmt("class Foo { constructor () { } [\"constructor\"] () { } }",
|
||||
classStmt(ident("Foo"), null,
|
||||
[simpleConstructor,
|
||||
classMethod(computedName(lit("constructor")),
|
||||
funExpr(null, [], blockStmt([])),
|
||||
"method", false)]));
|
||||
|
||||
// It is an error to have a generator or accessor named constructor
|
||||
assertError("class Foo { *constructor() { } }", SyntaxError);
|
||||
assertError("class Foo { get constructor() { } }", SyntaxError);
|
||||
assertError("class Foo { set constructor() { } }", SyntaxError);
|
||||
|
||||
/* Semicolons */
|
||||
// Allow Semicolons in Class Definitions
|
||||
assertStmt("class Foo { constructor() { }; }", emptyFooClass);
|
||||
|
||||
// Allow more than one semicolon, even in otherwise trivial classses
|
||||
assertStmt("class Foo { ;;; constructor() { } }", emptyFooClass);
|
||||
|
||||
// Semicolons are optional, even if the methods share a line
|
||||
setClassMethods(stmt, [simpleMethod("method", "method", false), simpleConstructor]);
|
||||
assertStmt("class Foo { method() { } constructor() { } }", stmt);
|
||||
|
||||
/* Generators */
|
||||
// No yield as a class name inside a generator
|
||||
assertError("function *foo() {\
|
||||
class yield {\
|
||||
constructor() { } \
|
||||
}\
|
||||
}", SyntaxError);
|
||||
|
||||
// Methods may be generators, but not accessors
|
||||
assertError("class Foo { constructor() { } *get foo() { } }", SyntaxError);
|
||||
assertError("class Foo { constructor() { } *set foo() { } }", SyntaxError);
|
||||
|
||||
setClassMethods(stmt, [simpleMethod("method", "method", true), simpleConstructor]);
|
||||
assertStmt("class Foo { *method() { } constructor() { } }", stmt);
|
||||
|
||||
/* Strictness */
|
||||
// yield is a strict-mode keyword, and class definitions are always strict.
|
||||
assertError("class Foo { constructor() { var yield; } }", SyntaxError);
|
||||
// Beware of the strictness of computed property names. Here use bareword
|
||||
// deletion (a deprecated action) to check.
|
||||
assertError("class Foo { constructor() { } [delete bar]() { }}", SyntaxError);
|
||||
|
||||
/* Bindings */
|
||||
// Classes should bind lexically, so they should collide with other in-block
|
||||
// lexical bindings
|
||||
assertError("{ let Foo; class Foo { constructor() { } } }", TypeError);
|
||||
assertError("{ const Foo = 0; class Foo { constructor() { } } }", TypeError);
|
||||
assertError("{ class Foo { constructor() { } } class Foo { constructor() { } } }", TypeError);
|
||||
|
||||
// Can't make a lexical binding inside a block.
|
||||
assertError("if (1) class Foo { constructor() { } }", SyntaxError);
|
||||
|
||||
/* EOF */
|
||||
// Clipped classes should throw a syntax error
|
||||
assertError("class Foo {", SyntaxError);
|
||||
assertError("class Foo {;", SyntaxError);
|
||||
assertError("class Foo { constructor", SyntaxError);
|
||||
assertError("class Foo { constructor(", SyntaxError);
|
||||
assertError("class Foo { constructor()", SyntaxError);
|
||||
assertError("class Foo { constructor()", SyntaxError);
|
||||
assertError("class Foo { constructor() {", SyntaxError);
|
||||
assertError("class Foo { constructor() { }", SyntaxError);
|
||||
}
|
||||
|
||||
if (classesEnabled())
|
||||
testClasses();
|
||||
|
||||
|
||||
// Source location information
|
||||
|
||||
|
||||
|
@ -1981,6 +1981,19 @@ Debugger::ensureExecutionObservabilityOfScript(JSContext *cx, JSScript *script)
|
||||
return updateExecutionObservability(cx, obs, Observing);
|
||||
}
|
||||
|
||||
/* static */ bool
|
||||
Debugger::ensureExecutionObservabilityOfOsrFrame(JSContext *cx, InterpreterFrame *frame)
|
||||
{
|
||||
MOZ_ASSERT(frame->isDebuggee());
|
||||
if (frame->script()->hasBaselineScript() &&
|
||||
frame->script()->baselineScript()->hasDebugInstrumentation())
|
||||
{
|
||||
return true;
|
||||
}
|
||||
ExecutionObservableFrame obs(frame);
|
||||
return updateExecutionObservabilityOfFrames(cx, obs, Observing);
|
||||
}
|
||||
|
||||
/* static */ bool
|
||||
Debugger::ensureExecutionObservabilityOfFrame(JSContext *cx, AbstractFramePtr frame)
|
||||
{
|
||||
|
@ -441,6 +441,8 @@ class Debugger : private mozilla::LinkedListElement<Debugger>
|
||||
IsObserving observing);
|
||||
|
||||
public:
|
||||
static bool ensureExecutionObservabilityOfOsrFrame(JSContext *cx, InterpreterFrame *frame);
|
||||
|
||||
// Public for DebuggerScript_setBreakpoint.
|
||||
static bool ensureExecutionObservabilityOfScript(JSContext *cx, JSScript *script);
|
||||
|
||||
|
@ -1660,8 +1660,6 @@ CASE(JSOP_UNUSED105)
|
||||
CASE(JSOP_UNUSED107)
|
||||
CASE(JSOP_UNUSED125)
|
||||
CASE(JSOP_UNUSED126)
|
||||
CASE(JSOP_UNUSED146)
|
||||
CASE(JSOP_UNUSED147)
|
||||
CASE(JSOP_UNUSED148)
|
||||
CASE(JSOP_BACKPATCH)
|
||||
CASE(JSOP_UNUSED150)
|
||||
@ -3233,7 +3231,13 @@ CASE(JSOP_MUTATEPROTO)
|
||||
END_CASE(JSOP_MUTATEPROTO)
|
||||
|
||||
CASE(JSOP_INITPROP)
|
||||
CASE(JSOP_INITLOCKEDPROP)
|
||||
CASE(JSOP_INITHIDDENPROP)
|
||||
{
|
||||
static_assert(JSOP_INITPROP_LENGTH == JSOP_INITLOCKEDPROP_LENGTH,
|
||||
"initprop and initlockedprop must be the same size");
|
||||
static_assert(JSOP_INITPROP_LENGTH == JSOP_INITHIDDENPROP_LENGTH,
|
||||
"initprop and inithiddenprop must be the same size");
|
||||
/* Load the property's initial value into rval. */
|
||||
MOZ_ASSERT(REGS.stackDepth() >= 2);
|
||||
RootedValue &rval = rootValue0;
|
||||
@ -3241,14 +3245,16 @@ CASE(JSOP_INITPROP)
|
||||
|
||||
/* Load the object being initialized into lval/obj. */
|
||||
RootedNativeObject &obj = rootNativeObject0;
|
||||
obj = ®S.sp[-2].toObject().as<PlainObject>();
|
||||
obj = ®S.sp[-2].toObject().as<NativeObject>();
|
||||
MOZ_ASSERT(obj->is<PlainObject>() || obj->is<JSFunction>());
|
||||
|
||||
PropertyName *name = script->getName(REGS.pc);
|
||||
|
||||
RootedId &id = rootId0;
|
||||
id = NameToId(name);
|
||||
|
||||
if (!NativeDefineProperty(cx, obj, id, rval, nullptr, nullptr, JSPROP_ENUMERATE))
|
||||
unsigned propAttrs = GetInitDataPropAttrs(JSOp(*REGS.pc));
|
||||
if (!NativeDefineProperty(cx, obj, id, rval, nullptr, nullptr, propAttrs))
|
||||
goto error;
|
||||
|
||||
REGS.sp--;
|
||||
@ -4021,6 +4027,22 @@ js::RunOnceScriptPrologue(JSContext *cx, HandleScript script)
|
||||
return true;
|
||||
}
|
||||
|
||||
unsigned
|
||||
js::GetInitDataPropAttrs(JSOp op)
|
||||
{
|
||||
switch (op) {
|
||||
case JSOP_INITPROP:
|
||||
return JSPROP_ENUMERATE;
|
||||
case JSOP_INITLOCKEDPROP:
|
||||
return JSPROP_PERMANENT | JSPROP_READONLY;
|
||||
case JSOP_INITHIDDENPROP:
|
||||
// Non-enumerable, but writable and configurable
|
||||
return 0;
|
||||
default:;
|
||||
}
|
||||
MOZ_CRASH("Unknown data initprop");
|
||||
}
|
||||
|
||||
bool
|
||||
js::InitGetterSetterOperation(JSContext *cx, jsbytecode *pc, HandleObject obj, HandleId id,
|
||||
HandleObject val)
|
||||
|
@ -377,6 +377,9 @@ bool
|
||||
InitGetterSetterOperation(JSContext *cx, jsbytecode *pc, HandleObject obj, HandlePropertyName name,
|
||||
HandleObject val);
|
||||
|
||||
unsigned
|
||||
GetInitDataPropAttrs(JSOp op);
|
||||
|
||||
bool
|
||||
EnterWithOperation(JSContext *cx, AbstractFramePtr frame, HandleValue val, HandleObject staticWith);
|
||||
|
||||
|
@ -43,8 +43,8 @@
|
||||
macro(with, with, TOK_WITH, JSVERSION_DEFAULT) \
|
||||
macro(import, import, TOK_IMPORT, JSVERSION_DEFAULT) \
|
||||
macro(export, export, TOK_EXPORT, JSVERSION_DEFAULT) \
|
||||
macro(class, class_, TOK_CLASS, JSVERSION_DEFAULT) \
|
||||
/* Reserved keywords. */ \
|
||||
macro(class, class_, TOK_RESERVED, JSVERSION_DEFAULT) \
|
||||
macro(enum, enum_, TOK_RESERVED, JSVERSION_DEFAULT) \
|
||||
macro(extends, extends, TOK_RESERVED, JSVERSION_DEFAULT) \
|
||||
macro(super, super, TOK_RESERVED, JSVERSION_DEFAULT) \
|
||||
|
@ -1406,10 +1406,29 @@
|
||||
* Stack: => intrinsicHolder
|
||||
*/ \
|
||||
macro(JSOP_BINDINTRINSIC, 145, "bindintrinsic", NULL, 5, 0, 1, JOF_ATOM|JOF_NAME|JOF_SET) \
|
||||
\
|
||||
/*
|
||||
* Initialize a non-configurable, non-writable, non-enumerable data-property on an object.
|
||||
*
|
||||
* Pops the top two values on the stack as 'val' and 'obj', defines
|
||||
* 'nameIndex' property of 'obj' as 'val', pushes 'obj' onto the stack.
|
||||
* Category: Literals
|
||||
* Type: Object
|
||||
* Operands: uint32_t nameIndex
|
||||
* Stack: obj, val => obj
|
||||
*/ \
|
||||
macro(JSOP_INITLOCKEDPROP, 146, "initlockedprop", NULL, 5, 2, 1, JOF_ATOM|JOF_PROP|JOF_SET|JOF_DETECTING) \
|
||||
/*
|
||||
* Initialize a non-enumerable data-property on an object.
|
||||
*
|
||||
* Pops the top two values on the stack as 'val' and 'obj', defines
|
||||
* 'nameIndex' property of 'obj' as 'val', pushes 'obj' onto the stack.
|
||||
* Category: Literals
|
||||
* Type: Object
|
||||
* Operands: uint32_t nameIndex
|
||||
* Stack: obj, val => obj
|
||||
*/ \
|
||||
macro(JSOP_INITHIDDENPROP, 147,"inithiddenprop", NULL, 5, 2, 1, JOF_ATOM|JOF_PROP|JOF_SET|JOF_DETECTING) \
|
||||
/* Unused. */ \
|
||||
macro(JSOP_UNUSED146, 146,"unused146", NULL, 1, 0, 0, JOF_BYTE) \
|
||||
macro(JSOP_UNUSED147, 147,"unused147", NULL, 1, 0, 0, JOF_BYTE) \
|
||||
macro(JSOP_UNUSED148, 148,"unused148", NULL, 1, 0, 0, JOF_BYTE) \
|
||||
\
|
||||
/*
|
||||
|
@ -120,6 +120,8 @@ JSRuntime::JSRuntime(JSRuntime *parentRuntime)
|
||||
jitStackLimit_(0xbad),
|
||||
activation_(nullptr),
|
||||
profilingActivation_(nullptr),
|
||||
profilerSampleBufferGen_(0),
|
||||
profilerSampleBufferLapCount_(1),
|
||||
asmJSActivationStack_(nullptr),
|
||||
parentRuntime(parentRuntime),
|
||||
interrupt_(false),
|
||||
@ -372,6 +374,9 @@ JSRuntime::~JSRuntime()
|
||||
/* Allow the GC to release scripts that were being profiled. */
|
||||
profilingScripts = false;
|
||||
|
||||
/* Set the profiler sampler buffer generation to invalid. */
|
||||
profilerSampleBufferGen_ = UINT32_MAX;
|
||||
|
||||
JS::PrepareForFullGC(this);
|
||||
gc.gc(GC_NORMAL, JS::gcreason::DESTROY_RUNTIME);
|
||||
}
|
||||
@ -834,3 +839,11 @@ js::AssertCurrentThreadCanLock(RuntimeLock which)
|
||||
}
|
||||
|
||||
#endif // DEBUG
|
||||
|
||||
JS_FRIEND_API(void)
|
||||
JS::UpdateJSRuntimeProfilerSampleBufferGen(JSRuntime *runtime, uint32_t generation,
|
||||
uint32_t lapCount)
|
||||
{
|
||||
runtime->setProfilerSampleBufferGen(generation);
|
||||
runtime->updateProfilerSampleBufferLapCount(lapCount);
|
||||
}
|
||||
|
@ -641,6 +641,21 @@ struct JSRuntime : public JS::shadow::Runtime,
|
||||
*/
|
||||
js::Activation * volatile profilingActivation_;
|
||||
|
||||
/*
|
||||
* The profiler sampler generation after the latest sample.
|
||||
*
|
||||
* The lapCount indicates the number of largest number of 'laps'
|
||||
* (wrapping from high to low) that occurred when writing entries
|
||||
* into the sample buffer. All JitcodeGlobalMap entries referenced
|
||||
* from a given sample are assigned the generation of the sample buffer
|
||||
* at the START of the run. If multiple laps occur, then some entries
|
||||
* (towards the end) will be written out with the "wrong" generation.
|
||||
* The lapCount indicates the required fudge factor to use to compare
|
||||
* entry generations with the sample buffer generation.
|
||||
*/
|
||||
mozilla::Atomic<uint32_t, mozilla::ReleaseAcquire> profilerSampleBufferGen_;
|
||||
mozilla::Atomic<uint32_t, mozilla::ReleaseAcquire> profilerSampleBufferLapCount_;
|
||||
|
||||
/* See AsmJSActivation comment. */
|
||||
js::AsmJSActivation * volatile asmJSActivationStack_;
|
||||
|
||||
@ -662,6 +677,31 @@ struct JSRuntime : public JS::shadow::Runtime,
|
||||
return offsetof(JSRuntime, profilingActivation_);
|
||||
}
|
||||
|
||||
uint32_t profilerSampleBufferGen() {
|
||||
return profilerSampleBufferGen_;
|
||||
}
|
||||
void setProfilerSampleBufferGen(uint32_t gen) {
|
||||
profilerSampleBufferGen_ = gen;
|
||||
}
|
||||
|
||||
uint32_t profilerSampleBufferLapCount() {
|
||||
MOZ_ASSERT(profilerSampleBufferLapCount_ > 0);
|
||||
return profilerSampleBufferLapCount_;
|
||||
}
|
||||
void updateProfilerSampleBufferLapCount(uint32_t lapCount) {
|
||||
MOZ_ASSERT(profilerSampleBufferLapCount_ > 0);
|
||||
|
||||
// Use compareExchange to make sure we have monotonic increase.
|
||||
for (;;) {
|
||||
uint32_t curLapCount = profilerSampleBufferLapCount_;
|
||||
if (curLapCount >= lapCount)
|
||||
break;
|
||||
|
||||
if (profilerSampleBufferLapCount_.compareExchange(curLapCount, lapCount))
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
js::AsmJSActivation *asmJSActivationStack() const {
|
||||
return asmJSActivationStack_;
|
||||
}
|
||||
|
@ -379,83 +379,113 @@ SavedFrame::checkThis(JSContext *cx, CallArgs &args, const char *fnName,
|
||||
// - unsigned argc
|
||||
// - Value *vp
|
||||
// - const char *fnName
|
||||
// - Value defaultVal
|
||||
// These parameters will be defined after calling this macro:
|
||||
// - CallArgs args
|
||||
// - Rooted<SavedFrame *> frame (will be non-null)
|
||||
#define THIS_SAVEDFRAME(cx, argc, vp, fnName, defaultVal, args, frame) \
|
||||
#define THIS_SAVEDFRAME(cx, argc, vp, fnName, args, frame) \
|
||||
CallArgs args = CallArgsFromVp(argc, vp); \
|
||||
RootedSavedFrame frame(cx); \
|
||||
if (!checkThis(cx, args, fnName, &frame)) \
|
||||
return false; \
|
||||
if (!frame) { \
|
||||
args.rval().set(defaultVal); \
|
||||
return true; \
|
||||
return false;
|
||||
|
||||
} /* namespace js */
|
||||
|
||||
namespace JS {
|
||||
|
||||
static inline js::SavedFrame *
|
||||
UnwrapSavedFrame(JSContext *cx, HandleObject obj)
|
||||
{
|
||||
if (!obj)
|
||||
return nullptr;
|
||||
RootedObject savedFrameObj(cx, CheckedUnwrap(obj));
|
||||
MOZ_ASSERT(savedFrameObj);
|
||||
MOZ_ASSERT(js::SavedFrame::isSavedFrameAndNotProto(*savedFrameObj));
|
||||
js::RootedSavedFrame frame(cx, &savedFrameObj->as<js::SavedFrame>());
|
||||
return GetFirstSubsumedFrame(cx, frame);
|
||||
}
|
||||
|
||||
JS_PUBLIC_API(SavedFrameResult)
|
||||
GetSavedFrameSource(JSContext *cx, HandleObject savedFrame, MutableHandleString sourcep)
|
||||
{
|
||||
js::RootedSavedFrame frame(cx, UnwrapSavedFrame(cx, savedFrame));
|
||||
if (!frame) {
|
||||
sourcep.set(cx->runtime()->emptyString);
|
||||
return SavedFrameResult::AccessDenied;
|
||||
}
|
||||
sourcep.set(frame->getSource());
|
||||
return SavedFrameResult::Ok;
|
||||
}
|
||||
|
||||
JS_PUBLIC_API(SavedFrameResult)
|
||||
GetSavedFrameLine(JSContext *cx, HandleObject savedFrame, uint32_t *linep)
|
||||
{
|
||||
MOZ_ASSERT(linep);
|
||||
js::RootedSavedFrame frame(cx, UnwrapSavedFrame(cx, savedFrame));
|
||||
if (!frame) {
|
||||
*linep = 0;
|
||||
return SavedFrameResult::AccessDenied;
|
||||
}
|
||||
*linep = frame->getLine();
|
||||
return SavedFrameResult::Ok;
|
||||
}
|
||||
|
||||
JS_PUBLIC_API(SavedFrameResult)
|
||||
GetSavedFrameColumn(JSContext *cx, HandleObject savedFrame, uint32_t *columnp)
|
||||
{
|
||||
MOZ_ASSERT(columnp);
|
||||
js::RootedSavedFrame frame(cx, UnwrapSavedFrame(cx, savedFrame));
|
||||
if (!frame) {
|
||||
*columnp = 0;
|
||||
return SavedFrameResult::AccessDenied;
|
||||
}
|
||||
*columnp = frame->getColumn();
|
||||
return SavedFrameResult::Ok;
|
||||
}
|
||||
|
||||
JS_PUBLIC_API(SavedFrameResult)
|
||||
GetSavedFrameFunctionDisplayName(JSContext *cx, HandleObject savedFrame, MutableHandleString namep)
|
||||
{
|
||||
js::RootedSavedFrame frame(cx, UnwrapSavedFrame(cx, savedFrame));
|
||||
if (!frame) {
|
||||
namep.set(nullptr);
|
||||
return SavedFrameResult::AccessDenied;
|
||||
}
|
||||
namep.set(frame->getFunctionDisplayName());
|
||||
return SavedFrameResult::Ok;
|
||||
}
|
||||
|
||||
JS_PUBLIC_API(SavedFrameResult)
|
||||
GetSavedFrameParent(JSContext *cx, HandleObject savedFrame, MutableHandleObject parentp)
|
||||
{
|
||||
js::RootedSavedFrame frame(cx, UnwrapSavedFrame(cx, savedFrame));
|
||||
if (!frame) {
|
||||
parentp.set(nullptr);
|
||||
return SavedFrameResult::AccessDenied;
|
||||
}
|
||||
js::RootedSavedFrame parent(cx, frame->getParent());
|
||||
parentp.set(js::GetFirstSubsumedFrame(cx, parent));
|
||||
return SavedFrameResult::Ok;
|
||||
}
|
||||
|
||||
JS_PUBLIC_API(bool)
|
||||
StringifySavedFrameStack(JSContext *cx, HandleObject stack, MutableHandleString stringp)
|
||||
{
|
||||
js::RootedSavedFrame frame(cx, UnwrapSavedFrame(cx, stack));
|
||||
if (!frame) {
|
||||
stringp.set(cx->runtime()->emptyString);
|
||||
return true;
|
||||
}
|
||||
|
||||
/* static */ bool
|
||||
SavedFrame::sourceProperty(JSContext *cx, unsigned argc, Value *vp)
|
||||
{
|
||||
THIS_SAVEDFRAME(cx, argc, vp, "(get source)", NullValue(), args, frame);
|
||||
args.rval().setString(frame->getSource());
|
||||
return true;
|
||||
}
|
||||
|
||||
/* static */ bool
|
||||
SavedFrame::lineProperty(JSContext *cx, unsigned argc, Value *vp)
|
||||
{
|
||||
THIS_SAVEDFRAME(cx, argc, vp, "(get line)", NullValue(), args, frame);
|
||||
uint32_t line = frame->getLine();
|
||||
args.rval().setNumber(line);
|
||||
return true;
|
||||
}
|
||||
|
||||
/* static */ bool
|
||||
SavedFrame::columnProperty(JSContext *cx, unsigned argc, Value *vp)
|
||||
{
|
||||
THIS_SAVEDFRAME(cx, argc, vp, "(get column)", NullValue(), args, frame);
|
||||
uint32_t column = frame->getColumn();
|
||||
args.rval().setNumber(column);
|
||||
return true;
|
||||
}
|
||||
|
||||
/* static */ bool
|
||||
SavedFrame::functionDisplayNameProperty(JSContext *cx, unsigned argc, Value *vp)
|
||||
{
|
||||
THIS_SAVEDFRAME(cx, argc, vp, "(get functionDisplayName)", NullValue(), args, frame);
|
||||
RootedAtom name(cx, frame->getFunctionDisplayName());
|
||||
if (name)
|
||||
args.rval().setString(name);
|
||||
else
|
||||
args.rval().setNull();
|
||||
return true;
|
||||
}
|
||||
|
||||
/* static */ bool
|
||||
SavedFrame::parentProperty(JSContext *cx, unsigned argc, Value *vp)
|
||||
{
|
||||
THIS_SAVEDFRAME(cx, argc, vp, "(get parent)", NullValue(), args, frame);
|
||||
RootedSavedFrame parent(cx, frame->getParent());
|
||||
args.rval().setObjectOrNull(GetFirstSubsumedFrame(cx, parent));
|
||||
return true;
|
||||
}
|
||||
|
||||
/* static */ bool
|
||||
SavedFrame::toStringMethod(JSContext *cx, unsigned argc, Value *vp)
|
||||
{
|
||||
THIS_SAVEDFRAME(cx, argc, vp, "toString", StringValue(cx->runtime()->emptyString), args, frame);
|
||||
StringBuffer sb(cx);
|
||||
js::StringBuffer sb(cx);
|
||||
DebugOnly<JSSubsumesOp> subsumes = cx->runtime()->securityCallbacks->subsumes;
|
||||
DebugOnly<JSPrincipals *> principals = cx->compartment()->principals;
|
||||
|
||||
RootedSavedFrame parent(cx);
|
||||
js::RootedSavedFrame parent(cx);
|
||||
do {
|
||||
MOZ_ASSERT_IF(subsumes, (*subsumes)(principals, frame->getPrincipals()));
|
||||
if (frame->isSelfHosted())
|
||||
goto nextIteration;
|
||||
|
||||
{
|
||||
RootedAtom name(cx, frame->getFunctionDisplayName());
|
||||
if (!frame->isSelfHosted()) {
|
||||
js::RootedAtom name(cx, frame->getFunctionDisplayName());
|
||||
if ((name && !sb.append(name))
|
||||
|| !sb.append('@')
|
||||
|| !sb.append(frame->getSource())
|
||||
@ -469,15 +499,88 @@ SavedFrame::toStringMethod(JSContext *cx, unsigned argc, Value *vp)
|
||||
}
|
||||
}
|
||||
|
||||
nextIteration:
|
||||
parent = frame->getParent();
|
||||
frame = GetFirstSubsumedFrame(cx, parent);
|
||||
frame = js::GetFirstSubsumedFrame(cx, parent);
|
||||
} while (frame);
|
||||
|
||||
JSString *str = sb.finishString();
|
||||
if (!str)
|
||||
return false;
|
||||
args.rval().setString(str);
|
||||
stringp.set(str);
|
||||
return true;
|
||||
}
|
||||
|
||||
} /* namespace JS */
|
||||
|
||||
namespace js {
|
||||
|
||||
/* static */ bool
|
||||
SavedFrame::sourceProperty(JSContext *cx, unsigned argc, Value *vp)
|
||||
{
|
||||
THIS_SAVEDFRAME(cx, argc, vp, "(get source)", args, frame);
|
||||
RootedString source(cx);
|
||||
if (JS::GetSavedFrameSource(cx, frame, &source) == JS::SavedFrameResult::Ok)
|
||||
args.rval().setString(source);
|
||||
else
|
||||
args.rval().setNull();
|
||||
return true;
|
||||
}
|
||||
|
||||
/* static */ bool
|
||||
SavedFrame::lineProperty(JSContext *cx, unsigned argc, Value *vp)
|
||||
{
|
||||
THIS_SAVEDFRAME(cx, argc, vp, "(get line)", args, frame);
|
||||
uint32_t line;
|
||||
if (JS::GetSavedFrameLine(cx, frame, &line) == JS::SavedFrameResult::Ok)
|
||||
args.rval().setNumber(line);
|
||||
else
|
||||
args.rval().setNull();
|
||||
return true;
|
||||
}
|
||||
|
||||
/* static */ bool
|
||||
SavedFrame::columnProperty(JSContext *cx, unsigned argc, Value *vp)
|
||||
{
|
||||
THIS_SAVEDFRAME(cx, argc, vp, "(get column)", args, frame);
|
||||
uint32_t column;
|
||||
if (JS::GetSavedFrameColumn(cx, frame, &column) == JS::SavedFrameResult::Ok)
|
||||
args.rval().setNumber(column);
|
||||
else
|
||||
args.rval().setNull();
|
||||
return true;
|
||||
}
|
||||
|
||||
/* static */ bool
|
||||
SavedFrame::functionDisplayNameProperty(JSContext *cx, unsigned argc, Value *vp)
|
||||
{
|
||||
THIS_SAVEDFRAME(cx, argc, vp, "(get functionDisplayName)", args, frame);
|
||||
RootedString name(cx);
|
||||
JS::SavedFrameResult result = JS::GetSavedFrameFunctionDisplayName(cx, frame, &name);
|
||||
if (result == JS::SavedFrameResult::Ok && name)
|
||||
args.rval().setString(name);
|
||||
else
|
||||
args.rval().setNull();
|
||||
return true;
|
||||
}
|
||||
|
||||
/* static */ bool
|
||||
SavedFrame::parentProperty(JSContext *cx, unsigned argc, Value *vp)
|
||||
{
|
||||
THIS_SAVEDFRAME(cx, argc, vp, "(get parent)", args, frame);
|
||||
RootedObject parent(cx);
|
||||
(void) JS::GetSavedFrameParent(cx, frame, &parent);
|
||||
args.rval().setObjectOrNull(parent);
|
||||
return true;
|
||||
}
|
||||
|
||||
/* static */ bool
|
||||
SavedFrame::toStringMethod(JSContext *cx, unsigned argc, Value *vp)
|
||||
{
|
||||
THIS_SAVEDFRAME(cx, argc, vp, "toString", args, frame);
|
||||
RootedString string(cx);
|
||||
if (!JS::StringifySavedFrameStack(cx, frame, &string))
|
||||
return false;
|
||||
args.rval().setString(string);
|
||||
return true;
|
||||
}
|
||||
|
||||
|
@ -48,6 +48,11 @@ class SavedFrame : public NativeObject {
|
||||
|
||||
bool isSelfHosted();
|
||||
|
||||
static bool isSavedFrameAndNotProto(JSObject &obj) {
|
||||
return obj.is<SavedFrame>() &&
|
||||
!obj.as<SavedFrame>().getReservedSlot(JSSLOT_SOURCE).isNull();
|
||||
}
|
||||
|
||||
struct Lookup;
|
||||
struct HashPolicy;
|
||||
|
||||
|
@ -1714,8 +1714,10 @@ ActivationIterator::settle()
|
||||
activation_ = activation_->prev();
|
||||
}
|
||||
|
||||
JS::ProfilingFrameIterator::ProfilingFrameIterator(JSRuntime *rt, const RegisterState &state)
|
||||
JS::ProfilingFrameIterator::ProfilingFrameIterator(JSRuntime *rt, const RegisterState &state,
|
||||
uint32_t sampleBufferGen)
|
||||
: rt_(rt),
|
||||
sampleBufferGen_(sampleBufferGen),
|
||||
activation_(rt->profilingActivation()),
|
||||
savedPrevJitTop_(nullptr)
|
||||
{
|
||||
@ -1877,6 +1879,10 @@ JS::ProfilingFrameIterator::extractStack(Frame *frames, uint32_t offset, uint32_
|
||||
jit::JitcodeGlobalTable *table = rt_->jitRuntime()->getJitcodeGlobalTable();
|
||||
jit::JitcodeGlobalEntry entry;
|
||||
table->lookupInfallible(returnAddr, &entry, rt_);
|
||||
if (hasSampleBufferGen())
|
||||
table->lookupForSampler(returnAddr, &entry, rt_, sampleBufferGen_);
|
||||
else
|
||||
table->lookup(returnAddr, &entry, rt_);
|
||||
|
||||
MOZ_ASSERT(entry.isIon() || entry.isIonCache() || entry.isBaseline() || entry.isDummy());
|
||||
|
||||
|
@ -324,6 +324,9 @@ class TypeSet
|
||||
bool isSingleton() const {
|
||||
return isObject() && !!(data & 1);
|
||||
}
|
||||
bool isSingletonUnchecked() const {
|
||||
return isObjectUnchecked() && !!(data & 1);
|
||||
}
|
||||
|
||||
inline JSObject *singleton() const;
|
||||
inline JSObject *singletonNoBarrier() const;
|
||||
@ -333,6 +336,9 @@ class TypeSet
|
||||
bool isGroup() const {
|
||||
return isObject() && !(data & 1);
|
||||
}
|
||||
bool isGroupUnchecked() const {
|
||||
return isObjectUnchecked() && !(data & 1);
|
||||
}
|
||||
|
||||
inline ObjectGroup *group() const;
|
||||
inline ObjectGroup *groupNoBarrier() const;
|
||||
|
@ -29,11 +29,11 @@ namespace js {
|
||||
*
|
||||
* https://developer.mozilla.org/en-US/docs/SpiderMonkey/Internals/Bytecode
|
||||
*/
|
||||
static const uint32_t XDR_BYTECODE_VERSION_SUBTRAHEND = 234;
|
||||
static const uint32_t XDR_BYTECODE_VERSION_SUBTRAHEND = 238;
|
||||
static const uint32_t XDR_BYTECODE_VERSION =
|
||||
uint32_t(0xb973c0de - XDR_BYTECODE_VERSION_SUBTRAHEND);
|
||||
|
||||
static_assert(JSErr_Limit == 367,
|
||||
static_assert(JSErr_Limit == 372,
|
||||
"GREETINGS, POTENTIAL SUBTRAHEND INCREMENTER! If you added or "
|
||||
"removed MSG_DEFs from js.msg, you should increment "
|
||||
"XDR_BYTECODE_VERSION_SUBTRAHEND and update this assertion's "
|
||||
|
@ -256,6 +256,7 @@ class MochitestRunner(MozbuildObject):
|
||||
runByDir=False,
|
||||
useTestMediaDevices=False,
|
||||
timeout=None,
|
||||
max_timeouts=None,
|
||||
**kwargs):
|
||||
"""Runs a mochitest.
|
||||
|
||||
@ -393,6 +394,8 @@ class MochitestRunner(MozbuildObject):
|
||||
options.useTestMediaDevices = useTestMediaDevices
|
||||
if timeout:
|
||||
options.timeout = int(timeout)
|
||||
if max_timeouts:
|
||||
options.maxTimeouts = int(max_timeouts)
|
||||
|
||||
options.failureFile = failure_file_path
|
||||
if install_extension is not None:
|
||||
@ -729,6 +732,10 @@ def MochitestCommand(func):
|
||||
help='The per-test timeout time in seconds (default: 60 seconds)')
|
||||
func = timeout(func)
|
||||
|
||||
max_timeouts = CommandArgument('--max-timeouts', default=None,
|
||||
help='The maximum number of timeouts permitted before halting testing')
|
||||
func = max_timeouts(func)
|
||||
|
||||
return func
|
||||
|
||||
|
||||
|
@ -478,6 +478,12 @@ class MochitestOptions(optparse.OptionParser):
|
||||
"dest": "debuggerInteractive",
|
||||
"help": "prevents the test harness from redirecting stdout and stderr for interactive debuggers",
|
||||
}],
|
||||
[["--max-timeouts"],
|
||||
{ "type": "int",
|
||||
"dest": "maxTimeouts",
|
||||
"help": "maximum number of timeouts permitted before halting testing",
|
||||
"default": None,
|
||||
}],
|
||||
]
|
||||
|
||||
def __init__(self, **kwargs):
|
||||
|
@ -592,6 +592,8 @@ class MochitestUtilsMixin(object):
|
||||
self.urlOpts.append("autorun=1")
|
||||
if options.timeout:
|
||||
self.urlOpts.append("timeout=%d" % options.timeout)
|
||||
if options.maxTimeouts:
|
||||
self.urlOpts.append("maxTimeouts=%d" % options.maxTimeouts)
|
||||
if options.closeWhenDone:
|
||||
self.urlOpts.append("closeWhenDone=1")
|
||||
if options.webapprtContent:
|
||||
|
@ -146,6 +146,10 @@ if (params.interactiveDebugger) {
|
||||
TestRunner.structuredLogger.interactiveDebugger = true;
|
||||
}
|
||||
|
||||
if (params.maxTimeouts) {
|
||||
TestRunner.maxTimeouts = params.maxTimeouts;
|
||||
}
|
||||
|
||||
// Log things to the console if appropriate.
|
||||
TestRunner.logger.addListener("dumpListener", consoleLevel + "", function(msg) {
|
||||
dump(msg.info.join(' ') + "\n");
|
||||
|
@ -12,12 +12,6 @@
|
||||
[Test removing the end of appended data.]
|
||||
expected: FAIL
|
||||
|
||||
[Test remove with a NEGATIVE_INFINITY end.]
|
||||
expected: FAIL
|
||||
|
||||
[Test remove with a NaN end.]
|
||||
expected: FAIL
|
||||
|
||||
[Test remove with a start at the duration.]
|
||||
expected:
|
||||
if (os == "win") and (version != "5.1.2600"): FAIL
|
||||
|
@ -152,6 +152,12 @@ public:
|
||||
mPseudoStack = nullptr;
|
||||
mPlatformData = nullptr;
|
||||
}
|
||||
|
||||
uint32_t bufferGeneration() const {
|
||||
MOZ_ASSERT(mBuffer->mGeneration >= 0);
|
||||
return mBuffer->mGeneration;
|
||||
}
|
||||
|
||||
private:
|
||||
FRIEND_TEST(ThreadProfile, InsertOneTag);
|
||||
FRIEND_TEST(ThreadProfile, InsertOneTagWithTinyBuffer);
|
||||
|
@ -486,6 +486,7 @@ void mergeStacksIntoProfile(ThreadProfile& aProfile, TickSample* aSample, Native
|
||||
// like the native stack, the JS stack is iterated youngest-to-oldest and we
|
||||
// need to iterate oldest-to-youngest when adding entries to aProfile.
|
||||
|
||||
uint32_t startBufferGen = aProfile.bufferGeneration();
|
||||
uint32_t jsCount = 0;
|
||||
JS::ProfilingFrameIterator::Frame jsFrames[1000];
|
||||
{
|
||||
@ -500,7 +501,9 @@ void mergeStacksIntoProfile(ThreadProfile& aProfile, TickSample* aSample, Native
|
||||
registerState.lr = aSample->lr;
|
||||
#endif
|
||||
|
||||
JS::ProfilingFrameIterator jsIter(pseudoStack->mRuntime, registerState);
|
||||
JS::ProfilingFrameIterator jsIter(pseudoStack->mRuntime,
|
||||
registerState,
|
||||
startBufferGen);
|
||||
for (; jsCount < maxFrames && !jsIter.done(); ++jsIter) {
|
||||
uint32_t extracted = jsIter.extractStack(jsFrames, jsCount, maxFrames);
|
||||
MOZ_ASSERT(extracted <= (maxFrames - jsCount));
|
||||
@ -601,6 +604,16 @@ void mergeStacksIntoProfile(ThreadProfile& aProfile, TickSample* aSample, Native
|
||||
aProfile.addTag(ProfileEntry('l', (void*)aNativeStack.pc_array[nativeIndex]));
|
||||
nativeIndex--;
|
||||
}
|
||||
|
||||
MOZ_ASSERT(aProfile.bufferGeneration() >= startBufferGen);
|
||||
uint32_t lapCount = aProfile.bufferGeneration() - startBufferGen;
|
||||
|
||||
// Update the JS runtime with the current profile sample buffer generation.
|
||||
if (pseudoStack->mRuntime) {
|
||||
JS::UpdateJSRuntimeProfilerSampleBufferGen(pseudoStack->mRuntime,
|
||||
aProfile.bufferGeneration(),
|
||||
lapCount);
|
||||
}
|
||||
}
|
||||
|
||||
#ifdef USE_NS_STACKWALK
|
||||
|
@ -3575,7 +3575,7 @@ IMEInputHandler::IsOrWouldBeFocused()
|
||||
NS_ENSURE_TRUE(!Destroyed(), false);
|
||||
NSWindow* window = [mView window];
|
||||
NS_ENSURE_TRUE(window, false);
|
||||
return [window firstResponder] == mView;
|
||||
return [window firstResponder] == mView && ![window attachedSheet];
|
||||
|
||||
NS_OBJC_END_TRY_ABORT_BLOCK_RETURN(false);
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user