зеркало из https://github.com/mozilla/gecko-dev.git
Merge inbound to mozilla-central. a=merge
This commit is contained in:
Коммит
ec14417cc3
|
@ -283,6 +283,10 @@ exports.ANIMATION_TYPE_FOR_LONGHANDS = [
|
|||
"border-bottom-right-radius",
|
||||
"border-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
|
||||
|
|
Загрузка…
Ссылка в новой задаче