/* * Copyright (c) 2016-present Samsung Electronics Co., Ltd * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include "Escargot.h" #include "esprima.h" #include "interpreter/ByteCode.h" #include "parser/ast/AST.h" #include "parser/CodeBlock.h" #include "double-conversion.h" #include "ieee.h" #include "wtfbridge.h" using namespace JSC::Yarr; namespace Escargot { namespace esprima { enum Token { BooleanLiteralToken = 1, EOFToken = 2, IdentifierToken = 3, KeywordToken = 4, NullLiteralToken = 5, NumericLiteralToken = 6, PunctuatorToken = 7, StringLiteralToken = 8, RegularExpressionToken = 9, TemplateToken = 10 }; enum PlaceHolders { ArrowParameterPlaceHolder }; enum PunctuatorsKind { LeftParenthesis, RightParenthesis, LeftBrace, RightBrace, Period, PeriodPeriodPeriod, Comma, Colon, SemiColon, LeftSquareBracket, RightSquareBracket, GuessMark, Wave, UnsignedRightShift, RightShift, LeftShift, Plus, Minus, Multiply, Divide, Mod, ExclamationMark, StrictEqual, NotStrictEqual, Equal, NotEqual, LogicalAnd, LogicalOr, PlusPlus, MinusMinus, BitwiseAnd, BitwiseOr, BitwiseXor, LeftInequality, RightInequality, InPunctuator, InstanceOfPunctuator, Substitution, UnsignedRightShiftEqual, RightShiftEqual, LeftShiftEqual, PlusEqual, MinusEqual, MultiplyEqual, DivideEqual, ModEqual, // ExclamationMarkEqual, BitwiseAndEqual, BitwiseOrEqual, BitwiseXorEqual, LeftInequalityEqual, RightInequalityEqual, SubstitutionEnd, Arrow, PunctuatorsKindEnd, }; enum KeywordKind { NotKeyword, If, In, Do, Var, For, New, Try, This, Else, Case, Void, With, Enum, Await, While, Break, Catch, Throw, Const, Class, Super, Return, Typeof, Delete, Switch, Export, Import, Default, Finally, Extends, Function, Continue, Debugger, InstanceofKeyword, StrictModeReservedWord, Implements, Interface, Package, Private, Protected, Public, Static, Yield, Let, KeywordKindEnd }; ALWAYS_INLINE bool isDecimalDigit(char16_t ch) { return (ch >= '0' && ch <= '9'); // 0..9 } ALWAYS_INLINE bool isHexDigit(char16_t ch) { return isDecimalDigit(ch) || (ch >= 'a' && ch <= 'f') || (ch >= 'A' && ch <= 'F'); } ALWAYS_INLINE bool isOctalDigit(char16_t ch) { return (ch >= '0' && ch < '8'); // 0..7 } ALWAYS_INLINE char16_t octalValue(char16_t ch) { ASSERT(isOctalDigit(ch)); return ch - '0'; } ALWAYS_INLINE uint8_t toHexNumericValue(char16_t ch) { return ch < 'A' ? ch - '0' : (ch - 'A' + 10) & 0xF; } struct ParserCharPiece { char16_t data[3]; size_t length; ParserCharPiece(const char16_t a) { data[0] = a; data[1] = 0; length = 1; } ParserCharPiece(const char32_t a) { if (a < 0x10000) { data[0] = a; data[1] = 0; length = 1; } else { data[0] = (char16_t)(0xD800 + ((a - 0x10000) >> 10)); data[1] = (char16_t)(0xDC00 + ((a - 0x10000) & 1023)); data[2] = 0; length = 2; } } ParserCharPiece(const char16_t a, const char16_t b) { data[0] = a; data[1] = b; data[2] = 0; length = 2; } }; // ECMA-262 11.6 Identifier Names and Identifiers ALWAYS_INLINE ParserCharPiece fromCodePoint(char32_t cp) { if (cp < 0x10000) { return ParserCharPiece((char16_t)cp); } else { return ParserCharPiece((char16_t)(0xD800 + ((cp - 0x10000) >> 10)), (char16_t)(0xDC00 + ((cp - 0x10000) & 1023))); } } extern bool isIdentifierPartData[0x10000]; extern bool isIdentifierStartData[0x10000]; ALWAYS_INLINE bool isIdentifierPart(char32_t ch) { return ch < 0x10000 && isIdentifierPartData[ch]; } ALWAYS_INLINE bool isIdentifierStart(char32_t ch) { return ch < 0x10000 && isIdentifierStartData[ch]; } struct Curly { char m_curly[4]; Curly() {} Curly(const char curly[4]) { m_curly[0] = curly[0]; m_curly[1] = curly[1]; m_curly[2] = curly[2]; m_curly[3] = curly[3]; } }; namespace Messages { const char* UnexpectedToken = "Unexpected token %s"; const char* UnexpectedTokenIllegal = "Unexpected token ILLEGAL"; const char* UnexpectedNumber = "Unexpected number"; const char* UnexpectedString = "Unexpected string"; const char* UnexpectedIdentifier = "Unexpected identifier"; const char* UnexpectedReserved = "Unexpected reserved word"; const char* UnexpectedTemplate = "Unexpected quasi %s"; const char* UnexpectedEOS = "Unexpected end of input"; const char* NewlineAfterThrow = "Illegal newline after throw"; const char* InvalidRegExp = "Invalid regular expression"; const char* UnterminatedRegExp = "Invalid regular expression: missing /"; const char* InvalidLHSInAssignment = "Invalid left-hand side in assignment"; const char* InvalidLHSInForIn = "Invalid left-hand side in for-in"; const char* InvalidLHSInForLoop = "Invalid left-hand side in for-loop"; const char* MultipleDefaultsInSwitch = "More than one default clause in switch statement"; const char* NoCatchOrFinally = "Missing catch or finally after try"; const char* UnknownLabel = "Undefined label \'%s\'"; const char* Redeclaration = "%s \'%s\' has already been declared"; const char* IllegalContinue = "Illegal continue statement"; const char* IllegalBreak = "Illegal break statement"; const char* IllegalReturn = "Illegal return statement"; const char* StrictModeWith = "Strict mode code may not include a with statement"; const char* StrictCatchVariable = "Catch variable may not be eval or arguments in strict mode"; const char* StrictVarName = "Variable name may not be eval or arguments in strict mode"; const char* StrictParamName = "Parameter name eval or arguments is not allowed in strict mode"; const char* StrictParamDupe = "Strict mode function may not have duplicate parameter names"; const char* StrictFunctionName = "Function name may not be eval or arguments in strict mode"; const char* StrictOctalLiteral = "Octal literals are not allowed in strict mode."; const char* StrictLeadingZeroLiteral = "Decimals with leading zeros are not allowed in strict mode."; const char* StrictDelete = "Delete of an unqualified identifier in strict mode."; const char* StrictLHSAssignment = "Assignment to eval or arguments is not allowed in strict mode"; const char* StrictLHSPostfix = "Postfix increment/decrement may not have eval or arguments operand in strict mode"; const char* StrictLHSPrefix = "Prefix increment/decrement may not have eval or arguments operand in strict mode"; const char* StrictReservedWord = "Use of future reserved word in strict mode"; const char* TemplateOctalLiteral = "Octal literals are not allowed in template strings."; const char* ParameterAfterRestParameter = "Rest parameter must be last formal parameter"; const char* DefaultRestParameter = "Unexpected token ="; const char* ObjectPatternAsRestParameter = "Unexpected token {"; const char* DuplicateProtoProperty = "Duplicate __proto__ fields are not allowed in object literals"; const char* ConstructorSpecialMethod = "Class constructor may not be an accessor"; const char* DuplicateConstructor = "A class may only have one constructor"; const char* StaticPrototype = "Classes may not have static property named prototype"; const char* MissingFromClause = "Unexpected token"; const char* NoAsAfterImportNamespace = "Unexpected token"; const char* InvalidModuleSpecifier = "Unexpected token"; const char* IllegalImportDeclaration = "Unexpected token"; const char* IllegalExportDeclaration = "Unexpected token"; const char* DuplicateBinding = "Duplicate binding %s"; const char* ForInOfLoopInitializer = "%s loop variable declaration may not have an initializer"; } /* export interface Comment { multiLine: boolean; slice: number[]; range: number[]; loc: any; } */ struct ParserError : public gc { String* description; size_t index; size_t line; size_t col; ParserError(size_t index, size_t line, size_t col, String* description) { this->index = index; this->line = line; this->col = col; this->description = description; } ParserError(size_t index, size_t line, size_t col, const char* description) { this->index = index; this->line = line; this->col = col; this->description = new ASCIIString(description); } }; struct ScanTemplteResult { UTF16StringData valueCooked; StringView raw; size_t head; size_t tail; }; struct ScanRegExpResult { String* body; String* flags; }; class Scanner; class ScannerResult : public RefCounted { public: Scanner* scanner; Token type : 4; bool startWithZero : 1; bool octal : 1; bool plain : 1; bool head : 1; int prec; size_t lineNumber; size_t lineStart; size_t start; size_t end; size_t index; StringView valueString; KeywordKind valueKeywordKind; union { PunctuatorsKind valuePunctuatorsKind; double valueNumber; ScanTemplteResult valueTemplate; ScanRegExpResult valueRegexp; }; ~ScannerResult(); inline void operator delete(void* obj) { } inline void operator delete(void*, void*) {} inline void operator delete[](void* obj) {} inline void operator delete[](void*, void*) {} ScannerResult() { } ScannerResult(Scanner* scanner, Token type, size_t lineNumber, size_t lineStart, size_t start, size_t end) { this->scanner = scanner; this->type = type; this->startWithZero = this->octal = false; this->plain = false; this->head = false; this->prec = -1; this->valueKeywordKind = NotKeyword; this->lineNumber = lineNumber; this->valueNumber = 0; this->lineStart = lineStart; this->start = start; this->end = this->index = end; } ScannerResult(Scanner* scanner, Token type, StringView valueString, size_t lineNumber, size_t lineStart, size_t start, size_t end) { this->scanner = scanner; this->type = type; this->startWithZero = this->octal = false; this->plain = false; this->head = false; this->prec = -1; this->valueKeywordKind = NotKeyword; this->valueString = valueString; this->lineNumber = lineNumber; this->valueNumber = 0; this->lineStart = lineStart; this->start = start; this->end = this->index = end; } ScannerResult(Scanner* scanner, Token type, double value, size_t lineNumber, size_t lineStart, size_t start, size_t end) { this->scanner = scanner; this->type = type; this->startWithZero = this->octal = false; this->plain = false; this->head = false; this->valueKeywordKind = NotKeyword; this->valueNumber = value; this->lineNumber = lineNumber; this->lineStart = lineStart; this->start = start; this->end = this->index = end; if (end != SIZE_MAX) { this->end = end; } } ScannerResult(Scanner* scanner, Token type, ScanTemplteResult value, size_t lineNumber, size_t lineStart, size_t start, size_t end) { this->scanner = scanner; this->type = type; this->startWithZero = this->octal = false; this->plain = false; this->head = false; this->prec = -1; this->valueKeywordKind = NotKeyword; this->valueNumber = 0; this->valueTemplate = value; this->lineNumber = lineNumber; this->lineStart = lineStart; this->start = start; this->end = this->index = end; if (end != SIZE_MAX) { this->end = end; } } }; class ErrorHandler : public gc { public: // errors: Error[]; // tolerant: boolean; ErrorHandler() { // this->errors = []; // this->tolerant = false; } // recordError(error: Error): void { // this->errors.push(error); // }; void tolerate(Error* error) { /* if (this->tolerant) { this->recordError(error); } else { throw error; }*/ throw error; } Error* constructError(String* msg, size_t column) { Error* error = new (NoGC) Error(msg); error->column = column; return error; // try { // throw error; // } catch (base) { /* istanbul ignore else */ // if (Object.create && Object.defineProperty) { // error = Object.create(base); // Object.defineProperty(error, 'column', { value: column }); // } // } finally { // return error; // } } Error* createError(size_t index, size_t line, size_t col, String* description, ErrorObject::Code code) { UTF16StringDataNonGCStd msg = u"Line "; char lineStringBuf[512]; snprintf(lineStringBuf, sizeof(lineStringBuf), "%zu", line); std::string lineString = lineStringBuf; msg += UTF16StringDataNonGCStd(lineString.begin(), lineString.end()); msg += u": "; if (description->length()) { msg += UTF16StringDataNonGCStd(description->toUTF16StringData().data()); } Error* error = constructError(new UTF16String(msg.data(), msg.length()), col); error->index = index; error->lineNumber = line; error->description = description; error->errorCode = code; return error; }; void throwError(size_t index, size_t line, size_t col, String* description, ErrorObject::Code code) { throw this->createError(index, line, col, description, code); } void tolerateError(size_t index, size_t line, size_t col, String* description, ErrorObject::Code code) { Error* error = this->createError(index, line, col, description, code); /* if (this->tolerant) { this->recordError(error); } else { throw error; }*/ throw error; } }; #define SCANNER_RESULT_POOL_INITIAL_SIZE 128 class Scanner : public gc { public: BufferedStringView source; ErrorHandler* errorHandler; // trackComment: boolean; size_t length; size_t index; size_t lineNumber; size_t lineStart; std::vector curlyStack; bool isPoolEnabled; ScannerResult* initialResultMemoryPool[SCANNER_RESULT_POOL_INITIAL_SIZE]; size_t initialResultMemoryPoolSize; std::vector> resultMemoryPool; char scannerResultInnerPool[SCANNER_RESULT_POOL_INITIAL_SIZE * sizeof(ScannerResult)]; ~Scanner() { isPoolEnabled = false; } Scanner(BufferedStringView code, ErrorHandler* handler, size_t startLine = 0, size_t startColumn = 0) { isPoolEnabled = true; source = code; errorHandler = handler; // trackComment = false; length = code.length(); index = 0; lineNumber = ((length > 0) ? 1 : 0) + startLine; lineStart = startColumn; initialResultMemoryPoolSize = SCANNER_RESULT_POOL_INITIAL_SIZE; ScannerResult* ptr = (ScannerResult*)scannerResultInnerPool; for (size_t i = 0; i < SCANNER_RESULT_POOL_INITIAL_SIZE; i++) { ptr[i].scanner = this; initialResultMemoryPool[i] = &ptr[i]; } } ScannerResult* createScannerResult() { if (initialResultMemoryPoolSize) { initialResultMemoryPoolSize--; return initialResultMemoryPool[initialResultMemoryPoolSize]; } else if (resultMemoryPool.size() == 0) { auto ret = (ScannerResult*)GC_MALLOC(sizeof(ScannerResult)); return ret; } else { auto ret = resultMemoryPool.back(); resultMemoryPool.pop_back(); return ret; } } bool eof() { return index >= length; } void throwUnexpectedToken(const char* message = Messages::UnexpectedTokenIllegal) { this->errorHandler->throwError(this->index, this->lineNumber, this->index - this->lineStart + 1, new ASCIIString(message), ErrorObject::SyntaxError); } /* tolerateUnexpectedToken() { this->errorHandler.tolerateError(this->index, this->lineNumber, this->index - this->lineStart + 1, Messages.UnexpectedTokenIllegal); }; */ void tolerateUnexpectedToken() { throwUnexpectedToken(); } // ECMA-262 11.4 Comments // skipSingleLineComment(offset: number): Comment[] { void skipSingleLineComment(size_t /*offset*/) { // let comments: Comment[]; // size_t start, loc; /* if (this->trackComment) { comments = []; start = this->index - offset; loc = { start: { line: this->lineNumber, column: this->index - this->lineStart - offset }, end: {} }; }*/ while (!this->eof()) { char16_t ch = this->source.bufferedCharAt(this->index); ++this->index; if (isLineTerminator(ch)) { /* if (this->trackComment) { loc.end = { line: this->lineNumber, column: this->index - this->lineStart - 1 }; const entry: Comment = { multiLine: false, slice: [start + offset, this->index - 1], range: [start, this->index - 1], loc: loc }; comments.push(entry); }*/ if (ch == 13 && this->source.bufferedCharAt(this->index) == 10) { ++this->index; } ++this->lineNumber; this->lineStart = this->index; // return comments; return; } } /* if (this->trackComment) { loc.end = { line: this->lineNumber, column: this->index - this->lineStart }; const entry: Comment = { multiLine: false, slice: [start + offset, this->index], range: [start, this->index], loc: loc }; comments.push(entry); }*/ // return comments; return; } // skipMultiLineComment(): Comment[] { void skipMultiLineComment() { // let comments: Comment[]; // size_t start, loc; /* if (this->trackComment) { comments = []; start = this->index - 2; loc = { start: { line: this->lineNumber, column: this->index - this->lineStart - 2 }, end: {} }; } */ while (!this->eof()) { char16_t ch = this->source.bufferedCharAt(this->index); if (isLineTerminator(ch)) { if (ch == 0x0D && this->source.bufferedCharAt(this->index + 1) == 0x0A) { ++this->index; } ++this->lineNumber; ++this->index; this->lineStart = this->index; } else if (ch == 0x2A) { // Block comment ends with '*/'. if (this->source.bufferedCharAt(this->index + 1) == 0x2F) { this->index += 2; /* if (this->trackComment) { loc.end = { line: this->lineNumber, column: this->index - this->lineStart }; const entry: Comment = { multiLine: true, slice: [start + 2, this->index - 2], range: [start, this->index], loc: loc }; comments.push(entry); } return comments; */ return; } ++this->index; } else { ++this->index; } } /* // Ran off the end of the file - the whole thing is a comment if (this->trackComment) { loc.end = { line: this->lineNumber, column: this->index - this->lineStart }; const entry: Comment = { multiLine: true, slice: [start + 2, this->index], range: [start, this->index], loc: loc }; comments.push(entry); }*/ tolerateUnexpectedToken(); // return comments; return; } void scanComments() { bool start = (this->index == 0); while (LIKELY(!this->eof())) { char16_t ch = this->source.bufferedCharAt(this->index); if (isWhiteSpace(ch)) { ++this->index; } else if (isLineTerminator(ch)) { ++this->index; if (ch == 0x0D && this->source.bufferedCharAt(this->index) == 0x0A) { ++this->index; } ++this->lineNumber; this->lineStart = this->index; start = true; } else if (ch == 0x2F) { // U+002F is '/' ch = this->source.bufferedCharAt(this->index + 1); if (ch == 0x2F) { this->index += 2; this->skipSingleLineComment(2); start = true; } else if (ch == 0x2A) { // U+002A is '*' this->index += 2; this->skipMultiLineComment(); } else { break; } } else if (start && ch == 0x2D) { // U+002D is '-' // U+003E is '>' if ((this->source.bufferedCharAt(this->index + 1) == 0x2D) && (this->source.bufferedCharAt(this->index + 2) == 0x3E)) { // '-->' is a single-line comment this->index += 3; this->skipSingleLineComment(3); } else { break; } } else if (ch == 0x3C) { // U+003C is '<' if (this->length > this->index + 4) { if (this->source.bufferedCharAt(this->index + 1) == '!' && this->source.bufferedCharAt(this->index + 2) == '-' && this->source.bufferedCharAt(this->index + 3) == '-') { this->index += 4; // `