ParseNode * ParseNode::append(ParseNodeKind kind, JSOp op, ParseNode *left, ParseNode *right, FullParseHandler *handler) { if (!left || !right) return nullptr; MOZ_ASSERT(left->isKind(kind) && left->isOp(op) && (js_CodeSpec[op].format & JOF_LEFTASSOC)); ListNode *list; if (left->pn_arity == PN_LIST) { list = &left->as<ListNode>(); } else { ParseNode *pn1 = left->pn_left, *pn2 = left->pn_right; list = handler->new_<ListNode>(kind, op, pn1); if (!list) return nullptr; list->append(pn2); } list->append(right); list->pn_pos.end = right->pn_pos.end; return list; }
ParseNode* ParseNode::appendOrCreateList(ParseNodeKind kind, JSOp op, ParseNode* left, ParseNode* right, FullParseHandler* handler, ParseContext<FullParseHandler>* pc) { // The asm.js specification is written in ECMAScript grammar terms that // specify *only* a binary tree. It's a royal pain to implement the asm.js // spec to act upon n-ary lists as created below. So for asm.js, form a // binary tree of lists exactly as ECMAScript would by skipping the // following optimization. if (!pc->useAsmOrInsideUseAsm()) { // Left-associative trees of a given operator (e.g. |a + b + c|) are // binary trees in the spec: (+ (+ a b) c) in Lisp terms. Recursively // processing such a tree, exactly implemented that way, would blow the // the stack. We use a list node that uses O(1) stack to represent // such operations: (+ a b c). if (left->isKind(kind) && left->isOp(op) && (js_CodeSpec[op].format & JOF_LEFTASSOC)) { ListNode* list = &left->as<ListNode>(); list->append(right); list->pn_pos.end = right->pn_pos.end; return list; } } ParseNode* list = handler->new_<ListNode>(kind, op, left); if (!list) return nullptr; list->append(right); return list; }
ParseNode * ParseNode::append(ParseNodeKind kind, JSOp op, ParseNode *left, ParseNode *right, FullParseHandler *handler) { if (!left || !right) return NULL; JS_ASSERT(left->isKind(kind) && left->isOp(op) && (js_CodeSpec[op].format & JOF_LEFTASSOC)); ListNode *list; if (left->pn_arity == PN_LIST) { list = &left->as<ListNode>(); } else { ParseNode *pn1 = left->pn_left, *pn2 = left->pn_right; list = handler->new_<ListNode>(kind, op, pn1); if (!list) return NULL; list->append(pn2); if (kind == PNK_ADD) { if (pn1->isKind(PNK_STRING)) list->pn_xflags |= PNX_STRCAT; else if (!pn1->isKind(PNK_NUMBER)) list->pn_xflags |= PNX_CANTFOLD; if (pn2->isKind(PNK_STRING)) list->pn_xflags |= PNX_STRCAT; else if (!pn2->isKind(PNK_NUMBER)) list->pn_xflags |= PNX_CANTFOLD; } } list->append(right); list->pn_pos.end = right->pn_pos.end; if (kind == PNK_ADD) { if (right->isKind(PNK_STRING)) list->pn_xflags |= PNX_STRCAT; else if (!right->isKind(PNK_NUMBER)) list->pn_xflags |= PNX_CANTFOLD; } return list; }
int BuiltinCallNode::foldOps(VSLDef *cdef, VSLNode** node) { assert (this == *node); int changes = 0; // Apply on all arguments changes += CallNode::foldOps(cdef, node); // If non-associative, return if (!VSLBuiltin::isAssoc(_index)) return changes; // If arg is not a list, return if (!arg()->isListNode()) return changes; ListNode *args = (ListNode *)arg(); // dirty trick // First arg must be a builtin call if (!args->head()->isBuiltinCallNode()) return changes; BuiltinCallNode *callee = (BuiltinCallNode *)args->head(); // dirty trick // First arg must call the same function if (_index != callee->_index) return changes; // Arg must be a list if (!callee->arg()->isListNode()) return changes; ListNode *callArgs = (ListNode *)callee->arg(); // dirty trick // Insert list if (VSEFlags::show_optimize) { std::cout << "\n" << cdef->longname() << ": foldOps: replacing\n" << *this << '\n'; std::cout.flush(); } int err = callArgs->append(args->tail()); if (err) { if (VSEFlags::show_optimize) { std::cout << "ABORTING (no replace) since append impossible\n"; std::cout.flush(); } return changes; } VSLNode *newArgs = callee->arg(); callee->arg() = 0; args->tail() = 0; delete args; arg() = newArgs; if (VSEFlags::show_optimize) { std::cout << "by " << *this << '\n'; std::cout.flush(); } changes++; return changes; }