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))