Bug 786339 - Remove two implicit conversions from NULL to false in the JS code; r...
[gecko.git] / js / src / frontend / BytecodeCompiler.cpp
blob147f5836e973903fa00bf46e1492fae59e17075d
1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
2 * vim: set ts=8 sw=4 et tw=99:
4 * This Source Code Form is subject to the terms of the Mozilla Public
5 * License, v. 2.0. If a copy of the MPL was not distributed with this
6 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
8 #include "frontend/BytecodeCompiler.h"
10 #include "jsprobes.h"
12 #include "frontend/BytecodeEmitter.h"
13 #include "frontend/FoldConstants.h"
14 #include "frontend/NameFunctions.h"
15 #include "vm/GlobalObject.h"
17 #include "jsinferinlines.h"
19 #include "frontend/ParseMaps-inl.h"
20 #include "frontend/Parser-inl.h"
21 #include "frontend/SharedContext-inl.h"
23 using namespace js;
24 using namespace js::frontend;
26 static bool
27 CheckLength(JSContext *cx, size_t length)
29 // Note this limit is simply so we can store sourceStart and sourceEnd in
30 // JSScript as 32-bits. It could be lifted fairly easily, since the compiler
31 // is using size_t internally already.
32 if (length > UINT32_MAX) {
33 JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_SOURCE_TOO_LONG);
34 return false;
36 return true;
39 static bool
40 SetSourceMap(JSContext *cx, TokenStream &tokenStream, ScriptSource *ss, JSScript *script)
42 if (tokenStream.hasSourceMap()) {
43 if (!ss->setSourceMap(cx, tokenStream.releaseSourceMap(), script->filename))
44 return false;
46 return true;
49 JSScript *
50 frontend::CompileScript(JSContext *cx, HandleObject scopeChain, StackFrame *callerFrame,
51 const CompileOptions &options,
52 const jschar *chars, size_t length,
53 JSString *source_ /* = NULL */,
54 unsigned staticLevel /* = 0 */)
56 RootedString source(cx, source_);
58 class ProbesManager
60 const char* filename;
61 unsigned lineno;
63 public:
64 ProbesManager(const char *f, unsigned l) : filename(f), lineno(l) {
65 Probes::compileScriptBegin(filename, lineno);
67 ~ProbesManager() { Probes::compileScriptEnd(filename, lineno); }
69 ProbesManager probesManager(options.filename, options.lineno);
72 * The scripted callerFrame can only be given for compile-and-go scripts
73 * and non-zero static level requires callerFrame.
75 JS_ASSERT_IF(callerFrame, options.compileAndGo);
76 JS_ASSERT_IF(staticLevel != 0, callerFrame);
78 if (!CheckLength(cx, length))
79 return NULL;
80 JS_ASSERT_IF(staticLevel != 0, options.sourcePolicy != CompileOptions::LAZY_SOURCE);
81 ScriptSource *ss = cx->new_<ScriptSource>();
82 if (!ss)
83 return NULL;
84 ScriptSourceHolder ssh(cx->runtime, ss);
85 SourceCompressionToken sct(cx);
86 switch (options.sourcePolicy) {
87 case CompileOptions::SAVE_SOURCE:
88 if (!ss->setSourceCopy(cx, chars, length, false, &sct))
89 return NULL;
90 break;
91 case CompileOptions::LAZY_SOURCE:
92 ss->setSourceRetrievable();
93 break;
94 case CompileOptions::NO_SOURCE:
95 break;
98 Parser parser(cx, options, chars, length, /* foldConstants = */ true);
99 if (!parser.init())
100 return NULL;
101 parser.sct = &sct;
103 SharedContext sc(cx, scopeChain, /* fun = */ NULL, /* funbox = */ NULL, StrictModeFromContext(cx));
105 ParseContext pc(&parser, &sc, staticLevel, /* bodyid = */ 0);
106 if (!pc.init())
107 return NULL;
109 bool savedCallerFun = options.compileAndGo && callerFrame && callerFrame->isFunctionFrame();
110 Rooted<JSScript*> script(cx, JSScript::Create(cx, NullPtr(), savedCallerFun,
111 options, staticLevel, ss, 0, length));
112 if (!script)
113 return NULL;
115 // Global/eval script bindings are always empty (all names are added to the
116 // scope dynamically via JSOP_DEFFUN/VAR).
117 if (!script->bindings.initWithTemporaryStorage(cx, 0, 0, NULL))
118 return NULL;
120 // We can specialize a bit for the given scope chain if that scope chain is the global object.
121 JSObject *globalScope = scopeChain && scopeChain == &scopeChain->global() ? (JSObject*) scopeChain : NULL;
122 JS_ASSERT_IF(globalScope, globalScope->isNative());
123 JS_ASSERT_IF(globalScope, JSCLASS_HAS_GLOBAL_FLAG_AND_SLOTS(globalScope->getClass()));
125 BytecodeEmitter bce(/* parent = */ NULL, &parser, &sc, script, callerFrame, !!globalScope,
126 options.lineno, options.selfHostingMode);
127 if (!bce.init())
128 return NULL;
130 /* If this is a direct call to eval, inherit the caller's strictness. */
131 if (callerFrame && callerFrame->script()->strictModeCode)
132 sc.strictModeState = StrictMode::STRICT;
134 if (options.compileAndGo) {
135 if (source) {
137 * Save eval program source in script->atoms[0] for the
138 * eval cache (see EvalCacheLookup in jsobj.cpp).
140 JSAtom *atom = AtomizeString(cx, source);
141 jsatomid _;
142 if (!atom || !bce.makeAtomIndex(atom, &_))
143 return NULL;
146 if (callerFrame && callerFrame->isFunctionFrame()) {
148 * An eval script in a caller frame needs to have its enclosing
149 * function captured in case it refers to an upvar, and someone
150 * wishes to decompile it while it's running.
152 ObjectBox *funbox = parser.newObjectBox(callerFrame->fun());
153 if (!funbox)
154 return NULL;
155 funbox->emitLink = bce.objectList.lastbox;
156 bce.objectList.lastbox = funbox;
157 bce.objectList.length++;
161 ParseNode *pn;
162 #if JS_HAS_XML_SUPPORT
163 pn = NULL;
164 bool onlyXML;
165 onlyXML = true;
166 #endif
168 TokenStream &tokenStream = parser.tokenStream;
170 ParseNode *stringsAtStart = ListNode::create(PNK_STATEMENTLIST, &parser);
171 if (!stringsAtStart)
172 return NULL;
173 stringsAtStart->makeEmpty();
174 bool ok = parser.processDirectives(stringsAtStart) && EmitTree(cx, &bce, stringsAtStart);
175 parser.freeTree(stringsAtStart);
176 if (!ok)
177 return NULL;
179 JS_ASSERT(sc.strictModeState != StrictMode::UNKNOWN);
180 for (;;) {
181 TokenKind tt = tokenStream.peekToken(TSF_OPERAND);
182 if (tt <= TOK_EOF) {
183 if (tt == TOK_EOF)
184 break;
185 JS_ASSERT(tt == TOK_ERROR);
186 return NULL;
189 pn = parser.statement();
190 if (!pn)
191 return NULL;
193 if (!FoldConstants(cx, pn, &parser))
194 return NULL;
195 if (!NameFunctions(cx, pn))
196 return NULL;
198 pc.functionList = NULL;
200 if (!EmitTree(cx, &bce, pn))
201 return NULL;
203 #if JS_HAS_XML_SUPPORT
204 if (!pn->isKind(PNK_SEMI) || !pn->pn_kid || !pn->pn_kid->isXMLItem())
205 onlyXML = false;
206 #endif
207 parser.freeTree(pn);
210 if (!SetSourceMap(cx, tokenStream, ss, script))
211 return NULL;
213 #if JS_HAS_XML_SUPPORT
215 * Prevent XML data theft via <script src="http://victim.com/foo.xml">.
216 * For background, see:
218 * https://bugzilla.mozilla.org/show_bug.cgi?id=336551
220 if (pn && onlyXML && !callerFrame) {
221 parser.reportError(NULL, JSMSG_XML_WHOLE_PROGRAM);
222 return NULL;
224 #endif
226 // It's an error to use |arguments| in a function that has a rest parameter.
227 if (callerFrame && callerFrame->isFunctionFrame() && callerFrame->fun()->hasRest()) {
228 PropertyName *arguments = cx->runtime->atomState.argumentsAtom;
229 for (AtomDefnRange r = pc.lexdeps->all(); !r.empty(); r.popFront()) {
230 if (r.front().key() == arguments) {
231 parser.reportError(NULL, JSMSG_ARGUMENTS_AND_REST);
232 return NULL;
238 * Nowadays the threaded interpreter needs a stop instruction, so we
239 * do have to emit that here.
241 if (Emit1(cx, &bce, JSOP_STOP) < 0)
242 return NULL;
244 if (!JSScript::fullyInitFromEmitter(cx, script, &bce))
245 return NULL;
247 bce.tellDebuggerAboutCompiledScript(cx);
249 return script;
252 // Compile a JS function body, which might appear as the value of an event
253 // handler attribute in an HTML <INPUT> tag, or in a Function() constructor.
254 bool
255 frontend::CompileFunctionBody(JSContext *cx, HandleFunction fun, CompileOptions options,
256 const AutoNameVector &formals, const jschar *chars, size_t length)
258 if (!CheckLength(cx, length))
259 return false;
260 ScriptSource *ss = cx->new_<ScriptSource>();
261 if (!ss)
262 return false;
263 ScriptSourceHolder ssh(cx->runtime, ss);
264 SourceCompressionToken sct(cx);
265 JS_ASSERT(options.sourcePolicy != CompileOptions::LAZY_SOURCE);
266 if (options.sourcePolicy == CompileOptions::SAVE_SOURCE) {
267 if (!ss->setSourceCopy(cx, chars, length, true, &sct))
268 return false;
271 options.setCompileAndGo(false);
272 Parser parser(cx, options, chars, length, /* foldConstants = */ true);
273 if (!parser.init())
274 return false;
275 parser.sct = &sct;
277 JS_ASSERT(fun);
278 SharedContext funsc(cx, /* scopeChain = */ NULL, fun, /* funbox = */ NULL,
279 StrictModeFromContext(cx));
280 fun->setArgCount(formals.length());
282 unsigned staticLevel = 0;
283 ParseContext funpc(&parser, &funsc, staticLevel, /* bodyid = */ 0);
284 if (!funpc.init())
285 return false;
287 /* FIXME: make Function format the source for a function definition. */
288 ParseNode *fn = FunctionNode::create(PNK_NAME, &parser);
289 if (!fn)
290 return false;
292 fn->pn_body = NULL;
293 fn->pn_cookie.makeFree();
295 ParseNode *argsbody = ListNode::create(PNK_ARGSBODY, &parser);
296 if (!argsbody)
297 return false;
298 argsbody->setOp(JSOP_NOP);
299 argsbody->makeEmpty();
300 fn->pn_body = argsbody;
302 for (unsigned i = 0; i < formals.length(); i++) {
303 if (!DefineArg(&parser, fn, formals[i]))
304 return false;
308 * After we're done parsing, we must fold constants, analyze any nested
309 * functions, and generate code for this function, including a stop opcode
310 * at the end.
312 ParseNode *pn = parser.functionBody(Parser::StatementListBody);
313 if (!pn)
314 return false;
316 if (!parser.tokenStream.matchToken(TOK_EOF)) {
317 parser.reportError(NULL, JSMSG_SYNTAX_ERROR);
318 return false;
321 if (!FoldConstants(cx, pn, &parser))
322 return false;
324 Rooted<JSScript*> script(cx, JSScript::Create(cx, NullPtr(), false, options,
325 staticLevel, ss, 0, length));
326 if (!script)
327 return false;
329 if (!funpc.generateFunctionBindings(cx, &script->bindings))
330 return false;
332 BytecodeEmitter funbce(/* parent = */ NULL, &parser, &funsc, script, /* callerFrame = */ NULL,
333 /* hasGlobalScope = */ false, options.lineno);
334 if (!funbce.init())
335 return false;
337 if (!NameFunctions(cx, pn))
338 return false;
340 if (fn->pn_body) {
341 JS_ASSERT(fn->pn_body->isKind(PNK_ARGSBODY));
342 fn->pn_body->append(pn);
343 fn->pn_body->pn_pos = pn->pn_pos;
344 pn = fn->pn_body;
347 if (!SetSourceMap(cx, parser.tokenStream, ss, script))
348 return false;
350 if (!EmitFunctionScript(cx, &funbce, pn))
351 return false;
353 return true;