mirror of
https://repo.dactyloidae.xyz/Dactyloidae/UXP.git
synced 2026-10-28 01:57:30 +09:00
Issue #2173 - Introduce FunctionNode::syntaxKind instead of JSOPs
This moves some "implementation details" to later in the emitter,
and also keeps syntax information available for later use.
Based-on: 1518391/{3+4}
This commit is contained in:
parent
0dac791851
commit
0132ae2101
6 changed files with 138 additions and 133 deletions
|
|
@ -5735,7 +5735,7 @@ BytecodeEmitter::emitFunction(FunctionNode* funNode, bool needsProto)
|
||||||
/* Non-hoisted functions simply emit their respective op. */
|
/* Non-hoisted functions simply emit their respective op. */
|
||||||
if (!funNode->functionIsHoisted()) {
|
if (!funNode->functionIsHoisted()) {
|
||||||
/* JSOP_LAMBDA_ARROW is always preceded by a new.target */
|
/* JSOP_LAMBDA_ARROW is always preceded by a new.target */
|
||||||
MOZ_ASSERT(fun->isArrow() == (funNode->getOp() == JSOP_LAMBDA_ARROW));
|
MOZ_ASSERT(fun->isArrow() == (funNode->syntaxKind() == FunctionSyntaxKind::Arrow));
|
||||||
if (funbox->isAsync()) {
|
if (funbox->isAsync()) {
|
||||||
MOZ_ASSERT(!needsProto);
|
MOZ_ASSERT(!needsProto);
|
||||||
return emitAsyncWrapper(index, funbox->needsHomeObject(), fun->isArrow(),
|
return emitAsyncWrapper(index, funbox->needsHomeObject(), fun->isArrow(),
|
||||||
|
|
@ -5753,13 +5753,16 @@ BytecodeEmitter::emitFunction(FunctionNode* funNode, bool needsProto)
|
||||||
}
|
}
|
||||||
|
|
||||||
if (needsProto) {
|
if (needsProto) {
|
||||||
MOZ_ASSERT(funNode->getOp() == JSOP_LAMBDA);
|
MOZ_ASSERT(funNode->syntaxKind() == FunctionSyntaxKind::DerivedClassConstructor);
|
||||||
return emitIndex32(JSOP_FUNWITHPROTO, index);
|
return emitIndex32(JSOP_FUNWITHPROTO, index);
|
||||||
}
|
}
|
||||||
|
|
||||||
// This is a FunctionExpression, ArrowFunctionExpression, or class
|
// This is a FunctionExpression, ArrowFunctionExpression, or class
|
||||||
// constructor. Emit the single instruction (without location info).
|
// constructor. Emit the single instruction (without location info).
|
||||||
return emitIndex32(funNode->getOp(), index);
|
JSOp op = funNode->syntaxKind() == FunctionSyntaxKind::Arrow
|
||||||
|
? JSOP_LAMBDA_ARROW
|
||||||
|
: JSOP_LAMBDA;
|
||||||
|
return emitIndex32(op, index);
|
||||||
}
|
}
|
||||||
|
|
||||||
MOZ_ASSERT(!needsProto);
|
MOZ_ASSERT(!needsProto);
|
||||||
|
|
@ -5787,7 +5790,7 @@ BytecodeEmitter::emitFunction(FunctionNode* funNode, bool needsProto)
|
||||||
return false;
|
return false;
|
||||||
} else {
|
} else {
|
||||||
MOZ_ASSERT(sc->isGlobalContext() || sc->isEvalContext());
|
MOZ_ASSERT(sc->isGlobalContext() || sc->isEvalContext());
|
||||||
MOZ_ASSERT(funNode->getOp() == JSOP_NOP);
|
MOZ_ASSERT(funNode->syntaxKind() == FunctionSyntaxKind::Statement);
|
||||||
switchToPrologue();
|
switchToPrologue();
|
||||||
if (funbox->isAsync()) {
|
if (funbox->isAsync()) {
|
||||||
if (!emitAsyncWrapper(index, fun->isMethod(), fun->isArrow(),
|
if (!emitAsyncWrapper(index, fun->isMethod(), fun->isArrow(),
|
||||||
|
|
|
||||||
|
|
@ -722,16 +722,8 @@ FOR_EACH_PARSENODE_SUBCLASS(DECLARE_AS)
|
||||||
pn->setDirectRHSAnonFunction(true);
|
pn->setDirectRHSAnonFunction(true);
|
||||||
}
|
}
|
||||||
|
|
||||||
FunctionNodeType newFunctionStatement() {
|
FunctionNodeType newFunction(FunctionSyntaxKind syntaxKind) {
|
||||||
return new_<FunctionNode>(JSOP_NOP, pos());
|
return new_<FunctionNode>(syntaxKind, pos());
|
||||||
}
|
|
||||||
|
|
||||||
FunctionNodeType newFunctionExpression() {
|
|
||||||
return new_<FunctionNode>(JSOP_LAMBDA, pos());
|
|
||||||
}
|
|
||||||
|
|
||||||
FunctionNodeType newArrowFunction() {
|
|
||||||
return new_<FunctionNode>(JSOP_LAMBDA_ARROW, pos());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
bool setComprehensionLambdaBody(FunctionNodeType funNode, ListNodeType body) {
|
bool setComprehensionLambdaBody(FunctionNodeType funNode, ListNodeType body) {
|
||||||
|
|
|
||||||
|
|
@ -227,6 +227,7 @@ IsTypeofKind(ParseNodeKind kind)
|
||||||
* time to specialize arg and var bytecodes early.
|
* time to specialize arg and var bytecodes early.
|
||||||
* body: PNK_PARAMSBODY or null for lazily-parsed function, ordinarily;
|
* body: PNK_PARAMSBODY or null for lazily-parsed function, ordinarily;
|
||||||
* PNK_LEXICALSCOPE for implicit function in genexpr
|
* PNK_LEXICALSCOPE for implicit function in genexpr
|
||||||
|
* syntaxKind: the syntax of the function
|
||||||
* PNK_PARAMSBODY (ListNode)
|
* PNK_PARAMSBODY (ListNode)
|
||||||
* head: list of formal parameters with
|
* head: list of formal parameters with
|
||||||
* * Name node with non-empty name for SingleNameBinding without
|
* * Name node with non-empty name for SingleNameBinding without
|
||||||
|
|
@ -606,6 +607,49 @@ class typeName;
|
||||||
FOR_EACH_PARSENODE_SUBCLASS(DECLARE_CLASS)
|
FOR_EACH_PARSENODE_SUBCLASS(DECLARE_CLASS)
|
||||||
#undef DECLARE_CLASS
|
#undef DECLARE_CLASS
|
||||||
|
|
||||||
|
enum class FunctionSyntaxKind
|
||||||
|
{
|
||||||
|
Expression, // A non-arrow function expression.
|
||||||
|
Statement, // A named function appearing as a Statement.
|
||||||
|
Arrow,
|
||||||
|
Method,
|
||||||
|
ClassConstructor,
|
||||||
|
DerivedClassConstructor,
|
||||||
|
Getter,
|
||||||
|
GetterNoExpressionClosure, // Deprecated syntax: get a() this._a; (bug 1203742)
|
||||||
|
Setter,
|
||||||
|
SetterNoExpressionClosure // Deprecated syntax: set a(x) this._a = x;
|
||||||
|
};
|
||||||
|
|
||||||
|
static inline bool
|
||||||
|
IsConstructorKind(FunctionSyntaxKind kind)
|
||||||
|
{
|
||||||
|
return kind == FunctionSyntaxKind::ClassConstructor ||
|
||||||
|
kind == FunctionSyntaxKind::DerivedClassConstructor;
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline bool
|
||||||
|
IsGetterKind(FunctionSyntaxKind kind)
|
||||||
|
{
|
||||||
|
return kind == FunctionSyntaxKind::Getter ||
|
||||||
|
kind == FunctionSyntaxKind::GetterNoExpressionClosure;
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline bool
|
||||||
|
IsSetterKind(FunctionSyntaxKind kind)
|
||||||
|
{
|
||||||
|
return kind == FunctionSyntaxKind::Setter ||
|
||||||
|
kind == FunctionSyntaxKind::SetterNoExpressionClosure;
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline bool
|
||||||
|
IsMethodDefinitionKind(FunctionSyntaxKind kind)
|
||||||
|
{
|
||||||
|
return kind == FunctionSyntaxKind::Method ||
|
||||||
|
IsConstructorKind(kind) ||
|
||||||
|
IsGetterKind(kind) || IsSetterKind(kind);
|
||||||
|
}
|
||||||
|
|
||||||
class ParseNode
|
class ParseNode
|
||||||
{
|
{
|
||||||
uint16_t pn_type; /* PNK_* type */
|
uint16_t pn_type; /* PNK_* type */
|
||||||
|
|
@ -738,6 +782,7 @@ class ParseNode
|
||||||
friend class FunctionNode;
|
friend class FunctionNode;
|
||||||
FunctionBox* funbox;
|
FunctionBox* funbox;
|
||||||
ParseNode* body;
|
ParseNode* body;
|
||||||
|
FunctionSyntaxKind syntaxKind;
|
||||||
} function;
|
} function;
|
||||||
struct {
|
struct {
|
||||||
private:
|
private:
|
||||||
|
|
@ -1427,14 +1472,12 @@ ParseNode::isForLoopDeclaration() const
|
||||||
class FunctionNode : public ParseNode
|
class FunctionNode : public ParseNode
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
FunctionNode(JSOp op, const TokenPos& pos)
|
FunctionNode(FunctionSyntaxKind syntaxKind, const TokenPos& pos)
|
||||||
: ParseNode(PNK_FUNCTION, op, PN_FUNCTION, pos)
|
: ParseNode(PNK_FUNCTION, JSOP_NOP, PN_FUNCTION, pos)
|
||||||
{
|
{
|
||||||
MOZ_ASSERT(op == JSOP_NOP || // statement, module
|
|
||||||
op == JSOP_LAMBDA_ARROW || // arrow function
|
|
||||||
op == JSOP_LAMBDA); // expression, method, comprehension, accessor, &c.
|
|
||||||
MOZ_ASSERT(!pn_u.function.body);
|
MOZ_ASSERT(!pn_u.function.body);
|
||||||
MOZ_ASSERT(!pn_u.function.funbox);
|
MOZ_ASSERT(!pn_u.function.funbox);
|
||||||
|
pn_u.function.syntaxKind = syntaxKind;
|
||||||
}
|
}
|
||||||
|
|
||||||
static bool test(const ParseNode& node) {
|
static bool test(const ParseNode& node) {
|
||||||
|
|
@ -1468,11 +1511,10 @@ class FunctionNode : public ParseNode
|
||||||
return &pn_u.function.body;
|
return &pn_u.function.body;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
FunctionSyntaxKind syntaxKind() const { return pn_u.function.syntaxKind; }
|
||||||
|
|
||||||
bool functionIsHoisted() const {
|
bool functionIsHoisted() const {
|
||||||
MOZ_ASSERT(isOp(JSOP_LAMBDA) || // lambda
|
return syntaxKind() == FunctionSyntaxKind::Statement;
|
||||||
isOp(JSOP_LAMBDA_ARROW) || // arrow function
|
|
||||||
isOp(JSOP_NOP)); // body-level function stmt in global code
|
|
||||||
return isOp(JSOP_NOP);
|
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
@ -2247,45 +2289,6 @@ enum ParseReportKind
|
||||||
ParseStrictError
|
ParseStrictError
|
||||||
};
|
};
|
||||||
|
|
||||||
enum FunctionSyntaxKind
|
|
||||||
{
|
|
||||||
Expression,
|
|
||||||
Statement,
|
|
||||||
Arrow,
|
|
||||||
Method,
|
|
||||||
ClassConstructor,
|
|
||||||
DerivedClassConstructor,
|
|
||||||
Getter,
|
|
||||||
GetterNoExpressionClosure,
|
|
||||||
Setter,
|
|
||||||
SetterNoExpressionClosure
|
|
||||||
};
|
|
||||||
|
|
||||||
static inline bool
|
|
||||||
IsConstructorKind(FunctionSyntaxKind kind)
|
|
||||||
{
|
|
||||||
return kind == ClassConstructor || kind == DerivedClassConstructor;
|
|
||||||
}
|
|
||||||
|
|
||||||
static inline bool
|
|
||||||
IsGetterKind(FunctionSyntaxKind kind)
|
|
||||||
{
|
|
||||||
return kind == Getter || kind == GetterNoExpressionClosure;
|
|
||||||
}
|
|
||||||
|
|
||||||
static inline bool
|
|
||||||
IsSetterKind(FunctionSyntaxKind kind)
|
|
||||||
{
|
|
||||||
return kind == Setter || kind == SetterNoExpressionClosure;
|
|
||||||
}
|
|
||||||
|
|
||||||
static inline bool
|
|
||||||
IsMethodDefinitionKind(FunctionSyntaxKind kind)
|
|
||||||
{
|
|
||||||
return kind == Method || IsConstructorKind(kind) ||
|
|
||||||
IsGetterKind(kind) || IsSetterKind(kind);
|
|
||||||
}
|
|
||||||
|
|
||||||
static inline ParseNode*
|
static inline ParseNode*
|
||||||
FunctionFormalParametersList(ParseNode* fn, unsigned* numFormals)
|
FunctionFormalParametersList(ParseNode* fn, unsigned* numFormals)
|
||||||
{
|
{
|
||||||
|
|
|
||||||
|
|
@ -560,12 +560,12 @@ FunctionBox::initWithEnclosingParseContext(ParseContext* enclosing, FunctionSynt
|
||||||
allowNewTarget_ = true;
|
allowNewTarget_ = true;
|
||||||
allowSuperProperty_ = fun->allowSuperProperty();
|
allowSuperProperty_ = fun->allowSuperProperty();
|
||||||
|
|
||||||
if (kind == ClassConstructor || kind == DerivedClassConstructor) {
|
if (IsConstructorKind(kind)) {
|
||||||
auto stmt = enclosing->findInnermostStatement<ParseContext::ClassStatement>();
|
auto stmt = enclosing->findInnermostStatement<ParseContext::ClassStatement>();
|
||||||
MOZ_ASSERT(stmt);
|
MOZ_ASSERT(stmt);
|
||||||
stmt->constructorBox = this;
|
stmt->constructorBox = this;
|
||||||
|
|
||||||
if (kind == DerivedClassConstructor) {
|
if (kind == FunctionSyntaxKind::DerivedClassConstructor) {
|
||||||
setDerivedClassConstructor();
|
setDerivedClassConstructor();
|
||||||
allowSuperCall_ = true;
|
allowSuperCall_ = true;
|
||||||
needsThisTDZChecks_ = true;
|
needsThisTDZChecks_ = true;
|
||||||
|
|
@ -2547,7 +2547,7 @@ Parser<FullParseHandler>::standaloneFunction(HandleFunction fun,
|
||||||
tokenStream.ungetToken();
|
tokenStream.ungetToken();
|
||||||
}
|
}
|
||||||
|
|
||||||
FunctionNodeType funNode = handler.newFunctionStatement();
|
FunctionNodeType funNode = handler.newFunction(FunctionSyntaxKind::Statement);
|
||||||
if (!funNode)
|
if (!funNode)
|
||||||
return null();
|
return null();
|
||||||
|
|
||||||
|
|
@ -2569,7 +2569,7 @@ Parser<FullParseHandler>::standaloneFunction(HandleFunction fun,
|
||||||
|
|
||||||
YieldHandling yieldHandling = GetYieldHandling(generatorKind);
|
YieldHandling yieldHandling = GetYieldHandling(generatorKind);
|
||||||
AutoAwaitIsKeyword<FullParseHandler> awaitIsKeyword(this, asyncKind == AsyncFunction);
|
AutoAwaitIsKeyword<FullParseHandler> awaitIsKeyword(this, asyncKind == AsyncFunction);
|
||||||
if (!functionFormalParametersAndBody(InAllowed, yieldHandling, funNode, Statement,
|
if (!functionFormalParametersAndBody(InAllowed, yieldHandling, funNode, FunctionSyntaxKind::Statement,
|
||||||
parameterListEnd, /* isStandaloneFunction = */ true))
|
parameterListEnd, /* isStandaloneFunction = */ true))
|
||||||
{
|
{
|
||||||
return null();
|
return null();
|
||||||
|
|
@ -2734,16 +2734,16 @@ Parser<ParseHandler>::functionBody(InHandling inHandling, YieldHandling yieldHan
|
||||||
MOZ_ASSERT(pc->lastYieldOffset != startYieldOffset);
|
MOZ_ASSERT(pc->lastYieldOffset != startYieldOffset);
|
||||||
|
|
||||||
// These should throw while parsing the yield expression.
|
// These should throw while parsing the yield expression.
|
||||||
MOZ_ASSERT(kind != Arrow);
|
MOZ_ASSERT(kind != FunctionSyntaxKind::Arrow);
|
||||||
MOZ_ASSERT(!IsGetterKind(kind));
|
MOZ_ASSERT(!IsGetterKind(kind));
|
||||||
MOZ_ASSERT(!IsSetterKind(kind));
|
MOZ_ASSERT(!IsSetterKind(kind));
|
||||||
MOZ_ASSERT(!IsConstructorKind(kind));
|
MOZ_ASSERT(!IsConstructorKind(kind));
|
||||||
MOZ_ASSERT(kind != Method);
|
MOZ_ASSERT(kind != FunctionSyntaxKind::Method);
|
||||||
MOZ_ASSERT(type != ExpressionBody);
|
MOZ_ASSERT(type != ExpressionBody);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case StarGenerator:
|
case StarGenerator:
|
||||||
MOZ_ASSERT(kind != Arrow);
|
MOZ_ASSERT(kind != FunctionSyntaxKind::Arrow);
|
||||||
MOZ_ASSERT(type == StatementListBody);
|
MOZ_ASSERT(type == StatementListBody);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
@ -2762,7 +2762,7 @@ Parser<ParseHandler>::functionBody(InHandling inHandling, YieldHandling yieldHan
|
||||||
// Declare the 'arguments' and 'this' bindings if necessary before
|
// Declare the 'arguments' and 'this' bindings if necessary before
|
||||||
// finishing up the scope so these special bindings get marked as closed
|
// finishing up the scope so these special bindings get marked as closed
|
||||||
// over if necessary. Arrow functions don't have these bindings.
|
// over if necessary. Arrow functions don't have these bindings.
|
||||||
if (kind != Arrow) {
|
if (kind != FunctionSyntaxKind::Arrow) {
|
||||||
if (!declareFunctionArgumentsObject())
|
if (!declareFunctionArgumentsObject())
|
||||||
return null();
|
return null();
|
||||||
if (!declareFunctionThis())
|
if (!declareFunctionThis())
|
||||||
|
|
@ -2778,7 +2778,7 @@ Parser<ParseHandler>::newFunction(HandleAtom atom, FunctionSyntaxKind kind,
|
||||||
GeneratorKind generatorKind, FunctionAsyncKind asyncKind,
|
GeneratorKind generatorKind, FunctionAsyncKind asyncKind,
|
||||||
HandleObject proto)
|
HandleObject proto)
|
||||||
{
|
{
|
||||||
MOZ_ASSERT_IF(kind == Statement, atom != nullptr);
|
MOZ_ASSERT_IF(kind == FunctionSyntaxKind::Statement, atom != nullptr);
|
||||||
|
|
||||||
RootedFunction fun(context);
|
RootedFunction fun(context);
|
||||||
|
|
||||||
|
|
@ -2788,39 +2788,39 @@ Parser<ParseHandler>::newFunction(HandleAtom atom, FunctionSyntaxKind kind,
|
||||||
bool isGlobalSelfHostedBuiltin = false;
|
bool isGlobalSelfHostedBuiltin = false;
|
||||||
#endif
|
#endif
|
||||||
switch (kind) {
|
switch (kind) {
|
||||||
case Expression:
|
case FunctionSyntaxKind::Expression:
|
||||||
flags = (generatorKind == NotGenerator && asyncKind == SyncFunction
|
flags = (generatorKind == NotGenerator && asyncKind == SyncFunction
|
||||||
? JSFunction::INTERPRETED_LAMBDA
|
? JSFunction::INTERPRETED_LAMBDA
|
||||||
: JSFunction::INTERPRETED_LAMBDA_GENERATOR_OR_ASYNC);
|
: JSFunction::INTERPRETED_LAMBDA_GENERATOR_OR_ASYNC);
|
||||||
break;
|
break;
|
||||||
case Arrow:
|
case FunctionSyntaxKind::Arrow:
|
||||||
flags = JSFunction::INTERPRETED_LAMBDA_ARROW;
|
flags = JSFunction::INTERPRETED_LAMBDA_ARROW;
|
||||||
allocKind = gc::AllocKind::FUNCTION_EXTENDED;
|
allocKind = gc::AllocKind::FUNCTION_EXTENDED;
|
||||||
break;
|
break;
|
||||||
case Method:
|
case FunctionSyntaxKind::Method:
|
||||||
MOZ_ASSERT(generatorKind == NotGenerator || generatorKind == StarGenerator);
|
MOZ_ASSERT(generatorKind == NotGenerator || generatorKind == StarGenerator);
|
||||||
flags = (generatorKind == NotGenerator && asyncKind == SyncFunction
|
flags = (generatorKind == NotGenerator && asyncKind == SyncFunction
|
||||||
? JSFunction::INTERPRETED_METHOD
|
? JSFunction::INTERPRETED_METHOD
|
||||||
: JSFunction::INTERPRETED_METHOD_GENERATOR_OR_ASYNC);
|
: JSFunction::INTERPRETED_METHOD_GENERATOR_OR_ASYNC);
|
||||||
allocKind = gc::AllocKind::FUNCTION_EXTENDED;
|
allocKind = gc::AllocKind::FUNCTION_EXTENDED;
|
||||||
break;
|
break;
|
||||||
case ClassConstructor:
|
case FunctionSyntaxKind::ClassConstructor:
|
||||||
case DerivedClassConstructor:
|
case FunctionSyntaxKind::DerivedClassConstructor:
|
||||||
flags = JSFunction::INTERPRETED_CLASS_CONSTRUCTOR;
|
flags = JSFunction::INTERPRETED_CLASS_CONSTRUCTOR;
|
||||||
allocKind = gc::AllocKind::FUNCTION_EXTENDED;
|
allocKind = gc::AllocKind::FUNCTION_EXTENDED;
|
||||||
break;
|
break;
|
||||||
case Getter:
|
case FunctionSyntaxKind::Getter:
|
||||||
case GetterNoExpressionClosure:
|
case FunctionSyntaxKind::GetterNoExpressionClosure:
|
||||||
flags = JSFunction::INTERPRETED_GETTER;
|
flags = JSFunction::INTERPRETED_GETTER;
|
||||||
allocKind = gc::AllocKind::FUNCTION_EXTENDED;
|
allocKind = gc::AllocKind::FUNCTION_EXTENDED;
|
||||||
break;
|
break;
|
||||||
case Setter:
|
case FunctionSyntaxKind::Setter:
|
||||||
case SetterNoExpressionClosure:
|
case FunctionSyntaxKind::SetterNoExpressionClosure:
|
||||||
flags = JSFunction::INTERPRETED_SETTER;
|
flags = JSFunction::INTERPRETED_SETTER;
|
||||||
allocKind = gc::AllocKind::FUNCTION_EXTENDED;
|
allocKind = gc::AllocKind::FUNCTION_EXTENDED;
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
MOZ_ASSERT(kind == Statement);
|
MOZ_ASSERT(kind == FunctionSyntaxKind::Statement);
|
||||||
#ifdef DEBUG
|
#ifdef DEBUG
|
||||||
if (options().selfHostingMode && !pc->isFunctionBox()) {
|
if (options().selfHostingMode && !pc->isFunctionBox()) {
|
||||||
isGlobalSelfHostedBuiltin = true;
|
isGlobalSelfHostedBuiltin = true;
|
||||||
|
|
@ -2989,7 +2989,7 @@ Parser<ParseHandler>::functionArguments(YieldHandling yieldHandling, FunctionSyn
|
||||||
// async a => 1
|
// async a => 1
|
||||||
// ^
|
// ^
|
||||||
TokenStream::Modifier argModifier = TokenStream::Operand;
|
TokenStream::Modifier argModifier = TokenStream::Operand;
|
||||||
if (kind == Arrow) {
|
if (kind == FunctionSyntaxKind::Arrow) {
|
||||||
TokenKind tt;
|
TokenKind tt;
|
||||||
// In async function, the first token after `async` is already gotten
|
// In async function, the first token after `async` is already gotten
|
||||||
// with TokenStream::None.
|
// with TokenStream::None.
|
||||||
|
|
@ -3008,7 +3008,7 @@ Parser<ParseHandler>::functionArguments(YieldHandling yieldHandling, FunctionSyn
|
||||||
if (!tokenStream.getToken(&tt, firstTokenModifier))
|
if (!tokenStream.getToken(&tt, firstTokenModifier))
|
||||||
return false;
|
return false;
|
||||||
if (tt != TOK_LP) {
|
if (tt != TOK_LP) {
|
||||||
error(kind == Arrow ? JSMSG_BAD_ARROW_ARGS : JSMSG_PAREN_BEFORE_FORMAL);
|
error(kind == FunctionSyntaxKind::Arrow ? JSMSG_BAD_ARROW_ARGS : JSMSG_PAREN_BEFORE_FORMAL);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -3036,7 +3036,9 @@ Parser<ParseHandler>::functionArguments(YieldHandling yieldHandling, FunctionSyn
|
||||||
bool hasRest = false;
|
bool hasRest = false;
|
||||||
bool hasDefault = false;
|
bool hasDefault = false;
|
||||||
bool duplicatedParam = false;
|
bool duplicatedParam = false;
|
||||||
bool disallowDuplicateParams = kind == Arrow || kind == Method || kind == ClassConstructor;
|
bool disallowDuplicateParams = kind == FunctionSyntaxKind::Arrow ||
|
||||||
|
kind == FunctionSyntaxKind::Method ||
|
||||||
|
kind == FunctionSyntaxKind::ClassConstructor;
|
||||||
AtomVector& positionalFormals = pc->positionalFormalParameterNames();
|
AtomVector& positionalFormals = pc->positionalFormalParameterNames();
|
||||||
|
|
||||||
if (IsGetterKind(kind)) {
|
if (IsGetterKind(kind)) {
|
||||||
|
|
@ -3257,7 +3259,7 @@ Parser<FullParseHandler>::skipLazyInnerFunction(FunctionNode* funNode, uint32_t
|
||||||
// Only expression closure can be Statement kind.
|
// Only expression closure can be Statement kind.
|
||||||
// If we remove expression closure, we can remove isExprBody flag from
|
// If we remove expression closure, we can remove isExprBody flag from
|
||||||
// LazyScript and JSScript.
|
// LazyScript and JSScript.
|
||||||
if (kind == Statement && funbox->isExprBody()) {
|
if (kind == FunctionSyntaxKind::Statement && funbox->isExprBody()) {
|
||||||
if (!matchOrInsertSemicolonAfterExpression())
|
if (!matchOrInsertSemicolonAfterExpression())
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
@ -3351,7 +3353,7 @@ Parser<ParseHandler>::functionDefinition(uint32_t toStringStart, FunctionNodeTyp
|
||||||
GeneratorKind generatorKind, FunctionAsyncKind asyncKind,
|
GeneratorKind generatorKind, FunctionAsyncKind asyncKind,
|
||||||
bool tryAnnexB /* = false */)
|
bool tryAnnexB /* = false */)
|
||||||
{
|
{
|
||||||
MOZ_ASSERT_IF(kind == Statement, funName);
|
MOZ_ASSERT_IF(kind == FunctionSyntaxKind::Statement, funName);
|
||||||
|
|
||||||
// When fully parsing a LazyScript, we do not fully reparse its inner
|
// When fully parsing a LazyScript, we do not fully reparse its inner
|
||||||
// functions, which are also lazy. Instead, their free variables and
|
// functions, which are also lazy. Instead, their free variables and
|
||||||
|
|
@ -3586,7 +3588,20 @@ Parser<FullParseHandler>::standaloneLazyFunction(HandleFunction fun, bool strict
|
||||||
{
|
{
|
||||||
MOZ_ASSERT(checkOptionsCalled);
|
MOZ_ASSERT(checkOptionsCalled);
|
||||||
|
|
||||||
FunctionNodeType funNode = handler.newFunctionStatement();
|
FunctionSyntaxKind syntaxKind = FunctionSyntaxKind::Statement;
|
||||||
|
if (fun->isClassConstructor()) {
|
||||||
|
syntaxKind = FunctionSyntaxKind::ClassConstructor;
|
||||||
|
} else if (fun->isMethod()) {
|
||||||
|
syntaxKind = FunctionSyntaxKind::Method;
|
||||||
|
} else if (fun->isGetter()) {
|
||||||
|
syntaxKind = FunctionSyntaxKind::Getter;
|
||||||
|
} else if (fun->isSetter()) {
|
||||||
|
syntaxKind = FunctionSyntaxKind::Setter;
|
||||||
|
} else if (fun->isArrow()) {
|
||||||
|
syntaxKind = FunctionSyntaxKind::Arrow;
|
||||||
|
}
|
||||||
|
|
||||||
|
FunctionNodeType funNode = handler.newFunction(syntaxKind);
|
||||||
if (!funNode)
|
if (!funNode)
|
||||||
return null();
|
return null();
|
||||||
|
|
||||||
|
|
@ -3613,17 +3628,6 @@ Parser<FullParseHandler>::standaloneLazyFunction(HandleFunction fun, bool strict
|
||||||
return null();
|
return null();
|
||||||
|
|
||||||
YieldHandling yieldHandling = GetYieldHandling(generatorKind);
|
YieldHandling yieldHandling = GetYieldHandling(generatorKind);
|
||||||
FunctionSyntaxKind syntaxKind = Statement;
|
|
||||||
if (fun->isClassConstructor())
|
|
||||||
syntaxKind = ClassConstructor;
|
|
||||||
else if (fun->isMethod())
|
|
||||||
syntaxKind = Method;
|
|
||||||
else if (fun->isGetter())
|
|
||||||
syntaxKind = Getter;
|
|
||||||
else if (fun->isSetter())
|
|
||||||
syntaxKind = Setter;
|
|
||||||
else if (fun->isArrow())
|
|
||||||
syntaxKind = Arrow;
|
|
||||||
|
|
||||||
if (!functionFormalParametersAndBody(InAllowed, yieldHandling, funNode, syntaxKind)) {
|
if (!functionFormalParametersAndBody(InAllowed, yieldHandling, funNode, syntaxKind)) {
|
||||||
MOZ_ASSERT(directives == newDirectives);
|
MOZ_ASSERT(directives == newDirectives);
|
||||||
|
|
@ -3656,7 +3660,8 @@ Parser<ParseHandler>::functionFormalParametersAndBody(InHandling inHandling,
|
||||||
// See below for an explanation why arrow function parameters and arrow
|
// See below for an explanation why arrow function parameters and arrow
|
||||||
// function bodies are parsed with different yield/await settings.
|
// function bodies are parsed with different yield/await settings.
|
||||||
{
|
{
|
||||||
bool asyncOrArrowInAsync = funbox->isAsync() || (kind == Arrow && awaitIsKeyword());
|
bool asyncOrArrowInAsync = funbox->isAsync() ||
|
||||||
|
(kind == FunctionSyntaxKind::Arrow && awaitIsKeyword());
|
||||||
AutoAwaitIsKeyword<ParseHandler> awaitIsKeyword(this, asyncOrArrowInAsync);
|
AutoAwaitIsKeyword<ParseHandler> awaitIsKeyword(this, asyncOrArrowInAsync);
|
||||||
if (!functionArguments(yieldHandling, kind, funNode))
|
if (!functionArguments(yieldHandling, kind, funNode))
|
||||||
return false;
|
return false;
|
||||||
|
|
@ -3671,7 +3676,7 @@ Parser<ParseHandler>::functionFormalParametersAndBody(InHandling inHandling,
|
||||||
pc->functionScope().useAsVarScope(pc);
|
pc->functionScope().useAsVarScope(pc);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (kind == Arrow) {
|
if (kind == FunctionSyntaxKind::Arrow) {
|
||||||
bool matched;
|
bool matched;
|
||||||
if (!tokenStream.matchToken(&matched, TOK_ARROW))
|
if (!tokenStream.matchToken(&matched, TOK_ARROW))
|
||||||
return false;
|
return false;
|
||||||
|
|
@ -3695,14 +3700,15 @@ Parser<ParseHandler>::functionFormalParametersAndBody(InHandling inHandling,
|
||||||
return false;
|
return false;
|
||||||
uint32_t openedPos = 0;
|
uint32_t openedPos = 0;
|
||||||
if (tt != TOK_LC) {
|
if (tt != TOK_LC) {
|
||||||
if (funbox->isStarGenerator() || kind == Method ||
|
if (funbox->isStarGenerator() || kind == FunctionSyntaxKind::Method ||
|
||||||
kind == GetterNoExpressionClosure || kind == SetterNoExpressionClosure ||
|
kind == FunctionSyntaxKind::GetterNoExpressionClosure ||
|
||||||
|
kind == FunctionSyntaxKind::SetterNoExpressionClosure ||
|
||||||
IsConstructorKind(kind)) {
|
IsConstructorKind(kind)) {
|
||||||
error(JSMSG_CURLY_BEFORE_BODY);
|
error(JSMSG_CURLY_BEFORE_BODY);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (kind != Arrow) {
|
if (kind != FunctionSyntaxKind::Arrow) {
|
||||||
#if JS_HAS_EXPR_CLOSURES
|
#if JS_HAS_EXPR_CLOSURES
|
||||||
if (!warnOnceAboutExprClosure())
|
if (!warnOnceAboutExprClosure())
|
||||||
return false;
|
return false;
|
||||||
|
|
@ -3734,10 +3740,11 @@ Parser<ParseHandler>::functionFormalParametersAndBody(InHandling inHandling,
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
if ((kind == Statement || kind == Expression) && fun->explicitName()) {
|
if ((kind == FunctionSyntaxKind::Statement ||
|
||||||
|
kind == FunctionSyntaxKind::Expression) && fun->explicitName()) {
|
||||||
RootedPropertyName propertyName(context, fun->explicitName()->asPropertyName());
|
RootedPropertyName propertyName(context, fun->explicitName()->asPropertyName());
|
||||||
YieldHandling nameYieldHandling;
|
YieldHandling nameYieldHandling;
|
||||||
if (kind == Expression) {
|
if (kind == FunctionSyntaxKind::Expression) {
|
||||||
// Named lambda has binding inside it.
|
// Named lambda has binding inside it.
|
||||||
nameYieldHandling = bodyYieldHandling;
|
nameYieldHandling = bodyYieldHandling;
|
||||||
} else {
|
} else {
|
||||||
|
|
@ -3765,12 +3772,12 @@ Parser<ParseHandler>::functionFormalParametersAndBody(InHandling inHandling,
|
||||||
funbox->setEnd(pos().end);
|
funbox->setEnd(pos().end);
|
||||||
} else {
|
} else {
|
||||||
#if !JS_HAS_EXPR_CLOSURES
|
#if !JS_HAS_EXPR_CLOSURES
|
||||||
MOZ_ASSERT(kind == Arrow);
|
MOZ_ASSERT(kind == FunctionSyntaxKind::Arrow);
|
||||||
#endif
|
#endif
|
||||||
if (tokenStream.hadError())
|
if (tokenStream.hadError())
|
||||||
return false;
|
return false;
|
||||||
funbox->setEnd(pos().end);
|
funbox->setEnd(pos().end);
|
||||||
if (kind == Statement && !matchOrInsertSemicolonAfterExpression())
|
if (kind == FunctionSyntaxKind::Statement && !matchOrInsertSemicolonAfterExpression())
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -3864,13 +3871,14 @@ Parser<ParseHandler>::functionStmt(uint32_t toStringStart, YieldHandling yieldHa
|
||||||
return null();
|
return null();
|
||||||
}
|
}
|
||||||
|
|
||||||
FunctionNodeType funNode = handler.newFunctionStatement();
|
FunctionNodeType funNode = handler.newFunction(FunctionSyntaxKind::Statement);
|
||||||
if (!funNode)
|
if (!funNode)
|
||||||
return null();
|
return null();
|
||||||
|
|
||||||
YieldHandling newYieldHandling = GetYieldHandling(generatorKind);
|
YieldHandling newYieldHandling = GetYieldHandling(generatorKind);
|
||||||
return functionDefinition(toStringStart, funNode, InAllowed, newYieldHandling,
|
return functionDefinition(toStringStart, funNode, InAllowed, newYieldHandling,
|
||||||
name, Statement, generatorKind, asyncKind, tryAnnexB);
|
name, FunctionSyntaxKind::Statement, generatorKind,
|
||||||
|
asyncKind, tryAnnexB);
|
||||||
}
|
}
|
||||||
|
|
||||||
template <typename ParseHandler>
|
template <typename ParseHandler>
|
||||||
|
|
@ -3903,15 +3911,15 @@ Parser<ParseHandler>::functionExpr(uint32_t toStringStart, InvokedPrediction inv
|
||||||
tokenStream.ungetToken();
|
tokenStream.ungetToken();
|
||||||
}
|
}
|
||||||
|
|
||||||
FunctionNodeType funNode = handler.newFunctionExpression();
|
FunctionNodeType funNode = handler.newFunction(FunctionSyntaxKind::Expression);
|
||||||
if (!funNode)
|
if (!funNode)
|
||||||
return null();
|
return null();
|
||||||
|
|
||||||
if (invoked)
|
if (invoked)
|
||||||
funNode = handler.setLikelyIIFE(funNode);
|
funNode = handler.setLikelyIIFE(funNode);
|
||||||
|
|
||||||
return functionDefinition(toStringStart, funNode, InAllowed, yieldHandling, name, Expression,
|
return functionDefinition(toStringStart, funNode, InAllowed, yieldHandling, name,
|
||||||
generatorKind, asyncKind);
|
FunctionSyntaxKind::Expression, generatorKind, asyncKind);
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
|
@ -8395,12 +8403,12 @@ Parser<ParseHandler>::assignExpr(InHandling inHandling, YieldHandling yieldHandl
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
FunctionNodeType funNode = handler.newArrowFunction();
|
FunctionNodeType funNode = handler.newFunction(FunctionSyntaxKind::Arrow);
|
||||||
if (!funNode)
|
if (!funNode)
|
||||||
return null();
|
return null();
|
||||||
|
|
||||||
Node arrowFunc = functionDefinition(toStringStart, funNode, inHandling, yieldHandling, nullptr,
|
Node arrowFunc = functionDefinition(toStringStart, funNode, inHandling, yieldHandling, nullptr,
|
||||||
Arrow, NotGenerator, asyncKind);
|
FunctionSyntaxKind::Arrow, NotGenerator, asyncKind);
|
||||||
if (!arrowFunc)
|
if (!arrowFunc)
|
||||||
return null();
|
return null();
|
||||||
|
|
||||||
|
|
@ -8792,7 +8800,7 @@ template <typename ParseHandler>
|
||||||
typename ParseHandler::Node
|
typename ParseHandler::Node
|
||||||
Parser<ParseHandler>::generatorComprehensionLambda(unsigned begin)
|
Parser<ParseHandler>::generatorComprehensionLambda(unsigned begin)
|
||||||
{
|
{
|
||||||
FunctionNodeType genfn = handler.newFunctionExpression();
|
FunctionNodeType genfn = handler.newFunction(FunctionSyntaxKind::Expression);
|
||||||
if (!genfn)
|
if (!genfn)
|
||||||
return null();
|
return null();
|
||||||
|
|
||||||
|
|
@ -8807,7 +8815,7 @@ Parser<ParseHandler>::generatorComprehensionLambda(unsigned begin)
|
||||||
if (!proto)
|
if (!proto)
|
||||||
return null();
|
return null();
|
||||||
|
|
||||||
RootedFunction fun(context, newFunction(/* atom = */ nullptr, Expression,
|
RootedFunction fun(context, newFunction(/* atom = */ nullptr, FunctionSyntaxKind::Expression,
|
||||||
StarGenerator, SyncFunction, proto));
|
StarGenerator, SyncFunction, proto));
|
||||||
if (!fun)
|
if (!fun)
|
||||||
return null();
|
return null();
|
||||||
|
|
@ -8819,7 +8827,7 @@ Parser<ParseHandler>::generatorComprehensionLambda(unsigned begin)
|
||||||
if (!genFunbox)
|
if (!genFunbox)
|
||||||
return null();
|
return null();
|
||||||
genFunbox->isGenexpLambda = true;
|
genFunbox->isGenexpLambda = true;
|
||||||
genFunbox->initWithEnclosingParseContext(outerpc, Expression);
|
genFunbox->initWithEnclosingParseContext(outerpc, FunctionSyntaxKind::Expression);
|
||||||
|
|
||||||
ParseContext genpc(this, genFunbox, /* newDirectives = */ nullptr);
|
ParseContext genpc(this, genFunbox, /* newDirectives = */ nullptr);
|
||||||
if (!genpc.init())
|
if (!genpc.init())
|
||||||
|
|
@ -10316,37 +10324,37 @@ typename ParseHandler::FunctionNodeType
|
||||||
Parser<ParseHandler>::methodDefinition(uint32_t toStringStart, PropertyType propType,
|
Parser<ParseHandler>::methodDefinition(uint32_t toStringStart, PropertyType propType,
|
||||||
HandleAtom funName)
|
HandleAtom funName)
|
||||||
{
|
{
|
||||||
FunctionSyntaxKind kind;
|
FunctionSyntaxKind syntaxKind;
|
||||||
switch (propType) {
|
switch (propType) {
|
||||||
case PropertyType::Getter:
|
case PropertyType::Getter:
|
||||||
kind = Getter;
|
syntaxKind = FunctionSyntaxKind::Getter;
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case PropertyType::GetterNoExpressionClosure:
|
case PropertyType::GetterNoExpressionClosure:
|
||||||
kind = GetterNoExpressionClosure;
|
syntaxKind = FunctionSyntaxKind::GetterNoExpressionClosure;
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case PropertyType::Setter:
|
case PropertyType::Setter:
|
||||||
kind = Setter;
|
syntaxKind = FunctionSyntaxKind::Setter;
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case PropertyType::SetterNoExpressionClosure:
|
case PropertyType::SetterNoExpressionClosure:
|
||||||
kind = SetterNoExpressionClosure;
|
syntaxKind = FunctionSyntaxKind::SetterNoExpressionClosure;
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case PropertyType::Method:
|
case PropertyType::Method:
|
||||||
case PropertyType::GeneratorMethod:
|
case PropertyType::GeneratorMethod:
|
||||||
case PropertyType::AsyncMethod:
|
case PropertyType::AsyncMethod:
|
||||||
case PropertyType::AsyncGeneratorMethod:
|
case PropertyType::AsyncGeneratorMethod:
|
||||||
kind = Method;
|
syntaxKind = FunctionSyntaxKind::Method;
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case PropertyType::Constructor:
|
case PropertyType::Constructor:
|
||||||
kind = ClassConstructor;
|
syntaxKind = FunctionSyntaxKind::ClassConstructor;
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case PropertyType::DerivedConstructor:
|
case PropertyType::DerivedConstructor:
|
||||||
kind = DerivedClassConstructor;
|
syntaxKind = FunctionSyntaxKind::DerivedClassConstructor;
|
||||||
break;
|
break;
|
||||||
|
|
||||||
default:
|
default:
|
||||||
|
|
@ -10365,12 +10373,12 @@ Parser<ParseHandler>::methodDefinition(uint32_t toStringStart, PropertyType prop
|
||||||
|
|
||||||
YieldHandling yieldHandling = GetYieldHandling(generatorKind);
|
YieldHandling yieldHandling = GetYieldHandling(generatorKind);
|
||||||
|
|
||||||
FunctionNodeType funNode = handler.newFunctionExpression();
|
FunctionNodeType funNode = handler.newFunction(syntaxKind);
|
||||||
if (!funNode)
|
if (!funNode)
|
||||||
return null();
|
return null();
|
||||||
|
|
||||||
return functionDefinition(toStringStart, funNode, InAllowed, yieldHandling, funName,
|
return functionDefinition(toStringStart, funNode, InAllowed, yieldHandling, funName,
|
||||||
kind, generatorKind, asyncKind);
|
syntaxKind, generatorKind, asyncKind);
|
||||||
}
|
}
|
||||||
|
|
||||||
template <typename ParseHandler>
|
template <typename ParseHandler>
|
||||||
|
|
|
||||||
|
|
@ -411,9 +411,7 @@ FOR_EACH_PARSENODE_SUBCLASS(DECLARE_AS)
|
||||||
|
|
||||||
void checkAndSetIsDirectRHSAnonFunction(Node pn) {}
|
void checkAndSetIsDirectRHSAnonFunction(Node pn) {}
|
||||||
|
|
||||||
FunctionNodeType newFunctionStatement() { return NodeFunctionDefinition; }
|
FunctionNodeType newFunction(FunctionSyntaxKind syntaxKind) { return NodeFunctionDefinition; }
|
||||||
FunctionNodeType newFunctionExpression() { return NodeFunctionDefinition; }
|
|
||||||
FunctionNodeType newArrowFunction() { return NodeFunctionDefinition; }
|
|
||||||
|
|
||||||
bool setComprehensionLambdaBody(FunctionNodeType funNode, ListNodeType body) { return true; }
|
bool setComprehensionLambdaBody(FunctionNodeType funNode, ListNodeType body) { return true; }
|
||||||
void setFunctionFormalParametersAndBody(FunctionNodeType funNode, ListNodeType paramsBody) {}
|
void setFunctionFormalParametersAndBody(FunctionNodeType funNode, ListNodeType paramsBody) {}
|
||||||
|
|
|
||||||
|
|
@ -7044,7 +7044,8 @@ ParseFunction(ModuleValidator& m, FunctionNode** funNodeOut, unsigned* line)
|
||||||
if (!name)
|
if (!name)
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
FunctionNode* funNode = m.parser().handler.newFunctionStatement();
|
FunctionSyntaxKind syntaxKind = FunctionSyntaxKind::Statement;
|
||||||
|
FunctionNode* funNode = m.parser().handler.newFunction(syntaxKind);
|
||||||
if (!funNode)
|
if (!funNode)
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
|
|
@ -7058,14 +7059,14 @@ ParseFunction(ModuleValidator& m, FunctionNode** funNodeOut, unsigned* line)
|
||||||
SyncFunction, /* tryAnnexB = */ false);
|
SyncFunction, /* tryAnnexB = */ false);
|
||||||
if (!funbox)
|
if (!funbox)
|
||||||
return false;
|
return false;
|
||||||
funbox->initWithEnclosingParseContext(outerpc, frontend::Statement);
|
funbox->initWithEnclosingParseContext(outerpc, syntaxKind);
|
||||||
|
|
||||||
Directives newDirectives = directives;
|
Directives newDirectives = directives;
|
||||||
ParseContext funpc(&m.parser(), funbox, &newDirectives);
|
ParseContext funpc(&m.parser(), funbox, &newDirectives);
|
||||||
if (!funpc.init())
|
if (!funpc.init())
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
if (!m.parser().functionFormalParametersAndBody(InAllowed, YieldIsName, funNode, Statement)) {
|
if (!m.parser().functionFormalParametersAndBody(InAllowed, YieldIsName, funNode, syntaxKind)) {
|
||||||
if (tokenStream.hadError() || directives == newDirectives)
|
if (tokenStream.hadError() || directives == newDirectives)
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue