JSScript * frontend::CompileScript(JSContext *cx, HandleObject scopeChain, StackFrame *callerFrame, const CompileOptions &options, const jschar *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 NULL; ScriptSource *ss = cx->new_<ScriptSource>(); if (!ss) return NULL; AutoAttachToRuntime attacher(cx->runtime, ss); SourceCompressionToken sct(cx); if (!cx->hasRunOption(JSOPTION_ONLY_CNG_SOURCE) || options.compileAndGo) { if (!ss->setSourceCopy(cx, chars, length, false, &sct)) return NULL; } Parser parser(cx, options, chars, length, /* foldConstants = */ true); if (!parser.init()) return NULL; parser.sct = &sct; SharedContext sc(cx, scopeChain, /* fun = */ NULL, /* funbox = */ NULL, StrictModeFromContext(cx)); TreeContext tc(&parser, &sc, staticLevel, /* bodyid = */ 0); if (!tc.init()) return 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 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, options.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 (options.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.hasBinding(cx, arguments)); } /* * 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, 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; } bool canLazilyParse = CanLazilyParse(cx, options); Maybe<Parser<SyntaxParseHandler> > syntaxParser; if (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, canLazilyParse ? &syntaxParser.ref() : NULL, NULL); parser.sct = &sct; JS_ASSERT(fun); JS_ASSERT(fun->isTenured()); 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()->is<GlobalObject>(), 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; }
// 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, Bindings *bindings, const jschar *chars, size_t length) { if (!CheckLength(cx, length)) return NULL; ScriptSource *ss = cx->new_<ScriptSource>(); if (!ss) return NULL; AutoAttachToRuntime attacher(cx->runtime, ss); SourceCompressionToken sct(cx); if (!ss->setSourceCopy(cx, chars, length, true, &sct)) return NULL; options.setCompileAndGo(false); Parser parser(cx, options, chars, length, /* foldConstants = */ true); if (!parser.init()) return false; parser.sct = &sct; JS_ASSERT(fun); SharedContext funsc(cx, /* scopeChain = */ NULL, fun, /* funbox = */ NULL, StrictModeFromContext(cx)); funsc.bindings.transfer(bindings); fun->setArgCount(funsc.bindings.numArgs()); unsigned staticLevel = 0; TreeContext funtc(&parser, &funsc, staticLevel, /* bodyid = */ 0); if (!funtc.init()) return false; Rooted<JSScript*> script(cx, JSScript::Create(cx, NullPtr(), false, options, staticLevel, ss, 0, length)); if (!script) return false; StackFrame *nullCallerFrame = NULL; BytecodeEmitter funbce(/* parent = */ NULL, &parser, &funsc, script, nullCallerFrame, /* hasGlobalScope = */ false, options.lineno); if (!funbce.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; unsigned nargs = fun->nargs; if (nargs) { /* * NB: do not use AutoLocalNameArray because it will release space * allocated from cx->tempLifoAlloc by DefineArg. */ BindingVector names(cx); if (!GetOrderedBindings(cx, funsc.bindings, &names)) return false; for (unsigned i = 0; i < nargs; i++) { if (!DefineArg(fn, names[i].maybeName, 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.reportError(NULL, JSMSG_SYNTAX_ERROR); return false; } if (!FoldConstants(cx, pn, &parser)) return false; if (!AnalyzeFunctions(&parser, nullCallerFrame)) 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; }
// 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(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_FUNCTION, &parser); if (!fn) return false; fn->pn_body = NULL; fn->pn_funbox = 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 = */ NullFramePtr(), /* 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; }
// 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); SharedContext funsc(cx, /* scopeChain = */ NULL, funbox, sms); fun->setArgCount(formals.length()); unsigned staticLevel = 0; ParseContext funpc(&parser, &funsc, 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; if (!funpc.generateFunctionBindings(cx, &script->bindings)) return false; BytecodeEmitter funbce(/* parent = */ NULL, &parser, &funsc, 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; }
/* * Compile a JS function body, which might appear as the value of an event * handler attribute in an HTML <INPUT> tag. */ 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) { 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)) { fn = NULL; } else { for (unsigned i = 0; i < nargs; i++) { if (!DefineArg(fn, names[i].maybeAtom, i, &parser)) { fn = NULL; break; } } } } } /* * 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 = fn ? parser.functionBody(Parser::StatementListBody) : NULL; if (pn) { if (!parser.tokenStream.matchToken(TOK_EOF)) { parser.reportErrorNumber(NULL, JSREPORT_ERROR, JSMSG_SYNTAX_ERROR); pn = NULL; } else if (!FoldConstants(cx, pn, &parser)) { /* FoldConstants reported the error already. */ pn = NULL; } else if (!AnalyzeFunctions(&parser)) { pn = NULL; } else { 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)) pn = NULL; } } return pn != NULL; }
/* * 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; }
/* * 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; }