~ubuntu-branches/ubuntu/saucy/mozjs17/saucy

« back to all changes in this revision

Viewing changes to js/src/frontend/BytecodeCompiler.cpp

  • Committer: Package Import Robot
  • Author(s): Rico Tzschichholz
  • Date: 2013-05-25 12:24:23 UTC
  • Revision ID: package-import@ubuntu.com-20130525122423-zmxucrhtensw90xy
Tags: upstream-17.0.0
ImportĀ upstreamĀ versionĀ 17.0.0

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
 
2
 * vim: set ts=8 sw=4 et tw=99:
 
3
 *
 
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/. */
 
7
 
 
8
#include "frontend/BytecodeCompiler.h"
 
9
 
 
10
#include "jsprobes.h"
 
11
 
 
12
#include "frontend/BytecodeEmitter.h"
 
13
#include "frontend/FoldConstants.h"
 
14
#include "frontend/NameFunctions.h"
 
15
#include "vm/GlobalObject.h"
 
16
 
 
17
#include "jsinferinlines.h"
 
18
 
 
19
#include "frontend/ParseMaps-inl.h"
 
20
#include "frontend/Parser-inl.h"
 
21
#include "frontend/SharedContext-inl.h"
 
22
 
 
23
using namespace js;
 
24
using namespace js::frontend;
 
25
 
 
26
static bool
 
27
CheckLength(JSContext *cx, size_t length)
 
28
{
 
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;
 
35
    }
 
36
    return true;
 
37
}
 
38
 
 
39
static bool
 
40
SetSourceMap(JSContext *cx, TokenStream &tokenStream, ScriptSource *ss, JSScript *script)
 
41
{
 
42
    if (tokenStream.hasSourceMap()) {
 
43
        if (!ss->setSourceMap(cx, tokenStream.releaseSourceMap(), script->filename))
 
44
            return false;
 
45
    }
 
46
    return true;
 
47
}
 
48
 
 
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 */)
 
55
{
 
56
    RootedString source(cx, source_);
 
57
 
 
58
    class ProbesManager
 
59
    {
 
60
        const char* filename;
 
61
        unsigned lineno;
 
62
 
 
63
      public:
 
64
        ProbesManager(const char *f, unsigned l) : filename(f), lineno(l) {
 
65
            Probes::compileScriptBegin(filename, lineno);
 
66
        }
 
67
        ~ProbesManager() { Probes::compileScriptEnd(filename, lineno); }
 
68
    };
 
69
    ProbesManager probesManager(options.filename, options.lineno);
 
70
 
 
71
    /*
 
72
     * The scripted callerFrame can only be given for compile-and-go scripts
 
73
     * and non-zero static level requires callerFrame.
 
74
     */
 
75
    JS_ASSERT_IF(callerFrame, options.compileAndGo);
 
76
    JS_ASSERT_IF(staticLevel != 0, callerFrame);
 
77
 
 
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;
 
96
    }
 
97
 
 
98
    Parser parser(cx, options, chars, length, /* foldConstants = */ true);
 
99
    if (!parser.init())
 
100
        return NULL;
 
101
    parser.sct = &sct;
 
102
 
 
103
    SharedContext sc(cx, scopeChain, /* fun = */ NULL, /* funbox = */ NULL, StrictModeFromContext(cx));
 
104
 
 
105
    ParseContext pc(&parser, &sc, staticLevel, /* bodyid = */ 0);
 
106
    if (!pc.init())
 
107
        return NULL;
 
108
 
 
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;
 
114
 
 
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;
 
119
 
 
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()));
 
124
 
 
125
    BytecodeEmitter bce(/* parent = */ NULL, &parser, &sc, script, callerFrame, !!globalScope,
 
126
                        options.lineno, options.selfHostingMode);
 
127
    if (!bce.init())
 
128
        return NULL;
 
129
 
 
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;
 
133
 
 
134
    if (options.compileAndGo) {
 
135
        if (source) {
 
136
            /*
 
137
             * Save eval program source in script->atoms[0] for the
 
138
             * eval cache (see EvalCacheLookup in jsobj.cpp).
 
139
             */
 
140
            JSAtom *atom = AtomizeString(cx, source);
 
141
            jsatomid _;
 
142
            if (!atom || !bce.makeAtomIndex(atom, &_))
 
143
                return NULL;
 
144
        }
 
145
 
 
146
        if (callerFrame && callerFrame->isFunctionFrame()) {
 
147
            /*
 
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.
 
151
             */
 
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++;
 
158
        }
 
159
    }
 
160
 
 
161
    ParseNode *pn;
 
162
#if JS_HAS_XML_SUPPORT
 
163
    pn = NULL;
 
164
    bool onlyXML;
 
165
    onlyXML = true;
 
166
#endif
 
167
 
 
168
    TokenStream &tokenStream = parser.tokenStream;
 
169
    {
 
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;
 
178
    }
 
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;
 
187
        }
 
188
 
 
189
        pn = parser.statement();
 
190
        if (!pn)
 
191
            return NULL;
 
192
 
 
193
        if (!FoldConstants(cx, pn, &parser))
 
194
            return NULL;
 
195
        if (!NameFunctions(cx, pn))
 
196
            return NULL;
 
197
 
 
198
        pc.functionList = NULL;
 
199
 
 
200
        if (!EmitTree(cx, &bce, pn))
 
201
            return NULL;
 
202
 
 
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);
 
208
    }
 
209
 
 
210
    if (!SetSourceMap(cx, tokenStream, ss, script))
 
211
        return NULL;
 
212
 
 
213
#if JS_HAS_XML_SUPPORT
 
214
    /*
 
215
     * Prevent XML data theft via <script src="http://victim.com/foo.xml">.
 
216
     * For background, see:
 
217
     *
 
218
     * https://bugzilla.mozilla.org/show_bug.cgi?id=336551
 
219
     */
 
220
    if (pn && onlyXML && !callerFrame) {
 
221
        parser.reportError(NULL, JSMSG_XML_WHOLE_PROGRAM);
 
222
        return NULL;
 
223
    }
 
224
#endif
 
225
 
 
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;
 
233
            }
 
234
        }
 
235
    }
 
236
 
 
237
    /*
 
238
     * Nowadays the threaded interpreter needs a stop instruction, so we
 
239
     * do have to emit that here.
 
240
     */
 
241
    if (Emit1(cx, &bce, JSOP_STOP) < 0)
 
242
        return NULL;
 
243
 
 
244
    if (!JSScript::fullyInitFromEmitter(cx, script, &bce))
 
245
        return NULL;
 
246
 
 
247
    bce.tellDebuggerAboutCompiledScript(cx);
 
248
 
 
249
    return script;
 
250
}
 
251
 
 
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)
 
257
{
 
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;
 
269
    }
 
270
 
 
271
    options.setCompileAndGo(false);
 
272
    Parser parser(cx, options, chars, length, /* foldConstants = */ true);
 
273
    if (!parser.init())
 
274
        return false;
 
275
    parser.sct = &sct;
 
276
 
 
277
    JS_ASSERT(fun);
 
278
    SharedContext funsc(cx, /* scopeChain = */ NULL, fun, /* funbox = */ NULL,
 
279
                        StrictModeFromContext(cx));
 
280
    fun->setArgCount(formals.length());
 
281
 
 
282
    unsigned staticLevel = 0;
 
283
    ParseContext funpc(&parser, &funsc, staticLevel, /* bodyid = */ 0);
 
284
    if (!funpc.init())
 
285
        return false;
 
286
 
 
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;
 
291
 
 
292
    fn->pn_body = NULL;
 
293
    fn->pn_cookie.makeFree();
 
294
 
 
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;
 
301
 
 
302
    for (unsigned i = 0; i < formals.length(); i++) {
 
303
        if (!DefineArg(&parser, fn, formals[i]))
 
304
            return false;
 
305
    }
 
306
 
 
307
    /*
 
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.
 
311
     */
 
312
    ParseNode *pn = parser.functionBody(Parser::StatementListBody);
 
313
    if (!pn) 
 
314
        return false;
 
315
 
 
316
    if (!parser.tokenStream.matchToken(TOK_EOF)) {
 
317
        parser.reportError(NULL, JSMSG_SYNTAX_ERROR);
 
318
        return false;
 
319
    }
 
320
 
 
321
    if (!FoldConstants(cx, pn, &parser))
 
322
        return false;
 
323
 
 
324
    Rooted<JSScript*> script(cx, JSScript::Create(cx, NullPtr(), false, options,
 
325
                                                  staticLevel, ss, 0, length));
 
326
    if (!script)
 
327
        return false;
 
328
 
 
329
    if (!funpc.generateFunctionBindings(cx, &script->bindings))
 
330
        return false;
 
331
 
 
332
    BytecodeEmitter funbce(/* parent = */ NULL, &parser, &funsc, script, /* callerFrame = */ NULL,
 
333
                           /* hasGlobalScope = */ false, options.lineno);
 
334
    if (!funbce.init())
 
335
        return false;
 
336
 
 
337
    if (!NameFunctions(cx, pn))
 
338
        return NULL;
 
339
 
 
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;
 
345
    }
 
346
 
 
347
    if (!SetSourceMap(cx, parser.tokenStream, ss, script))
 
348
        return false;
 
349
 
 
350
    if (!EmitFunctionScript(cx, &funbce, pn))
 
351
        return false;
 
352
 
 
353
    return true;
 
354
}