Merge inbound to mozilla-central. a=merge

This commit is contained in:
Margareta Eliza Balazs 2019-01-18 11:40:07 +02:00
Родитель 7ebb432ccd 7cace07078
Коммит ec14417cc3
44 изменённых файлов: 2434 добавлений и 533 удалений

Просмотреть файл

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