/* * This function assumes the cloned tree is for use in the same statement and * binding context as the original tree. */ static ParseNode * CloneParseTree(ParseNode *opn, Parser *parser) { TreeContext *tc = parser->tc; JS_CHECK_RECURSION(tc->sc->context, return NULL); ParseNode *pn = parser->new_<ParseNode>(opn->getKind(), opn->getOp(), opn->getArity(), opn->pn_pos); if (!pn) return NULL; pn->setInParens(opn->isInParens()); pn->setDefn(opn->isDefn()); pn->setUsed(opn->isUsed()); switch (pn->getArity()) { #define NULLCHECK(e) JS_BEGIN_MACRO if (!(e)) return NULL; JS_END_MACRO case PN_FUNC: NULLCHECK(pn->pn_funbox = parser->newFunctionBox(opn->pn_funbox->object, pn, tc, opn->pn_funbox->strictModeState)); NULLCHECK(pn->pn_body = CloneParseTree(opn->pn_body, parser)); pn->pn_cookie = opn->pn_cookie; pn->pn_dflags = opn->pn_dflags; pn->pn_blockid = opn->pn_blockid; break; case PN_LIST: pn->makeEmpty(); for (ParseNode *opn2 = opn->pn_head; opn2; opn2 = opn2->pn_next) { ParseNode *pn2; NULLCHECK(pn2 = CloneParseTree(opn2, parser)); pn->append(pn2); } pn->pn_xflags = opn->pn_xflags; break; case PN_TERNARY: NULLCHECK(pn->pn_kid1 = CloneParseTree(opn->pn_kid1, parser)); NULLCHECK(pn->pn_kid2 = CloneParseTree(opn->pn_kid2, parser)); NULLCHECK(pn->pn_kid3 = CloneParseTree(opn->pn_kid3, parser)); break; case PN_BINARY: NULLCHECK(pn->pn_left = CloneParseTree(opn->pn_left, parser)); if (opn->pn_right != opn->pn_left) NULLCHECK(pn->pn_right = CloneParseTree(opn->pn_right, parser)); else pn->pn_right = pn->pn_left; pn->pn_pval = opn->pn_pval; pn->pn_iflags = opn->pn_iflags; break; case PN_UNARY: NULLCHECK(pn->pn_kid = CloneParseTree(opn->pn_kid, parser)); pn->pn_hidden = opn->pn_hidden; break; case PN_NAME: // PN_NAME could mean several arms in pn_u, so copy the whole thing. pn->pn_u = opn->pn_u; if (opn->isUsed()) { /* * The old name is a use of its pn_lexdef. Make the clone also be a * use of that definition. */ Definition *dn = pn->pn_lexdef; pn->pn_link = dn->dn_uses; dn->dn_uses = pn; } else if (opn->pn_expr) { NULLCHECK(pn->pn_expr = CloneParseTree(opn->pn_expr, parser)); /* * If the old name is a definition, the new one has pn_defn set. * Make the old name a use of the new node. */ if (opn->isDefn()) { opn->setDefn(false); LinkUseToDef(opn, (Definition *) pn); } } break; case PN_NAMESET: pn->pn_names = opn->pn_names; NULLCHECK(pn->pn_tree = CloneParseTree(opn->pn_tree, parser)); break; case PN_NULLARY: // Even PN_NULLARY may have data (xmlpi for E4X -- what a botch). pn->pn_u = opn->pn_u; break; #undef NULLCHECK } return pn; }
// pre: root != NULL void doParse(ParseNode *root, std::string inFileName, std::string outBaseName) { std::map<std::string, std::string> firstManualToken; FaSymbolTable symTab; FaRuleIr ruleIr(&symTab); // Iterate thru children of start symbol. for(ParseNode::iterator_type i = root->begin(); i != root->end(); ++i) { ParseNode *statement = *i; ParseNode *statementHead = *statement->begin(); std::string statementHeadText = statementHead->getTokenText(); if(statementHeadText.empty()) { // name "::=" alternate { '|' alternate } ';' // name ::= IDENT | "start" ; ///std::cout << "[name]" << std::endl; std::string ruleName = (*statementHead->begin())->getTokenText(); ParseNode::iterator_type j = statement->begin(); ++j; // skip ruleName ++j; // skip "::=" for(;;) { ParseNode *alternate = *j; parseAlternate(symTab, ruleIr, alternate, ruleName); ++j; // skip alternate if((*j)->getTokenText() != "|") break; // found ';' at end ++j; // skip '|' } } else if(statementHeadText == "token") { for(ParseNode::iterator_type j = ++statement->begin(); j != statement->end(); ++j) { std::string s = (*j)->getTokenText(); if(s == ";") break; ruleIr.addToken(s); } } else if(statementHeadText == "keyword") { std::string s1 = (*(++statement->begin()))->getTokenText(); std::string s2 = (*(++(++statement->begin())))->getTokenText(); ruleIr.addKeyword(s1, s2); } else if(statementHeadText == "copyright") { ruleIr.setCopyright(unquote((*(++statement->begin()))->getTokenText())); } else if(statementHeadText == "prefix") { ruleIr.setPrefix(unquote((*(++statement->begin()))->getTokenText())); } else if(statementHeadText == "manual") { ParseNode::iterator_type j = ++statement->begin(); std::string s0 = (*j)->getTokenText(); ++j; std::string s1 = unquote((*j)->getTokenText()); ++j; ruleIr.addToken(s0); firstManualToken[s0] = s1; if(s1.empty() || (s1.size() & 1) != 0) { std::string msg = "invalid manual token definition (both string literals must be nonempty and even in length)"; throw ParseSpecificError((*i)->getLine(), (*i)->getFile(), msg); } } } std::string err = ruleIr.convertGrammar(); if(!err.empty()) { std::cerr << "error: " << inFileName << ": " << err << std::endl; throw ParseError(); } std::string fn1 = "out_" + outBaseName + "_parser.h"; std::string fn2 = "out_" + outBaseName + "_header.h"; std::string fn3 = "out_" + outBaseName + "_lexer.h"; std::ofstream fo1(fn1.c_str()); ruleIr.writeParser(fo1, fn1); std::ofstream fo2(fn2.c_str()); ruleIr.writeHeader(fo2, fn2); std::ofstream fo3(fn3.c_str()); ruleIr.writeLexer(fo3, fn3, firstManualToken); }
/* * Mark as funargs any functions that reach up to one or more upvars across an * already-known funarg. The parser will flag the o_m lambda as a funarg in: * * function f(o, p) { * o.m = function o_m(a) { * function g() { return p; } * function h() { return a; } * return g() + h(); * } * } * * but without this extra marking phase, function g will not be marked as a * funarg since it is called from within its parent scope. But g reaches up to * f's parameter p, so if o_m escapes f's activation scope, g does too and * cannot assume that p's stack slot is still alive. In contast function h * neither escapes nor uses an upvar "above" o_m's level. * * If function g itself contained lambdas that contained non-lambdas that reach * up above its level, then those non-lambdas would have to be marked too. This * process is potentially exponential in the number of functions, but generally * not so complex. But it can't be done during a single recursive traversal of * the funbox tree, so we must use a work queue. * * Return the minimal "skipmin" for funbox and its siblings. This is the delta * between the static level of the bodies of funbox and its peers (which must * be funbox->level + 1), and the static level of the nearest upvar among all * the upvars contained by funbox and its peers. If there are no upvars, return * FREE_STATIC_LEVEL. Thus this function never returns 0. */ static uintN FindFunArgs(FunctionBox *funbox, int level, FunctionBoxQueue *queue) { uintN allskipmin = UpvarCookie::FREE_LEVEL; do { ParseNode *fn = funbox->node; JS_ASSERT(fn->isArity(PN_FUNC)); int fnlevel = level; /* * An eval can leak funbox, functions along its ancestor line, and its * immediate kids. Since FindFunArgs uses DFS and the parser propagates * TCF_FUN_HEAVYWEIGHT bottom up, funbox's ancestor function nodes have * already been marked as funargs by this point. Therefore we have to * flag only funbox->node and funbox->kids' nodes here. * * Generators need to be treated in the same way. Even if the value * of a generator function doesn't escape, anything defined or referred * to inside the generator can escape through a call to the generator. * We could imagine doing static analysis to track the calls and see * if any iterators or values returned by iterators escape, but that * would be hard, so instead we just assume everything might escape. */ if (funbox->tcflags & (TCF_FUN_HEAVYWEIGHT | TCF_FUN_IS_GENERATOR)) { fn->setFunArg(); for (FunctionBox *kid = funbox->kids; kid; kid = kid->siblings) kid->node->setFunArg(); } /* * Compute in skipmin the least distance from fun's static level up to * an upvar, whether used directly by fun, or indirectly by a function * nested in fun. */ uintN skipmin = UpvarCookie::FREE_LEVEL; ParseNode *pn = fn->pn_body; if (pn->isKind(PNK_UPVARS)) { AtomDefnMapPtr &upvars = pn->pn_names; JS_ASSERT(upvars->count() != 0); for (AtomDefnRange r = upvars->all(); !r.empty(); r.popFront()) { Definition *defn = r.front().value(); Definition *lexdep = defn->resolve(); if (!lexdep->isFreeVar()) { uintN upvarLevel = lexdep->frameLevel(); if (int(upvarLevel) <= fnlevel) fn->setFunArg(); uintN skip = (funbox->level + 1) - upvarLevel; if (skip < skipmin) skipmin = skip; } } } /* * If this function escapes, whether directly (the parser detects such * escapes) or indirectly (because this non-escaping function uses an * upvar that reaches across an outer function boundary where the outer * function escapes), enqueue it for further analysis, and bump fnlevel * to trap any non-escaping children. */ if (fn->isFunArg()) { queue->push(funbox); fnlevel = int(funbox->level); } /* * Now process the current function's children, and recalibrate their * cumulative skipmin to be relative to the current static level. */ if (funbox->kids) { uintN kidskipmin = FindFunArgs(funbox->kids, fnlevel, queue); JS_ASSERT(kidskipmin != 0); if (kidskipmin != UpvarCookie::FREE_LEVEL) { --kidskipmin; if (kidskipmin != 0 && kidskipmin < skipmin) skipmin = kidskipmin; } } /* * Finally, after we've traversed all of the current function's kids, * minimize allskipmin against our accumulated skipmin. Minimize across * funbox and all of its siblings, to compute our return value. */ if (skipmin != UpvarCookie::FREE_LEVEL) { if (skipmin < allskipmin) allskipmin = skipmin; } } while ((funbox = funbox->siblings) != NULL); return allskipmin; }
// Compile a JS function body, which might appear as the value of an event // handler attribute in an HTML <INPUT> tag, or in a Function() constructor. bool frontend::CompileFunctionBody(JSContext *cx, MutableHandleFunction fun, CompileOptions options, const AutoNameVector &formals, const jschar *chars, size_t length, bool isAsmJSRecompile) { if (!CheckLength(cx, length)) return false; ScriptSource *ss = cx->new_<ScriptSource>(); if (!ss) return false; if (options.filename && !ss->setFilename(cx, options.filename)) return false; ScriptSourceHolder ssh(ss); SourceCompressionToken sct(cx); JS_ASSERT(options.sourcePolicy != CompileOptions::LAZY_SOURCE); if (options.sourcePolicy == CompileOptions::SAVE_SOURCE) { if (!ss->setSourceCopy(cx, chars, length, true, &sct)) return false; } options.setCompileAndGo(false); Parser<FullParseHandler> parser(cx, options, chars, length, /* foldConstants = */ true); if (!parser.init()) return false; parser.sct = &sct; JS_ASSERT(fun); fun->setArgCount(formals.length()); /* FIXME: make Function format the source for a function definition. */ ParseNode *fn = CodeNode::create(PNK_FUNCTION, &parser.handler); if (!fn) return false; fn->pn_body = NULL; fn->pn_funbox = NULL; fn->pn_cookie.makeFree(); ParseNode *argsbody = ListNode::create(PNK_ARGSBODY, &parser.handler); if (!argsbody) return false; argsbody->setOp(JSOP_NOP); argsbody->makeEmpty(); fn->pn_body = argsbody; Rooted<JSScript*> script(cx, JSScript::Create(cx, NullPtr(), false, options, /* staticLevel = */ 0, ss, /* sourceStart = */ 0, length)); if (!script) return false; // If the context is strict, immediately parse the body in strict // mode. Otherwise, we parse it normally. If we see a "use strict" // directive, we backup and reparse it as strict. TokenStream::Position start; parser.tokenStream.tell(&start); bool initiallyStrict = StrictModeFromContext(cx); bool becameStrict; FunctionBox *funbox; ParseNode *pn = parser.standaloneFunctionBody(fun, formals, script, fn, &funbox, initiallyStrict, &becameStrict); if (!pn) { if (initiallyStrict || !becameStrict || parser.tokenStream.hadError()) return false; // Reparse in strict mode. parser.tokenStream.seek(start); pn = parser.standaloneFunctionBody(fun, formals, script, fn, &funbox, /* strict = */ true); if (!pn) return false; } if (!NameFunctions(cx, pn)) return false; if (fn->pn_body) { JS_ASSERT(fn->pn_body->isKind(PNK_ARGSBODY)); fn->pn_body->append(pn); fn->pn_body->pn_pos = pn->pn_pos; pn = fn->pn_body; } bool generateBytecode = true; #ifdef JS_ION JS_ASSERT_IF(isAsmJSRecompile, fn->pn_funbox->useAsm); if (fn->pn_funbox->useAsm && !isAsmJSRecompile) { RootedFunction moduleFun(cx); if (!CompileAsmJS(cx, parser.tokenStream, fn, options, ss, /* bufStart = */ 0, /* bufEnd = */ length, &moduleFun)) return false; if (moduleFun) { funbox->object = moduleFun; fun.set(moduleFun); // replace the existing function with the LinkAsmJS native generateBytecode = false; } } #endif if (generateBytecode) { BytecodeEmitter funbce(/* parent = */ NULL, &parser, funbox, script, /* evalCaller = */ NullPtr(), /* hasGlobalScope = */ false, options.lineno); if (!funbce.init()) return false; if (!EmitFunctionScript(cx, &funbce, pn)) return false; } if (!SetSourceMap(cx, parser.tokenStream, ss, script)) return false; if (!sct.complete()) return false; return true; }
UnrootedScript frontend::CompileScript(JSContext *cx, HandleObject scopeChain, StackFrame *callerFrame, const CompileOptions &options, StableCharPtr chars, size_t length, JSString *source_ /* = NULL */, unsigned staticLevel /* = 0 */) { RootedString source(cx, source_); class ProbesManager { const char* filename; unsigned lineno; public: ProbesManager(const char *f, unsigned l) : filename(f), lineno(l) { Probes::compileScriptBegin(filename, lineno); } ~ProbesManager() { Probes::compileScriptEnd(filename, lineno); } }; ProbesManager probesManager(options.filename, options.lineno); /* * The scripted callerFrame can only be given for compile-and-go scripts * and non-zero static level requires callerFrame. */ JS_ASSERT_IF(callerFrame, options.compileAndGo); JS_ASSERT_IF(staticLevel != 0, callerFrame); if (!CheckLength(cx, length)) return UnrootedScript(NULL); JS_ASSERT_IF(staticLevel != 0, options.sourcePolicy != CompileOptions::LAZY_SOURCE); ScriptSource *ss = cx->new_<ScriptSource>(); if (!ss) return UnrootedScript(NULL); ScriptSourceHolder ssh(cx->runtime, ss); SourceCompressionToken sct(cx); switch (options.sourcePolicy) { case CompileOptions::SAVE_SOURCE: if (!ss->setSourceCopy(cx, chars, length, false, &sct)) return UnrootedScript(NULL); break; case CompileOptions::LAZY_SOURCE: ss->setSourceRetrievable(); break; case CompileOptions::NO_SOURCE: break; } Parser parser(cx, options, chars, length, /* foldConstants = */ true); if (!parser.init()) return UnrootedScript(NULL); parser.sct = &sct; GlobalSharedContext globalsc(cx, scopeChain, StrictModeFromContext(cx)); ParseContext pc(&parser, &globalsc, staticLevel, /* bodyid = */ 0); if (!pc.init()) return UnrootedScript(NULL); bool savedCallerFun = options.compileAndGo && callerFrame && callerFrame->isFunctionFrame(); Rooted<JSScript*> script(cx, JSScript::Create(cx, NullPtr(), savedCallerFun, options, staticLevel, ss, 0, length)); if (!script) return UnrootedScript(NULL); // Global/eval script bindings are always empty (all names are added to the // scope dynamically via JSOP_DEFFUN/VAR). InternalHandle<Bindings*> bindings(script, &script->bindings); if (!Bindings::initWithTemporaryStorage(cx, bindings, 0, 0, NULL)) return UnrootedScript(NULL); // We can specialize a bit for the given scope chain if that scope chain is the global object. JSObject *globalScope = scopeChain && scopeChain == &scopeChain->global() ? (JSObject*) scopeChain : NULL; JS_ASSERT_IF(globalScope, globalScope->isNative()); JS_ASSERT_IF(globalScope, JSCLASS_HAS_GLOBAL_FLAG_AND_SLOTS(globalScope->getClass())); BytecodeEmitter bce(/* parent = */ NULL, &parser, &globalsc, script, callerFrame, !!globalScope, options.lineno, options.selfHostingMode); if (!bce.init()) return UnrootedScript(NULL); /* If this is a direct call to eval, inherit the caller's strictness. */ if (callerFrame && callerFrame->script()->strict) globalsc.strict = true; if (options.compileAndGo) { if (source) { /* * Save eval program source in script->atoms[0] for the * eval cache (see EvalCacheLookup in jsobj.cpp). */ JSAtom *atom = AtomizeString(cx, source); jsatomid _; if (!atom || !bce.makeAtomIndex(atom, &_)) return UnrootedScript(NULL); } if (callerFrame && callerFrame->isFunctionFrame()) { /* * An eval script in a caller frame needs to have its enclosing * function captured in case it refers to an upvar, and someone * wishes to decompile it while it's running. */ JSFunction *fun = callerFrame->fun(); ObjectBox *funbox = parser.newFunctionBox(fun, &pc, fun->strict()); if (!funbox) return UnrootedScript(NULL); bce.objectList.add(funbox); } } ParseNode *pn; #if JS_HAS_XML_SUPPORT pn = NULL; bool onlyXML; onlyXML = true; #endif TokenStream &tokenStream = parser.tokenStream; bool canHaveDirectives = true; for (;;) { TokenKind tt = tokenStream.peekToken(TSF_OPERAND); if (tt <= TOK_EOF) { if (tt == TOK_EOF) break; JS_ASSERT(tt == TOK_ERROR); return UnrootedScript(NULL); } pn = parser.statement(); if (!pn) return UnrootedScript(NULL); if (canHaveDirectives) { if (!parser.maybeParseDirective(pn, &canHaveDirectives)) return UnrootedScript(NULL); } if (!FoldConstants(cx, pn, &parser)) return UnrootedScript(NULL); if (!NameFunctions(cx, pn)) return UnrootedScript(NULL); if (!EmitTree(cx, &bce, pn)) return UnrootedScript(NULL); #if JS_HAS_XML_SUPPORT if (!pn->isKind(PNK_SEMI) || !pn->pn_kid || !pn->pn_kid->isXMLItem()) onlyXML = false; #endif parser.freeTree(pn); } if (!SetSourceMap(cx, tokenStream, ss, script)) return UnrootedScript(NULL); #if JS_HAS_XML_SUPPORT /* * Prevent XML data theft via <script src="http://victim.com/foo.xml">. * For background, see: * * https://bugzilla.mozilla.org/show_bug.cgi?id=336551 */ if (pn && onlyXML && !callerFrame) { parser.reportError(NULL, JSMSG_XML_WHOLE_PROGRAM); return UnrootedScript(NULL); } #endif // It's an error to use |arguments| in a function that has a rest parameter. if (callerFrame && callerFrame->isFunctionFrame() && callerFrame->fun()->hasRest()) { HandlePropertyName arguments = cx->names().arguments; for (AtomDefnRange r = pc.lexdeps->all(); !r.empty(); r.popFront()) { if (r.front().key() == arguments) { parser.reportError(NULL, JSMSG_ARGUMENTS_AND_REST); return UnrootedScript(NULL); } } } /* * Nowadays the threaded interpreter needs a stop instruction, so we * do have to emit that here. */ if (Emit1(cx, &bce, JSOP_STOP) < 0) return UnrootedScript(NULL); if (!JSScript::fullyInitFromEmitter(cx, script, &bce)) return UnrootedScript(NULL); bce.tellDebuggerAboutCompiledScript(cx); if (!sct.complete()) return UnrootedScript(NULL); return script; }
// Compile a JS function body, which might appear as the value of an event // handler attribute in an HTML <INPUT> tag, or in a Function() constructor. bool frontend::CompileFunctionBody(JSContext *cx, JSFunction *fun, JSPrincipals *principals, JSPrincipals *originPrincipals, Bindings *bindings, const jschar *chars, size_t length, const char *filename, unsigned lineno, JSVersion version) { Parser parser(cx, principals, originPrincipals, chars, length, filename, lineno, version, /* callerFrame = */ NULL, /* foldConstants = */ true, /* compileAndGo = */ false); if (!parser.init()) return false; JS_ASSERT(fun); SharedContext funsc(cx, /* scopeChain = */ NULL, fun, /* funbox = */ NULL); unsigned staticLevel = 0; TreeContext funtc(&parser, &funsc, staticLevel); if (!funtc.init()) return false; GlobalObject *globalObject = fun->getParent() ? &fun->getParent()->global() : NULL; Rooted<JSScript*> script(cx); script = JSScript::Create(cx, /* savedCallerFun = */ false, principals, originPrincipals, /* compileAndGo = */ false, /* noScriptRval = */ false, globalObject, version, staticLevel); if (!script) return false; BytecodeEmitter funbce(&parser, &funsc, script, lineno); if (!funbce.init()) return false; funsc.bindings.transfer(cx, bindings); fun->setArgCount(funsc.bindings.numArgs()); if (!GenerateBlockId(&funsc, funsc.bodyid)) return false; /* FIXME: make Function format the source for a function definition. */ ParseNode *fn = FunctionNode::create(PNK_NAME, &parser); if (!fn) return false; fn->pn_body = NULL; fn->pn_cookie.makeFree(); ParseNode *argsbody = ListNode::create(PNK_ARGSBODY, &parser); if (!argsbody) return false; argsbody->setOp(JSOP_NOP); argsbody->makeEmpty(); fn->pn_body = argsbody; unsigned nargs = fun->nargs; if (nargs) { /* * NB: do not use AutoLocalNameArray because it will release space * allocated from cx->tempLifoAlloc by DefineArg. */ BindingNames names(cx); if (!funsc.bindings.getLocalNameArray(cx, &names)) return false; for (unsigned i = 0; i < nargs; i++) { if (!DefineArg(fn, names[i].maybeAtom, i, &parser)) return false; } } /* * After we're done parsing, we must fold constants, analyze any nested * functions, and generate code for this function, including a stop opcode * at the end. */ ParseNode *pn = parser.functionBody(Parser::StatementListBody); if (!pn) return false; if (!parser.tokenStream.matchToken(TOK_EOF)) { parser.reportErrorNumber(NULL, JSREPORT_ERROR, JSMSG_SYNTAX_ERROR); return false; } if (!FoldConstants(cx, pn, &parser)) return false; if (!AnalyzeFunctions(&parser)) return false; if (fn->pn_body) { JS_ASSERT(fn->pn_body->isKind(PNK_ARGSBODY)); fn->pn_body->append(pn); fn->pn_body->pn_pos = pn->pn_pos; pn = fn->pn_body; } if (!EmitFunctionScript(cx, &funbce, pn)) return false; return true; }
static void SetFunctionKinds(FunctionBox *funbox, bool *isHeavyweight, bool topInFunction, bool isDirectEval) { for (; funbox; funbox = funbox->siblings) { ParseNode *fn = funbox->node; if (!fn) continue; ParseNode *pn = fn->pn_body; if (!pn) continue; if (funbox->kids) SetFunctionKinds(funbox->kids, isHeavyweight, topInFunction, isDirectEval); JSFunction *fun = funbox->function(); JS_ASSERT(fun->kind() == JSFUN_INTERPRETED); if (funbox->funIsHeavyweight()) { /* nothing to do */ } else if (isDirectEval || funbox->inAnyDynamicScope()) { /* * Either we are in a with-block or a function scope that is * subject to direct eval; or we are compiling strict direct eval * code. * * In either case, fun may reference names that are not bound but * are not necessarily global either. (In the strict direct eval * case, we could bind them, but currently do not bother; see * the comment about strict mode code in BindTopLevelVar.) */ JS_ASSERT(!fun->isNullClosure()); } else { bool hasUpvars = false; if (pn->isKind(PNK_UPVARS)) { AtomDefnMapPtr upvars = pn->pn_names; JS_ASSERT(!upvars->empty()); /* Determine whether the this function contains upvars. */ for (AtomDefnRange r = upvars->all(); !r.empty(); r.popFront()) { if (!r.front().value()->resolve()->isFreeVar()) { hasUpvars = true; break; } } } if (!hasUpvars) { /* No lexical dependencies => null closure, for best performance. */ fun->setKind(JSFUN_NULL_CLOSURE); } } if (fun->kind() == JSFUN_INTERPRETED && pn->isKind(PNK_UPVARS)) { /* * We loop again over all upvars, and for each non-free upvar, * ensure that its containing function has been flagged as * heavyweight. * * The emitter must see funIsHeavyweight() accurately before * generating any code for a tree of nested functions. */ AtomDefnMapPtr upvars = pn->pn_names; JS_ASSERT(!upvars->empty()); for (AtomDefnRange r = upvars->all(); !r.empty(); r.popFront()) { Definition *defn = r.front().value(); Definition *lexdep = defn->resolve(); if (!lexdep->isFreeVar()) FlagHeavyweights(lexdep, funbox, isHeavyweight, topInFunction); } } } }
void printHelp(ParseNode const& p) { printf(" %12s %s\n", p.token(), p.helpString().c_str()); }
NodeDeclPtr XmlSceneParser::Impl::PrepareNode (const ParseNode& decl) { try { //попытка найти параметры в кеше if (NodeDeclPtr* node_decl_ptr = cache.FindValue<NodeDeclPtr> (decl)) return *node_decl_ptr; NodeDeclPtr node_decl (new NodeDecl, false); //парсинг базовых свойств node_decl->name = get<const char*> (decl, "id", ""); //парсинг точки поворота if (decl.First ("pivot")) { stl::auto_ptr<NodeDecl::Pivot> pivot (new NodeDecl::Pivot); ParseAttribute (decl, "pivot", pivot->position); pivot->has_orientation_pivot = strcmp (get<const char*> (decl, "orientation_pivot", "true"), "true") == 0; pivot->has_scale_pivot = strcmp (get<const char*> (decl, "scale_pivot", "true"), "true") == 0; node_decl->pivot = pivot; } //парсинг трансформаций node_decl->is_world_transform = strcmp (get<const char*> (decl, "bind_space", "local"), "world") == 0; if (ParseNode tm_node = decl.First ("transform")) { math::mat4f tm; ParseAttribute (tm_node, "", tm); affine_decompose (tm, node_decl->position, node_decl->orientation, node_decl->scale); } else { ParseAttribute (decl, "position", node_decl->position); ParseAttribute (decl, "scale", node_decl->scale); if (ParseNode rotation_node = decl.First ("rotation")) { float rotation [3] = {0.0f, 0.0f, 0.0f}; ParseAttribute (rotation_node, "", 3, &rotation [0]); node_decl->orientation = to_quat (degree (rotation [0]), degree (rotation [1]), degree (rotation [2])); } else { ParseAttribute (decl, "orientation", node_decl->orientation); } } node_decl->orientation_inherit = strcmp (get<const char*> (decl, "orientation_inherit", "true"), "true") == 0; node_decl->scale_inherit = strcmp (get<const char*> (decl, "scale_inherit", "true"), "true") == 0; //парсинг пользовательских свойств PropertyMap* properties = 0; if (decl.First ("property")) node_decl->properties.reset (properties = new PropertyMap); for (Parser::NamesakeIterator iter = decl.First ("property"); iter; ++iter) { ParseNode property_decl = *iter; const char* name = get<const char*> (property_decl, "name"); PropertyType type = get_property_type (property_decl.First ("type")); size_t property_index = properties->AddProperty (name, type, 1); stl::string value = get<const char*> (property_decl, "value"); properties->SetProperty (property_index, value.c_str ()); } //парсинг after node if (ParseNode before_node_decl = decl.First ("before_node")) { node_decl->before_node.reset (new stl::string); *node_decl->before_node = get<const char*> (before_node_decl, ""); } //парсинг привязки к родителю if (ParseNode parent_name_decl = decl.First ("parent")) { node_decl->parent_name.reset (new stl::string); *node_decl->parent_name = get<const char*> (parent_name_decl, ""); } //регистрация дескриптора узла cache.SetValue (decl, node_decl); return node_decl; } catch (xtl::exception& e) { e.touch ("scene_graph::XmlSceneParser::Impl::PrepareNode"); throw; } }
ParseNode gen_stmt(const ParseNode & content, const std::string & rules) { sprintf(codegen_buf, rules.c_str() , content.fs.CurrentTerm.what.c_str()); ParseNode newnode = ParseNode(gen_flex(Term{ TokenMeta::NT_STATEMENT, string(codegen_buf) }), nullptr); newnode.addchild(new ParseNode(content)); return newnode; }
// Compile a JS function body, which might appear as the value of an event // handler attribute in an HTML <INPUT> tag, or in a Function() constructor. bool frontend::CompileFunctionBody(JSContext *cx, MutableHandleFunction fun, CompileOptions options, const AutoNameVector &formals, const jschar *chars, size_t length, bool isAsmJSRecompile) { SkipRoot skip(cx, &chars); if (!CheckLength(cx, length)) return false; ScriptSource *ss = cx->new_<ScriptSource>(); if (!ss) return false; if (options.filename && !ss->setFilename(cx, options.filename)) return false; JS::RootedScriptSource sourceObject(cx, ScriptSourceObject::create(cx, ss)); if (!sourceObject) return false; SourceCompressionToken sct(cx); JS_ASSERT(options.sourcePolicy != CompileOptions::LAZY_SOURCE); if (options.sourcePolicy == CompileOptions::SAVE_SOURCE) { if (!ss->setSourceCopy(cx, chars, length, true, &sct)) return false; } Maybe<Parser<SyntaxParseHandler> > syntaxParser; if (options.canLazilyParse) { syntaxParser.construct(cx, options, chars, length, /* foldConstants = */ false, (Parser<SyntaxParseHandler> *) NULL, (LazyScript *) NULL); } JS_ASSERT(!options.forEval); Parser<FullParseHandler> parser(cx, options, chars, length, /* foldConstants = */ true, options.canLazilyParse ? &syntaxParser.ref() : NULL, NULL); parser.sct = &sct; JS_ASSERT(fun); fun->setArgCount(formals.length()); /* FIXME: make Function format the source for a function definition. */ ParseNode *fn = CodeNode::create(PNK_FUNCTION, &parser.handler); if (!fn) return false; fn->pn_body = NULL; fn->pn_funbox = NULL; fn->pn_cookie.makeFree(); ParseNode *argsbody = ListNode::create(PNK_ARGSBODY, &parser.handler); if (!argsbody) return false; argsbody->setOp(JSOP_NOP); argsbody->makeEmpty(); fn->pn_body = argsbody; Rooted<JSScript*> script(cx, JSScript::Create(cx, NullPtr(), false, options, /* staticLevel = */ 0, sourceObject, /* sourceStart = */ 0, length)); if (!script) return false; // If the context is strict, immediately parse the body in strict // mode. Otherwise, we parse it normally. If we see a "use strict" // directive, we backup and reparse it as strict. TokenStream::Position start(parser.keepAtoms); parser.tokenStream.tell(&start); bool strict = StrictModeFromContext(cx); bool becameStrict; FunctionBox *funbox; ParseNode *pn; while (true) { pn = parser.standaloneFunctionBody(fun, formals, script, fn, &funbox, strict, &becameStrict); if (pn) break; if (parser.hadAbortedSyntaxParse()) { // Hit some unrecoverable ambiguity during an inner syntax parse. // Syntax parsing has now been disabled in the parser, so retry // the parse. parser.clearAbortedSyntaxParse(); } else { // If the function became strict, reparse in strict mode. if (strict || !becameStrict || parser.tokenStream.hadError()) return false; strict = true; } parser.tokenStream.seek(start); } if (!NameFunctions(cx, pn)) return false; if (fn->pn_body) { JS_ASSERT(fn->pn_body->isKind(PNK_ARGSBODY)); fn->pn_body->append(pn); fn->pn_body->pn_pos = pn->pn_pos; pn = fn->pn_body; } bool generateBytecode = true; #ifdef JS_ION JS_ASSERT_IF(isAsmJSRecompile, fn->pn_funbox->useAsm); if (fn->pn_funbox->useAsm && !isAsmJSRecompile) { RootedFunction moduleFun(cx); if (!CompileAsmJS(cx, parser.tokenStream, fn, options, ss, /* bufStart = */ 0, /* bufEnd = */ length, &moduleFun)) return false; if (moduleFun) { funbox->object = moduleFun; fun.set(moduleFun); // replace the existing function with the LinkAsmJS native generateBytecode = false; } } #endif if (generateBytecode) { /* * The reason for checking fun->environment() below is that certain * consumers of JS::CompileFunction, namely * nsEventListenerManager::CompileEventHandlerInternal, passes in a * NULL environment. This compiled function is never used, but instead * is cloned immediately onto the right scope chain. */ BytecodeEmitter funbce(/* parent = */ NULL, &parser, funbox, script, /* insideEval = */ false, /* evalCaller = */ NullPtr(), fun->environment() && fun->environment()->isGlobal(), options.lineno); if (!funbce.init()) return false; if (!EmitFunctionScript(cx, &funbce, pn)) return false; } if (!SetSourceMap(cx, parser.tokenStream, ss, script)) return false; if (!sct.complete()) return false; return true; }
ParseNode gen_paramtable(ParseNode & paramtable_elem, ParseNode & paramtable) { ParseNode newnode = ParseNode(gen_flex(Term{ TokenMeta::NT_PARAMTABLE, "" }), nullptr); bool dimen1 = false, dimen2 = false, arg1 = false, arg2 = false, va1 = false, va2 = false;; if (paramtable_elem.fs.CurrentTerm.token == TokenMeta::NT_DIMENSLICE) { dimen1 = true; } if (paramtable_elem.fs.CurrentTerm.token == TokenMeta::NT_ARGTABLE_PURE) { arg1 = true; } if (TokenMeta::iselement(paramtable_elem.fs.CurrentTerm.token)) { va1 = true; } if (paramtable.fs.CurrentTerm.token == TokenMeta::NT_PARAMTABLE_DIMENSLICE) { dimen2 = true; } if (paramtable.fs.CurrentTerm.token == TokenMeta::NT_ARGTABLE_PURE) { arg2 = true; } if (TokenMeta::iselement(paramtable.fs.CurrentTerm.token)) { va2 = true; } if (paramtable_elem.fs.CurrentTerm.token == TokenMeta::NT_KEYVALUE && paramtable.fs.CurrentTerm.token == TokenMeta::NT_PARAMTABLE) { // all keyvalue pair newnode = gen_flattern(paramtable_elem, paramtable, "%s, %s", TokenMeta::NT_PARAMTABLE); } else if(paramtable.fs.CurrentTerm.token == TokenMeta::NT_PARAMTABLE){ // there is keyvalue pair // this is possible because of rule `dimen_slice : exp ',' paramtable ` // there is keyvalue if (dimen1) { // promote dimen_slice to paramtable for (int i = 0; i < paramtable_elem.child.size(); i++) { newnode.addchild(new ParseNode(*paramtable_elem.child[i])); } } else { // do not promote exp to keyvalue // TODO newnode.addchild(new ParseNode(paramtable_elem)); } // assume paramtable is flatterned for (int i = 0; i < paramtable.child.size(); i++) { newnode.addchild(new ParseNode(*paramtable.child[i])); } sprintf(codegen_buf, "%s, %s", paramtable_elem.fs.CurrentTerm.what.c_str(), paramtable.fs.CurrentTerm.what.c_str()); newnode.fs.CurrentTerm.what = string(codegen_buf); } else if ((dimen1 || arg1 || va1) && (dimen2 || arg2 || va2)) { // all dimen_slice or argument_pure or variable newnode = ParseNode(gen_flex(Term{ (dimen1 || dimen2) ? TokenMeta::NT_PARAMTABLE_DIMENSLICE : TokenMeta::NT_ARGTABLE_PURE, "" }), nullptr); if (dimen1 || arg1) { for (int i = 0; i < paramtable_elem.child.size(); i++) { newnode.addchild(new ParseNode(*paramtable_elem.child[i])); } } else { newnode.addchild(new ParseNode(paramtable_elem)); } // assume paramtable is flatterned if (dimen2 || arg2) { for (int i = 0; i < paramtable.child.size(); i++) { newnode.addchild(new ParseNode(*paramtable.child[i])); } } else { newnode.addchild(new ParseNode(paramtable)); } sprintf(codegen_buf, "%s, %s", paramtable_elem.fs.CurrentTerm.what.c_str(), paramtable.fs.CurrentTerm.what.c_str()); newnode.fs.CurrentTerm.what = string(codegen_buf); } else { print_error("bad param table"); } return newnode; }
static void SetFunctionKinds(FunctionBox *funbox, uint32_t *tcflags, bool isDirectEval) { for (; funbox; funbox = funbox->siblings) { ParseNode *fn = funbox->node; ParseNode *pn = fn->pn_body; if (funbox->kids) SetFunctionKinds(funbox->kids, tcflags, isDirectEval); JSFunction *fun = funbox->function(); JS_ASSERT(fun->kind() == JSFUN_INTERPRETED); if (funbox->tcflags & TCF_FUN_HEAVYWEIGHT) { /* nothing to do */ } else if (isDirectEval || funbox->inAnyDynamicScope()) { /* * Either we are in a with-block or a function scope that is * subject to direct eval; or we are compiling strict direct eval * code. * * In either case, fun may reference names that are not bound but * are not necessarily global either. (In the strict direct eval * case, we could bind them, but currently do not bother; see * the comment about strict mode code in BindTopLevelVar.) */ JS_ASSERT(!fun->isNullClosure()); } else { bool hasUpvars = false; bool canFlatten = true; if (pn->isKind(PNK_UPVARS)) { AtomDefnMapPtr upvars = pn->pn_names; JS_ASSERT(!upvars->empty()); /* * For each lexical dependency from this closure to an outer * binding, analyze whether it is safe to copy the binding's * value into a flat closure slot when the closure is formed. */ for (AtomDefnRange r = upvars->all(); !r.empty(); r.popFront()) { Definition *defn = r.front().value(); Definition *lexdep = defn->resolve(); if (!lexdep->isFreeVar()) { hasUpvars = true; if (!CanFlattenUpvar(lexdep, funbox, *tcflags)) { /* * Can't flatten. Enclosing functions holding * variables used by this function will be flagged * heavyweight below. FIXME bug 545759: re-enable * partial flat closures. */ canFlatten = false; break; } } } } /* * Top-level functions, and (extension) functions not at top level * which are also not directly within other functions, aren't * flattened. */ if (fn->isOp(JSOP_DEFFUN)) canFlatten = false; if (!hasUpvars) { /* No lexical dependencies => null closure, for best performance. */ fun->setKind(JSFUN_NULL_CLOSURE); } else if (canFlatten) { fun->setKind(JSFUN_FLAT_CLOSURE); switch (fn->getOp()) { case JSOP_DEFLOCALFUN: fn->setOp(JSOP_DEFLOCALFUN_FC); break; case JSOP_LAMBDA: fn->setOp(JSOP_LAMBDA_FC); break; default: /* js::frontend::EmitTree's PNK_FUNCTION case sets op. */ JS_ASSERT(fn->isOp(JSOP_NOP)); } } } if (fun->kind() == JSFUN_INTERPRETED && pn->isKind(PNK_UPVARS)) { /* * One or more upvars cannot be safely snapshot into a flat * closure's non-reserved slot (see JSOP_GETFCSLOT), so we loop * again over all upvars, and for each non-free upvar, ensure that * its containing function has been flagged as heavyweight. * * The emitter must see TCF_FUN_HEAVYWEIGHT accurately before * generating any code for a tree of nested functions. */ AtomDefnMapPtr upvars = pn->pn_names; JS_ASSERT(!upvars->empty()); for (AtomDefnRange r = upvars->all(); !r.empty(); r.popFront()) { Definition *defn = r.front().value(); Definition *lexdep = defn->resolve(); if (!lexdep->isFreeVar()) FlagHeavyweights(lexdep, funbox, tcflags); } } if (funbox->joinable()) fun->setJoinable(); } }
static bool MarkFunArgs(JSContext *cx, FunctionBox *funbox, uint32_t functionCount) { FunctionBoxQueue queue; if (!queue.init(functionCount)) { js_ReportOutOfMemory(cx); return false; } FindFunArgs(funbox, -1, &queue); while ((funbox = queue.pull()) != NULL) { ParseNode *fn = funbox->node; JS_ASSERT(fn->isFunArg()); ParseNode *pn = fn->pn_body; if (pn->isKind(PNK_UPVARS)) { AtomDefnMapPtr upvars = pn->pn_names; JS_ASSERT(!upvars->empty()); for (AtomDefnRange r = upvars->all(); !r.empty(); r.popFront()) { Definition *defn = r.front().value(); Definition *lexdep = defn->resolve(); if (!lexdep->isFreeVar() && !lexdep->isFunArg() && (lexdep->kind() == Definition::FUNCTION || lexdep->isOp(JSOP_CALLEE))) { /* * Mark this formerly-Algol-like function as an escaping * function (i.e., as a funarg), because it is used from * another funarg. * * Progress is guaranteed because we set the funarg flag * here, which suppresses revisiting this function (thanks * to the !lexdep->isFunArg() test just above). */ lexdep->setFunArg(); FunctionBox *afunbox; if (lexdep->isOp(JSOP_CALLEE)) { /* * A named function expression will not appear to be a * funarg if it is immediately applied. However, if its * name is used in an escaping function nested within * it, then it must become flagged as a funarg again. * See bug 545980. */ afunbox = funbox; uintN calleeLevel = lexdep->pn_cookie.level(); uintN staticLevel = afunbox->level + 1U; while (staticLevel != calleeLevel) { afunbox = afunbox->parent; --staticLevel; } JS_ASSERT(afunbox->level + 1U == calleeLevel); afunbox->node->setFunArg(); } else { afunbox = lexdep->pn_funbox; } queue.push(afunbox); /* * Walk over nested functions again, now that we have * changed the level across which it is unsafe to access * upvars using the runtime dynamic link (frame chain). */ if (afunbox->kids) FindFunArgs(afunbox->kids, afunbox->level, &queue); } } } } return true; }
/* * Used by Parser::forStatement and comprehensionTail to clone the TARGET in * for (var/const/let TARGET in EXPR) * * opn must be the pn_head of a node produced by Parser::variables, so its form * is known to be LHS = NAME | [LHS] | {id:LHS}. * * The cloned tree is for use only in the same statement and binding context as * the original tree. */ ParseNode * js::CloneLeftHandSide(ParseNode *opn, Parser *parser) { ParseNode *pn = parser->new_<ParseNode>(opn->getKind(), opn->getOp(), opn->getArity(), opn->pn_pos); if (!pn) return NULL; pn->setInParens(opn->isInParens()); pn->setDefn(opn->isDefn()); pn->setUsed(opn->isUsed()); #if JS_HAS_DESTRUCTURING if (opn->isArity(PN_LIST)) { JS_ASSERT(opn->isKind(PNK_RB) || opn->isKind(PNK_RC)); pn->makeEmpty(); for (ParseNode *opn2 = opn->pn_head; opn2; opn2 = opn2->pn_next) { ParseNode *pn2; if (opn->isKind(PNK_RC)) { JS_ASSERT(opn2->isArity(PN_BINARY)); JS_ASSERT(opn2->isKind(PNK_COLON)); ParseNode *tag = CloneParseTree(opn2->pn_left, parser); if (!tag) return NULL; ParseNode *target = CloneLeftHandSide(opn2->pn_right, parser); if (!target) return NULL; pn2 = parser->new_<BinaryNode>(PNK_COLON, JSOP_INITPROP, opn2->pn_pos, tag, target); } else if (opn2->isArity(PN_NULLARY)) { JS_ASSERT(opn2->isKind(PNK_COMMA)); pn2 = CloneParseTree(opn2, parser); } else { pn2 = CloneLeftHandSide(opn2, parser); } if (!pn2) return NULL; pn->append(pn2); } pn->pn_xflags = opn->pn_xflags; return pn; } #endif JS_ASSERT(opn->isArity(PN_NAME)); JS_ASSERT(opn->isKind(PNK_NAME)); /* If opn is a definition or use, make pn a use. */ pn->pn_u.name = opn->pn_u.name; pn->setOp(JSOP_SETNAME); if (opn->isUsed()) { Definition *dn = pn->pn_lexdef; pn->pn_link = dn->dn_uses; dn->dn_uses = pn; } else { pn->pn_expr = NULL; if (opn->isDefn()) { /* We copied some definition-specific state into pn. Clear it out. */ pn->pn_cookie.makeFree(); pn->pn_dflags &= ~PND_BOUND; pn->setDefn(false); LinkUseToDef(pn, (Definition *) opn); } } return pn; }
ParseNode gen_array_generate_paramtable(const ParseNode & argtable) { /* give initial value */ /* `B(1:2:3)` can be either a single-element argtable or a exp, this can probably lead to reduction conflicts, so merge rules */ /* NOTE fortran use a 1d list to initialize a 2d(or higher) array, however, contrary to c++ and most other language does, it store them in a **conlumn - first order**. for a 2d array, it means you a order of a(1)(1)->a(2)(1)->a(lb_1)(1)->a(1)(2) */ ParseNode newnode = ParseNode(); vector<ParseNode *> slices; vector<ParseNode *> hiddens; std::vector<std::string> ans; string fi; for (int i = 0; i < argtable.child.size() + 1; i++) { int stat = 0; if (i == argtable.child.size()) { stat = 0; }else { if (argtable.child[i]->fs.CurrentTerm.token == TokenMeta::NT_EXPRESSION) { if (argtable.child[i]/* NT_EXPRESSION */->child[0]->fs.CurrentTerm.token == TokenMeta::NT_FUCNTIONARRAY) { // slice stat = 1; slices.push_back(argtable.child[i]->child[0]); } else if (argtable.child[i]/* NT_EXPRESSION */->child[0]->fs.CurrentTerm.token == TokenMeta::NT_HIDDENDO) { // hidden_do stat = 2; hiddens.push_back(argtable.child[i]->child[0]); } } else { /* for1array<_Container_value_type> & farr, const std::vector<int> & lower_bound , const std::vector<int> & size, const std::vector<T> & values */ // set in gen_vardef.cpp sprintf(codegen_buf, "init_for1array(%%s, %%s, %%s, std::vector<%%s>{%s});\n", /* value */ argtable.fs.CurrentTerm.what.c_str()); goto CAN_ONLY_GEN_ONE; } } if (stat != 1 && !slices.empty()) { for (int i = 0; i < slices.size(); i++) { ans.push_back(slices[i]->fs.CurrentTerm.what); } slices.clear(); } if (stat != 2 && !hiddens.empty()) { for (int i = 0; i < hiddens.size(); i++) { ans.push_back(hiddens[i]->fs.CurrentTerm.what); } hiddens.clear(); } } for (int i = 0; i < ans.size(); i++) { if (i != 0) fi += " + "; fi += ans[i]; } sprintf(codegen_buf, "%%s = %s;\n", /* value */ fi.c_str()); CAN_ONLY_GEN_ONE: newnode.fs.CurrentTerm = Term{ TokenMeta::NT_ARRAYBUILDER_VALUE, string(codegen_buf) }; newnode.addchild(new ParseNode(argtable)); // argtable return newnode; }
JSScript * frontend::CompileScript(JSContext *cx, HandleObject scopeChain, StackFrame *callerFrame, JSPrincipals *principals, JSPrincipals *originPrincipals, bool compileAndGo, bool noScriptRval, const jschar *chars, size_t length, const char *filename, unsigned lineno, JSVersion version, JSString *source_ /* = NULL */, unsigned staticLevel /* = 0 */) { RootedString source(cx, source_); class ProbesManager { const char* filename; unsigned lineno; public: ProbesManager(const char *f, unsigned l) : filename(f), lineno(l) { Probes::compileScriptBegin(filename, lineno); } ~ProbesManager() { Probes::compileScriptEnd(filename, lineno); } }; ProbesManager probesManager(filename, lineno); /* * The scripted callerFrame can only be given for compile-and-go scripts * and non-zero static level requires callerFrame. */ JS_ASSERT_IF(callerFrame, compileAndGo); JS_ASSERT_IF(staticLevel != 0, callerFrame); Parser parser(cx, principals, originPrincipals, chars, length, filename, lineno, version, /* foldConstants = */ true, compileAndGo); if (!parser.init()) return NULL; SharedContext sc(cx, scopeChain, /* fun = */ NULL, /* funbox = */ NULL, StrictModeFromContext(cx)); TreeContext tc(&parser, &sc, staticLevel, /* bodyid = */ 0); if (!tc.init()) return NULL; bool savedCallerFun = compileAndGo && callerFrame && callerFrame->isFunctionFrame(); Rooted<JSScript*> script(cx, JSScript::Create(cx, /* enclosingScope = */ NullPtr(), savedCallerFun, principals, originPrincipals, compileAndGo, noScriptRval, version, staticLevel)); if (!script) return NULL; // We can specialize a bit for the given scope chain if that scope chain is the global object. JSObject *globalScope = scopeChain && scopeChain == &scopeChain->global() ? (JSObject*) scopeChain : NULL; JS_ASSERT_IF(globalScope, globalScope->isNative()); JS_ASSERT_IF(globalScope, JSCLASS_HAS_GLOBAL_FLAG_AND_SLOTS(globalScope->getClass())); BytecodeEmitter bce(/* parent = */ NULL, &parser, &sc, script, callerFrame, !!globalScope, lineno); if (!bce.init()) return NULL; /* If this is a direct call to eval, inherit the caller's strictness. */ if (callerFrame && callerFrame->isScriptFrame() && callerFrame->script()->strictModeCode) sc.strictModeState = StrictMode::STRICT; if (compileAndGo) { if (source) { /* * Save eval program source in script->atoms[0] for the * eval cache (see EvalCacheLookup in jsobj.cpp). */ JSAtom *atom = js_AtomizeString(cx, source); jsatomid _; if (!atom || !bce.makeAtomIndex(atom, &_)) return NULL; } if (callerFrame && callerFrame->isFunctionFrame()) { /* * An eval script in a caller frame needs to have its enclosing * function captured in case it refers to an upvar, and someone * wishes to decompile it while it's running. */ ObjectBox *funbox = parser.newObjectBox(callerFrame->fun()); if (!funbox) return NULL; funbox->emitLink = bce.objectList.lastbox; bce.objectList.lastbox = funbox; bce.objectList.length++; } } ParseNode *pn; #if JS_HAS_XML_SUPPORT pn = NULL; bool onlyXML; onlyXML = true; #endif TokenStream &tokenStream = parser.tokenStream; { ParseNode *stringsAtStart = ListNode::create(PNK_STATEMENTLIST, &parser); if (!stringsAtStart) return NULL; stringsAtStart->makeEmpty(); bool ok = parser.processDirectives(stringsAtStart) && EmitTree(cx, &bce, stringsAtStart); parser.freeTree(stringsAtStart); if (!ok) return NULL; } JS_ASSERT(sc.strictModeState != StrictMode::UNKNOWN); for (;;) { TokenKind tt = tokenStream.peekToken(TSF_OPERAND); if (tt <= TOK_EOF) { if (tt == TOK_EOF) break; JS_ASSERT(tt == TOK_ERROR); return NULL; } pn = parser.statement(); if (!pn) return NULL; if (!FoldConstants(cx, pn, &parser)) return NULL; if (!AnalyzeFunctions(&parser, callerFrame)) return NULL; tc.functionList = NULL; if (!EmitTree(cx, &bce, pn)) return NULL; #if JS_HAS_XML_SUPPORT if (!pn->isKind(PNK_SEMI) || !pn->pn_kid || !pn->pn_kid->isXMLItem()) onlyXML = false; #endif parser.freeTree(pn); } #if JS_HAS_XML_SUPPORT /* * Prevent XML data theft via <script src="http://victim.com/foo.xml">. * For background, see: * * https://bugzilla.mozilla.org/show_bug.cgi?id=336551 */ if (pn && onlyXML && !callerFrame) { parser.reportError(NULL, JSMSG_XML_WHOLE_PROGRAM); return NULL; } #endif // It's an error to use |arguments| in a function that has a rest parameter. if (callerFrame && callerFrame->isFunctionFrame() && callerFrame->fun()->hasRest()) { PropertyName *arguments = cx->runtime->atomState.argumentsAtom; for (AtomDefnRange r = tc.lexdeps->all(); !r.empty(); r.popFront()) { if (r.front().key() == arguments) { parser.reportError(NULL, JSMSG_ARGUMENTS_AND_REST); return NULL; } } // We're not in a function context, so we don't expect any bindings. JS_ASSERT(sc.bindings.lookup(cx, arguments, NULL) == NONE); } /* * Nowadays the threaded interpreter needs a stop instruction, so we * do have to emit that here. */ if (Emit1(cx, &bce, JSOP_STOP) < 0) return NULL; if (!JSScript::fullyInitFromEmitter(cx, script, &bce)) return NULL; bce.tellDebuggerAboutCompiledScript(cx); return script; }
// Compile a JS function body, which might appear as the value of an event // handler attribute in an HTML <INPUT> tag, or in a Function() constructor. bool frontend::CompileFunctionBody(JSContext *cx, HandleFunction fun, CompileOptions options, const AutoNameVector &formals, const jschar *chars, size_t length) { if (!CheckLength(cx, length)) return false; ScriptSource *ss = cx->new_<ScriptSource>(); if (!ss) return false; ScriptSourceHolder ssh(cx->runtime, ss); SourceCompressionToken sct(cx); JS_ASSERT(options.sourcePolicy != CompileOptions::LAZY_SOURCE); if (options.sourcePolicy == CompileOptions::SAVE_SOURCE) { if (!ss->setSourceCopy(cx, chars, length, true, &sct)) return false; } options.setCompileAndGo(false); Parser parser(cx, options, chars, length, /* foldConstants = */ true); if (!parser.init()) return false; parser.sct = &sct; JS_ASSERT(fun); StrictMode sms = StrictModeFromContext(cx); FunctionBox *funbox = parser.newFunctionBox(fun, /* outerpc = */ NULL, sms); fun->setArgCount(formals.length()); unsigned staticLevel = 0; ParseContext funpc(&parser, funbox, staticLevel, /* bodyid = */ 0); if (!funpc.init()) return false; /* FIXME: make Function format the source for a function definition. */ ParseNode *fn = FunctionNode::create(PNK_NAME, &parser); if (!fn) return false; fn->pn_body = NULL; fn->pn_cookie.makeFree(); ParseNode *argsbody = ListNode::create(PNK_ARGSBODY, &parser); if (!argsbody) return false; argsbody->setOp(JSOP_NOP); argsbody->makeEmpty(); fn->pn_body = argsbody; for (unsigned i = 0; i < formals.length(); i++) { if (!DefineArg(&parser, fn, formals[i])) return false; } /* * After we're done parsing, we must fold constants, analyze any nested * functions, and generate code for this function, including a stop opcode * at the end. */ ParseNode *pn = parser.functionBody(Parser::StatementListBody); if (!pn) return false; if (!parser.tokenStream.matchToken(TOK_EOF)) { parser.reportError(NULL, JSMSG_SYNTAX_ERROR); return false; } if (!FoldConstants(cx, pn, &parser)) return false; Rooted<JSScript*> script(cx, JSScript::Create(cx, NullPtr(), false, options, staticLevel, ss, 0, length)); if (!script) return false; InternalHandle<Bindings*> bindings(script, &script->bindings); if (!funpc.generateFunctionBindings(cx, bindings)) return false; BytecodeEmitter funbce(/* parent = */ NULL, &parser, funbox, script, /* callerFrame = */ NULL, /* hasGlobalScope = */ false, options.lineno); if (!funbce.init()) return false; if (!NameFunctions(cx, pn)) return false; if (fn->pn_body) { JS_ASSERT(fn->pn_body->isKind(PNK_ARGSBODY)); fn->pn_body->append(pn); fn->pn_body->pn_pos = pn->pn_pos; pn = fn->pn_body; } if (!SetSourceMap(cx, parser.tokenStream, ss, script)) return false; if (!EmitFunctionScript(cx, &funbce, pn)) return false; return true; }
JSScript * frontend::CompileScript(JSContext *cx, JSObject *scopeChain, StackFrame *callerFrame, JSPrincipals *principals, JSPrincipals *originPrincipals, bool compileAndGo, bool noScriptRval, bool needScriptGlobal, const jschar *chars, size_t length, const char *filename, unsigned lineno, JSVersion version, JSString *source /* = NULL */, unsigned staticLevel /* = 0 */) { class ProbesManager { const char* filename; unsigned lineno; public: ProbesManager(const char *f, unsigned l) : filename(f), lineno(l) { Probes::compileScriptBegin(filename, lineno); } ~ProbesManager() { Probes::compileScriptEnd(filename, lineno); } }; ProbesManager probesManager(filename, lineno); /* * The scripted callerFrame can only be given for compile-and-go scripts * and non-zero static level requires callerFrame. */ JS_ASSERT_IF(callerFrame, compileAndGo); JS_ASSERT_IF(staticLevel != 0, callerFrame); Parser parser(cx, principals, originPrincipals, chars, length, filename, lineno, version, callerFrame, /* foldConstants = */ true, compileAndGo); if (!parser.init()) return NULL; SharedContext sc(cx, scopeChain, /* fun = */ NULL, /* funbox = */ NULL); TreeContext tc(&parser, &sc, staticLevel); if (!tc.init()) return NULL; bool savedCallerFun = compileAndGo && callerFrame && callerFrame->isFunctionFrame(); GlobalObject *globalObject = needScriptGlobal ? GetCurrentGlobal(cx) : NULL; Rooted<JSScript*> script(cx); script = JSScript::Create(cx, savedCallerFun, principals, originPrincipals, compileAndGo, noScriptRval, globalObject, version, staticLevel); if (!script) return NULL; BytecodeEmitter bce(&parser, &sc, script, lineno); if (!bce.init()) return NULL; // We can specialize a bit for the given scope chain if that scope chain is the global object. JSObject *globalObj = scopeChain && scopeChain == &scopeChain->global() ? &scopeChain->global() : NULL; JS_ASSERT_IF(globalObj, globalObj->isNative()); JS_ASSERT_IF(globalObj, JSCLASS_HAS_GLOBAL_FLAG_AND_SLOTS(globalObj->getClass())); GlobalScope globalScope(cx, globalObj); bce.globalScope = &globalScope; /* If this is a direct call to eval, inherit the caller's strictness. */ if (callerFrame && callerFrame->isScriptFrame() && callerFrame->script()->strictModeCode) sc.setInStrictMode(); if (compileAndGo) { if (source) { /* * Save eval program source in script->atoms[0] for the * eval cache (see EvalCacheLookup in jsobj.cpp). */ JSAtom *atom = js_AtomizeString(cx, source); jsatomid _; if (!atom || !bce.makeAtomIndex(atom, &_)) return NULL; } if (callerFrame && callerFrame->isFunctionFrame()) { /* * An eval script in a caller frame needs to have its enclosing * function captured in case it refers to an upvar, and someone * wishes to decompile it while it's running. */ ObjectBox *funbox = parser.newObjectBox(callerFrame->fun()); if (!funbox) return NULL; funbox->emitLink = bce.objectList.lastbox; bce.objectList.lastbox = funbox; bce.objectList.length++; } } /* * Inline this->statements to emit as we go to save AST space. We must * generate our script-body blockid since we aren't calling Statements. */ if (!GenerateBlockId(&sc, sc.bodyid)) return NULL; ParseNode *pn; #if JS_HAS_XML_SUPPORT pn = NULL; bool onlyXML; onlyXML = true; #endif bool inDirectivePrologue = true; TokenStream &tokenStream = parser.tokenStream; tokenStream.setOctalCharacterEscape(false); for (;;) { TokenKind tt = tokenStream.peekToken(TSF_OPERAND); if (tt <= TOK_EOF) { if (tt == TOK_EOF) break; JS_ASSERT(tt == TOK_ERROR); return NULL; } pn = parser.statement(); if (!pn) return NULL; if (inDirectivePrologue && !parser.recognizeDirectivePrologue(pn, &inDirectivePrologue)) return NULL; if (!FoldConstants(cx, pn, bce.parser)) return NULL; if (!AnalyzeFunctions(bce.parser)) return NULL; tc.functionList = NULL; if (!EmitTree(cx, &bce, pn)) return NULL; #if JS_HAS_XML_SUPPORT if (!pn->isKind(PNK_SEMI) || !pn->pn_kid || !pn->pn_kid->isXMLItem()) onlyXML = false; #endif bce.parser->freeTree(pn); } #if JS_HAS_XML_SUPPORT /* * Prevent XML data theft via <script src="http://victim.com/foo.xml">. * For background, see: * * https://bugzilla.mozilla.org/show_bug.cgi?id=336551 */ if (pn && onlyXML && !callerFrame) { parser.reportErrorNumber(NULL, JSREPORT_ERROR, JSMSG_XML_WHOLE_PROGRAM); return NULL; } #endif if (!parser.checkForArgumentsAndRest()) return NULL; /* * Nowadays the threaded interpreter needs a stop instruction, so we * do have to emit that here. */ if (Emit1(cx, &bce, JSOP_STOP) < 0) return NULL; if (!script->fullyInitFromEmitter(cx, &bce)) return NULL; if (!MarkInnerAndOuterFunctions(cx, script)) return NULL; return script; }
ParseNode gen_interface(const ParseNode & wrappers) { ParseNode newnode = ParseNode(gen_flex(Term{TokenMeta::NT_INTERFACE, ""/*wrappers.fs.CurrentTerm.what*/}), nullptr); newnode.addchild(new ParseNode(wrappers)); return newnode; }
void FundamentalType::fromParseNode(const ParseNode& node) { mName = node.getAttr("name"); }
void f (const ParseNode& node) { printf ("node '%s': source='%s' line=%lu\n", node.Name (), node.Source (), node.LineNumber ()); throw TestException (); }
// Compile a JS function body, which might appear as the value of an event // handler attribute in an HTML <INPUT> tag, or in a Function() constructor. bool frontend::CompileFunctionBody(JSContext *cx, HandleFunction fun, CompileOptions options, const AutoNameVector &formals, StableCharPtr chars, size_t length) { if (!CheckLength(cx, length)) return false; ScriptSource *ss = cx->new_<ScriptSource>(); if (!ss) return false; ScriptSourceHolder ssh(cx->runtime, ss); SourceCompressionToken sct(cx); JS_ASSERT(options.sourcePolicy != CompileOptions::LAZY_SOURCE); if (options.sourcePolicy == CompileOptions::SAVE_SOURCE) { if (!ss->setSourceCopy(cx, chars, length, true, &sct)) return false; } options.setCompileAndGo(false); Parser parser(cx, options, chars, length, /* foldConstants = */ true); if (!parser.init()) return false; parser.sct = &sct; JS_ASSERT(fun); fun->setArgCount(formals.length()); /* FIXME: make Function format the source for a function definition. */ ParseNode *fn = FunctionNode::create(PNK_NAME, &parser); if (!fn) return false; fn->pn_body = NULL; fn->pn_cookie.makeFree(); ParseNode *argsbody = ListNode::create(PNK_ARGSBODY, &parser); if (!argsbody) return false; argsbody->setOp(JSOP_NOP); argsbody->makeEmpty(); fn->pn_body = argsbody; Rooted<JSScript*> script(cx, JSScript::Create(cx, NullPtr(), false, options, /* staticLevel = */ 0, ss, /* sourceStart = */ 0, length)); if (!script) return false; // If the context is strict, immediately parse the body in strict // mode. Otherwise, we parse it normally. If we see a "use strict" // directive, we backup and reparse it as strict. TokenStream::Position start; parser.tokenStream.tell(&start); bool initiallyStrict = StrictModeFromContext(cx); bool becameStrict; FunctionBox *funbox; ParseNode *pn = parser.standaloneFunctionBody(fun, formals, script, fn, &funbox, initiallyStrict, &becameStrict); if (!pn) { if (initiallyStrict || !becameStrict || parser.tokenStream.hadError()) return false; // Reparse in strict mode. parser.tokenStream.seek(start); pn = parser.standaloneFunctionBody(fun, formals, script, fn, &funbox, /* strict = */ true); if (!pn) return false; } BytecodeEmitter funbce(/* parent = */ NULL, &parser, funbox, script, /* callerFrame = */ NULL, /* hasGlobalScope = */ false, options.lineno); if (!funbce.init()) return false; if (!NameFunctions(cx, pn)) return false; if (fn->pn_body) { JS_ASSERT(fn->pn_body->isKind(PNK_ARGSBODY)); fn->pn_body->append(pn); fn->pn_body->pn_pos = pn->pn_pos; pn = fn->pn_body; } if (!SetSourceMap(cx, parser.tokenStream, ss, script)) return false; if (!EmitFunctionScript(cx, &funbce, pn)) return false; if (!sct.complete()) return false; return true; }
void Typedef::fromParseNode(const ParseNode& node) { mName = node.getAttr("name"); mContext = getParseNodeFromId(node.getAttr("context"))->base; mLocation = new Location(node.getIntAttr("line"), (File*)getParseNodeFromId(node.getAttr("file"))->base); mType = getParseNodeFromId(node.getAttr("type"))->base; }
JSScript * frontend::CompileScript(JSContext *cx, JSObject *scopeChain, StackFrame *callerFrame, JSPrincipals *principals, JSPrincipals *originPrincipals, uint32_t tcflags, const jschar *chars, size_t length, const char *filename, unsigned lineno, JSVersion version, JSString *source /* = NULL */, unsigned staticLevel /* = 0 */) { TokenKind tt; ParseNode *pn; JSScript *script; bool inDirectivePrologue; JS_ASSERT(!(tcflags & ~(TCF_COMPILE_N_GO | TCF_NO_SCRIPT_RVAL | TCF_COMPILE_FOR_EVAL | TCF_NEED_SCRIPT_GLOBAL))); /* * The scripted callerFrame can only be given for compile-and-go scripts * and non-zero static level requires callerFrame. */ JS_ASSERT_IF(callerFrame, tcflags & TCF_COMPILE_N_GO); JS_ASSERT_IF(staticLevel != 0, callerFrame); Parser parser(cx, principals, originPrincipals, callerFrame); if (!parser.init(chars, length, filename, lineno, version)) return NULL; TokenStream &tokenStream = parser.tokenStream; BytecodeEmitter bce(&parser, tokenStream.getLineno()); if (!bce.init(cx, TreeContext::USED_AS_TREE_CONTEXT)) return NULL; Probes::compileScriptBegin(cx, filename, lineno); MUST_FLOW_THROUGH("out"); // We can specialize a bit for the given scope chain if that scope chain is the global object. JSObject *globalObj = scopeChain && scopeChain == &scopeChain->global() ? &scopeChain->global() : NULL; JS_ASSERT_IF(globalObj, globalObj->isNative()); JS_ASSERT_IF(globalObj, JSCLASS_HAS_GLOBAL_FLAG_AND_SLOTS(globalObj->getClass())); /* Null script early in case of error, to reduce our code footprint. */ script = NULL; GlobalScope globalScope(cx, globalObj, &bce); bce.flags |= tcflags; bce.setScopeChain(scopeChain); bce.globalScope = &globalScope; if (!SetStaticLevel(&bce, staticLevel)) goto out; /* If this is a direct call to eval, inherit the caller's strictness. */ if (callerFrame && callerFrame->isScriptFrame() && callerFrame->script()->strictModeCode) { bce.flags |= TCF_STRICT_MODE_CODE; tokenStream.setStrictMode(); } #ifdef DEBUG bool savedCallerFun; savedCallerFun = false; #endif if (tcflags & TCF_COMPILE_N_GO) { if (source) { /* * Save eval program source in script->atoms[0] for the * eval cache (see EvalCacheLookup in jsobj.cpp). */ JSAtom *atom = js_AtomizeString(cx, source); jsatomid _; if (!atom || !bce.makeAtomIndex(atom, &_)) goto out; } if (callerFrame && callerFrame->isFunctionFrame()) { /* * An eval script in a caller frame needs to have its enclosing * function captured in case it refers to an upvar, and someone * wishes to decompile it while it's running. */ ObjectBox *funbox = parser.newObjectBox(callerFrame->fun()); if (!funbox) goto out; funbox->emitLink = bce.objectList.lastbox; bce.objectList.lastbox = funbox; bce.objectList.length++; #ifdef DEBUG savedCallerFun = true; #endif } } /* * Inline this->statements to emit as we go to save AST space. We must * generate our script-body blockid since we aren't calling Statements. */ uint32_t bodyid; if (!GenerateBlockId(&bce, bodyid)) goto out; bce.bodyid = bodyid; #if JS_HAS_XML_SUPPORT pn = NULL; bool onlyXML; onlyXML = true; #endif inDirectivePrologue = true; tokenStream.setOctalCharacterEscape(false); for (;;) { tt = tokenStream.peekToken(TSF_OPERAND); if (tt <= TOK_EOF) { if (tt == TOK_EOF) break; JS_ASSERT(tt == TOK_ERROR); goto out; } pn = parser.statement(); if (!pn) goto out; JS_ASSERT(!bce.blockNode); if (inDirectivePrologue && !parser.recognizeDirectivePrologue(pn, &inDirectivePrologue)) goto out; if (!FoldConstants(cx, pn, &bce)) goto out; if (!AnalyzeFunctions(&bce)) goto out; bce.functionList = NULL; if (!EmitTree(cx, &bce, pn)) goto out; #if JS_HAS_XML_SUPPORT if (!pn->isKind(PNK_SEMI) || !pn->pn_kid || !pn->pn_kid->isXMLItem()) onlyXML = false; #endif bce.freeTree(pn); } #if JS_HAS_XML_SUPPORT /* * Prevent XML data theft via <script src="http://victim.com/foo.xml">. * For background, see: * * https://bugzilla.mozilla.org/show_bug.cgi?id=336551 */ if (pn && onlyXML && !callerFrame) { parser.reportErrorNumber(NULL, JSREPORT_ERROR, JSMSG_XML_WHOLE_PROGRAM); goto out; } #endif /* * Nowadays the threaded interpreter needs a stop instruction, so we * do have to emit that here. */ if (Emit1(cx, &bce, JSOP_STOP) < 0) goto out; JS_ASSERT(bce.version() == version); script = JSScript::NewScriptFromEmitter(cx, &bce); if (!script) goto out; JS_ASSERT(script->savedCallerFun == savedCallerFun); if (!DefineGlobals(cx, globalScope, script)) script = NULL; out: Probes::compileScriptEnd(cx, script, filename, lineno); return script; }
/* * Resolve the name of a function. If the function already has a name * listed, then it is skipped. Otherwise an intelligent name is guessed to * assign to the function's displayAtom field */ JSAtom *resolveFun(ParseNode *pn, HandleAtom prefix) { JS_ASSERT(pn != NULL && pn->isKind(PNK_FUNCTION)); RootedFunction fun(cx, pn->pn_funbox->function()); if (nparents == 0) return NULL; StringBuffer buf(cx); this->buf = &buf; /* If the function already has a name, use that */ if (fun->displayAtom() != NULL) { if (prefix == NULL) return fun->atom(); if (!buf.append(prefix) || !buf.append("/") || !buf.append(fun->displayAtom())) return NULL; return buf.finishAtom(); } /* If a prefix is specified, then it is a form of namespace */ if (prefix != NULL && (!buf.append(prefix) || !buf.append("/"))) return NULL; /* Gather all nodes relevant to naming */ ParseNode *toName[MaxParents]; size_t size; ParseNode *assignment = gatherNameable(toName, &size); /* If the function is assigned to something, then that is very relevant */ if (assignment) { if (assignment->isAssignment()) assignment = assignment->pn_left; if (!nameExpression(assignment)) return NULL; } /* * Other than the actual assignment, other relevant nodes to naming are * those in object initializers and then particular nodes marking a * contribution. */ for (int pos = size - 1; pos >= 0; pos--) { ParseNode *node = toName[pos]; if (node->isKind(PNK_COLON)) { ParseNode *left = node->pn_left; if (left->isKind(PNK_NAME) || left->isKind(PNK_STRING)) { if (!appendPropertyReference(left->pn_atom)) return NULL; } else if (left->isKind(PNK_NUMBER)) { if (!appendNumericPropertyReference(left->pn_dval)) return NULL; } } else { /* * Don't have consecutive '<' characters, and also don't start * with a '<' character. */ if (!buf.empty() && *(buf.end() - 1) != '<' && !buf.append("<")) return NULL; } } /* * functions which are "genuinely anonymous" but are contained in some * other namespace are rather considered as "contributing" to the outer * function, so give them a contribution symbol here. */ if (!buf.empty() && *(buf.end() - 1) == '/' && !buf.append("<")) return NULL; if (buf.empty()) return NULL; fun->setGuessedAtom(buf.finishAtom()); return fun->displayAtom(); }
bool ModuleBuilder::processExport(frontend::ParseNode* pn) { MOZ_ASSERT(pn->isKind(PNK_EXPORT) || pn->isKind(PNK_EXPORT_DEFAULT)); MOZ_ASSERT(pn->getArity() == pn->isKind(PNK_EXPORT) ? PN_UNARY : PN_BINARY); bool isDefault = pn->getKind() == PNK_EXPORT_DEFAULT; ParseNode* kid = isDefault ? pn->pn_left : pn->pn_kid; switch (kid->getKind()) { case PNK_EXPORT_SPEC_LIST: MOZ_ASSERT(!isDefault); for (ParseNode* spec = kid->pn_head; spec; spec = spec->pn_next) { MOZ_ASSERT(spec->isKind(PNK_EXPORT_SPEC)); RootedAtom localName(cx_, spec->pn_left->pn_atom); RootedAtom exportName(cx_, spec->pn_right->pn_atom); if (!appendExportEntry(exportName, localName)) return false; } break; case PNK_FUNCTION: { RootedFunction func(cx_, kid->pn_funbox->function()); RootedAtom localName(cx_, func->atom()); RootedAtom exportName(cx_, isDefault ? cx_->names().default_ : localName.get()); if (!appendExportEntry(exportName, localName)) return false; break; } case PNK_CLASS: { const ClassNode& cls = kid->as<ClassNode>(); MOZ_ASSERT(cls.names()); RootedAtom localName(cx_, cls.names()->innerBinding()->pn_atom); RootedAtom exportName(cx_, isDefault ? cx_->names().default_ : localName.get()); if (!appendExportEntry(exportName, localName)) return false; break; } case PNK_VAR: case PNK_CONST: case PNK_LET: { MOZ_ASSERT(kid->isArity(PN_LIST)); for (ParseNode* var = kid->pn_head; var; var = var->pn_next) { if (var->isKind(PNK_ASSIGN)) var = var->pn_left; MOZ_ASSERT(var->isKind(PNK_NAME)); RootedAtom localName(cx_, var->pn_atom); RootedAtom exportName(cx_, isDefault ? cx_->names().default_ : localName.get()); if (!appendExportEntry(exportName, localName)) return false; } break; } default: MOZ_ASSERT(isDefault); RootedAtom localName(cx_, cx_->names().starDefaultStar); RootedAtom exportName(cx_, cx_->names().default_); if (!appendExportEntry(exportName, localName)) return false; break; } return true; }
bool ModuleBuilder::buildAndInit(frontend::ParseNode* moduleNode) { MOZ_ASSERT(moduleNode->isKind(PNK_MODULE)); ParseNode* stmtsNode = moduleNode->pn_expr; MOZ_ASSERT(stmtsNode->isKind(PNK_STATEMENTLIST)); MOZ_ASSERT(stmtsNode->isArity(PN_LIST)); for (ParseNode* pn = stmtsNode->pn_head; pn; pn = pn->pn_next) { switch (pn->getKind()) { case PNK_IMPORT: if (!processImport(pn)) return false; break; case PNK_EXPORT: case PNK_EXPORT_DEFAULT: if (!processExport(pn)) return false; break; case PNK_EXPORT_FROM: if (!processExportFrom(pn)) return false; break; default: break; } } for (const auto& e : exportEntries_) { RootedExportEntry exp(cx_, e); if (!exp->moduleRequest()) { RootedImportEntry importEntry(cx_, importEntryFor(exp->localName())); if (!importEntry) { if (!appendLocalExportEntry(exp)) return false; } else { if (importEntry->importName() == cx_->names().star) { if (!appendLocalExportEntry(exp)) return false; } else { RootedAtom exportName(cx_, exp->exportName()); RootedAtom moduleRequest(cx_, importEntry->moduleRequest()); RootedAtom importName(cx_, importEntry->importName()); RootedExportEntry exportEntry(cx_); exportEntry = ExportEntryObject::create(cx_, exportName, moduleRequest, importName, nullptr); if (!exportEntry || !indirectExportEntries_.append(exportEntry)) return false; } } } else if (exp->importName() == cx_->names().star) { if (!starExportEntries_.append(exp)) return false; } else { if (!indirectExportEntries_.append(exp)) return false; } } RootedArrayObject requestedModules(cx_, createArray<JSAtom*>(requestedModules_)); if (!requestedModules) return false; RootedArrayObject importEntries(cx_, createArray<ImportEntryObject*>(importEntries_)); if (!importEntries) return false; RootedArrayObject localExportEntries(cx_, createArray<ExportEntryObject*>(localExportEntries_)); if (!localExportEntries) return false; RootedArrayObject indirectExportEntries(cx_); indirectExportEntries = createArray<ExportEntryObject*>(indirectExportEntries_); if (!indirectExportEntries) return false; RootedArrayObject starExportEntries(cx_, createArray<ExportEntryObject*>(starExportEntries_)); if (!starExportEntries) return false; module_->initImportExportData(requestedModules, importEntries, localExportEntries, indirectExportEntries, starExportEntries); return true; }