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-top-left-radius",
"border-top-right-radius",
"border-start-start-radius",
"border-start-end-radius",
"border-end-start-radius",
"border-end-end-radius",
"bottom",
"column-gap",
"column-width",

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

@ -2857,6 +2857,10 @@ exports.CSS_PROPERTIES = {
"border-inline-end-color",
"border-inline-end-style",
"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-end",
"margin-inline-start",
@ -4128,6 +4132,30 @@ exports.CSS_PROPERTIES = {
"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": {
"isInherited": false,
"subproperties": [
@ -4757,6 +4785,30 @@ exports.CSS_PROPERTIES = {
"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": {
"isInherited": false,
"subproperties": [

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

@ -2174,7 +2174,7 @@ static void DOMGCSliceCallback(JSContext* aCx, JS::GCProgress aProgress,
if (StaticPrefs::javascript_options_mem_notify() ||
Telemetry::CanRecordExtended()) {
nsString json;
json.Adopt(aDesc.formatJSON(aCx, PR_Now()));
json.Adopt(aDesc.formatJSONTelemetry(aCx, PR_Now()));
RefPtr<NotifyGCEndRunnable> notify =
new NotifyGCEndRunnable(std::move(json));
SystemGroup::Dispatch(TaskCategory::GarbageCollection,

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

@ -1447,7 +1447,7 @@ void SkScalerContext_Mac::generateImage(const SkGlyph& glyph) {
if ((glyph.fMaskFormat == SkMask::kLCD16_Format) ||
(glyph.fMaskFormat == SkMask::kA8_Format
&& requestSmooth
&& smooth_behavior() == SmoothBehavior::subpixel))
&& smooth_behavior() != SmoothBehavior::none))
{
const uint8_t* linear = gLinearCoverageFromCGLCDValue.data();

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

@ -634,15 +634,16 @@ struct JS_PUBLIC_API GCDescription {
char16_t* formatSliceMessage(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 endTime(JSContext* cx) const;
mozilla::TimeStamp lastSliceStart(JSContext* cx) const;
mozilla::TimeStamp lastSliceEnd(JSContext* cx) const;
JS::UniqueChars sliceToJSON(JSContext* cx) const;
JS::UniqueChars summaryToJSON(JSContext* cx) const;
char16_t* formatJSONTelemetry(JSContext* cx, uint64_t timestamp) const;
JS::UniqueChars sliceToJSONProfiler(JSContext* cx) const;
JS::UniqueChars formatJSONProfiler(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
size_t preBytes = cx->runtime()->gc.usage.gcBytes();
size_t preBytes = cx->runtime()->gc.heapSize.gcBytes();
#endif
if (zone) {
@ -433,7 +433,7 @@ static bool GC(JSContext* cx, unsigned argc, Value* vp) {
char buf[256] = {'\0'};
#ifndef JS_MORE_DETERMINISTIC
SprintfLiteral(buf, "before %zu, after %zu\n", preBytes,
cx->runtime()->gc.usage.gcBytes());
cx->runtime()->gc.heapSize.gcBytes());
#endif
return ReturnStringCopy(cx, args, buf);
}

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

@ -37,8 +37,10 @@
#include "frontend/ForOfEmitter.h"
#include "frontend/ForOfLoopControl.h"
#include "frontend/IfEmitter.h"
#include "frontend/LabelEmitter.h" // LabelEmitter
#include "frontend/ModuleSharedContext.h"
#include "frontend/NameOpEmitter.h"
#include "frontend/ObjectEmitter.h" // PropertyEmitter, ObjectEmitter, ClassEmitter
#include "frontend/ParseNode.h"
#include "frontend/Parser.h"
#include "frontend/PropOpEmitter.h"
@ -3030,24 +3032,30 @@ bool BytecodeEmitter::setOrEmitSetFunName(ParseNode* maybeFun,
if (maybeFun->isKind(ParseNodeKind::Function)) {
// Function doesn't have 'name' property at this point.
// Set function's name at compile time.
JSFunction* fun = maybeFun->as<CodeNode>().funbox()->function();
// 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;
return setFunName(maybeFun->as<CodeNode>().funbox()->function(), name);
}
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;
if (!makeAtomIndex(name, &nameIndex)) {
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
// the comment on emitSwitch.
MOZ_NEVER_INLINE bool BytecodeEmitter::emitLabeledStatement(
const LabeledStatement* pn) {
/*
* Emit a JSOP_LABEL instruction. The argument is the offset to the statement
* following the labeled statement.
*/
uint32_t index;
if (!makeAtomIndex(pn->label(), &index)) {
const LabeledStatement* labeledStmt) {
LabelEmitter label(this);
if (!label.emitLabel(labeledStmt->label())) {
return false;
}
JumpList top;
if (!emitJump(JSOP_LABEL, &top)) {
if (!emitTree(labeledStmt->statement())) {
return false;
}
/* 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)) {
if (!label.emitEnd()) {
return false;
}
@ -7567,27 +7557,31 @@ bool BytecodeEmitter::emitConditionalExpression(
return true;
}
bool BytecodeEmitter::emitPropertyList(ListNode* obj,
MutableHandlePlainObject objp,
bool BytecodeEmitter::emitPropertyList(ListNode* obj, PropertyEmitter& pe,
PropListType type) {
// [stack] CTOR? OBJ
for (ParseNode* propdef : obj->contents()) {
if (propdef->is<ClassField>()) {
// TODO(khyperia): Implement private field access.
return false;
}
if (!updateSourceCoordNotes(propdef->pn_pos.begin)) {
return false;
}
// Handle __proto__: v specially because *only* this form, and no other
// involving "__proto__", performs [[Prototype]] mutation.
if (propdef->isKind(ParseNodeKind::MutateProto)) {
// [stack] OBJ
MOZ_ASSERT(type == ObjectLiteral);
if (!emitTree(propdef->as<UnaryNode>().kid())) {
if (!pe.prepareForProtoValue(Some(propdef->pn_pos.begin))) {
// [stack] OBJ
return false;
}
objp.set(nullptr);
if (!emit1(JSOP_MUTATEPROTO)) {
if (!emitTree(propdef->as<UnaryNode>().kid())) {
// [stack] OBJ PROTO
return false;
}
if (!pe.emitMutateProto()) {
// [stack] OBJ
return false;
}
continue;
@ -7595,193 +7589,216 @@ bool BytecodeEmitter::emitPropertyList(ListNode* obj,
if (propdef->isKind(ParseNodeKind::Spread)) {
MOZ_ASSERT(type == ObjectLiteral);
if (!emit1(JSOP_DUP)) {
// [stack] OBJ
if (!pe.prepareForSpreadOperand(Some(propdef->pn_pos.begin))) {
// [stack] OBJ OBJ
return false;
}
if (!emitTree(propdef->as<UnaryNode>().kid())) {
// [stack] OBJ OBJ VAL
return false;
}
if (!emitCopyDataProperties(CopyOption::Unfiltered)) {
if (!pe.emitSpread()) {
// [stack] OBJ
return false;
}
objp.set(nullptr);
continue;
}
bool extraPop = false;
if (type == ClassBody && propdef->as<ClassMethod>().isStatic()) {
extraPop = true;
if (!emit1(JSOP_DUP2)) {
BinaryNode* prop = &propdef->as<BinaryNode>();
ParseNode* key = prop->left();
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;
}
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;
}
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. */
ParseNode* key = propdef->as<BinaryNode>().left();
bool isIndex = false;
if (key->isKind(ParseNodeKind::NumberExpr)) {
if (!emitNumberOp(key->as<NumericLiteral>().value())) {
return false;
}
isIndex = true;
} else if (key->isKind(ParseNodeKind::ObjectPropertyName) ||
key->isKind(ParseNodeKind::StringExpr)) {
// EmitClass took care of constructor already.
if (key->isKind(ParseNodeKind::ObjectPropertyName) ||
key->isKind(ParseNodeKind::StringExpr)) {
// [stack] CTOR? OBJ
// emitClass took care of constructor already.
if (type == ClassBody &&
key->as<NameNode>().atom() == cx->names().constructor &&
!propdef->as<ClassMethod>().isStatic()) {
continue;
}
} else {
MOZ_ASSERT(key->isKind(ParseNodeKind::ComputedName));
if (!emitComputedPropertyName(&key->as<UnaryNode>())) {
if (!pe.prepareForPropValue(Some(propdef->pn_pos.begin), kind)) {
// [stack] CTOR? OBJ CTOR?
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. */
ParseNode* propVal = propdef->as<BinaryNode>().right();
if (!emitTree(propVal)) {
MOZ_ASSERT(key->isKind(ParseNodeKind::ComputedName));
// [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;
}
JSOp op = propdef->getOp();
MOZ_ASSERT(op == JSOP_INITPROP || op == JSOP_INITPROP_GETTER ||
op == JSOP_INITPROP_SETTER);
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)) {
switch (op) {
case JSOP_INITPROP:
if (!pe.emitInitComputedProp(isPropertyAnonFunctionOrClass)) {
// [stack] CTOR? OBJ
return false;
}
}
if (!emitDupAt(1 + isIndex + isAsync)) {
return false;
}
if (!emit1(JSOP_INITHOMEOBJECT)) {
return false;
}
if (isAsync) {
if (!emit1(JSOP_POP)) {
break;
case JSOP_INITPROP_GETTER:
MOZ_ASSERT(isPropertyAnonFunctionOrClass);
if (!pe.emitInitComputedGetter()) {
// [stack] CTOR? OBJ
return false;
}
}
}
// Class methods are not enumerable.
if (type == ClassBody) {
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)) {
break;
case JSOP_INITPROP_SETTER:
MOZ_ASSERT(isPropertyAnonFunctionOrClass);
if (!pe.emitInitComputedSetter()) {
// [stack] CTOR? OBJ
return false;
}
if (!emit2(JSOP_SETFUNNAME, uint8_t(prefixKind))) {
return false;
}
}
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;
}
break;
default:
MOZ_CRASH("Invalid op");
}
}
return true;
@ -7795,43 +7812,22 @@ MOZ_NEVER_INLINE bool BytecodeEmitter::emitObject(ListNode* objNode) {
return emitSingletonInitialiser(objNode);
}
/*
* 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__).
*/
ptrdiff_t offset = this->offset();
if (!emitNewInit()) {
// [stack]
ObjectEmitter oe(this);
if (!oe.emitObject(objNode->count())) {
// [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(objNode->count());
RootedPlainObject obj(
cx, NewBuiltinClassInstance<PlainObject>(cx, kind, TenuredObject));
if (!obj) {
if (!emitPropertyList(objNode, oe, ObjectLiteral)) {
// [stack] OBJ
return false;
}
if (!emitPropertyList(objNode, &obj, ObjectLiteral)) {
if (!oe.emitEnd()) {
// [stack] OBJ
return false;
}
if (obj) {
// The object survived and has a predictable shape: update the original
// bytecode.
if (!replaceNewInitWithNewObject(obj, offset)) {
return false;
}
}
return true;
}
@ -8407,7 +8403,11 @@ bool BytecodeEmitter::emitFunctionBody(ParseNode* funBody) {
}
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()) {
return false;
}
@ -8424,272 +8424,108 @@ bool BytecodeEmitter::emitLexicalInitialization(NameNode* name) {
return true;
}
// This follows ES6 14.5.14 (ClassDefinitionEvaluation) and ES6 14.5.15
// (BindingClassDeclarationEvaluation).
bool BytecodeEmitter::emitClass(ClassNode* classNode) {
ClassNames* names = classNode->names();
ParseNode* heritageExpression = classNode->heritage();
ListNode* classMembers = classNode->memberList();
CodeNode* constructor = nullptr;
for (ParseNode* mn : classMembers->contents()) {
static MOZ_ALWAYS_INLINE CodeNode* FindConstructor(JSContext* cx,
ListNode* classMethods) {
for (ParseNode* mn : classMethods->contents()) {
if (mn->is<ClassField>()) {
// TODO(khyperia): Implement private field access.
return false;
continue;
}
ClassMethod& method = mn->as<ClassMethod>();
ParseNode& methodName = method.name();
if (!method.isStatic() &&
(methodName.isKind(ParseNodeKind::ObjectPropertyName) ||
methodName.isKind(ParseNodeKind::StringExpr)) &&
methodName.as<NameNode>().atom() == cx->names().constructor) {
constructor = &method.method();
break;
return &method.method();
}
}
bool savedStrictness = sc->setLocalStrictMode(true);
return nullptr;
}
Maybe<TDZCheckCache> tdzCache;
Maybe<EmitterScope> emitterScope;
// This follows ES6 14.5.14 (ClassDefinitionEvaluation) and ES6 14.5.15
// (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) {
tdzCache.emplace(this);
emitterScope.emplace(this);
if (!emitterScope->enterLexical(this, ScopeKind::Lexical,
classNode->scopeBindings())) {
innerName = names->innerBinding()->name();
MOZ_ASSERT(innerName);
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;
}
}
// 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
// the constructor, we have to make it second, but we want the prototype
// 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.
if (heritageExpression) {
InternalIfEmitter ifThenElse(this);
bool isDerived = !!heritageExpression;
if (isDerived) {
if (!emitTree(heritageExpression)) {
// [stack] ... HERITAGE
// [stack] HERITAGE
return false;
}
// Heritage must be null or a non-generator constructor
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
if (!ce.emitDerivedClass(innerName)) {
// [stack] HERITAGE HOMEOBJ
return false;
}
} else {
if (!emitNewInit()) {
// [stack] ... HOMEOBJ
if (!ce.emitClass(innerName)) {
// [stack] HOMEOBJ
return false;
}
}
// Stack currently has HOMEOBJ followed by optional HERITAGE. When HERITAGE
// is not used, an implicit value of %FunctionPrototype% is implied.
if (constructor) {
if (!emitFunction(constructor, !!heritageExpression)) {
// [stack] ... HOMEOBJ CONSTRUCTOR
bool needsHomeObject = constructor->funbox()->needsHomeObject();
// HERITAGE is consumed inside emitFunction.
if (!emitFunction(constructor, isDerived)) {
// [stack] HOMEOBJ CTOR
return false;
}
if (constructor->funbox()->needsHomeObject()) {
if (!emitDupAt(1)) {
// [stack] ... HOMEOBJ CONSTRUCTOR HOMEOBJ
return false;
}
if (!emit1(JSOP_INITHOMEOBJECT)) {
// [stack] ... HOMEOBJ CONSTRUCTOR
return false;
}
if (!ce.emitInitConstructor(needsHomeObject)) {
// [stack] CTOR HOMEOBJ
return false;
}
} else {
// 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.
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)) {
if (!ce.emitInitDefaultConstructor(Some(classNode->pn_pos.begin),
Some(classNode->pn_pos.end))) {
// [stack] CTOR HOMEOBJ
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 (!emit1(JSOP_SWAP)) {
// [stack] ... CONSTRUCTOR HOMEOBJ
if (!emitPropertyList(classMembers, ce, ClassBody)) {
// [stack] CTOR HOMEOBJ
return false;
}
if (!emit1(JSOP_DUP2)) {
// [stack] ... CONSTRUCTOR HOMEOBJ CONSTRUCTOR HOMEOBJ
if (!ce.emitEnd(kind)) {
// [stack] # class declaration
// [stack]
// [stack] # class expression
// [stack] CTOR
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;
}

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

@ -112,6 +112,7 @@ class CallOrNewEmitter;
class ElemOpEmitter;
class EmitterScope;
class NestableControl;
class PropertyEmitter;
class TDZCheckCache;
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 emitPropertyList(ListNode* obj,
MutableHandlePlainObject objp,
MOZ_MUST_USE bool emitPropertyList(ListNode* obj, PropertyEmitter& pe,
PropListType type);
// 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_NEVER_INLINE MOZ_MUST_USE bool emitLabeledStatement(
const LabeledStatement* pn);
const LabeledStatement* labeledStmt);
MOZ_NEVER_INLINE MOZ_MUST_USE bool emitLexicalScope(
LexicalScopeNode* lexicalScope);
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 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 emitCallSiteObject(CallSiteNode* callSiteObj);
@ -851,6 +853,7 @@ struct MOZ_STACK_CLASS BytecodeEmitter {
MOZ_MUST_USE bool emitInitializeFunctionSpecialNames();
MOZ_MUST_USE bool emitFunctionBody(ParseNode* funBody);
MOZ_MUST_USE bool emitLexicalInitialization(NameNode* name);
MOZ_MUST_USE bool emitLexicalInitialization(JSAtom* name);
// 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',
'IfEmitter.cpp',
'JumpList.cpp',
'LabelEmitter.cpp',
'NameFunctions.cpp',
'NameOpEmitter.cpp',
'ObjectEmitter.cpp',
'ParseContext.cpp',
'ParseNode.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
// the world and do a full, non-incremental GC right now, if possible.
if (isIncrementalGCInProgress() &&
cx->zone()->usage.gcBytes() > cx->zone()->threshold.gcTriggerBytes()) {
cx->zone()->zoneSize.gcBytes() > cx->zone()->threshold.gcTriggerBytes()) {
PrepareZoneForGC(cx->zone());
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.
if ((checkThresholds != ShouldCheckThresholds::DontCheckThresholds) &&
(usage.gcBytes() >= tunables.gcMaxBytes()))
(heapSize.gcBytes() >= tunables.gcMaxBytes()))
return nullptr;
Arena* arena = chunk->allocateArena(rt, zone, thingKind, lock);
zone->usage.addGCArena();
zone->zoneSize.addGCArena();
// Trigger an incremental slice if needed.
if (checkThresholds != ShouldCheckThresholds::DontCheckThresholds) {

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

@ -892,7 +892,7 @@ void Chunk::updateChunkListAfterFree(JSRuntime* rt, 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);
}
@ -902,7 +902,7 @@ GCRuntime::GCRuntime(JSRuntime* rt)
atomsZone(nullptr),
stats_(rt),
marker(rt),
usage(nullptr),
heapSize(nullptr),
rootsHash(256),
nextCellUniqueId_(LargestTaggedNullCellPointer +
1), // Ensure disjoint from null tagged pointers.
@ -1379,7 +1379,7 @@ bool GCRuntime::setParameter(JSGCParamKey key, uint32_t value,
return false;
}
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);
}
}
@ -1607,7 +1607,7 @@ void GCRuntime::resetParameter(JSGCParamKey key, AutoLockGC& lock) {
default:
tunables.resetParameter(key, lock);
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);
}
}
@ -1685,7 +1685,7 @@ uint32_t GCRuntime::getParameter(JSGCParamKey key, const AutoLockGC& lock) {
case JSGC_MAX_MALLOC_BYTES:
return mallocCounter.maxBytes();
case JSGC_BYTES:
return uint32_t(usage.gcBytes());
return uint32_t(heapSize.gcBytes());
case JSGC_MODE:
return uint32_t(mode);
case JSGC_UNUSED_CHUNKS:
@ -3280,7 +3280,7 @@ void GCRuntime::maybeAllocTriggerZoneGC(Zone* zone, const AutoLockGC& lock) {
MOZ_ASSERT(!JS::RuntimeHeapIsCollecting());
size_t usedBytes = zone->usage.gcBytes();
size_t usedBytes = zone->zoneSize.gcBytes();
size_t thresholdBytes = zone->threshold.gcTriggerBytes();
if (usedBytes >= thresholdBytes) {
@ -3378,7 +3378,7 @@ void GCRuntime::maybeGC(Zone* zone) {
float threshold = zone->threshold.eagerAllocTrigger(
schedulingState.inHighFrequencyGCMode());
float usedBytes = zone->usage.gcBytes();
float usedBytes = zone->zoneSize.gcBytes();
if (usedBytes > 1024 * 1024 && usedBytes >= threshold &&
!isIncrementalGCInProgress() && !isBackgroundSweeping()) {
stats().recordTrigger(usedBytes, threshold);
@ -5782,7 +5782,7 @@ IncrementalProgress GCRuntime::endSweepingSweepGroup(FreeOp* fop,
for (SweepGroupZonesIter zone(rt); !zone.done(); zone.next()) {
AutoLockGC lock(rt);
zone->changeGCState(Zone::Sweep, Zone::Finished);
zone->threshold.updateAfterGC(zone->usage.gcBytes(), invocationKind,
zone->threshold.updateAfterGC(zone->zoneSize.gcBytes(), invocationKind,
tunables, schedulingState, lock);
zone->updateAllGCMallocCountersOnGCEnd(lock);
zone->arenas.unmarkPreMarkedFreeCells();
@ -7258,7 +7258,7 @@ GCRuntime::IncrementalResult GCRuntime::budgetIncrementalGC(
continue;
}
if (zone->usage.gcBytes() >= zone->threshold.gcTriggerBytes()) {
if (zone->zoneSize.gcBytes() >= zone->threshold.gcTriggerBytes()) {
CheckZoneIsScheduled(zone, reason, "GC bytes");
budget.makeUnlimited();
stats().nonincremental(AbortReason::GCBytesTrigger);
@ -7314,7 +7314,7 @@ class AutoScheduleZonesForGC {
// This is a heuristic to reduce the total number of collections.
bool inHighFrequencyMode = gc->schedulingState.inHighFrequencyGCMode();
if (zone->usage.gcBytes() >=
if (zone->zoneSize.gcBytes() >=
zone->threshold.eagerAllocTrigger(inHighFrequencyMode)) {
zone->scheduleGC();
}
@ -8136,7 +8136,7 @@ void GCRuntime::mergeRealms(Realm* source, Realm* target) {
targetZoneIsCollecting);
target->zone()->addTenuredAllocsSinceMinorGC(
source->zone()->getAndResetTenuredAllocsSinceMinorGC());
target->zone()->usage.adopt(source->zone()->usage);
target->zone()->zoneSize.adopt(source->zone()->zoneSize);
target->zone()->adoptUniqueIds(source->zone());
target->zone()->adoptMallocBytes(source->zone());
@ -8592,9 +8592,10 @@ JS::dbg::GarbageCollectionEvent::Ptr JS::GCDescription::toGCEvent(
cx->runtime()->gc.majorGCCount());
}
char16_t* JS::GCDescription::formatJSON(JSContext* cx,
uint64_t timestamp) const {
UniqueChars cstr = cx->runtime()->gc.stats().renderJsonMessage(timestamp);
char16_t* JS::GCDescription::formatJSONTelemetry(JSContext* cx,
uint64_t timestamp) const {
UniqueChars cstr = cx->runtime()->gc.stats().renderJsonMessage(timestamp,
gcstats::Statistics::JSONUse::TELEMETRY);
size_t nchars = strlen(cstr.get());
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;
}
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();
MOZ_ASSERT(slices > 0);
return cx->runtime()->gc.stats().renderJsonSlice(slices - 1);
}
JS::UniqueChars JS::GCDescription::summaryToJSON(JSContext* cx) const {
return cx->runtime()->gc.stats().renderJsonMessage(0, false);
JS::UniqueChars JS::GCDescription::formatJSONProfiler(JSContext* cx) const {
return cx->runtime()->gc.stats().renderJsonMessage(0,
js::gcstats::Statistics::JSONUse::PROFILER);
}
JS_PUBLIC_API JS::UniqueChars JS::MinorGcToJSON(JSContext* cx) {
@ -8721,7 +8723,7 @@ namespace MemInfo {
static bool GCBytesGetter(JSContext* cx, unsigned argc, Value* 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;
}
@ -8770,7 +8772,7 @@ static bool MinorGCCountGetter(JSContext* cx, unsigned argc, Value* vp) {
static bool ZoneGCBytesGetter(JSContext* cx, unsigned argc, Value* vp) {
CallArgs args = CallArgsFromVp(argc, vp);
args.rval().setNumber(double(cx->zone()->usage.gcBytes()));
args.rval().setNumber(double(cx->zone()->zoneSize.gcBytes()));
return true;
}

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

@ -714,8 +714,8 @@ class GCRuntime {
Vector<JS::GCCellPtr, 0, SystemAllocPolicy> unmarkGrayStack;
/* Track heap usage for this runtime. */
HeapUsage usage;
/* Track heap size for this runtime. */
HeapSize heapSize;
/* GC scheduling state and parameters. */
GCSchedulingTunables tunables;

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

@ -842,12 +842,12 @@ static_assert(
* Tracks the used sizes for owned heap data and automatically maintains the
* 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
* 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
@ -861,7 +861,7 @@ class HeapUsage {
gcBytes_;
public:
explicit HeapUsage(HeapUsage* parent) : parent_(parent), gcBytes_(0) {}
explicit HeapSize(HeapSize* parent) : parent_(parent), gcBytes_(0) {}
size_t gcBytes() const { return gcBytes_; }
@ -880,7 +880,7 @@ class HeapUsage {
}
/* Pair to adoptArenas. Adopts the attendant usage statistics. */
void adopt(HeapUsage& other) {
void adopt(HeapSize& other) {
gcBytes_ += other.gcBytes_;
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
// overflowed, we disable the nursery. The next time we allocate, we'll fail
// because gcBytes >= gcMaxBytes.
if (rt->gc.usage.gcBytes() >= tunables().gcMaxBytes()) {
if (rt->gc.heapSize.gcBytes() >= tunables().gcMaxBytes()) {
disable();
}
// Disable the nursery if the user changed the configuration setting. The

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

@ -327,7 +327,7 @@ UniqueChars Statistics::formatCompactSummaryMessage() const {
zoneStats.collectedZoneCount, zoneStats.zoneCount,
zoneStats.sweptZoneCount, zoneStats.collectedCompartmentCount,
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]);
if (!fragments.append(DuplicateString(buffer))) {
@ -449,7 +449,7 @@ UniqueChars Statistics::formatDetailedDescription() const {
zoneStats.compartmentCount, zoneStats.sweptCompartmentCount,
getCount(COUNT_MINOR_GC), getCount(COUNT_STOREBUFFER_OVERFLOW),
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),
double(ArenaSize * getCount(COUNT_ARENA_RELOCATED)) / bytesPerMiB,
@ -585,8 +585,7 @@ void Statistics::writeLogMessage(const char* fmt, ...) {
}
#endif
UniqueChars Statistics::renderJsonMessage(uint64_t timestamp,
bool includeSlices) const {
UniqueChars Statistics::renderJsonMessage(uint64_t timestamp, Statistics::JSONUse use) const {
/*
* The format of the JSON message is specified by the GCMajorMarkerPayload
* type in perf.html
@ -607,9 +606,9 @@ UniqueChars Statistics::renderJsonMessage(uint64_t timestamp,
json.beginObject();
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
for (unsigned i = 0; i < slices_.length(); i++) {
formatJsonSlice(i, json);
@ -627,10 +626,11 @@ UniqueChars Statistics::renderJsonMessage(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:
// Telemetry ping code:
// toolkit/components/telemetry/GCTelemetry.jsm
// toolkit/components/telemetry/other/GCTelemetry.jsm
// Telemetry documentation:
// toolkit/components/telemetry/docs/data/main-ping.rst
// Telemetry tests:
@ -676,7 +676,11 @@ void Statistics::formatJsonDescription(uint64_t timestamp,
json.property("nonincremental_reason",
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);
if (addedChunks) {
json.property("added_chunks", addedChunks); // #18
@ -694,7 +698,7 @@ void Statistics::formatJsonSliceDescription(unsigned i, const SliceData& slice,
JSONPrinter& json) const {
// If you change JSON properties here, please update:
// Telemetry ping code:
// toolkit/components/telemetry/GCTelemetry.jsm
// toolkit/components/telemetry/other/GCTelemetry.jsm
// Telemetry documentation:
// toolkit/components/telemetry/docs/data/main-ping.rst
// Telemetry tests:
@ -742,7 +746,8 @@ Statistics::Statistics(JSRuntime* rt)
gcDebugFile(nullptr),
nonincrementalReason_(gc::AbortReason::None),
allocsSinceMinorGC({0, 0}),
preBytes(0),
preHeapSize(0),
postHeapSize(0),
thresholdTriggered(false),
triggerAmount(0.0),
triggerThreshold(0.0),
@ -971,7 +976,7 @@ void Statistics::beginGC(JSGCInvocationKind kind) {
gckind = kind;
nonincrementalReason_ = gc::AbortReason::None;
preBytes = runtime->gc.usage.gcBytes();
preHeapSize = runtime->gc.heapSize.gcBytes();
startingMajorGCNumber = runtime->gc.majorGCCount();
startingSliceNumber = runtime->gc.gcNumber();
}
@ -979,6 +984,7 @@ void Statistics::beginGC(JSGCInvocationKind kind) {
void Statistics::endGC() {
TimeDuration sccTotal, sccLongest;
sccDurations(&sccTotal, &sccLongest);
postHeapSize = runtime->gc.heapSize.gcBytes();
runtime->addTelemetry(JS_TELEMETRY_GC_IS_ZONE_GC,
!zoneStats.isFullCollection());

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

@ -286,10 +286,14 @@ struct Statistics {
// Print total profile times on shutdown.
void printTotalProfileTimes();
// Return JSON for a whole major GC, optionally including detailed
// per-slice data.
UniqueChars renderJsonMessage(uint64_t timestamp,
bool includeSlices = true) const;
enum JSONUse {
TELEMETRY,
PROFILER
};
// 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.
UniqueChars renderJsonSlice(size_t sliceNum) const;
@ -356,8 +360,9 @@ struct Statistics {
uint32_t tenured;
} allocsSinceMinorGC;
/* Allocated space before the GC started. */
size_t preBytes;
/* Heap size before and after the GC ran. */
size_t preHeapSize;
size_t postHeapSize;
/* If the GC was triggered by exceeding some threshold, record the
* threshold and the value that exceeded it. */
@ -439,7 +444,7 @@ struct Statistics {
UniqueChars formatDetailedPhaseTimes(const PhaseTimeTable& phaseTimes) 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,
JSONPrinter&) const;
void formatJsonPhaseTimes(const PhaseTimeTable& phaseTimes,

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

@ -52,7 +52,7 @@ JS::Zone::Zone(JSRuntime* rt)
functionToStringCache_(this),
keepAtomsCount(this, 0),
purgeAtomsDeferred(this, 0),
usage(&rt->gc.usage),
zoneSize(&rt->gc.heapSize),
threshold(),
gcDelayBytes(0),
tenuredStrings(this, 0),

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

@ -544,8 +544,8 @@ class Zone : public JS::shadow::Zone,
return functionToStringCache_.ref();
}
// Track heap usage under this Zone.
js::gc::HeapUsage usage;
// Track heap size under this Zone.
js::gc::HeapSize zoneSize;
// Thresholds used to trigger GC.
js::gc::ZoneHeapThreshold threshold;

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

@ -24,7 +24,7 @@ static void NonIncrementalGCSliceCallback(JSContext* cx,
if (progress == GC_CYCLE_END) {
mozilla::UniquePtr<char16_t> summary(desc.formatSummaryMessage(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) {
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 1517297.html
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" ]
},
"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": {
domProp: "borderInlineStart",
inherited: false,

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

@ -64,6 +64,10 @@ var supported_properties = {
"border-bottom-right-radius": [ test_radius_transition ],
"border-top-left-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,
test_float_aboveOne_transition,
test_float_zeroToOne_clamped ],

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

@ -621,6 +621,7 @@ package org.mozilla.geckoview {
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);
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_ANALYTIC = 2;
field public static final int CATEGORY_CONTENT = 8;

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

@ -3816,7 +3816,7 @@ public class GeckoSession implements Parcelable {
@IntDef(flag = true,
value = { CATEGORY_NONE, CATEGORY_AD, CATEGORY_ANALYTIC,
CATEGORY_SOCIAL, CATEGORY_CONTENT, CATEGORY_ALL,
CATEGORY_TEST })
CATEGORY_TEST, CATEGORY_AD_EXT })
/* package */ @interface Category {}
static final int CATEGORY_NONE = 0;
@ -3844,6 +3844,10 @@ public class GeckoSession implements Parcelable {
* Block all known trackers.
*/
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.

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

@ -14,6 +14,13 @@ import org.mozilla.geckoview.GeckoSession.TrackingProtectionDelegate;
private static final String ANALYTIC = "analytics-track-digest256";
private static final String SOCIAL = "social-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) {
StringBuilder builder = new StringBuilder();
@ -36,6 +43,11 @@ import org.mozilla.geckoview.GeckoSession.TrackingProtectionDelegate;
if ((categories & TrackingProtectionDelegate.CATEGORY_CONTENT) != 0) {
builder.append(CONTENT).append(',');
}
if ((categories & TrackingProtectionDelegate.CATEGORY_AD_EXT) != 0) {
for (final String l: AD_EXT) {
builder.append(l).append(',');
}
}
// Trim final ','.
return builder.substring(0, builder.length() - 1);
}
@ -57,6 +69,12 @@ import org.mozilla.geckoview.GeckoSession.TrackingProtectionDelegate;
if (list.indexOf(CONTENT) != -1) {
category |= TrackingProtectionDelegate.CATEGORY_CONTENT;
}
for (final String l: AD_EXT) {
if (list.indexOf(l) != -1) {
category |= TrackingProtectionDelegate.CATEGORY_AD_EXT;
break;
}
}
return category;
}
}

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

@ -26,6 +26,8 @@ description: GeckoView API Changelog.
- Added `@UiThread` to `GeckoSession.releaseSession` and `GeckoSession.setSession`
## v65
- Added experimental ad-blocking category to `GeckoSession.TrackingProtectionDelegate`.
- Moved [`CompositorController`][65.1], [`DynamicToolbarAnimator`][65.2],
[`OverscrollEdgeEffect`][65.3], [`PanZoomController`][65.4] from
`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.25]: ../GeckoResult.html
[api-version]: e7a6a3ed65c75f7cb278b693adfa09cae5238ca2
[api-version]: 45d1d8774e913a3077d7c489274184fd301f14fc

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

@ -5571,7 +5571,7 @@ pref("urlclassifier.features.cryptomining.blacklistTables", "");
pref("urlclassifier.features.cryptomining.whitelistTables", "");
// 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
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)
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.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.

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

@ -12,6 +12,7 @@
#include "mozilla/ScopeExit.h"
#include "mozilla/TypedEnumBits.h"
#include "mozilla/Unused.h"
#define COPY_CODES(NBYTES) \
do { \
@ -137,6 +138,7 @@ class WindowsDllDetourPatcher final : public WindowsDllPatcherBase<VMPolicy> {
continue;
}
#elif defined(_M_ARM64)
Unused << opcode1;
MOZ_RELEASE_ASSERT(false, "Shouldn't get here");
#else
#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]
pub fn block_flow_direction(&self) -> BlockFlowDirection {
match (self.is_vertical(), self.is_vertical_lr()) {
@ -1314,3 +1357,11 @@ pub enum PhysicalSide {
Bottom,
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"]
PHYSICAL_SIZES = ["width", "height"]
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
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_CORNERS = [(corner, False) for corner in PHYSICAL_CORNERS] + [(corner, True) for corner in LOGICAL_CORNERS]
SYSTEM_FONT_LONGHANDS = """font_family font_size font_style
font_variant_caps font_stretch font_kerning
@ -239,12 +242,14 @@ class Longhand(object):
def all_physical_mapped_properties(self):
assert self.logical
logical_side = None
for s in LOGICAL_SIDES + LOGICAL_SIZES:
for s in LOGICAL_SIDES + LOGICAL_SIZES + LOGICAL_CORNERS:
if s in self.name:
assert not logical_side
logical_side = s
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-", "")
for physical_side in physical]

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

@ -4,7 +4,7 @@
<%!
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',
@ -842,12 +842,16 @@
<%
side = None
size = None
corner = None
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_corner = [s for s in LOGICAL_CORNERS if s in name]
if len(maybe_side) == 1:
side = maybe_side[0]
elif len(maybe_size) == 1:
size = maybe_size[0]
elif len(maybe_corner) == 1:
corner = maybe_corner[0]
def phys_ident(side, phy_side):
return to_rust_ident(name.replace(side, phy_side).replace("inset-", ""))
%>
@ -860,6 +864,15 @@
}
% 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:
<%
# (horizontal, vertical)

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

@ -3,7 +3,7 @@
* file, You can obtain one at https://mozilla.org/MPL/2.0/. */
<%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,
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>
% 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(
"border-" + corner + "-radius",
"border-%s-radius" % corner_name,
"BorderCornerRadius",
"computed::BorderCornerRadius::zero()",
"parse",
extra_prefixes="webkit",
spec="https://drafts.csswg.org/css-backgrounds/#border-%s-radius" % corner,
extra_prefixes=prefixes,
spec=maybe_logical_spec(corner, "radius"),
boxed=True,
flags="APPLIES_TO_FIRST_LETTER",
animation_value_type="BorderCornerRadius",
logical_group="border-radius",
logical=is_logical,
)}
% 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};
}
/**
* 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.
*
@ -101,6 +136,7 @@ export function runTests(group) {
const logicals = Object.values(group.logical);
const physicals = Object.values(group.physical);
const shorthands = group.shorthands ? Object.entries(group.shorthands) : null;
const is_corner = group.property == "border-*-radius";
test(function() {
const expected = [];
@ -141,7 +177,22 @@ export function runTests(group) {
const associated = {};
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[physicalProp] = logicalProp;
}

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

@ -266,8 +266,8 @@ const downloadQuery = query => {
// const endedBefore = normalizeDownloadTime(query.endedBefore, true);
// const endedAfter = normalizeDownloadTime(query.endedAfter, false);
const totalBytesGreater = query.totalBytesGreater;
const totalBytesLess = query.totalBytesLess != null ? query.totalBytesLess : Number.MAX_VALUE;
const totalBytesGreater = query.totalBytesGreater !== null ? query.totalBytesGreater : -1;
const totalBytesLess = query.totalBytesLess !== null ? query.totalBytesLess : Number.MAX_VALUE;
// Handle options for which we can have a regular expression and/or
// an explicit value to match.
@ -323,7 +323,7 @@ const downloadQuery = query => {
// todo endedBefore, endedAfter
if (item.totalBytes == -1) {
if (query.totalBytesGreater != null || query.totalBytesLess != null) {
if (query.totalBytesGreater !== null || query.totalBytesLess !== null) {
return false;
}
} else if (item.totalBytes <= totalBytesGreater || item.totalBytes >= totalBytesLess) {

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

@ -112,7 +112,8 @@
},
"mime": {
"description": "The file's MIME type.",
"type": "string"
"type": "string",
"optional": true
},
"startTime": {
"description": "Number of milliseconds between the unix epoch and when this download began.",
@ -257,8 +258,7 @@
"totalBytesGreater": {
"description": "Limits results to downloads whose totalBytes is greater than the given integer.",
"optional": true,
"type": "number",
"default": -1
"type": "number"
},
"totalBytesLess": {
"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: */
"use strict";
PromiseTestUtils.whitelistRejectionsGlobally(/Message manager disconnected/);
ChromeUtils.import("resource://gre/modules/Downloads.jsm");
const server = createHttpServer();
@ -424,3 +426,49 @@ add_task(async function test_search() {
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(
"GCMajor", MakeUnique<GCMajorMarkerPayload>(
aDesc.startTime(aContext), aDesc.endTime(aContext),
aDesc.summaryToJSON(aContext)));
aDesc.formatJSONProfiler(aContext)));
} else if (aProgress == JS::GC_SLICE_END) {
profiler_add_marker("GCSlice", MakeUnique<GCSliceMarkerPayload>(
aDesc.lastSliceStart(aContext),
aDesc.lastSliceEnd(aContext),
aDesc.sliceToJSON(aContext)));
aDesc.sliceToJSONProfiler(aContext)));
}
}
#endif