DXR is a code search and navigation tool aimed at making sense of large projects. It supports full-text and regex searches as well as structural queries.

Implementation

Mercurial (b6d82b1a6b02)

VCS Links

Line Code
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*-
 * vim: set ts=8 sts=2 et sw=2 tw=80:
 * This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

#ifndef frontend_FunctionEmitter_h
#define frontend_FunctionEmitter_h

#include "mozilla/Attributes.h"  // MOZ_STACK_CLASS, MOZ_MUST_USE

#include <stdint.h>  // uint16_t, uint32_t

#include "frontend/DefaultEmitter.h"       // DefaultEmitter
#include "frontend/DestructuringFlavor.h"  // DestructuringFlavor
#include "frontend/EmitterScope.h"         // EmitterScope
#include "frontend/SharedContext.h"        // FunctionBox
#include "frontend/TDZCheckCache.h"        // TDZCheckCache
#include "frontend/TryEmitter.h"           // TryEmitter
#include "gc/Rooting.h"                    // JS::Rooted, JS::Handle
#include "vm/BytecodeUtil.h"               // JSOp
#include "vm/JSAtom.h"                     // JSAtom
#include "vm/JSFunction.h"                 // JSFunction

namespace js {
namespace frontend {

struct BytecodeEmitter;

// Class for emitting function declaration, expression, or method etc.
//
// This class handles the enclosing script's part (function object creation,
// declaration, etc). The content of the function script is handled by
// FunctionScriptEmitter and FunctionParamsEmitter.
//
// Usage: (check for the return value is omitted for simplicity)
//
//   `function f() {}`, non lazy script
//     FunctionEmitter fe(this, funbox_for_f, FunctionSyntaxKind::Statement,
//                        FunctionEmitter::IsHoisted::No);
//     fe.prepareForNonLazy();
//
//     // Emit script with FunctionScriptEmitter here.
//     ...
//
//     fe.emitNonLazyEnd();
//
//   `function f() {}`, lazy script
//     FunctionEmitter fe(this, funbox_for_f, FunctionSyntaxKind::Statement,
//                        FunctionEmitter::IsHoisted::No);
//     fe.emitLazy();
//
//   `function f() {}`, emitting hoisted function again
//     // See emitAgain comment for more details
//     FunctionEmitter fe(this, funbox_for_f, FunctionSyntaxKind::Statement,
//                        FunctionEmitter::IsHoisted::Yes);
//     fe.emitAgain();
//
//   `function f() { "use asm"; }`
//     FunctionEmitter fe(this, funbox_for_f, FunctionSyntaxKind::Statement,
//                        FunctionEmitter::IsHoisted::No);
//     fe.emitAsmJSModule();
//
class MOZ_STACK_CLASS FunctionEmitter {
 public:
  enum class IsHoisted { No, Yes };

 private:
  BytecodeEmitter* bce_;

  FunctionBox* funbox_;

  // Function linked from funbox_.
  JS::Rooted<JSFunction*> fun_;

  // Function's explicit name.
  JS::Rooted<JSAtom*> name_;

  FunctionSyntaxKind syntaxKind_;
  IsHoisted isHoisted_;

#ifdef DEBUG
  // The state of this emitter.
  //
  // +-------+
  // | Start |-+
  // +-------+ |
  //           |
  //   +-------+
  //   |
  //   | [non-lazy function]
  //   |   prepareForNonLazy  +---------+ emitNonLazyEnd     +-----+
  //   +--------------------->| NonLazy |---------------->+->| End |
  //   |                      +---------+                 ^  +-----+
  //   |                                                  |
  //   | [lazy function]                                  |
  //   |   emitLazy                                       |
  //   +------------------------------------------------->+
  //   |                                                  ^
  //   | [emitting hoisted function again]                |
  //   |   emitAgain                                      |
  //   +------------------------------------------------->+
  //   |                                                  ^
  //   | [asm.js module]                                  |
  //   |   emitAsmJSModule                                |
  //   +--------------------------------------------------+
  //
  enum class State {
    // The initial state.
    Start,

    // After calling prepareForNonLazy.
    NonLazy,

    // After calling emitNonLazyEnd, emitLazy, emitAgain, or emitAsmJSModule.
    End
  };
  State state_ = State::Start;
#endif

 public:
  FunctionEmitter(BytecodeEmitter* bce, FunctionBox* funbox,
                  FunctionSyntaxKind syntaxKind, IsHoisted isHoisted);

  MOZ_MUST_USE bool prepareForNonLazy();
  MOZ_MUST_USE bool emitNonLazyEnd();

  MOZ_MUST_USE bool emitLazy();

  MOZ_MUST_USE bool emitAgain();

  MOZ_MUST_USE bool emitAsmJSModule();

 private:
  // Common code for non-lazy and lazy functions.
  MOZ_MUST_USE bool interpretedCommon();

  // Emit the function declaration, expression, method etc.
  // This leaves function object on the stack for expression etc,
  // and doesn't for declaration.
  MOZ_MUST_USE bool emitFunction();

  // Helper methods used by emitFunction for each case.
  // `index` is the object index of the function.
  MOZ_MUST_USE bool emitNonHoisted(unsigned index);
  MOZ_MUST_USE bool emitHoisted(unsigned index);
  MOZ_MUST_USE bool emitTopLevelFunction(unsigned index);
  MOZ_MUST_USE bool emitNewTargetForArrow();
};

// Class for emitting function script.
// Parameters are handled by FunctionParamsEmitter.
//
// Usage: (check for the return value is omitted for simplicity)
//
//   `function f(a) { expr }`
//     FunctionScriptEmitter fse(this, funbox_for_f,
//                               Some(offset_of_opening_paren),
//                               Some(offset_of_closing_brace));
//     fse.prepareForParameters();
//
//     // Emit parameters with FunctionParamsEmitter here.
//     ...
//
//     fse.prepareForBody();
//     emit(expr);
//     fse.emitEnd();
//
//     // Do NameFunctions operation here if needed.
//
//     fse.initScript();
//
class MOZ_STACK_CLASS FunctionScriptEmitter {
 private:
  BytecodeEmitter* bce_;

  FunctionBox* funbox_;

  // Scope for the function name for a named lambda.
  // None for anonymous function.
  mozilla::Maybe<EmitterScope> namedLambdaEmitterScope_;

  // Scope for function body.
  mozilla::Maybe<EmitterScope> functionEmitterScope_;

  // Scope for the extra body var.
  // None if `funbox_->hasExtraBodyVarScope() == false`.
  mozilla::Maybe<EmitterScope> extraBodyVarEmitterScope_;

  mozilla::Maybe<TDZCheckCache> tdzCache_;

  // try-catch block for async function parameter and body.
  mozilla::Maybe<TryEmitter> rejectTryCatch_;

  // See the comment for constructor.
  mozilla::Maybe<uint32_t> paramStart_;
  mozilla::Maybe<uint32_t> bodyEnd_;

#ifdef DEBUG
  // The state of this emitter.
  //
  // +-------+ prepareForParameters  +------------+
  // | Start |---------------------->| Parameters |-+
  // +-------+                       +------------+ |
  //                                                |
  //   +--------------------------------------------+
  //   |
  //   | prepareForBody  +------+ emitEndBody  +---------+
  //   +---------------->| Body |------------->| EndBody |-+
  //                     +------+              +---------+ |
  //                                                       |
  //     +-------------------------------------------------+
  //     |
  //     | initScript  +-----+
  //     +------------>| End |
  //                   +-----+
  enum class State {
    // The initial state.
    Start,

    // After calling prepareForParameters.
    Parameters,

    // After calling prepareForBody.
    Body,

    // After calling emitEndBody.
    EndBody,

    // After calling initScript.
    End
  };
  State state_ = State::Start;
#endif

 public:
  // Parameters are the offset in the source code for each character below:
  //
  //   function f(a, b, ...c) { ... }
  //             ^                  ^
  //             |                  |
  //             paramStart         bodyEnd
  //
  // Can be Nothing() if not available.
  FunctionScriptEmitter(BytecodeEmitter* bce, FunctionBox* funbox,
                        const mozilla::Maybe<uint32_t>& paramStart,
                        const mozilla::Maybe<uint32_t>& bodyEnd)
      : bce_(bce),
        funbox_(funbox),
        paramStart_(paramStart),
        bodyEnd_(bodyEnd) {}

  MOZ_MUST_USE bool prepareForParameters();
  MOZ_MUST_USE bool prepareForBody();
  MOZ_MUST_USE bool emitEndBody();

  // Initialize JSScript for this function.
  // WARNING: There shouldn't be any fallible operation for the function
  //          compilation after `initScript` call.
  //          See the comment inside JSScript::fullyInitFromEmitter for
  //          more details.
  MOZ_MUST_USE bool initScript();

 private:
  MOZ_MUST_USE bool emitExtraBodyVarScope();

  // Async functions have implicit try-catch blocks to convert exceptions
  // into promise rejections.
  MOZ_MUST_USE bool emitAsyncFunctionRejectPrologue();
  MOZ_MUST_USE bool emitAsyncFunctionRejectEpilogue();
};

// Class for emitting function parameters.
//
// Usage: (check for the return value is omitted for simplicity)
//
//   `function f(a, b=10, ...c) {}`
//     FunctionParamsEmitter fpe(this, funbox_for_f);
//
//     fpe.emitSimple(atom_of_a);
//
//     fpe.prepareForDefault();
//     emit(10);
//     fpe.emitDefaultEnd(atom_of_b);
//
//     fpe.emitRest(atom_of_c);
//
//   `function f([a], [b]=[1], ...[c]) {}`
//     FunctionParamsEmitter fpe(this, funbox_for_f);
//
//     fpe.prepareForDestructuring();
//     emit(destructuring_for_[a]);
//     fpe.emitDestructuringEnd();
//
//     fpe.prepareForDestructuringDefaultInitializer();
//     emit([1]);
//     fpe.prepareForDestructuringDefault();
//     emit(destructuring_for_[b]);
//     fpe.emitDestructuringDefaultEnd();
//
//     fpe.prepareForDestructuringRest();
//     emit(destructuring_for_[c]);
//     fpe.emitDestructuringRestEnd();
//
class MOZ_STACK_CLASS FunctionParamsEmitter {
 private:
  BytecodeEmitter* bce_;

  FunctionBox* funbox_;

  // The pointer to `FunctionScriptEmitter::functionEmitterScope_`,
  // passed via `BytecodeEmitter::innermostEmitterScope()`.
  EmitterScope* functionEmitterScope_;

  // The slot for the current parameter.
  // NOTE: after emitting rest parameter, this isn't incremented.
  uint16_t argSlot_ = 0;

  // DefaultEmitter for default parameter.
  mozilla::Maybe<DefaultEmitter> default_;

  // Scope for each parameter expression.
  // Populated only when there's `eval` in parameters.
  mozilla::Maybe<EmitterScope> paramExprVarEmitterScope_;

#ifdef DEBUG
  // The state of this emitter.
  //
  // +----------------------------------------------------------+
  // |                                                          |
  // |  +-------+                                               |
  // +->| Start |-+                                             |
  //    +-------+ |                                             |
  //              |                                             |
  // +------------+                                             |
  // |                                                          |
  // | [single binding, wihtout default]                        |
  // |   emitSimple                                             |
  // +--------------------------------------------------------->+
  // |                                                          ^
  // | [single binding, with default]                           |
  // |   prepareForDefault  +---------+ emitDefaultEnd          |
  // +--------------------->| Default |------------------------>+
  // |                      +---------+                         ^
  // |                                                          |
  // | [destructuring, without default]                         |
  // |   prepareForDestructuring  +---------------+             |
  // +--------------------------->| Destructuring |-+           |
  // |                            +---------------+ |           |
  // |                                              |           |
  // |    +-----------------------------------------+           |
  // |    |                                                     |
  // |    | emitDestructuringEnd                                |
  // |    +---------------------------------------------------->+
  // |                                                          ^
  // | [destructuring, with default]                            |
  // |   prepareForDestructuringDefaultInitializer              |
  // +---------------------------------------------+            |
  // |                                             |            |
  // |    +----------------------------------------+            |
  // |    |                                                     |
  // |    |  +---------------------------------+                |
  // |    +->| DestructuringDefaultInitializer |-+              |
  // |       +---------------------------------+ |              |
  // |                                           |              |
  // |      +------------------------------------+              |
  // |      |                                                   |
  // |      | prepareForDestructuringDefault                    |
  // |      +-------------------------------+                   |
  // |                                      |                   |
  // |        +-----------------------------+                   |
  // |        |                                                 |
  // |        |  +----------------------+                       |
  // |        +->| DestructuringDefault |-+                     |
  // |           +----------------------+ |                     |
  // |                                    |                     |
  // |          +-------------------------+                     |
  // |          |                                               |
  // |          | emitDestructuringDefaultEnd                   |
  // |          +---------------------------------------------->+
  // |
  // | [single binding rest]
  // |   emitRest                                                  +-----+
  // +--------------------------------------------------------->+->| End |
  // |                                                          ^  +-----+
  // | [destructuring rest]                                     |
  // |   prepareForDestructuringRest   +-------------------+    |
  // +-------------------------------->| DestructuringRest |-+  |
  //                                   +-------------------+ |  |
  //                                                         |  |
  //    +----------------------------------------------------+  |
  //    |                                                       |
  //    | emitDestructuringRestEnd                              |
  //    +-------------------------------------------------------+
  //
  enum class State {
    // The initial state, or after emitting non-rest parameter.
    Start,

    // After calling prepareForDefault.
    Default,

    // After calling prepareForDestructuring.
    Destructuring,

    // After calling prepareForDestructuringDefaultInitializer.
    DestructuringDefaultInitializer,

    // After calling prepareForDestructuringDefault.
    DestructuringDefault,

    // After calling prepareForDestructuringRest.
    DestructuringRest,

    // After calling emitRest or emitDestructuringRestEnd.
    End,
  };
  State state_ = State::Start;
#endif

 public:
  FunctionParamsEmitter(BytecodeEmitter* bce, FunctionBox* funbox);

  // paramName is used only when there's at least one expression in the
  // paramerters (funbox_->hasParameterExprs == true).
  MOZ_MUST_USE bool emitSimple(JS::Handle<JSAtom*> paramName);

  MOZ_MUST_USE bool prepareForDefault();
  MOZ_MUST_USE bool emitDefaultEnd(JS::Handle<JSAtom*> paramName);

  MOZ_MUST_USE bool prepareForDestructuring();
  MOZ_MUST_USE bool emitDestructuringEnd();

  MOZ_MUST_USE bool prepareForDestructuringDefaultInitializer();
  MOZ_MUST_USE bool prepareForDestructuringDefault();
  MOZ_MUST_USE bool emitDestructuringDefaultEnd();

  MOZ_MUST_USE bool emitRest(JS::Handle<JSAtom*> paramName);

  MOZ_MUST_USE bool prepareForDestructuringRest();
  MOZ_MUST_USE bool emitDestructuringRestEnd();

  MOZ_MUST_USE DestructuringFlavor getDestructuringFlavor();

 private:
  // Enter/leave var scope for `eval` if necessary.
  MOZ_MUST_USE bool enterParameterExpressionVarScope();
  MOZ_MUST_USE bool leaveParameterExpressionVarScope();

  MOZ_MUST_USE bool prepareForInitializer();
  MOZ_MUST_USE bool emitInitializerEnd();

  MOZ_MUST_USE bool emitRestArray();

  MOZ_MUST_USE bool emitAssignment(JS::Handle<JSAtom*> paramName);
};

} /* namespace frontend */
} /* namespace js */

#endif /* frontend_FunctionEmitter_h */