diff options
Diffstat (limited to 'Source/JavaScriptCore/interpreter')
-rw-r--r-- | Source/JavaScriptCore/interpreter/Interpreter.cpp | 35 | ||||
-rw-r--r-- | Source/JavaScriptCore/interpreter/VMInspector.cpp | 463 | ||||
-rw-r--r-- | Source/JavaScriptCore/interpreter/VMInspector.h | 47 |
3 files changed, 525 insertions, 20 deletions
diff --git a/Source/JavaScriptCore/interpreter/Interpreter.cpp b/Source/JavaScriptCore/interpreter/Interpreter.cpp index 0d475b416..397ac8474 100644 --- a/Source/JavaScriptCore/interpreter/Interpreter.cpp +++ b/Source/JavaScriptCore/interpreter/Interpreter.cpp @@ -100,9 +100,9 @@ Interpreter::ErrorHandlingMode::~ErrorHandlingMode() // The Interpreter::StackPolicy class is used to compute a stack capacity // requirement to ensure that we have enough room on the native stack for: -// 1. the max cummulative stack used by the interpreter and all code +// 1. the max cumulative stack used by the interpreter and all code // paths sub of it up till leaf functions. -// 2. the max cummulative stack used by the interpreter before it reaches +// 2. the max cumulative stack used by the interpreter before it reaches // the next checkpoint (execute...() function) in the interpreter. // // The interpreter can be run on different threads and hence, different @@ -116,11 +116,11 @@ Interpreter::ErrorHandlingMode::~ErrorHandlingMode() Interpreter::StackPolicy::StackPolicy(Interpreter& interpreter, const StackBounds& stack) : m_interpreter(interpreter) { - int size = stack.size(); + const size_t size = stack.size(); - const int DEFAULT_REQUIRED_STACK = 1024 * 1024; - const int DEFAULT_MINIMUM_USEABLE_STACK = 128 * 1024; - const int DEFAULT_ERROR_MODE_REQUIRED_STACK = 32 * 1024; + const size_t DEFAULT_REQUIRED_STACK = 1024 * 1024; + const size_t DEFAULT_MINIMUM_USEABLE_STACK = 128 * 1024; + const size_t DEFAULT_ERROR_MODE_REQUIRED_STACK = 32 * 1024; // Here's the policy in a nutshell: // @@ -152,7 +152,7 @@ Interpreter::StackPolicy::StackPolicy(Interpreter& interpreter, const StackBound // ^ ^ // start current sp // - // This smaller requried capacity also means that we won't re-trigger + // This smaller required capacity also means that we won't re-trigger // a stack overflow for processing the exception caused by the original // StackOverflowError. // @@ -169,15 +169,16 @@ Interpreter::StackPolicy::StackPolicy(Interpreter& interpreter, const StackBound // The minimum useable capacity is DEFAULT_MINIMUM_USEABLE_STACK. // In this case, the requiredCapacity is whatever is left of the // total stack capacity after we have give JS its minimum stack - // i.e. requiredCapcity can even be 0 if there's not enough stack. + // i.e. requiredCapacity can even be 0 if there's not enough stack. // Policy 1: Normal mode: required = DEFAULT_REQUIRED_STACK. - // Policy 2: Erro mode: required = DEFAULT_ERROR_MODE_REQUIRED_STACK. - int requiredCapacity = !m_interpreter.m_errorHandlingModeReentry ? + // Policy 2: Error mode: required = DEFAULT_ERROR_MODE_REQUIRED_STACK. + size_t requiredCapacity = !m_interpreter.m_errorHandlingModeReentry ? DEFAULT_REQUIRED_STACK : DEFAULT_ERROR_MODE_REQUIRED_STACK; - int useableStack = size - requiredCapacity; + size_t useableStack = (requiredCapacity <= size) ? + size - requiredCapacity : DEFAULT_MINIMUM_USEABLE_STACK; // Policy 3: Ensure the useable stack is not too small: if (useableStack < DEFAULT_MINIMUM_USEABLE_STACK) @@ -190,9 +191,8 @@ Interpreter::StackPolicy::StackPolicy(Interpreter& interpreter, const StackBound // Re-compute the requiredCapacity based on the adjusted useable stack // size: - // interpreter stack checks: requiredCapacity = size - useableStack; - ASSERT((requiredCapacity >= 0) && (requiredCapacity < size)); + ASSERT(requiredCapacity < size); m_requiredCapacity = requiredCapacity; } @@ -948,10 +948,13 @@ failedJSONP: // object. // Compile source to bytecode if necessary: - JSObject* error = program->compile(callFrame, scope); - if (error) + if (JSObject* error = program->initalizeGlobalProperties(globalData, callFrame, scope)) return checkedReturn(throwError(callFrame, error)); - CodeBlock* codeBlock = &program->generatedBytecode(); + + if (JSObject* error = program->compile(callFrame, scope)) + return checkedReturn(throwError(callFrame, error)); + + ProgramCodeBlock* codeBlock = &program->generatedBytecode(); // Push the call frame for this invocation: ASSERT(codeBlock->numParameters() == 1); // 1 parameter for 'this'. diff --git a/Source/JavaScriptCore/interpreter/VMInspector.cpp b/Source/JavaScriptCore/interpreter/VMInspector.cpp index 566d4e8e0..58bc15075 100644 --- a/Source/JavaScriptCore/interpreter/VMInspector.cpp +++ b/Source/JavaScriptCore/interpreter/VMInspector.cpp @@ -28,6 +28,10 @@ #if ENABLE(VMINSPECTOR) +#include <stdio.h> +#include <wtf/ASCIICType.h> +#include <wtf/text/WTFString.h> + namespace JSC { const char* VMInspector::getTypeName(JSValue value) @@ -105,6 +109,465 @@ int VMInspector::countFrames(CallFrame* frame) return count; } + +//============================================================================ +// class FormatPrinter +// - implements functionality to support fprintf. +// +// The FormatPrinter classes do the real formatting and printing. +// By default, the superclass FormatPrinter will print to stdout (printf). +// Each of the subclass will implement the other ...printf() options. +// The subclasses are: +// +// FileFormatPrinter - fprintf +// StringFormatPrinter - sprintf +// StringNFormatPrinter - snprintf + +class FormatPrinter { +public: + virtual ~FormatPrinter() { } + + void print(const char* format, va_list args); + +protected: + // Low level printers: + bool printArg(const char* format, ...); + virtual bool printArg(const char* format, va_list args); + + // JS type specific printers: + void printWTFString(va_list args, bool verbose); +}; + + +// The public print() function is the real workhorse behind the printf +// family of functions. print() deciphers the % formatting, translate them +// to primitive formats, and dispatches to underlying printArg() functions +// to do the printing. +// +// The non-public internal printArg() function is virtual and is responsible +// for handling the variations between printf, fprintf, sprintf, and snprintf. + +void FormatPrinter::print(const char* format, va_list args) +{ + const char* p = format; + const char* errorStr; + + // buffer is only used for 2 purposes: + // 1. To temporarily hold a copy of normal chars (not needing formatting) + // to be passed to printArg() and printed. + // + // The incoming format string may contain a string of normal chars much + // longer than 128, but we handle this by breaking them out to 128 chars + // fragments and printing each fragment before re-using the buffer to + // load up the next fragment. + // + // 2. To hold a single "%..." format to be passed to printArg() to process + // a single va_arg. + + char buffer[129]; // 128 chars + null terminator. + char* end = &buffer[sizeof(buffer) - 1]; + const char* startOfFormatSpecifier = 0; + + while (true) { + char c = *p++; + char* curr = buffer; + + // Print leading normal chars: + while (c != '\0' && c != '%') { + *curr++ = c; + if (curr == end) { + // Out of buffer space. Flush the fragment, and start over. + *curr = '\0'; + bool success = printArg("%s", buffer); + if (!success) { + errorStr = buffer; + goto handleError; + } + curr = buffer; + } + c = *p++; + } + // If we have stuff in the buffer, flush the fragment: + if (curr != buffer) { + ASSERT(curr < end + 1); + *curr = '\0'; + bool success = printArg("%s", buffer); + if (!success) { + errorStr = buffer; + goto handleError; + } + } + + // End if there are not more chars to print: + if (c == '\0') + break; + + // If we get here, we've must have seen a '%': + startOfFormatSpecifier = p - 1; + ASSERT(*startOfFormatSpecifier == '%'); + c = *p++; + + // Check for "%%" case: + if (c == '%') { + bool success = printArg("%c", '%'); + if (!success) { + errorStr = p - 2; + goto handleError; + } + continue; + } + + // Check for JS (%J<x>) formatting extensions: + if (c == 'J') { + bool verbose = false; + + c = *p++; + if (UNLIKELY(c == '\0')) { + errorStr = p - 2; // Rewind to % in "%J\0" + goto handleError; + } + + if (c == '+') { + verbose = true; + c= *p++; + if (UNLIKELY(c == '\0')) { + errorStr = p - 3; // Rewind to % in "%J+\0" + goto handleError; + } + } + + switch (c) { + // %Js - WTF::String* + case 's': { + printWTFString(args, verbose); + continue; + } + } // END switch. + + // Check for non-JS extensions: + } else if (c == 'b') { + int value = va_arg(args, int); + printArg("%s", value ? "TRUE" : "FALSE"); + continue; + } + + // If we didn't handle the format in one of the above cases, + // rewind p and let the standard formatting check handle it + // if possible: + p = startOfFormatSpecifier; + ASSERT(*p == '%'); + + // Check for standard formatting: + // A format specifier always starts with a % and ends with some + // alphabet. We'll do the simple thing and scan until the next + // alphabet, or the end of string. + + // In the following, we're going to use buffer as storage for a copy + // of a single format specifier. Hence, conceptually, we can think of + // 'buffer' as synonymous with 'argFormat' here: + +#define ABORT_IF_FORMAT_TOO_LONG(curr) \ + do { \ + if (UNLIKELY(curr >= end)) \ + goto formatTooLong; \ + } while (false) + + curr = buffer; + *curr++ = *p++; // Output the first % in the format specifier. + c = *p++; // Grab the next char in the format specifier. + + // Checks for leading modifiers e.g. "%-d": + // 0, -, ' ', +, '\'' + if (c == '0' || c == '-' || c == ' ' || c == '+' || c == '\'' || c == '#') { + ABORT_IF_FORMAT_TOO_LONG(curr); + *curr++ = c; + c = *p++; + } + + // Checks for decimal digit field width modifiers e.g. "%2f": + while (c >= '0' && c <= '9') { + ABORT_IF_FORMAT_TOO_LONG(curr); + *curr++ = c; + c = *p++; + } + + // Checks for '.' e.g. "%2.f": + if (c == '.') { + ABORT_IF_FORMAT_TOO_LONG(curr); + *curr++ = c; + c = *p++; + + // Checks for decimal digit precision modifiers e.g. "%.2f": + while (c >= '0' && c <= '9') { + ABORT_IF_FORMAT_TOO_LONG(curr); + *curr++ = c; + c = *p++; + } + } + + // Checks for the modifier <m> where <m> can be: + // l, h, j, t, z + // e.g. "%ld" + if (c == 'l' || c == 'h' || c == 'j' || c == 't' || c == 'z' || c == 'L') { + ABORT_IF_FORMAT_TOO_LONG(curr); + *curr++ = c; + char prevChar = c; + c = *p++; + + // Checks for the modifier ll or hh in %<x><m>: + if ((prevChar == 'l' || prevChar == 'h') && c == prevChar) { + ABORT_IF_FORMAT_TOO_LONG(curr); + *curr++ = c; + c = *p++; + } + } + + // Checks for %<x> where <x> can be: + // d, i, n, o, u, x, X + // But hey, we're just going to do the simple thing and allow any + // alphabet. The user is expected to pass correct format specifiers. + // We won't do any format checking here. We'll just pass it on, and the + // underlying ...printf() implementation may do the needed checking + // at its discretion. + while (c != '\0' && !isASCIIAlpha(c)) { + ABORT_IF_FORMAT_TOO_LONG(curr); + *curr++ = c; + c = *p++; + } + + ABORT_IF_FORMAT_TOO_LONG(curr); + *curr++ = c; + if (c == '\0') { + // Uh oh. Bad format. We should have gotten an alphabet instead. + // Print the supposed format as a string instead: + errorStr = buffer; + goto handleError; + } + + // Otherwise, we have the alpha that terminates the format. + // Terminate the buffer (i.e. argFormat) string: + ASSERT(isASCIIAlpha(c)); + ABORT_IF_FORMAT_TOO_LONG(curr); + *curr = '\0'; + + bool success = printArg(buffer, args); + if (!success) { + errorStr = buffer; + goto handleError; + } + } +#undef ABORT_IF_FORMAT_TOO_LONG + + return; + +formatTooLong: + // Print the error string: + ASSERT(!!startOfFormatSpecifier); + p = startOfFormatSpecifier; + ASSERT(p >= format); + printArg("ERROR @ Format too long at \"%s\"\n", p); + return; + +handleError: + // We've got an error. Can't do any more work. Print an error message if + // possible and then just return. + + // The errorStr may be pointing into the middle of buffer, or the original + // format string. Move the string to buffer for consistency, and also so + // that we can strip it of newlines below. + if (errorStr != buffer) { + size_t length = strlen(errorStr); + if (length > sizeof(buffer) - 1) + length = sizeof(buffer) - 1; + memmove(buffer, errorStr, length); + buffer[length] = '\0'; // Terminate the moved error string. + } + // Strip the newlines: + char* cp = buffer; + while (*cp) { + if (*cp == '\n' || *cp == '\r') + *cp = ' '; + cp++; + } + // Print the error string: + printArg("ERROR @ \"%s\"\n", buffer); +} + + +bool FormatPrinter::printArg(const char* format, ...) +{ + va_list args; + va_start(args, format); + bool success = printArg(format, args); + va_end(args); + return success; +} + +bool FormatPrinter::printArg(const char* format, va_list args) +{ + int count = ::vprintf(format, args); + return (count >= 0); // Fail if less than 0 chars printed. +} + + +// %Js - WTF::String* +// verbose mode prints: WTF::String "<your string>" +void FormatPrinter::printWTFString(va_list args, bool verbose) +{ + const String* str = va_arg(args, const String*); + + // Print verbose header if appropriate: + if (verbose) + printArg("WTF::String \""); + + // Print the string itself: + if (!str->isEmpty()) { + if (str->is8Bit()) { + const LChar* chars = str->characters8(); + printArg("%s", reinterpret_cast<const char*>(chars)); + } else { + const UChar* chars = str->characters16(); + printArg("%S", reinterpret_cast<const wchar_t*>(chars)); + } + } + + // Print verbose footer if appropriate: + if (verbose) + printArg("\""); +} + + +//============================================================================ +// class FileFormatPrinter +// - implements functionality to support fprintf. + +class FileFormatPrinter: public FormatPrinter { +public: + FileFormatPrinter(FILE*); +private: + virtual bool printArg(const char* format, va_list args); + + FILE* m_file; +}; + +FileFormatPrinter::FileFormatPrinter(FILE* file) + : m_file(file) +{ +} + +bool FileFormatPrinter::printArg(const char* format, va_list args) +{ + int count = ::vfprintf(m_file, format, args); + return (count >= 0); // Fail if less than 0 chars printed. +} + + +//============================================================================ +// class StringFormatPrinter +// - implements functionality to support sprintf. + +class StringFormatPrinter: public FormatPrinter { +public: + StringFormatPrinter(char* buffer); +private: + virtual bool printArg(const char* format, va_list args); + + char* m_buffer; +}; + +StringFormatPrinter::StringFormatPrinter(char* buffer) + : m_buffer(buffer) +{ +} + +bool StringFormatPrinter::printArg(const char* format, va_list args) +{ + int count = ::vsprintf(m_buffer, format, args); + m_buffer += count; + return (count >= 0); // Fail if less than 0 chars printed. +} + + +//============================================================================ +// class StringNFormatPrinter +// - implements functionality to support snprintf. + +class StringNFormatPrinter: public FormatPrinter { +public: + StringNFormatPrinter(char* buffer, size_t); +private: + virtual bool printArg(const char* format, va_list args); + + char* m_buffer; + size_t m_size; +}; + + +StringNFormatPrinter::StringNFormatPrinter(char* buffer, size_t size) + : m_buffer(buffer) + , m_size(size) +{ +} + +bool StringNFormatPrinter::printArg(const char* format, va_list args) +{ + if (m_size > 0) { + int count = ::vsnprintf(m_buffer, m_size, format, args); + + // According to vsnprintf specs, ... + bool success = (count >= 0); + if (static_cast<size_t>(count) >= m_size) { + // If count > size, then we didn't have enough buffer space. + count = m_size; + } + + // Adjust the buffer to what's left if appropriate: + if (success) { + m_buffer += count; + m_size -= count; + } + return success; + } + // No more room to print. Declare it a fail: + return false; +} + + +//============================================================================ +// VMInspector printf family of methods: + +void VMInspector::fprintf(FILE* file, const char* format, ...) +{ + va_list args; + va_start(args, format); + FileFormatPrinter(file).print(format, args); + va_end(args); +} + +void VMInspector::printf(const char* format, ...) +{ + va_list args; + va_start(args, format); + FormatPrinter().print(format, args); + va_end(args); +} + +void VMInspector::sprintf(char* buffer, const char* format, ...) +{ + va_list args; + va_start(args, format); + StringFormatPrinter(buffer).print(format, args); + va_end(args); +} + +void VMInspector::snprintf(char* buffer, size_t size, const char* format, ...) +{ + va_list args; + va_start(args, format); + StringNFormatPrinter(buffer, size).print(format, args); + va_end(args); +} + } // namespace JSC #endif // ENABLE(VMINSPECTOR) diff --git a/Source/JavaScriptCore/interpreter/VMInspector.h b/Source/JavaScriptCore/interpreter/VMInspector.h index 6806cafa1..81ca26c6f 100644 --- a/Source/JavaScriptCore/interpreter/VMInspector.h +++ b/Source/JavaScriptCore/interpreter/VMInspector.h @@ -28,23 +28,62 @@ #define ENABLE_VMINSPECTOR 0 -#if ENABLE(VMINSPECTOR) - #include "CallFrame.h" #include "JSValue.h" +#include <stdarg.h> +#include <stdio.h> +#include <wtf/text/WTFString.h> namespace JSC { +#if ENABLE(VMINSPECTOR) + class VMInspector { public: static JS_EXPORT_PRIVATE const char* getTypeName(JSValue); static JS_EXPORT_PRIVATE void dumpFrame0(CallFrame*); static JS_EXPORT_PRIVATE void dumpFrame(CallFrame*, const char* prefix = 0, const char* funcName = 0, const char* file = 0, int line = -1); static JS_EXPORT_PRIVATE int countFrames(CallFrame*); -}; -} // namespace JSC + // Special family of ...printf() functions that support, in addition to the + // standard % formats (e.g. %d, %s, etc), the following extra JSC formatting + // options, %J<x>, where <x> consists of: + // + // + - verbose mode modifier. + // Used in combination with other options. Must come after the %J. + // s - WTF::String* + // + // Examples of usage: + // + // WTF::String str("My WTF String"); + // + // // Printing the string. Will print: + // // The wtf string says: "My WTF String" and is NOT EMPTY. + // + // VMInspector::printf("The wtf string says: \"%Js\" and is %s\n", + // &str, str.isEmpty()?"EMPTY":"NOT EMPTY"); + // + // // Printing the string with verbose mode. Will print: + // // <WTF::String "My WTF String"> + // + // VMInspector::printf("<%J+s>\n", &str); + // + // Also added some convenience non-JS formats: + // + // %b - boolean (va_args will look for an int). + // Prints TRUE if non-zero, else prints FALSE. + // + // Caution: the user is expected to pass the correctly matched arguments + // to pair with the corresponding % fomatting. + + static JS_EXPORT_PRIVATE void fprintf(FILE*, const char* format, ...); + static JS_EXPORT_PRIVATE void printf(const char* format, ...); + static JS_EXPORT_PRIVATE void sprintf(char*, const char* format, ...); + static JS_EXPORT_PRIVATE void snprintf(char*, size_t, const char* format, ...); +}; #endif // ENABLE(VMINSPECTOR) +} // namespace JSC + #endif // VMInspector.h |