merge mozilla-inbound to mozilla-central a=merge

This commit is contained in:
Carsten "Tomcat" Book 2015-02-26 11:57:05 +01:00
commit f8ac7262e0
82 changed files with 2644 additions and 475 deletions

View File

@ -223,7 +223,7 @@ let OutputGenerator = {
},
_getOutputName: function _getOutputName(aName) {
return aName.replace(' ', '');
return aName.replace(/\s/g, '');
},
roleRuleMap: {

View File

@ -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'
});
}
});

View File

@ -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>

View File

@ -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.

View File

@ -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.

View File

@ -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);
}

View File

@ -40,5 +40,5 @@ interface SourceBuffer : EventTarget {
[Throws]
void abort();
[Throws]
void remove(double start, double end);
void remove(double start, unrestricted double end);
};

View File

@ -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();
}

View File

@ -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.

View File

@ -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 */

View File

@ -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(

View File

@ -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_)

View File

@ -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)

View File

@ -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);
}

View File

@ -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");

View File

@ -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);

View File

@ -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;

View File

@ -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)

View File

@ -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);

View File

@ -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);

View File

@ -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 {

View File

@ -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; }

View File

@ -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") \

View File

@ -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()) {

View File

@ -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);

View File

@ -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);

View File

@ -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;

View 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");

View File

@ -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);

View File

@ -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) \

View File

@ -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;
}

View File

@ -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);
}

View File

@ -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();

View File

@ -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.

View File

@ -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();

View File

@ -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();

View File

@ -2856,7 +2856,6 @@ GetPreviousRawFrame(FrameType *frame)
JitProfilingFrameIterator::JitProfilingFrameIterator(void *exitFrame)
{
// Exit frame was en
ExitFrameLayout *frame = (ExitFrameLayout *) exitFrame;
FrameType prevType = frame->prevType();

View File

@ -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;
}

View File

@ -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
};

View File

@ -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;
}

View File

@ -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>

View File

@ -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);

View File

@ -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")

View File

@ -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)

View File

@ -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 */

View File

@ -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 */

View File

@ -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();

View File

@ -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)
{

View File

@ -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;

View File

@ -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 */

View File

@ -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

View 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");

View 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");

View 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");

View File

@ -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);

View 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");

View 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");

View 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");

View File

@ -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;
}
}

View 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");

View File

@ -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

View File

@ -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)
{

View File

@ -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);

View File

@ -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 = &REGS.sp[-2].toObject().as<PlainObject>();
obj = &REGS.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)

View File

@ -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);

View File

@ -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) \

View File

@ -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) \
\
/*

View File

@ -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);
}

View File

@ -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_;
}

View File

@ -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;
}

View File

@ -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;

View File

@ -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());

View File

@ -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;

View File

@ -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 "

View File

@ -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

View File

@ -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):

View File

@ -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:

View File

@ -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");

View File

@ -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

View File

@ -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);

View File

@ -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

View File

@ -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);
}