зеркало из https://github.com/mozilla/gecko-dev.git
Merge inbound to mozilla-central. a=merge
This commit is contained in:
Коммит
ec14417cc3
|
@ -283,6 +283,10 @@ exports.ANIMATION_TYPE_FOR_LONGHANDS = [
|
||||||
"border-bottom-right-radius",
|
"border-bottom-right-radius",
|
||||||
"border-top-left-radius",
|
"border-top-left-radius",
|
||||||
"border-top-right-radius",
|
"border-top-right-radius",
|
||||||
|
"border-start-start-radius",
|
||||||
|
"border-start-end-radius",
|
||||||
|
"border-end-start-radius",
|
||||||
|
"border-end-end-radius",
|
||||||
"bottom",
|
"bottom",
|
||||||
"column-gap",
|
"column-gap",
|
||||||
"column-width",
|
"column-width",
|
||||||
|
|
|
@ -2857,6 +2857,10 @@ exports.CSS_PROPERTIES = {
|
||||||
"border-inline-end-color",
|
"border-inline-end-color",
|
||||||
"border-inline-end-style",
|
"border-inline-end-style",
|
||||||
"border-inline-end-width",
|
"border-inline-end-width",
|
||||||
|
"border-start-start-radius",
|
||||||
|
"border-start-end-radius",
|
||||||
|
"border-end-start-radius",
|
||||||
|
"border-end-end-radius",
|
||||||
"margin-block-start",
|
"margin-block-start",
|
||||||
"margin-block-end",
|
"margin-block-end",
|
||||||
"margin-inline-start",
|
"margin-inline-start",
|
||||||
|
@ -4128,6 +4132,30 @@ exports.CSS_PROPERTIES = {
|
||||||
"unset"
|
"unset"
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
|
"border-end-end-radius": {
|
||||||
|
"isInherited": false,
|
||||||
|
"subproperties": [
|
||||||
|
"border-end-end-radius"
|
||||||
|
],
|
||||||
|
"supports": [],
|
||||||
|
"values": [
|
||||||
|
"inherit",
|
||||||
|
"initial",
|
||||||
|
"unset"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"border-end-start-radius": {
|
||||||
|
"isInherited": false,
|
||||||
|
"subproperties": [
|
||||||
|
"border-end-start-radius"
|
||||||
|
],
|
||||||
|
"supports": [],
|
||||||
|
"values": [
|
||||||
|
"inherit",
|
||||||
|
"initial",
|
||||||
|
"unset"
|
||||||
|
]
|
||||||
|
},
|
||||||
"border-image": {
|
"border-image": {
|
||||||
"isInherited": false,
|
"isInherited": false,
|
||||||
"subproperties": [
|
"subproperties": [
|
||||||
|
@ -4757,6 +4785,30 @@ exports.CSS_PROPERTIES = {
|
||||||
"unset"
|
"unset"
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
|
"border-start-end-radius": {
|
||||||
|
"isInherited": false,
|
||||||
|
"subproperties": [
|
||||||
|
"border-start-end-radius"
|
||||||
|
],
|
||||||
|
"supports": [],
|
||||||
|
"values": [
|
||||||
|
"inherit",
|
||||||
|
"initial",
|
||||||
|
"unset"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"border-start-start-radius": {
|
||||||
|
"isInherited": false,
|
||||||
|
"subproperties": [
|
||||||
|
"border-start-start-radius"
|
||||||
|
],
|
||||||
|
"supports": [],
|
||||||
|
"values": [
|
||||||
|
"inherit",
|
||||||
|
"initial",
|
||||||
|
"unset"
|
||||||
|
]
|
||||||
|
},
|
||||||
"border-style": {
|
"border-style": {
|
||||||
"isInherited": false,
|
"isInherited": false,
|
||||||
"subproperties": [
|
"subproperties": [
|
||||||
|
|
|
@ -2174,7 +2174,7 @@ static void DOMGCSliceCallback(JSContext* aCx, JS::GCProgress aProgress,
|
||||||
if (StaticPrefs::javascript_options_mem_notify() ||
|
if (StaticPrefs::javascript_options_mem_notify() ||
|
||||||
Telemetry::CanRecordExtended()) {
|
Telemetry::CanRecordExtended()) {
|
||||||
nsString json;
|
nsString json;
|
||||||
json.Adopt(aDesc.formatJSON(aCx, PR_Now()));
|
json.Adopt(aDesc.formatJSONTelemetry(aCx, PR_Now()));
|
||||||
RefPtr<NotifyGCEndRunnable> notify =
|
RefPtr<NotifyGCEndRunnable> notify =
|
||||||
new NotifyGCEndRunnable(std::move(json));
|
new NotifyGCEndRunnable(std::move(json));
|
||||||
SystemGroup::Dispatch(TaskCategory::GarbageCollection,
|
SystemGroup::Dispatch(TaskCategory::GarbageCollection,
|
||||||
|
|
|
@ -1447,7 +1447,7 @@ void SkScalerContext_Mac::generateImage(const SkGlyph& glyph) {
|
||||||
if ((glyph.fMaskFormat == SkMask::kLCD16_Format) ||
|
if ((glyph.fMaskFormat == SkMask::kLCD16_Format) ||
|
||||||
(glyph.fMaskFormat == SkMask::kA8_Format
|
(glyph.fMaskFormat == SkMask::kA8_Format
|
||||||
&& requestSmooth
|
&& requestSmooth
|
||||||
&& smooth_behavior() == SmoothBehavior::subpixel))
|
&& smooth_behavior() != SmoothBehavior::none))
|
||||||
{
|
{
|
||||||
const uint8_t* linear = gLinearCoverageFromCGLCDValue.data();
|
const uint8_t* linear = gLinearCoverageFromCGLCDValue.data();
|
||||||
|
|
||||||
|
|
|
@ -634,15 +634,16 @@ struct JS_PUBLIC_API GCDescription {
|
||||||
|
|
||||||
char16_t* formatSliceMessage(JSContext* cx) const;
|
char16_t* formatSliceMessage(JSContext* cx) const;
|
||||||
char16_t* formatSummaryMessage(JSContext* cx) const;
|
char16_t* formatSummaryMessage(JSContext* cx) const;
|
||||||
char16_t* formatJSON(JSContext* cx, uint64_t timestamp) const;
|
|
||||||
|
|
||||||
mozilla::TimeStamp startTime(JSContext* cx) const;
|
mozilla::TimeStamp startTime(JSContext* cx) const;
|
||||||
mozilla::TimeStamp endTime(JSContext* cx) const;
|
mozilla::TimeStamp endTime(JSContext* cx) const;
|
||||||
mozilla::TimeStamp lastSliceStart(JSContext* cx) const;
|
mozilla::TimeStamp lastSliceStart(JSContext* cx) const;
|
||||||
mozilla::TimeStamp lastSliceEnd(JSContext* cx) const;
|
mozilla::TimeStamp lastSliceEnd(JSContext* cx) const;
|
||||||
|
|
||||||
JS::UniqueChars sliceToJSON(JSContext* cx) const;
|
char16_t* formatJSONTelemetry(JSContext* cx, uint64_t timestamp) const;
|
||||||
JS::UniqueChars summaryToJSON(JSContext* cx) const;
|
|
||||||
|
JS::UniqueChars sliceToJSONProfiler(JSContext* cx) const;
|
||||||
|
JS::UniqueChars formatJSONProfiler(JSContext* cx) const;
|
||||||
|
|
||||||
JS::dbg::GarbageCollectionEvent::Ptr toGCEvent(JSContext* cx) const;
|
JS::dbg::GarbageCollectionEvent::Ptr toGCEvent(JSContext* cx) const;
|
||||||
};
|
};
|
||||||
|
|
|
@ -418,7 +418,7 @@ static bool GC(JSContext* cx, unsigned argc, Value* vp) {
|
||||||
}
|
}
|
||||||
|
|
||||||
#ifndef JS_MORE_DETERMINISTIC
|
#ifndef JS_MORE_DETERMINISTIC
|
||||||
size_t preBytes = cx->runtime()->gc.usage.gcBytes();
|
size_t preBytes = cx->runtime()->gc.heapSize.gcBytes();
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
if (zone) {
|
if (zone) {
|
||||||
|
@ -433,7 +433,7 @@ static bool GC(JSContext* cx, unsigned argc, Value* vp) {
|
||||||
char buf[256] = {'\0'};
|
char buf[256] = {'\0'};
|
||||||
#ifndef JS_MORE_DETERMINISTIC
|
#ifndef JS_MORE_DETERMINISTIC
|
||||||
SprintfLiteral(buf, "before %zu, after %zu\n", preBytes,
|
SprintfLiteral(buf, "before %zu, after %zu\n", preBytes,
|
||||||
cx->runtime()->gc.usage.gcBytes());
|
cx->runtime()->gc.heapSize.gcBytes());
|
||||||
#endif
|
#endif
|
||||||
return ReturnStringCopy(cx, args, buf);
|
return ReturnStringCopy(cx, args, buf);
|
||||||
}
|
}
|
||||||
|
|
|
@ -37,8 +37,10 @@
|
||||||
#include "frontend/ForOfEmitter.h"
|
#include "frontend/ForOfEmitter.h"
|
||||||
#include "frontend/ForOfLoopControl.h"
|
#include "frontend/ForOfLoopControl.h"
|
||||||
#include "frontend/IfEmitter.h"
|
#include "frontend/IfEmitter.h"
|
||||||
|
#include "frontend/LabelEmitter.h" // LabelEmitter
|
||||||
#include "frontend/ModuleSharedContext.h"
|
#include "frontend/ModuleSharedContext.h"
|
||||||
#include "frontend/NameOpEmitter.h"
|
#include "frontend/NameOpEmitter.h"
|
||||||
|
#include "frontend/ObjectEmitter.h" // PropertyEmitter, ObjectEmitter, ClassEmitter
|
||||||
#include "frontend/ParseNode.h"
|
#include "frontend/ParseNode.h"
|
||||||
#include "frontend/Parser.h"
|
#include "frontend/Parser.h"
|
||||||
#include "frontend/PropOpEmitter.h"
|
#include "frontend/PropOpEmitter.h"
|
||||||
|
@ -3030,24 +3032,30 @@ bool BytecodeEmitter::setOrEmitSetFunName(ParseNode* maybeFun,
|
||||||
if (maybeFun->isKind(ParseNodeKind::Function)) {
|
if (maybeFun->isKind(ParseNodeKind::Function)) {
|
||||||
// Function doesn't have 'name' property at this point.
|
// Function doesn't have 'name' property at this point.
|
||||||
// Set function's name at compile time.
|
// Set function's name at compile time.
|
||||||
JSFunction* fun = maybeFun->as<CodeNode>().funbox()->function();
|
return setFunName(maybeFun->as<CodeNode>().funbox()->function(), name);
|
||||||
|
|
||||||
// The inferred name may already be set if this function is an
|
|
||||||
// interpreted lazy function and we OOM'ed after we set the inferred
|
|
||||||
// name the first time.
|
|
||||||
if (fun->hasInferredName()) {
|
|
||||||
MOZ_ASSERT(fun->isInterpretedLazy());
|
|
||||||
MOZ_ASSERT(fun->inferredName() == name);
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
fun->setInferredName(name);
|
|
||||||
return true;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
MOZ_ASSERT(maybeFun->isKind(ParseNodeKind::ClassDecl));
|
MOZ_ASSERT(maybeFun->isKind(ParseNodeKind::ClassDecl));
|
||||||
|
|
||||||
|
return emitSetClassConstructorName(name);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool BytecodeEmitter::setFunName(JSFunction* fun, JSAtom* name) {
|
||||||
|
// The inferred name may already be set if this function is an interpreted
|
||||||
|
// lazy function and we OOM'ed after we set the inferred name the first
|
||||||
|
// time.
|
||||||
|
if (fun->hasInferredName()) {
|
||||||
|
MOZ_ASSERT(fun->isInterpretedLazy());
|
||||||
|
MOZ_ASSERT(fun->inferredName() == name);
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
fun->setInferredName(name);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool BytecodeEmitter::emitSetClassConstructorName(JSAtom* name) {
|
||||||
uint32_t nameIndex;
|
uint32_t nameIndex;
|
||||||
if (!makeAtomIndex(name, &nameIndex)) {
|
if (!makeAtomIndex(name, &nameIndex)) {
|
||||||
return false;
|
return false;
|
||||||
|
@ -7498,33 +7506,15 @@ MOZ_NEVER_INLINE bool BytecodeEmitter::emitIncOrDec(UnaryNode* incDec) {
|
||||||
// Using MOZ_NEVER_INLINE in here is a workaround for llvm.org/pr14047. See
|
// Using MOZ_NEVER_INLINE in here is a workaround for llvm.org/pr14047. See
|
||||||
// the comment on emitSwitch.
|
// the comment on emitSwitch.
|
||||||
MOZ_NEVER_INLINE bool BytecodeEmitter::emitLabeledStatement(
|
MOZ_NEVER_INLINE bool BytecodeEmitter::emitLabeledStatement(
|
||||||
const LabeledStatement* pn) {
|
const LabeledStatement* labeledStmt) {
|
||||||
/*
|
LabelEmitter label(this);
|
||||||
* Emit a JSOP_LABEL instruction. The argument is the offset to the statement
|
if (!label.emitLabel(labeledStmt->label())) {
|
||||||
* following the labeled statement.
|
|
||||||
*/
|
|
||||||
uint32_t index;
|
|
||||||
if (!makeAtomIndex(pn->label(), &index)) {
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
if (!emitTree(labeledStmt->statement())) {
|
||||||
JumpList top;
|
|
||||||
if (!emitJump(JSOP_LABEL, &top)) {
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
if (!label.emitEnd()) {
|
||||||
/* Emit code for the labeled statement. */
|
|
||||||
LabelControl controlInfo(this, pn->label(), offset());
|
|
||||||
|
|
||||||
if (!emitTree(pn->statement())) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Patch the JSOP_LABEL offset. */
|
|
||||||
JumpTarget brk{lastNonJumpTargetOffset()};
|
|
||||||
patchJumpsToTarget(top, brk);
|
|
||||||
|
|
||||||
if (!controlInfo.patchBreaks(this)) {
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -7567,27 +7557,31 @@ bool BytecodeEmitter::emitConditionalExpression(
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool BytecodeEmitter::emitPropertyList(ListNode* obj,
|
bool BytecodeEmitter::emitPropertyList(ListNode* obj, PropertyEmitter& pe,
|
||||||
MutableHandlePlainObject objp,
|
|
||||||
PropListType type) {
|
PropListType type) {
|
||||||
|
// [stack] CTOR? OBJ
|
||||||
|
|
||||||
for (ParseNode* propdef : obj->contents()) {
|
for (ParseNode* propdef : obj->contents()) {
|
||||||
if (propdef->is<ClassField>()) {
|
if (propdef->is<ClassField>()) {
|
||||||
// TODO(khyperia): Implement private field access.
|
// TODO(khyperia): Implement private field access.
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
if (!updateSourceCoordNotes(propdef->pn_pos.begin)) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Handle __proto__: v specially because *only* this form, and no other
|
// Handle __proto__: v specially because *only* this form, and no other
|
||||||
// involving "__proto__", performs [[Prototype]] mutation.
|
// involving "__proto__", performs [[Prototype]] mutation.
|
||||||
if (propdef->isKind(ParseNodeKind::MutateProto)) {
|
if (propdef->isKind(ParseNodeKind::MutateProto)) {
|
||||||
|
// [stack] OBJ
|
||||||
MOZ_ASSERT(type == ObjectLiteral);
|
MOZ_ASSERT(type == ObjectLiteral);
|
||||||
if (!emitTree(propdef->as<UnaryNode>().kid())) {
|
if (!pe.prepareForProtoValue(Some(propdef->pn_pos.begin))) {
|
||||||
|
// [stack] OBJ
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
objp.set(nullptr);
|
if (!emitTree(propdef->as<UnaryNode>().kid())) {
|
||||||
if (!emit1(JSOP_MUTATEPROTO)) {
|
// [stack] OBJ PROTO
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (!pe.emitMutateProto()) {
|
||||||
|
// [stack] OBJ
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
continue;
|
continue;
|
||||||
|
@ -7595,193 +7589,216 @@ bool BytecodeEmitter::emitPropertyList(ListNode* obj,
|
||||||
|
|
||||||
if (propdef->isKind(ParseNodeKind::Spread)) {
|
if (propdef->isKind(ParseNodeKind::Spread)) {
|
||||||
MOZ_ASSERT(type == ObjectLiteral);
|
MOZ_ASSERT(type == ObjectLiteral);
|
||||||
|
// [stack] OBJ
|
||||||
if (!emit1(JSOP_DUP)) {
|
if (!pe.prepareForSpreadOperand(Some(propdef->pn_pos.begin))) {
|
||||||
|
// [stack] OBJ OBJ
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!emitTree(propdef->as<UnaryNode>().kid())) {
|
if (!emitTree(propdef->as<UnaryNode>().kid())) {
|
||||||
|
// [stack] OBJ OBJ VAL
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
if (!pe.emitSpread()) {
|
||||||
if (!emitCopyDataProperties(CopyOption::Unfiltered)) {
|
// [stack] OBJ
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
objp.set(nullptr);
|
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool extraPop = false;
|
BinaryNode* prop = &propdef->as<BinaryNode>();
|
||||||
if (type == ClassBody && propdef->as<ClassMethod>().isStatic()) {
|
|
||||||
extraPop = true;
|
ParseNode* key = prop->left();
|
||||||
if (!emit1(JSOP_DUP2)) {
|
ParseNode* propVal = prop->right();
|
||||||
|
bool isPropertyAnonFunctionOrClass = propVal->isDirectRHSAnonFunction();
|
||||||
|
JSOp op = propdef->getOp();
|
||||||
|
MOZ_ASSERT(op == JSOP_INITPROP || op == JSOP_INITPROP_GETTER ||
|
||||||
|
op == JSOP_INITPROP_SETTER);
|
||||||
|
|
||||||
|
auto emitValue = [this, &propVal, &pe]() {
|
||||||
|
// [stack] CTOR? OBJ CTOR? KEY?
|
||||||
|
|
||||||
|
if (!emitTree(propVal)) {
|
||||||
|
// [stack] CTOR? OBJ CTOR? KEY? VAL
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
if (!emit1(JSOP_POP)) {
|
|
||||||
|
if (propVal->isKind(ParseNodeKind::Function) &&
|
||||||
|
propVal->as<CodeNode>().funbox()->needsHomeObject()) {
|
||||||
|
FunctionBox* funbox = propVal->as<CodeNode>().funbox();
|
||||||
|
MOZ_ASSERT(funbox->function()->allowSuperProperty());
|
||||||
|
|
||||||
|
if (!pe.emitInitHomeObject(funbox->asyncKind())) {
|
||||||
|
// [stack] CTOR? OBJ CTOR? KEY? FUN
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
};
|
||||||
|
|
||||||
|
PropertyEmitter::Kind kind =
|
||||||
|
(type == ClassBody && propdef->as<ClassMethod>().isStatic())
|
||||||
|
? PropertyEmitter::Kind::Static
|
||||||
|
: PropertyEmitter::Kind::Prototype;
|
||||||
|
if (key->isKind(ParseNodeKind::NumberExpr)) {
|
||||||
|
// [stack] CTOR? OBJ
|
||||||
|
if (!pe.prepareForIndexPropKey(Some(propdef->pn_pos.begin), kind)) {
|
||||||
|
// [stack] CTOR? OBJ CTOR?
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
if (!emitNumberOp(key->as<NumericLiteral>().value())) {
|
||||||
|
// [stack] CTOR? OBJ CTOR? KEY
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (!pe.prepareForIndexPropValue()) {
|
||||||
|
// [stack] CTOR? OBJ CTOR? KEY
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (!emitValue()) {
|
||||||
|
// [stack] CTOR? OBJ CTOR? KEY VAL
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
switch (op) {
|
||||||
|
case JSOP_INITPROP:
|
||||||
|
if (!pe.emitInitIndexProp(isPropertyAnonFunctionOrClass)) {
|
||||||
|
// [stack] CTOR? OBJ
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case JSOP_INITPROP_GETTER:
|
||||||
|
MOZ_ASSERT(!isPropertyAnonFunctionOrClass);
|
||||||
|
if (!pe.emitInitIndexGetter()) {
|
||||||
|
// [stack] CTOR? OBJ
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case JSOP_INITPROP_SETTER:
|
||||||
|
MOZ_ASSERT(!isPropertyAnonFunctionOrClass);
|
||||||
|
if (!pe.emitInitIndexSetter()) {
|
||||||
|
// [stack] CTOR? OBJ
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
MOZ_CRASH("Invalid op");
|
||||||
|
}
|
||||||
|
|
||||||
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Emit an index for t[2] for later consumption by JSOP_INITELEM. */
|
if (key->isKind(ParseNodeKind::ObjectPropertyName) ||
|
||||||
ParseNode* key = propdef->as<BinaryNode>().left();
|
key->isKind(ParseNodeKind::StringExpr)) {
|
||||||
bool isIndex = false;
|
// [stack] CTOR? OBJ
|
||||||
if (key->isKind(ParseNodeKind::NumberExpr)) {
|
|
||||||
if (!emitNumberOp(key->as<NumericLiteral>().value())) {
|
// emitClass took care of constructor already.
|
||||||
return false;
|
|
||||||
}
|
|
||||||
isIndex = true;
|
|
||||||
} else if (key->isKind(ParseNodeKind::ObjectPropertyName) ||
|
|
||||||
key->isKind(ParseNodeKind::StringExpr)) {
|
|
||||||
// EmitClass took care of constructor already.
|
|
||||||
if (type == ClassBody &&
|
if (type == ClassBody &&
|
||||||
key->as<NameNode>().atom() == cx->names().constructor &&
|
key->as<NameNode>().atom() == cx->names().constructor &&
|
||||||
!propdef->as<ClassMethod>().isStatic()) {
|
!propdef->as<ClassMethod>().isStatic()) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
} else {
|
|
||||||
MOZ_ASSERT(key->isKind(ParseNodeKind::ComputedName));
|
if (!pe.prepareForPropValue(Some(propdef->pn_pos.begin), kind)) {
|
||||||
if (!emitComputedPropertyName(&key->as<UnaryNode>())) {
|
// [stack] CTOR? OBJ CTOR?
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
isIndex = true;
|
if (!emitValue()) {
|
||||||
|
// [stack] CTOR? OBJ CTOR? VAL
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
RootedFunction anonFunction(cx);
|
||||||
|
if (isPropertyAnonFunctionOrClass) {
|
||||||
|
MOZ_ASSERT(op == JSOP_INITPROP);
|
||||||
|
|
||||||
|
if (propVal->isKind(ParseNodeKind::Function)) {
|
||||||
|
// When the value is function, we set the function's name
|
||||||
|
// at the compile-time, instead of emitting SETFUNNAME.
|
||||||
|
FunctionBox* funbox = propVal->as<CodeNode>().funbox();
|
||||||
|
anonFunction = funbox->function();
|
||||||
|
} else {
|
||||||
|
// Only object literal can have a property where key is
|
||||||
|
// name and value is an anonymous class.
|
||||||
|
//
|
||||||
|
// ({ foo: class {} });
|
||||||
|
MOZ_ASSERT(type == ObjectLiteral);
|
||||||
|
MOZ_ASSERT(propVal->isKind(ParseNodeKind::ClassDecl));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
RootedAtom keyAtom(cx, key->as<NameNode>().atom());
|
||||||
|
switch (op) {
|
||||||
|
case JSOP_INITPROP:
|
||||||
|
if (!pe.emitInitProp(keyAtom, isPropertyAnonFunctionOrClass,
|
||||||
|
anonFunction)) {
|
||||||
|
// [stack] CTOR? OBJ
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case JSOP_INITPROP_GETTER:
|
||||||
|
MOZ_ASSERT(!isPropertyAnonFunctionOrClass);
|
||||||
|
if (!pe.emitInitGetter(keyAtom)) {
|
||||||
|
// [stack] CTOR? OBJ
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case JSOP_INITPROP_SETTER:
|
||||||
|
MOZ_ASSERT(!isPropertyAnonFunctionOrClass);
|
||||||
|
if (!pe.emitInitSetter(keyAtom)) {
|
||||||
|
// [stack] CTOR? OBJ
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
MOZ_CRASH("Invalid op");
|
||||||
|
}
|
||||||
|
|
||||||
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Emit code for the property initializer. */
|
MOZ_ASSERT(key->isKind(ParseNodeKind::ComputedName));
|
||||||
ParseNode* propVal = propdef->as<BinaryNode>().right();
|
|
||||||
if (!emitTree(propVal)) {
|
// [stack] CTOR? OBJ
|
||||||
|
|
||||||
|
if (!pe.prepareForComputedPropKey(Some(propdef->pn_pos.begin), kind)) {
|
||||||
|
// [stack] CTOR? OBJ CTOR?
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (!emitTree(key->as<UnaryNode>().kid())) {
|
||||||
|
// [stack] CTOR? OBJ CTOR? KEY
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (!pe.prepareForComputedPropValue()) {
|
||||||
|
// [stack] CTOR? OBJ CTOR? KEY
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (!emitValue()) {
|
||||||
|
// [stack] CTOR? OBJ CTOR? KEY VAL
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
JSOp op = propdef->getOp();
|
switch (op) {
|
||||||
MOZ_ASSERT(op == JSOP_INITPROP || op == JSOP_INITPROP_GETTER ||
|
case JSOP_INITPROP:
|
||||||
op == JSOP_INITPROP_SETTER);
|
if (!pe.emitInitComputedProp(isPropertyAnonFunctionOrClass)) {
|
||||||
|
// [stack] CTOR? OBJ
|
||||||
FunctionPrefixKind prefixKind = op == JSOP_INITPROP_GETTER
|
|
||||||
? FunctionPrefixKind::Get
|
|
||||||
: op == JSOP_INITPROP_SETTER
|
|
||||||
? FunctionPrefixKind::Set
|
|
||||||
: FunctionPrefixKind::None;
|
|
||||||
|
|
||||||
if (op == JSOP_INITPROP_GETTER || op == JSOP_INITPROP_SETTER) {
|
|
||||||
objp.set(nullptr);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (propVal->isKind(ParseNodeKind::Function) &&
|
|
||||||
propVal->as<CodeNode>().funbox()->needsHomeObject()) {
|
|
||||||
FunctionBox* funbox = propVal->as<CodeNode>().funbox();
|
|
||||||
MOZ_ASSERT(funbox->function()->allowSuperProperty());
|
|
||||||
bool isAsync = funbox->isAsync();
|
|
||||||
if (isAsync) {
|
|
||||||
if (!emit1(JSOP_SWAP)) {
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
break;
|
||||||
if (!emitDupAt(1 + isIndex + isAsync)) {
|
case JSOP_INITPROP_GETTER:
|
||||||
return false;
|
MOZ_ASSERT(isPropertyAnonFunctionOrClass);
|
||||||
}
|
if (!pe.emitInitComputedGetter()) {
|
||||||
if (!emit1(JSOP_INITHOMEOBJECT)) {
|
// [stack] CTOR? OBJ
|
||||||
return false;
|
|
||||||
}
|
|
||||||
if (isAsync) {
|
|
||||||
if (!emit1(JSOP_POP)) {
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
break;
|
||||||
}
|
case JSOP_INITPROP_SETTER:
|
||||||
|
MOZ_ASSERT(isPropertyAnonFunctionOrClass);
|
||||||
// Class methods are not enumerable.
|
if (!pe.emitInitComputedSetter()) {
|
||||||
if (type == ClassBody) {
|
// [stack] CTOR? OBJ
|
||||||
switch (op) {
|
|
||||||
case JSOP_INITPROP:
|
|
||||||
op = JSOP_INITHIDDENPROP;
|
|
||||||
break;
|
|
||||||
case JSOP_INITPROP_GETTER:
|
|
||||||
op = JSOP_INITHIDDENPROP_GETTER;
|
|
||||||
break;
|
|
||||||
case JSOP_INITPROP_SETTER:
|
|
||||||
op = JSOP_INITHIDDENPROP_SETTER;
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
MOZ_CRASH("Invalid op");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (isIndex) {
|
|
||||||
objp.set(nullptr);
|
|
||||||
switch (op) {
|
|
||||||
case JSOP_INITPROP:
|
|
||||||
op = JSOP_INITELEM;
|
|
||||||
break;
|
|
||||||
case JSOP_INITHIDDENPROP:
|
|
||||||
op = JSOP_INITHIDDENELEM;
|
|
||||||
break;
|
|
||||||
case JSOP_INITPROP_GETTER:
|
|
||||||
op = JSOP_INITELEM_GETTER;
|
|
||||||
break;
|
|
||||||
case JSOP_INITHIDDENPROP_GETTER:
|
|
||||||
op = JSOP_INITHIDDENELEM_GETTER;
|
|
||||||
break;
|
|
||||||
case JSOP_INITPROP_SETTER:
|
|
||||||
op = JSOP_INITELEM_SETTER;
|
|
||||||
break;
|
|
||||||
case JSOP_INITHIDDENPROP_SETTER:
|
|
||||||
op = JSOP_INITHIDDENELEM_SETTER;
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
MOZ_CRASH("Invalid op");
|
|
||||||
}
|
|
||||||
if (propVal->isDirectRHSAnonFunction()) {
|
|
||||||
if (!emitDupAt(1)) {
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
if (!emit2(JSOP_SETFUNNAME, uint8_t(prefixKind))) {
|
break;
|
||||||
return false;
|
default:
|
||||||
}
|
MOZ_CRASH("Invalid op");
|
||||||
}
|
|
||||||
if (!emit1(op)) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
MOZ_ASSERT(key->isKind(ParseNodeKind::ObjectPropertyName) ||
|
|
||||||
key->isKind(ParseNodeKind::StringExpr));
|
|
||||||
|
|
||||||
uint32_t index;
|
|
||||||
if (!makeAtomIndex(key->as<NameNode>().atom(), &index)) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (objp) {
|
|
||||||
MOZ_ASSERT(type == ObjectLiteral);
|
|
||||||
MOZ_ASSERT(!IsHiddenInitOp(op));
|
|
||||||
MOZ_ASSERT(!objp->inDictionaryMode());
|
|
||||||
Rooted<jsid> id(cx, AtomToId(key->as<NameNode>().atom()));
|
|
||||||
if (!NativeDefineDataProperty(cx, objp, id, UndefinedHandleValue,
|
|
||||||
JSPROP_ENUMERATE)) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
if (objp->inDictionaryMode()) {
|
|
||||||
objp.set(nullptr);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (propVal->isDirectRHSAnonFunction()) {
|
|
||||||
MOZ_ASSERT(prefixKind == FunctionPrefixKind::None);
|
|
||||||
|
|
||||||
RootedAtom keyName(cx, key->as<NameNode>().atom());
|
|
||||||
if (!setOrEmitSetFunName(propVal, keyName)) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (!emitIndex32(op, index)) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (extraPop) {
|
|
||||||
if (!emit1(JSOP_POP)) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
|
@ -7795,43 +7812,22 @@ MOZ_NEVER_INLINE bool BytecodeEmitter::emitObject(ListNode* objNode) {
|
||||||
return emitSingletonInitialiser(objNode);
|
return emitSingletonInitialiser(objNode);
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
// [stack]
|
||||||
* Emit code for {p:a, '%q':b, 2:c} that is equivalent to constructing
|
|
||||||
* a new object and defining (in source order) each property on the object
|
ObjectEmitter oe(this);
|
||||||
* (or mutating the object's [[Prototype]], in the case of __proto__).
|
if (!oe.emitObject(objNode->count())) {
|
||||||
*/
|
// [stack] OBJ
|
||||||
ptrdiff_t offset = this->offset();
|
|
||||||
if (!emitNewInit()) {
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
if (!emitPropertyList(objNode, oe, ObjectLiteral)) {
|
||||||
// Try to construct the shape of the object as we go, so we can emit a
|
// [stack] OBJ
|
||||||
// JSOP_NEWOBJECT with the final shape instead.
|
|
||||||
// In the case of computed property names and indices, we cannot fix the
|
|
||||||
// shape at bytecode compile time. When the shape cannot be determined,
|
|
||||||
// |obj| is nulled out.
|
|
||||||
|
|
||||||
// No need to do any guessing for the object kind, since we know the upper
|
|
||||||
// bound of how many properties we plan to have.
|
|
||||||
gc::AllocKind kind = gc::GetGCObjectKind(objNode->count());
|
|
||||||
RootedPlainObject obj(
|
|
||||||
cx, NewBuiltinClassInstance<PlainObject>(cx, kind, TenuredObject));
|
|
||||||
if (!obj) {
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
if (!oe.emitEnd()) {
|
||||||
if (!emitPropertyList(objNode, &obj, ObjectLiteral)) {
|
// [stack] OBJ
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (obj) {
|
|
||||||
// The object survived and has a predictable shape: update the original
|
|
||||||
// bytecode.
|
|
||||||
if (!replaceNewInitWithNewObject(obj, offset)) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -8407,7 +8403,11 @@ bool BytecodeEmitter::emitFunctionBody(ParseNode* funBody) {
|
||||||
}
|
}
|
||||||
|
|
||||||
bool BytecodeEmitter::emitLexicalInitialization(NameNode* name) {
|
bool BytecodeEmitter::emitLexicalInitialization(NameNode* name) {
|
||||||
NameOpEmitter noe(this, name->name(), NameOpEmitter::Kind::Initialize);
|
return emitLexicalInitialization(name->name());
|
||||||
|
}
|
||||||
|
|
||||||
|
bool BytecodeEmitter::emitLexicalInitialization(JSAtom* name) {
|
||||||
|
NameOpEmitter noe(this, name, NameOpEmitter::Kind::Initialize);
|
||||||
if (!noe.prepareForRhs()) {
|
if (!noe.prepareForRhs()) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
@ -8424,272 +8424,108 @@ bool BytecodeEmitter::emitLexicalInitialization(NameNode* name) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
// This follows ES6 14.5.14 (ClassDefinitionEvaluation) and ES6 14.5.15
|
static MOZ_ALWAYS_INLINE CodeNode* FindConstructor(JSContext* cx,
|
||||||
// (BindingClassDeclarationEvaluation).
|
ListNode* classMethods) {
|
||||||
bool BytecodeEmitter::emitClass(ClassNode* classNode) {
|
for (ParseNode* mn : classMethods->contents()) {
|
||||||
ClassNames* names = classNode->names();
|
|
||||||
ParseNode* heritageExpression = classNode->heritage();
|
|
||||||
ListNode* classMembers = classNode->memberList();
|
|
||||||
CodeNode* constructor = nullptr;
|
|
||||||
for (ParseNode* mn : classMembers->contents()) {
|
|
||||||
if (mn->is<ClassField>()) {
|
if (mn->is<ClassField>()) {
|
||||||
// TODO(khyperia): Implement private field access.
|
// TODO(khyperia): Implement private field access.
|
||||||
return false;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
ClassMethod& method = mn->as<ClassMethod>();
|
ClassMethod& method = mn->as<ClassMethod>();
|
||||||
ParseNode& methodName = method.name();
|
ParseNode& methodName = method.name();
|
||||||
if (!method.isStatic() &&
|
if (!method.isStatic() &&
|
||||||
(methodName.isKind(ParseNodeKind::ObjectPropertyName) ||
|
(methodName.isKind(ParseNodeKind::ObjectPropertyName) ||
|
||||||
methodName.isKind(ParseNodeKind::StringExpr)) &&
|
methodName.isKind(ParseNodeKind::StringExpr)) &&
|
||||||
methodName.as<NameNode>().atom() == cx->names().constructor) {
|
methodName.as<NameNode>().atom() == cx->names().constructor) {
|
||||||
constructor = &method.method();
|
return &method.method();
|
||||||
break;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
bool savedStrictness = sc->setLocalStrictMode(true);
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
Maybe<TDZCheckCache> tdzCache;
|
// This follows ES6 14.5.14 (ClassDefinitionEvaluation) and ES6 14.5.15
|
||||||
Maybe<EmitterScope> emitterScope;
|
// (BindingClassDeclarationEvaluation).
|
||||||
|
bool BytecodeEmitter::emitClass(ClassNode* classNode) {
|
||||||
|
ClassNames* names = classNode->names();
|
||||||
|
ParseNode* heritageExpression = classNode->heritage();
|
||||||
|
ListNode* classMembers = classNode->memberList();
|
||||||
|
CodeNode* constructor = FindConstructor(cx, classMembers);
|
||||||
|
|
||||||
|
// [stack]
|
||||||
|
|
||||||
|
ClassEmitter ce(this);
|
||||||
|
RootedAtom innerName(cx);
|
||||||
|
ClassEmitter::Kind kind = ClassEmitter::Kind::Expression;
|
||||||
if (names) {
|
if (names) {
|
||||||
tdzCache.emplace(this);
|
innerName = names->innerBinding()->name();
|
||||||
emitterScope.emplace(this);
|
MOZ_ASSERT(innerName);
|
||||||
if (!emitterScope->enterLexical(this, ScopeKind::Lexical,
|
|
||||||
classNode->scopeBindings())) {
|
if (names->outerBinding()) {
|
||||||
|
MOZ_ASSERT(names->outerBinding()->name());
|
||||||
|
MOZ_ASSERT(names->outerBinding()->name() == innerName);
|
||||||
|
kind = ClassEmitter::Kind::Declaration;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!ce.emitScopeForNamedClass(classNode->scopeBindings())) {
|
||||||
|
// [stack]
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Pseudocode for class declarations:
|
|
||||||
//
|
|
||||||
// class extends BaseExpression {
|
|
||||||
// constructor() { ... }
|
|
||||||
// ...
|
|
||||||
// }
|
|
||||||
//
|
|
||||||
//
|
|
||||||
// if defined <BaseExpression> {
|
|
||||||
// let heritage = BaseExpression;
|
|
||||||
//
|
|
||||||
// if (heritage !== null) {
|
|
||||||
// funProto = heritage;
|
|
||||||
// objProto = heritage.prototype;
|
|
||||||
// } else {
|
|
||||||
// funProto = %FunctionPrototype%;
|
|
||||||
// objProto = null;
|
|
||||||
// }
|
|
||||||
// } else {
|
|
||||||
// objProto = %ObjectPrototype%;
|
|
||||||
// }
|
|
||||||
//
|
|
||||||
// let homeObject = ObjectCreate(objProto);
|
|
||||||
//
|
|
||||||
// if defined <constructor> {
|
|
||||||
// if defined <BaseExpression> {
|
|
||||||
// cons = DefineMethod(<constructor>, proto=homeObject,
|
|
||||||
// funProto=funProto);
|
|
||||||
// } else {
|
|
||||||
// cons = DefineMethod(<constructor>, proto=homeObject);
|
|
||||||
// }
|
|
||||||
// } else {
|
|
||||||
// if defined <BaseExpression> {
|
|
||||||
// cons = DefaultDerivedConstructor(proto=homeObject,
|
|
||||||
// funProto=funProto);
|
|
||||||
// } else {
|
|
||||||
// cons = DefaultConstructor(proto=homeObject);
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
//
|
|
||||||
// cons.prototype = homeObject;
|
|
||||||
// homeObject.constructor = cons;
|
|
||||||
//
|
|
||||||
// EmitPropertyList(...)
|
|
||||||
|
|
||||||
// This is kind of silly. In order to the get the home object defined on
|
// This is kind of silly. In order to the get the home object defined on
|
||||||
// the constructor, we have to make it second, but we want the prototype
|
// the constructor, we have to make it second, but we want the prototype
|
||||||
// on top for EmitPropertyList, because we expect static properties to be
|
// on top for EmitPropertyList, because we expect static properties to be
|
||||||
// rarer. The result is a few more swaps than we would like. Such is life.
|
// rarer. The result is a few more swaps than we would like. Such is life.
|
||||||
if (heritageExpression) {
|
bool isDerived = !!heritageExpression;
|
||||||
InternalIfEmitter ifThenElse(this);
|
if (isDerived) {
|
||||||
|
|
||||||
if (!emitTree(heritageExpression)) {
|
if (!emitTree(heritageExpression)) {
|
||||||
// [stack] ... HERITAGE
|
// [stack] HERITAGE
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
if (!ce.emitDerivedClass(innerName)) {
|
||||||
// Heritage must be null or a non-generator constructor
|
// [stack] HERITAGE HOMEOBJ
|
||||||
if (!emit1(JSOP_CHECKCLASSHERITAGE)) {
|
|
||||||
// [stack] ... HERITAGE
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
// [IF] (heritage !== null)
|
|
||||||
if (!emit1(JSOP_DUP)) {
|
|
||||||
// [stack] ... HERITAGE HERITAGE
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
if (!emit1(JSOP_NULL)) {
|
|
||||||
// [stack] ... HERITAGE HERITAGE NULL
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
if (!emit1(JSOP_STRICTNE)) {
|
|
||||||
// [stack] ... HERITAGE NE
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
// [THEN] funProto = heritage, objProto = heritage.prototype
|
|
||||||
if (!ifThenElse.emitThenElse()) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
if (!emit1(JSOP_DUP)) {
|
|
||||||
// [stack] ... HERITAGE HERITAGE
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
if (!emitAtomOp(cx->names().prototype, JSOP_GETPROP)) {
|
|
||||||
// [stack] ... HERITAGE PROTO
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
// [ELSE] funProto = %FunctionPrototype%, objProto = null
|
|
||||||
if (!ifThenElse.emitElse()) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
if (!emit1(JSOP_POP)) {
|
|
||||||
// [stack] ...
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
if (!emit2(JSOP_BUILTINPROTO, JSProto_Function)) {
|
|
||||||
// [stack] ... PROTO
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
if (!emit1(JSOP_NULL)) {
|
|
||||||
// [stack] ... PROTO NULL
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
// [ENDIF]
|
|
||||||
if (!ifThenElse.emitEnd()) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!emit1(JSOP_OBJWITHPROTO)) {
|
|
||||||
// [stack] ... HERITAGE HOMEOBJ
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
if (!emit1(JSOP_SWAP)) {
|
|
||||||
// [stack] ... HOMEOBJ HERITAGE
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
if (!emitNewInit()) {
|
if (!ce.emitClass(innerName)) {
|
||||||
// [stack] ... HOMEOBJ
|
// [stack] HOMEOBJ
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Stack currently has HOMEOBJ followed by optional HERITAGE. When HERITAGE
|
// Stack currently has HOMEOBJ followed by optional HERITAGE. When HERITAGE
|
||||||
// is not used, an implicit value of %FunctionPrototype% is implied.
|
// is not used, an implicit value of %FunctionPrototype% is implied.
|
||||||
|
|
||||||
if (constructor) {
|
if (constructor) {
|
||||||
if (!emitFunction(constructor, !!heritageExpression)) {
|
bool needsHomeObject = constructor->funbox()->needsHomeObject();
|
||||||
// [stack] ... HOMEOBJ CONSTRUCTOR
|
// HERITAGE is consumed inside emitFunction.
|
||||||
|
if (!emitFunction(constructor, isDerived)) {
|
||||||
|
// [stack] HOMEOBJ CTOR
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
if (constructor->funbox()->needsHomeObject()) {
|
if (!ce.emitInitConstructor(needsHomeObject)) {
|
||||||
if (!emitDupAt(1)) {
|
// [stack] CTOR HOMEOBJ
|
||||||
// [stack] ... HOMEOBJ CONSTRUCTOR HOMEOBJ
|
return false;
|
||||||
return false;
|
|
||||||
}
|
|
||||||
if (!emit1(JSOP_INITHOMEOBJECT)) {
|
|
||||||
// [stack] ... HOMEOBJ CONSTRUCTOR
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
// In the case of default class constructors, emit the start and end
|
if (!ce.emitInitDefaultConstructor(Some(classNode->pn_pos.begin),
|
||||||
// offsets in the source buffer as source notes so that when we
|
Some(classNode->pn_pos.end))) {
|
||||||
// actually make the constructor during execution, we can give it the
|
// [stack] CTOR HOMEOBJ
|
||||||
// correct toString output.
|
|
||||||
ptrdiff_t classStart = ptrdiff_t(classNode->pn_pos.begin);
|
|
||||||
ptrdiff_t classEnd = ptrdiff_t(classNode->pn_pos.end);
|
|
||||||
if (!newSrcNote3(SRC_CLASS_SPAN, classStart, classEnd)) {
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
JSAtom* name = names ? names->innerBinding()->as<NameNode>().atom()
|
|
||||||
: cx->names().empty;
|
|
||||||
if (heritageExpression) {
|
|
||||||
if (!emitAtomOp(name, JSOP_DERIVEDCONSTRUCTOR)) {
|
|
||||||
// [stack] ... HOMEOBJ CONSTRUCTOR
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
if (!emitAtomOp(name, JSOP_CLASSCONSTRUCTOR)) {
|
|
||||||
// [stack] ... HOMEOBJ CONSTRUCTOR
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
if (!emitPropertyList(classMembers, ce, ClassBody)) {
|
||||||
if (!emit1(JSOP_SWAP)) {
|
// [stack] CTOR HOMEOBJ
|
||||||
// [stack] ... CONSTRUCTOR HOMEOBJ
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
if (!ce.emitEnd(kind)) {
|
||||||
if (!emit1(JSOP_DUP2)) {
|
// [stack] # class declaration
|
||||||
// [stack] ... CONSTRUCTOR HOMEOBJ CONSTRUCTOR HOMEOBJ
|
// [stack]
|
||||||
|
// [stack] # class expression
|
||||||
|
// [stack] CTOR
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
if (!emitAtomOp(cx->names().prototype, JSOP_INITLOCKEDPROP)) {
|
|
||||||
// [stack] ... CONSTRUCTOR HOMEOBJ CONSTRUCTOR
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
if (!emitAtomOp(cx->names().constructor, JSOP_INITHIDDENPROP)) {
|
|
||||||
// [stack] ... CONSTRUCTOR HOMEOBJ
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
RootedPlainObject obj(cx);
|
|
||||||
if (!emitPropertyList(classMembers, &obj, ClassBody)) {
|
|
||||||
// [stack] ... CONSTRUCTOR HOMEOBJ
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!emit1(JSOP_POP)) {
|
|
||||||
// [stack] ... CONSTRUCTOR
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (names) {
|
|
||||||
NameNode* innerName = names->innerBinding();
|
|
||||||
if (!emitLexicalInitialization(innerName)) {
|
|
||||||
// [stack] ... CONSTRUCTOR
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Pop the inner scope.
|
|
||||||
if (!emitterScope->leave(this)) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
emitterScope.reset();
|
|
||||||
|
|
||||||
if (NameNode* outerName = names->outerBinding()) {
|
|
||||||
if (!emitLexicalInitialization(outerName)) {
|
|
||||||
// [stack] ... CONSTRUCTOR
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
// Only class statements make outer bindings, and they do not leave
|
|
||||||
// themselves on the stack.
|
|
||||||
if (!emit1(JSOP_POP)) {
|
|
||||||
// [stack] ...
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// The CONSTRUCTOR is left on stack if this is an expression.
|
|
||||||
|
|
||||||
MOZ_ALWAYS_TRUE(sc->setLocalStrictMode(savedStrictness));
|
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
|
@ -112,6 +112,7 @@ class CallOrNewEmitter;
|
||||||
class ElemOpEmitter;
|
class ElemOpEmitter;
|
||||||
class EmitterScope;
|
class EmitterScope;
|
||||||
class NestableControl;
|
class NestableControl;
|
||||||
|
class PropertyEmitter;
|
||||||
class TDZCheckCache;
|
class TDZCheckCache;
|
||||||
|
|
||||||
struct MOZ_STACK_CLASS BytecodeEmitter {
|
struct MOZ_STACK_CLASS BytecodeEmitter {
|
||||||
|
@ -604,8 +605,7 @@ struct MOZ_STACK_CLASS BytecodeEmitter {
|
||||||
|
|
||||||
MOZ_MUST_USE bool emitHoistedFunctionsInList(ListNode* stmtList);
|
MOZ_MUST_USE bool emitHoistedFunctionsInList(ListNode* stmtList);
|
||||||
|
|
||||||
MOZ_MUST_USE bool emitPropertyList(ListNode* obj,
|
MOZ_MUST_USE bool emitPropertyList(ListNode* obj, PropertyEmitter& pe,
|
||||||
MutableHandlePlainObject objp,
|
|
||||||
PropListType type);
|
PropListType type);
|
||||||
|
|
||||||
// To catch accidental misuse, emitUint16Operand/emit3 assert that they are
|
// To catch accidental misuse, emitUint16Operand/emit3 assert that they are
|
||||||
|
@ -687,7 +687,7 @@ struct MOZ_STACK_CLASS BytecodeEmitter {
|
||||||
MOZ_MUST_USE bool emitWith(BinaryNode* withNode);
|
MOZ_MUST_USE bool emitWith(BinaryNode* withNode);
|
||||||
|
|
||||||
MOZ_NEVER_INLINE MOZ_MUST_USE bool emitLabeledStatement(
|
MOZ_NEVER_INLINE MOZ_MUST_USE bool emitLabeledStatement(
|
||||||
const LabeledStatement* pn);
|
const LabeledStatement* labeledStmt);
|
||||||
MOZ_NEVER_INLINE MOZ_MUST_USE bool emitLexicalScope(
|
MOZ_NEVER_INLINE MOZ_MUST_USE bool emitLexicalScope(
|
||||||
LexicalScopeNode* lexicalScope);
|
LexicalScopeNode* lexicalScope);
|
||||||
MOZ_MUST_USE bool emitLexicalScopeBody(
|
MOZ_MUST_USE bool emitLexicalScopeBody(
|
||||||
|
@ -783,6 +783,8 @@ struct MOZ_STACK_CLASS BytecodeEmitter {
|
||||||
|
|
||||||
MOZ_MUST_USE bool setOrEmitSetFunName(ParseNode* maybeFun, HandleAtom name);
|
MOZ_MUST_USE bool setOrEmitSetFunName(ParseNode* maybeFun, HandleAtom name);
|
||||||
|
|
||||||
|
MOZ_MUST_USE bool setFunName(JSFunction* fun, JSAtom* name);
|
||||||
|
MOZ_MUST_USE bool emitSetClassConstructorName(JSAtom* name);
|
||||||
MOZ_MUST_USE bool emitInitializer(ParseNode* initializer, ParseNode* pattern);
|
MOZ_MUST_USE bool emitInitializer(ParseNode* initializer, ParseNode* pattern);
|
||||||
|
|
||||||
MOZ_MUST_USE bool emitCallSiteObject(CallSiteNode* callSiteObj);
|
MOZ_MUST_USE bool emitCallSiteObject(CallSiteNode* callSiteObj);
|
||||||
|
@ -851,6 +853,7 @@ struct MOZ_STACK_CLASS BytecodeEmitter {
|
||||||
MOZ_MUST_USE bool emitInitializeFunctionSpecialNames();
|
MOZ_MUST_USE bool emitInitializeFunctionSpecialNames();
|
||||||
MOZ_MUST_USE bool emitFunctionBody(ParseNode* funBody);
|
MOZ_MUST_USE bool emitFunctionBody(ParseNode* funBody);
|
||||||
MOZ_MUST_USE bool emitLexicalInitialization(NameNode* name);
|
MOZ_MUST_USE bool emitLexicalInitialization(NameNode* name);
|
||||||
|
MOZ_MUST_USE bool emitLexicalInitialization(JSAtom* name);
|
||||||
|
|
||||||
// Emit bytecode for the spread operator.
|
// Emit bytecode for the spread operator.
|
||||||
//
|
//
|
||||||
|
|
|
@ -0,0 +1,56 @@
|
||||||
|
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*-
|
||||||
|
* vim: set ts=8 sts=2 et sw=2 tw=80:
|
||||||
|
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||||
|
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||||
|
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||||
|
|
||||||
|
#include "frontend/LabelEmitter.h"
|
||||||
|
|
||||||
|
#include "mozilla/Assertions.h" // MOZ_ASSERT
|
||||||
|
|
||||||
|
#include "frontend/BytecodeEmitter.h" // BytecodeEmitter
|
||||||
|
#include "vm/Opcodes.h" // JSOP_*
|
||||||
|
|
||||||
|
using namespace js;
|
||||||
|
using namespace js::frontend;
|
||||||
|
|
||||||
|
bool LabelEmitter::emitLabel(JSAtom* name) {
|
||||||
|
MOZ_ASSERT(state_ == State::Start);
|
||||||
|
|
||||||
|
// Emit a JSOP_LABEL instruction. The operand is the offset to the
|
||||||
|
// statement following the labeled statement.
|
||||||
|
uint32_t index;
|
||||||
|
if (!bce_->makeAtomIndex(name, &index)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (!bce_->emitJump(JSOP_LABEL, &top_)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
controlInfo_.emplace(bce_, name, bce_->offset());
|
||||||
|
|
||||||
|
#ifdef DEBUG
|
||||||
|
state_ = State::Label;
|
||||||
|
#endif
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool LabelEmitter::emitEnd() {
|
||||||
|
MOZ_ASSERT(state_ == State::Label);
|
||||||
|
|
||||||
|
// Patch the JSOP_LABEL offset.
|
||||||
|
JumpTarget brk{bce_->lastNonJumpTargetOffset()};
|
||||||
|
bce_->patchJumpsToTarget(top_, brk);
|
||||||
|
|
||||||
|
// Patch the break/continue to this label.
|
||||||
|
if (!controlInfo_->patchBreaks(bce_)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
controlInfo_.reset();
|
||||||
|
|
||||||
|
#ifdef DEBUG
|
||||||
|
state_ = State::End;
|
||||||
|
#endif
|
||||||
|
return true;
|
||||||
|
}
|
|
@ -0,0 +1,70 @@
|
||||||
|
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*-
|
||||||
|
* vim: set ts=8 sts=2 et sw=2 tw=80:
|
||||||
|
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||||
|
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||||
|
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||||
|
|
||||||
|
#ifndef frontend_LabelEmitter_h
|
||||||
|
#define frontend_LabelEmitter_h
|
||||||
|
|
||||||
|
#include "mozilla/Attributes.h" // MOZ_MUST_USE, MOZ_STACK_CLASS
|
||||||
|
#include "mozilla/Maybe.h" // Maybe
|
||||||
|
|
||||||
|
#include "frontend/BytecodeControlStructures.h" // LabelControl
|
||||||
|
#include "frontend/JumpList.h" // JumpList
|
||||||
|
|
||||||
|
class JSAtom;
|
||||||
|
|
||||||
|
namespace js {
|
||||||
|
namespace frontend {
|
||||||
|
|
||||||
|
struct BytecodeEmitter;
|
||||||
|
|
||||||
|
// Class for emitting labeled statement.
|
||||||
|
//
|
||||||
|
// Usage: (check for the return value is omitted for simplicity)
|
||||||
|
//
|
||||||
|
// `label: expr;`
|
||||||
|
// LabelEmitter le(this);
|
||||||
|
// le.emitLabel(name_of_label);
|
||||||
|
// emit(expr);
|
||||||
|
// le.emitEnd();
|
||||||
|
//
|
||||||
|
class MOZ_STACK_CLASS LabelEmitter {
|
||||||
|
BytecodeEmitter* bce_;
|
||||||
|
|
||||||
|
// The offset of the JSOP_LABEL.
|
||||||
|
JumpList top_;
|
||||||
|
|
||||||
|
mozilla::Maybe<LabelControl> controlInfo_;
|
||||||
|
|
||||||
|
#ifdef DEBUG
|
||||||
|
// The state of this emitter.
|
||||||
|
//
|
||||||
|
// +-------+ emitLabel +-------+ emitEnd +-----+
|
||||||
|
// | Start |---------->| Label |-------->| End |
|
||||||
|
// +-------+ +-------+ +-----+
|
||||||
|
enum class State {
|
||||||
|
// The initial state.
|
||||||
|
Start,
|
||||||
|
|
||||||
|
// After calling emitLabel.
|
||||||
|
Label,
|
||||||
|
|
||||||
|
// After calling emitEnd.
|
||||||
|
End
|
||||||
|
};
|
||||||
|
State state_ = State::Start;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
public:
|
||||||
|
explicit LabelEmitter(BytecodeEmitter* bce) : bce_(bce) {}
|
||||||
|
|
||||||
|
MOZ_MUST_USE bool emitLabel(JSAtom* name);
|
||||||
|
MOZ_MUST_USE bool emitEnd();
|
||||||
|
};
|
||||||
|
|
||||||
|
} /* namespace frontend */
|
||||||
|
} /* namespace js */
|
||||||
|
|
||||||
|
#endif /* frontend_LabelEmitter_h */
|
|
@ -0,0 +1,813 @@
|
||||||
|
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*-
|
||||||
|
* vim: set ts=8 sts=2 et sw=2 tw=80:
|
||||||
|
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||||
|
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||||
|
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||||
|
|
||||||
|
#include "frontend/ObjectEmitter.h"
|
||||||
|
|
||||||
|
#include "mozilla/Assertions.h" // MOZ_ASSERT
|
||||||
|
|
||||||
|
#include "frontend/BytecodeEmitter.h" // BytecodeEmitter
|
||||||
|
#include "frontend/IfEmitter.h" // IfEmitter
|
||||||
|
#include "frontend/SharedContext.h" // SharedContext
|
||||||
|
#include "frontend/SourceNotes.h" // SRC_*
|
||||||
|
#include "gc/AllocKind.h" // AllocKind
|
||||||
|
#include "js/Id.h" // jsid
|
||||||
|
#include "js/Value.h" // UndefinedHandleValue
|
||||||
|
#include "vm/BytecodeUtil.h" // IsHiddenInitOp
|
||||||
|
#include "vm/JSContext.h" // JSContext
|
||||||
|
#include "vm/NativeObject.h" // NativeDefineDataProperty
|
||||||
|
#include "vm/ObjectGroup.h" // TenuredObject
|
||||||
|
#include "vm/Opcodes.h" // JSOP_*
|
||||||
|
#include "vm/Runtime.h" // JSAtomState (cx->names())
|
||||||
|
|
||||||
|
#include "gc/ObjectKind-inl.h" // GetGCObjectKind
|
||||||
|
#include "vm/JSAtom-inl.h" // AtomToId
|
||||||
|
#include "vm/JSObject-inl.h" // NewBuiltinClassInstance
|
||||||
|
|
||||||
|
using namespace js;
|
||||||
|
using namespace js::frontend;
|
||||||
|
|
||||||
|
using mozilla::Maybe;
|
||||||
|
|
||||||
|
PropertyEmitter::PropertyEmitter(BytecodeEmitter* bce)
|
||||||
|
: bce_(bce), obj_(bce->cx) {}
|
||||||
|
|
||||||
|
bool PropertyEmitter::prepareForProtoValue(const Maybe<uint32_t>& keyPos) {
|
||||||
|
MOZ_ASSERT(propertyState_ == PropertyState::Start ||
|
||||||
|
propertyState_ == PropertyState::Init);
|
||||||
|
|
||||||
|
// [stack] CTOR? OBJ CTOR?
|
||||||
|
|
||||||
|
if (keyPos) {
|
||||||
|
if (!bce_->updateSourceCoordNotes(*keyPos)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#ifdef DEBUG
|
||||||
|
propertyState_ = PropertyState::ProtoValue;
|
||||||
|
#endif
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool PropertyEmitter::emitMutateProto() {
|
||||||
|
MOZ_ASSERT(propertyState_ == PropertyState::ProtoValue);
|
||||||
|
|
||||||
|
// [stack] OBJ PROTO
|
||||||
|
|
||||||
|
if (!bce_->emit1(JSOP_MUTATEPROTO)) {
|
||||||
|
// [stack] OBJ
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
obj_ = nullptr;
|
||||||
|
#ifdef DEBUG
|
||||||
|
propertyState_ = PropertyState::Init;
|
||||||
|
#endif
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool PropertyEmitter::prepareForSpreadOperand(
|
||||||
|
const Maybe<uint32_t>& spreadPos) {
|
||||||
|
MOZ_ASSERT(propertyState_ == PropertyState::Start ||
|
||||||
|
propertyState_ == PropertyState::Init);
|
||||||
|
|
||||||
|
// [stack] OBJ
|
||||||
|
|
||||||
|
if (spreadPos) {
|
||||||
|
if (!bce_->updateSourceCoordNotes(*spreadPos)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (!bce_->emit1(JSOP_DUP)) {
|
||||||
|
// [stack] OBJ OBJ
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
#ifdef DEBUG
|
||||||
|
propertyState_ = PropertyState::SpreadOperand;
|
||||||
|
#endif
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool PropertyEmitter::emitSpread() {
|
||||||
|
MOZ_ASSERT(propertyState_ == PropertyState::SpreadOperand);
|
||||||
|
|
||||||
|
// [stack] OBJ OBJ VAL
|
||||||
|
|
||||||
|
if (!bce_->emitCopyDataProperties(BytecodeEmitter::CopyOption::Unfiltered)) {
|
||||||
|
// [stack] OBJ
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
obj_ = nullptr;
|
||||||
|
#ifdef DEBUG
|
||||||
|
propertyState_ = PropertyState::Init;
|
||||||
|
#endif
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
MOZ_ALWAYS_INLINE bool PropertyEmitter::prepareForProp(
|
||||||
|
const Maybe<uint32_t>& keyPos, bool isStatic, bool isIndexOrComputed) {
|
||||||
|
isStatic_ = isStatic;
|
||||||
|
isIndexOrComputed_ = isIndexOrComputed;
|
||||||
|
|
||||||
|
// [stack] CTOR? OBJ
|
||||||
|
|
||||||
|
if (keyPos) {
|
||||||
|
if (!bce_->updateSourceCoordNotes(*keyPos)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (isStatic_) {
|
||||||
|
if (!bce_->emit1(JSOP_DUP2)) {
|
||||||
|
// [stack] CTOR HOMEOBJ CTOR HOMEOBJ
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (!bce_->emit1(JSOP_POP)) {
|
||||||
|
// [stack] CTOR HOMEOBJ CTOR
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool PropertyEmitter::prepareForPropValue(const Maybe<uint32_t>& keyPos,
|
||||||
|
Kind kind /* = Kind::Prototype */) {
|
||||||
|
MOZ_ASSERT(propertyState_ == PropertyState::Start ||
|
||||||
|
propertyState_ == PropertyState::Init);
|
||||||
|
|
||||||
|
// [stack] CTOR? OBJ
|
||||||
|
|
||||||
|
if (!prepareForProp(keyPos,
|
||||||
|
/* isStatic_ = */ kind == Kind::Static,
|
||||||
|
/* isIndexOrComputed = */ false)) {
|
||||||
|
// [stack] CTOR? OBJ CTOR?
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
#ifdef DEBUG
|
||||||
|
propertyState_ = PropertyState::PropValue;
|
||||||
|
#endif
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool PropertyEmitter::prepareForIndexPropKey(
|
||||||
|
const Maybe<uint32_t>& keyPos, Kind kind /* = Kind::Prototype */) {
|
||||||
|
MOZ_ASSERT(propertyState_ == PropertyState::Start ||
|
||||||
|
propertyState_ == PropertyState::Init);
|
||||||
|
|
||||||
|
// [stack] CTOR? OBJ
|
||||||
|
|
||||||
|
obj_ = nullptr;
|
||||||
|
|
||||||
|
if (!prepareForProp(keyPos,
|
||||||
|
/* isStatic_ = */ kind == Kind::Static,
|
||||||
|
/* isIndexOrComputed = */ true)) {
|
||||||
|
// [stack] CTOR? OBJ CTOR?
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
#ifdef DEBUG
|
||||||
|
propertyState_ = PropertyState::IndexKey;
|
||||||
|
#endif
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool PropertyEmitter::prepareForIndexPropValue() {
|
||||||
|
MOZ_ASSERT(propertyState_ == PropertyState::IndexKey);
|
||||||
|
|
||||||
|
// [stack] CTOR? OBJ CTOR? KEY
|
||||||
|
|
||||||
|
#ifdef DEBUG
|
||||||
|
propertyState_ = PropertyState::IndexValue;
|
||||||
|
#endif
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool PropertyEmitter::prepareForComputedPropKey(
|
||||||
|
const Maybe<uint32_t>& keyPos, Kind kind /* = Kind::Prototype */) {
|
||||||
|
MOZ_ASSERT(propertyState_ == PropertyState::Start ||
|
||||||
|
propertyState_ == PropertyState::Init);
|
||||||
|
|
||||||
|
// [stack] CTOR? OBJ
|
||||||
|
|
||||||
|
obj_ = nullptr;
|
||||||
|
|
||||||
|
if (!prepareForProp(keyPos,
|
||||||
|
/* isStatic_ = */ kind == Kind::Static,
|
||||||
|
/* isIndexOrComputed = */ true)) {
|
||||||
|
// [stack] CTOR? OBJ CTOR?
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
#ifdef DEBUG
|
||||||
|
propertyState_ = PropertyState::ComputedKey;
|
||||||
|
#endif
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool PropertyEmitter::prepareForComputedPropValue() {
|
||||||
|
MOZ_ASSERT(propertyState_ == PropertyState::ComputedKey);
|
||||||
|
|
||||||
|
// [stack] CTOR? OBJ CTOR? KEY
|
||||||
|
|
||||||
|
if (!bce_->emit1(JSOP_TOID)) {
|
||||||
|
// [stack] CTOR? OBJ CTOR? KEY
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
#ifdef DEBUG
|
||||||
|
propertyState_ = PropertyState::ComputedValue;
|
||||||
|
#endif
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool PropertyEmitter::emitInitHomeObject(
|
||||||
|
FunctionAsyncKind kind /* = FunctionAsyncKind::SyncFunction */) {
|
||||||
|
MOZ_ASSERT(propertyState_ == PropertyState::PropValue ||
|
||||||
|
propertyState_ == PropertyState::IndexValue ||
|
||||||
|
propertyState_ == PropertyState::ComputedValue);
|
||||||
|
|
||||||
|
// [stack] CTOR? HOMEOBJ CTOR? KEY? FUN
|
||||||
|
|
||||||
|
bool isAsync = kind == FunctionAsyncKind::AsyncFunction;
|
||||||
|
if (isAsync) {
|
||||||
|
// [stack] CTOR? HOMEOBJ CTOR? KEY? UNWRAPPED WRAPPED
|
||||||
|
if (!bce_->emit1(JSOP_SWAP)) {
|
||||||
|
// [stack] CTOR? HOMEOBJ CTOR? KEY? WRAPPED UNWRAPPED
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// There are the following values on the stack conditionally, between
|
||||||
|
// HOMEOBJ and FUN:
|
||||||
|
// * the 2nd CTOR if isStatic_
|
||||||
|
// * KEY if isIndexOrComputed_
|
||||||
|
// * WRAPPED if isAsync
|
||||||
|
//
|
||||||
|
// JSOP_INITHOMEOBJECT uses one of the following:
|
||||||
|
// * HOMEOBJ if !isStatic_
|
||||||
|
// (`super.foo` points the super prototype property)
|
||||||
|
// * the 2nd CTOR if isStatic_
|
||||||
|
// (`super.foo` points the super constructor property)
|
||||||
|
if (!bce_->emitDupAt(1 + isIndexOrComputed_ + isAsync)) {
|
||||||
|
// [stack] # non-static method
|
||||||
|
// [stack] CTOR? HOMEOBJ CTOR KEY? WRAPPED? FUN CTOR
|
||||||
|
// [stack] # static method
|
||||||
|
// [stack] CTOR? HOMEOBJ KEY? WRAPPED? FUN HOMEOBJ
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (!bce_->emit1(JSOP_INITHOMEOBJECT)) {
|
||||||
|
// [stack] CTOR? HOMEOBJ CTOR? KEY? WRAPPED? FUN
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (isAsync) {
|
||||||
|
if (!bce_->emit1(JSOP_POP)) {
|
||||||
|
// [stack] CTOR? HOMEOBJ CTOR? KEY? WRAPPED
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#ifdef DEBUG
|
||||||
|
if (propertyState_ == PropertyState::PropValue) {
|
||||||
|
propertyState_ = PropertyState::InitHomeObj;
|
||||||
|
} else if (propertyState_ == PropertyState::IndexValue) {
|
||||||
|
propertyState_ = PropertyState::InitHomeObjForIndex;
|
||||||
|
} else {
|
||||||
|
propertyState_ = PropertyState::InitHomeObjForComputed;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool PropertyEmitter::emitInitProp(
|
||||||
|
JS::Handle<JSAtom*> key, bool isPropertyAnonFunctionOrClass /* = false */,
|
||||||
|
JS::Handle<JSFunction*> anonFunction /* = nullptr */) {
|
||||||
|
return emitInit(isClass_ ? JSOP_INITHIDDENPROP : JSOP_INITPROP, key,
|
||||||
|
isPropertyAnonFunctionOrClass, anonFunction);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool PropertyEmitter::emitInitGetter(JS::Handle<JSAtom*> key) {
|
||||||
|
return emitInit(isClass_ ? JSOP_INITHIDDENPROP_GETTER : JSOP_INITPROP_GETTER,
|
||||||
|
key, false, nullptr);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool PropertyEmitter::emitInitSetter(JS::Handle<JSAtom*> key) {
|
||||||
|
return emitInit(isClass_ ? JSOP_INITHIDDENPROP_SETTER : JSOP_INITPROP_SETTER,
|
||||||
|
key, false, nullptr);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool PropertyEmitter::emitInitIndexProp(
|
||||||
|
bool isPropertyAnonFunctionOrClass /* = false */) {
|
||||||
|
return emitInitIndexOrComputed(isClass_ ? JSOP_INITHIDDENELEM : JSOP_INITELEM,
|
||||||
|
FunctionPrefixKind::None,
|
||||||
|
isPropertyAnonFunctionOrClass);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool PropertyEmitter::emitInitIndexGetter() {
|
||||||
|
return emitInitIndexOrComputed(
|
||||||
|
isClass_ ? JSOP_INITHIDDENELEM_GETTER : JSOP_INITELEM_GETTER,
|
||||||
|
FunctionPrefixKind::Get, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool PropertyEmitter::emitInitIndexSetter() {
|
||||||
|
return emitInitIndexOrComputed(
|
||||||
|
isClass_ ? JSOP_INITHIDDENELEM_SETTER : JSOP_INITELEM_SETTER,
|
||||||
|
FunctionPrefixKind::Set, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool PropertyEmitter::emitInitComputedProp(
|
||||||
|
bool isPropertyAnonFunctionOrClass /* = false */) {
|
||||||
|
return emitInitIndexOrComputed(isClass_ ? JSOP_INITHIDDENELEM : JSOP_INITELEM,
|
||||||
|
FunctionPrefixKind::None,
|
||||||
|
isPropertyAnonFunctionOrClass);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool PropertyEmitter::emitInitComputedGetter() {
|
||||||
|
return emitInitIndexOrComputed(
|
||||||
|
isClass_ ? JSOP_INITHIDDENELEM_GETTER : JSOP_INITELEM_GETTER,
|
||||||
|
FunctionPrefixKind::Get, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool PropertyEmitter::emitInitComputedSetter() {
|
||||||
|
return emitInitIndexOrComputed(
|
||||||
|
isClass_ ? JSOP_INITHIDDENELEM_SETTER : JSOP_INITELEM_SETTER,
|
||||||
|
FunctionPrefixKind::Set, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool PropertyEmitter::emitInit(JSOp op, JS::Handle<JSAtom*> key,
|
||||||
|
bool isPropertyAnonFunctionOrClass,
|
||||||
|
JS::Handle<JSFunction*> anonFunction) {
|
||||||
|
MOZ_ASSERT(propertyState_ == PropertyState::PropValue ||
|
||||||
|
propertyState_ == PropertyState::InitHomeObj);
|
||||||
|
|
||||||
|
MOZ_ASSERT(op == JSOP_INITPROP || op == JSOP_INITHIDDENPROP ||
|
||||||
|
op == JSOP_INITPROP_GETTER || op == JSOP_INITHIDDENPROP_GETTER ||
|
||||||
|
op == JSOP_INITPROP_SETTER || op == JSOP_INITHIDDENPROP_SETTER);
|
||||||
|
|
||||||
|
// [stack] CTOR? OBJ CTOR? VAL
|
||||||
|
|
||||||
|
uint32_t index;
|
||||||
|
if (!bce_->makeAtomIndex(key, &index)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (obj_) {
|
||||||
|
MOZ_ASSERT(!IsHiddenInitOp(op));
|
||||||
|
MOZ_ASSERT(!obj_->inDictionaryMode());
|
||||||
|
JS::Rooted<JS::PropertyKey> propKey(bce_->cx, AtomToId(key));
|
||||||
|
if (!NativeDefineDataProperty(bce_->cx, obj_, propKey, UndefinedHandleValue,
|
||||||
|
JSPROP_ENUMERATE)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (obj_->inDictionaryMode()) {
|
||||||
|
obj_ = nullptr;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (isPropertyAnonFunctionOrClass) {
|
||||||
|
MOZ_ASSERT(op == JSOP_INITPROP || op == JSOP_INITHIDDENPROP);
|
||||||
|
|
||||||
|
if (anonFunction) {
|
||||||
|
if (!bce_->setFunName(anonFunction, key)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// NOTE: This is setting the constructor's name of the class which is
|
||||||
|
// the property value. Not of the enclosing class.
|
||||||
|
if (!bce_->emitSetClassConstructorName(key)) {
|
||||||
|
// [stack] CTOR? OBJ CTOR? FUN
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!bce_->emitIndex32(op, index)) {
|
||||||
|
// [stack] CTOR? OBJ CTOR?
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!emitPopClassConstructor()) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
#ifdef DEBUG
|
||||||
|
propertyState_ = PropertyState::Init;
|
||||||
|
#endif
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool PropertyEmitter::emitInitIndexOrComputed(
|
||||||
|
JSOp op, FunctionPrefixKind prefixKind,
|
||||||
|
bool isPropertyAnonFunctionOrClass) {
|
||||||
|
MOZ_ASSERT(propertyState_ == PropertyState::IndexValue ||
|
||||||
|
propertyState_ == PropertyState::InitHomeObjForIndex ||
|
||||||
|
propertyState_ == PropertyState::ComputedValue ||
|
||||||
|
propertyState_ == PropertyState::InitHomeObjForComputed);
|
||||||
|
|
||||||
|
MOZ_ASSERT(op == JSOP_INITELEM || op == JSOP_INITHIDDENELEM ||
|
||||||
|
op == JSOP_INITELEM_GETTER || op == JSOP_INITHIDDENELEM_GETTER ||
|
||||||
|
op == JSOP_INITELEM_SETTER || op == JSOP_INITHIDDENELEM_SETTER);
|
||||||
|
|
||||||
|
// [stack] CTOR? OBJ CTOR? KEY VAL
|
||||||
|
|
||||||
|
if (isPropertyAnonFunctionOrClass) {
|
||||||
|
if (!bce_->emitDupAt(1)) {
|
||||||
|
// [stack] CTOR? OBJ CTOR? KEY FUN FUN
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (!bce_->emit2(JSOP_SETFUNNAME, uint8_t(prefixKind))) {
|
||||||
|
// [stack] CTOR? OBJ CTOR? KEY FUN
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!bce_->emit1(op)) {
|
||||||
|
// [stack] CTOR? OBJ CTOR?
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!emitPopClassConstructor()) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
#ifdef DEBUG
|
||||||
|
propertyState_ = PropertyState::Init;
|
||||||
|
#endif
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool PropertyEmitter::emitPopClassConstructor() {
|
||||||
|
if (isStatic_) {
|
||||||
|
// [stack] CTOR HOMEOBJ CTOR
|
||||||
|
|
||||||
|
if (!bce_->emit1(JSOP_POP)) {
|
||||||
|
// [stack] CTOR HOMEOBJ
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
ObjectEmitter::ObjectEmitter(BytecodeEmitter* bce) : PropertyEmitter(bce) {}
|
||||||
|
|
||||||
|
bool ObjectEmitter::emitObject(size_t propertyCount) {
|
||||||
|
MOZ_ASSERT(propertyState_ == PropertyState::Start);
|
||||||
|
MOZ_ASSERT(objectState_ == ObjectState::Start);
|
||||||
|
|
||||||
|
// [stack]
|
||||||
|
|
||||||
|
// Emit code for {p:a, '%q':b, 2:c} that is equivalent to constructing
|
||||||
|
// a new object and defining (in source order) each property on the object
|
||||||
|
// (or mutating the object's [[Prototype]], in the case of __proto__).
|
||||||
|
top_ = bce_->offset();
|
||||||
|
if (!bce_->emitNewInit()) {
|
||||||
|
// [stack] OBJ
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Try to construct the shape of the object as we go, so we can emit a
|
||||||
|
// JSOP_NEWOBJECT with the final shape instead.
|
||||||
|
// In the case of computed property names and indices, we cannot fix the
|
||||||
|
// shape at bytecode compile time. When the shape cannot be determined,
|
||||||
|
// |obj| is nulled out.
|
||||||
|
|
||||||
|
// No need to do any guessing for the object kind, since we know the upper
|
||||||
|
// bound of how many properties we plan to have.
|
||||||
|
gc::AllocKind kind = gc::GetGCObjectKind(propertyCount);
|
||||||
|
obj_ = NewBuiltinClassInstance<PlainObject>(bce_->cx, kind, TenuredObject);
|
||||||
|
if (!obj_) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
#ifdef DEBUG
|
||||||
|
objectState_ = ObjectState::Object;
|
||||||
|
#endif
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool ObjectEmitter::emitEnd() {
|
||||||
|
MOZ_ASSERT(propertyState_ == PropertyState::Start ||
|
||||||
|
propertyState_ == PropertyState::Init);
|
||||||
|
MOZ_ASSERT(objectState_ == ObjectState::Object);
|
||||||
|
|
||||||
|
// [stack] OBJ
|
||||||
|
|
||||||
|
if (obj_) {
|
||||||
|
// The object survived and has a predictable shape: update the original
|
||||||
|
// bytecode.
|
||||||
|
if (!bce_->replaceNewInitWithNewObject(obj_, top_)) {
|
||||||
|
// [stack] OBJ
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#ifdef DEBUG
|
||||||
|
objectState_ = ObjectState::End;
|
||||||
|
#endif
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
AutoSaveLocalStrictMode::AutoSaveLocalStrictMode(SharedContext* sc) : sc_(sc) {
|
||||||
|
savedStrictness_ = sc_->setLocalStrictMode(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
AutoSaveLocalStrictMode::~AutoSaveLocalStrictMode() {
|
||||||
|
if (sc_) {
|
||||||
|
restore();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void AutoSaveLocalStrictMode::restore() {
|
||||||
|
MOZ_ALWAYS_TRUE(sc_->setLocalStrictMode(savedStrictness_));
|
||||||
|
sc_ = nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
ClassEmitter::ClassEmitter(BytecodeEmitter* bce)
|
||||||
|
: PropertyEmitter(bce), strictMode_(bce->sc), name_(bce->cx) {
|
||||||
|
isClass_ = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool ClassEmitter::emitScopeForNamedClass(
|
||||||
|
JS::Handle<LexicalScope::Data*> scopeBindings) {
|
||||||
|
MOZ_ASSERT(propertyState_ == PropertyState::Start);
|
||||||
|
MOZ_ASSERT(classState_ == ClassState::Start);
|
||||||
|
|
||||||
|
tdzCacheForInnerName_.emplace(bce_);
|
||||||
|
innerNameScope_.emplace(bce_);
|
||||||
|
if (!innerNameScope_->enterLexical(bce_, ScopeKind::Lexical, scopeBindings)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
#ifdef DEBUG
|
||||||
|
classState_ = ClassState::Scope;
|
||||||
|
#endif
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool ClassEmitter::emitClass(JS::Handle<JSAtom*> name) {
|
||||||
|
MOZ_ASSERT(propertyState_ == PropertyState::Start);
|
||||||
|
MOZ_ASSERT(classState_ == ClassState::Start ||
|
||||||
|
classState_ == ClassState::Scope);
|
||||||
|
|
||||||
|
// [stack]
|
||||||
|
|
||||||
|
setName(name);
|
||||||
|
isDerived_ = false;
|
||||||
|
|
||||||
|
if (!bce_->emitNewInit()) {
|
||||||
|
// [stack] HOMEOBJ
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
#ifdef DEBUG
|
||||||
|
classState_ = ClassState::Class;
|
||||||
|
#endif
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool ClassEmitter::emitDerivedClass(JS::Handle<JSAtom*> name) {
|
||||||
|
MOZ_ASSERT(propertyState_ == PropertyState::Start);
|
||||||
|
MOZ_ASSERT(classState_ == ClassState::Start ||
|
||||||
|
classState_ == ClassState::Scope);
|
||||||
|
|
||||||
|
// [stack]
|
||||||
|
|
||||||
|
setName(name);
|
||||||
|
isDerived_ = true;
|
||||||
|
|
||||||
|
InternalIfEmitter ifThenElse(bce_);
|
||||||
|
|
||||||
|
// Heritage must be null or a non-generator constructor
|
||||||
|
if (!bce_->emit1(JSOP_CHECKCLASSHERITAGE)) {
|
||||||
|
// [stack] HERITAGE
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// [IF] (heritage !== null)
|
||||||
|
if (!bce_->emit1(JSOP_DUP)) {
|
||||||
|
// [stack] HERITAGE HERITAGE
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (!bce_->emit1(JSOP_NULL)) {
|
||||||
|
// [stack] HERITAGE HERITAGE NULL
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (!bce_->emit1(JSOP_STRICTNE)) {
|
||||||
|
// [stack] HERITAGE NE
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// [THEN] funProto = heritage, objProto = heritage.prototype
|
||||||
|
if (!ifThenElse.emitThenElse()) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (!bce_->emit1(JSOP_DUP)) {
|
||||||
|
// [stack] HERITAGE HERITAGE
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (!bce_->emitAtomOp(bce_->cx->names().prototype, JSOP_GETPROP)) {
|
||||||
|
// [stack] HERITAGE PROTO
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// [ELSE] funProto = %FunctionPrototype%, objProto = null
|
||||||
|
if (!ifThenElse.emitElse()) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (!bce_->emit1(JSOP_POP)) {
|
||||||
|
// [stack]
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (!bce_->emit2(JSOP_BUILTINPROTO, JSProto_Function)) {
|
||||||
|
// [stack] PROTO
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (!bce_->emit1(JSOP_NULL)) {
|
||||||
|
// [stack] PROTO NULL
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// [ENDIF]
|
||||||
|
if (!ifThenElse.emitEnd()) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!bce_->emit1(JSOP_OBJWITHPROTO)) {
|
||||||
|
// [stack] HERITAGE HOMEOBJ
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (!bce_->emit1(JSOP_SWAP)) {
|
||||||
|
// [stack] HOMEOBJ HERITAGE
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
#ifdef DEBUG
|
||||||
|
classState_ = ClassState::Class;
|
||||||
|
#endif
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void ClassEmitter::setName(JS::Handle<JSAtom*> name) {
|
||||||
|
name_ = name;
|
||||||
|
if (!name_) {
|
||||||
|
name_ = bce_->cx->names().empty;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bool ClassEmitter::emitInitConstructor(bool needsHomeObject) {
|
||||||
|
MOZ_ASSERT(propertyState_ == PropertyState::Start);
|
||||||
|
MOZ_ASSERT(classState_ == ClassState::Class);
|
||||||
|
|
||||||
|
// [stack] HOMEOBJ CTOR
|
||||||
|
|
||||||
|
if (needsHomeObject) {
|
||||||
|
if (!bce_->emitDupAt(1)) {
|
||||||
|
// [stack] HOMEOBJ CTOR HOMEOBJ
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (!bce_->emit1(JSOP_INITHOMEOBJECT)) {
|
||||||
|
// [stack] HOMEOBJ CTOR
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!initProtoAndCtor()) {
|
||||||
|
// [stack] CTOR HOMEOBJ
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
#ifdef DEBUG
|
||||||
|
classState_ = ClassState::InitConstructor;
|
||||||
|
#endif
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool ClassEmitter::emitInitDefaultConstructor(const Maybe<uint32_t>& classStart,
|
||||||
|
const Maybe<uint32_t>& classEnd) {
|
||||||
|
MOZ_ASSERT(propertyState_ == PropertyState::Start);
|
||||||
|
MOZ_ASSERT(classState_ == ClassState::Class);
|
||||||
|
|
||||||
|
if (classStart && classEnd) {
|
||||||
|
// In the case of default class constructors, emit the start and end
|
||||||
|
// offsets in the source buffer as source notes so that when we
|
||||||
|
// actually make the constructor during execution, we can give it the
|
||||||
|
// correct toString output.
|
||||||
|
if (!bce_->newSrcNote3(SRC_CLASS_SPAN, ptrdiff_t(*classStart),
|
||||||
|
ptrdiff_t(*classEnd))) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (isDerived_) {
|
||||||
|
// [stack] HERITAGE PROTO
|
||||||
|
if (!bce_->emitAtomOp(name_, JSOP_DERIVEDCONSTRUCTOR)) {
|
||||||
|
// [stack] HOMEOBJ CTOR
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// [stack] HOMEOBJ
|
||||||
|
if (!bce_->emitAtomOp(name_, JSOP_CLASSCONSTRUCTOR)) {
|
||||||
|
// [stack] HOMEOBJ CTOR
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!initProtoAndCtor()) {
|
||||||
|
// [stack] CTOR HOMEOBJ
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
#ifdef DEBUG
|
||||||
|
classState_ = ClassState::InitConstructor;
|
||||||
|
#endif
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool ClassEmitter::initProtoAndCtor() {
|
||||||
|
// [stack] HOMEOBJ CTOR
|
||||||
|
|
||||||
|
if (!bce_->emit1(JSOP_SWAP)) {
|
||||||
|
// [stack] CTOR HOMEOBJ
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (!bce_->emit1(JSOP_DUP2)) {
|
||||||
|
// [stack] CTOR HOMEOBJ CTOR HOMEOBJ
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (!bce_->emitAtomOp(bce_->cx->names().prototype, JSOP_INITLOCKEDPROP)) {
|
||||||
|
// [stack] CTOR HOMEOBJ CTOR
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (!bce_->emitAtomOp(bce_->cx->names().constructor, JSOP_INITHIDDENPROP)) {
|
||||||
|
// [stack] CTOR HOMEOBJ
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool ClassEmitter::emitEnd(Kind kind) {
|
||||||
|
MOZ_ASSERT(propertyState_ == PropertyState::Start ||
|
||||||
|
propertyState_ == PropertyState::Init);
|
||||||
|
MOZ_ASSERT(classState_ == ClassState::InitConstructor);
|
||||||
|
|
||||||
|
// [stack] CTOR HOMEOBJ
|
||||||
|
|
||||||
|
if (!bce_->emit1(JSOP_POP)) {
|
||||||
|
// [stack] CTOR
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (name_ != bce_->cx->names().empty) {
|
||||||
|
MOZ_ASSERT(tdzCacheForInnerName_.isSome());
|
||||||
|
MOZ_ASSERT(innerNameScope_.isSome());
|
||||||
|
|
||||||
|
if (!bce_->emitLexicalInitialization(name_)) {
|
||||||
|
// [stack] CTOR
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!innerNameScope_->leave(bce_)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
innerNameScope_.reset();
|
||||||
|
|
||||||
|
if (kind == Kind::Declaration) {
|
||||||
|
if (!bce_->emitLexicalInitialization(name_)) {
|
||||||
|
// [stack] CTOR
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
// Only class statements make outer bindings, and they do not leave
|
||||||
|
// themselves on the stack.
|
||||||
|
if (!bce_->emit1(JSOP_POP)) {
|
||||||
|
// [stack]
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
tdzCacheForInnerName_.reset();
|
||||||
|
} else {
|
||||||
|
// [stack] CTOR
|
||||||
|
|
||||||
|
MOZ_ASSERT(tdzCacheForInnerName_.isNothing());
|
||||||
|
}
|
||||||
|
|
||||||
|
// [stack] # class declaration
|
||||||
|
// [stack]
|
||||||
|
// [stack] # class expression
|
||||||
|
// [stack] CTOR
|
||||||
|
|
||||||
|
strictMode_.restore();
|
||||||
|
|
||||||
|
#ifdef DEBUG
|
||||||
|
classState_ = ClassState::End;
|
||||||
|
#endif
|
||||||
|
return true;
|
||||||
|
}
|
|
@ -0,0 +1,722 @@
|
||||||
|
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*-
|
||||||
|
* vim: set ts=8 sts=2 et sw=2 tw=80:
|
||||||
|
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||||
|
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||||
|
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||||
|
|
||||||
|
#ifndef frontend_ObjectEmitter_h
|
||||||
|
#define frontend_ObjectEmitter_h
|
||||||
|
|
||||||
|
#include "mozilla/Attributes.h" // MOZ_MUST_USE, MOZ_STACK_CLASS, MOZ_ALWAYS_INLINE, MOZ_RAII
|
||||||
|
#include "mozilla/Maybe.h" // Maybe
|
||||||
|
|
||||||
|
#include <stddef.h> // size_t, ptrdiff_t
|
||||||
|
#include <stdint.h> // uint32_t
|
||||||
|
|
||||||
|
#include "frontend/EmitterScope.h" // EmitterScope
|
||||||
|
#include "frontend/TDZCheckCache.h" // TDZCheckCache
|
||||||
|
#include "js/RootingAPI.h" // JS::Handle, JS::Rooted
|
||||||
|
#include "vm/BytecodeUtil.h" // JSOp
|
||||||
|
#include "vm/JSAtom.h" // JSAtom
|
||||||
|
#include "vm/JSFunction.h" // JSFunction, FunctionPrefixKind
|
||||||
|
#include "vm/JSScript.h" // FunctionAsyncKind
|
||||||
|
#include "vm/NativeObject.h" // PlainObject
|
||||||
|
#include "vm/Scope.h" // LexicalScope
|
||||||
|
|
||||||
|
namespace js {
|
||||||
|
|
||||||
|
namespace frontend {
|
||||||
|
|
||||||
|
struct BytecodeEmitter;
|
||||||
|
class SharedContext;
|
||||||
|
|
||||||
|
// Class for emitting bytecode for object and class properties.
|
||||||
|
// See ObjectEmitter and ClassEmitter for usage.
|
||||||
|
class MOZ_STACK_CLASS PropertyEmitter {
|
||||||
|
public:
|
||||||
|
enum class Kind {
|
||||||
|
// Prototype property.
|
||||||
|
Prototype,
|
||||||
|
|
||||||
|
// Class static property.
|
||||||
|
Static
|
||||||
|
};
|
||||||
|
|
||||||
|
protected:
|
||||||
|
BytecodeEmitter* bce_;
|
||||||
|
|
||||||
|
// True if the object is class.
|
||||||
|
// Set by ClassEmitter.
|
||||||
|
bool isClass_ = false;
|
||||||
|
|
||||||
|
// True if the property is class static method.
|
||||||
|
bool isStatic_ = false;
|
||||||
|
|
||||||
|
// True if the property has computed or index key.
|
||||||
|
bool isIndexOrComputed_ = false;
|
||||||
|
|
||||||
|
// An object which keeps the shape of this object literal.
|
||||||
|
// This fields is reset to nullptr whenever the object literal turns out to
|
||||||
|
// have at least one numeric, computed, spread or __proto__ property, or
|
||||||
|
// the object becomes dictionary mode.
|
||||||
|
// This field is used only in ObjectEmitter.
|
||||||
|
JS::Rooted<PlainObject*> obj_;
|
||||||
|
|
||||||
|
#ifdef DEBUG
|
||||||
|
// The state of this emitter.
|
||||||
|
//
|
||||||
|
// +-------+
|
||||||
|
// | Start |-+
|
||||||
|
// +-------+ |
|
||||||
|
// |
|
||||||
|
// +---------+
|
||||||
|
// |
|
||||||
|
// | +------------------------------------------------------------+
|
||||||
|
// | | |
|
||||||
|
// | | [normal property/method/accessor] |
|
||||||
|
// | v prepareForPropValue +-----------+ +------+ |
|
||||||
|
// +->+----------------------->| PropValue |-+ +->| Init |-+
|
||||||
|
// | +-----------+ | | +------+
|
||||||
|
// | | |
|
||||||
|
// | +----------------------------------+ +-----------+
|
||||||
|
// | | |
|
||||||
|
// | +-+---------------------------------------+ |
|
||||||
|
// | | | |
|
||||||
|
// | | [method with super] | |
|
||||||
|
// | | emitInitHomeObject +-------------+ v |
|
||||||
|
// | +--------------------->| InitHomeObj |->+ |
|
||||||
|
// | +-------------+ | |
|
||||||
|
// | | |
|
||||||
|
// | +-------------------------------------- + |
|
||||||
|
// | | |
|
||||||
|
// | | emitInitProp |
|
||||||
|
// | | emitInitGetter |
|
||||||
|
// | | emitInitSetter |
|
||||||
|
// | +------------------------------------------------------>+
|
||||||
|
// | ^
|
||||||
|
// | [index property/method/accessor] |
|
||||||
|
// | prepareForIndexPropKey +----------+ |
|
||||||
|
// +-------------------------->| IndexKey |-+ |
|
||||||
|
// | +----------+ | |
|
||||||
|
// | | |
|
||||||
|
// | +-------------------------------------+ |
|
||||||
|
// | | |
|
||||||
|
// | | prepareForIndexPropValue +------------+ |
|
||||||
|
// | +------------------------->| IndexValue |-+ |
|
||||||
|
// | +------------+ | |
|
||||||
|
// | | |
|
||||||
|
// | +---------------------------------------+ |
|
||||||
|
// | | |
|
||||||
|
// | +-+--------------------------------------------------+ |
|
||||||
|
// | | | |
|
||||||
|
// | | [method with super] | |
|
||||||
|
// | | emitInitHomeObject +---------------------+ v |
|
||||||
|
// | +--------------------->| InitHomeObjForIndex |---->+ |
|
||||||
|
// | +---------------------+ | |
|
||||||
|
// | | |
|
||||||
|
// | +--------------------------------------------------+ |
|
||||||
|
// | | |
|
||||||
|
// | | emitInitIndexProp |
|
||||||
|
// | | emitInitIndexGetter |
|
||||||
|
// | | emitInitIndexSetter |
|
||||||
|
// | +---------------------------------------------------->+
|
||||||
|
// | |
|
||||||
|
// | [computed property/method/accessor] |
|
||||||
|
// | prepareForComputedPropKey +-------------+ |
|
||||||
|
// +----------------------------->| ComputedKey |-+ |
|
||||||
|
// | +-------------+ | |
|
||||||
|
// | | |
|
||||||
|
// | +-------------------------------------------+ |
|
||||||
|
// | | |
|
||||||
|
// | | prepareForComputedPropValue +---------------+ |
|
||||||
|
// | +---------------------------->| ComputedValue |-+ |
|
||||||
|
// | +---------------+ | |
|
||||||
|
// | | |
|
||||||
|
// | +---------------------------------------------+ |
|
||||||
|
// | | |
|
||||||
|
// | +-+--------------------------------------------------+ |
|
||||||
|
// | | | |
|
||||||
|
// | | [method with super] | |
|
||||||
|
// | | emitInitHomeObject +------------------------+ v |
|
||||||
|
// | +--------------------->| InitHomeObjForComputed |->+ |
|
||||||
|
// | +------------------------+ | |
|
||||||
|
// | | |
|
||||||
|
// | +--------------------------------------------------+ |
|
||||||
|
// | | |
|
||||||
|
// | | emitInitComputedProp |
|
||||||
|
// | | emitInitComputedGetter |
|
||||||
|
// | | emitInitComputedSetter |
|
||||||
|
// | +---------------------------------------------------->+
|
||||||
|
// | ^
|
||||||
|
// | |
|
||||||
|
// | [__proto__] |
|
||||||
|
// | prepareForProtoValue +------------+ emitMutateProto |
|
||||||
|
// +------------------------>| ProtoValue |-------------------->+
|
||||||
|
// | +------------+ ^
|
||||||
|
// | |
|
||||||
|
// | [...prop] |
|
||||||
|
// | prepareForSpreadOperand +---------------+ emitSpread |
|
||||||
|
// +-------------------------->| SpreadOperand |----------------+
|
||||||
|
// +---------------+
|
||||||
|
enum class PropertyState {
|
||||||
|
// The initial state.
|
||||||
|
Start,
|
||||||
|
|
||||||
|
// After calling prepareForPropValue.
|
||||||
|
PropValue,
|
||||||
|
|
||||||
|
// After calling emitInitHomeObject, from PropValue.
|
||||||
|
InitHomeObj,
|
||||||
|
|
||||||
|
// After calling prepareForIndexPropKey.
|
||||||
|
IndexKey,
|
||||||
|
|
||||||
|
// prepareForIndexPropValue.
|
||||||
|
IndexValue,
|
||||||
|
|
||||||
|
// After calling emitInitHomeObject, from IndexValue.
|
||||||
|
InitHomeObjForIndex,
|
||||||
|
|
||||||
|
// After calling prepareForComputedPropKey.
|
||||||
|
ComputedKey,
|
||||||
|
|
||||||
|
// prepareForComputedPropValue.
|
||||||
|
ComputedValue,
|
||||||
|
|
||||||
|
// After calling emitInitHomeObject, from ComputedValue.
|
||||||
|
InitHomeObjForComputed,
|
||||||
|
|
||||||
|
// After calling prepareForProtoValue.
|
||||||
|
ProtoValue,
|
||||||
|
|
||||||
|
// After calling prepareForSpreadOperand.
|
||||||
|
SpreadOperand,
|
||||||
|
|
||||||
|
// After calling one of emitInitProp, emitInitGetter, emitInitSetter,
|
||||||
|
// emitInitIndexOrComputedProp, emitInitIndexOrComputedGetter,
|
||||||
|
// emitInitIndexOrComputedSetter, emitMutateProto, or emitSpread.
|
||||||
|
Init,
|
||||||
|
};
|
||||||
|
PropertyState propertyState_ = PropertyState::Start;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
public:
|
||||||
|
explicit PropertyEmitter(BytecodeEmitter* bce);
|
||||||
|
|
||||||
|
// Parameters are the offset in the source code for each character below:
|
||||||
|
//
|
||||||
|
// { __proto__: protoValue }
|
||||||
|
// ^
|
||||||
|
// |
|
||||||
|
// keyPos
|
||||||
|
MOZ_MUST_USE bool prepareForProtoValue(
|
||||||
|
const mozilla::Maybe<uint32_t>& keyPos);
|
||||||
|
MOZ_MUST_USE bool emitMutateProto();
|
||||||
|
|
||||||
|
// { ...obj }
|
||||||
|
// ^
|
||||||
|
// |
|
||||||
|
// spreadPos
|
||||||
|
MOZ_MUST_USE bool prepareForSpreadOperand(
|
||||||
|
const mozilla::Maybe<uint32_t>& spreadPos);
|
||||||
|
MOZ_MUST_USE bool emitSpread();
|
||||||
|
|
||||||
|
// { key: value }
|
||||||
|
// ^
|
||||||
|
// |
|
||||||
|
// keyPos
|
||||||
|
MOZ_MUST_USE bool prepareForPropValue(const mozilla::Maybe<uint32_t>& keyPos,
|
||||||
|
Kind kind = Kind::Prototype);
|
||||||
|
|
||||||
|
// { 1: value }
|
||||||
|
// ^
|
||||||
|
// |
|
||||||
|
// keyPos
|
||||||
|
MOZ_MUST_USE bool prepareForIndexPropKey(
|
||||||
|
const mozilla::Maybe<uint32_t>& keyPos, Kind kind = Kind::Prototype);
|
||||||
|
MOZ_MUST_USE bool prepareForIndexPropValue();
|
||||||
|
|
||||||
|
// { [ key ]: value }
|
||||||
|
// ^
|
||||||
|
// |
|
||||||
|
// keyPos
|
||||||
|
MOZ_MUST_USE bool prepareForComputedPropKey(
|
||||||
|
const mozilla::Maybe<uint32_t>& keyPos, Kind kind = Kind::Prototype);
|
||||||
|
MOZ_MUST_USE bool prepareForComputedPropValue();
|
||||||
|
|
||||||
|
MOZ_MUST_USE bool emitInitHomeObject(
|
||||||
|
FunctionAsyncKind kind = FunctionAsyncKind::SyncFunction);
|
||||||
|
|
||||||
|
// @param key
|
||||||
|
// Property key
|
||||||
|
// @param isPropertyAnonFunctionOrClass
|
||||||
|
// True if the property value is an anonymous function or
|
||||||
|
// an anonymous class
|
||||||
|
// @param anonFunction
|
||||||
|
// The anonymous function object for property value
|
||||||
|
MOZ_MUST_USE bool emitInitProp(
|
||||||
|
JS::Handle<JSAtom*> key, bool isPropertyAnonFunctionOrClass = false,
|
||||||
|
JS::Handle<JSFunction*> anonFunction = nullptr);
|
||||||
|
MOZ_MUST_USE bool emitInitGetter(JS::Handle<JSAtom*> key);
|
||||||
|
MOZ_MUST_USE bool emitInitSetter(JS::Handle<JSAtom*> key);
|
||||||
|
|
||||||
|
MOZ_MUST_USE bool emitInitIndexProp(
|
||||||
|
bool isPropertyAnonFunctionOrClass = false);
|
||||||
|
MOZ_MUST_USE bool emitInitIndexGetter();
|
||||||
|
MOZ_MUST_USE bool emitInitIndexSetter();
|
||||||
|
|
||||||
|
MOZ_MUST_USE bool emitInitComputedProp(
|
||||||
|
bool isPropertyAnonFunctionOrClass = false);
|
||||||
|
MOZ_MUST_USE bool emitInitComputedGetter();
|
||||||
|
MOZ_MUST_USE bool emitInitComputedSetter();
|
||||||
|
|
||||||
|
private:
|
||||||
|
MOZ_MUST_USE MOZ_ALWAYS_INLINE bool prepareForProp(
|
||||||
|
const mozilla::Maybe<uint32_t>& keyPos, bool isStatic, bool isComputed);
|
||||||
|
|
||||||
|
// @param op
|
||||||
|
// Opcode for initializing property
|
||||||
|
// @param prefixKind
|
||||||
|
// None, Get, or Set
|
||||||
|
// @param key
|
||||||
|
// Atom of the property if the property key is not computed
|
||||||
|
// @param isPropertyAnonFunctionOrClass
|
||||||
|
// True if the property is either an anonymous function or an
|
||||||
|
// anonymous class
|
||||||
|
// @param anonFunction
|
||||||
|
// Anonymous function object for the property
|
||||||
|
MOZ_MUST_USE bool emitInit(JSOp op, JS::Handle<JSAtom*> key,
|
||||||
|
bool isPropertyAnonFunctionOrClass,
|
||||||
|
JS::Handle<JSFunction*> anonFunction);
|
||||||
|
MOZ_MUST_USE bool emitInitIndexOrComputed(JSOp op,
|
||||||
|
FunctionPrefixKind prefixKind,
|
||||||
|
bool isPropertyAnonFunctionOrClass);
|
||||||
|
|
||||||
|
MOZ_MUST_USE bool emitPopClassConstructor();
|
||||||
|
};
|
||||||
|
|
||||||
|
// Class for emitting bytecode for object literal.
|
||||||
|
//
|
||||||
|
// Usage: (check for the return value is omitted for simplicity)
|
||||||
|
//
|
||||||
|
// `{}`
|
||||||
|
// ObjectEmitter oe(this);
|
||||||
|
// oe.emitObject(0);
|
||||||
|
// oe.emitEnd();
|
||||||
|
//
|
||||||
|
// `{ prop: 10 }`
|
||||||
|
// ObjectEmitter oe(this);
|
||||||
|
// oe.emitObject(1);
|
||||||
|
//
|
||||||
|
// oe.prepareForPropValue(Some(offset_of_prop));
|
||||||
|
// emit(10);
|
||||||
|
// oe.emitInitProp(atom_of_prop);
|
||||||
|
//
|
||||||
|
// oe.emitEnd();
|
||||||
|
//
|
||||||
|
// `{ prop: function() {} }`, when property value is anonymous function
|
||||||
|
// ObjectEmitter oe(this);
|
||||||
|
// oe.emitObject(1);
|
||||||
|
//
|
||||||
|
// oe.prepareForPropValue(Some(offset_of_prop));
|
||||||
|
// emit(function);
|
||||||
|
// oe.emitInitProp(atom_of_prop, true, function_object);
|
||||||
|
//
|
||||||
|
// oe.emitEnd();
|
||||||
|
//
|
||||||
|
// `{ get prop() { ... }, set prop(v) { ... } }`
|
||||||
|
// ObjectEmitter oe(this);
|
||||||
|
// oe.emitObject(2);
|
||||||
|
//
|
||||||
|
// oe.prepareForPropValue(Some(offset_of_prop));
|
||||||
|
// emit(function_for_getter);
|
||||||
|
// oe.emitInitGetter(atom_of_prop);
|
||||||
|
//
|
||||||
|
// oe.prepareForPropValue(Some(offset_of_prop));
|
||||||
|
// emit(function_for_setter);
|
||||||
|
// oe.emitInitSetter(atom_of_prop);
|
||||||
|
//
|
||||||
|
// oe.emitEnd();
|
||||||
|
//
|
||||||
|
// `{ 1: 10, get 2() { ... }, set 3(v) { ... } }`
|
||||||
|
// ObjectEmitter oe(this);
|
||||||
|
// oe.emitObject(3);
|
||||||
|
//
|
||||||
|
// oe.prepareForIndexPropKey(Some(offset_of_prop));
|
||||||
|
// emit(1);
|
||||||
|
// oe.prepareForIndexPropValue();
|
||||||
|
// emit(10);
|
||||||
|
// oe.emitInitIndexedProp(atom_of_prop);
|
||||||
|
//
|
||||||
|
// oe.prepareForIndexPropKey(Some(offset_of_opening_bracket));
|
||||||
|
// emit(2);
|
||||||
|
// oe.prepareForIndexPropValue();
|
||||||
|
// emit(function_for_getter);
|
||||||
|
// oe.emitInitIndexGetter();
|
||||||
|
//
|
||||||
|
// oe.prepareForIndexPropKey(Some(offset_of_opening_bracket));
|
||||||
|
// emit(3);
|
||||||
|
// oe.prepareForIndexPropValue();
|
||||||
|
// emit(function_for_setter);
|
||||||
|
// oe.emitInitIndexSetter();
|
||||||
|
//
|
||||||
|
// oe.emitEnd();
|
||||||
|
//
|
||||||
|
// `{ [prop1]: 10, get [prop2]() { ... }, set [prop3](v) { ... } }`
|
||||||
|
// ObjectEmitter oe(this);
|
||||||
|
// oe.emitObject(3);
|
||||||
|
//
|
||||||
|
// oe.prepareForComputedPropKey(Some(offset_of_opening_bracket));
|
||||||
|
// emit(prop1);
|
||||||
|
// oe.prepareForComputedPropValue();
|
||||||
|
// emit(10);
|
||||||
|
// oe.emitInitComputedProp();
|
||||||
|
//
|
||||||
|
// oe.prepareForComputedPropKey(Some(offset_of_opening_bracket));
|
||||||
|
// emit(prop2);
|
||||||
|
// oe.prepareForComputedPropValue();
|
||||||
|
// emit(function_for_getter);
|
||||||
|
// oe.emitInitComputedGetter();
|
||||||
|
//
|
||||||
|
// oe.prepareForComputedPropKey(Some(offset_of_opening_bracket));
|
||||||
|
// emit(prop3);
|
||||||
|
// oe.prepareForComputedPropValue();
|
||||||
|
// emit(function_for_setter);
|
||||||
|
// oe.emitInitComputedSetter();
|
||||||
|
//
|
||||||
|
// oe.emitEnd();
|
||||||
|
//
|
||||||
|
// `{ __proto__: obj }`
|
||||||
|
// ObjectEmitter oe(this);
|
||||||
|
// oe.emitObject(1);
|
||||||
|
// oe.prepareForProtoValue(Some(offset_of___proto__));
|
||||||
|
// emit(obj);
|
||||||
|
// oe.emitMutateProto();
|
||||||
|
// oe.emitEnd();
|
||||||
|
//
|
||||||
|
// `{ ...obj }`
|
||||||
|
// ObjectEmitter oe(this);
|
||||||
|
// oe.emitObject(1);
|
||||||
|
// oe.prepareForSpreadOperand(Some(offset_of_triple_dots));
|
||||||
|
// emit(obj);
|
||||||
|
// oe.emitSpread();
|
||||||
|
// oe.emitEnd();
|
||||||
|
//
|
||||||
|
class MOZ_STACK_CLASS ObjectEmitter : public PropertyEmitter {
|
||||||
|
private:
|
||||||
|
// The offset of JSOP_NEWINIT, which is replced by JSOP_NEWOBJECT later
|
||||||
|
// when the object is known to have a fixed shape.
|
||||||
|
ptrdiff_t top_ = 0;
|
||||||
|
|
||||||
|
#ifdef DEBUG
|
||||||
|
// The state of this emitter.
|
||||||
|
//
|
||||||
|
// +-------+ emitObject +--------+
|
||||||
|
// | Start |----------->| Object |-+
|
||||||
|
// +-------+ +--------+ |
|
||||||
|
// |
|
||||||
|
// +-----------------------------+
|
||||||
|
// |
|
||||||
|
// | (do PropertyEmitter operation) emitEnd +-----+
|
||||||
|
// +-------------------------------+--------->| End |
|
||||||
|
// +-----+
|
||||||
|
enum class ObjectState {
|
||||||
|
// The initial state.
|
||||||
|
Start,
|
||||||
|
|
||||||
|
// After calling emitObject.
|
||||||
|
Object,
|
||||||
|
|
||||||
|
// After calling emitEnd.
|
||||||
|
End,
|
||||||
|
};
|
||||||
|
ObjectState objectState_ = ObjectState::Start;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
public:
|
||||||
|
explicit ObjectEmitter(BytecodeEmitter* bce);
|
||||||
|
|
||||||
|
MOZ_MUST_USE bool emitObject(size_t propertyCount);
|
||||||
|
MOZ_MUST_USE bool emitEnd();
|
||||||
|
};
|
||||||
|
|
||||||
|
// Save and restore the strictness.
|
||||||
|
// Used by class declaration/expression to temporarily enable strict mode.
|
||||||
|
class MOZ_RAII AutoSaveLocalStrictMode {
|
||||||
|
SharedContext* sc_;
|
||||||
|
bool savedStrictness_;
|
||||||
|
|
||||||
|
public:
|
||||||
|
explicit AutoSaveLocalStrictMode(SharedContext* sc);
|
||||||
|
~AutoSaveLocalStrictMode();
|
||||||
|
|
||||||
|
// Force restore the strictness now.
|
||||||
|
void restore();
|
||||||
|
};
|
||||||
|
|
||||||
|
// Class for emitting bytecode for JS class.
|
||||||
|
//
|
||||||
|
// Usage: (check for the return value is omitted for simplicity)
|
||||||
|
//
|
||||||
|
// `class {}`
|
||||||
|
// ClassEmitter ce(this);
|
||||||
|
// ce.emitClass();
|
||||||
|
//
|
||||||
|
// ce.emitInitDefaultConstructor(Some(offset_of_class),
|
||||||
|
// Some(offset_of_closing_bracket));
|
||||||
|
//
|
||||||
|
// ce.emitEnd(ClassEmitter::Kind::Expression);
|
||||||
|
//
|
||||||
|
// `class { constructor() { ... } }`
|
||||||
|
// ClassEmitter ce(this);
|
||||||
|
// ce.emitClass();
|
||||||
|
//
|
||||||
|
// emit(function_for_constructor);
|
||||||
|
// ce.emitInitConstructor(/* needsHomeObject = */ false);
|
||||||
|
//
|
||||||
|
// ce.emitEnd(ClassEmitter::Kind::Expression);
|
||||||
|
//
|
||||||
|
// `class X { constructor() { ... } }`
|
||||||
|
// ClassEmitter ce(this);
|
||||||
|
// ce.emitScopeForNamedClass(scopeBindingForName);
|
||||||
|
// ce.emitClass(atom_of_X);
|
||||||
|
//
|
||||||
|
// ce.emitInitDefaultConstructor(Some(offset_of_class),
|
||||||
|
// Some(offset_of_closing_bracket));
|
||||||
|
//
|
||||||
|
// ce.emitEnd(ClassEmitter::Kind::Expression);
|
||||||
|
//
|
||||||
|
// `class X { constructor() { ... } }`
|
||||||
|
// ClassEmitter ce(this);
|
||||||
|
// ce.emitScopeForNamedClass(scopeBindingForName);
|
||||||
|
// ce.emitClass(atom_of_X);
|
||||||
|
//
|
||||||
|
// emit(function_for_constructor);
|
||||||
|
// ce.emitInitConstructor(/* needsHomeObject = */ false);
|
||||||
|
//
|
||||||
|
// ce.emitEnd(ClassEmitter::Kind::Expression);
|
||||||
|
//
|
||||||
|
// `class X extends Y { constructor() { ... } }`
|
||||||
|
// ClassEmitter ce(this);
|
||||||
|
// ce.emitScopeForNamedClass(scopeBindingForName);
|
||||||
|
//
|
||||||
|
// emit(Y);
|
||||||
|
// ce.emitDerivedClass(atom_of_X);
|
||||||
|
//
|
||||||
|
// emit(function_for_constructor);
|
||||||
|
// ce.emitInitConstructor(/* needsHomeObject = */ false);
|
||||||
|
//
|
||||||
|
// ce.emitEnd(ClassEmitter::Kind::Expression);
|
||||||
|
//
|
||||||
|
// `class X extends Y { constructor() { ... super.f(); ... } }`
|
||||||
|
// ClassEmitter ce(this);
|
||||||
|
// ce.emitScopeForNamedClass(scopeBindingForName);
|
||||||
|
//
|
||||||
|
// emit(Y);
|
||||||
|
// ce.emitDerivedClass(atom_of_X);
|
||||||
|
//
|
||||||
|
// emit(function_for_constructor);
|
||||||
|
// // pass true if constructor contains super.prop access
|
||||||
|
// ce.emitInitConstructor(/* needsHomeObject = */ true);
|
||||||
|
//
|
||||||
|
// ce.emitEnd(ClassEmitter::Kind::Expression);
|
||||||
|
//
|
||||||
|
// `m() {}` in class
|
||||||
|
// // after emitInitConstructor/emitInitDefaultConstructor
|
||||||
|
// ce.prepareForPropValue(Some(offset_of_m));
|
||||||
|
// emit(function_for_m);
|
||||||
|
// ce.emitInitProp(atom_of_m);
|
||||||
|
//
|
||||||
|
// `m() { super.f(); }` in class
|
||||||
|
// // after emitInitConstructor/emitInitDefaultConstructor
|
||||||
|
// ce.prepareForPropValue(Some(offset_of_m));
|
||||||
|
// emit(function_for_m);
|
||||||
|
// ce.emitInitHomeObject();
|
||||||
|
// ce.emitInitProp(atom_of_m);
|
||||||
|
//
|
||||||
|
// `async m() { super.f(); }` in class
|
||||||
|
// // after emitInitConstructor/emitInitDefaultConstructor
|
||||||
|
// ce.prepareForPropValue(Some(offset_of_m));
|
||||||
|
// emit(function_for_m);
|
||||||
|
// ce.emitInitHomeObject(FunctionAsyncKind::Async);
|
||||||
|
// ce.emitInitProp(atom_of_m);
|
||||||
|
//
|
||||||
|
// `get p() { super.f(); }` in class
|
||||||
|
// // after emitInitConstructor/emitInitDefaultConstructor
|
||||||
|
// ce.prepareForPropValue(Some(offset_of_p));
|
||||||
|
// emit(function_for_p);
|
||||||
|
// ce.emitInitHomeObject();
|
||||||
|
// ce.emitInitGetter(atom_of_m);
|
||||||
|
//
|
||||||
|
// `static m() {}` in class
|
||||||
|
// // after emitInitConstructor/emitInitDefaultConstructor
|
||||||
|
// ce.prepareForPropValue(Some(offset_of_m),
|
||||||
|
// PropertyEmitter::Kind::Static);
|
||||||
|
// emit(function_for_m);
|
||||||
|
// ce.emitInitProp(atom_of_m);
|
||||||
|
//
|
||||||
|
// `static get [p]() { super.f(); }` in class
|
||||||
|
// // after emitInitConstructor/emitInitDefaultConstructor
|
||||||
|
// ce.prepareForComputedPropValue(Some(offset_of_m),
|
||||||
|
// PropertyEmitter::Kind::Static);
|
||||||
|
// emit(p);
|
||||||
|
// ce.prepareForComputedPropValue();
|
||||||
|
// emit(function_for_m);
|
||||||
|
// ce.emitInitHomeObject();
|
||||||
|
// ce.emitInitComputedGetter();
|
||||||
|
//
|
||||||
|
class MOZ_STACK_CLASS ClassEmitter : public PropertyEmitter {
|
||||||
|
public:
|
||||||
|
enum class Kind {
|
||||||
|
// Class expression.
|
||||||
|
Expression,
|
||||||
|
|
||||||
|
// Class declaration.
|
||||||
|
Declaration,
|
||||||
|
};
|
||||||
|
|
||||||
|
private:
|
||||||
|
// Pseudocode for class declarations:
|
||||||
|
//
|
||||||
|
// class extends BaseExpression {
|
||||||
|
// constructor() { ... }
|
||||||
|
// ...
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
//
|
||||||
|
// if defined <BaseExpression> {
|
||||||
|
// let heritage = BaseExpression;
|
||||||
|
//
|
||||||
|
// if (heritage !== null) {
|
||||||
|
// funProto = heritage;
|
||||||
|
// objProto = heritage.prototype;
|
||||||
|
// } else {
|
||||||
|
// funProto = %FunctionPrototype%;
|
||||||
|
// objProto = null;
|
||||||
|
// }
|
||||||
|
// } else {
|
||||||
|
// objProto = %ObjectPrototype%;
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// let homeObject = ObjectCreate(objProto);
|
||||||
|
//
|
||||||
|
// if defined <constructor> {
|
||||||
|
// if defined <BaseExpression> {
|
||||||
|
// cons = DefineMethod(<constructor>, proto=homeObject,
|
||||||
|
// funProto=funProto);
|
||||||
|
// } else {
|
||||||
|
// cons = DefineMethod(<constructor>, proto=homeObject);
|
||||||
|
// }
|
||||||
|
// } else {
|
||||||
|
// if defined <BaseExpression> {
|
||||||
|
// cons = DefaultDerivedConstructor(proto=homeObject,
|
||||||
|
// funProto=funProto);
|
||||||
|
// } else {
|
||||||
|
// cons = DefaultConstructor(proto=homeObject);
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// cons.prototype = homeObject;
|
||||||
|
// homeObject.constructor = cons;
|
||||||
|
//
|
||||||
|
// EmitPropertyList(...)
|
||||||
|
|
||||||
|
bool isDerived_ = false;
|
||||||
|
|
||||||
|
mozilla::Maybe<TDZCheckCache> tdzCacheForInnerName_;
|
||||||
|
mozilla::Maybe<EmitterScope> innerNameScope_;
|
||||||
|
AutoSaveLocalStrictMode strictMode_;
|
||||||
|
|
||||||
|
#ifdef DEBUG
|
||||||
|
// The state of this emitter.
|
||||||
|
//
|
||||||
|
// +-------+
|
||||||
|
// | Start |-+------------------------------------>+-+
|
||||||
|
// +-------+ | ^ |
|
||||||
|
// | [named class] | |
|
||||||
|
// | emitScopeForNamedClass +-------+ | |
|
||||||
|
// +-------------------------->| Scope |-+ |
|
||||||
|
// +-------+ |
|
||||||
|
// |
|
||||||
|
// +-----------------------------------------------+
|
||||||
|
// |
|
||||||
|
// | emitClass +-------+
|
||||||
|
// +-+----------------->+->| Class |-+
|
||||||
|
// | ^ +-------+ |
|
||||||
|
// | emitDerivedClass | |
|
||||||
|
// +------------------+ |
|
||||||
|
// |
|
||||||
|
// +-------------------------------+
|
||||||
|
// |
|
||||||
|
// |
|
||||||
|
// | emitInitConstructor +-----------------+
|
||||||
|
// +-+--------------------------->+->| InitConstructor |-+
|
||||||
|
// | ^ +-----------------+ |
|
||||||
|
// | emitInitDefaultConstructor | |
|
||||||
|
// +----------------------------+ |
|
||||||
|
// |
|
||||||
|
// +---------------------------------------------------+
|
||||||
|
// |
|
||||||
|
// | (do PropertyEmitter operation) emitEnd +-----+
|
||||||
|
// +-------------------------------+--------->| End |
|
||||||
|
// +-----+
|
||||||
|
enum class ClassState {
|
||||||
|
// The initial state.
|
||||||
|
Start,
|
||||||
|
|
||||||
|
// After calling emitScopeForNamedClass.
|
||||||
|
Scope,
|
||||||
|
|
||||||
|
// After calling emitClass or emitDerivedClass.
|
||||||
|
Class,
|
||||||
|
|
||||||
|
// After calling emitInitConstructor or emitInitDefaultConstructor.
|
||||||
|
InitConstructor,
|
||||||
|
|
||||||
|
// After calling emitEnd.
|
||||||
|
End,
|
||||||
|
};
|
||||||
|
ClassState classState_ = ClassState::Start;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
JS::Rooted<JSAtom*> name_;
|
||||||
|
|
||||||
|
public:
|
||||||
|
explicit ClassEmitter(BytecodeEmitter* bce);
|
||||||
|
|
||||||
|
MOZ_MUST_USE bool emitScopeForNamedClass(
|
||||||
|
JS::Handle<LexicalScope::Data*> scopeBindings);
|
||||||
|
|
||||||
|
// @param name
|
||||||
|
// Name of the class (nullptr if this is anonymous class)
|
||||||
|
MOZ_MUST_USE bool emitClass(JS::Handle<JSAtom*> name);
|
||||||
|
MOZ_MUST_USE bool emitDerivedClass(JS::Handle<JSAtom*> name);
|
||||||
|
|
||||||
|
// @param needsHomeObject
|
||||||
|
// True if the constructor contains `super.foo`
|
||||||
|
MOZ_MUST_USE bool emitInitConstructor(bool needsHomeObject);
|
||||||
|
|
||||||
|
// Parameters are the offset in the source code for each character below:
|
||||||
|
//
|
||||||
|
// class X { foo() {} }
|
||||||
|
// ^ ^
|
||||||
|
// | |
|
||||||
|
// | classEnd
|
||||||
|
// |
|
||||||
|
// classStart
|
||||||
|
//
|
||||||
|
MOZ_MUST_USE bool emitInitDefaultConstructor(
|
||||||
|
const mozilla::Maybe<uint32_t>& classStart,
|
||||||
|
const mozilla::Maybe<uint32_t>& classEnd);
|
||||||
|
|
||||||
|
MOZ_MUST_USE bool emitEnd(Kind kind);
|
||||||
|
|
||||||
|
private:
|
||||||
|
void setName(JS::Handle<JSAtom*> name);
|
||||||
|
MOZ_MUST_USE bool initProtoAndCtor();
|
||||||
|
};
|
||||||
|
|
||||||
|
} /* namespace frontend */
|
||||||
|
} /* namespace js */
|
||||||
|
|
||||||
|
#endif /* frontend_ObjectEmitter_h */
|
|
@ -41,8 +41,10 @@ UNIFIED_SOURCES += [
|
||||||
'ForOfLoopControl.cpp',
|
'ForOfLoopControl.cpp',
|
||||||
'IfEmitter.cpp',
|
'IfEmitter.cpp',
|
||||||
'JumpList.cpp',
|
'JumpList.cpp',
|
||||||
|
'LabelEmitter.cpp',
|
||||||
'NameFunctions.cpp',
|
'NameFunctions.cpp',
|
||||||
'NameOpEmitter.cpp',
|
'NameOpEmitter.cpp',
|
||||||
|
'ObjectEmitter.cpp',
|
||||||
'ParseContext.cpp',
|
'ParseContext.cpp',
|
||||||
'ParseNode.cpp',
|
'ParseNode.cpp',
|
||||||
'PropOpEmitter.cpp',
|
'PropOpEmitter.cpp',
|
||||||
|
|
|
@ -349,7 +349,7 @@ bool GCRuntime::gcIfNeededAtAllocation(JSContext* cx) {
|
||||||
// an incremental GC, we're growing faster than we're GCing, so stop
|
// an incremental GC, we're growing faster than we're GCing, so stop
|
||||||
// the world and do a full, non-incremental GC right now, if possible.
|
// the world and do a full, non-incremental GC right now, if possible.
|
||||||
if (isIncrementalGCInProgress() &&
|
if (isIncrementalGCInProgress() &&
|
||||||
cx->zone()->usage.gcBytes() > cx->zone()->threshold.gcTriggerBytes()) {
|
cx->zone()->zoneSize.gcBytes() > cx->zone()->threshold.gcTriggerBytes()) {
|
||||||
PrepareZoneForGC(cx->zone());
|
PrepareZoneForGC(cx->zone());
|
||||||
gc(GC_NORMAL, JS::gcreason::INCREMENTAL_TOO_SLOW);
|
gc(GC_NORMAL, JS::gcreason::INCREMENTAL_TOO_SLOW);
|
||||||
}
|
}
|
||||||
|
@ -567,11 +567,11 @@ Arena* GCRuntime::allocateArena(Chunk* chunk, Zone* zone, AllocKind thingKind,
|
||||||
|
|
||||||
// Fail the allocation if we are over our heap size limits.
|
// Fail the allocation if we are over our heap size limits.
|
||||||
if ((checkThresholds != ShouldCheckThresholds::DontCheckThresholds) &&
|
if ((checkThresholds != ShouldCheckThresholds::DontCheckThresholds) &&
|
||||||
(usage.gcBytes() >= tunables.gcMaxBytes()))
|
(heapSize.gcBytes() >= tunables.gcMaxBytes()))
|
||||||
return nullptr;
|
return nullptr;
|
||||||
|
|
||||||
Arena* arena = chunk->allocateArena(rt, zone, thingKind, lock);
|
Arena* arena = chunk->allocateArena(rt, zone, thingKind, lock);
|
||||||
zone->usage.addGCArena();
|
zone->zoneSize.addGCArena();
|
||||||
|
|
||||||
// Trigger an incremental slice if needed.
|
// Trigger an incremental slice if needed.
|
||||||
if (checkThresholds != ShouldCheckThresholds::DontCheckThresholds) {
|
if (checkThresholds != ShouldCheckThresholds::DontCheckThresholds) {
|
||||||
|
|
|
@ -892,7 +892,7 @@ void Chunk::updateChunkListAfterFree(JSRuntime* rt, const AutoLockGC& lock) {
|
||||||
}
|
}
|
||||||
|
|
||||||
void GCRuntime::releaseArena(Arena* arena, const AutoLockGC& lock) {
|
void GCRuntime::releaseArena(Arena* arena, const AutoLockGC& lock) {
|
||||||
arena->zone->usage.removeGCArena();
|
arena->zone->zoneSize.removeGCArena();
|
||||||
arena->chunk()->releaseArena(rt, arena, lock);
|
arena->chunk()->releaseArena(rt, arena, lock);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -902,7 +902,7 @@ GCRuntime::GCRuntime(JSRuntime* rt)
|
||||||
atomsZone(nullptr),
|
atomsZone(nullptr),
|
||||||
stats_(rt),
|
stats_(rt),
|
||||||
marker(rt),
|
marker(rt),
|
||||||
usage(nullptr),
|
heapSize(nullptr),
|
||||||
rootsHash(256),
|
rootsHash(256),
|
||||||
nextCellUniqueId_(LargestTaggedNullCellPointer +
|
nextCellUniqueId_(LargestTaggedNullCellPointer +
|
||||||
1), // Ensure disjoint from null tagged pointers.
|
1), // Ensure disjoint from null tagged pointers.
|
||||||
|
@ -1379,7 +1379,7 @@ bool GCRuntime::setParameter(JSGCParamKey key, uint32_t value,
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
for (ZonesIter zone(rt, WithAtoms); !zone.done(); zone.next()) {
|
for (ZonesIter zone(rt, WithAtoms); !zone.done(); zone.next()) {
|
||||||
zone->threshold.updateAfterGC(zone->usage.gcBytes(), GC_NORMAL,
|
zone->threshold.updateAfterGC(zone->zoneSize.gcBytes(), GC_NORMAL,
|
||||||
tunables, schedulingState, lock);
|
tunables, schedulingState, lock);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1607,7 +1607,7 @@ void GCRuntime::resetParameter(JSGCParamKey key, AutoLockGC& lock) {
|
||||||
default:
|
default:
|
||||||
tunables.resetParameter(key, lock);
|
tunables.resetParameter(key, lock);
|
||||||
for (ZonesIter zone(rt, WithAtoms); !zone.done(); zone.next()) {
|
for (ZonesIter zone(rt, WithAtoms); !zone.done(); zone.next()) {
|
||||||
zone->threshold.updateAfterGC(zone->usage.gcBytes(), GC_NORMAL,
|
zone->threshold.updateAfterGC(zone->zoneSize.gcBytes(), GC_NORMAL,
|
||||||
tunables, schedulingState, lock);
|
tunables, schedulingState, lock);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1685,7 +1685,7 @@ uint32_t GCRuntime::getParameter(JSGCParamKey key, const AutoLockGC& lock) {
|
||||||
case JSGC_MAX_MALLOC_BYTES:
|
case JSGC_MAX_MALLOC_BYTES:
|
||||||
return mallocCounter.maxBytes();
|
return mallocCounter.maxBytes();
|
||||||
case JSGC_BYTES:
|
case JSGC_BYTES:
|
||||||
return uint32_t(usage.gcBytes());
|
return uint32_t(heapSize.gcBytes());
|
||||||
case JSGC_MODE:
|
case JSGC_MODE:
|
||||||
return uint32_t(mode);
|
return uint32_t(mode);
|
||||||
case JSGC_UNUSED_CHUNKS:
|
case JSGC_UNUSED_CHUNKS:
|
||||||
|
@ -3280,7 +3280,7 @@ void GCRuntime::maybeAllocTriggerZoneGC(Zone* zone, const AutoLockGC& lock) {
|
||||||
|
|
||||||
MOZ_ASSERT(!JS::RuntimeHeapIsCollecting());
|
MOZ_ASSERT(!JS::RuntimeHeapIsCollecting());
|
||||||
|
|
||||||
size_t usedBytes = zone->usage.gcBytes();
|
size_t usedBytes = zone->zoneSize.gcBytes();
|
||||||
size_t thresholdBytes = zone->threshold.gcTriggerBytes();
|
size_t thresholdBytes = zone->threshold.gcTriggerBytes();
|
||||||
|
|
||||||
if (usedBytes >= thresholdBytes) {
|
if (usedBytes >= thresholdBytes) {
|
||||||
|
@ -3378,7 +3378,7 @@ void GCRuntime::maybeGC(Zone* zone) {
|
||||||
|
|
||||||
float threshold = zone->threshold.eagerAllocTrigger(
|
float threshold = zone->threshold.eagerAllocTrigger(
|
||||||
schedulingState.inHighFrequencyGCMode());
|
schedulingState.inHighFrequencyGCMode());
|
||||||
float usedBytes = zone->usage.gcBytes();
|
float usedBytes = zone->zoneSize.gcBytes();
|
||||||
if (usedBytes > 1024 * 1024 && usedBytes >= threshold &&
|
if (usedBytes > 1024 * 1024 && usedBytes >= threshold &&
|
||||||
!isIncrementalGCInProgress() && !isBackgroundSweeping()) {
|
!isIncrementalGCInProgress() && !isBackgroundSweeping()) {
|
||||||
stats().recordTrigger(usedBytes, threshold);
|
stats().recordTrigger(usedBytes, threshold);
|
||||||
|
@ -5782,7 +5782,7 @@ IncrementalProgress GCRuntime::endSweepingSweepGroup(FreeOp* fop,
|
||||||
for (SweepGroupZonesIter zone(rt); !zone.done(); zone.next()) {
|
for (SweepGroupZonesIter zone(rt); !zone.done(); zone.next()) {
|
||||||
AutoLockGC lock(rt);
|
AutoLockGC lock(rt);
|
||||||
zone->changeGCState(Zone::Sweep, Zone::Finished);
|
zone->changeGCState(Zone::Sweep, Zone::Finished);
|
||||||
zone->threshold.updateAfterGC(zone->usage.gcBytes(), invocationKind,
|
zone->threshold.updateAfterGC(zone->zoneSize.gcBytes(), invocationKind,
|
||||||
tunables, schedulingState, lock);
|
tunables, schedulingState, lock);
|
||||||
zone->updateAllGCMallocCountersOnGCEnd(lock);
|
zone->updateAllGCMallocCountersOnGCEnd(lock);
|
||||||
zone->arenas.unmarkPreMarkedFreeCells();
|
zone->arenas.unmarkPreMarkedFreeCells();
|
||||||
|
@ -7258,7 +7258,7 @@ GCRuntime::IncrementalResult GCRuntime::budgetIncrementalGC(
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (zone->usage.gcBytes() >= zone->threshold.gcTriggerBytes()) {
|
if (zone->zoneSize.gcBytes() >= zone->threshold.gcTriggerBytes()) {
|
||||||
CheckZoneIsScheduled(zone, reason, "GC bytes");
|
CheckZoneIsScheduled(zone, reason, "GC bytes");
|
||||||
budget.makeUnlimited();
|
budget.makeUnlimited();
|
||||||
stats().nonincremental(AbortReason::GCBytesTrigger);
|
stats().nonincremental(AbortReason::GCBytesTrigger);
|
||||||
|
@ -7314,7 +7314,7 @@ class AutoScheduleZonesForGC {
|
||||||
|
|
||||||
// This is a heuristic to reduce the total number of collections.
|
// This is a heuristic to reduce the total number of collections.
|
||||||
bool inHighFrequencyMode = gc->schedulingState.inHighFrequencyGCMode();
|
bool inHighFrequencyMode = gc->schedulingState.inHighFrequencyGCMode();
|
||||||
if (zone->usage.gcBytes() >=
|
if (zone->zoneSize.gcBytes() >=
|
||||||
zone->threshold.eagerAllocTrigger(inHighFrequencyMode)) {
|
zone->threshold.eagerAllocTrigger(inHighFrequencyMode)) {
|
||||||
zone->scheduleGC();
|
zone->scheduleGC();
|
||||||
}
|
}
|
||||||
|
@ -8136,7 +8136,7 @@ void GCRuntime::mergeRealms(Realm* source, Realm* target) {
|
||||||
targetZoneIsCollecting);
|
targetZoneIsCollecting);
|
||||||
target->zone()->addTenuredAllocsSinceMinorGC(
|
target->zone()->addTenuredAllocsSinceMinorGC(
|
||||||
source->zone()->getAndResetTenuredAllocsSinceMinorGC());
|
source->zone()->getAndResetTenuredAllocsSinceMinorGC());
|
||||||
target->zone()->usage.adopt(source->zone()->usage);
|
target->zone()->zoneSize.adopt(source->zone()->zoneSize);
|
||||||
target->zone()->adoptUniqueIds(source->zone());
|
target->zone()->adoptUniqueIds(source->zone());
|
||||||
target->zone()->adoptMallocBytes(source->zone());
|
target->zone()->adoptMallocBytes(source->zone());
|
||||||
|
|
||||||
|
@ -8592,9 +8592,10 @@ JS::dbg::GarbageCollectionEvent::Ptr JS::GCDescription::toGCEvent(
|
||||||
cx->runtime()->gc.majorGCCount());
|
cx->runtime()->gc.majorGCCount());
|
||||||
}
|
}
|
||||||
|
|
||||||
char16_t* JS::GCDescription::formatJSON(JSContext* cx,
|
char16_t* JS::GCDescription::formatJSONTelemetry(JSContext* cx,
|
||||||
uint64_t timestamp) const {
|
uint64_t timestamp) const {
|
||||||
UniqueChars cstr = cx->runtime()->gc.stats().renderJsonMessage(timestamp);
|
UniqueChars cstr = cx->runtime()->gc.stats().renderJsonMessage(timestamp,
|
||||||
|
gcstats::Statistics::JSONUse::TELEMETRY);
|
||||||
|
|
||||||
size_t nchars = strlen(cstr.get());
|
size_t nchars = strlen(cstr.get());
|
||||||
UniqueTwoByteChars out(js_pod_malloc<char16_t>(nchars + 1));
|
UniqueTwoByteChars out(js_pod_malloc<char16_t>(nchars + 1));
|
||||||
|
@ -8623,14 +8624,15 @@ TimeStamp JS::GCDescription::lastSliceEnd(JSContext* cx) const {
|
||||||
return cx->runtime()->gc.stats().slices().back().end;
|
return cx->runtime()->gc.stats().slices().back().end;
|
||||||
}
|
}
|
||||||
|
|
||||||
JS::UniqueChars JS::GCDescription::sliceToJSON(JSContext* cx) const {
|
JS::UniqueChars JS::GCDescription::sliceToJSONProfiler(JSContext* cx) const {
|
||||||
size_t slices = cx->runtime()->gc.stats().slices().length();
|
size_t slices = cx->runtime()->gc.stats().slices().length();
|
||||||
MOZ_ASSERT(slices > 0);
|
MOZ_ASSERT(slices > 0);
|
||||||
return cx->runtime()->gc.stats().renderJsonSlice(slices - 1);
|
return cx->runtime()->gc.stats().renderJsonSlice(slices - 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
JS::UniqueChars JS::GCDescription::summaryToJSON(JSContext* cx) const {
|
JS::UniqueChars JS::GCDescription::formatJSONProfiler(JSContext* cx) const {
|
||||||
return cx->runtime()->gc.stats().renderJsonMessage(0, false);
|
return cx->runtime()->gc.stats().renderJsonMessage(0,
|
||||||
|
js::gcstats::Statistics::JSONUse::PROFILER);
|
||||||
}
|
}
|
||||||
|
|
||||||
JS_PUBLIC_API JS::UniqueChars JS::MinorGcToJSON(JSContext* cx) {
|
JS_PUBLIC_API JS::UniqueChars JS::MinorGcToJSON(JSContext* cx) {
|
||||||
|
@ -8721,7 +8723,7 @@ namespace MemInfo {
|
||||||
|
|
||||||
static bool GCBytesGetter(JSContext* cx, unsigned argc, Value* vp) {
|
static bool GCBytesGetter(JSContext* cx, unsigned argc, Value* vp) {
|
||||||
CallArgs args = CallArgsFromVp(argc, vp);
|
CallArgs args = CallArgsFromVp(argc, vp);
|
||||||
args.rval().setNumber(double(cx->runtime()->gc.usage.gcBytes()));
|
args.rval().setNumber(double(cx->runtime()->gc.heapSize.gcBytes()));
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -8770,7 +8772,7 @@ static bool MinorGCCountGetter(JSContext* cx, unsigned argc, Value* vp) {
|
||||||
|
|
||||||
static bool ZoneGCBytesGetter(JSContext* cx, unsigned argc, Value* vp) {
|
static bool ZoneGCBytesGetter(JSContext* cx, unsigned argc, Value* vp) {
|
||||||
CallArgs args = CallArgsFromVp(argc, vp);
|
CallArgs args = CallArgsFromVp(argc, vp);
|
||||||
args.rval().setNumber(double(cx->zone()->usage.gcBytes()));
|
args.rval().setNumber(double(cx->zone()->zoneSize.gcBytes()));
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -714,8 +714,8 @@ class GCRuntime {
|
||||||
|
|
||||||
Vector<JS::GCCellPtr, 0, SystemAllocPolicy> unmarkGrayStack;
|
Vector<JS::GCCellPtr, 0, SystemAllocPolicy> unmarkGrayStack;
|
||||||
|
|
||||||
/* Track heap usage for this runtime. */
|
/* Track heap size for this runtime. */
|
||||||
HeapUsage usage;
|
HeapSize heapSize;
|
||||||
|
|
||||||
/* GC scheduling state and parameters. */
|
/* GC scheduling state and parameters. */
|
||||||
GCSchedulingTunables tunables;
|
GCSchedulingTunables tunables;
|
||||||
|
|
|
@ -842,12 +842,12 @@ static_assert(
|
||||||
* Tracks the used sizes for owned heap data and automatically maintains the
|
* Tracks the used sizes for owned heap data and automatically maintains the
|
||||||
* memory usage relationship between GCRuntime and Zones.
|
* memory usage relationship between GCRuntime and Zones.
|
||||||
*/
|
*/
|
||||||
class HeapUsage {
|
class HeapSize {
|
||||||
/*
|
/*
|
||||||
* A heap usage that contains our parent's heap usage, or null if this is
|
* A heap usage that contains our parent's heap usage, or null if this is
|
||||||
* the top-level usage container.
|
* the top-level usage container.
|
||||||
*/
|
*/
|
||||||
HeapUsage* const parent_;
|
HeapSize* const parent_;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* The approximate number of bytes in use on the GC heap, to the nearest
|
* The approximate number of bytes in use on the GC heap, to the nearest
|
||||||
|
@ -861,7 +861,7 @@ class HeapUsage {
|
||||||
gcBytes_;
|
gcBytes_;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
explicit HeapUsage(HeapUsage* parent) : parent_(parent), gcBytes_(0) {}
|
explicit HeapSize(HeapSize* parent) : parent_(parent), gcBytes_(0) {}
|
||||||
|
|
||||||
size_t gcBytes() const { return gcBytes_; }
|
size_t gcBytes() const { return gcBytes_; }
|
||||||
|
|
||||||
|
@ -880,7 +880,7 @@ class HeapUsage {
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Pair to adoptArenas. Adopts the attendant usage statistics. */
|
/* Pair to adoptArenas. Adopts the attendant usage statistics. */
|
||||||
void adopt(HeapUsage& other) {
|
void adopt(HeapSize& other) {
|
||||||
gcBytes_ += other.gcBytes_;
|
gcBytes_ += other.gcBytes_;
|
||||||
other.gcBytes_ = 0;
|
other.gcBytes_ = 0;
|
||||||
}
|
}
|
||||||
|
|
|
@ -828,7 +828,7 @@ void js::Nursery::collect(JS::gcreason::Reason reason) {
|
||||||
// We ignore gcMaxBytes when allocating for minor collection. However, if we
|
// We ignore gcMaxBytes when allocating for minor collection. However, if we
|
||||||
// overflowed, we disable the nursery. The next time we allocate, we'll fail
|
// overflowed, we disable the nursery. The next time we allocate, we'll fail
|
||||||
// because gcBytes >= gcMaxBytes.
|
// because gcBytes >= gcMaxBytes.
|
||||||
if (rt->gc.usage.gcBytes() >= tunables().gcMaxBytes()) {
|
if (rt->gc.heapSize.gcBytes() >= tunables().gcMaxBytes()) {
|
||||||
disable();
|
disable();
|
||||||
}
|
}
|
||||||
// Disable the nursery if the user changed the configuration setting. The
|
// Disable the nursery if the user changed the configuration setting. The
|
||||||
|
|
|
@ -327,7 +327,7 @@ UniqueChars Statistics::formatCompactSummaryMessage() const {
|
||||||
zoneStats.collectedZoneCount, zoneStats.zoneCount,
|
zoneStats.collectedZoneCount, zoneStats.zoneCount,
|
||||||
zoneStats.sweptZoneCount, zoneStats.collectedCompartmentCount,
|
zoneStats.sweptZoneCount, zoneStats.collectedCompartmentCount,
|
||||||
zoneStats.compartmentCount, zoneStats.sweptCompartmentCount,
|
zoneStats.compartmentCount, zoneStats.sweptCompartmentCount,
|
||||||
double(preBytes) / bytesPerMiB,
|
double(preHeapSize) / bytesPerMiB,
|
||||||
counts[COUNT_NEW_CHUNK] - counts[COUNT_DESTROY_CHUNK],
|
counts[COUNT_NEW_CHUNK] - counts[COUNT_DESTROY_CHUNK],
|
||||||
counts[COUNT_NEW_CHUNK] + counts[COUNT_DESTROY_CHUNK]);
|
counts[COUNT_NEW_CHUNK] + counts[COUNT_DESTROY_CHUNK]);
|
||||||
if (!fragments.append(DuplicateString(buffer))) {
|
if (!fragments.append(DuplicateString(buffer))) {
|
||||||
|
@ -449,7 +449,7 @@ UniqueChars Statistics::formatDetailedDescription() const {
|
||||||
zoneStats.compartmentCount, zoneStats.sweptCompartmentCount,
|
zoneStats.compartmentCount, zoneStats.sweptCompartmentCount,
|
||||||
getCount(COUNT_MINOR_GC), getCount(COUNT_STOREBUFFER_OVERFLOW),
|
getCount(COUNT_MINOR_GC), getCount(COUNT_STOREBUFFER_OVERFLOW),
|
||||||
mmu20 * 100., mmu50 * 100., t(sccTotal), t(sccLongest),
|
mmu20 * 100., mmu50 * 100., t(sccTotal), t(sccLongest),
|
||||||
double(preBytes) / bytesPerMiB,
|
double(preHeapSize) / bytesPerMiB,
|
||||||
getCount(COUNT_NEW_CHUNK) - getCount(COUNT_DESTROY_CHUNK),
|
getCount(COUNT_NEW_CHUNK) - getCount(COUNT_DESTROY_CHUNK),
|
||||||
getCount(COUNT_NEW_CHUNK) + getCount(COUNT_DESTROY_CHUNK),
|
getCount(COUNT_NEW_CHUNK) + getCount(COUNT_DESTROY_CHUNK),
|
||||||
double(ArenaSize * getCount(COUNT_ARENA_RELOCATED)) / bytesPerMiB,
|
double(ArenaSize * getCount(COUNT_ARENA_RELOCATED)) / bytesPerMiB,
|
||||||
|
@ -585,8 +585,7 @@ void Statistics::writeLogMessage(const char* fmt, ...) {
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
UniqueChars Statistics::renderJsonMessage(uint64_t timestamp,
|
UniqueChars Statistics::renderJsonMessage(uint64_t timestamp, Statistics::JSONUse use) const {
|
||||||
bool includeSlices) const {
|
|
||||||
/*
|
/*
|
||||||
* The format of the JSON message is specified by the GCMajorMarkerPayload
|
* The format of the JSON message is specified by the GCMajorMarkerPayload
|
||||||
* type in perf.html
|
* type in perf.html
|
||||||
|
@ -607,9 +606,9 @@ UniqueChars Statistics::renderJsonMessage(uint64_t timestamp,
|
||||||
|
|
||||||
json.beginObject();
|
json.beginObject();
|
||||||
json.property("status", "completed"); // JSON Key #1
|
json.property("status", "completed"); // JSON Key #1
|
||||||
formatJsonDescription(timestamp, json); // #2-22
|
formatJsonDescription(timestamp, json, use); // #2-22
|
||||||
|
|
||||||
if (includeSlices) {
|
if (use == Statistics::JSONUse::TELEMETRY) {
|
||||||
json.beginListProperty("slices_list"); // #23
|
json.beginListProperty("slices_list"); // #23
|
||||||
for (unsigned i = 0; i < slices_.length(); i++) {
|
for (unsigned i = 0; i < slices_.length(); i++) {
|
||||||
formatJsonSlice(i, json);
|
formatJsonSlice(i, json);
|
||||||
|
@ -627,10 +626,11 @@ UniqueChars Statistics::renderJsonMessage(uint64_t timestamp,
|
||||||
}
|
}
|
||||||
|
|
||||||
void Statistics::formatJsonDescription(uint64_t timestamp,
|
void Statistics::formatJsonDescription(uint64_t timestamp,
|
||||||
JSONPrinter& json) const {
|
JSONPrinter& json,
|
||||||
|
JSONUse use) const {
|
||||||
// If you change JSON properties here, please update:
|
// If you change JSON properties here, please update:
|
||||||
// Telemetry ping code:
|
// Telemetry ping code:
|
||||||
// toolkit/components/telemetry/GCTelemetry.jsm
|
// toolkit/components/telemetry/other/GCTelemetry.jsm
|
||||||
// Telemetry documentation:
|
// Telemetry documentation:
|
||||||
// toolkit/components/telemetry/docs/data/main-ping.rst
|
// toolkit/components/telemetry/docs/data/main-ping.rst
|
||||||
// Telemetry tests:
|
// Telemetry tests:
|
||||||
|
@ -676,7 +676,11 @@ void Statistics::formatJsonDescription(uint64_t timestamp,
|
||||||
json.property("nonincremental_reason",
|
json.property("nonincremental_reason",
|
||||||
ExplainAbortReason(nonincrementalReason_)); // #16
|
ExplainAbortReason(nonincrementalReason_)); // #16
|
||||||
}
|
}
|
||||||
json.property("allocated_bytes", preBytes); // #17
|
json.property("allocated_bytes", preHeapSize); // #17
|
||||||
|
if (use == Statistics::JSONUse::PROFILER) {
|
||||||
|
json.property("post_heap_size", postHeapSize);
|
||||||
|
}
|
||||||
|
|
||||||
uint32_t addedChunks = getCount(COUNT_NEW_CHUNK);
|
uint32_t addedChunks = getCount(COUNT_NEW_CHUNK);
|
||||||
if (addedChunks) {
|
if (addedChunks) {
|
||||||
json.property("added_chunks", addedChunks); // #18
|
json.property("added_chunks", addedChunks); // #18
|
||||||
|
@ -694,7 +698,7 @@ void Statistics::formatJsonSliceDescription(unsigned i, const SliceData& slice,
|
||||||
JSONPrinter& json) const {
|
JSONPrinter& json) const {
|
||||||
// If you change JSON properties here, please update:
|
// If you change JSON properties here, please update:
|
||||||
// Telemetry ping code:
|
// Telemetry ping code:
|
||||||
// toolkit/components/telemetry/GCTelemetry.jsm
|
// toolkit/components/telemetry/other/GCTelemetry.jsm
|
||||||
// Telemetry documentation:
|
// Telemetry documentation:
|
||||||
// toolkit/components/telemetry/docs/data/main-ping.rst
|
// toolkit/components/telemetry/docs/data/main-ping.rst
|
||||||
// Telemetry tests:
|
// Telemetry tests:
|
||||||
|
@ -742,7 +746,8 @@ Statistics::Statistics(JSRuntime* rt)
|
||||||
gcDebugFile(nullptr),
|
gcDebugFile(nullptr),
|
||||||
nonincrementalReason_(gc::AbortReason::None),
|
nonincrementalReason_(gc::AbortReason::None),
|
||||||
allocsSinceMinorGC({0, 0}),
|
allocsSinceMinorGC({0, 0}),
|
||||||
preBytes(0),
|
preHeapSize(0),
|
||||||
|
postHeapSize(0),
|
||||||
thresholdTriggered(false),
|
thresholdTriggered(false),
|
||||||
triggerAmount(0.0),
|
triggerAmount(0.0),
|
||||||
triggerThreshold(0.0),
|
triggerThreshold(0.0),
|
||||||
|
@ -971,7 +976,7 @@ void Statistics::beginGC(JSGCInvocationKind kind) {
|
||||||
gckind = kind;
|
gckind = kind;
|
||||||
nonincrementalReason_ = gc::AbortReason::None;
|
nonincrementalReason_ = gc::AbortReason::None;
|
||||||
|
|
||||||
preBytes = runtime->gc.usage.gcBytes();
|
preHeapSize = runtime->gc.heapSize.gcBytes();
|
||||||
startingMajorGCNumber = runtime->gc.majorGCCount();
|
startingMajorGCNumber = runtime->gc.majorGCCount();
|
||||||
startingSliceNumber = runtime->gc.gcNumber();
|
startingSliceNumber = runtime->gc.gcNumber();
|
||||||
}
|
}
|
||||||
|
@ -979,6 +984,7 @@ void Statistics::beginGC(JSGCInvocationKind kind) {
|
||||||
void Statistics::endGC() {
|
void Statistics::endGC() {
|
||||||
TimeDuration sccTotal, sccLongest;
|
TimeDuration sccTotal, sccLongest;
|
||||||
sccDurations(&sccTotal, &sccLongest);
|
sccDurations(&sccTotal, &sccLongest);
|
||||||
|
postHeapSize = runtime->gc.heapSize.gcBytes();
|
||||||
|
|
||||||
runtime->addTelemetry(JS_TELEMETRY_GC_IS_ZONE_GC,
|
runtime->addTelemetry(JS_TELEMETRY_GC_IS_ZONE_GC,
|
||||||
!zoneStats.isFullCollection());
|
!zoneStats.isFullCollection());
|
||||||
|
|
|
@ -286,10 +286,14 @@ struct Statistics {
|
||||||
// Print total profile times on shutdown.
|
// Print total profile times on shutdown.
|
||||||
void printTotalProfileTimes();
|
void printTotalProfileTimes();
|
||||||
|
|
||||||
// Return JSON for a whole major GC, optionally including detailed
|
enum JSONUse {
|
||||||
// per-slice data.
|
TELEMETRY,
|
||||||
UniqueChars renderJsonMessage(uint64_t timestamp,
|
PROFILER
|
||||||
bool includeSlices = true) const;
|
};
|
||||||
|
|
||||||
|
// Return JSON for a whole major GC. If use == PROFILER then
|
||||||
|
// detailed per-slice data and some other fields will be included.
|
||||||
|
UniqueChars renderJsonMessage(uint64_t timestamp, JSONUse use) const;
|
||||||
|
|
||||||
// Return JSON for the timings of just the given slice.
|
// Return JSON for the timings of just the given slice.
|
||||||
UniqueChars renderJsonSlice(size_t sliceNum) const;
|
UniqueChars renderJsonSlice(size_t sliceNum) const;
|
||||||
|
@ -356,8 +360,9 @@ struct Statistics {
|
||||||
uint32_t tenured;
|
uint32_t tenured;
|
||||||
} allocsSinceMinorGC;
|
} allocsSinceMinorGC;
|
||||||
|
|
||||||
/* Allocated space before the GC started. */
|
/* Heap size before and after the GC ran. */
|
||||||
size_t preBytes;
|
size_t preHeapSize;
|
||||||
|
size_t postHeapSize;
|
||||||
|
|
||||||
/* If the GC was triggered by exceeding some threshold, record the
|
/* If the GC was triggered by exceeding some threshold, record the
|
||||||
* threshold and the value that exceeded it. */
|
* threshold and the value that exceeded it. */
|
||||||
|
@ -439,7 +444,7 @@ struct Statistics {
|
||||||
UniqueChars formatDetailedPhaseTimes(const PhaseTimeTable& phaseTimes) const;
|
UniqueChars formatDetailedPhaseTimes(const PhaseTimeTable& phaseTimes) const;
|
||||||
UniqueChars formatDetailedTotals() const;
|
UniqueChars formatDetailedTotals() const;
|
||||||
|
|
||||||
void formatJsonDescription(uint64_t timestamp, JSONPrinter&) const;
|
void formatJsonDescription(uint64_t timestamp, JSONPrinter&, JSONUse) const;
|
||||||
void formatJsonSliceDescription(unsigned i, const SliceData& slice,
|
void formatJsonSliceDescription(unsigned i, const SliceData& slice,
|
||||||
JSONPrinter&) const;
|
JSONPrinter&) const;
|
||||||
void formatJsonPhaseTimes(const PhaseTimeTable& phaseTimes,
|
void formatJsonPhaseTimes(const PhaseTimeTable& phaseTimes,
|
||||||
|
|
|
@ -52,7 +52,7 @@ JS::Zone::Zone(JSRuntime* rt)
|
||||||
functionToStringCache_(this),
|
functionToStringCache_(this),
|
||||||
keepAtomsCount(this, 0),
|
keepAtomsCount(this, 0),
|
||||||
purgeAtomsDeferred(this, 0),
|
purgeAtomsDeferred(this, 0),
|
||||||
usage(&rt->gc.usage),
|
zoneSize(&rt->gc.heapSize),
|
||||||
threshold(),
|
threshold(),
|
||||||
gcDelayBytes(0),
|
gcDelayBytes(0),
|
||||||
tenuredStrings(this, 0),
|
tenuredStrings(this, 0),
|
||||||
|
|
|
@ -544,8 +544,8 @@ class Zone : public JS::shadow::Zone,
|
||||||
return functionToStringCache_.ref();
|
return functionToStringCache_.ref();
|
||||||
}
|
}
|
||||||
|
|
||||||
// Track heap usage under this Zone.
|
// Track heap size under this Zone.
|
||||||
js::gc::HeapUsage usage;
|
js::gc::HeapSize zoneSize;
|
||||||
|
|
||||||
// Thresholds used to trigger GC.
|
// Thresholds used to trigger GC.
|
||||||
js::gc::ZoneHeapThreshold threshold;
|
js::gc::ZoneHeapThreshold threshold;
|
||||||
|
|
|
@ -24,7 +24,7 @@ static void NonIncrementalGCSliceCallback(JSContext* cx,
|
||||||
if (progress == GC_CYCLE_END) {
|
if (progress == GC_CYCLE_END) {
|
||||||
mozilla::UniquePtr<char16_t> summary(desc.formatSummaryMessage(cx));
|
mozilla::UniquePtr<char16_t> summary(desc.formatSummaryMessage(cx));
|
||||||
mozilla::UniquePtr<char16_t> message(desc.formatSliceMessage(cx));
|
mozilla::UniquePtr<char16_t> message(desc.formatSliceMessage(cx));
|
||||||
mozilla::UniquePtr<char16_t> json(desc.formatJSON(cx, 0));
|
mozilla::UniquePtr<char16_t> json(desc.formatJSONTelemetry(cx, 0));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1415,5 +1415,5 @@ JS_FRIEND_API JS::Value js::MaybeGetScriptPrivate(JSObject* object) {
|
||||||
}
|
}
|
||||||
|
|
||||||
JS_FRIEND_API uint64_t js::GetGCHeapUsageForObjectZone(JSObject* obj) {
|
JS_FRIEND_API uint64_t js::GetGCHeapUsageForObjectZone(JSObject* obj) {
|
||||||
return obj->zone()->usage.gcBytes();
|
return obj->zone()->zoneSize.gcBytes();
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,13 @@
|
||||||
|
<!DOCTYPE html>
|
||||||
|
<head>
|
||||||
|
<script>
|
||||||
|
function go() {
|
||||||
|
document.getElementById("tweakMe").style.overflowAnchor = "none";
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
</head>
|
||||||
|
<body onload="go()">
|
||||||
|
<div style="position:fixed">
|
||||||
|
<div id="tweakMe">Hi</div>
|
||||||
|
</div>
|
||||||
|
</body>
|
|
@ -724,3 +724,4 @@ pref(layout.css.column-span.enabled,true) load 1507196.html
|
||||||
pref(layout.css.column-span.enabled,true) load 1517033.html
|
pref(layout.css.column-span.enabled,true) load 1517033.html
|
||||||
pref(layout.css.column-span.enabled,true) load 1517297.html
|
pref(layout.css.column-span.enabled,true) load 1517297.html
|
||||||
load 1520798-1.xul
|
load 1520798-1.xul
|
||||||
|
load 1520798-2.html
|
||||||
|
|
|
@ -1592,6 +1592,94 @@ var gCSSProperties = {
|
||||||
],
|
],
|
||||||
invalid_values: [ "-1px", "4px -2px", "inherit 2px", "2px inherit", "2", "2px 2", "2 2px", "2px calc(0px + rubbish)", "unset 2px", "2px unset" ]
|
invalid_values: [ "-1px", "4px -2px", "inherit 2px", "2px inherit", "2", "2px 2", "2 2px", "2px calc(0px + rubbish)", "unset 2px", "2px unset" ]
|
||||||
},
|
},
|
||||||
|
"border-start-start-radius": {
|
||||||
|
domProp: "borderStartStartRadius",
|
||||||
|
inherited: false,
|
||||||
|
type: CSS_TYPE_LONGHAND,
|
||||||
|
applies_to_first_letter: true,
|
||||||
|
logical: true,
|
||||||
|
prerequisites: { "width": "200px", "height": "100px", "display": "inline-block"},
|
||||||
|
initial_values: [ "0", "0px", "calc(-2px)" ],
|
||||||
|
other_values: [ "0%", "3%", "1px", "2em", // circular
|
||||||
|
"3% 2%", "1px 4px", "2em 2pt", // elliptical
|
||||||
|
"calc(-1%)",
|
||||||
|
"calc(2px)",
|
||||||
|
"calc(50%)",
|
||||||
|
"calc(3*25px)",
|
||||||
|
"calc(3*25px) 5px",
|
||||||
|
"5px calc(3*25px)",
|
||||||
|
"calc(20%) calc(3*25px)",
|
||||||
|
"calc(25px*3)",
|
||||||
|
"calc(3*25px + 50%)",
|
||||||
|
],
|
||||||
|
invalid_values: [ "-1px", "4px -2px", "inherit 2px", "2px inherit", "2", "2px 2", "2 2px", "2px calc(0px + rubbish)", "unset 2px", "2px unset" ]
|
||||||
|
},
|
||||||
|
"border-start-end-radius": {
|
||||||
|
domProp: "borderStartEndRadius",
|
||||||
|
inherited: false,
|
||||||
|
type: CSS_TYPE_LONGHAND,
|
||||||
|
applies_to_first_letter: true,
|
||||||
|
logical: true,
|
||||||
|
prerequisites: { "width": "200px", "height": "100px", "display": "inline-block"},
|
||||||
|
initial_values: [ "0", "0px", "calc(-2px)" ],
|
||||||
|
other_values: [ "0%", "3%", "1px", "2em", // circular
|
||||||
|
"3% 2%", "1px 4px", "2em 2pt", // elliptical
|
||||||
|
"calc(-1%)",
|
||||||
|
"calc(2px)",
|
||||||
|
"calc(50%)",
|
||||||
|
"calc(3*25px)",
|
||||||
|
"calc(3*25px) 5px",
|
||||||
|
"5px calc(3*25px)",
|
||||||
|
"calc(20%) calc(3*25px)",
|
||||||
|
"calc(25px*3)",
|
||||||
|
"calc(3*25px + 50%)",
|
||||||
|
],
|
||||||
|
invalid_values: [ "-1px", "4px -2px", "inherit 2px", "2px inherit", "2", "2px 2", "2 2px", "2px calc(0px + rubbish)", "unset 2px", "2px unset" ]
|
||||||
|
},
|
||||||
|
"border-end-start-radius": {
|
||||||
|
domProp: "borderEndStartRadius",
|
||||||
|
inherited: false,
|
||||||
|
type: CSS_TYPE_LONGHAND,
|
||||||
|
applies_to_first_letter: true,
|
||||||
|
logical: true,
|
||||||
|
prerequisites: { "width": "200px", "height": "100px", "display": "inline-block"},
|
||||||
|
initial_values: [ "0", "0px", "calc(-2px)" ],
|
||||||
|
other_values: [ "0%", "3%", "1px", "2em", // circular
|
||||||
|
"3% 2%", "1px 4px", "2em 2pt", // elliptical
|
||||||
|
"calc(-1%)",
|
||||||
|
"calc(2px)",
|
||||||
|
"calc(50%)",
|
||||||
|
"calc(3*25px)",
|
||||||
|
"calc(3*25px) 5px",
|
||||||
|
"5px calc(3*25px)",
|
||||||
|
"calc(20%) calc(3*25px)",
|
||||||
|
"calc(25px*3)",
|
||||||
|
"calc(3*25px + 50%)",
|
||||||
|
],
|
||||||
|
invalid_values: [ "-1px", "4px -2px", "inherit 2px", "2px inherit", "2", "2px 2", "2 2px", "2px calc(0px + rubbish)", "unset 2px", "2px unset" ]
|
||||||
|
},
|
||||||
|
"border-end-end-radius": {
|
||||||
|
domProp: "borderEndEndRadius",
|
||||||
|
inherited: false,
|
||||||
|
type: CSS_TYPE_LONGHAND,
|
||||||
|
applies_to_first_letter: true,
|
||||||
|
logical: true,
|
||||||
|
prerequisites: { "width": "200px", "height": "100px", "display": "inline-block"},
|
||||||
|
initial_values: [ "0", "0px", "calc(-2px)" ],
|
||||||
|
other_values: [ "0%", "3%", "1px", "2em", // circular
|
||||||
|
"3% 2%", "1px 4px", "2em 2pt", // elliptical
|
||||||
|
"calc(-1%)",
|
||||||
|
"calc(2px)",
|
||||||
|
"calc(50%)",
|
||||||
|
"calc(3*25px)",
|
||||||
|
"calc(3*25px) 5px",
|
||||||
|
"5px calc(3*25px)",
|
||||||
|
"calc(20%) calc(3*25px)",
|
||||||
|
"calc(25px*3)",
|
||||||
|
"calc(3*25px + 50%)",
|
||||||
|
],
|
||||||
|
invalid_values: [ "-1px", "4px -2px", "inherit 2px", "2px inherit", "2", "2px 2", "2 2px", "2px calc(0px + rubbish)", "unset 2px", "2px unset" ]
|
||||||
|
},
|
||||||
"border-inline-start": {
|
"border-inline-start": {
|
||||||
domProp: "borderInlineStart",
|
domProp: "borderInlineStart",
|
||||||
inherited: false,
|
inherited: false,
|
||||||
|
|
|
@ -64,6 +64,10 @@ var supported_properties = {
|
||||||
"border-bottom-right-radius": [ test_radius_transition ],
|
"border-bottom-right-radius": [ test_radius_transition ],
|
||||||
"border-top-left-radius": [ test_radius_transition ],
|
"border-top-left-radius": [ test_radius_transition ],
|
||||||
"border-top-right-radius": [ test_radius_transition ],
|
"border-top-right-radius": [ test_radius_transition ],
|
||||||
|
"border-start-start-radius": [ test_radius_transition ],
|
||||||
|
"border-start-end-radius": [ test_radius_transition ],
|
||||||
|
"border-end-start-radius": [ test_radius_transition ],
|
||||||
|
"border-end-end-radius": [ test_radius_transition ],
|
||||||
"-moz-box-flex": [ test_float_zeroToOne_transition,
|
"-moz-box-flex": [ test_float_zeroToOne_transition,
|
||||||
test_float_aboveOne_transition,
|
test_float_aboveOne_transition,
|
||||||
test_float_zeroToOne_clamped ],
|
test_float_zeroToOne_clamped ],
|
||||||
|
|
|
@ -621,6 +621,7 @@ package org.mozilla.geckoview {
|
||||||
public static interface GeckoSession.TrackingProtectionDelegate {
|
public static interface GeckoSession.TrackingProtectionDelegate {
|
||||||
method @android.support.annotation.UiThread public void onTrackerBlocked(@android.support.annotation.NonNull org.mozilla.geckoview.GeckoSession, @android.support.annotation.Nullable java.lang.String, int);
|
method @android.support.annotation.UiThread public void onTrackerBlocked(@android.support.annotation.NonNull org.mozilla.geckoview.GeckoSession, @android.support.annotation.Nullable java.lang.String, int);
|
||||||
field public static final int CATEGORY_AD = 1;
|
field public static final int CATEGORY_AD = 1;
|
||||||
|
field public static final int CATEGORY_AD_EXT = 64;
|
||||||
field public static final int CATEGORY_ALL = 31;
|
field public static final int CATEGORY_ALL = 31;
|
||||||
field public static final int CATEGORY_ANALYTIC = 2;
|
field public static final int CATEGORY_ANALYTIC = 2;
|
||||||
field public static final int CATEGORY_CONTENT = 8;
|
field public static final int CATEGORY_CONTENT = 8;
|
||||||
|
|
|
@ -3816,7 +3816,7 @@ public class GeckoSession implements Parcelable {
|
||||||
@IntDef(flag = true,
|
@IntDef(flag = true,
|
||||||
value = { CATEGORY_NONE, CATEGORY_AD, CATEGORY_ANALYTIC,
|
value = { CATEGORY_NONE, CATEGORY_AD, CATEGORY_ANALYTIC,
|
||||||
CATEGORY_SOCIAL, CATEGORY_CONTENT, CATEGORY_ALL,
|
CATEGORY_SOCIAL, CATEGORY_CONTENT, CATEGORY_ALL,
|
||||||
CATEGORY_TEST })
|
CATEGORY_TEST, CATEGORY_AD_EXT })
|
||||||
/* package */ @interface Category {}
|
/* package */ @interface Category {}
|
||||||
|
|
||||||
static final int CATEGORY_NONE = 0;
|
static final int CATEGORY_NONE = 0;
|
||||||
|
@ -3844,6 +3844,10 @@ public class GeckoSession implements Parcelable {
|
||||||
* Block all known trackers.
|
* Block all known trackers.
|
||||||
*/
|
*/
|
||||||
static final int CATEGORY_ALL = (1 << 5) - 1;
|
static final int CATEGORY_ALL = (1 << 5) - 1;
|
||||||
|
/**
|
||||||
|
* Experimental: Block advertisements.
|
||||||
|
*/
|
||||||
|
static final int CATEGORY_AD_EXT = 1 << 6;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A tracking element has been blocked from loading.
|
* A tracking element has been blocked from loading.
|
||||||
|
|
|
@ -14,6 +14,13 @@ import org.mozilla.geckoview.GeckoSession.TrackingProtectionDelegate;
|
||||||
private static final String ANALYTIC = "analytics-track-digest256";
|
private static final String ANALYTIC = "analytics-track-digest256";
|
||||||
private static final String SOCIAL = "social-track-digest256";
|
private static final String SOCIAL = "social-track-digest256";
|
||||||
private static final String CONTENT = "content-track-digest256";
|
private static final String CONTENT = "content-track-digest256";
|
||||||
|
private static final String[] AD_EXT = new String[] {
|
||||||
|
"fanboy-annoyance-digest256",
|
||||||
|
"fanboy-social-digest256",
|
||||||
|
"easylist-digest25",
|
||||||
|
"easyprivacy-digest25",
|
||||||
|
"adguard-digest25"
|
||||||
|
};
|
||||||
|
|
||||||
/* package */ static String buildPrefValue(int categories) {
|
/* package */ static String buildPrefValue(int categories) {
|
||||||
StringBuilder builder = new StringBuilder();
|
StringBuilder builder = new StringBuilder();
|
||||||
|
@ -36,6 +43,11 @@ import org.mozilla.geckoview.GeckoSession.TrackingProtectionDelegate;
|
||||||
if ((categories & TrackingProtectionDelegate.CATEGORY_CONTENT) != 0) {
|
if ((categories & TrackingProtectionDelegate.CATEGORY_CONTENT) != 0) {
|
||||||
builder.append(CONTENT).append(',');
|
builder.append(CONTENT).append(',');
|
||||||
}
|
}
|
||||||
|
if ((categories & TrackingProtectionDelegate.CATEGORY_AD_EXT) != 0) {
|
||||||
|
for (final String l: AD_EXT) {
|
||||||
|
builder.append(l).append(',');
|
||||||
|
}
|
||||||
|
}
|
||||||
// Trim final ','.
|
// Trim final ','.
|
||||||
return builder.substring(0, builder.length() - 1);
|
return builder.substring(0, builder.length() - 1);
|
||||||
}
|
}
|
||||||
|
@ -57,6 +69,12 @@ import org.mozilla.geckoview.GeckoSession.TrackingProtectionDelegate;
|
||||||
if (list.indexOf(CONTENT) != -1) {
|
if (list.indexOf(CONTENT) != -1) {
|
||||||
category |= TrackingProtectionDelegate.CATEGORY_CONTENT;
|
category |= TrackingProtectionDelegate.CATEGORY_CONTENT;
|
||||||
}
|
}
|
||||||
|
for (final String l: AD_EXT) {
|
||||||
|
if (list.indexOf(l) != -1) {
|
||||||
|
category |= TrackingProtectionDelegate.CATEGORY_AD_EXT;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
return category;
|
return category;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -26,6 +26,8 @@ description: GeckoView API Changelog.
|
||||||
- Added `@UiThread` to `GeckoSession.releaseSession` and `GeckoSession.setSession`
|
- Added `@UiThread` to `GeckoSession.releaseSession` and `GeckoSession.setSession`
|
||||||
|
|
||||||
## v65
|
## v65
|
||||||
|
- Added experimental ad-blocking category to `GeckoSession.TrackingProtectionDelegate`.
|
||||||
|
|
||||||
- Moved [`CompositorController`][65.1], [`DynamicToolbarAnimator`][65.2],
|
- Moved [`CompositorController`][65.1], [`DynamicToolbarAnimator`][65.2],
|
||||||
[`OverscrollEdgeEffect`][65.3], [`PanZoomController`][65.4] from
|
[`OverscrollEdgeEffect`][65.3], [`PanZoomController`][65.4] from
|
||||||
`org.mozilla.gecko.gfx` to [`org.mozilla.geckoview`][65.5]
|
`org.mozilla.gecko.gfx` to [`org.mozilla.geckoview`][65.5]
|
||||||
|
@ -113,4 +115,4 @@ description: GeckoView API Changelog.
|
||||||
[65.24]: ../CrashReporter.html#sendCrashReport-android.content.Context-android.os.Bundle-java.lang.String-
|
[65.24]: ../CrashReporter.html#sendCrashReport-android.content.Context-android.os.Bundle-java.lang.String-
|
||||||
[65.25]: ../GeckoResult.html
|
[65.25]: ../GeckoResult.html
|
||||||
|
|
||||||
[api-version]: e7a6a3ed65c75f7cb278b693adfa09cae5238ca2
|
[api-version]: 45d1d8774e913a3077d7c489274184fd301f14fc
|
||||||
|
|
|
@ -5571,7 +5571,7 @@ pref("urlclassifier.features.cryptomining.blacklistTables", "");
|
||||||
pref("urlclassifier.features.cryptomining.whitelistTables", "");
|
pref("urlclassifier.features.cryptomining.whitelistTables", "");
|
||||||
|
|
||||||
// These tables will never trigger a gethash call.
|
// These tables will never trigger a gethash call.
|
||||||
pref("urlclassifier.disallow_completions", "test-malware-simple,test-harmful-simple,test-phish-simple,test-unwanted-simple,test-track-simple,test-trackwhite-simple,test-block-simple,goog-downloadwhite-digest256,base-track-digest256,mozstd-trackwhite-digest256,content-track-digest256,mozplugin-block-digest256,mozplugin2-block-digest256,block-flash-digest256,except-flash-digest256,allow-flashallow-digest256,except-flashallow-digest256,block-flashsubdoc-digest256,except-flashsubdoc-digest256,goog-passwordwhite-proto,ads-track-digest256,social-track-digest256,analytics-track-digest256,base-fingerprinting-track-digest256,content-fingerprinting-track-digest256,base-cryptomining-track-digest256,content-cryptomining-track-digest256");
|
pref("urlclassifier.disallow_completions", "test-malware-simple,test-harmful-simple,test-phish-simple,test-unwanted-simple,test-track-simple,test-trackwhite-simple,test-block-simple,goog-downloadwhite-digest256,base-track-digest256,mozstd-trackwhite-digest256,content-track-digest256,mozplugin-block-digest256,mozplugin2-block-digest256,block-flash-digest256,except-flash-digest256,allow-flashallow-digest256,except-flashallow-digest256,block-flashsubdoc-digest256,except-flashsubdoc-digest256,goog-passwordwhite-proto,ads-track-digest256,social-track-digest256,analytics-track-digest256,base-fingerprinting-track-digest256,content-fingerprinting-track-digest256,base-cryptomining-track-digest256,content-cryptomining-track-digest256,fanboy-annoyance-digest256,fanboy-social-digest256,easylist-digest256,easyprivacy-digest256,adguard-digest256");
|
||||||
|
|
||||||
// Number of random entries to send with a gethash request
|
// Number of random entries to send with a gethash request
|
||||||
pref("urlclassifier.gethashnoise", 4);
|
pref("urlclassifier.gethashnoise", 4);
|
||||||
|
@ -5640,7 +5640,7 @@ pref("browser.safebrowsing.reportPhishURL", "https://%LOCALE%.phish-report.mozil
|
||||||
|
|
||||||
// Mozilla Safe Browsing provider (for tracking protection and plugin blocking)
|
// Mozilla Safe Browsing provider (for tracking protection and plugin blocking)
|
||||||
pref("browser.safebrowsing.provider.mozilla.pver", "2.2");
|
pref("browser.safebrowsing.provider.mozilla.pver", "2.2");
|
||||||
pref("browser.safebrowsing.provider.mozilla.lists", "base-track-digest256,mozstd-trackwhite-digest256,content-track-digest256,mozplugin-block-digest256,mozplugin2-block-digest256,block-flash-digest256,except-flash-digest256,allow-flashallow-digest256,except-flashallow-digest256,block-flashsubdoc-digest256,except-flashsubdoc-digest256,ads-track-digest256,social-track-digest256,analytics-track-digest256,base-fingerprinting-track-digest256,content-fingerprinting-track-digest256,base-cryptomining-track-digest256,content-cryptomining-track-digest256");
|
pref("browser.safebrowsing.provider.mozilla.lists", "base-track-digest256,mozstd-trackwhite-digest256,content-track-digest256,mozplugin-block-digest256,mozplugin2-block-digest256,block-flash-digest256,except-flash-digest256,allow-flashallow-digest256,except-flashallow-digest256,block-flashsubdoc-digest256,except-flashsubdoc-digest256,ads-track-digest256,social-track-digest256,analytics-track-digest256,base-fingerprinting-track-digest256,content-fingerprinting-track-digest256,base-cryptomining-track-digest256,content-cryptomining-track-digest256,fanboy-annoyance-digest256,fanboy-social-digest256,easylist-digest256,easyprivacy-digest256,adguard-digest256");
|
||||||
pref("browser.safebrowsing.provider.mozilla.updateURL", "https://shavar.services.mozilla.com/downloads?client=SAFEBROWSING_ID&appver=%MAJOR_VERSION%&pver=2.2");
|
pref("browser.safebrowsing.provider.mozilla.updateURL", "https://shavar.services.mozilla.com/downloads?client=SAFEBROWSING_ID&appver=%MAJOR_VERSION%&pver=2.2");
|
||||||
pref("browser.safebrowsing.provider.mozilla.gethashURL", "https://shavar.services.mozilla.com/gethash?client=SAFEBROWSING_ID&appver=%MAJOR_VERSION%&pver=2.2");
|
pref("browser.safebrowsing.provider.mozilla.gethashURL", "https://shavar.services.mozilla.com/gethash?client=SAFEBROWSING_ID&appver=%MAJOR_VERSION%&pver=2.2");
|
||||||
// Set to a date in the past to force immediate download in new profiles.
|
// Set to a date in the past to force immediate download in new profiles.
|
||||||
|
|
|
@ -12,6 +12,7 @@
|
||||||
|
|
||||||
#include "mozilla/ScopeExit.h"
|
#include "mozilla/ScopeExit.h"
|
||||||
#include "mozilla/TypedEnumBits.h"
|
#include "mozilla/TypedEnumBits.h"
|
||||||
|
#include "mozilla/Unused.h"
|
||||||
|
|
||||||
#define COPY_CODES(NBYTES) \
|
#define COPY_CODES(NBYTES) \
|
||||||
do { \
|
do { \
|
||||||
|
@ -137,6 +138,7 @@ class WindowsDllDetourPatcher final : public WindowsDllPatcherBase<VMPolicy> {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
#elif defined(_M_ARM64)
|
#elif defined(_M_ARM64)
|
||||||
|
Unused << opcode1;
|
||||||
MOZ_RELEASE_ASSERT(false, "Shouldn't get here");
|
MOZ_RELEASE_ASSERT(false, "Shouldn't get here");
|
||||||
#else
|
#else
|
||||||
#error "Unknown processor type"
|
#error "Unknown processor type"
|
||||||
|
|
|
@ -171,6 +171,49 @@ impl WritingMode {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
fn physical_sides_to_corner(block_side: PhysicalSide, inline_side: PhysicalSide) -> PhysicalCorner {
|
||||||
|
match (block_side, inline_side) {
|
||||||
|
(PhysicalSide::Top, PhysicalSide::Left) |
|
||||||
|
(PhysicalSide::Left, PhysicalSide::Top) => PhysicalCorner::TopLeft,
|
||||||
|
(PhysicalSide::Top, PhysicalSide::Right) |
|
||||||
|
(PhysicalSide::Right, PhysicalSide::Top) => PhysicalCorner::TopRight,
|
||||||
|
(PhysicalSide::Bottom, PhysicalSide::Right) |
|
||||||
|
(PhysicalSide::Right, PhysicalSide::Bottom) => PhysicalCorner::BottomRight,
|
||||||
|
(PhysicalSide::Bottom, PhysicalSide::Left) |
|
||||||
|
(PhysicalSide::Left, PhysicalSide::Bottom) => PhysicalCorner::BottomLeft,
|
||||||
|
_ => unreachable!("block and inline sides must be orthogonal")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
pub fn start_start_physical_corner(&self) -> PhysicalCorner {
|
||||||
|
WritingMode::physical_sides_to_corner(
|
||||||
|
self.block_start_physical_side(),
|
||||||
|
self.inline_start_physical_side())
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
pub fn start_end_physical_corner(&self) -> PhysicalCorner {
|
||||||
|
WritingMode::physical_sides_to_corner(
|
||||||
|
self.block_start_physical_side(),
|
||||||
|
self.inline_end_physical_side())
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
pub fn end_start_physical_corner(&self) -> PhysicalCorner {
|
||||||
|
WritingMode::physical_sides_to_corner(
|
||||||
|
self.block_end_physical_side(),
|
||||||
|
self.inline_start_physical_side())
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
pub fn end_end_physical_corner(&self) -> PhysicalCorner {
|
||||||
|
WritingMode::physical_sides_to_corner(
|
||||||
|
self.block_end_physical_side(),
|
||||||
|
self.inline_end_physical_side())
|
||||||
|
}
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn block_flow_direction(&self) -> BlockFlowDirection {
|
pub fn block_flow_direction(&self) -> BlockFlowDirection {
|
||||||
match (self.is_vertical(), self.is_vertical_lr()) {
|
match (self.is_vertical(), self.is_vertical_lr()) {
|
||||||
|
@ -1314,3 +1357,11 @@ pub enum PhysicalSide {
|
||||||
Bottom,
|
Bottom,
|
||||||
Left,
|
Left,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, Copy, Debug, PartialEq)]
|
||||||
|
pub enum PhysicalCorner {
|
||||||
|
TopLeft,
|
||||||
|
TopRight,
|
||||||
|
BottomRight,
|
||||||
|
BottomLeft,
|
||||||
|
}
|
||||||
|
|
|
@ -8,10 +8,13 @@ PHYSICAL_SIDES = ["top", "right", "bottom", "left"]
|
||||||
LOGICAL_SIDES = ["block-start", "block-end", "inline-start", "inline-end"]
|
LOGICAL_SIDES = ["block-start", "block-end", "inline-start", "inline-end"]
|
||||||
PHYSICAL_SIZES = ["width", "height"]
|
PHYSICAL_SIZES = ["width", "height"]
|
||||||
LOGICAL_SIZES = ["block-size", "inline-size"]
|
LOGICAL_SIZES = ["block-size", "inline-size"]
|
||||||
|
PHYSICAL_CORNERS = ["top-left", "top-right", "bottom-right", "bottom-left"]
|
||||||
|
LOGICAL_CORNERS = ["start-start", "start-end", "end-start", "end-end"]
|
||||||
|
|
||||||
# bool is True when logical
|
# bool is True when logical
|
||||||
ALL_SIDES = [(side, False) for side in PHYSICAL_SIDES] + [(side, True) for side in LOGICAL_SIDES]
|
ALL_SIDES = [(side, False) for side in PHYSICAL_SIDES] + [(side, True) for side in LOGICAL_SIDES]
|
||||||
ALL_SIZES = [(size, False) for size in PHYSICAL_SIZES] + [(size, True) for size in LOGICAL_SIZES]
|
ALL_SIZES = [(size, False) for size in PHYSICAL_SIZES] + [(size, True) for size in LOGICAL_SIZES]
|
||||||
|
ALL_CORNERS = [(corner, False) for corner in PHYSICAL_CORNERS] + [(corner, True) for corner in LOGICAL_CORNERS]
|
||||||
|
|
||||||
SYSTEM_FONT_LONGHANDS = """font_family font_size font_style
|
SYSTEM_FONT_LONGHANDS = """font_family font_size font_style
|
||||||
font_variant_caps font_stretch font_kerning
|
font_variant_caps font_stretch font_kerning
|
||||||
|
@ -239,12 +242,14 @@ class Longhand(object):
|
||||||
def all_physical_mapped_properties(self):
|
def all_physical_mapped_properties(self):
|
||||||
assert self.logical
|
assert self.logical
|
||||||
logical_side = None
|
logical_side = None
|
||||||
for s in LOGICAL_SIDES + LOGICAL_SIZES:
|
for s in LOGICAL_SIDES + LOGICAL_SIZES + LOGICAL_CORNERS:
|
||||||
if s in self.name:
|
if s in self.name:
|
||||||
assert not logical_side
|
assert not logical_side
|
||||||
logical_side = s
|
logical_side = s
|
||||||
assert logical_side
|
assert logical_side
|
||||||
physical = PHYSICAL_SIDES if logical_side in LOGICAL_SIDES else PHYSICAL_SIZES
|
physical = PHYSICAL_SIDES if logical_side in LOGICAL_SIDES else \
|
||||||
|
PHYSICAL_SIZES if logical_side in LOGICAL_SIZES else \
|
||||||
|
LOGICAL_CORNERS
|
||||||
return [self.name.replace(logical_side, physical_side).replace("inset-", "")
|
return [self.name.replace(logical_side, physical_side).replace("inset-", "")
|
||||||
for physical_side in physical]
|
for physical_side in physical]
|
||||||
|
|
||||||
|
|
|
@ -4,7 +4,7 @@
|
||||||
|
|
||||||
<%!
|
<%!
|
||||||
from data import Keyword, to_rust_ident, to_camel_case
|
from data import Keyword, to_rust_ident, to_camel_case
|
||||||
from data import LOGICAL_SIDES, PHYSICAL_SIDES, LOGICAL_SIZES, SYSTEM_FONT_LONGHANDS
|
from data import LOGICAL_CORNERS, PHYSICAL_CORNERS, LOGICAL_SIDES, PHYSICAL_SIDES, LOGICAL_SIZES, SYSTEM_FONT_LONGHANDS
|
||||||
%>
|
%>
|
||||||
|
|
||||||
<%def name="predefined_type(name, type, initial_value, parse_method='parse',
|
<%def name="predefined_type(name, type, initial_value, parse_method='parse',
|
||||||
|
@ -842,12 +842,16 @@
|
||||||
<%
|
<%
|
||||||
side = None
|
side = None
|
||||||
size = None
|
size = None
|
||||||
|
corner = None
|
||||||
maybe_side = [s for s in LOGICAL_SIDES if s in name]
|
maybe_side = [s for s in LOGICAL_SIDES if s in name]
|
||||||
maybe_size = [s for s in LOGICAL_SIZES if s in name]
|
maybe_size = [s for s in LOGICAL_SIZES if s in name]
|
||||||
|
maybe_corner = [s for s in LOGICAL_CORNERS if s in name]
|
||||||
if len(maybe_side) == 1:
|
if len(maybe_side) == 1:
|
||||||
side = maybe_side[0]
|
side = maybe_side[0]
|
||||||
elif len(maybe_size) == 1:
|
elif len(maybe_size) == 1:
|
||||||
size = maybe_size[0]
|
size = maybe_size[0]
|
||||||
|
elif len(maybe_corner) == 1:
|
||||||
|
corner = maybe_corner[0]
|
||||||
def phys_ident(side, phy_side):
|
def phys_ident(side, phy_side):
|
||||||
return to_rust_ident(name.replace(side, phy_side).replace("inset-", ""))
|
return to_rust_ident(name.replace(side, phy_side).replace("inset-", ""))
|
||||||
%>
|
%>
|
||||||
|
@ -860,6 +864,15 @@
|
||||||
}
|
}
|
||||||
% endfor
|
% endfor
|
||||||
}
|
}
|
||||||
|
% elif corner is not None:
|
||||||
|
use crate::logical_geometry::PhysicalCorner;
|
||||||
|
match wm.${to_rust_ident(corner)}_physical_corner() {
|
||||||
|
% for phy_corner in PHYSICAL_CORNERS:
|
||||||
|
PhysicalCorner::${to_camel_case(phy_corner)} => {
|
||||||
|
${caller.inner(physical_ident=phys_ident(corner, phy_corner))}
|
||||||
|
}
|
||||||
|
% endfor
|
||||||
|
}
|
||||||
% elif size is not None:
|
% elif size is not None:
|
||||||
<%
|
<%
|
||||||
# (horizontal, vertical)
|
# (horizontal, vertical)
|
||||||
|
|
|
@ -3,7 +3,7 @@
|
||||||
* file, You can obtain one at https://mozilla.org/MPL/2.0/. */
|
* file, You can obtain one at https://mozilla.org/MPL/2.0/. */
|
||||||
|
|
||||||
<%namespace name="helpers" file="/helpers.mako.rs" />
|
<%namespace name="helpers" file="/helpers.mako.rs" />
|
||||||
<% from data import Keyword, Method, PHYSICAL_SIDES, ALL_SIDES, maybe_moz_logical_alias %>
|
<% from data import Keyword, Method, ALL_CORNERS, PHYSICAL_SIDES, ALL_SIDES, maybe_moz_logical_alias %>
|
||||||
|
|
||||||
<% data.new_style_struct("Border", inherited=False,
|
<% data.new_style_struct("Border", inherited=False,
|
||||||
additional_methods=[Method("border_" + side + "_has_nonzero_width",
|
additional_methods=[Method("border_" + side + "_has_nonzero_width",
|
||||||
|
@ -70,17 +70,27 @@ ${helpers.gecko_keyword_conversion(
|
||||||
)}
|
)}
|
||||||
|
|
||||||
// FIXME(#4126): when gfx supports painting it, make this Size2D<LengthPercentage>
|
// FIXME(#4126): when gfx supports painting it, make this Size2D<LengthPercentage>
|
||||||
% for corner in ["top-left", "top-right", "bottom-right", "bottom-left"]:
|
% for corner in ALL_CORNERS:
|
||||||
|
<%
|
||||||
|
corner_name = corner[0]
|
||||||
|
is_logical = corner[1]
|
||||||
|
if is_logical:
|
||||||
|
prefixes = None
|
||||||
|
else:
|
||||||
|
prefixes = "webkit"
|
||||||
|
%>
|
||||||
${helpers.predefined_type(
|
${helpers.predefined_type(
|
||||||
"border-" + corner + "-radius",
|
"border-%s-radius" % corner_name,
|
||||||
"BorderCornerRadius",
|
"BorderCornerRadius",
|
||||||
"computed::BorderCornerRadius::zero()",
|
"computed::BorderCornerRadius::zero()",
|
||||||
"parse",
|
"parse",
|
||||||
extra_prefixes="webkit",
|
extra_prefixes=prefixes,
|
||||||
spec="https://drafts.csswg.org/css-backgrounds/#border-%s-radius" % corner,
|
spec=maybe_logical_spec(corner, "radius"),
|
||||||
boxed=True,
|
boxed=True,
|
||||||
flags="APPLIES_TO_FIRST_LETTER",
|
flags="APPLIES_TO_FIRST_LETTER",
|
||||||
animation_value_type="BorderCornerRadius",
|
animation_value_type="BorderCornerRadius",
|
||||||
|
logical_group="border-radius",
|
||||||
|
logical=is_logical,
|
||||||
)}
|
)}
|
||||||
% endfor
|
% endfor
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,18 @@
|
||||||
|
<!DOCTYPE html>
|
||||||
|
<meta charset="utf-8" />
|
||||||
|
<title>CSS Logical Properties: flow-relative border-radius</title>
|
||||||
|
<link rel="author" title="Mats Palmgren" href="mailto:mats@mozilla.com" />
|
||||||
|
<link rel="help" href="https://drafts.csswg.org/css-logical-1/#border-radius-properties">
|
||||||
|
<meta name="assert" content="This test checks the interaction of the flow-relative border-*-radius properties with the physical ones in different writing modes." />
|
||||||
|
<script src="/resources/testharness.js"></script>
|
||||||
|
<script src="/resources/testharnessreport.js"></script>
|
||||||
|
|
||||||
|
<div id="log"></div>
|
||||||
|
|
||||||
|
<script type="module">
|
||||||
|
import {runTests, createCornerPropertyGroup} from "./resources/test-box-properties.js";
|
||||||
|
runTests(createCornerPropertyGroup("border-*-radius", {
|
||||||
|
type: "length",
|
||||||
|
prerequisites: {"border-style": "solid"},
|
||||||
|
}));
|
||||||
|
</script>
|
|
@ -66,6 +66,41 @@ export function createBoxPropertyGroup(property, descriptor) {
|
||||||
return {logical, physical, shorthands, type, prerequisites, property};
|
return {logical, physical, shorthands, type, prerequisites, property};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a group physical and logical box-corner properties.
|
||||||
|
*
|
||||||
|
* @param {string} property
|
||||||
|
* A string representing the property names, like "border-*-radius".
|
||||||
|
* @param {Object} descriptor
|
||||||
|
* @param {string|string[]} descriptor.type
|
||||||
|
* Describes the kind of values accepted by the property, like "length".
|
||||||
|
* Must be a key or a collection of keys from the `testValues` object.
|
||||||
|
* @param {Object={}} descriptor.prerequisites
|
||||||
|
* Represents property declarations that are needed by `property` to work.
|
||||||
|
* For example, border-width properties require a border style.
|
||||||
|
*/
|
||||||
|
export function createCornerPropertyGroup(property, descriptor) {
|
||||||
|
const logical = {};
|
||||||
|
const physical = {};
|
||||||
|
const shorthands = {};
|
||||||
|
for (const logicalCorner of ["start-start", "start-end", "end-start", "end-end"]) {
|
||||||
|
const prop = property.replace("*", logicalCorner);
|
||||||
|
const [block_side, inline_side] = logicalCorner.split("-");
|
||||||
|
const b = "block" + block_side.charAt(0).toUpperCase() + block_side.slice(1);
|
||||||
|
const i = "inline" + inline_side.charAt(0).toUpperCase() + inline_side.slice(1);
|
||||||
|
const index = b + "-" + i; // e.g. "blockStart-inlineEnd"
|
||||||
|
logical[index] = prop;
|
||||||
|
}
|
||||||
|
let prerequisites = "";
|
||||||
|
for (const physicalCorner of ["top-left", "top-right", "bottom-left", "bottom-right"]) {
|
||||||
|
const prop = property.replace("*", physicalCorner);
|
||||||
|
physical[physicalCorner] = prop;
|
||||||
|
prerequisites += makeDeclaration(descriptor.prerequisites, physicalCorner);
|
||||||
|
}
|
||||||
|
const type = [].concat(descriptor.type);
|
||||||
|
return {logical, physical, shorthands, type, prerequisites, property};
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Creates a group of physical and logical sizing properties.
|
* Creates a group of physical and logical sizing properties.
|
||||||
*
|
*
|
||||||
|
@ -101,6 +136,7 @@ export function runTests(group) {
|
||||||
const logicals = Object.values(group.logical);
|
const logicals = Object.values(group.logical);
|
||||||
const physicals = Object.values(group.physical);
|
const physicals = Object.values(group.physical);
|
||||||
const shorthands = group.shorthands ? Object.entries(group.shorthands) : null;
|
const shorthands = group.shorthands ? Object.entries(group.shorthands) : null;
|
||||||
|
const is_corner = group.property == "border-*-radius";
|
||||||
|
|
||||||
test(function() {
|
test(function() {
|
||||||
const expected = [];
|
const expected = [];
|
||||||
|
@ -141,7 +177,22 @@ export function runTests(group) {
|
||||||
|
|
||||||
const associated = {};
|
const associated = {};
|
||||||
for (const [logicalSide, logicalProp] of Object.entries(group.logical)) {
|
for (const [logicalSide, logicalProp] of Object.entries(group.logical)) {
|
||||||
const physicalProp = group.physical[writingMode[logicalSide]];
|
let physicalProp;
|
||||||
|
if (is_corner) {
|
||||||
|
const [ block_side, inline_side] = logicalSide.split("-");
|
||||||
|
const physicalSide1 = writingMode[block_side];
|
||||||
|
const physicalSide2 = writingMode[inline_side];
|
||||||
|
let physicalCorner;
|
||||||
|
// mirror "left-top" to "top-left" etc
|
||||||
|
if (["top", "bottom"].includes(physicalSide1)) {
|
||||||
|
physicalCorner = physicalSide1 + "-" + physicalSide2;
|
||||||
|
} else {
|
||||||
|
physicalCorner = physicalSide2 + "-" + physicalSide1;
|
||||||
|
}
|
||||||
|
physicalProp = group.physical[physicalCorner];
|
||||||
|
} else {
|
||||||
|
physicalProp = group.physical[writingMode[logicalSide]];
|
||||||
|
}
|
||||||
associated[logicalProp] = physicalProp;
|
associated[logicalProp] = physicalProp;
|
||||||
associated[physicalProp] = logicalProp;
|
associated[physicalProp] = logicalProp;
|
||||||
}
|
}
|
||||||
|
|
|
@ -266,8 +266,8 @@ const downloadQuery = query => {
|
||||||
// const endedBefore = normalizeDownloadTime(query.endedBefore, true);
|
// const endedBefore = normalizeDownloadTime(query.endedBefore, true);
|
||||||
// const endedAfter = normalizeDownloadTime(query.endedAfter, false);
|
// const endedAfter = normalizeDownloadTime(query.endedAfter, false);
|
||||||
|
|
||||||
const totalBytesGreater = query.totalBytesGreater;
|
const totalBytesGreater = query.totalBytesGreater !== null ? query.totalBytesGreater : -1;
|
||||||
const totalBytesLess = query.totalBytesLess != null ? query.totalBytesLess : Number.MAX_VALUE;
|
const totalBytesLess = query.totalBytesLess !== null ? query.totalBytesLess : Number.MAX_VALUE;
|
||||||
|
|
||||||
// Handle options for which we can have a regular expression and/or
|
// Handle options for which we can have a regular expression and/or
|
||||||
// an explicit value to match.
|
// an explicit value to match.
|
||||||
|
@ -323,7 +323,7 @@ const downloadQuery = query => {
|
||||||
// todo endedBefore, endedAfter
|
// todo endedBefore, endedAfter
|
||||||
|
|
||||||
if (item.totalBytes == -1) {
|
if (item.totalBytes == -1) {
|
||||||
if (query.totalBytesGreater != null || query.totalBytesLess != null) {
|
if (query.totalBytesGreater !== null || query.totalBytesLess !== null) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
} else if (item.totalBytes <= totalBytesGreater || item.totalBytes >= totalBytesLess) {
|
} else if (item.totalBytes <= totalBytesGreater || item.totalBytes >= totalBytesLess) {
|
||||||
|
|
|
@ -112,7 +112,8 @@
|
||||||
},
|
},
|
||||||
"mime": {
|
"mime": {
|
||||||
"description": "The file's MIME type.",
|
"description": "The file's MIME type.",
|
||||||
"type": "string"
|
"type": "string",
|
||||||
|
"optional": true
|
||||||
},
|
},
|
||||||
"startTime": {
|
"startTime": {
|
||||||
"description": "Number of milliseconds between the unix epoch and when this download began.",
|
"description": "Number of milliseconds between the unix epoch and when this download began.",
|
||||||
|
@ -257,8 +258,7 @@
|
||||||
"totalBytesGreater": {
|
"totalBytesGreater": {
|
||||||
"description": "Limits results to downloads whose totalBytes is greater than the given integer.",
|
"description": "Limits results to downloads whose totalBytes is greater than the given integer.",
|
||||||
"optional": true,
|
"optional": true,
|
||||||
"type": "number",
|
"type": "number"
|
||||||
"default": -1
|
|
||||||
},
|
},
|
||||||
"totalBytesLess": {
|
"totalBytesLess": {
|
||||||
"description": "Limits results to downloads whose totalBytes is less than the given integer.",
|
"description": "Limits results to downloads whose totalBytes is less than the given integer.",
|
||||||
|
|
|
@ -2,6 +2,8 @@
|
||||||
/* vim: set sts=2 sw=2 et tw=80: */
|
/* vim: set sts=2 sw=2 et tw=80: */
|
||||||
"use strict";
|
"use strict";
|
||||||
|
|
||||||
|
PromiseTestUtils.whitelistRejectionsGlobally(/Message manager disconnected/);
|
||||||
|
|
||||||
ChromeUtils.import("resource://gre/modules/Downloads.jsm");
|
ChromeUtils.import("resource://gre/modules/Downloads.jsm");
|
||||||
|
|
||||||
const server = createHttpServer();
|
const server = createHttpServer();
|
||||||
|
@ -424,3 +426,49 @@ add_task(async function test_search() {
|
||||||
|
|
||||||
await extension.unload();
|
await extension.unload();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// Test that downloads with totalBytes of -1 (ie, that have not yet started)
|
||||||
|
// work properly. See bug 1519762 for details of a past regression in
|
||||||
|
// this area.
|
||||||
|
add_task(async function test_inprogress() {
|
||||||
|
let resume, resumePromise = new Promise(resolve => { resume = resolve; });
|
||||||
|
server.registerPathHandler("/slow", async (request, response) => {
|
||||||
|
response.processAsync();
|
||||||
|
await resumePromise;
|
||||||
|
response.setHeader("Content-type", "text/plain");
|
||||||
|
response.write("");
|
||||||
|
response.finish();
|
||||||
|
});
|
||||||
|
|
||||||
|
let extension = ExtensionTestUtils.loadExtension({
|
||||||
|
manifest: {
|
||||||
|
permissions: ["downloads"],
|
||||||
|
},
|
||||||
|
background() {
|
||||||
|
browser.test.onMessage.addListener(async (msg, url) => {
|
||||||
|
let id = await browser.downloads.download({url});
|
||||||
|
let full = await browser.downloads.search({id});
|
||||||
|
|
||||||
|
browser.test.assertEq(full.length, 1,
|
||||||
|
"Found new download in search results");
|
||||||
|
browser.test.assertEq(full[0].totalBytes, -1,
|
||||||
|
"New download still has totalBytes == -1");
|
||||||
|
|
||||||
|
browser.downloads.onChanged.addListener(info => {
|
||||||
|
if (info.id == id && info.state.current == "complete") {
|
||||||
|
browser.test.notifyPass("done");
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
browser.test.sendMessage("started");
|
||||||
|
});
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
await extension.startup();
|
||||||
|
extension.sendMessage("go", `${BASE}/slow`);
|
||||||
|
await extension.awaitMessage("started");
|
||||||
|
resume();
|
||||||
|
await extension.awaitFinish("done");
|
||||||
|
await extension.unload();
|
||||||
|
});
|
||||||
|
|
|
@ -800,12 +800,12 @@ void CycleCollectedJSRuntime::TraverseNativeRoots(
|
||||||
profiler_add_marker(
|
profiler_add_marker(
|
||||||
"GCMajor", MakeUnique<GCMajorMarkerPayload>(
|
"GCMajor", MakeUnique<GCMajorMarkerPayload>(
|
||||||
aDesc.startTime(aContext), aDesc.endTime(aContext),
|
aDesc.startTime(aContext), aDesc.endTime(aContext),
|
||||||
aDesc.summaryToJSON(aContext)));
|
aDesc.formatJSONProfiler(aContext)));
|
||||||
} else if (aProgress == JS::GC_SLICE_END) {
|
} else if (aProgress == JS::GC_SLICE_END) {
|
||||||
profiler_add_marker("GCSlice", MakeUnique<GCSliceMarkerPayload>(
|
profiler_add_marker("GCSlice", MakeUnique<GCSliceMarkerPayload>(
|
||||||
aDesc.lastSliceStart(aContext),
|
aDesc.lastSliceStart(aContext),
|
||||||
aDesc.lastSliceEnd(aContext),
|
aDesc.lastSliceEnd(aContext),
|
||||||
aDesc.sliceToJSON(aContext)));
|
aDesc.sliceToJSONProfiler(aContext)));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
Загрузка…
Ссылка в новой задаче