Permalink
Browse files

Bug 1405943 - Part 1: Implement Pipeline Operator |>. r=arai

  • Loading branch information...
karszawa committed Oct 17, 2017
1 parent c974f70 commit 24b109a6bc192cddba390e8ac4ce337bc9977a8b
View
@@ -254,3 +254,15 @@ with only_when('--enable-compile-environment'):
set_config('LIBFUZZER', enable_libfuzzer)
set_define('LIBFUZZER', enable_libfuzzer)
# Enable pipeline operator
# ===================================================
js_option('--enable-pipeline-operator', default=False, help='Enable pipeline operator')
@depends('--enable-pipeline-operator')
def enable_pipeline_operator(value):
if value:
return True
set_config('ENABLE_PIPELINE_OPERATOR', enable_pipeline_operator)
set_define('ENABLE_PIPELINE_OPERATOR', enable_pipeline_operator)
@@ -78,7 +78,7 @@ enum BinaryOperator {
/* binary */
BINOP_BITOR, BINOP_BITXOR, BINOP_BITAND,
/* misc */
BINOP_IN, BINOP_INSTANCEOF,
BINOP_IN, BINOP_INSTANCEOF, BINOP_PIPELINE,
BINOP_LIMIT
};
@@ -154,6 +154,7 @@ static const char* const binopNames[] = {
"&", /* BINOP_BITAND */
"in", /* BINOP_IN */
"instanceof", /* BINOP_INSTANCEOF */
"|>", /* BINOP_PIPELINE */
};
static const char* const unopNames[] = {
@@ -1952,6 +1953,8 @@ ASTSerializer::binop(ParseNodeKind kind)
return BINOP_IN;
case PNK_INSTANCEOF:
return BINOP_INSTANCEOF;
case PNK_PIPELINE:
return BINOP_PIPELINE;
default:
return BINOP_ERR;
}
@@ -2895,6 +2898,7 @@ ASTSerializer::expression(ParseNode* pn, MutableHandleValue dst)
builder.assignmentExpression(op, lhs, rhs, &pn->pn_pos, dst);
}
case PNK_PIPELINE:
case PNK_ADD:
case PNK_SUB:
case PNK_STRICTEQ:
@@ -3387,6 +3387,12 @@ BytecodeEmitter::checkSideEffects(ParseNode* pn, bool* answer)
*answer = true;
return true;
case PNK_PIPELINE:
MOZ_ASSERT(pn->isArity(PN_LIST));
MOZ_ASSERT(pn->pn_count >= 2);
*answer = true;
return true;
// Classes typically introduce names. Even if no name is introduced,
// the heritage and/or class body (through computed property names)
// usually have effects.
@@ -9467,80 +9473,33 @@ BytecodeEmitter::isRestParameter(ParseNode* pn)
}
bool
BytecodeEmitter::emitCallOrNew(ParseNode* pn, ValueUsage valueUsage /* = ValueUsage::WantValue */)
BytecodeEmitter::emitCallee(ParseNode* callee, ParseNode* call, bool spread, bool* callop)
{
bool callop = pn->isKind(PNK_CALL) || pn->isKind(PNK_TAGGED_TEMPLATE);
/*
* Emit callable invocation or operator new (constructor call) code.
* First, emit code for the left operand to evaluate the callable or
* constructable object expression.
*
* For operator new, we emit JSOP_GETPROP instead of JSOP_CALLPROP, etc.
* This is necessary to interpose the lambda-initialized method read
* barrier -- see the code in jsinterp.cpp for JSOP_LAMBDA followed by
* JSOP_{SET,INIT}PROP.
*
* Then (or in a call case that has no explicit reference-base
* object) we emit JSOP_UNDEFINED to produce the undefined |this|
* value required for calls (which non-strict mode functions
* will box into the global object).
*/
uint32_t argc = pn->pn_count - 1;
if (argc >= ARGC_LIMIT) {
parser.reportError(callop ? JSMSG_TOO_MANY_FUN_ARGS : JSMSG_TOO_MANY_CON_ARGS);
return false;
}
ParseNode* pn2 = pn->pn_head;
bool spread = JOF_OPTYPE(pn->getOp()) == JOF_BYTE;
switch (pn2->getKind()) {
switch (callee->getKind()) {
case PNK_NAME:
if (emitterMode == BytecodeEmitter::SelfHosting && !spread) {
// Calls to "forceInterpreter", "callFunction",
// "callContentFunction", or "resumeGenerator" in self-hosted
// code generate inline bytecode.
if (pn2->name() == cx->names().callFunction ||
pn2->name() == cx->names().callContentFunction ||
pn2->name() == cx->names().constructContentFunction)
{
return emitSelfHostedCallFunction(pn);
}
if (pn2->name() == cx->names().resumeGenerator)
return emitSelfHostedResumeGenerator(pn);
if (pn2->name() == cx->names().forceInterpreter)
return emitSelfHostedForceInterpreter(pn);
if (pn2->name() == cx->names().allowContentIter)
return emitSelfHostedAllowContentIter(pn);
if (pn2->name() == cx->names().defineDataPropertyIntrinsic && pn->pn_count == 4)
return emitSelfHostedDefineDataProperty(pn);
if (pn2->name() == cx->names().hasOwn)
return emitSelfHostedHasOwn(pn);
// Fall through.
}
if (!emitGetName(pn2, callop))
if (!emitGetName(callee, *callop))
return false;
break;
case PNK_DOT:
MOZ_ASSERT(emitterMode != BytecodeEmitter::SelfHosting);
if (pn2->as<PropertyAccess>().isSuper()) {
if (!emitSuperPropOp(pn2, JSOP_GETPROP_SUPER, /* isCall = */ callop))
if (callee->as<PropertyAccess>().isSuper()) {
if (!emitSuperPropOp(callee, JSOP_GETPROP_SUPER, /* isCall = */ *callop))
return false;
} else {
if (!emitPropOp(pn2, callop ? JSOP_CALLPROP : JSOP_GETPROP))
if (!emitPropOp(callee, *callop ? JSOP_CALLPROP : JSOP_GETPROP))
return false;
}
break;
case PNK_ELEM:
MOZ_ASSERT(emitterMode != BytecodeEmitter::SelfHosting);
if (pn2->as<PropertyByValue>().isSuper()) {
if (!emitSuperElemOp(pn2, JSOP_GETELEM_SUPER, /* isCall = */ callop))
if (callee->as<PropertyByValue>().isSuper()) {
if (!emitSuperElemOp(callee, JSOP_GETELEM_SUPER, /* isCall = */ *callop))
return false;
} else {
if (!emitElemOp(pn2, callop ? JSOP_CALLELEM : JSOP_GETELEM))
if (!emitElemOp(callee, *callop ? JSOP_CALLELEM : JSOP_GETELEM))
return false;
if (callop) {
if (*callop) {
if (!emit1(JSOP_SWAP))
return false;
}
@@ -9561,28 +9520,120 @@ BytecodeEmitter::emitCallOrNew(ParseNode* pn, ValueUsage valueUsage /* = ValueUs
MOZ_ASSERT(!emittingRunOnceLambda);
if (checkRunOnceContext()) {
emittingRunOnceLambda = true;
if (!emitTree(pn2))
if (!emitTree(callee))
return false;
emittingRunOnceLambda = false;
} else {
if (!emitTree(pn2))
if (!emitTree(callee))
return false;
}
callop = false;
*callop = false;
break;
case PNK_SUPERBASE:
MOZ_ASSERT(pn->isKind(PNK_SUPERCALL));
MOZ_ASSERT(parser.isSuperBase(pn2));
MOZ_ASSERT(call->isKind(PNK_SUPERCALL));
MOZ_ASSERT(parser.isSuperBase(callee));
if (!emit1(JSOP_SUPERFUN))
return false;
break;
default:
if (!emitTree(pn2))
if (!emitTree(callee))
return false;
callop = false; /* trigger JSOP_UNDEFINED after */
*callop = false; /* trigger JSOP_UNDEFINED after */
break;
}
return true;
}
bool
BytecodeEmitter::emitPipeline(ParseNode* pn)
{
MOZ_ASSERT(pn->isArity(PN_LIST));
MOZ_ASSERT(pn->pn_count >= 2);
if (!emitTree(pn->pn_head))
return false;
ParseNode* callee = pn->pn_head->pn_next;
do {
bool callop = true;
if (!emitCallee(callee, pn, false, &callop))
return false;
// Emit room for |this|
if (!callop) {
if (!emit1(JSOP_UNDEFINED))
return false;
}
if (!emit2(JSOP_PICK, 2))
return false;
if (!emitCall(JSOP_CALL, 1, pn))
return false;
checkTypeSet(JSOP_CALL);
} while ((callee = callee->pn_next));
return true;
}
bool
BytecodeEmitter::emitCallOrNew(ParseNode* pn, ValueUsage valueUsage /* = ValueUsage::WantValue */)
{
bool callop = pn->isKind(PNK_CALL) || pn->isKind(PNK_TAGGED_TEMPLATE);
/*
* Emit callable invocation or operator new (constructor call) code.
* First, emit code for the left operand to evaluate the callable or
* constructable object expression.
*
* For operator new, we emit JSOP_GETPROP instead of JSOP_CALLPROP, etc.
* This is necessary to interpose the lambda-initialized method read
* barrier -- see the code in jsinterp.cpp for JSOP_LAMBDA followed by
* JSOP_{SET,INIT}PROP.
*
* Then (or in a call case that has no explicit reference-base
* object) we emit JSOP_UNDEFINED to produce the undefined |this|
* value required for calls (which non-strict mode functions
* will box into the global object).
*/
uint32_t argc = pn->pn_count - 1;
if (argc >= ARGC_LIMIT) {
parser.reportError(callop ? JSMSG_TOO_MANY_FUN_ARGS : JSMSG_TOO_MANY_CON_ARGS);
return false;
}
ParseNode* pn2 = pn->pn_head;
bool spread = JOF_OPTYPE(pn->getOp()) == JOF_BYTE;
if (pn2->isKind(PNK_NAME) && emitterMode == BytecodeEmitter::SelfHosting && !spread) {
// Calls to "forceInterpreter", "callFunction",
// "callContentFunction", or "resumeGenerator" in self-hosted
// code generate inline bytecode.
if (pn2->name() == cx->names().callFunction ||
pn2->name() == cx->names().callContentFunction ||
pn2->name() == cx->names().constructContentFunction)
{
return emitSelfHostedCallFunction(pn);
}
if (pn2->name() == cx->names().resumeGenerator)
return emitSelfHostedResumeGenerator(pn);
if (pn2->name() == cx->names().forceInterpreter)
return emitSelfHostedForceInterpreter(pn);
if (pn2->name() == cx->names().allowContentIter)
return emitSelfHostedAllowContentIter(pn);
if (pn2->name() == cx->names().defineDataPropertyIntrinsic && pn->pn_count == 4)
return emitSelfHostedDefineDataProperty(pn);
if (pn2->name() == cx->names().hasOwn)
return emitSelfHostedHasOwn(pn);
// Fall through
}
if (!emitCallee(pn2, pn, spread, &callop))
return false;
bool isNewOp = pn->getOp() == JSOP_NEW || pn->getOp() == JSOP_SPREADNEW ||
pn->getOp() == JSOP_SUPERCALL || pn->getOp() == JSOP_SPREADSUPERCALL;
@@ -9701,6 +9752,9 @@ BytecodeEmitter::emitCallOrNew(ParseNode* pn, ValueUsage valueUsage /* = ValueUs
}
static const JSOp ParseNodeKindToJSOp[] = {
// JSOP_NOP is for pipeline operator which does not emit its own JSOp
// but has highest precedence in binary operators
JSOP_NOP,
JSOP_OR,
JSOP_AND,
JSOP_BITOR,
@@ -11087,6 +11141,11 @@ BytecodeEmitter::emitTree(ParseNode* pn, ValueUsage valueUsage /* = ValueUsage::
return false;
break;
case PNK_PIPELINE:
if (!emitPipeline(pn))
return false;
break;
case PNK_TYPEOFNAME:
if (!emitTypeof(pn, JSOP_TYPEOF))
return false;
@@ -835,6 +835,10 @@ struct MOZ_STACK_CLASS BytecodeEmitter
MOZ_MUST_USE bool emitSuperElemOperands(ParseNode* pn,
EmitElemOption opts = EmitElemOption::Get);
MOZ_MUST_USE bool emitSuperElemOp(ParseNode* pn, JSOp op, bool isCall = false);
MOZ_MUST_USE bool emitCallee(ParseNode* callee, ParseNode* call, bool spread, bool* callop);
MOZ_MUST_USE bool emitPipeline(ParseNode* pn);
};
} /* namespace frontend */
@@ -405,6 +405,11 @@ ContainsHoistedDeclaration(JSContext* cx, ParseNode* node, bool* result)
MOZ_CRASH("ContainsHoistedDeclaration should have indicated false on "
"some parent node without recurring to test this node");
case PNK_PIPELINE:
MOZ_ASSERT(node->isArity(PN_LIST));
*result = false;
return true;
case PNK_LIMIT: // invalid sentinel value
MOZ_CRASH("unexpected PNK_LIMIT in node");
}
@@ -1724,6 +1729,9 @@ Fold(JSContext* cx, ParseNode** pnp, Parser<FullParseHandler, char16_t>& parser,
return Fold(cx, &expr, parser, inGenexpLambda);
return true;
case PNK_PIPELINE:
return true;
case PNK_AND:
case PNK_OR:
return FoldAndOr(cx, pnp, parser, inGenexpLambda);
@@ -687,6 +687,7 @@ class NameResolver
case PNK_DIV:
case PNK_MOD:
case PNK_POW:
case PNK_PIPELINE:
case PNK_COMMA:
case PNK_NEW:
case PNK_CALL:
@@ -447,6 +447,7 @@ PushNodeChildren(ParseNode* pn, NodeStack* stack)
case PNK_DIV:
case PNK_MOD:
case PNK_POW:
case PNK_PIPELINE:
case PNK_COMMA:
case PNK_NEW:
case PNK_CALL:
@@ -129,6 +129,7 @@ class ObjectBox;
* Binary operators. \
* These must be in the same order as TOK_OR and friends in TokenStream.h. \
*/ \
F(PIPELINE) \
F(OR) \
F(AND) \
F(BITOR) \
@@ -186,7 +187,7 @@ enum ParseNodeKind : uint16_t
FOR_EACH_PARSE_NODE_KIND(EMIT_ENUM)
#undef EMIT_ENUM
PNK_LIMIT, /* domain size */
PNK_BINOP_FIRST = PNK_OR,
PNK_BINOP_FIRST = PNK_PIPELINE,
PNK_BINOP_LAST = PNK_POW,
PNK_ASSIGNMENT_START = PNK_ASSIGN,
PNK_ASSIGNMENT_LAST = PNK_POWASSIGN
Oops, something went wrong.

0 comments on commit 24b109a

Please sign in to comment.