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"
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"
24 using namespace js::frontend
;
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
);
40 SetSourceMap(JSContext
*cx
, TokenStream
&tokenStream
, ScriptSource
*ss
, JSScript
*script
)
42 if (tokenStream
.hasSourceMap()) {
43 if (!ss
->setSourceMap(cx
, tokenStream
.releaseSourceMap(), script
->filename
))
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_
);
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
))
80 JS_ASSERT_IF(staticLevel
!= 0, options
.sourcePolicy
!= CompileOptions::LAZY_SOURCE
);
81 ScriptSource
*ss
= cx
->new_
<ScriptSource
>();
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
))
91 case CompileOptions::LAZY_SOURCE
:
92 ss
->setSourceRetrievable();
94 case CompileOptions::NO_SOURCE
:
98 Parser
parser(cx
, options
, chars
, length
, /* foldConstants = */ true);
103 SharedContext
sc(cx
, scopeChain
, /* fun = */ NULL
, /* funbox = */ NULL
, StrictModeFromContext(cx
));
105 ParseContext
pc(&parser
, &sc
, staticLevel
, /* bodyid = */ 0);
109 bool savedCallerFun
= options
.compileAndGo
&& callerFrame
&& callerFrame
->isFunctionFrame();
110 Rooted
<JSScript
*> script(cx
, JSScript::Create(cx
, NullPtr(), savedCallerFun
,
111 options
, staticLevel
, ss
, 0, length
));
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
))
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
);
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
) {
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
);
142 if (!atom
|| !bce
.makeAtomIndex(atom
, &_
))
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());
155 funbox
->emitLink
= bce
.objectList
.lastbox
;
156 bce
.objectList
.lastbox
= funbox
;
157 bce
.objectList
.length
++;
162 #if JS_HAS_XML_SUPPORT
168 TokenStream
&tokenStream
= parser
.tokenStream
;
170 ParseNode
*stringsAtStart
= ListNode::create(PNK_STATEMENTLIST
, &parser
);
173 stringsAtStart
->makeEmpty();
174 bool ok
= parser
.processDirectives(stringsAtStart
) && EmitTree(cx
, &bce
, stringsAtStart
);
175 parser
.freeTree(stringsAtStart
);
179 JS_ASSERT(sc
.strictModeState
!= StrictMode::UNKNOWN
);
181 TokenKind tt
= tokenStream
.peekToken(TSF_OPERAND
);
185 JS_ASSERT(tt
== TOK_ERROR
);
189 pn
= parser
.statement();
193 if (!FoldConstants(cx
, pn
, &parser
))
195 if (!NameFunctions(cx
, pn
))
198 pc
.functionList
= NULL
;
200 if (!EmitTree(cx
, &bce
, pn
))
203 #if JS_HAS_XML_SUPPORT
204 if (!pn
->isKind(PNK_SEMI
) || !pn
->pn_kid
|| !pn
->pn_kid
->isXMLItem())
210 if (!SetSourceMap(cx
, tokenStream
, ss
, script
))
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
);
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
);
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)
244 if (!JSScript::fullyInitFromEmitter(cx
, script
, &bce
))
247 bce
.tellDebuggerAboutCompiledScript(cx
);
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.
255 frontend::CompileFunctionBody(JSContext
*cx
, HandleFunction fun
, CompileOptions options
,
256 const AutoNameVector
&formals
, const jschar
*chars
, size_t length
)
258 if (!CheckLength(cx
, length
))
260 ScriptSource
*ss
= cx
->new_
<ScriptSource
>();
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
))
271 options
.setCompileAndGo(false);
272 Parser
parser(cx
, options
, chars
, length
, /* foldConstants = */ true);
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);
287 /* FIXME: make Function format the source for a function definition. */
288 ParseNode
*fn
= FunctionNode::create(PNK_NAME
, &parser
);
293 fn
->pn_cookie
.makeFree();
295 ParseNode
*argsbody
= ListNode::create(PNK_ARGSBODY
, &parser
);
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
]))
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
312 ParseNode
*pn
= parser
.functionBody(Parser::StatementListBody
);
316 if (!parser
.tokenStream
.matchToken(TOK_EOF
)) {
317 parser
.reportError(NULL
, JSMSG_SYNTAX_ERROR
);
321 if (!FoldConstants(cx
, pn
, &parser
))
324 Rooted
<JSScript
*> script(cx
, JSScript::Create(cx
, NullPtr(), false, options
,
325 staticLevel
, ss
, 0, length
));
329 if (!funpc
.generateFunctionBindings(cx
, &script
->bindings
))
332 BytecodeEmitter
funbce(/* parent = */ NULL
, &parser
, &funsc
, script
, /* callerFrame = */ NULL
,
333 /* hasGlobalScope = */ false, options
.lineno
);
337 if (!NameFunctions(cx
, pn
))
341 JS_ASSERT(fn
->pn_body
->isKind(PNK_ARGSBODY
));
342 fn
->pn_body
->append(pn
);
343 fn
->pn_body
->pn_pos
= pn
->pn_pos
;
347 if (!SetSourceMap(cx
, parser
.tokenStream
, ss
, script
))
350 if (!EmitFunctionScript(cx
, &funbce
, pn
))