X-Git-Url: https://git.saurik.com/apple/javascriptcore.git/blobdiff_plain/4e4e5a6f2694187498445a6ac6f1634ce8141119..1981f5dfe8d77d97469d20652f712a09400c48ed:/jsc.cpp diff --git a/jsc.cpp b/jsc.cpp index ae47d55..95bc334 100644 --- a/jsc.cpp +++ b/jsc.cpp @@ -24,13 +24,16 @@ #include "BytecodeGenerator.h" #include "Completion.h" -#include "CurrentTime.h" +#include +#include "ExceptionHelpers.h" #include "InitializeThreading.h" +#include "Interpreter.h" #include "JSArray.h" +#include "JSCTypedArrayStubs.h" #include "JSFunction.h" #include "JSLock.h" #include "JSString.h" -#include "PrototypeFunction.h" +#include #include "SamplingTool.h" #include #include @@ -42,8 +45,12 @@ #endif #if HAVE(READLINE) +// readline/history.h has a Function typedef which conflicts with the WTF::Function template from WTF/Forward.h +// We #define it to something else to avoid this conflict. +#define Function ReadlineFunction #include #include +#undef Function #endif #if HAVE(SYS_TIME_H) @@ -65,25 +72,34 @@ #include #endif +#if CPU(ARM_THUMB2) +#include +#include +#endif + using namespace JSC; using namespace WTF; -static void cleanupGlobalData(JSGlobalData*); static bool fillBufferWithContentsOfFile(const UString& fileName, Vector& buffer); -static JSValue JSC_HOST_CALL functionPrint(ExecState*, JSObject*, JSValue, const ArgList&); -static JSValue JSC_HOST_CALL functionDebug(ExecState*, JSObject*, JSValue, const ArgList&); -static JSValue JSC_HOST_CALL functionGC(ExecState*, JSObject*, JSValue, const ArgList&); -static JSValue JSC_HOST_CALL functionVersion(ExecState*, JSObject*, JSValue, const ArgList&); -static JSValue JSC_HOST_CALL functionRun(ExecState*, JSObject*, JSValue, const ArgList&); -static JSValue JSC_HOST_CALL functionLoad(ExecState*, JSObject*, JSValue, const ArgList&); -static JSValue JSC_HOST_CALL functionCheckSyntax(ExecState*, JSObject*, JSValue, const ArgList&); -static JSValue JSC_HOST_CALL functionReadline(ExecState*, JSObject*, JSValue, const ArgList&); -static NO_RETURN_WITH_VALUE JSValue JSC_HOST_CALL functionQuit(ExecState*, JSObject*, JSValue, const ArgList&); +static EncodedJSValue JSC_HOST_CALL functionPrint(ExecState*); +static EncodedJSValue JSC_HOST_CALL functionDebug(ExecState*); +static EncodedJSValue JSC_HOST_CALL functionJSCStack(ExecState*); +static EncodedJSValue JSC_HOST_CALL functionGC(ExecState*); +#ifndef NDEBUG +static EncodedJSValue JSC_HOST_CALL functionReleaseExecutableMemory(ExecState*); +#endif +static EncodedJSValue JSC_HOST_CALL functionVersion(ExecState*); +static EncodedJSValue JSC_HOST_CALL functionRun(ExecState*); +static EncodedJSValue JSC_HOST_CALL functionLoad(ExecState*); +static EncodedJSValue JSC_HOST_CALL functionCheckSyntax(ExecState*); +static EncodedJSValue JSC_HOST_CALL functionReadline(ExecState*); +static EncodedJSValue JSC_HOST_CALL functionPreciseTime(ExecState*); +static NO_RETURN_WITH_VALUE EncodedJSValue JSC_HOST_CALL functionQuit(ExecState*); #if ENABLE(SAMPLING_FLAGS) -static JSValue JSC_HOST_CALL functionSetSamplingFlags(ExecState*, JSObject*, JSValue, const ArgList&); -static JSValue JSC_HOST_CALL functionClearSamplingFlags(ExecState*, JSObject*, JSValue, const ArgList&); +static EncodedJSValue JSC_HOST_CALL functionSetSamplingFlags(ExecState*); +static EncodedJSValue JSC_HOST_CALL functionClearSamplingFlags(ExecState*); #endif struct Script { @@ -97,8 +113,8 @@ struct Script { } }; -struct Options { - Options() +struct CommandLine { + CommandLine() : interactive(false) , dump(false) { @@ -111,7 +127,6 @@ struct Options { }; static const char interactivePrompt[] = "> "; -static const UString interpreterName("Interpreter"); class StopWatch { public: @@ -140,143 +155,249 @@ long StopWatch::getElapsedMS() } class GlobalObject : public JSGlobalObject { +private: + GlobalObject(JSGlobalData&, Structure*); + public: - GlobalObject(const Vector& arguments); - virtual UString className() const { return "global"; } + typedef JSGlobalObject Base; + + static GlobalObject* create(JSGlobalData& globalData, Structure* structure, const Vector& arguments) + { + GlobalObject* object = new (NotNull, allocateCell(globalData.heap)) GlobalObject(globalData, structure); + object->finishCreation(globalData, arguments); + return object; + } + + static const ClassInfo s_info; + + static Structure* createStructure(JSGlobalData& globalData, JSValue prototype) + { + return Structure::create(globalData, 0, prototype, TypeInfo(GlobalObjectType, StructureFlags), &s_info); + } + +protected: + void finishCreation(JSGlobalData& globalData, const Vector& arguments) + { + Base::finishCreation(globalData); + + addFunction(globalData, "debug", functionDebug, 1); + addFunction(globalData, "print", functionPrint, 1); + addFunction(globalData, "quit", functionQuit, 0); + addFunction(globalData, "gc", functionGC, 0); +#ifndef NDEBUG + addFunction(globalData, "releaseExecutableMemory", functionReleaseExecutableMemory, 0); +#endif + addFunction(globalData, "version", functionVersion, 1); + addFunction(globalData, "run", functionRun, 1); + addFunction(globalData, "load", functionLoad, 1); + addFunction(globalData, "checkSyntax", functionCheckSyntax, 1); + addFunction(globalData, "jscStack", functionJSCStack, 1); + addFunction(globalData, "readline", functionReadline, 0); + addFunction(globalData, "preciseTime", functionPreciseTime, 0); +#if ENABLE(SAMPLING_FLAGS) + addFunction(globalData, "setSamplingFlags", functionSetSamplingFlags, 1); + addFunction(globalData, "clearSamplingFlags", functionClearSamplingFlags, 1); +#endif + + addConstructableFunction(globalData, "Uint8Array", constructJSUint8Array, 1); + addConstructableFunction(globalData, "Uint8ClampedArray", constructJSUint8ClampedArray, 1); + addConstructableFunction(globalData, "Uint16Array", constructJSUint16Array, 1); + addConstructableFunction(globalData, "Uint32Array", constructJSUint32Array, 1); + addConstructableFunction(globalData, "Int8Array", constructJSInt8Array, 1); + addConstructableFunction(globalData, "Int16Array", constructJSInt16Array, 1); + addConstructableFunction(globalData, "Int32Array", constructJSInt32Array, 1); + addConstructableFunction(globalData, "Float32Array", constructJSFloat32Array, 1); + addConstructableFunction(globalData, "Float64Array", constructJSFloat64Array, 1); + + JSArray* array = constructEmptyArray(globalExec()); + for (size_t i = 0; i < arguments.size(); ++i) + array->putDirectIndex(globalExec(), i, jsString(globalExec(), arguments[i]), false); + putDirect(globalData, Identifier(globalExec(), "arguments"), array); + } + + void addFunction(JSGlobalData& globalData, const char* name, NativeFunction function, unsigned arguments) + { + Identifier identifier(globalExec(), name); + putDirect(globalData, identifier, JSFunction::create(globalExec(), this, arguments, identifier, function)); + } + + void addConstructableFunction(JSGlobalData& globalData, const char* name, NativeFunction function, unsigned arguments) + { + Identifier identifier(globalExec(), name); + putDirect(globalData, identifier, JSFunction::create(globalExec(), this, arguments, identifier, function, NoIntrinsic, function)); + } }; COMPILE_ASSERT(!IsInteger::value, WTF_IsInteger_GlobalObject_false); ASSERT_CLASS_FITS_IN_CELL(GlobalObject); -GlobalObject::GlobalObject(const Vector& arguments) - : JSGlobalObject() -{ - putDirectFunction(globalExec(), new (globalExec()) NativeFunctionWrapper(globalExec(), prototypeFunctionStructure(), 1, Identifier(globalExec(), "debug"), functionDebug)); - putDirectFunction(globalExec(), new (globalExec()) NativeFunctionWrapper(globalExec(), prototypeFunctionStructure(), 1, Identifier(globalExec(), "print"), functionPrint)); - putDirectFunction(globalExec(), new (globalExec()) NativeFunctionWrapper(globalExec(), prototypeFunctionStructure(), 0, Identifier(globalExec(), "quit"), functionQuit)); - putDirectFunction(globalExec(), new (globalExec()) NativeFunctionWrapper(globalExec(), prototypeFunctionStructure(), 0, Identifier(globalExec(), "gc"), functionGC)); - putDirectFunction(globalExec(), new (globalExec()) NativeFunctionWrapper(globalExec(), prototypeFunctionStructure(), 1, Identifier(globalExec(), "version"), functionVersion)); - putDirectFunction(globalExec(), new (globalExec()) NativeFunctionWrapper(globalExec(), prototypeFunctionStructure(), 1, Identifier(globalExec(), "run"), functionRun)); - putDirectFunction(globalExec(), new (globalExec()) NativeFunctionWrapper(globalExec(), prototypeFunctionStructure(), 1, Identifier(globalExec(), "load"), functionLoad)); - putDirectFunction(globalExec(), new (globalExec()) NativeFunctionWrapper(globalExec(), prototypeFunctionStructure(), 1, Identifier(globalExec(), "checkSyntax"), functionCheckSyntax)); - putDirectFunction(globalExec(), new (globalExec()) NativeFunctionWrapper(globalExec(), prototypeFunctionStructure(), 0, Identifier(globalExec(), "readline"), functionReadline)); +const ClassInfo GlobalObject::s_info = { "global", &JSGlobalObject::s_info, 0, ExecState::globalObjectTable, CREATE_METHOD_TABLE(GlobalObject) }; -#if ENABLE(SAMPLING_FLAGS) - putDirectFunction(globalExec(), new (globalExec()) NativeFunctionWrapper(globalExec(), prototypeFunctionStructure(), 1, Identifier(globalExec(), "setSamplingFlags"), functionSetSamplingFlags)); - putDirectFunction(globalExec(), new (globalExec()) NativeFunctionWrapper(globalExec(), prototypeFunctionStructure(), 1, Identifier(globalExec(), "clearSamplingFlags"), functionClearSamplingFlags)); -#endif +GlobalObject::GlobalObject(JSGlobalData& globalData, Structure* structure) + : JSGlobalObject(globalData, structure) +{ +} - JSObject* array = constructEmptyArray(globalExec()); - for (size_t i = 0; i < arguments.size(); ++i) - array->put(globalExec(), i, jsString(globalExec(), arguments[i])); - putDirect(Identifier(globalExec(), "arguments"), array); +static inline SourceCode jscSource(const char* utf8, const UString& filename) +{ + // Find the the first non-ascii character, or nul. + const char* pos = utf8; + while (*pos > 0) + pos++; + size_t asciiLength = pos - utf8; + + // Fast case - string is all ascii. + if (!*pos) + return makeSource(UString(utf8, asciiLength), filename); + + // Slow case - contains non-ascii characters, use fromUTF8WithLatin1Fallback. + ASSERT(*pos < 0); + ASSERT(strlen(utf8) == asciiLength + strlen(pos)); + String source = String::fromUTF8WithLatin1Fallback(utf8, asciiLength + strlen(pos)); + return makeSource(source.impl(), filename); } -JSValue JSC_HOST_CALL functionPrint(ExecState* exec, JSObject*, JSValue, const ArgList& args) +EncodedJSValue JSC_HOST_CALL functionPrint(ExecState* exec) { - for (unsigned i = 0; i < args.size(); ++i) { + for (unsigned i = 0; i < exec->argumentCount(); ++i) { if (i) putchar(' '); - printf("%s", args.at(i).toString(exec).UTF8String().data()); + printf("%s", exec->argument(i).toString(exec)->value(exec).utf8().data()); } putchar('\n'); fflush(stdout); - return jsUndefined(); + return JSValue::encode(jsUndefined()); +} + +EncodedJSValue JSC_HOST_CALL functionDebug(ExecState* exec) +{ + fprintf(stderr, "--> %s\n", exec->argument(0).toString(exec)->value(exec).utf8().data()); + return JSValue::encode(jsUndefined()); } -JSValue JSC_HOST_CALL functionDebug(ExecState* exec, JSObject*, JSValue, const ArgList& args) +EncodedJSValue JSC_HOST_CALL functionJSCStack(ExecState* exec) { - fprintf(stderr, "--> %s\n", args.at(0).toString(exec).UTF8String().data()); - return jsUndefined(); + String trace = "--> Stack trace:\n"; + Vector stackTrace; + Interpreter::getStackTrace(&exec->globalData(), stackTrace); + int i = 0; + + for (Vector::iterator iter = stackTrace.begin(); iter < stackTrace.end(); iter++) { + StackFrame level = *iter; + trace += String::format(" %i %s\n", i, level.toString(exec).utf8().data()); + i++; + } + fprintf(stderr, "%s", trace.utf8().data()); + return JSValue::encode(jsUndefined()); } -JSValue JSC_HOST_CALL functionGC(ExecState* exec, JSObject*, JSValue, const ArgList&) +EncodedJSValue JSC_HOST_CALL functionGC(ExecState* exec) { - JSLock lock(SilenceAssertionsOnly); + JSLockHolder lock(exec); exec->heap()->collectAllGarbage(); - return jsUndefined(); + return JSValue::encode(jsUndefined()); +} + +#ifndef NDEBUG +EncodedJSValue JSC_HOST_CALL functionReleaseExecutableMemory(ExecState* exec) +{ + JSLockHolder lock(exec); + exec->globalData().releaseExecutableMemory(); + return JSValue::encode(jsUndefined()); } +#endif -JSValue JSC_HOST_CALL functionVersion(ExecState*, JSObject*, JSValue, const ArgList&) +EncodedJSValue JSC_HOST_CALL functionVersion(ExecState*) { // We need this function for compatibility with the Mozilla JS tests but for now // we don't actually do any version-specific handling - return jsUndefined(); + return JSValue::encode(jsUndefined()); } -JSValue JSC_HOST_CALL functionRun(ExecState* exec, JSObject*, JSValue, const ArgList& args) +EncodedJSValue JSC_HOST_CALL functionRun(ExecState* exec) { - StopWatch stopWatch; - UString fileName = args.at(0).toString(exec); + UString fileName = exec->argument(0).toString(exec)->value(exec); Vector script; if (!fillBufferWithContentsOfFile(fileName, script)) - return throwError(exec, GeneralError, "Could not open file."); + return JSValue::encode(throwError(exec, createError(exec, "Could not open file."))); - JSGlobalObject* globalObject = exec->lexicalGlobalObject(); + GlobalObject* globalObject = GlobalObject::create(exec->globalData(), GlobalObject::createStructure(exec->globalData(), jsNull()), Vector()); + JSValue exception; + StopWatch stopWatch; stopWatch.start(); - evaluate(globalObject->globalExec(), globalObject->globalScopeChain(), makeSource(script.data(), fileName)); + evaluate(globalObject->globalExec(), globalObject->globalScopeChain(), jscSource(script.data(), fileName), JSValue(), &exception); stopWatch.stop(); - return jsNumber(globalObject->globalExec(), stopWatch.getElapsedMS()); + if (!!exception) { + throwError(globalObject->globalExec(), exception); + return JSValue::encode(jsUndefined()); + } + + return JSValue::encode(jsNumber(stopWatch.getElapsedMS())); } -JSValue JSC_HOST_CALL functionLoad(ExecState* exec, JSObject* o, JSValue v, const ArgList& args) +EncodedJSValue JSC_HOST_CALL functionLoad(ExecState* exec) { - UNUSED_PARAM(o); - UNUSED_PARAM(v); - UString fileName = args.at(0).toString(exec); + UString fileName = exec->argument(0).toString(exec)->value(exec); Vector script; if (!fillBufferWithContentsOfFile(fileName, script)) - return throwError(exec, GeneralError, "Could not open file."); + return JSValue::encode(throwError(exec, createError(exec, "Could not open file."))); JSGlobalObject* globalObject = exec->lexicalGlobalObject(); - Completion result = evaluate(globalObject->globalExec(), globalObject->globalScopeChain(), makeSource(script.data(), fileName)); - if (result.complType() == Throw) - exec->setException(result.value()); - return result.value(); + + JSValue evaluationException; + JSValue result = evaluate(globalObject->globalExec(), globalObject->globalScopeChain(), jscSource(script.data(), fileName), JSValue(), &evaluationException); + if (evaluationException) + throwError(exec, evaluationException); + return JSValue::encode(result); } -JSValue JSC_HOST_CALL functionCheckSyntax(ExecState* exec, JSObject* o, JSValue v, const ArgList& args) +EncodedJSValue JSC_HOST_CALL functionCheckSyntax(ExecState* exec) { - UNUSED_PARAM(o); - UNUSED_PARAM(v); - UString fileName = args.at(0).toString(exec); + UString fileName = exec->argument(0).toString(exec)->value(exec); Vector script; if (!fillBufferWithContentsOfFile(fileName, script)) - return throwError(exec, GeneralError, "Could not open file."); + return JSValue::encode(throwError(exec, createError(exec, "Could not open file."))); JSGlobalObject* globalObject = exec->lexicalGlobalObject(); - Completion result = checkSyntax(globalObject->globalExec(), makeSource(script.data(), fileName)); - if (result.complType() == Throw) - exec->setException(result.value()); - return result.value(); + + StopWatch stopWatch; + stopWatch.start(); + + JSValue syntaxException; + bool validSyntax = checkSyntax(globalObject->globalExec(), jscSource(script.data(), fileName), &syntaxException); + stopWatch.stop(); + + if (!validSyntax) + throwError(exec, syntaxException); + return JSValue::encode(jsNumber(stopWatch.getElapsedMS())); } #if ENABLE(SAMPLING_FLAGS) -JSValue JSC_HOST_CALL functionSetSamplingFlags(ExecState* exec, JSObject*, JSValue, const ArgList& args) +EncodedJSValue JSC_HOST_CALL functionSetSamplingFlags(ExecState* exec) { - for (unsigned i = 0; i < args.size(); ++i) { - unsigned flag = static_cast(args.at(i).toNumber(exec)); + for (unsigned i = 0; i < exec->argumentCount(); ++i) { + unsigned flag = static_cast(exec->argument(i).toNumber(exec)); if ((flag >= 1) && (flag <= 32)) SamplingFlags::setFlag(flag); } - return jsNull(); + return JSValue::encode(jsNull()); } -JSValue JSC_HOST_CALL functionClearSamplingFlags(ExecState* exec, JSObject*, JSValue, const ArgList& args) +EncodedJSValue JSC_HOST_CALL functionClearSamplingFlags(ExecState* exec) { - for (unsigned i = 0; i < args.size(); ++i) { - unsigned flag = static_cast(args.at(i).toNumber(exec)); + for (unsigned i = 0; i < exec->argumentCount(); ++i) { + unsigned flag = static_cast(exec->argument(i).toNumber(exec)); if ((flag >= 1) && (flag <= 32)) SamplingFlags::clearFlag(flag); } - return jsNull(); + return JSValue::encode(jsNull()); } #endif -JSValue JSC_HOST_CALL functionReadline(ExecState* exec, JSObject*, JSValue, const ArgList&) +EncodedJSValue JSC_HOST_CALL functionReadline(ExecState* exec) { Vector line; int c; @@ -287,22 +408,21 @@ JSValue JSC_HOST_CALL functionReadline(ExecState* exec, JSObject*, JSValue, cons line.append(c); } line.append('\0'); - return jsString(exec, line.data()); + return JSValue::encode(jsString(exec, line.data())); } -JSValue JSC_HOST_CALL functionQuit(ExecState* exec, JSObject*, JSValue, const ArgList&) +EncodedJSValue JSC_HOST_CALL functionPreciseTime(ExecState*) { - // Technically, destroying the heap in the middle of JS execution is a no-no, - // but we want to maintain compatibility with the Mozilla test suite, so - // we pretend that execution has terminated to avoid ASSERTs, then tear down the heap. - exec->globalData().dynamicGlobalObject = 0; + return JSValue::encode(jsNumber(currentTime())); +} - cleanupGlobalData(&exec->globalData()); +EncodedJSValue JSC_HOST_CALL functionQuit(ExecState*) +{ exit(EXIT_SUCCESS); #if COMPILER(MSVC) && OS(WINCE) // Without this, Visual Studio will complain that this method does not return a value. - return jsUndefined(); + return JSValue::encode(jsUndefined()); #endif } @@ -311,7 +431,7 @@ JSValue JSC_HOST_CALL functionQuit(ExecState* exec, JSObject*, JSValue, const Ar // be in a separate main function because the jscmain function requires object // unwinding. -#if COMPILER(MSVC) && !defined(_DEBUG) +#if COMPILER(MSVC) && !COMPILER(INTEL) && !defined(_DEBUG) && !OS(WINCE) #define TRY __try { #define EXCEPT(x) } __except (EXCEPTION_EXECUTE_HANDLER) { x; } #else @@ -319,11 +439,27 @@ JSValue JSC_HOST_CALL functionQuit(ExecState* exec, JSObject*, JSValue, const Ar #define EXCEPT(x) #endif -int jscmain(int argc, char** argv, JSGlobalData*); +int jscmain(int argc, char** argv); int main(int argc, char** argv) { -#if defined(_DEBUG) && OS(WINDOWS) +#if CPU(ARM_THUMB2) + // Enabled IEEE754 denormal support. + fenv_t env; + fegetenv( &env ); + env.__fpscr &= ~0x01000000u; + fesetenv( &env ); +#endif + +#if OS(WINDOWS) +#if !OS(WINCE) + // Cygwin calls ::SetErrorMode(SEM_FAILCRITICALERRORS), which we will inherit. This is bad for + // testing/debugging, as it causes the post-mortem debugger not to be invoked. We reset the + // error mode here to work around Cygwin's behavior. See . + ::SetErrorMode(0); +#endif + +#if defined(_DEBUG) _CrtSetReportFile(_CRT_WARN, _CRTDBG_FILE_STDERR); _CrtSetReportMode(_CRT_WARN, _CRTDBG_MODE_FILE); _CrtSetReportFile(_CRT_ERROR, _CRTDBG_FILE_STDERR); @@ -332,7 +468,6 @@ int main(int argc, char** argv) _CrtSetReportMode(_CRT_ASSERT, _CRTDBG_MODE_FILE); #endif -#if COMPILER(MSVC) && !OS(WINCE) timeBeginPeriod(1); #endif @@ -341,37 +476,28 @@ int main(int argc, char** argv) #endif // Initialize JSC before getting JSGlobalData. + WTF::initializeMainThread(); JSC::initializeThreading(); // We can't use destructors in the following code because it uses Windows // Structured Exception Handling int res = 0; - JSGlobalData* globalData = JSGlobalData::create(ThreadStackTypeLarge).releaseRef(); TRY - res = jscmain(argc, argv, globalData); + res = jscmain(argc, argv); EXCEPT(res = 3) - - cleanupGlobalData(globalData); return res; } -static void cleanupGlobalData(JSGlobalData* globalData) -{ - JSLock lock(SilenceAssertionsOnly); - globalData->heap.destroy(); - globalData->deref(); -} - static bool runWithScripts(GlobalObject* globalObject, const Vector