| 1 | /* |
| 2 | * Copyright (C) 2008-2019 Apple Inc. All rights reserved. |
| 3 | * Copyright (C) 2008 Cameron Zwarich <cwzwarich@uwaterloo.ca> |
| 4 | * Copyright (C) 2012 Igalia, S.L. |
| 5 | * |
| 6 | * Redistribution and use in source and binary forms, with or without |
| 7 | * modification, are permitted provided that the following conditions |
| 8 | * are met: |
| 9 | * |
| 10 | * 1. Redistributions of source code must retain the above copyright |
| 11 | * notice, this list of conditions and the following disclaimer. |
| 12 | * 2. Redistributions in binary form must reproduce the above copyright |
| 13 | * notice, this list of conditions and the following disclaimer in the |
| 14 | * documentation and/or other materials provided with the distribution. |
| 15 | * 3. Neither the name of Apple Inc. ("Apple") nor the names of |
| 16 | * its contributors may be used to endorse or promote products derived |
| 17 | * from this software without specific prior written permission. |
| 18 | * |
| 19 | * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND ANY |
| 20 | * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED |
| 21 | * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE |
| 22 | * DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS CONTRIBUTORS BE LIABLE FOR ANY |
| 23 | * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES |
| 24 | * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; |
| 25 | * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND |
| 26 | * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT |
| 27 | * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF |
| 28 | * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
| 29 | */ |
| 30 | |
| 31 | #pragma once |
| 32 | |
| 33 | #include "CodeBlock.h" |
| 34 | #include "Instruction.h" |
| 35 | #include "Interpreter.h" |
| 36 | #include "JSAsyncGeneratorFunction.h" |
| 37 | #include "JSBigInt.h" |
| 38 | #include "JSGeneratorFunction.h" |
| 39 | #include "JSTemplateObjectDescriptor.h" |
| 40 | #include "Label.h" |
| 41 | #include "LabelScope.h" |
| 42 | #include "Nodes.h" |
| 43 | #include "ParserError.h" |
| 44 | #include "ProfileTypeBytecodeFlag.h" |
| 45 | #include "RegisterID.h" |
| 46 | #include "StaticPropertyAnalyzer.h" |
| 47 | #include "SymbolTable.h" |
| 48 | #include "UnlinkedCodeBlock.h" |
| 49 | #include <functional> |
| 50 | #include <wtf/CheckedArithmetic.h> |
| 51 | #include <wtf/HashFunctions.h> |
| 52 | #include <wtf/Optional.h> |
| 53 | #include <wtf/SegmentedVector.h> |
| 54 | #include <wtf/SetForScope.h> |
| 55 | #include <wtf/Vector.h> |
| 56 | |
| 57 | namespace JSC { |
| 58 | |
| 59 | class JSImmutableButterfly; |
| 60 | class Identifier; |
| 61 | class IndexedForInContext; |
| 62 | class StructureForInContext; |
| 63 | |
| 64 | enum ExpectedFunction { |
| 65 | NoExpectedFunction, |
| 66 | ExpectObjectConstructor, |
| 67 | ExpectArrayConstructor |
| 68 | }; |
| 69 | |
| 70 | enum class EmitAwait { Yes, No }; |
| 71 | |
| 72 | enum class DebuggableCall { Yes, No }; |
| 73 | enum class ThisResolutionType { Local, Scoped }; |
| 74 | |
| 75 | class CallArguments { |
| 76 | public: |
| 77 | CallArguments(BytecodeGenerator&, ArgumentsNode*, unsigned additionalArguments = 0); |
| 78 | |
| 79 | RegisterID* thisRegister() { return m_argv[0].get(); } |
| 80 | RegisterID* argumentRegister(unsigned i) { return m_argv[i + 1].get(); } |
| 81 | unsigned stackOffset() { return -m_argv[0]->index() + CallFrame::headerSizeInRegisters; } |
| 82 | unsigned argumentCountIncludingThis() { return m_argv.size() - m_padding; } |
| 83 | ArgumentsNode* argumentsNode() { return m_argumentsNode; } |
| 84 | |
| 85 | private: |
| 86 | ArgumentsNode* m_argumentsNode; |
| 87 | Vector<RefPtr<RegisterID>, 8, UnsafeVectorOverflow> m_argv; |
| 88 | unsigned m_padding; |
| 89 | }; |
| 90 | |
| 91 | // https://tc39.github.io/ecma262/#sec-completion-record-specification-type |
| 92 | // |
| 93 | // For the Break and Continue cases, instead of using the Break and Continue enum values |
| 94 | // below, we use the unique jumpID of the break and continue statement as the encoding |
| 95 | // for the CompletionType value. emitFinallyCompletion() uses this jumpID value later |
| 96 | // to determine the appropriate jump target to jump to after executing the relevant finally |
| 97 | // blocks. The jumpID is computed as: |
| 98 | // jumpID = bytecodeOffset (of the break/continue node) + CompletionType::NumberOfTypes. |
| 99 | // Hence, there won't be any collision between jumpIDs and CompletionType enums. |
| 100 | enum class CompletionType : int { |
| 101 | Normal, |
| 102 | Throw, |
| 103 | Return, |
| 104 | NumberOfTypes |
| 105 | }; |
| 106 | |
| 107 | inline CompletionType bytecodeOffsetToJumpID(unsigned offset) |
| 108 | { |
| 109 | int jumpIDAsInt = offset + static_cast<int>(CompletionType::NumberOfTypes); |
| 110 | ASSERT(jumpIDAsInt >= static_cast<int>(CompletionType::NumberOfTypes)); |
| 111 | return static_cast<CompletionType>(jumpIDAsInt); |
| 112 | } |
| 113 | |
| 114 | struct FinallyJump { |
| 115 | FinallyJump(CompletionType jumpID, int targetLexicalScopeIndex, Label& targetLabel) |
| 116 | : jumpID(jumpID) |
| 117 | , targetLexicalScopeIndex(targetLexicalScopeIndex) |
| 118 | , targetLabel(targetLabel) |
| 119 | { } |
| 120 | |
| 121 | CompletionType jumpID; |
| 122 | int targetLexicalScopeIndex; |
| 123 | Ref<Label> targetLabel; |
| 124 | }; |
| 125 | |
| 126 | class FinallyContext { |
| 127 | public: |
| 128 | FinallyContext() { } |
| 129 | FinallyContext(BytecodeGenerator&, Label& finallyLabel); |
| 130 | |
| 131 | FinallyContext* outerContext() const { return m_outerContext; } |
| 132 | Label* finallyLabel() const { return m_finallyLabel; } |
| 133 | |
| 134 | RegisterID* completionTypeRegister() const { return m_completionRecord.typeRegister.get(); } |
| 135 | RegisterID* completionValueRegister() const { return m_completionRecord.valueRegister.get(); } |
| 136 | |
| 137 | uint32_t numberOfBreaksOrContinues() const { return m_numberOfBreaksOrContinues.unsafeGet(); } |
| 138 | void incNumberOfBreaksOrContinues() { m_numberOfBreaksOrContinues++; } |
| 139 | |
| 140 | bool handlesReturns() const { return m_handlesReturns; } |
| 141 | void setHandlesReturns() { m_handlesReturns = true; } |
| 142 | |
| 143 | void registerJump(CompletionType jumpID, int lexicalScopeIndex, Label& targetLabel) |
| 144 | { |
| 145 | m_jumps.append(FinallyJump(jumpID, lexicalScopeIndex, targetLabel)); |
| 146 | } |
| 147 | |
| 148 | size_t numberOfJumps() const { return m_jumps.size(); } |
| 149 | FinallyJump& jumps(size_t i) { return m_jumps[i]; } |
| 150 | |
| 151 | private: |
| 152 | FinallyContext* m_outerContext { nullptr }; |
| 153 | Label* m_finallyLabel { nullptr }; |
| 154 | Checked<uint32_t, WTF::CrashOnOverflow> m_numberOfBreaksOrContinues; |
| 155 | bool m_handlesReturns { false }; |
| 156 | Vector<FinallyJump> m_jumps; |
| 157 | struct { |
| 158 | RefPtr<RegisterID> typeRegister; |
| 159 | RefPtr<RegisterID> valueRegister; |
| 160 | } m_completionRecord; |
| 161 | }; |
| 162 | |
| 163 | struct ControlFlowScope { |
| 164 | typedef uint8_t Type; |
| 165 | enum { |
| 166 | Label, |
| 167 | Finally |
| 168 | }; |
| 169 | ControlFlowScope(Type type, int lexicalScopeIndex, FinallyContext* finallyContext = nullptr) |
| 170 | : type(type) |
| 171 | , lexicalScopeIndex(lexicalScopeIndex) |
| 172 | , finallyContext(finallyContext) |
| 173 | { } |
| 174 | |
| 175 | bool isLabelScope() const { return type == Label; } |
| 176 | bool isFinallyScope() const { return type == Finally; } |
| 177 | |
| 178 | Type type; |
| 179 | int lexicalScopeIndex; |
| 180 | FinallyContext* finallyContext; |
| 181 | }; |
| 182 | |
| 183 | class ForInContext : public RefCounted<ForInContext> { |
| 184 | WTF_MAKE_FAST_ALLOCATED; |
| 185 | WTF_MAKE_NONCOPYABLE(ForInContext); |
| 186 | public: |
| 187 | virtual ~ForInContext() = default; |
| 188 | |
| 189 | bool isValid() const { return m_isValid; } |
| 190 | void invalidate() { m_isValid = false; } |
| 191 | |
| 192 | enum class Type : uint8_t { |
| 193 | IndexedForIn, |
| 194 | StructureForIn |
| 195 | }; |
| 196 | |
| 197 | Type type() const { return m_type; } |
| 198 | bool isIndexedForInContext() const { return m_type == Type::IndexedForIn; } |
| 199 | bool isStructureForInContext() const { return m_type == Type::StructureForIn; } |
| 200 | |
| 201 | IndexedForInContext& asIndexedForInContext() |
| 202 | { |
| 203 | ASSERT(isIndexedForInContext()); |
| 204 | return *reinterpret_cast<IndexedForInContext*>(this); |
| 205 | } |
| 206 | |
| 207 | StructureForInContext& asStructureForInContext() |
| 208 | { |
| 209 | ASSERT(isStructureForInContext()); |
| 210 | return *reinterpret_cast<StructureForInContext*>(this); |
| 211 | } |
| 212 | |
| 213 | RegisterID* local() const { return m_localRegister.get(); } |
| 214 | |
| 215 | protected: |
| 216 | ForInContext(RegisterID* localRegister, Type type, unsigned bodyBytecodeStartOffset) |
| 217 | : m_localRegister(localRegister) |
| 218 | , m_type(type) |
| 219 | , m_bodyBytecodeStartOffset(bodyBytecodeStartOffset) |
| 220 | { } |
| 221 | |
| 222 | unsigned bodyBytecodeStartOffset() const { return m_bodyBytecodeStartOffset; } |
| 223 | |
| 224 | void finalize(BytecodeGenerator&, UnlinkedCodeBlock*, unsigned bodyBytecodeEndOffset); |
| 225 | |
| 226 | private: |
| 227 | RefPtr<RegisterID> m_localRegister; |
| 228 | bool m_isValid { true }; |
| 229 | Type m_type; |
| 230 | unsigned m_bodyBytecodeStartOffset; |
| 231 | }; |
| 232 | |
| 233 | class StructureForInContext : public ForInContext { |
| 234 | using Base = ForInContext; |
| 235 | public: |
| 236 | using GetInst = std::tuple<unsigned, int>; |
| 237 | |
| 238 | StructureForInContext(RegisterID* localRegister, RegisterID* indexRegister, RegisterID* propertyRegister, RegisterID* enumeratorRegister, unsigned bodyBytecodeStartOffset) |
| 239 | : ForInContext(localRegister, Type::StructureForIn, bodyBytecodeStartOffset) |
| 240 | , m_indexRegister(indexRegister) |
| 241 | , m_propertyRegister(propertyRegister) |
| 242 | , m_enumeratorRegister(enumeratorRegister) |
| 243 | { |
| 244 | } |
| 245 | |
| 246 | RegisterID* index() const { return m_indexRegister.get(); } |
| 247 | RegisterID* property() const { return m_propertyRegister.get(); } |
| 248 | RegisterID* enumerator() const { return m_enumeratorRegister.get(); } |
| 249 | |
| 250 | void addGetInst(unsigned instIndex, int propertyRegIndex) |
| 251 | { |
| 252 | m_getInsts.append(GetInst { instIndex, propertyRegIndex }); |
| 253 | } |
| 254 | |
| 255 | void finalize(BytecodeGenerator&, UnlinkedCodeBlock*, unsigned bodyBytecodeEndOffset); |
| 256 | |
| 257 | private: |
| 258 | RefPtr<RegisterID> m_indexRegister; |
| 259 | RefPtr<RegisterID> m_propertyRegister; |
| 260 | RefPtr<RegisterID> m_enumeratorRegister; |
| 261 | Vector<GetInst> m_getInsts; |
| 262 | }; |
| 263 | |
| 264 | class IndexedForInContext : public ForInContext { |
| 265 | using Base = ForInContext; |
| 266 | public: |
| 267 | IndexedForInContext(RegisterID* localRegister, RegisterID* indexRegister, unsigned bodyBytecodeStartOffset) |
| 268 | : ForInContext(localRegister, Type::IndexedForIn, bodyBytecodeStartOffset) |
| 269 | , m_indexRegister(indexRegister) |
| 270 | { |
| 271 | } |
| 272 | |
| 273 | RegisterID* index() const { return m_indexRegister.get(); } |
| 274 | |
| 275 | void finalize(BytecodeGenerator&, UnlinkedCodeBlock*, unsigned bodyBytecodeEndOffset); |
| 276 | void addGetInst(unsigned instIndex, int propertyIndex) { m_getInsts.append({ instIndex, propertyIndex }); } |
| 277 | |
| 278 | private: |
| 279 | RefPtr<RegisterID> m_indexRegister; |
| 280 | Vector<std::pair<unsigned, int>> m_getInsts; |
| 281 | }; |
| 282 | |
| 283 | struct TryData { |
| 284 | Ref<Label> target; |
| 285 | HandlerType handlerType; |
| 286 | }; |
| 287 | |
| 288 | struct TryContext { |
| 289 | Ref<Label> start; |
| 290 | TryData* tryData; |
| 291 | }; |
| 292 | |
| 293 | class Variable { |
| 294 | public: |
| 295 | enum VariableKind { NormalVariable, SpecialVariable }; |
| 296 | |
| 297 | Variable() |
| 298 | : m_offset() |
| 299 | , m_local(nullptr) |
| 300 | , m_attributes(0) |
| 301 | , m_kind(NormalVariable) |
| 302 | , m_symbolTableConstantIndex(0) // This is meaningless here for this kind of Variable. |
| 303 | , m_isLexicallyScoped(false) |
| 304 | { |
| 305 | } |
| 306 | |
| 307 | Variable(const Identifier& ident) |
| 308 | : m_ident(ident) |
| 309 | , m_local(nullptr) |
| 310 | , m_attributes(0) |
| 311 | , m_kind(NormalVariable) // This is somewhat meaningless here for this kind of Variable. |
| 312 | , m_symbolTableConstantIndex(0) // This is meaningless here for this kind of Variable. |
| 313 | , m_isLexicallyScoped(false) |
| 314 | { |
| 315 | } |
| 316 | |
| 317 | Variable(const Identifier& ident, VarOffset offset, RegisterID* local, unsigned attributes, VariableKind kind, int symbolTableConstantIndex, bool isLexicallyScoped) |
| 318 | : m_ident(ident) |
| 319 | , m_offset(offset) |
| 320 | , m_local(local) |
| 321 | , m_attributes(attributes) |
| 322 | , m_kind(kind) |
| 323 | , m_symbolTableConstantIndex(symbolTableConstantIndex) |
| 324 | , m_isLexicallyScoped(isLexicallyScoped) |
| 325 | { |
| 326 | } |
| 327 | |
| 328 | // If it's unset, then it is a non-locally-scoped variable. If it is set, then it could be |
| 329 | // a stack variable, a scoped variable in a local scope, or a variable captured in the |
| 330 | // direct arguments object. |
| 331 | bool isResolved() const { return !!m_offset; } |
| 332 | int symbolTableConstantIndex() const { ASSERT(isResolved() && !isSpecial()); return m_symbolTableConstantIndex; } |
| 333 | |
| 334 | const Identifier& ident() const { return m_ident; } |
| 335 | |
| 336 | VarOffset offset() const { return m_offset; } |
| 337 | bool isLocal() const { return m_offset.isStack(); } |
| 338 | RegisterID* local() const { return m_local; } |
| 339 | |
| 340 | bool isReadOnly() const { return m_attributes & PropertyAttribute::ReadOnly; } |
| 341 | bool isSpecial() const { return m_kind != NormalVariable; } |
| 342 | bool isConst() const { return isReadOnly() && m_isLexicallyScoped; } |
| 343 | void setIsReadOnly() { m_attributes |= PropertyAttribute::ReadOnly; } |
| 344 | |
| 345 | void dump(PrintStream&) const; |
| 346 | |
| 347 | private: |
| 348 | Identifier m_ident; |
| 349 | VarOffset m_offset; |
| 350 | RegisterID* m_local; |
| 351 | unsigned m_attributes; |
| 352 | VariableKind m_kind; |
| 353 | int m_symbolTableConstantIndex; |
| 354 | bool m_isLexicallyScoped; |
| 355 | }; |
| 356 | |
| 357 | struct TryRange { |
| 358 | Ref<Label> start; |
| 359 | Ref<Label> end; |
| 360 | TryData* tryData; |
| 361 | }; |
| 362 | |
| 363 | class BytecodeGenerator { |
| 364 | WTF_MAKE_FAST_ALLOCATED; |
| 365 | WTF_MAKE_NONCOPYABLE(BytecodeGenerator); |
| 366 | |
| 367 | friend class BoundLabel; |
| 368 | friend class FinallyContext; |
| 369 | friend class Label; |
| 370 | friend class IndexedForInContext; |
| 371 | friend class StructureForInContext; |
| 372 | public: |
| 373 | typedef DeclarationStacks::FunctionStack FunctionStack; |
| 374 | |
| 375 | BytecodeGenerator(VM&, ProgramNode*, UnlinkedProgramCodeBlock*, OptionSet<CodeGenerationMode>, const VariableEnvironment*); |
| 376 | BytecodeGenerator(VM&, FunctionNode*, UnlinkedFunctionCodeBlock*, OptionSet<CodeGenerationMode>, const VariableEnvironment*); |
| 377 | BytecodeGenerator(VM&, EvalNode*, UnlinkedEvalCodeBlock*, OptionSet<CodeGenerationMode>, const VariableEnvironment*); |
| 378 | BytecodeGenerator(VM&, ModuleProgramNode*, UnlinkedModuleProgramCodeBlock*, OptionSet<CodeGenerationMode>, const VariableEnvironment*); |
| 379 | |
| 380 | ~BytecodeGenerator(); |
| 381 | |
| 382 | VM* vm() const { return m_vm; } |
| 383 | ParserArena& parserArena() const { return m_scopeNode->parserArena(); } |
| 384 | const CommonIdentifiers& propertyNames() const { return *m_vm->propertyNames; } |
| 385 | |
| 386 | bool isConstructor() const { return m_codeBlock->isConstructor(); } |
| 387 | DerivedContextType derivedContextType() const { return m_derivedContextType; } |
| 388 | bool usesArrowFunction() const { return m_scopeNode->usesArrowFunction(); } |
| 389 | bool needsToUpdateArrowFunctionContext() const { return m_needsToUpdateArrowFunctionContext; } |
| 390 | bool usesEval() const { return m_scopeNode->usesEval(); } |
| 391 | bool usesThis() const { return m_scopeNode->usesThis(); } |
| 392 | ConstructorKind constructorKind() const { return m_codeBlock->constructorKind(); } |
| 393 | SuperBinding superBinding() const { return m_codeBlock->superBinding(); } |
| 394 | JSParserScriptMode scriptMode() const { return m_codeBlock->scriptMode(); } |
| 395 | |
| 396 | template<typename Node, typename UnlinkedCodeBlock> |
| 397 | static ParserError generate(VM& vm, Node* node, const SourceCode& sourceCode, UnlinkedCodeBlock* unlinkedCodeBlock, OptionSet<CodeGenerationMode> codeGenerationMode, const VariableEnvironment* environment) |
| 398 | { |
| 399 | MonotonicTime before; |
| 400 | if (UNLIKELY(Options::reportBytecodeCompileTimes())) |
| 401 | before = MonotonicTime::now(); |
| 402 | |
| 403 | DeferGC deferGC(vm.heap); |
| 404 | auto bytecodeGenerator = std::make_unique<BytecodeGenerator>(vm, node, unlinkedCodeBlock, codeGenerationMode, environment); |
| 405 | auto result = bytecodeGenerator->generate(); |
| 406 | |
| 407 | if (UNLIKELY(Options::reportBytecodeCompileTimes())) { |
| 408 | MonotonicTime after = MonotonicTime::now(); |
| 409 | dataLogLn(result.isValid() ? "Failed to compile #" : "Compiled #" , CodeBlockHash(sourceCode, unlinkedCodeBlock->isConstructor() ? CodeForConstruct : CodeForCall), " into bytecode " , bytecodeGenerator->instructions().size(), " instructions in " , (after - before).milliseconds(), " ms." ); |
| 410 | } |
| 411 | return result; |
| 412 | } |
| 413 | |
| 414 | bool isArgumentNumber(const Identifier&, int); |
| 415 | |
| 416 | Variable variable(const Identifier&, ThisResolutionType = ThisResolutionType::Local); |
| 417 | |
| 418 | enum ExistingVariableMode { VerifyExisting, IgnoreExisting }; |
| 419 | void createVariable(const Identifier&, VarKind, SymbolTable*, ExistingVariableMode = VerifyExisting); // Creates the variable, or asserts that the already-created variable is sufficiently compatible. |
| 420 | |
| 421 | // Returns the register storing "this" |
| 422 | RegisterID* thisRegister() { return &m_thisRegister; } |
| 423 | RegisterID* argumentsRegister() { return m_argumentsRegister; } |
| 424 | RegisterID* newTarget() |
| 425 | { |
| 426 | return m_newTargetRegister; |
| 427 | } |
| 428 | |
| 429 | RegisterID* scopeRegister() { return m_scopeRegister; } |
| 430 | |
| 431 | RegisterID* generatorRegister() { return m_generatorRegister; } |
| 432 | |
| 433 | RegisterID* promiseCapabilityRegister() { return m_promiseCapabilityRegister; } |
| 434 | |
| 435 | // Returns the next available temporary register. Registers returned by |
| 436 | // newTemporary require a modified form of reference counting: any |
| 437 | // register with a refcount of 0 is considered "available", meaning that |
| 438 | // the next instruction may overwrite it. |
| 439 | RegisterID* newTemporary(); |
| 440 | |
| 441 | // The same as newTemporary(), but this function returns "suggestion" if |
| 442 | // "suggestion" is a temporary. This function is helpful in situations |
| 443 | // where you've put "suggestion" in a RefPtr, but you'd like to allow |
| 444 | // the next instruction to overwrite it anyway. |
| 445 | RegisterID* newTemporaryOr(RegisterID* suggestion) { return suggestion->isTemporary() ? suggestion : newTemporary(); } |
| 446 | |
| 447 | // Functions for handling of dst register |
| 448 | |
| 449 | RegisterID* ignoredResult() { return &m_ignoredResultRegister; } |
| 450 | |
| 451 | // This will be allocated in the temporary region of registers, but it will |
| 452 | // not be marked as a temporary. This will ensure that finalDestination() does |
| 453 | // not overwrite a block scope variable that it mistakes as a temporary. These |
| 454 | // registers can be (and are) reclaimed when the lexical scope they belong to |
| 455 | // is no longer on the symbol table stack. |
| 456 | RegisterID* newBlockScopeVariable(); |
| 457 | |
| 458 | // Returns a place to write intermediate values of an operation |
| 459 | // which reuses dst if it is safe to do so. |
| 460 | RegisterID* tempDestination(RegisterID* dst) |
| 461 | { |
| 462 | return (dst && dst != ignoredResult() && dst->isTemporary()) ? dst : newTemporary(); |
| 463 | } |
| 464 | |
| 465 | // Returns the place to write the final output of an operation. |
| 466 | RegisterID* finalDestination(RegisterID* originalDst, RegisterID* tempDst = 0) |
| 467 | { |
| 468 | if (originalDst && originalDst != ignoredResult()) |
| 469 | return originalDst; |
| 470 | ASSERT(tempDst != ignoredResult()); |
| 471 | if (tempDst && tempDst->isTemporary()) |
| 472 | return tempDst; |
| 473 | return newTemporary(); |
| 474 | } |
| 475 | |
| 476 | RegisterID* destinationForAssignResult(RegisterID* dst) |
| 477 | { |
| 478 | if (dst && dst != ignoredResult()) |
| 479 | return dst->isTemporary() ? dst : newTemporary(); |
| 480 | return 0; |
| 481 | } |
| 482 | |
| 483 | // Moves src to dst if dst is not null and is different from src, otherwise just returns src. |
| 484 | RegisterID* move(RegisterID* dst, RegisterID* src) |
| 485 | { |
| 486 | return dst == ignoredResult() ? nullptr : (dst && dst != src) ? emitMove(dst, src) : src; |
| 487 | } |
| 488 | |
| 489 | Ref<LabelScope> newLabelScope(LabelScope::Type, const Identifier* = 0); |
| 490 | Ref<Label> newLabel(); |
| 491 | Ref<Label> newEmittedLabel(); |
| 492 | |
| 493 | void emitNode(RegisterID* dst, StatementNode* n) |
| 494 | { |
| 495 | SetForScope<bool> tailPositionPoisoner(m_inTailPosition, false); |
| 496 | return emitNodeInTailPosition(dst, n); |
| 497 | } |
| 498 | |
| 499 | void emitNodeInTailPosition(RegisterID* dst, StatementNode* n) |
| 500 | { |
| 501 | // Node::emitCode assumes that dst, if provided, is either a local or a referenced temporary. |
| 502 | ASSERT(!dst || dst == ignoredResult() || !dst->isTemporary() || dst->refCount()); |
| 503 | if (UNLIKELY(!m_vm->isSafeToRecurse())) { |
| 504 | emitThrowExpressionTooDeepException(); |
| 505 | return; |
| 506 | } |
| 507 | if (UNLIKELY(n->needsDebugHook())) |
| 508 | emitDebugHook(n); |
| 509 | n->emitBytecode(*this, dst); |
| 510 | } |
| 511 | |
| 512 | void recordOpcode(OpcodeID); |
| 513 | |
| 514 | ALWAYS_INLINE unsigned addMetadataFor(OpcodeID opcodeID) |
| 515 | { |
| 516 | return m_codeBlock->metadata().addEntry(opcodeID); |
| 517 | } |
| 518 | |
| 519 | void emitNode(StatementNode* n) |
| 520 | { |
| 521 | emitNode(nullptr, n); |
| 522 | } |
| 523 | |
| 524 | void emitNodeInTailPosition(StatementNode* n) |
| 525 | { |
| 526 | emitNodeInTailPosition(nullptr, n); |
| 527 | } |
| 528 | |
| 529 | RegisterID* emitNode(RegisterID* dst, ExpressionNode* n) |
| 530 | { |
| 531 | SetForScope<bool> tailPositionPoisoner(m_inTailPosition, false); |
| 532 | return emitNodeInTailPosition(dst, n); |
| 533 | } |
| 534 | |
| 535 | RegisterID* emitNodeInTailPosition(RegisterID* dst, ExpressionNode* n) |
| 536 | { |
| 537 | // Node::emitCode assumes that dst, if provided, is either a local or a referenced temporary. |
| 538 | ASSERT(!dst || dst == ignoredResult() || !dst->isTemporary() || dst->refCount()); |
| 539 | if (UNLIKELY(!m_vm->isSafeToRecurse())) |
| 540 | return emitThrowExpressionTooDeepException(); |
| 541 | if (UNLIKELY(n->needsDebugHook())) |
| 542 | emitDebugHook(n); |
| 543 | return n->emitBytecode(*this, dst); |
| 544 | } |
| 545 | |
| 546 | RegisterID* emitNode(ExpressionNode* n) |
| 547 | { |
| 548 | return emitNode(nullptr, n); |
| 549 | } |
| 550 | |
| 551 | RegisterID* emitNodeInTailPosition(ExpressionNode* n) |
| 552 | { |
| 553 | return emitNodeInTailPosition(nullptr, n); |
| 554 | } |
| 555 | |
| 556 | RegisterID* emitDefineClassElements(PropertyListNode* n, RegisterID* constructor, RegisterID* prototype) |
| 557 | { |
| 558 | ASSERT(constructor->refCount() && prototype->refCount()); |
| 559 | if (UNLIKELY(!m_vm->isSafeToRecurse())) |
| 560 | return emitThrowExpressionTooDeepException(); |
| 561 | if (UNLIKELY(n->needsDebugHook())) |
| 562 | emitDebugHook(n); |
| 563 | return n->emitBytecode(*this, constructor, prototype); |
| 564 | } |
| 565 | |
| 566 | RegisterID* emitNodeForProperty(RegisterID* dst, ExpressionNode* node) |
| 567 | { |
| 568 | if (node->isString()) { |
| 569 | if (Optional<uint32_t> index = parseIndex(static_cast<StringNode*>(node)->value())) |
| 570 | return emitLoad(dst, jsNumber(index.value())); |
| 571 | } |
| 572 | return emitNode(dst, node); |
| 573 | } |
| 574 | |
| 575 | RegisterID* emitNodeForProperty(ExpressionNode* n) |
| 576 | { |
| 577 | return emitNodeForProperty(nullptr, n); |
| 578 | } |
| 579 | |
| 580 | void emitNodeInConditionContext(ExpressionNode* n, Label& trueTarget, Label& falseTarget, FallThroughMode fallThroughMode) |
| 581 | { |
| 582 | if (UNLIKELY(!m_vm->isSafeToRecurse())) { |
| 583 | emitThrowExpressionTooDeepException(); |
| 584 | return; |
| 585 | } |
| 586 | n->emitBytecodeInConditionContext(*this, trueTarget, falseTarget, fallThroughMode); |
| 587 | } |
| 588 | |
| 589 | void emitExpressionInfo(const JSTextPosition& divot, const JSTextPosition& divotStart, const JSTextPosition& divotEnd) |
| 590 | { |
| 591 | ASSERT(divot.offset >= divotStart.offset); |
| 592 | ASSERT(divotEnd.offset >= divot.offset); |
| 593 | |
| 594 | int sourceOffset = m_scopeNode->source().startOffset(); |
| 595 | unsigned firstLine = m_scopeNode->source().firstLine().oneBasedInt(); |
| 596 | |
| 597 | int divotOffset = divot.offset - sourceOffset; |
| 598 | int startOffset = divot.offset - divotStart.offset; |
| 599 | int endOffset = divotEnd.offset - divot.offset; |
| 600 | |
| 601 | unsigned line = divot.line; |
| 602 | ASSERT(line >= firstLine); |
| 603 | line -= firstLine; |
| 604 | |
| 605 | int lineStart = divot.lineStartOffset; |
| 606 | if (lineStart > sourceOffset) |
| 607 | lineStart -= sourceOffset; |
| 608 | else |
| 609 | lineStart = 0; |
| 610 | |
| 611 | if (divotOffset < lineStart) |
| 612 | return; |
| 613 | |
| 614 | unsigned column = divotOffset - lineStart; |
| 615 | |
| 616 | unsigned instructionOffset = instructions().size(); |
| 617 | if (!m_isBuiltinFunction) |
| 618 | m_codeBlock->addExpressionInfo(instructionOffset, divotOffset, startOffset, endOffset, line, column); |
| 619 | } |
| 620 | |
| 621 | |
| 622 | ALWAYS_INLINE bool leftHandSideNeedsCopy(bool rightHasAssignments, bool rightIsPure) |
| 623 | { |
| 624 | return (m_codeType != FunctionCode || rightHasAssignments) && !rightIsPure; |
| 625 | } |
| 626 | |
| 627 | ALWAYS_INLINE RefPtr<RegisterID> emitNodeForLeftHandSide(ExpressionNode* n, bool rightHasAssignments, bool rightIsPure) |
| 628 | { |
| 629 | if (leftHandSideNeedsCopy(rightHasAssignments, rightIsPure)) { |
| 630 | RefPtr<RegisterID> dst = newTemporary(); |
| 631 | emitNode(dst.get(), n); |
| 632 | return dst; |
| 633 | } |
| 634 | |
| 635 | return emitNode(n); |
| 636 | } |
| 637 | |
| 638 | ALWAYS_INLINE RefPtr<RegisterID> emitNodeForLeftHandSideForProperty(ExpressionNode* n, bool rightHasAssignments, bool rightIsPure) |
| 639 | { |
| 640 | if (leftHandSideNeedsCopy(rightHasAssignments, rightIsPure)) { |
| 641 | RefPtr<RegisterID> dst = newTemporary(); |
| 642 | emitNodeForProperty(dst.get(), n); |
| 643 | return dst; |
| 644 | } |
| 645 | |
| 646 | return emitNodeForProperty(n); |
| 647 | } |
| 648 | |
| 649 | void hoistSloppyModeFunctionIfNecessary(const Identifier& functionName); |
| 650 | |
| 651 | private: |
| 652 | void emitTypeProfilerExpressionInfo(const JSTextPosition& startDivot, const JSTextPosition& endDivot); |
| 653 | public: |
| 654 | |
| 655 | // This doesn't emit expression info. If using this, make sure you shouldn't be emitting text offset. |
| 656 | void emitProfileType(RegisterID* registerToProfile, ProfileTypeBytecodeFlag); |
| 657 | // These variables are associated with variables in a program. They could be Locals, LocalClosureVar, or ClosureVar. |
| 658 | void emitProfileType(RegisterID* registerToProfile, const Variable&, const JSTextPosition& startDivot, const JSTextPosition& endDivot); |
| 659 | |
| 660 | void emitProfileType(RegisterID* registerToProfile, ProfileTypeBytecodeFlag, const JSTextPosition& startDivot, const JSTextPosition& endDivot); |
| 661 | // These are not associated with variables and don't have a global id. |
| 662 | void emitProfileType(RegisterID* registerToProfile, const JSTextPosition& startDivot, const JSTextPosition& endDivot); |
| 663 | |
| 664 | void emitProfileControlFlow(int); |
| 665 | |
| 666 | RegisterID* emitLoadArrowFunctionLexicalEnvironment(const Identifier&); |
| 667 | RegisterID* ensureThis(); |
| 668 | void emitLoadThisFromArrowFunctionLexicalEnvironment(); |
| 669 | RegisterID* emitLoadNewTargetFromArrowFunctionLexicalEnvironment(); |
| 670 | |
| 671 | unsigned addConstantIndex(); |
| 672 | RegisterID* emitLoad(RegisterID* dst, bool); |
| 673 | RegisterID* emitLoad(RegisterID* dst, const Identifier&); |
| 674 | RegisterID* emitLoad(RegisterID* dst, JSValue, SourceCodeRepresentation = SourceCodeRepresentation::Other); |
| 675 | RegisterID* emitLoad(RegisterID* dst, IdentifierSet& excludedList); |
| 676 | |
| 677 | template<typename UnaryOp, typename = std::enable_if_t<UnaryOp::opcodeID != op_negate>> |
| 678 | RegisterID* emitUnaryOp(RegisterID* dst, RegisterID* src) |
| 679 | { |
| 680 | UnaryOp::emit(this, dst, src); |
| 681 | return dst; |
| 682 | } |
| 683 | |
| 684 | RegisterID* emitUnaryOp(OpcodeID, RegisterID* dst, RegisterID* src, OperandTypes); |
| 685 | |
| 686 | template<typename BinaryOp> |
| 687 | std::enable_if_t< |
| 688 | BinaryOp::opcodeID != op_add |
| 689 | && BinaryOp::opcodeID != op_mul |
| 690 | && BinaryOp::opcodeID != op_sub |
| 691 | && BinaryOp::opcodeID != op_div, |
| 692 | RegisterID*> |
| 693 | emitBinaryOp(RegisterID* dst, RegisterID* src1, RegisterID* src2, OperandTypes) |
| 694 | { |
| 695 | BinaryOp::emit(this, dst, src1, src2); |
| 696 | return dst; |
| 697 | } |
| 698 | |
| 699 | template<typename BinaryOp> |
| 700 | std::enable_if_t< |
| 701 | BinaryOp::opcodeID == op_add |
| 702 | || BinaryOp::opcodeID == op_mul |
| 703 | || BinaryOp::opcodeID == op_sub |
| 704 | || BinaryOp::opcodeID == op_div, |
| 705 | RegisterID*> |
| 706 | emitBinaryOp(RegisterID* dst, RegisterID* src1, RegisterID* src2, OperandTypes types) |
| 707 | { |
| 708 | BinaryOp::emit(this, dst, src1, src2, types); |
| 709 | return dst; |
| 710 | } |
| 711 | |
| 712 | RegisterID* emitBinaryOp(OpcodeID, RegisterID* dst, RegisterID* src1, RegisterID* src2, OperandTypes); |
| 713 | |
| 714 | template<typename EqOp> |
| 715 | RegisterID* emitEqualityOp(RegisterID* dst, RegisterID* src1, RegisterID* src2) |
| 716 | { |
| 717 | if (!emitEqualityOpImpl(dst, src1, src2)) |
| 718 | EqOp::emit(this, dst, src1, src2); |
| 719 | return dst; |
| 720 | } |
| 721 | |
| 722 | bool emitEqualityOpImpl(RegisterID* dst, RegisterID* src1, RegisterID* src2); |
| 723 | |
| 724 | RegisterID* emitCreateThis(RegisterID* dst); |
| 725 | void emitTDZCheck(RegisterID* target); |
| 726 | bool needsTDZCheck(const Variable&); |
| 727 | void emitTDZCheckIfNecessary(const Variable&, RegisterID* target, RegisterID* scope); |
| 728 | void liftTDZCheckIfPossible(const Variable&); |
| 729 | RegisterID* emitNewObject(RegisterID* dst); |
| 730 | RegisterID* emitNewArray(RegisterID* dst, ElementNode*, unsigned length, IndexingType recommendedIndexingType); // stops at first elision |
| 731 | RegisterID* emitNewArrayBuffer(RegisterID* dst, JSImmutableButterfly*, IndexingType recommendedIndexingType); |
| 732 | // FIXME: new_array_with_spread should use an array allocation profile and take a recommendedIndexingType |
| 733 | RegisterID* emitNewArrayWithSpread(RegisterID* dst, ElementNode*); |
| 734 | RegisterID* emitNewArrayWithSize(RegisterID* dst, RegisterID* length); |
| 735 | |
| 736 | RegisterID* emitNewFunction(RegisterID* dst, FunctionMetadataNode*); |
| 737 | RegisterID* emitNewFunctionExpression(RegisterID* dst, FuncExprNode*); |
| 738 | RegisterID* emitNewDefaultConstructor(RegisterID* dst, ConstructorKind, const Identifier& name, const Identifier& ecmaName, const SourceCode& classSource); |
| 739 | RegisterID* emitNewArrowFunctionExpression(RegisterID*, ArrowFuncExprNode*); |
| 740 | RegisterID* emitNewMethodDefinition(RegisterID* dst, MethodDefinitionNode*); |
| 741 | RegisterID* emitNewRegExp(RegisterID* dst, RegExp*); |
| 742 | |
| 743 | void emitSetFunctionNameIfNeeded(ExpressionNode* valueNode, RegisterID* value, RegisterID* name); |
| 744 | |
| 745 | RegisterID* moveLinkTimeConstant(RegisterID* dst, LinkTimeConstant); |
| 746 | RegisterID* moveEmptyValue(RegisterID* dst); |
| 747 | |
| 748 | RegisterID* emitToNumber(RegisterID* dst, RegisterID* src); |
| 749 | RegisterID* emitToString(RegisterID* dst, RegisterID* src); |
| 750 | RegisterID* emitToObject(RegisterID* dst, RegisterID* src, const Identifier& message); |
| 751 | RegisterID* emitInc(RegisterID* srcDst); |
| 752 | RegisterID* emitDec(RegisterID* srcDst); |
| 753 | |
| 754 | RegisterID* emitOverridesHasInstance(RegisterID* dst, RegisterID* constructor, RegisterID* hasInstanceValue); |
| 755 | RegisterID* emitInstanceOf(RegisterID* dst, RegisterID* value, RegisterID* basePrototype); |
| 756 | RegisterID* emitInstanceOfCustom(RegisterID* dst, RegisterID* value, RegisterID* constructor, RegisterID* hasInstanceValue); |
| 757 | RegisterID* emitTypeOf(RegisterID* dst, RegisterID* src); |
| 758 | RegisterID* emitInByVal(RegisterID* dst, RegisterID* property, RegisterID* base); |
| 759 | RegisterID* emitInById(RegisterID* dst, RegisterID* base, const Identifier& property); |
| 760 | |
| 761 | RegisterID* emitTryGetById(RegisterID* dst, RegisterID* base, const Identifier& property); |
| 762 | RegisterID* emitGetById(RegisterID* dst, RegisterID* base, const Identifier& property); |
| 763 | RegisterID* emitGetById(RegisterID* dst, RegisterID* base, RegisterID* thisVal, const Identifier& property); |
| 764 | RegisterID* emitDirectGetById(RegisterID* dst, RegisterID* base, const Identifier& property); |
| 765 | RegisterID* emitPutById(RegisterID* base, const Identifier& property, RegisterID* value); |
| 766 | RegisterID* emitPutById(RegisterID* base, RegisterID* thisValue, const Identifier& property, RegisterID* value); |
| 767 | RegisterID* emitDirectPutById(RegisterID* base, const Identifier& property, RegisterID* value, PropertyNode::PutType); |
| 768 | RegisterID* emitDeleteById(RegisterID* dst, RegisterID* base, const Identifier&); |
| 769 | RegisterID* emitGetByVal(RegisterID* dst, RegisterID* base, RegisterID* property); |
| 770 | RegisterID* emitGetByVal(RegisterID* dst, RegisterID* base, RegisterID* thisValue, RegisterID* property); |
| 771 | RegisterID* emitPutByVal(RegisterID* base, RegisterID* property, RegisterID* value); |
| 772 | RegisterID* emitPutByVal(RegisterID* base, RegisterID* thisValue, RegisterID* property, RegisterID* value); |
| 773 | RegisterID* emitDirectPutByVal(RegisterID* base, RegisterID* property, RegisterID* value); |
| 774 | RegisterID* emitDeleteByVal(RegisterID* dst, RegisterID* base, RegisterID* property); |
| 775 | |
| 776 | void emitSuperSamplerBegin(); |
| 777 | void emitSuperSamplerEnd(); |
| 778 | |
| 779 | RegisterID* emitIdWithProfile(RegisterID* src, SpeculatedType profile); |
| 780 | void emitUnreachable(); |
| 781 | |
| 782 | void emitPutGetterById(RegisterID* base, const Identifier& property, unsigned propertyDescriptorOptions, RegisterID* getter); |
| 783 | void emitPutSetterById(RegisterID* base, const Identifier& property, unsigned propertyDescriptorOptions, RegisterID* setter); |
| 784 | void emitPutGetterSetter(RegisterID* base, const Identifier& property, unsigned attributes, RegisterID* getter, RegisterID* setter); |
| 785 | void emitPutGetterByVal(RegisterID* base, RegisterID* property, unsigned propertyDescriptorOptions, RegisterID* getter); |
| 786 | void emitPutSetterByVal(RegisterID* base, RegisterID* property, unsigned propertyDescriptorOptions, RegisterID* setter); |
| 787 | |
| 788 | RegisterID* emitGetArgument(RegisterID* dst, int32_t index); |
| 789 | |
| 790 | // Initialize object with generator fields (@generatorThis, @generatorNext, @generatorState, @generatorFrame) |
| 791 | void emitPutGeneratorFields(RegisterID* nextFunction); |
| 792 | |
| 793 | void emitPutAsyncGeneratorFields(RegisterID* nextFunction); |
| 794 | |
| 795 | ExpectedFunction expectedFunctionForIdentifier(const Identifier&); |
| 796 | RegisterID* emitCall(RegisterID* dst, RegisterID* func, ExpectedFunction, CallArguments&, const JSTextPosition& divot, const JSTextPosition& divotStart, const JSTextPosition& divotEnd, DebuggableCall); |
| 797 | RegisterID* emitCallInTailPosition(RegisterID* dst, RegisterID* func, ExpectedFunction, CallArguments&, const JSTextPosition& divot, const JSTextPosition& divotStart, const JSTextPosition& divotEnd, DebuggableCall); |
| 798 | RegisterID* emitCallEval(RegisterID* dst, RegisterID* func, CallArguments&, const JSTextPosition& divot, const JSTextPosition& divotStart, const JSTextPosition& divotEnd, DebuggableCall); |
| 799 | RegisterID* emitCallVarargs(RegisterID* dst, RegisterID* func, RegisterID* thisRegister, RegisterID* arguments, RegisterID* firstFreeRegister, int32_t firstVarArgOffset, const JSTextPosition& divot, const JSTextPosition& divotStart, const JSTextPosition& divotEnd, DebuggableCall); |
| 800 | RegisterID* emitCallVarargsInTailPosition(RegisterID* dst, RegisterID* func, RegisterID* thisRegister, RegisterID* arguments, RegisterID* firstFreeRegister, int32_t firstVarArgOffset, const JSTextPosition& divot, const JSTextPosition& divotStart, const JSTextPosition& divotEnd, DebuggableCall); |
| 801 | RegisterID* emitCallForwardArgumentsInTailPosition(RegisterID* dst, RegisterID* func, RegisterID* thisRegister, RegisterID* firstFreeRegister, int32_t firstVarArgOffset, const JSTextPosition& divot, const JSTextPosition& divotStart, const JSTextPosition& divotEnd, DebuggableCall); |
| 802 | |
| 803 | enum PropertyDescriptorOption { |
| 804 | PropertyConfigurable = 1, |
| 805 | PropertyWritable = 1 << 1, |
| 806 | PropertyEnumerable = 1 << 2, |
| 807 | }; |
| 808 | void emitCallDefineProperty(RegisterID* newObj, RegisterID* propertyNameRegister, |
| 809 | RegisterID* valueRegister, RegisterID* getterRegister, RegisterID* setterRegister, unsigned options, const JSTextPosition&); |
| 810 | |
| 811 | void emitEnumeration(ThrowableExpressionData* enumerationNode, ExpressionNode* subjectNode, const ScopedLambda<void(BytecodeGenerator&, RegisterID*)>& callBack, ForOfNode* = nullptr, RegisterID* forLoopSymbolTable = nullptr); |
| 812 | |
| 813 | RegisterID* emitGetTemplateObject(RegisterID* dst, TaggedTemplateNode*); |
| 814 | RegisterID* emitGetGlobalPrivate(RegisterID* dst, const Identifier& property); |
| 815 | |
| 816 | enum class ReturnFrom { Normal, Finally }; |
| 817 | RegisterID* emitReturn(RegisterID* src, ReturnFrom = ReturnFrom::Normal); |
| 818 | RegisterID* emitEnd(RegisterID* src); |
| 819 | |
| 820 | RegisterID* emitConstruct(RegisterID* dst, RegisterID* func, RegisterID* lazyThis, ExpectedFunction, CallArguments&, const JSTextPosition& divot, const JSTextPosition& divotStart, const JSTextPosition& divotEnd); |
| 821 | RegisterID* emitStrcat(RegisterID* dst, RegisterID* src, int count); |
| 822 | void emitToPrimitive(RegisterID* dst, RegisterID* src); |
| 823 | |
| 824 | ResolveType resolveType(); |
| 825 | RegisterID* emitResolveConstantLocal(RegisterID* dst, const Variable&); |
| 826 | RegisterID* emitResolveScope(RegisterID* dst, const Variable&); |
| 827 | RegisterID* emitGetFromScope(RegisterID* dst, RegisterID* scope, const Variable&, ResolveMode); |
| 828 | RegisterID* emitPutToScope(RegisterID* scope, const Variable&, RegisterID* value, ResolveMode, InitializationMode); |
| 829 | |
| 830 | RegisterID* emitResolveScopeForHoistingFuncDeclInEval(RegisterID* dst, const Identifier&); |
| 831 | |
| 832 | RegisterID* initializeVariable(const Variable&, RegisterID* value); |
| 833 | |
| 834 | void emitLabel(Label&); |
| 835 | void emitLoopHint(); |
| 836 | void emitJump(Label& target); |
| 837 | void emitJumpIfTrue(RegisterID* cond, Label& target); |
| 838 | void emitJumpIfFalse(RegisterID* cond, Label& target); |
| 839 | void emitJumpIfNotFunctionCall(RegisterID* cond, Label& target); |
| 840 | void emitJumpIfNotFunctionApply(RegisterID* cond, Label& target); |
| 841 | |
| 842 | template<typename BinOp, typename JmpOp> |
| 843 | bool fuseCompareAndJump(RegisterID* cond, Label& target, bool swapOperands = false); |
| 844 | |
| 845 | template<typename UnaryOp, typename JmpOp> |
| 846 | bool fuseTestAndJmp(RegisterID* cond, Label& target); |
| 847 | |
| 848 | void emitEnter(); |
| 849 | void emitCheckTraps(); |
| 850 | |
| 851 | RegisterID* emitHasIndexedProperty(RegisterID* dst, RegisterID* base, RegisterID* propertyName); |
| 852 | RegisterID* emitHasStructureProperty(RegisterID* dst, RegisterID* base, RegisterID* propertyName, RegisterID* enumerator); |
| 853 | RegisterID* emitHasGenericProperty(RegisterID* dst, RegisterID* base, RegisterID* propertyName); |
| 854 | RegisterID* emitGetPropertyEnumerator(RegisterID* dst, RegisterID* base); |
| 855 | RegisterID* emitGetEnumerableLength(RegisterID* dst, RegisterID* base); |
| 856 | RegisterID* emitGetStructurePropertyEnumerator(RegisterID* dst, RegisterID* base, RegisterID* length); |
| 857 | RegisterID* emitGetGenericPropertyEnumerator(RegisterID* dst, RegisterID* base, RegisterID* length, RegisterID* structureEnumerator); |
| 858 | RegisterID* emitEnumeratorStructurePropertyName(RegisterID* dst, RegisterID* enumerator, RegisterID* index); |
| 859 | RegisterID* emitEnumeratorGenericPropertyName(RegisterID* dst, RegisterID* enumerator, RegisterID* index); |
| 860 | RegisterID* emitToIndexString(RegisterID* dst, RegisterID* index); |
| 861 | |
| 862 | RegisterID* emitIsCellWithType(RegisterID* dst, RegisterID* src, JSType); |
| 863 | RegisterID* emitIsJSArray(RegisterID* dst, RegisterID* src) { return emitIsCellWithType(dst, src, ArrayType); } |
| 864 | RegisterID* emitIsProxyObject(RegisterID* dst, RegisterID* src) { return emitIsCellWithType(dst, src, ProxyObjectType); } |
| 865 | RegisterID* emitIsRegExpObject(RegisterID* dst, RegisterID* src) { return emitIsCellWithType(dst, src, RegExpObjectType); } |
| 866 | RegisterID* emitIsMap(RegisterID* dst, RegisterID* src) { return emitIsCellWithType(dst, src, JSMapType); } |
| 867 | RegisterID* emitIsSet(RegisterID* dst, RegisterID* src) { return emitIsCellWithType(dst, src, JSSetType); } |
| 868 | RegisterID* emitIsObject(RegisterID* dst, RegisterID* src); |
| 869 | RegisterID* emitIsNumber(RegisterID* dst, RegisterID* src); |
| 870 | RegisterID* emitIsUndefined(RegisterID* dst, RegisterID* src); |
| 871 | RegisterID* emitIsUndefinedOrNull(RegisterID* dst, RegisterID* src); |
| 872 | RegisterID* emitIsEmpty(RegisterID* dst, RegisterID* src); |
| 873 | RegisterID* emitIsDerivedArray(RegisterID* dst, RegisterID* src) { return emitIsCellWithType(dst, src, DerivedArrayType); } |
| 874 | void emitRequireObjectCoercible(RegisterID* value, const String& error); |
| 875 | |
| 876 | RegisterID* emitIteratorNext(RegisterID* dst, RegisterID* nextMethod, RegisterID* iterator, const ThrowableExpressionData* node, JSC::EmitAwait = JSC::EmitAwait::No); |
| 877 | RegisterID* emitIteratorNextWithValue(RegisterID* dst, RegisterID* nextMethod, RegisterID* iterator, RegisterID* value, const ThrowableExpressionData* node); |
| 878 | void emitIteratorClose(RegisterID* iterator, const ThrowableExpressionData* node, EmitAwait = EmitAwait::No); |
| 879 | |
| 880 | RegisterID* emitRestParameter(RegisterID* result, unsigned numParametersToSkip); |
| 881 | |
| 882 | bool emitReadOnlyExceptionIfNeeded(const Variable&); |
| 883 | |
| 884 | // Start a try block. 'start' must have been emitted. |
| 885 | TryData* pushTry(Label& start, Label& handlerLabel, HandlerType); |
| 886 | // End a try block. 'end' must have been emitted. |
| 887 | void popTry(TryData*, Label& end); |
| 888 | |
| 889 | void emitOutOfLineCatchHandler(RegisterID* thrownValueRegister, RegisterID* completionTypeRegister, TryData*); |
| 890 | void emitOutOfLineFinallyHandler(RegisterID* exceptionRegister, RegisterID* completionTypeRegister, TryData*); |
| 891 | |
| 892 | private: |
| 893 | static const int CurrentLexicalScopeIndex = -2; |
| 894 | static const int OutermostLexicalScopeIndex = -1; |
| 895 | |
| 896 | int currentLexicalScopeIndex() const |
| 897 | { |
| 898 | int size = static_cast<int>(m_lexicalScopeStack.size()); |
| 899 | ASSERT(static_cast<size_t>(size) == m_lexicalScopeStack.size()); |
| 900 | ASSERT(size >= 0); |
| 901 | if (!size) |
| 902 | return OutermostLexicalScopeIndex; |
| 903 | return size - 1; |
| 904 | } |
| 905 | |
| 906 | void emitOutOfLineExceptionHandler(RegisterID* exceptionRegister, RegisterID* thrownValueRegister, RegisterID* completionTypeRegister, TryData*); |
| 907 | |
| 908 | public: |
| 909 | void restoreScopeRegister(); |
| 910 | void restoreScopeRegister(int lexicalScopeIndex); |
| 911 | |
| 912 | int labelScopeDepthToLexicalScopeIndex(int labelScopeDepth); |
| 913 | |
| 914 | void emitThrow(RegisterID*); |
| 915 | RegisterID* emitArgumentCount(RegisterID*); |
| 916 | |
| 917 | void emitThrowStaticError(ErrorType, RegisterID*); |
| 918 | void emitThrowStaticError(ErrorType, const Identifier& message); |
| 919 | void emitThrowReferenceError(const String& message); |
| 920 | void emitThrowTypeError(const String& message); |
| 921 | void emitThrowTypeError(const Identifier& message); |
| 922 | void emitThrowRangeError(const Identifier& message); |
| 923 | void emitThrowOutOfMemoryError(); |
| 924 | |
| 925 | void emitPushCatchScope(VariableEnvironment&); |
| 926 | void emitPopCatchScope(VariableEnvironment&); |
| 927 | |
| 928 | RegisterID* emitGetIterator(RegisterID*, ThrowableExpressionData*); |
| 929 | RegisterID* emitGetAsyncIterator(RegisterID*, ThrowableExpressionData*); |
| 930 | |
| 931 | void emitAwait(RegisterID*); |
| 932 | void emitGetScope(); |
| 933 | RegisterID* emitPushWithScope(RegisterID* objectScope); |
| 934 | void emitPopWithScope(); |
| 935 | void emitPutThisToArrowFunctionContextScope(); |
| 936 | void emitPutNewTargetToArrowFunctionContextScope(); |
| 937 | void emitPutDerivedConstructorToArrowFunctionContextScope(); |
| 938 | RegisterID* emitLoadDerivedConstructorFromArrowFunctionLexicalEnvironment(); |
| 939 | |
| 940 | void emitDebugHook(DebugHookType, const JSTextPosition&); |
| 941 | void emitDebugHook(DebugHookType, unsigned line, unsigned charOffset, unsigned lineStart); |
| 942 | void emitDebugHook(StatementNode*); |
| 943 | void emitDebugHook(ExpressionNode*); |
| 944 | void emitWillLeaveCallFrameDebugHook(); |
| 945 | |
| 946 | void emitLoad(RegisterID* completionTypeRegister, CompletionType type) |
| 947 | { |
| 948 | emitLoad(completionTypeRegister, JSValue(static_cast<int>(type))); |
| 949 | } |
| 950 | |
| 951 | template<typename CompareOp> |
| 952 | void emitJumpIf(RegisterID* completionTypeRegister, CompletionType, Label& jumpTarget); |
| 953 | |
| 954 | bool emitJumpViaFinallyIfNeeded(int targetLabelScopeDepth, Label& jumpTarget); |
| 955 | bool emitReturnViaFinallyIfNeeded(RegisterID* returnRegister); |
| 956 | void emitFinallyCompletion(FinallyContext&, Label& normalCompletionLabel); |
| 957 | |
| 958 | public: |
| 959 | void pushFinallyControlFlowScope(FinallyContext&); |
| 960 | void popFinallyControlFlowScope(); |
| 961 | |
| 962 | void pushIndexedForInScope(RegisterID* local, RegisterID* index); |
| 963 | void popIndexedForInScope(RegisterID* local); |
| 964 | void pushStructureForInScope(RegisterID* local, RegisterID* index, RegisterID* property, RegisterID* enumerator); |
| 965 | void popStructureForInScope(RegisterID* local); |
| 966 | |
| 967 | LabelScope* breakTarget(const Identifier&); |
| 968 | LabelScope* continueTarget(const Identifier&); |
| 969 | |
| 970 | void beginSwitch(RegisterID*, SwitchInfo::SwitchType); |
| 971 | void endSwitch(uint32_t clauseCount, const Vector<Ref<Label>, 8>&, ExpressionNode**, Label& defaultLabel, int32_t min, int32_t range); |
| 972 | |
| 973 | void emitYieldPoint(RegisterID*, JSAsyncGeneratorFunction::AsyncGeneratorSuspendReason); |
| 974 | |
| 975 | void emitGeneratorStateLabel(); |
| 976 | void emitGeneratorStateChange(int32_t state); |
| 977 | RegisterID* emitYield(RegisterID* argument, JSAsyncGeneratorFunction::AsyncGeneratorSuspendReason = JSAsyncGeneratorFunction::AsyncGeneratorSuspendReason::Yield); |
| 978 | RegisterID* emitDelegateYield(RegisterID* argument, ThrowableExpressionData*); |
| 979 | RegisterID* generatorStateRegister() { return &m_parameters[static_cast<int32_t>(JSGeneratorFunction::GeneratorArgument::State)]; } |
| 980 | RegisterID* generatorValueRegister() { return &m_parameters[static_cast<int32_t>(JSGeneratorFunction::GeneratorArgument::Value)]; } |
| 981 | RegisterID* generatorResumeModeRegister() { return &m_parameters[static_cast<int32_t>(JSGeneratorFunction::GeneratorArgument::ResumeMode)]; } |
| 982 | RegisterID* generatorFrameRegister() { return &m_parameters[static_cast<int32_t>(JSGeneratorFunction::GeneratorArgument::Frame)]; } |
| 983 | |
| 984 | CodeType codeType() const { return m_codeType; } |
| 985 | |
| 986 | bool shouldBeConcernedWithCompletionValue() const { return m_codeType != FunctionCode; } |
| 987 | |
| 988 | bool shouldEmitDebugHooks() const { return m_codeGenerationMode.contains(CodeGenerationMode::Debugger) && !m_isBuiltinFunction; } |
| 989 | bool shouldEmitTypeProfilerHooks() const { return m_codeGenerationMode.contains(CodeGenerationMode::TypeProfiler); } |
| 990 | bool shouldEmitControlFlowProfilerHooks() const { return m_codeGenerationMode.contains(CodeGenerationMode::ControlFlowProfiler); } |
| 991 | |
| 992 | bool isStrictMode() const { return m_codeBlock->isStrictMode(); } |
| 993 | |
| 994 | SourceParseMode parseMode() const { return m_codeBlock->parseMode(); } |
| 995 | |
| 996 | bool isBuiltinFunction() const { return m_isBuiltinFunction; } |
| 997 | |
| 998 | OpcodeID lastOpcodeID() const { return m_lastOpcodeID; } |
| 999 | |
| 1000 | bool isDerivedConstructorContext() { return m_derivedContextType == DerivedContextType::DerivedConstructorContext; } |
| 1001 | bool isDerivedClassContext() { return m_derivedContextType == DerivedContextType::DerivedMethodContext; } |
| 1002 | bool isArrowFunction() { return m_codeBlock->isArrowFunction(); } |
| 1003 | |
| 1004 | enum class TDZCheckOptimization { Optimize, DoNotOptimize }; |
| 1005 | enum class NestedScopeType { IsNested, IsNotNested }; |
| 1006 | private: |
| 1007 | enum class TDZRequirement { UnderTDZ, NotUnderTDZ }; |
| 1008 | enum class ScopeType { CatchScope, LetConstScope, FunctionNameScope }; |
| 1009 | enum class ScopeRegisterType { Var, Block }; |
| 1010 | void pushLexicalScopeInternal(VariableEnvironment&, TDZCheckOptimization, NestedScopeType, RegisterID** constantSymbolTableResult, TDZRequirement, ScopeType, ScopeRegisterType); |
| 1011 | void initializeBlockScopedFunctions(VariableEnvironment&, FunctionStack&, RegisterID* constantSymbolTable); |
| 1012 | void popLexicalScopeInternal(VariableEnvironment&); |
| 1013 | template<typename LookUpVarKindFunctor> |
| 1014 | bool instantiateLexicalVariables(const VariableEnvironment&, SymbolTable*, ScopeRegisterType, LookUpVarKindFunctor); |
| 1015 | void emitPrefillStackTDZVariables(const VariableEnvironment&, SymbolTable*); |
| 1016 | void emitPopScope(RegisterID* dst, RegisterID* scope); |
| 1017 | RegisterID* emitGetParentScope(RegisterID* dst, RegisterID* scope); |
| 1018 | void emitPushFunctionNameScope(const Identifier& property, RegisterID* value, bool isCaptured); |
| 1019 | void emitNewFunctionExpressionCommon(RegisterID*, FunctionMetadataNode*); |
| 1020 | |
| 1021 | bool isNewTargetUsedInInnerArrowFunction(); |
| 1022 | bool isArgumentsUsedInInnerArrowFunction(); |
| 1023 | |
| 1024 | void emitToThis(); |
| 1025 | |
| 1026 | RegisterID* emitMove(RegisterID* dst, RegisterID* src); |
| 1027 | |
| 1028 | bool canDoPeepholeOptimization() const { return m_lastOpcodeID != op_end; } |
| 1029 | |
| 1030 | public: |
| 1031 | bool isSuperUsedInInnerArrowFunction(); |
| 1032 | bool isSuperCallUsedInInnerArrowFunction(); |
| 1033 | bool isThisUsedInInnerArrowFunction(); |
| 1034 | void pushLexicalScope(VariableEnvironmentNode*, TDZCheckOptimization, NestedScopeType = NestedScopeType::IsNotNested, RegisterID** constantSymbolTableResult = nullptr, bool shouldInitializeBlockScopedFunctions = true); |
| 1035 | void popLexicalScope(VariableEnvironmentNode*); |
| 1036 | void prepareLexicalScopeForNextForLoopIteration(VariableEnvironmentNode*, RegisterID* loopSymbolTable); |
| 1037 | int labelScopeDepth() const; |
| 1038 | UnlinkedArrayProfile newArrayProfile(); |
| 1039 | |
| 1040 | private: |
| 1041 | ParserError generate(); |
| 1042 | void reclaimFreeRegisters(); |
| 1043 | Variable variableForLocalEntry(const Identifier&, const SymbolTableEntry&, int symbolTableConstantIndex, bool isLexicallyScoped); |
| 1044 | |
| 1045 | RegisterID* kill(RegisterID* dst) |
| 1046 | { |
| 1047 | m_staticPropertyAnalyzer.kill(dst); |
| 1048 | return dst; |
| 1049 | } |
| 1050 | |
| 1051 | void retrieveLastUnaryOp(int& dstIndex, int& srcIndex); |
| 1052 | ALWAYS_INLINE void rewind(); |
| 1053 | |
| 1054 | void allocateCalleeSaveSpace(); |
| 1055 | void allocateAndEmitScope(); |
| 1056 | |
| 1057 | template<typename JumpOp> |
| 1058 | void setTargetForJumpInstruction(InstructionStream::MutableRef&, int target); |
| 1059 | |
| 1060 | using BigIntMapEntry = std::tuple<UniquedStringImpl*, uint8_t, bool>; |
| 1061 | |
| 1062 | using NumberMap = HashMap<double, JSValue>; |
| 1063 | using IdentifierStringMap = HashMap<UniquedStringImpl*, JSString*, IdentifierRepHash>; |
| 1064 | using IdentifierBigIntMap = HashMap<BigIntMapEntry, JSBigInt*>; |
| 1065 | using TemplateObjectDescriptorSet = HashSet<Ref<TemplateObjectDescriptor>>; |
| 1066 | using TemplateDescriptorMap = HashMap<uint64_t, JSTemplateObjectDescriptor*, WTF::IntHash<uint64_t>, WTF::UnsignedWithZeroKeyHashTraits<uint64_t>>; |
| 1067 | |
| 1068 | // Helper for emitCall() and emitConstruct(). This works because the set of |
| 1069 | // expected functions have identical behavior for both call and construct |
| 1070 | // (i.e. "Object()" is identical to "new Object()"). |
| 1071 | ExpectedFunction emitExpectedFunctionSnippet(RegisterID* dst, RegisterID* func, ExpectedFunction, CallArguments&, Label& done); |
| 1072 | |
| 1073 | template<typename CallOp> |
| 1074 | RegisterID* emitCall(RegisterID* dst, RegisterID* func, ExpectedFunction, CallArguments&, const JSTextPosition& divot, const JSTextPosition& divotStart, const JSTextPosition& divotEnd, DebuggableCall); |
| 1075 | |
| 1076 | RegisterID* emitCallIterator(RegisterID* iterator, RegisterID* argument, ThrowableExpressionData*); |
| 1077 | RegisterID* newRegister(); |
| 1078 | |
| 1079 | // Adds an anonymous local var slot. To give this slot a name, add it to symbolTable(). |
| 1080 | RegisterID* addVar() |
| 1081 | { |
| 1082 | ++m_codeBlock->m_numVars; |
| 1083 | RegisterID* result = newRegister(); |
| 1084 | ASSERT(VirtualRegister(result->index()).toLocal() == m_codeBlock->m_numVars - 1); |
| 1085 | result->ref(); // We should never free this slot. |
| 1086 | return result; |
| 1087 | } |
| 1088 | |
| 1089 | // Initializes the stack form the parameter; does nothing for the symbol table. |
| 1090 | RegisterID* initializeNextParameter(); |
| 1091 | UniquedStringImpl* visibleNameForParameter(DestructuringPatternNode*); |
| 1092 | |
| 1093 | RegisterID& registerFor(VirtualRegister reg) |
| 1094 | { |
| 1095 | if (reg.isLocal()) |
| 1096 | return m_calleeLocals[reg.toLocal()]; |
| 1097 | |
| 1098 | if (reg.offset() == CallFrameSlot::callee) |
| 1099 | return m_calleeRegister; |
| 1100 | |
| 1101 | ASSERT(m_parameters.size()); |
| 1102 | return m_parameters[reg.toArgument()]; |
| 1103 | } |
| 1104 | |
| 1105 | bool hasConstant(const Identifier&) const; |
| 1106 | unsigned addConstant(const Identifier&); |
| 1107 | RegisterID* addConstantValue(JSValue, SourceCodeRepresentation = SourceCodeRepresentation::Other); |
| 1108 | RegisterID* addConstantEmptyValue(); |
| 1109 | |
| 1110 | UnlinkedFunctionExecutable* makeFunction(FunctionMetadataNode* metadata) |
| 1111 | { |
| 1112 | DerivedContextType newDerivedContextType = DerivedContextType::None; |
| 1113 | |
| 1114 | if (SourceParseModeSet(SourceParseMode::ArrowFunctionMode, SourceParseMode::AsyncArrowFunctionMode, SourceParseMode::AsyncArrowFunctionBodyMode).contains(metadata->parseMode())) { |
| 1115 | if (constructorKind() == ConstructorKind::Extends || isDerivedConstructorContext()) |
| 1116 | newDerivedContextType = DerivedContextType::DerivedConstructorContext; |
| 1117 | else if (m_codeBlock->isClassContext() || isDerivedClassContext()) |
| 1118 | newDerivedContextType = DerivedContextType::DerivedMethodContext; |
| 1119 | } |
| 1120 | |
| 1121 | Optional<CompactVariableMap::Handle> optionalVariablesUnderTDZ = getVariablesUnderTDZ(); |
| 1122 | |
| 1123 | // FIXME: These flags, ParserModes and propagation to XXXCodeBlocks should be reorganized. |
| 1124 | // https://bugs.webkit.org/show_bug.cgi?id=151547 |
| 1125 | SourceParseMode parseMode = metadata->parseMode(); |
| 1126 | ConstructAbility constructAbility = constructAbilityForParseMode(parseMode); |
| 1127 | if (parseMode == SourceParseMode::MethodMode && metadata->constructorKind() != ConstructorKind::None) |
| 1128 | constructAbility = ConstructAbility::CanConstruct; |
| 1129 | |
| 1130 | return UnlinkedFunctionExecutable::create(m_vm, m_scopeNode->source(), metadata, isBuiltinFunction() ? UnlinkedBuiltinFunction : UnlinkedNormalFunction, constructAbility, scriptMode(), WTFMove(optionalVariablesUnderTDZ), newDerivedContextType); |
| 1131 | } |
| 1132 | |
| 1133 | Optional<CompactVariableMap::Handle> getVariablesUnderTDZ(); |
| 1134 | |
| 1135 | RegisterID* emitConstructVarargs(RegisterID* dst, RegisterID* func, RegisterID* thisRegister, RegisterID* arguments, RegisterID* firstFreeRegister, int32_t firstVarArgOffset, const JSTextPosition& divot, const JSTextPosition& divotStart, const JSTextPosition& divotEnd, DebuggableCall); |
| 1136 | template<typename CallOp> |
| 1137 | RegisterID* emitCallVarargs(RegisterID* dst, RegisterID* func, RegisterID* thisRegister, RegisterID* arguments, RegisterID* firstFreeRegister, int32_t firstVarArgOffset, const JSTextPosition& divot, const JSTextPosition& divotStart, const JSTextPosition& divotEnd, DebuggableCall); |
| 1138 | |
| 1139 | void emitLogShadowChickenPrologueIfNecessary(); |
| 1140 | void emitLogShadowChickenTailIfNecessary(); |
| 1141 | |
| 1142 | void initializeParameters(FunctionParameters&); |
| 1143 | void initializeVarLexicalEnvironment(int symbolTableConstantIndex, SymbolTable* functionSymbolTable, bool hasCapturedVariables); |
| 1144 | void initializeDefaultParameterValuesAndSetupFunctionScopeStack(FunctionParameters&, bool isSimpleParameterList, FunctionNode*, SymbolTable*, int symbolTableConstantIndex, const ScopedLambda<bool (UniquedStringImpl*)>& captures, bool shouldCreateArgumentsVariableInParameterScope); |
| 1145 | void initializeArrowFunctionContextScopeIfNeeded(SymbolTable* functionSymbolTable = nullptr, bool canReuseLexicalEnvironment = false); |
| 1146 | bool needsDerivedConstructorInArrowFunctionLexicalEnvironment(); |
| 1147 | |
| 1148 | enum class TDZNecessityLevel { |
| 1149 | NotNeeded, |
| 1150 | Optimize, |
| 1151 | DoNotOptimize |
| 1152 | }; |
| 1153 | typedef HashMap<RefPtr<UniquedStringImpl>, TDZNecessityLevel, IdentifierRepHash> TDZMap; |
| 1154 | |
| 1155 | public: |
| 1156 | JSString* addStringConstant(const Identifier&); |
| 1157 | JSValue addBigIntConstant(const Identifier&, uint8_t radix, bool sign); |
| 1158 | RegisterID* addTemplateObjectConstant(Ref<TemplateObjectDescriptor>&&, int); |
| 1159 | |
| 1160 | const InstructionStream& instructions() const { return m_writer; } |
| 1161 | |
| 1162 | RegisterID* emitThrowExpressionTooDeepException(); |
| 1163 | |
| 1164 | void write(uint8_t byte) { m_writer.write(byte); } |
| 1165 | void write(uint32_t i) { m_writer.write(i); } |
| 1166 | void alignWideOpcode(); |
| 1167 | |
| 1168 | class PreservedTDZStack { |
| 1169 | private: |
| 1170 | Vector<TDZMap> m_preservedTDZStack; |
| 1171 | friend class BytecodeGenerator; |
| 1172 | }; |
| 1173 | |
| 1174 | void preserveTDZStack(PreservedTDZStack&); |
| 1175 | void restoreTDZStack(const PreservedTDZStack&); |
| 1176 | |
| 1177 | template<typename Func> |
| 1178 | void withWriter(InstructionStreamWriter& writer, const Func& fn) |
| 1179 | { |
| 1180 | auto prevLastOpcodeID = m_lastOpcodeID; |
| 1181 | auto prevLastInstruction = m_lastInstruction; |
| 1182 | m_writer.swap(writer); |
| 1183 | m_lastOpcodeID = op_end; |
| 1184 | m_lastInstruction = m_writer.ref(); |
| 1185 | fn(); |
| 1186 | m_writer.swap(writer); |
| 1187 | m_lastOpcodeID = prevLastOpcodeID; |
| 1188 | m_lastInstruction = prevLastInstruction; |
| 1189 | } |
| 1190 | |
| 1191 | private: |
| 1192 | InstructionStreamWriter m_writer; |
| 1193 | |
| 1194 | OptionSet<CodeGenerationMode> m_codeGenerationMode; |
| 1195 | |
| 1196 | struct LexicalScopeStackEntry { |
| 1197 | SymbolTable* m_symbolTable; |
| 1198 | RegisterID* m_scope; |
| 1199 | bool m_isWithScope; |
| 1200 | int m_symbolTableConstantIndex; |
| 1201 | }; |
| 1202 | Vector<LexicalScopeStackEntry> m_lexicalScopeStack; |
| 1203 | |
| 1204 | Vector<TDZMap> m_TDZStack; |
| 1205 | Optional<size_t> m_varScopeLexicalScopeStackIndex; |
| 1206 | void pushTDZVariables(const VariableEnvironment&, TDZCheckOptimization, TDZRequirement); |
| 1207 | |
| 1208 | ScopeNode* const m_scopeNode; |
| 1209 | Strong<UnlinkedCodeBlock> m_codeBlock; |
| 1210 | |
| 1211 | // Some of these objects keep pointers to one another. They are arranged |
| 1212 | // to ensure a sane destruction order that avoids references to freed memory. |
| 1213 | HashSet<RefPtr<UniquedStringImpl>, IdentifierRepHash> m_functions; |
| 1214 | RegisterID m_ignoredResultRegister; |
| 1215 | RegisterID m_thisRegister; |
| 1216 | RegisterID m_calleeRegister; |
| 1217 | RegisterID* m_scopeRegister { nullptr }; |
| 1218 | RegisterID* m_topMostScope { nullptr }; |
| 1219 | RegisterID* m_argumentsRegister { nullptr }; |
| 1220 | RegisterID* m_lexicalEnvironmentRegister { nullptr }; |
| 1221 | RegisterID* m_generatorRegister { nullptr }; |
| 1222 | RegisterID* m_emptyValueRegister { nullptr }; |
| 1223 | RegisterID* m_newTargetRegister { nullptr }; |
| 1224 | RegisterID* m_isDerivedConstuctor { nullptr }; |
| 1225 | RegisterID* m_linkTimeConstantRegisters[LinkTimeConstantCount]; |
| 1226 | RegisterID* m_arrowFunctionContextLexicalEnvironmentRegister { nullptr }; |
| 1227 | RegisterID* m_promiseCapabilityRegister { nullptr }; |
| 1228 | |
| 1229 | FinallyContext* m_currentFinallyContext { nullptr }; |
| 1230 | |
| 1231 | SegmentedVector<RegisterID*, 16> m_localRegistersForCalleeSaveRegisters; |
| 1232 | SegmentedVector<RegisterID, 32> m_constantPoolRegisters; |
| 1233 | SegmentedVector<RegisterID, 32> m_calleeLocals; |
| 1234 | SegmentedVector<RegisterID, 32> m_parameters; |
| 1235 | SegmentedVector<Label, 32> m_labels; |
| 1236 | SegmentedVector<LabelScope, 32> m_labelScopes; |
| 1237 | unsigned m_finallyDepth { 0 }; |
| 1238 | unsigned m_localScopeDepth { 0 }; |
| 1239 | const CodeType m_codeType; |
| 1240 | |
| 1241 | unsigned localScopeDepth() const; |
| 1242 | void pushLocalControlFlowScope(); |
| 1243 | void popLocalControlFlowScope(); |
| 1244 | |
| 1245 | // FIXME: Restore overflow checking with UnsafeVectorOverflow once SegmentVector supports it. |
| 1246 | // https://bugs.webkit.org/show_bug.cgi?id=165980 |
| 1247 | SegmentedVector<ControlFlowScope, 16> m_controlFlowScopeStack; |
| 1248 | Vector<SwitchInfo> m_switchContextStack; |
| 1249 | Vector<Ref<ForInContext>> m_forInContextStack; |
| 1250 | Vector<TryContext> m_tryContextStack; |
| 1251 | unsigned m_yieldPoints { 0 }; |
| 1252 | |
| 1253 | Strong<SymbolTable> m_generatorFrameSymbolTable; |
| 1254 | int m_generatorFrameSymbolTableIndex { 0 }; |
| 1255 | |
| 1256 | enum FunctionVariableType : uint8_t { NormalFunctionVariable, TopLevelFunctionVariable }; |
| 1257 | Vector<std::pair<FunctionMetadataNode*, FunctionVariableType>> m_functionsToInitialize; |
| 1258 | bool m_needToInitializeArguments { false }; |
| 1259 | RestParameterNode* m_restParameter { nullptr }; |
| 1260 | |
| 1261 | Vector<TryRange> m_tryRanges; |
| 1262 | SegmentedVector<TryData, 8> m_tryData; |
| 1263 | |
| 1264 | int m_nextConstantOffset { 0 }; |
| 1265 | |
| 1266 | typedef HashMap<FunctionMetadataNode*, unsigned> FunctionOffsetMap; |
| 1267 | FunctionOffsetMap m_functionOffsets; |
| 1268 | |
| 1269 | // Constant pool |
| 1270 | IdentifierMap m_identifierMap; |
| 1271 | |
| 1272 | typedef HashMap<EncodedJSValueWithRepresentation, unsigned, EncodedJSValueWithRepresentationHash, EncodedJSValueWithRepresentationHashTraits> JSValueMap; |
| 1273 | JSValueMap m_jsValueMap; |
| 1274 | IdentifierStringMap m_stringMap; |
| 1275 | IdentifierBigIntMap m_bigIntMap; |
| 1276 | TemplateObjectDescriptorSet m_templateObjectDescriptorSet; |
| 1277 | TemplateDescriptorMap m_templateDescriptorMap; |
| 1278 | |
| 1279 | StaticPropertyAnalyzer m_staticPropertyAnalyzer; |
| 1280 | |
| 1281 | VM* m_vm; |
| 1282 | |
| 1283 | OpcodeID m_lastOpcodeID = op_end; |
| 1284 | InstructionStream::MutableRef m_lastInstruction { m_writer.ref() }; |
| 1285 | |
| 1286 | bool m_usesExceptions { false }; |
| 1287 | bool m_expressionTooDeep { false }; |
| 1288 | bool m_isBuiltinFunction { false }; |
| 1289 | bool m_usesNonStrictEval { false }; |
| 1290 | bool m_inTailPosition { false }; |
| 1291 | bool m_needsToUpdateArrowFunctionContext; |
| 1292 | bool m_hasCachedVariablesUnderTDZ { false }; |
| 1293 | DerivedContextType m_derivedContextType { DerivedContextType::None }; |
| 1294 | |
| 1295 | CompactVariableMap::Handle m_cachedVariablesUnderTDZ; |
| 1296 | |
| 1297 | struct CatchEntry { |
| 1298 | TryData* tryData; |
| 1299 | VirtualRegister exceptionRegister; |
| 1300 | VirtualRegister thrownValueRegister; |
| 1301 | VirtualRegister completionTypeRegister; |
| 1302 | }; |
| 1303 | Vector<CatchEntry> m_exceptionHandlersToEmit; |
| 1304 | }; |
| 1305 | |
| 1306 | } // namespace JSC |
| 1307 | |
| 1308 | namespace WTF { |
| 1309 | |
| 1310 | void printInternal(PrintStream&, JSC::Variable::VariableKind); |
| 1311 | |
| 1312 | } // namespace WTF |
| 1313 | |