diff options
author | Steve Block <steveblock@google.com> | 2009-12-15 10:12:09 +0000 |
---|---|---|
committer | Steve Block <steveblock@google.com> | 2009-12-17 17:41:10 +0000 |
commit | 643ca7872b450ea4efacab6188849e5aac2ba161 (patch) | |
tree | 6982576c228bcd1a7efe98afed544d840751094c /JavaScriptCore | |
parent | d026980fde6eb3b01c1fe49441174e89cd1be298 (diff) | |
download | external_webkit-643ca7872b450ea4efacab6188849e5aac2ba161.zip external_webkit-643ca7872b450ea4efacab6188849e5aac2ba161.tar.gz external_webkit-643ca7872b450ea4efacab6188849e5aac2ba161.tar.bz2 |
Merge webkit.org at r51976 : Initial merge by git.
Change-Id: Ib0e7e2f0fb4bee5a186610272edf3186f0986b43
Diffstat (limited to 'JavaScriptCore')
148 files changed, 7977 insertions, 3520 deletions
diff --git a/JavaScriptCore/API/APICast.h b/JavaScriptCore/API/APICast.h index b9167a8..4284c44 100644 --- a/JavaScriptCore/API/APICast.h +++ b/JavaScriptCore/API/APICast.h @@ -51,16 +51,20 @@ typedef struct OpaqueJSValue* JSObjectRef; inline JSC::ExecState* toJS(JSContextRef c) { + ASSERT(c); return reinterpret_cast<JSC::ExecState*>(const_cast<OpaqueJSContext*>(c)); } inline JSC::ExecState* toJS(JSGlobalContextRef c) { + ASSERT(c); return reinterpret_cast<JSC::ExecState*>(c); } -inline JSC::JSValue toJS(JSC::ExecState*, JSValueRef v) +inline JSC::JSValue toJS(JSC::ExecState* exec, JSValueRef v) { + ASSERT_UNUSED(exec, exec); + ASSERT(v); #if USE(JSVALUE32_64) JSC::JSCell* jsCell = reinterpret_cast<JSC::JSCell*>(const_cast<OpaqueJSValue*>(v)); if (!jsCell) @@ -73,6 +77,20 @@ inline JSC::JSValue toJS(JSC::ExecState*, JSValueRef v) #endif } +inline JSC::JSValue toJSForGC(JSC::ExecState* exec, JSValueRef v) +{ + ASSERT_UNUSED(exec, exec); + ASSERT(v); +#if USE(JSVALUE32_64) + JSC::JSCell* jsCell = reinterpret_cast<JSC::JSCell*>(const_cast<OpaqueJSValue*>(v)); + if (!jsCell) + return JSC::JSValue(); + return jsCell; +#else + return JSC::JSValue::decode(reinterpret_cast<JSC::EncodedJSValue>(const_cast<OpaqueJSValue*>(v))); +#endif +} + inline JSC::JSObject* toJS(JSObjectRef o) { return reinterpret_cast<JSC::JSObject*>(o); diff --git a/JavaScriptCore/API/JSCallbackObjectFunctions.h b/JavaScriptCore/API/JSCallbackObjectFunctions.h index 9b726e8..ed86a00 100644 --- a/JavaScriptCore/API/JSCallbackObjectFunctions.h +++ b/JavaScriptCore/API/JSCallbackObjectFunctions.h @@ -131,15 +131,15 @@ bool JSCallbackObject<Base>::getOwnPropertySlot(ExecState* exec, const Identifie JSLock::DropAllLocks dropAllLocks(exec); value = getProperty(ctx, thisRef, propertyNameRef.get(), &exception); } - exec->setException(toJS(exec, exception)); - if (value) { - slot.setValue(toJS(exec, value)); - return true; - } if (exception) { + exec->setException(toJS(exec, exception)); slot.setValue(jsUndefined()); return true; } + if (value) { + slot.setValue(toJS(exec, value)); + return true; + } } if (OpaqueJSClassStaticValuesTable* staticValues = jsClass->staticValues(exec)) { @@ -184,7 +184,8 @@ void JSCallbackObject<Base>::put(ExecState* exec, const Identifier& propertyName JSLock::DropAllLocks dropAllLocks(exec); result = setProperty(ctx, thisRef, propertyNameRef.get(), valueRef, &exception); } - exec->setException(toJS(exec, exception)); + if (exception) + exec->setException(toJS(exec, exception)); if (result || exception) return; } @@ -202,7 +203,8 @@ void JSCallbackObject<Base>::put(ExecState* exec, const Identifier& propertyName JSLock::DropAllLocks dropAllLocks(exec); result = setProperty(ctx, thisRef, propertyNameRef.get(), valueRef, &exception); } - exec->setException(toJS(exec, exception)); + if (exception) + exec->setException(toJS(exec, exception)); if (result || exception) return; } else @@ -240,7 +242,8 @@ bool JSCallbackObject<Base>::deleteProperty(ExecState* exec, const Identifier& p JSLock::DropAllLocks dropAllLocks(exec); result = deleteProperty(ctx, thisRef, propertyNameRef.get(), &exception); } - exec->setException(toJS(exec, exception)); + if (exception) + exec->setException(toJS(exec, exception)); if (result || exception) return true; } @@ -301,7 +304,8 @@ JSObject* JSCallbackObject<Base>::construct(ExecState* exec, JSObject* construct JSLock::DropAllLocks dropAllLocks(exec); result = toJS(callAsConstructor(execRef, constructorRef, argumentCount, arguments.data(), &exception)); } - exec->setException(toJS(exec, exception)); + if (exception) + exec->setException(toJS(exec, exception)); return result; } } @@ -325,7 +329,8 @@ bool JSCallbackObject<Base>::hasInstance(ExecState* exec, JSValue value, JSValue JSLock::DropAllLocks dropAllLocks(exec); result = hasInstance(execRef, thisRef, valueRef, &exception); } - exec->setException(toJS(exec, exception)); + if (exception) + exec->setException(toJS(exec, exception)); return result; } } @@ -363,7 +368,8 @@ JSValue JSCallbackObject<Base>::call(ExecState* exec, JSObject* functionObject, JSLock::DropAllLocks dropAllLocks(exec); result = toJS(exec, callAsFunction(execRef, functionRef, thisObjRef, argumentCount, arguments.data(), &exception)); } - exec->setException(toJS(exec, exception)); + if (exception) + exec->setException(toJS(exec, exception)); return result; } } @@ -435,7 +441,8 @@ double JSCallbackObject<Base>::toNumber(ExecState* exec) const } double dValue; - return toJS(exec, value).getNumber(dValue) ? dValue : NaN; + if (value) + return toJS(exec, value).getNumber(dValue) ? dValue : NaN; } return Base::toNumber(exec); @@ -459,7 +466,8 @@ UString JSCallbackObject<Base>::toString(ExecState* exec) const exec->setException(toJS(exec, exception)); return ""; } - return toJS(exec, value).getString(); + if (value) + return toJS(exec, value).getString(exec); } return Base::toString(exec); @@ -507,13 +515,14 @@ JSValue JSCallbackObject<Base>::staticValueGetter(ExecState* exec, const Identif JSLock::DropAllLocks dropAllLocks(exec); value = getProperty(toRef(exec), thisRef, propertyNameRef.get(), &exception); } - exec->setException(toJS(exec, exception)); + if (exception) { + exec->setException(toJS(exec, exception)); + return jsUndefined(); + } if (value) return toJS(exec, value); - if (exception) - return jsUndefined(); } - + return throwError(exec, ReferenceError, "Static value property defined with NULL getProperty callback."); } @@ -560,11 +569,12 @@ JSValue JSCallbackObject<Base>::callbackGetter(ExecState* exec, const Identifier JSLock::DropAllLocks dropAllLocks(exec); value = getProperty(toRef(exec), thisRef, propertyNameRef.get(), &exception); } - exec->setException(toJS(exec, exception)); + if (exception) { + exec->setException(toJS(exec, exception)); + return jsUndefined(); + } if (value) return toJS(exec, value); - if (exception) - return jsUndefined(); } return throwError(exec, ReferenceError, "hasProperty callback returned true for a property that doesn't exist."); diff --git a/JavaScriptCore/API/JSValueRef.cpp b/JavaScriptCore/API/JSValueRef.cpp index 2207181..31859d6 100644 --- a/JavaScriptCore/API/JSValueRef.cpp +++ b/JavaScriptCore/API/JSValueRef.cpp @@ -169,7 +169,7 @@ bool JSValueIsStrictEqual(JSContextRef ctx, JSValueRef a, JSValueRef b) JSValue jsA = toJS(exec, a); JSValue jsB = toJS(exec, b); - return JSValue::strictEqual(jsA, jsB); + return JSValue::strictEqual(exec, jsA, jsB); } bool JSValueIsInstanceOfConstructor(JSContextRef ctx, JSValueRef value, JSObjectRef constructor, JSValueRef* exception) @@ -307,7 +307,7 @@ void JSValueProtect(JSContextRef ctx, JSValueRef value) exec->globalData().heap.registerThread(); JSLock lock(exec); - JSValue jsValue = toJS(exec, value); + JSValue jsValue = toJSForGC(exec, value); gcProtect(jsValue); } @@ -317,6 +317,6 @@ void JSValueUnprotect(JSContextRef ctx, JSValueRef value) exec->globalData().heap.registerThread(); JSLock lock(exec); - JSValue jsValue = toJS(exec, value); + JSValue jsValue = toJSForGC(exec, value); gcUnprotect(jsValue); } diff --git a/JavaScriptCore/API/tests/testapi.c b/JavaScriptCore/API/tests/testapi.c index 152babc..e7aba0f 100644 --- a/JavaScriptCore/API/tests/testapi.c +++ b/JavaScriptCore/API/tests/testapi.c @@ -166,6 +166,10 @@ static JSValueRef MyObject_getProperty(JSContextRef context, JSObjectRef object, if (JSStringIsEqualToUTF8CString(propertyName, "cantFind")) { return JSValueMakeUndefined(context); } + + if (JSStringIsEqualToUTF8CString(propertyName, "hasPropertyLie")) { + return 0; + } if (JSStringIsEqualToUTF8CString(propertyName, "throwOnGet")) { return JSEvaluateScript(context, JSStringCreateWithUTF8CString("throw 'an exception'"), object, JSStringCreateWithUTF8CString("test script"), 1, exception); @@ -176,7 +180,7 @@ static JSValueRef MyObject_getProperty(JSContextRef context, JSObjectRef object, return JSValueMakeNumber(context, 1); } - return NULL; + return JSValueMakeNull(context); } static bool MyObject_setProperty(JSContextRef context, JSObjectRef object, JSStringRef propertyName, JSValueRef value, JSValueRef* exception) @@ -299,7 +303,7 @@ static JSValueRef MyObject_convertToType(JSContextRef context, JSObjectRef objec } // string conversion -- forward to default object class - return NULL; + return JSValueMakeNull(context); } static JSStaticValue evilStaticValues[] = { @@ -374,7 +378,7 @@ static JSValueRef EvilExceptionObject_convertToType(JSContextRef context, JSObje funcName = JSStringCreateWithUTF8CString("toStringExplicit"); break; default: - return NULL; + return JSValueMakeNull(context); break; } @@ -382,7 +386,7 @@ static JSValueRef EvilExceptionObject_convertToType(JSContextRef context, JSObje JSStringRelease(funcName); JSObjectRef function = JSValueToObject(context, func, exception); if (!function) - return NULL; + return JSValueMakeNull(context); JSValueRef value = JSObjectCallAsFunction(context, function, object, 0, NULL, exception); if (!value) { JSStringRef errorString = JSStringCreateWithUTF8CString("convertToType failed"); @@ -737,6 +741,15 @@ static void testInitializeFinalize() ASSERT(JSObjectGetPrivate(o) == (void*)3); } +static JSValueRef jsNumberValue = NULL; + +static void makeGlobalNumberValue(JSContextRef context) { + JSValueRef v = JSValueMakeNumber(context, 420); + JSValueProtect(context, v); + jsNumberValue = v; + v = NULL; +} + int main(int argc, char* argv[]) { const char *scriptPath = "testapi.js"; @@ -948,10 +961,12 @@ int main(int argc, char* argv[]) CFRelease(cfEmptyString); jsGlobalValue = JSObjectMake(context, NULL, NULL); + makeGlobalNumberValue(context); JSValueProtect(context, jsGlobalValue); JSGarbageCollect(context); ASSERT(JSValueIsObject(context, jsGlobalValue)); JSValueUnprotect(context, jsGlobalValue); + JSValueUnprotect(context, jsNumberValue); JSStringRef goodSyntax = JSStringCreateWithUTF8CString("x = 1;"); JSStringRef badSyntax = JSStringCreateWithUTF8CString("x := 1;"); diff --git a/JavaScriptCore/Android.mk b/JavaScriptCore/Android.mk index ba7f05a..2fca4f4 100644 --- a/JavaScriptCore/Android.mk +++ b/JavaScriptCore/Android.mk @@ -35,6 +35,10 @@ LOCAL_SRC_FILES := \ bytecode/StructureStubInfo.cpp \ \ bytecompiler/BytecodeGenerator.cpp \ +<<<<<<< HEAD:JavaScriptCore/Android.mk +======= + bytecompiler/NodesCodegen.cpp \ +>>>>>>> webkit.org at r51976:JavaScriptCore/Android.mk \ debugger/Debugger.cpp \ debugger/DebuggerActivation.cpp \ diff --git a/JavaScriptCore/ChangeLog b/JavaScriptCore/ChangeLog index fb09372..3e9187b 100644 --- a/JavaScriptCore/ChangeLog +++ b/JavaScriptCore/ChangeLog @@ -1,3 +1,2455 @@ +2009-12-10 Oliver Hunt <oliver@apple.com> + + Reviewed by Gavin Barraclough. + + Incorrect caching of prototype lookup with dictionary base + https://bugs.webkit.org/show_bug.cgi?id=32402 + + Make sure we don't add cached prototype lookup to the proto_list + lookup chain if the top level object is a dictionary. + + * jit/JITStubs.cpp: + (JSC::JITThunks::tryCacheGetByID): + +2009-12-10 Gavin Barraclough <barraclough@apple.com> + + Reviewed by Oliver Hunt. + + https://bugs.webkit.org/show_bug.cgi?id=32400 + Switch remaining cases of string addition to use ropes. + + ~1% progression on Sunspidey. + + * jit/JITStubs.cpp: + (JSC::DEFINE_STUB_FUNCTION): + * runtime/JSString.h: + (JSC::JSString::JSString): + (JSC::JSString::appendStringInConstruct): + * runtime/Operations.cpp: + (JSC::jsAddSlowCase): + * runtime/Operations.h: + (JSC::jsString): + (JSC::jsAdd): + +2009-12-10 Kent Hansen <kent.hansen@nokia.com> + + Reviewed by Geoffrey Garen. + + Remove JSObject::getPropertyAttributes() and all usage of it. + https://bugs.webkit.org/show_bug.cgi?id=31933 + + getOwnPropertyDescriptor() should be used instead. + + * JavaScriptCore.exp: + * JavaScriptCore.order: + * JavaScriptCore.vcproj/JavaScriptCore/JavaScriptCore.def: + * debugger/DebuggerActivation.cpp: + (JSC::DebuggerActivation::getOwnPropertyDescriptor): + * debugger/DebuggerActivation.h: + * runtime/JSObject.cpp: + (JSC::JSObject::propertyIsEnumerable): + * runtime/JSObject.h: + * runtime/JSVariableObject.cpp: + * runtime/JSVariableObject.h: + +2009-12-10 Gavin Barraclough <barraclough@apple.com> + + Reviewed by Oliver Hunt & Mark Rowe. + + https://bugs.webkit.org/show_bug.cgi?id=32367 + Add support for short Ropes (up to 3 entries) inline within JSString. + (rather than externally allocating an object to hold the rope). + Switch jsAdd of (JSString* + JSString*) to now make use of Ropes. + + ~1% progression on Sunspidey. + + * interpreter/Interpreter.cpp: + (JSC::Interpreter::privateExecute): + * jit/JITOpcodes.cpp: + (JSC::JIT::privateCompileCTIMachineTrampolines): + * jit/JITStubs.cpp: + (JSC::DEFINE_STUB_FUNCTION): + * runtime/JSString.cpp: + (JSC::JSString::resolveRope): + (JSC::JSString::toBoolean): + (JSC::JSString::getStringPropertyDescriptor): + * runtime/JSString.h: + (JSC::JSString::Rope::Fiber::deref): + (JSC::JSString::Rope::Fiber::ref): + (JSC::JSString::Rope::Fiber::refAndGetLength): + (JSC::JSString::Rope::append): + (JSC::JSString::JSString): + (JSC::JSString::~JSString): + (JSC::JSString::value): + (JSC::JSString::tryGetValue): + (JSC::JSString::length): + (JSC::JSString::canGetIndex): + (JSC::JSString::appendStringInConstruct): + (JSC::JSString::appendValueInConstructAndIncrementLength): + (JSC::JSString::isRope): + (JSC::JSString::string): + (JSC::JSString::ropeLength): + (JSC::JSString::getStringPropertySlot): + * runtime/Operations.h: + (JSC::jsString): + (JSC::jsAdd): + (JSC::resolveBase): + +2009-12-09 Anders Carlsson <andersca@apple.com> + + Reviewed by Geoffrey Garen. + + Fix three more things found by compiling with clang++. + + * runtime/Structure.h: + (JSC::StructureTransitionTable::reifySingleTransition): + Add the 'std' qualifier to the call to make_pair. + + * wtf/DateMath.cpp: + (WTF::initializeDates): + Incrementing a bool is deprecated according to the C++ specification. + + * wtf/PtrAndFlags.h: + (WTF::PtrAndFlags::PtrAndFlags): + Name lookup should not be done in dependent bases, so explicitly qualify the call to set. + +2009-12-09 Maciej Stachowiak <mjs@apple.com> + + Reviewed by Oliver Hunt. + + Google reader gets stuck in the "Loading..." state and does not complete + https://bugs.webkit.org/show_bug.cgi?id=32256 + <rdar://problem/7456388> + + * jit/JITArithmetic.cpp: + (JSC::JIT::emitSlow_op_jless): Fix some backward branches. + +2009-12-09 Gavin Barraclough <barraclough@apple.com> + + Reviewed by Oliver Hunt. + + https://bugs.webkit.org/show_bug.cgi?id=32228 + Make destruction of ropes non-recursive to prevent stack exhaustion. + Also, pass a UString& into initializeFiber rather than a Ustring::Rep*, + since the Rep is not being ref counted this could result in usage of a + Rep with refcount zero (where the Rep comes from a temporary UString + returned from a function). + + * runtime/JSString.cpp: + (JSC::JSString::Rope::destructNonRecursive): + (JSC::JSString::Rope::~Rope): + * runtime/JSString.h: + (JSC::JSString::Rope::initializeFiber): + * runtime/Operations.h: + (JSC::concatenateStrings): + +2009-12-09 Zoltan Herczeg <zherczeg@inf.u-szeged.hu> + + Reviewed by Eric Seidel. + + https://bugs.webkit.org/show_bug.cgi?id=31930 + + Update to r51457. ASSERTs changed to COMPILE_ASSERTs. + The speedup is 25%. + + * runtime/JSGlobalData.cpp: + (JSC::VPtrSet::VPtrSet): + +2009-12-09 Steve Block <steveblock@google.com> + + Reviewed by Adam Barth. + + Updates Android Makefiles with latest additions. + https://bugs.webkit.org/show_bug.cgi?id=32278 + + * Android.mk: Modified. + * Android.v8.wtf.mk: Modified. + +2009-12-09 Sam Weinig <sam@webkit.org> + + Reviewed by Gavin Barraclough. + + Fix a bug found while trying to compile JavaScriptCore with clang++. + + * yarr/RegexPattern.h: + (JSC::Yarr::PatternTerm::PatternTerm): Don't self assign here. Use false instead. + +2009-12-09 Anders Carlsson <andersca@apple.com> + + Reviewed by Sam Weinig. + + Attempt to fix the Windows build. + + * wtf/FastMalloc.h: + +2009-12-09 Anders Carlsson <andersca@apple.com> + + Reviewed by Sam Weinig. + + Fix some things found while trying to compile JavaScriptCore with clang++. + + * wtf/FastMalloc.h: + Add correct exception specifications for the allocation/deallocation operators. + + * wtf/Vector.h: + * wtf/VectorTraits.h: + Fix a bunch of struct/class mismatches. + +2009-12-08 Maciej Stachowiak <mjs@apple.com> + + Reviewed by Darin Adler. + + move code generation portions of Nodes.cpp to bytecompiler directory + https://bugs.webkit.org/show_bug.cgi?id=32284 + + * bytecompiler/NodesCodegen.cpp: Copied from parser/Nodes.cpp. Removed parts that + are not about codegen. + * parser/Nodes.cpp: Removed everything that is about codegen. + + Update build systems: + + * Android.mk: + * GNUmakefile.am: + * JavaScriptCore.gypi: + * JavaScriptCore.pri: + * JavaScriptCore.vcproj/JavaScriptCore/JavaScriptCore.vcproj: + * JavaScriptCore.xcodeproj/project.pbxproj: + * JavaScriptCoreSources.bkl: + +2009-12-08 Kevin Watters <kevinwatters@gmail.com> + + Reviewed by Kevin Ollivier. + + [wx] Mac plugins support. + + https://bugs.webkit.org/show_bug.cgi?id=32236 + + * wtf/Platform.h: + +2009-12-08 Dmitry Titov <dimich@chromium.org> + + Rubber-stamped by David Levin. + + Revert and reopen "Add asserts to RefCounted to make sure ref/deref happens on the right thread." + It may have caused massive increase of reported leaks on the bots. + https://bugs.webkit.org/show_bug.cgi?id=31639 + + * GNUmakefile.am: + * JavaScriptCore.gypi: + * JavaScriptCore.vcproj/WTF/WTF.vcproj: + * JavaScriptCore.xcodeproj/project.pbxproj: + * runtime/Structure.cpp: + (JSC::Structure::Structure): + * wtf/RefCounted.h: + (WTF::RefCountedBase::ref): + (WTF::RefCountedBase::hasOneRef): + (WTF::RefCountedBase::refCount): + (WTF::RefCountedBase::derefBase): + * wtf/ThreadVerifier.h: Removed. + +2009-12-08 Gustavo Noronha Silva <gustavo.noronha@collabora.co.uk> + + Reviewed by Darin Adler. + + Make WebKit build correctly on FreeBSD, IA64, and Alpha. + Based on work by Petr Salinger <Petr.Salinger@seznam.cz>, + and Colin Watson <cjwatson@ubuntu.com>. + + * wtf/Platform.h: + +2009-12-08 Dmitry Titov <dimich@chromium.org> + + Reviewed by Darin Adler. + + Add asserts to RefCounted to make sure ref/deref happens on the right thread. + https://bugs.webkit.org/show_bug.cgi?id=31639 + + * runtime/Structure.cpp: + (JSC::Structure::Structure): Disable thread verification on this class since it uses addressOfCount(). + * wtf/RefCounted.h: + (WTF::RefCountedBase::ref): Add ASSERT. + (WTF::RefCountedBase::hasOneRef): Ditto. + (WTF::RefCountedBase::refCount): Ditto. + (WTF::RefCountedBase::derefBase): Ditto. + (WTF::RefCountedBase::disableThreadVerification): delegate to ThreadVerifier method. + * wtf/ThreadVerifier.h: Added. + (WTF::ThreadVerifier::ThreadVerifier): New Debug-only class to verify that ref/deref of RefCounted is done on the same thread. + (WTF::ThreadVerifier::activate): Activates checks. Called when ref count becomes above 2. + (WTF::ThreadVerifier::deactivate): Deactivates checks. Called when ref count drops below 2. + (WTF::ThreadVerifier::disableThreadVerification): used on objects that should not be checked (StringImpl etc) + (WTF::ThreadVerifier::verifyThread): + * GNUmakefile.am: Add ThreadVerifier.h to the build file. + * JavaScriptCore.gypi: Ditto. + * JavaScriptCore.vcproj/WTF/WTF.vcproj: Ditto. + * JavaScriptCore.xcodeproj/project.pbxproj: Ditto. + +2009-12-08 Steve Block <steveblock@google.com> + + Reviewed by Adam Barth. + + [Android] Adds Makefiles for Android port. + https://bugs.webkit.org/show_bug.cgi?id=31325 + + * Android.mk: Added. + * Android.v8.wtf.mk: Added. + +2009-12-07 Dmitry Titov <dimich@chromium.org> + + Rubber-stamped by Darin Adler. + + Remove ENABLE_SHARED_SCRIPT flags + https://bugs.webkit.org/show_bug.cgi?id=32245 + This patch was obtained by "git revert" command and then un-reverting of ChangeLog files. + + * Configurations/FeatureDefines.xcconfig: + * wtf/Platform.h: + +2009-12-07 Gavin Barraclough <barraclough@apple.com> + + Reviewed by NOBODY (Windows build fixage part I). + + * JavaScriptCore.vcproj/JavaScriptCore/JavaScriptCore.def: + +2009-12-05 Gavin Barraclough <barraclough@apple.com> + + Reviewed by Oliver Hunt. + + https://bugs.webkit.org/show_bug.cgi?id=32184 + Handle out-of-memory conditions with JSC Ropes with a JS exception, rather than crashing. + Switch from using fastMalloc to tryFastMalloc, pass an ExecState to record the exception on. + + * API/JSCallbackObjectFunctions.h: + (JSC::::toString): + * API/JSValueRef.cpp: + (JSValueIsStrictEqual): + * JavaScriptCore.exp: + * bytecompiler/BytecodeGenerator.cpp: + (JSC::BytecodeGenerator::emitEqualityOp): + * debugger/DebuggerCallFrame.cpp: + (JSC::DebuggerCallFrame::functionName): + (JSC::DebuggerCallFrame::calculatedFunctionName): + * interpreter/Interpreter.cpp: + (JSC::Interpreter::callEval): + (JSC::Interpreter::privateExecute): + * jit/JITStubs.cpp: + (JSC::DEFINE_STUB_FUNCTION): + * profiler/ProfileGenerator.cpp: + (JSC::ProfileGenerator::addParentForConsoleStart): + * profiler/Profiler.cpp: + (JSC::Profiler::willExecute): + (JSC::Profiler::didExecute): + (JSC::Profiler::createCallIdentifier): + (JSC::createCallIdentifierFromFunctionImp): + * profiler/Profiler.h: + * runtime/ArrayPrototype.cpp: + (JSC::arrayProtoFuncIndexOf): + (JSC::arrayProtoFuncLastIndexOf): + * runtime/DateConstructor.cpp: + (JSC::constructDate): + * runtime/FunctionPrototype.cpp: + (JSC::functionProtoFuncToString): + * runtime/InternalFunction.cpp: + (JSC::InternalFunction::name): + (JSC::InternalFunction::displayName): + (JSC::InternalFunction::calculatedDisplayName): + * runtime/InternalFunction.h: + * runtime/JSCell.cpp: + (JSC::JSCell::getString): + * runtime/JSCell.h: + (JSC::JSValue::getString): + * runtime/JSONObject.cpp: + (JSC::gap): + (JSC::Stringifier::Stringifier): + (JSC::Stringifier::appendStringifiedValue): + * runtime/JSObject.cpp: + (JSC::JSObject::putDirectFunction): + (JSC::JSObject::putDirectFunctionWithoutTransition): + (JSC::JSObject::defineOwnProperty): + * runtime/JSObject.h: + * runtime/JSPropertyNameIterator.cpp: + (JSC::JSPropertyNameIterator::get): + * runtime/JSString.cpp: + (JSC::JSString::Rope::~Rope): + (JSC::JSString::resolveRope): + (JSC::JSString::getPrimitiveNumber): + (JSC::JSString::toNumber): + (JSC::JSString::toString): + (JSC::JSString::toThisString): + (JSC::JSString::getStringPropertyDescriptor): + * runtime/JSString.h: + (JSC::JSString::Rope::createOrNull): + (JSC::JSString::Rope::operator new): + (JSC::JSString::value): + (JSC::JSString::tryGetValue): + (JSC::JSString::getIndex): + (JSC::JSString::getStringPropertySlot): + (JSC::JSValue::toString): + * runtime/JSValue.h: + * runtime/NativeErrorConstructor.cpp: + (JSC::NativeErrorConstructor::NativeErrorConstructor): + * runtime/Operations.cpp: + (JSC::JSValue::strictEqualSlowCase): + * runtime/Operations.h: + (JSC::JSValue::equalSlowCaseInline): + (JSC::JSValue::strictEqualSlowCaseInline): + (JSC::JSValue::strictEqual): + (JSC::jsLess): + (JSC::jsLessEq): + (JSC::jsAdd): + (JSC::concatenateStrings): + * runtime/PropertyDescriptor.cpp: + (JSC::PropertyDescriptor::equalTo): + * runtime/PropertyDescriptor.h: + * runtime/StringPrototype.cpp: + (JSC::stringProtoFuncReplace): + (JSC::stringProtoFuncToLowerCase): + (JSC::stringProtoFuncToUpperCase): + +2009-12-07 Nikolas Zimmermann <nzimmermann@rim.com> + + Reviewed by Holger Freyther. + + Turn on (SVG) Filters support, by default. + https://bugs.webkit.org/show_bug.cgi?id=32224 + + * Configurations/FeatureDefines.xcconfig: Enable FILTERS build flag. + +2009-12-07 Steve Falkenburg <sfalken@apple.com> + + Build fix. Be flexible about which version of ICU is used on Windows. + + * JavaScriptCore.vcproj/jsc/jscCommon.vsprops: Add optional xcopy commands to copy ICU 4.2. + +2009-12-07 Maciej Stachowiak <mjs@apple.com> + + Reviewed by Oliver Hunt. + + op_loop_if_less JIT codegen is broken for 64-bit + https://bugs.webkit.org/show_bug.cgi?id=32221 + + * jit/JITOpcodes.cpp: + (JSC::JIT::emit_op_loop_if_false): Fix codegen in this version - test was backwards. + +2009-12-07 Oliver Hunt <oliver@apple.com> + + Reviewed by Maciej Stachowiak. + + Object.create fails if properties on the descriptor are getters + https://bugs.webkit.org/show_bug.cgi?id=32219 + + Correctly initialise the PropertySlots with the descriptor object. + + * runtime/ObjectConstructor.cpp: + (JSC::toPropertyDescriptor): + +2009-12-06 Maciej Stachowiak <mjs@apple.com> + + Not reviewed, build fix. + + Actually tested 64-bit *and* 32-bit build this time. + + * jit/JITOpcodes.cpp: + (JSC::JIT::emit_op_loop_if_false): + +2009-12-06 Maciej Stachowiak <mjs@apple.com> + + Not reviewed, build fix. + + Really really fix 64-bit build for prior patch (actually tested this time). + + * jit/JITOpcodes.cpp: + (JSC::JIT::emit_op_loop_if_false): + (JSC::JIT::emitSlow_op_loop_if_false): + +2009-12-06 Maciej Stachowiak <mjs@apple.com> + + Not reviewed, build fix. + + Really fix 64-bit build for prior patch. + + * jit/JITArithmetic.cpp: + (JSC::JIT::emitSlow_op_jless): + +2009-12-06 Maciej Stachowiak <mjs@apple.com> + + Not reviewed, build fix. + + Fix 64-bit build for prior patch. + + * jit/JITOpcodes.cpp: + (JSC::JIT::emitSlow_op_loop_if_less): + +2009-12-05 Maciej Stachowiak <mjs@apple.com> + + Reviewed by Oliver Hunt. + + conway benchmark spends half it's time in op_less (jump fusion fails) + https://bugs.webkit.org/show_bug.cgi?id=32190 + + <1% speedup on SunSpider and V8 + 2x speedup on "conway" benchmark + + Two optimizations: + 1) Improve codegen for logical operators &&, || and ! in a condition context + + When generating code for combinations of &&, || and !, in a + condition context (i.e. in an if statement or loop condition), we + used to produce a value, and then separately jump based on its + truthiness. Now we pass the false and true targets in, and let the + logical operators generate jumps directly. This helps in four + ways: + + a) Individual clauses of a short-circuit logical operator can now + jump directly to the then or else clause of an if statement (or to + the top or exit of a loop) instead of jumping to a jump. + + b) It used to be that jump fusion with the condition of the first + clause of a logical operator was inhibited, because the register + was ref'd to be used later, in the actual condition jump; this no + longer happens since a jump straight to the final target is + generated directly. + + c) It used to be that jump fusion with the condition of the second + clause of a logical operator was inhibited, because there was a + jump target right after the second clause and before the actual + condition jump. But now it's no longer necessary for the first + clause to jump there so jump fusion is not blocked. + + d) We avoid generating excess mov statements in some cases. + + As a concrete example this source: + + if (!((x < q && y < q) || (t < q && z < q))) { + // ... + } + + Used to generate this bytecode: + + [ 34] less r1, r-15, r-19 + [ 38] jfalse r1, 7(->45) + [ 41] less r1, r-16, r-19 + [ 45] jtrue r1, 14(->59) + [ 48] less r1, r-17, r-19 + [ 52] jfalse r1, 7(->59) + [ 55] less r1, r-18, r-19 + [ 59] jtrue r1, 17(->76) + + And now generates this bytecode (also taking advantage of the second optimization below): + + [ 34] jnless r-15, r-19, 8(->42) + [ 38] jless r-16, r-19, 26(->64) + [ 42] jnless r-17, r-19, 8(->50) + [ 46] jless r-18, r-19, 18(->64) + + Note the jump fusion and the fact that there's less jump + indirection - three of the four jumps go straight to the target + clause instead of indirecting through another jump. + + 2) Implement jless opcode to take advantage of the above, since we'll now often generate + a less followed by a jtrue where fusion is not forbidden. + + * parser/Nodes.h: + (JSC::ExpressionNode::hasConditionContextCodegen): Helper function to determine + whether a node supports special conditional codegen. Return false as this is the default. + (JSC::ExpressionNode::emitBytecodeInConditionContext): Assert not reached - only really + defined for nodes that do have conditional codegen. + (JSC::UnaryOpNode::expr): Add const version. + (JSC::LogicalNotNode::hasConditionContextCodegen): Returne true only if subexpression + supports it. + (JSC::LogicalOpNode::hasConditionContextCodegen): Return true. + * parser/Nodes.cpp: + (JSC::LogicalNotNode::emitBytecodeInConditionContext): Implemented - just swap + the true and false targets for the child node. + (JSC::LogicalOpNode::emitBytecodeInConditionContext): Implemented - handle jumps + directly, improving codegen quality. Also handles further nested conditional codegen. + (JSC::ConditionalNode::emitBytecode): Use condition context codegen when available. + (JSC::IfNode::emitBytecode): ditto + (JSC::IfElseNode::emitBytecode): ditto + (JSC::DoWhileNode::emitBytecode): ditto + (JSC::WhileNode::emitBytecode): ditto + (JSC::ForNode::emitBytecode): ditto + + * bytecode/Opcode.h: + - Added loop_if_false opcode - needed now that falsey jumps can be backwards. + - Added jless opcode to take advantage of new fusion opportunities. + * bytecode/CodeBlock.cpp: + (JSC::CodeBlock::dump): Handle above. + * bytecompiler/BytecodeGenerator.cpp: + (JSC::BytecodeGenerator::emitJumpIfTrue): Add peephole for less + jtrue ==> jless. + (JSC::BytecodeGenerator::emitJumpIfFalse): Add handling of backwrds falsey jumps. + * bytecompiler/BytecodeGenerator.h: + (JSC::BytecodeGenerator::emitNodeInConditionContext): Wrapper to handle tracking of + overly deep expressions etc. + * interpreter/Interpreter.cpp: + (JSC::Interpreter::privateExecute): Implement the two new opcodes (loop_if_false, jless). + * jit/JIT.cpp: + (JSC::JIT::privateCompileMainPass): Implement JIT support for the two new opcodes. + (JSC::JIT::privateCompileSlowCases): ditto + * jit/JIT.h: + * jit/JITArithmetic.cpp: + (JSC::JIT::emit_op_jless): + (JSC::JIT::emitSlow_op_jless): ditto + (JSC::JIT::emitBinaryDoubleOp): ditto + * jit/JITOpcodes.cpp: + (JSC::JIT::emitSlow_op_loop_if_less): ditto + (JSC::JIT::emit_op_loop_if_false): ditto + (JSC::JIT::emitSlow_op_loop_if_false): ditto + * jit/JITStubs.cpp: + * jit/JITStubs.h: + (JSC::): + +2009-12-04 Kent Hansen <kent.hansen@nokia.com> + + Reviewed by Darin Adler. + + JavaScript delete operator should return false for string properties + https://bugs.webkit.org/show_bug.cgi?id=32012 + + * runtime/StringObject.cpp: + (JSC::StringObject::deleteProperty): + +2009-12-03 Drew Wilson <atwilson@chromium.org> + + Rolled back r51633 because it causes a perf regression in Chromium. + + * wtf/Platform.h: + +2009-12-03 Gavin Barraclough <barraclough@apple.com> + + Try and fix the Windows build. + + * JavaScriptCore.vcproj/JavaScriptCore/JavaScriptCore.def: Export a symbol that should be exported. + +2009-12-03 Mark Rowe <mrowe@apple.com> + + Try and fix the Mac build. + + * JavaScriptCore.exp: Export a symbol that should be exported. + +2009-12-03 Oliver Hunt <oliver@apple.com> + + Reviewed by Gavin Barraclough. + + REGRESSION(4.0.3-48777): Crash in JSC::ExecState::propertyNames() (Debug-only?) + https://bugs.webkit.org/show_bug.cgi?id=32133 + + Work around odd GCC-ism and correct the scopechain for use by + calls made while a cachedcall is active on the callstack. + + * interpreter/CachedCall.h: + (JSC::CachedCall::newCallFrame): + * runtime/JSArray.cpp: + (JSC::AVLTreeAbstractorForArrayCompare::compare_key_key): + * runtime/StringPrototype.cpp: + (JSC::stringProtoFuncReplace): + +2009-12-03 Gavin Barraclough <barraclough@apple.com> + + Reviewed by Oliver "Brraaaaiiiinnnnnzzzzzzzz" Hunt. + + https://bugs.webkit.org/show_bug.cgi?id=32136 + Add a rope representation to JSString. Presently JSString always holds its data in UString form. + Instead, allow the result of a string concatenation to be represented in a tree form - with a + variable sized, reference-counted rope node retaining a set of UString::Reps (or other rope nopes). + + Strings must still currently be resolved down to a flat UString representation before being used, + but by holding the string in a rope representation during construction we can avoid copying data + until we know the final size of the string. + + ~2% progression on SunSpider (~25% on date-format-xparb, ~20% on string-validate-input). + + * JavaScriptCore.exp: + + - Update exports. + + * interpreter/Interpreter.cpp: + (JSC::Interpreter::privateExecute): + + - Make use of new JSString::length() method to avoid prematurely resolving ropes. + + * jit/JITOpcodes.cpp: + (JSC::JIT::privateCompileCTIMachineTrampolines): + + - Switch the string length trampoline to read the length directly from JSString::m_length, + rather than from the JSString's UString::Rep's 'len' property. + + * jit/JITStubs.cpp: + (JSC::DEFINE_STUB_FUNCTION): + + - Modify op_add such that addition of two strings, where either or both strings are already + in rope representation, produces a rope as a result. + + * runtime/JSString.cpp: + (JSC::JSString::Rope::~Rope): + (JSC::copyChars): + (JSC::JSString::resolveRope): + (JSC::JSString::getPrimitiveNumber): + (JSC::JSString::toBoolean): + (JSC::JSString::toNumber): + (JSC::JSString::toString): + (JSC::JSString::toThisString): + (JSC::JSString::getStringPropertyDescriptor): + * runtime/JSString.h: + (JSC::JSString::Rope::Fiber::Fiber): + (JSC::JSString::Rope::Fiber::destroy): + (JSC::JSString::Rope::Fiber::isRope): + (JSC::JSString::Rope::Fiber::rope): + (JSC::JSString::Rope::Fiber::string): + (JSC::JSString::Rope::create): + (JSC::JSString::Rope::initializeFiber): + (JSC::JSString::Rope::ropeLength): + (JSC::JSString::Rope::stringLength): + (JSC::JSString::Rope::fibers): + (JSC::JSString::Rope::Rope): + (JSC::JSString::Rope::operator new): + (JSC::JSString::JSString): + (JSC::JSString::value): + (JSC::JSString::length): + (JSC::JSString::isRope): + (JSC::JSString::rope): + (JSC::JSString::string): + (JSC::JSString::canGetIndex): + (JSC::jsSingleCharacterSubstring): + (JSC::JSString::getIndex): + (JSC::jsSubstring): + (JSC::JSString::getStringPropertySlot): + + - Add rope form. + + * runtime/Operations.h: + (JSC::jsAdd): + (JSC::concatenateStrings): + + - Update string concatenation, and addition of ropes, to produce ropes. + + * runtime/StringObject.cpp: + (JSC::StringObject::getOwnPropertyNames): + + - Make use of new JSString::length() method to avoid prematurely resolving ropes. + +2009-11-23 Jeremy Moskovich <jeremy@chromium.org> + + Reviewed by Eric Seidel. + + Switch Chrome/Mac to use Core Text APIs rather than ATSUI APIs. + https://bugs.webkit.org/show_bug.cgi?id=31802 + + No test since this is already covered by existing pixel tests. + + * wtf/Platform.h: #define USE_CORE_TEXT for Chrome/Mac. + +2009-12-02 Oliver Hunt <oliver@apple.com> + + Reviewed by Gavin Barraclough. + + Add files missed in prior patch. + + * runtime/JSZombie.cpp: + (JSC::): + (JSC::JSZombie::leakedZombieStructure): + * runtime/JSZombie.h: Added. + (JSC::JSZombie::JSZombie): + (JSC::JSZombie::isZombie): + (JSC::JSZombie::classInfo): + (JSC::JSZombie::isGetterSetter): + (JSC::JSZombie::isAPIValueWrapper): + (JSC::JSZombie::isPropertyNameIterator): + (JSC::JSZombie::getCallData): + (JSC::JSZombie::getConstructData): + (JSC::JSZombie::getUInt32): + (JSC::JSZombie::toPrimitive): + (JSC::JSZombie::getPrimitiveNumber): + (JSC::JSZombie::toBoolean): + (JSC::JSZombie::toNumber): + (JSC::JSZombie::toString): + (JSC::JSZombie::toObject): + (JSC::JSZombie::markChildren): + (JSC::JSZombie::put): + (JSC::JSZombie::deleteProperty): + (JSC::JSZombie::toThisObject): + (JSC::JSZombie::toThisString): + (JSC::JSZombie::toThisJSString): + (JSC::JSZombie::getJSNumber): + (JSC::JSZombie::getOwnPropertySlot): + +2009-12-02 Oliver Hunt <oliver@apple.com> + + Reviewed by Gavin Barraclough. + + Add zombies to JSC + https://bugs.webkit.org/show_bug.cgi?id=32103 + + Add a compile time flag to make the JSC collector replace "unreachable" + objects with zombie objects. The zombie object is a JSCell subclass that + ASSERTs on any attempt to use the JSCell methods. In addition there are + a number of additional assertions in bottleneck code to catch zombie usage + as quickly as possible. + + Grrr. Argh. Brains. + + * JavaScriptCore.xcodeproj/project.pbxproj: + * interpreter/Register.h: + (JSC::Register::Register): + * runtime/ArgList.h: + (JSC::MarkedArgumentBuffer::append): + (JSC::ArgList::ArgList): + * runtime/Collector.cpp: + (JSC::Heap::destroy): + (JSC::Heap::sweep): + * runtime/Collector.h: + * runtime/JSCell.h: + (JSC::JSCell::isZombie): + (JSC::JSValue::isZombie): + * runtime/JSValue.h: + (JSC::JSValue::decode): + (JSC::JSValue::JSValue): + * wtf/Platform.h: + +2009-12-01 Jens Alfke <snej@chromium.org> + + Reviewed by Darin Adler. + + Added variants of find/contains/add that allow a foreign key type to be used. + This will allow AtomicString-keyed maps to be queried by C string without + having to create a temporary AtomicString (see HTTPHeaderMap.) + The code for this is adapted from the equivalent in HashSet.h. + + * wtf/HashMap.h: + (WTF::HashMap::find): + (WTF::HashMap::contains): + (WTF::HashMap::add): + * wtf/HashSet.h: Changed "method" to "function member" in a comment. + +2009-12-01 Gustavo Noronha Silva <gustavo.noronha@collabora.co.uk> + + Revert 51551 because it broke GTK+. + + * wtf/Platform.h: + +2009-11-30 Gavin Barraclough <barraclough@apple.com> + + Windows Build fix. Reviewed by NOBODY. + + * JavaScriptCore.vcproj/JavaScriptCore/JavaScriptCore.def: + +2009-11-24 Gavin Barraclough <barraclough@apple.com> + + Reviewed by Geoff Garen. + + Bug 31859 - Make world selection for JSC IsolatedWorlds automagical. + + WebCore presently has to explicitly specify the world before entering into JSC, + which is a little fragile (particularly since property access via a + getter/setter might invoke execution). Instead derive the current world from + the lexical global object. + + Remove the temporary duct tape of willExecute/didExecute virtual hooks on the JSGlobalData::ClientData - these are no longer necessary. + + * API/JSBase.cpp: + (JSEvaluateScript): + * API/JSObjectRef.cpp: + (JSObjectCallAsFunction): + * JavaScriptCore.exp: + * runtime/JSGlobalData.cpp: + * runtime/JSGlobalData.h: + +2009-11-30 Laszlo Gombos <laszlo.1.gombos@nokia.com> + + Reviewed by Kenneth Rohde Christiansen. + + [Qt] Remove obsolete PLATFORM(KDE) code + https://bugs.webkit.org/show_bug.cgi?id=31958 + + KDE is now using unpatched QtWebKit. + + * parser/Lexer.cpp: Remove obsolete KDE_USE_FINAL guard + * wtf/Platform.h: Remove PLATFORM(KDE) definition and code + section that is guarded with it. + +2009-11-30 Jan-Arve Sæther <jan-arve.saether@nokia.com> + + Reviewed by Simon Hausmann. + + [Qt] Fix compilation with win32-icc + + The Intel compiler does not support the __has_trivial_constructor type + trait. The Intel Compiler can report itself as _MSC_VER >= 1400. The + reason for that is that the Intel Compiler depends on the Microsoft + Platform SDK, and in order to try to be "fully" MS compatible it will + "pretend" to be the same MS compiler as was shipped with the MS PSDK. + (Thus, compiling with win32-icc with VC8 SDK will make the source code + "think" the compiler at hand supports this type trait). + + * wtf/TypeTraits.h: + +2009-11-29 Laszlo Gombos <laszlo.1.gombos@nokia.com> + + Reviewed by Eric Seidel. + + [Qt] Mac build has JIT disabled + https://bugs.webkit.org/show_bug.cgi?id=31828 + + * wtf/Platform.h: Enable JIT for Qt Mac builds + +2009-11-28 Laszlo Gombos <laszlo.1.gombos@nokia.com> + + Reviewed by Eric Seidel. + + Apply workaround for the limitation of VirtualFree with MEM_RELEASE to all ports running on Windows + https://bugs.webkit.org/show_bug.cgi?id=31943 + + * runtime/MarkStack.h: + (JSC::MarkStack::MarkStackArray::shrinkAllocation): + +2009-11-28 Zoltan Herczeg <zherczeg@inf.u-szeged.hu> + + Reviewed by Gavin Barraclough. + + https://bugs.webkit.org/show_bug.cgi?id=31930 + + Seems a typo. We don't need ~270k memory to determine the vptrs. + + * runtime/JSGlobalData.cpp: + (JSC::VPtrSet::VPtrSet): + +2009-11-27 Shinichiro Hamaji <hamaji@chromium.org> + + Unreviewed. + + Move GOwnPtr* from wtf to wtf/gtk + https://bugs.webkit.org/show_bug.cgi?id=31793 + + Build fix for chromium after r51423. + Exclude gtk directory from chromium build. + + * JavaScriptCore.gyp/JavaScriptCore.gyp: + +2009-11-25 Oliver Hunt <oliver@apple.com> + + Reviewed by Gavin Barraclough. + + Incorrect behaviour of jneq_null in the interpreter + https://bugs.webkit.org/show_bug.cgi?id=31901 + + Correct the logic of jneq_null. This is already covered by existing tests. + + * interpreter/Interpreter.cpp: + (JSC::Interpreter::privateExecute): + +2009-11-26 Laszlo Gombos <laszlo.1.gombos@nokia.com> + + Reviewed by Oliver Hunt. + + Move GOwnPtr* from wtf to wtf/gtk + https://bugs.webkit.org/show_bug.cgi?id=31793 + + * GNUmakefile.am: Change the path for GOwnPtr.*. + * JavaScriptCore.gyp/JavaScriptCore.gyp: Remove + GOwnPtr.cpp from the exclude list. + * JavaScriptCore.gypi: Change the path for GOwnPtr.*. + * wscript: Remove GOwnPtr.cpp from the exclude list. + * wtf/GOwnPtr.cpp: Removed. + * wtf/GOwnPtr.h: Removed. + * wtf/Threading.h: Change the path for GOwnPtr.h. + * wtf/gtk/GOwnPtr.cpp: Copied from JavaScriptCore/wtf/GOwnPtr.cpp. + * wtf/gtk/GOwnPtr.h: Copied from JavaScriptCore/wtf/GOwnPtr.h. + * wtf/unicode/glib/UnicodeGLib.h: Change the path for GOwnPtr.h. + +2009-11-24 Dmitry Titov <dimich@chromium.org> + + Reviewed by Eric Seidel. + + Add ENABLE_SHARED_SCRIPT feature define and flag for build-webkit + https://bugs.webkit.org/show_bug.cgi?id=31444 + + * Configurations/FeatureDefines.xcconfig: + * wtf/Platform.h: + +2009-11-24 Chris Marrin <cmarrin@apple.com> + + Reviewed by Simon Fraser. + + Add ability to enable ACCELERATED_COMPOSITING on Windows (currently disabled) + https://bugs.webkit.org/show_bug.cgi?id=27314 + + * wtf/Platform.h: + +2009-11-24 Jason Smith <dark.panda@gmail.com> + + Reviewed by Alexey Proskuryakov. + + RegExp#exec's returned Array-like object behaves differently from + regular Arrays + https://bugs.webkit.org/show_bug.cgi?id=31689 + + * JavaScriptCore/runtime/RegExpConstructor.cpp: ensure that undefined + values are added to the returned RegExpMatchesArray + +2009-11-24 Oliver Hunt <oliver@apple.com> + + Reviewed by Alexey Proskuryakov. + + JSON.stringify performance on undefined is very poor + https://bugs.webkit.org/show_bug.cgi?id=31839 + + Switch from a UString to a Vector<UChar> when building + the JSON string, allowing us to safely remove the substr-copy + we otherwise did when unwinding an undefined property. + + Also turns out to be a ~5% speedup on stringification. + + * runtime/JSONObject.cpp: + (JSC::Stringifier::StringBuilder::append): + (JSC::Stringifier::stringify): + (JSC::Stringifier::Holder::appendNextProperty): + +2009-11-24 Mark Rowe <mrowe@apple.com> + + Fix production builds where the source tree may be read-only. + + * JavaScriptCore.xcodeproj/project.pbxproj: + +2009-11-23 Laszlo Gombos <laszlo.1.gombos@nokia.com> + + Reviewed by Kenneth Rohde Christiansen. + + Include "config.h" to meet Coding Style Guidelines + https://bugs.webkit.org/show_bug.cgi?id=31792 + + * wtf/unicode/UTF8.cpp: + * wtf/unicode/glib/UnicodeGLib.cpp: + * wtf/unicode/wince/UnicodeWince.cpp: + +2009-11-23 Geoffrey Garen <ggaren@apple.com> + + Reviewed by Oliver Hunt. + + Streamlined some Math functions where we expect or know the result not + to be representable as an int. + + SunSpider says 0.6% faster. + + * runtime/JSNumberCell.h: + (JSC::JSValue::JSValue): + * runtime/JSValue.h: + (JSC::JSValue::): + (JSC::jsDoubleNumber): + (JSC::JSValue::JSValue): Added a function for making a numeric JSValue + and skipping the "can I encode this as an int?" check, avoiding the + overhead of int <-> double roundtripping and double <-> double comparison + and branching. + + * runtime/MathObject.cpp: + (JSC::mathProtoFuncACos): + (JSC::mathProtoFuncASin): + (JSC::mathProtoFuncATan): + (JSC::mathProtoFuncATan2): + (JSC::mathProtoFuncCos): + (JSC::mathProtoFuncExp): + (JSC::mathProtoFuncLog): + (JSC::mathProtoFuncRandom): + (JSC::mathProtoFuncSin): + (JSC::mathProtoFuncSqrt): + (JSC::mathProtoFuncTan): For these functions, which we expect or know + to produce results not representable as ints, call jsDoubleNumber instead + of jsNumber. + +2009-11-23 Mark Rowe <mrowe@apple.com> + + Unreviewed. Unbreak the regression tests after r51329. + + * API/JSBase.cpp: + (JSEvaluateScript): Null-check clientData before dereferencing it. + * API/JSObjectRef.cpp: + (JSObjectCallAsFunction): Ditto. + +2009-11-23 Gavin Barraclough <barraclough@apple.com> + + Reviewed by Geoff Garen. + + Part 1/3 of <rdar://problem/7377477> REGRESSION: Many web pages fail to render after interesting script runs in isolated world + + Some clients of the JavaScriptCore API expect to be able to make callbacks over the JSC API, + and for this to automagically cause execution to take place in the world associated with the + global object associated with the ExecState (JSContextRef) passed. However this is not how + things work - the world must be explicitly set within WebCore. + + Making this work just for API calls to evaluate & call will be a far from perfect solution, + since direct (non-API) use of JSC still relies on WebCore setting the current world correctly. + A better solution would be to make this all work automagically all throughout WebCore, but this + will require more refactoring. + + Since the API is in JSC but worlds only exist in WebCore, add callbacks on the JSGlobalData::ClientData + to allow it to update the current world on entry/exit via the JSC API. This is temporary duck + tape, and should be removed once the current world no longer needs to be explicitly tracked. + + * API/JSBase.cpp: + (JSEvaluateScript): + * API/JSObjectRef.cpp: + (JSObjectCallAsFunction): + * JavaScriptCore.exp: + * runtime/JSGlobalData.cpp: + (JSC::JSGlobalData::ClientData::beginningExecution): + (JSC::JSGlobalData::ClientData::completedExecution): + * runtime/JSGlobalData.h: + +2009-11-23 Steve Block <steveblock@google.com> + + Reviewed by Dmitry Titov. + + Adds MainThreadAndroid.cpp with Android-specific WTF threading functions. + https://bugs.webkit.org/show_bug.cgi?id=31807 + + * wtf/android: Added. + * wtf/android/MainThreadAndroid.cpp: Added. + (WTF::timeoutFired): + (WTF::initializeMainThreadPlatform): + (WTF::scheduleDispatchFunctionsOnMainThread): + +2009-11-23 Alexey Proskuryakov <ap@apple.com> + + Reviewed by Brady Eidson. + + https://bugs.webkit.org/show_bug.cgi?id=31748 + Make WebSocketHandleCFNet respect proxy auto-configuration files via CFProxySupport + + * JavaScriptCore.exp: Export callOnMainThreadAndWait. + +2009-11-23 Laszlo Gombos <laszlo.1.gombos@nokia.com> + + Reviewed by Kenneth Rohde Christiansen. + + [Symbian] Fix lastIndexOf() for Symbian + https://bugs.webkit.org/show_bug.cgi?id=31773 + + Symbian soft floating point library has problems with operators + comparing NaN to numbers. Without a workaround lastIndexOf() + function does not work. + + Patch developed by David Leong. + + * runtime/StringPrototype.cpp: + (JSC::stringProtoFuncLastIndexOf):Add an extra test + to check for NaN for Symbian. + +2009-11-23 Steve Block <steveblock@google.com> + + Reviewed by Eric Seidel. + + Android port lacks implementation of atomicIncrement and atomicDecrement. + https://bugs.webkit.org/show_bug.cgi?id=31715 + + * wtf/Threading.h: Modified. + (WTF::atomicIncrement): Added Android implementation. + (WTF::atomicDecrement): Added Android implementation. + +2009-11-22 Laszlo Gombos <laszlo.1.gombos@nokia.com> + + Unreviewed. + + [Qt] Sort source lists and remove obsolete comments + from the build system. + + * JavaScriptCore.pri: + +2009-11-21 Laszlo Gombos <laszlo.1.gombos@nokia.com> + + Reviewed by Eric Seidel. + + [Qt][Mac] Turn on multiple JavaScript threads for QtWebkit on Mac + https://bugs.webkit.org/show_bug.cgi?id=31753 + + * wtf/Platform.h: + +2009-11-19 Steve Block <steveblock@google.com> + + Android port lacks configuration in Platform.h and config.h. + https://bugs.webkit.org/show_bug.cgi?id=31671 + + * wtf/Platform.h: Modified. Added Android-specific configuration. + +2009-11-19 Alexey Proskuryakov <ap@apple.com> + + Reviewed by Darin Adler. + + https://bugs.webkit.org/show_bug.cgi?id=31690 + Make SocketStreamHandleCFNet work on Windows + + * JavaScriptCore.vcproj/JavaScriptCore/JavaScriptCore.def: + * wtf/MainThread.cpp: + (WTF::FunctionWithContext::FunctionWithContext): + (WTF::dispatchFunctionsFromMainThread): + (WTF::callOnMainThreadAndWait): + * wtf/MainThread.h: + Re-add callOnMainThreadAndWait(), which was removed in bug 23926. + +2009-11-19 Dmitry Titov <dimich@chromium.org> + + Reviewed by David Levin. + + isMainThread() on Chromium (Mac and Linux) is so slow it timeouts LayoutTests.. + https://bugs.webkit.org/show_bug.cgi?id=31693 + + * wtf/ThreadingPthreads.cpp: + (WTF::initializeThreading): grab and use the pthread_t of the main thread instead of ThreadIdentifier. + (WTF::isMainThread): Ditto. + +2009-11-19 Laszlo Gombos <laszlo.1.gombos@nokia.com> + + Reviewed by Darin Adler. + + Remove HAVE(STRING_H) guard from JavaScriptCore + https://bugs.webkit.org/show_bug.cgi?id=31668 + + * config.h: + * runtime/UString.cpp: + +2009-11-19 Dumitru Daniliuc <dumi@chromium.org> + + Reviewed by Dmitry Titov. + + Fixing a bug in MessageQueue::removeIf() that leads to an + assertion failure. + + https://bugs.webkit.org/show_bug.cgi?id=31657 + + * wtf/MessageQueue.h: + (WTF::MessageQueue::removeIf): + +2009-11-19 Laszlo Gombos <laszlo.1.gombos@nokia.com> + + Reviewed by Darin Adler. + + Remove HAVE(FLOAT_H) guard + https://bugs.webkit.org/show_bug.cgi?id=31661 + + JavaScriptCore has a dependency on float.h, there is + no need to guard float.h. + + * runtime/DatePrototype.cpp: Remove include directive + for float.h as it is included in MathExtras.h already. + * runtime/Operations.cpp: Ditto. + * runtime/UString.cpp: Ditto. + * wtf/dtoa.cpp: Ditto. + * wtf/MathExtras.h: Remove HAVE(FLOAT_H) guard. + * wtf/Platform.h: Ditto. + +2009-11-19 Thiago Macieira <thiago.macieira@nokia.com> + + Reviewed by Simon Hausmann. + + Build fix for 32-bit Sparc machines: these machines are big-endian. + + * wtf/Platform.h: + +2009-11-18 Laszlo Gombos <laszlo.1.gombos@nokia.com> + + Reviewed by Kenneth Rohde Christiansen. + + [Qt] Remove support for Qt v4.3 or older versions + https://bugs.webkit.org/show_bug.cgi?id=29469 + + * JavaScriptCore.pro: + * jsc.pro: + * wtf/unicode/qt4/UnicodeQt4.h: + +2009-11-18 Kent Tamura <tkent@chromium.org> + + Reviewed by Darin Adler. + + Move UString::from(double) implementation to new + WTF::doubleToStringInJavaScriptFormat(), and expose it because WebCore + code will use it. + https://bugs.webkit.org/show_bug.cgi?id=31330 + + - Introduce new function createRep(const char*, unsigned) and + UString::UString(const char*, unsigned) to reduce 2 calls to strlen(). + - Fix a bug that dtoa() doesn't update *rve if the input value is NaN + or Infinity. + + No new tests because this doesn't change the behavior. + + * JavaScriptCore.exp: + * JavaScriptCore.vcproj/JavaScriptCore/JavaScriptCore.def: + * runtime/UString.cpp: + (JSC::createRep): + (JSC::UString::UString): + (JSC::UString::from): Move the code to doubleToStringInJavaScriptFormat(). + * runtime/UString.h: + * wtf/dtoa.cpp: + (WTF::dtoa): Fix a bug about rve. + (WTF::append): A helper for doubleToStringInJavaScriptFormat(). + (WTF::doubleToStringInJavaScriptFormat): Move the code from UString::from(double). + * wtf/dtoa.h: + +2009-11-18 Laszlo Gombos <laszlo.1.gombos@nokia.com> + + Reviewed by Kenneth Rohde Christiansen. + + [Qt] Remove WTF_USE_JAVASCRIPTCORE_BINDINGS as it is no longer used + https://bugs.webkit.org/show_bug.cgi?id=31643 + + * JavaScriptCore.pro: + +2009-11-18 Nate Chapin <japhet@chromium.org> + + Reviewed by Darin Fisher. + + Remove Chromium's unnecessary dependency on wtf's tcmalloc files. + + https://bugs.webkit.org/show_bug.cgi?id=31648 + + * JavaScriptCore.gyp/JavaScriptCore.gyp: + +2009-11-18 Thiago Macieira <thiago.macieira@nokia.com> + + Reviewed by Gavin Barraclough. + + [Qt] Implement symbol hiding for JSC's JIT functions. + + These functions are implemented directly in assembly, so they need the + proper directives to enable/disable visibility. On ELF systems, it's + .hidden, whereas on Mach-O systems (Mac) it's .private_extern. On + Windows, it's not necessary since you have to explicitly export. I + also implemented the AIX idiom, though it's unlikely anyone will + implement AIX/POWER JIT. + https://bugs.webkit.org/show_bug.cgi?id=30864 + + * jit/JITStubs.cpp: + +2009-11-18 Oliver Hunt <oliver@apple.com> + + Reviewed by Alexey Proskuryakov. + + Interpreter may do an out of range access when throwing an exception in the profiler. + https://bugs.webkit.org/show_bug.cgi?id=31635 + + Add bounds check. + + * interpreter/Interpreter.cpp: + (JSC::Interpreter::throwException): + +2009-11-18 Gabor Loki <loki@inf.u-szeged.hu> + + Reviewed by Darin Adler. + + Fix the clobber list of cacheFlush for ARM and Thumb2 on Linux + https://bugs.webkit.org/show_bug.cgi?id=31631 + + * jit/ExecutableAllocator.h: + (JSC::ExecutableAllocator::cacheFlush): + +2009-11-18 Harald Fernengel <harald.fernengel@nokia.com> + + Reviewed by Simon Hausmann. + + [Qt] Fix detection of linux-g++ + + Never use "linux-g++*" to check for linux-g++, since this will break embedded + builds which use linux-arm-g++ and friends. Use 'linux*-g++*' to check for any + g++ on linux mkspec. + + * JavaScriptCore.pri: + +2009-11-17 Jon Honeycutt <jhoneycutt@apple.com> + + Add JSContextRefPrivate.h to list of copied files. + + Reviewed by Mark Rowe. + + * JavaScriptCore.vcproj/JavaScriptCore/JavaScriptCoreGenerated.make: + +2009-11-17 Martin Robinson <martin.james.robinson@gmail.com> + + Reviewed by Adam Barth. + + [GTK] Style cleanup for GOwnPtr + https://bugs.webkit.org/show_bug.cgi?id=31506 + + Remove forward declaration in GOwnPtr and do some style cleanup. + + * wtf/GOwnPtr.cpp: + * wtf/GOwnPtr.h: + (WTF::GOwnPtr::GOwnPtr): + (WTF::GOwnPtr::~GOwnPtr): + (WTF::GOwnPtr::get): + (WTF::GOwnPtr::release): + (WTF::GOwnPtr::outPtr): + (WTF::GOwnPtr::set): + (WTF::GOwnPtr::clear): + (WTF::GOwnPtr::operator*): + (WTF::GOwnPtr::operator->): + (WTF::GOwnPtr::operator!): + (WTF::GOwnPtr::operator UnspecifiedBoolType): + (WTF::GOwnPtr::swap): + (WTF::swap): + (WTF::operator==): + (WTF::operator!=): + (WTF::getPtr): + (WTF::freeOwnedGPtr): + +2009-11-17 Oliver Hunt <oliver@apple.com> + + Reviewed by Maciej Stachowiak. + + Incorrect use of JavaScriptCore API in DumpRenderTree + https://bugs.webkit.org/show_bug.cgi?id=31577 + + Add assertions to the 'toJS' functions to catch mistakes like + this early. Restructure existing code which blindly passed potentially + null values to toJS when forwarding exceptions so that a null check is + performed first. + + * API/APICast.h: + (toJS): + (toJSForGC): + * API/JSCallbackObjectFunctions.h: + (JSC::::getOwnPropertySlot): + (JSC::::put): + (JSC::::deleteProperty): + (JSC::::construct): + (JSC::::hasInstance): + (JSC::::call): + (JSC::::toNumber): + (JSC::::toString): + (JSC::::staticValueGetter): + (JSC::::callbackGetter): + * API/tests/testapi.c: Fix errors in the API tester. + (MyObject_getProperty): + (MyObject_convertToType): + (EvilExceptionObject_convertToType): + +2009-11-16 Zoltan Herczeg <zherczeg@inf.u-szeged.hu> + + Reviewed by Gavin Barraclough. + + https://bugs.webkit.org/show_bug.cgi?id=31050 + + Minor fixes for JSVALUE32_64: branchConvertDoubleToInt32 + failed on a CortexA8 CPU, but not on a simulator; and + JITCall.cpp modifications was somehow not committed to mainline. + + * assembler/ARMAssembler.h: + (JSC::ARMAssembler::fmrs_r): + * assembler/MacroAssemblerARM.h: + (JSC::MacroAssemblerARM::branchConvertDoubleToInt32): + * jit/JITCall.cpp: + (JSC::JIT::compileOpCall): + +2009-11-16 Joerg Bornemann <joerg.bornemann@trolltech.com> + + Reviewed by Simon Hausmann. + + Fix Qt build on Windows CE 6. + + * JavaScriptCore.pri: Add missing include path. + * wtf/Platform.h: Include ce_time.h for Windows CE 6. + +2009-11-13 Zoltan Herczeg <zherczeg@inf.u-szeged.hu> + + Reviewed by Gavin Barraclough. + + https://bugs.webkit.org/show_bug.cgi?id=31050 + + Adding optimization support for mode JSVALUE32_64 + on ARM systems. + + * jit/JIT.h: + * jit/JITCall.cpp: + (JSC::JIT::compileOpCall): + * jit/JITPropertyAccess.cpp: + (JSC::JIT::emit_op_method_check): + (JSC::JIT::compileGetByIdHotPath): + (JSC::JIT::compileGetByIdSlowCase): + (JSC::JIT::emit_op_put_by_id): + +2009-11-14 Zoltan Herczeg <zherczeg@inf.u-szeged.hu> + + Reviewed by Gavin Barraclough. + + https://bugs.webkit.org/show_bug.cgi?id=31050 + + Adding JSVALUE32_64 support for ARM (but not turning it + on by default). All optimizations must be disabled, since + this patch is only the first of a series of patches. + + During the work, a lot of x86 specific code revealed and + made platform independent. + See revisions: 50531 50541 50593 50594 50595 + + * assembler/ARMAssembler.h: + (JSC::ARMAssembler::): + (JSC::ARMAssembler::fdivd_r): + * assembler/MacroAssemblerARM.h: + (JSC::MacroAssemblerARM::lshift32): + (JSC::MacroAssemblerARM::neg32): + (JSC::MacroAssemblerARM::rshift32): + (JSC::MacroAssemblerARM::branchOr32): + (JSC::MacroAssemblerARM::set8): + (JSC::MacroAssemblerARM::setTest8): + (JSC::MacroAssemblerARM::loadDouble): + (JSC::MacroAssemblerARM::divDouble): + (JSC::MacroAssemblerARM::convertInt32ToDouble): + (JSC::MacroAssemblerARM::zeroDouble): + * jit/JIT.cpp: + * jit/JIT.h: + * jit/JITOpcodes.cpp: + (JSC::JIT::privateCompileCTIMachineTrampolines): + * jit/JITStubs.cpp: + * wtf/StdLibExtras.h: + +2009-11-13 Dominik Röttsches <dominik.roettsches@access-company.com> + + Reviewed by Eric Seidel. + + Unify TextBoundaries implementations by only relying on WTF Unicode abstractions + https://bugs.webkit.org/show_bug.cgi?id=31468 + + Adding isAlphanumeric abstraction, required + by TextBoundaries.cpp. + + * wtf/unicode/glib/UnicodeGLib.h: + (WTF::Unicode::isAlphanumeric): + * wtf/unicode/icu/UnicodeIcu.h: + (WTF::Unicode::isAlphanumeric): + +2009-11-13 Norbert Leser <norbert.leser&nokia.com> + + Reviewed by Eric Seidel. + + Added macros for USERINCLUDE paths within symbian blocks + to guarantee inclusion of respective header files from local path + first (to avoid clashes with same names of header files in system include path). + + * JavaScriptCore.pri: + +2009-11-13 Oliver Hunt <oliver@apple.com> + + Reviewed by Geoff Garen. + + JSValueProtect and JSValueUnprotect don't protect API wrapper values + https://bugs.webkit.org/show_bug.cgi?id=31485 + + Make JSValueProtect/Unprotect use a new 'toJS' function, 'toJSForGC' that + does not attempt to to strip out API wrapper objects. + + * API/APICast.h: + (toJSForGC): + * API/JSValueRef.cpp: + (JSValueProtect): + (JSValueUnprotect): + * API/tests/testapi.c: + (makeGlobalNumberValue): + (main): + +2009-11-13 İsmail Dönmez <ismail@namtrac.org> + + Reviewed by Antti Koivisto. + + Fix typo, ce_time.cpp should be ce_time.c + + * JavaScriptCore.pri: + +2009-11-12 Steve VanDeBogart <vandebo@chromium.org> + + Reviewed by Adam Barth. + + Calculate the time offset only if we were able to parse + the date string. This saves an IPC in Chromium for + invalid date strings. + https://bugs.webkit.org/show_bug.cgi?id=31416 + + * wtf/DateMath.cpp: + (WTF::parseDateFromNullTerminatedCharacters): + (JSC::parseDateFromNullTerminatedCharacters): + +2009-11-12 Oliver Hunt <oliver@apple.com> + + Rollout r50896 until i can work out why it causes failures. + + * bytecompiler/BytecodeGenerator.cpp: + (JSC::BytecodeGenerator::emitReturn): + * interpreter/Interpreter.cpp: + (JSC::Interpreter::execute): + * parser/Nodes.cpp: + (JSC::EvalNode::emitBytecode): + +2009-11-12 Steve Falkenburg <sfalken@apple.com> + + Reviewed by Stephanie Lewis. + + Remove LIBRARY directive from def file to fix Debug_All target. + + * JavaScriptCore.vcproj/JavaScriptCore/JavaScriptCore.def: + +2009-11-12 Gustavo Noronha Silva <gustavo.noronha@collabora.co.uk> + + Rubber-stamped by Holger Freyther. + + Revert r50204, since it makes DRT crash on 32 bits release builds + for GTK+. + + * wtf/FastMalloc.h: + +2009-11-12 Oliver Hunt <oliver@apple.com> + + Reviewed by Gavin Barraclough. + + Start unifying entry logic for function and eval code. + + Eval now uses a ret instruction to end execution, and sets up + a callframe more in line with what we do for function entry. + + * bytecompiler/BytecodeGenerator.cpp: + (JSC::BytecodeGenerator::emitReturn): + * interpreter/Interpreter.cpp: + (JSC::Interpreter::execute): + * parser/Nodes.cpp: + (JSC::EvalNode::emitBytecode): + +2009-11-12 Richard Moe Gustavsen <richard.gustavsen@nokia.com> + + Reviewed by Kenneth Rohde Christiansen. + + [Qt] Disable pthread_setname_np. + + This allows Qt builds on Mac from 10.6 to run on earlier version + where this symbol is not present. + https://bugs.webkit.org/show_bug.cgi?id=31403 + + * wtf/Platform.h: + +2009-11-12 Thiago Macieira <thiago.macieira@nokia.com> + + Reviewed by Kenneth Rohde Christiansen. + + [Qt] Fix linking on Linux 32-bit. + + It was missing the ".text" directive at the top of the file, + indicating that code would follow. Without it, the assembler created + "NOTYPE" symbols, which would result in linker errors. + https://bugs.webkit.org/show_bug.cgi?id=30863 + + * jit/JITStubs.cpp: + +2009-11-11 Laszlo Gombos <laszlo.1.gombos@nokia.com> + + Reviewed by Alexey Proskuryakov. + + Refactor multiple JavaScriptCore threads + https://bugs.webkit.org/show_bug.cgi?id=31328 + + Remove the id field from the PlatformThread structure + as it is not used. + + * runtime/Collector.cpp: + (JSC::getCurrentPlatformThread): + (JSC::suspendThread): + (JSC::resumeThread): + (JSC::getPlatformThreadRegisters): + +2009-11-10 Geoffrey Garen <ggaren@apple.com> + + Linux build fix: Added an #include for UINT_MAX. + + * runtime/WeakRandom.h: + +2009-11-10 Geoffrey Garen <ggaren@apple.com> + + JavaScriptGlue build fix: Marked a file 'private' instead of 'project'. + + * JavaScriptCore.xcodeproj/project.pbxproj: + +2009-11-10 Geoffrey Garen <ggaren@apple.com> + + Reviewed by Gavin "avGni arBalroguch" Barraclough. + + Faster Math.random, based on GameRand. + + SunSpider says 1.4% faster. + + * GNUmakefile.am: + * JavaScriptCore.gypi: + * JavaScriptCore.vcproj/JavaScriptCore/JavaScriptCore.vcproj: + * JavaScriptCore.xcodeproj/project.pbxproj: Added the header to the project. + + * runtime/JSGlobalData.cpp: + (JSC::JSGlobalData::JSGlobalData): + * runtime/JSGlobalData.h: Use an object to track random number generation + state, initialized to the current time. + + * runtime/MathObject.cpp: + (JSC::MathObject::MathObject): + (JSC::mathProtoFuncRandom): Use the new hotness. + + * runtime/WeakRandom.h: Added. + (JSC::WeakRandom::WeakRandom): + (JSC::WeakRandom::get): + (JSC::WeakRandom::advance): The new hotness. + +2009-11-09 Geoffrey Garen <ggaren@apple.com> + + Reviewed by Oliver Hunt. + + Imported the v8 DST cache. + + SunSpider says 1.5% faster. + + * runtime/JSGlobalData.cpp: + (JSC::JSGlobalData::resetDateCache): Reset the DST cache when resetting + other date data. + + * runtime/JSGlobalData.h: + (JSC::DSTOffsetCache::DSTOffsetCache): + (JSC::DSTOffsetCache::reset): Added a struct for the DST cache. + + * wtf/DateMath.cpp: + (WTF::calculateDSTOffsetSimple): + (WTF::calculateDSTOffset): + (WTF::parseDateFromNullTerminatedCharacters): + (JSC::getDSTOffset): + (JSC::gregorianDateTimeToMS): + (JSC::msToGregorianDateTime): + (JSC::parseDateFromNullTerminatedCharacters): + * wtf/DateMath.h: The imported code for probing and updating the cache. + +2009-11-09 Geoffrey Garen <ggaren@apple.com> + + Reviewed by Oliver Hunt. + + Fixed an edge case that could cause the engine not to notice a timezone + change. + + No test because this case would require manual intervention to change + the timezone during the test. + + SunSpider reports no change. + + * runtime/DateInstanceCache.h: + (JSC::DateInstanceCache::DateInstanceCache): + (JSC::DateInstanceCache::reset): Added a helper function for resetting + this cache. Also, shrank the cache, since we'll be resetting it often. + + * runtime/JSGlobalData.cpp: + (JSC::JSGlobalData::resetDateCache): Include resetting the DateInstanceCache + in resetting Date data. (Otherwise, a cache hit could bypass a necessary + timezone update check.) + +2009-11-09 Geoffrey Garen <ggaren@apple.com> + + Reviewed by Sam Weinig. + + Some manual inlining and constant propogation in Date code. + + SunSpider reports a 0.4% speedup on date-*, no overall speedup. Shark + says some previously evident stalls are now gone. + + * runtime/DateConstructor.cpp: + (JSC::callDate): + * runtime/DateConversion.cpp: + (JSC::formatTime): + (JSC::formatTimeUTC): Split formatTime into UTC and non-UTC variants. + + * runtime/DateConversion.h: + * runtime/DateInstance.cpp: + (JSC::DateInstance::calculateGregorianDateTime): + (JSC::DateInstance::calculateGregorianDateTimeUTC): + * runtime/DateInstance.h: + (JSC::DateInstance::gregorianDateTime): + (JSC::DateInstance::gregorianDateTimeUTC): Split gregorianDateTime into + a UTC and non-UTC variant, and split each variant into a fast inline + case and a slow out-of-line case. + + * runtime/DatePrototype.cpp: + (JSC::formatLocaleDate): + (JSC::dateProtoFuncToString): + (JSC::dateProtoFuncToUTCString): + (JSC::dateProtoFuncToISOString): + (JSC::dateProtoFuncToDateString): + (JSC::dateProtoFuncToTimeString): + (JSC::dateProtoFuncGetFullYear): + (JSC::dateProtoFuncGetUTCFullYear): + (JSC::dateProtoFuncToGMTString): + (JSC::dateProtoFuncGetMonth): + (JSC::dateProtoFuncGetUTCMonth): + (JSC::dateProtoFuncGetDate): + (JSC::dateProtoFuncGetUTCDate): + (JSC::dateProtoFuncGetDay): + (JSC::dateProtoFuncGetUTCDay): + (JSC::dateProtoFuncGetHours): + (JSC::dateProtoFuncGetUTCHours): + (JSC::dateProtoFuncGetMinutes): + (JSC::dateProtoFuncGetUTCMinutes): + (JSC::dateProtoFuncGetSeconds): + (JSC::dateProtoFuncGetUTCSeconds): + (JSC::dateProtoFuncGetTimezoneOffset): + (JSC::setNewValueFromTimeArgs): + (JSC::setNewValueFromDateArgs): + (JSC::dateProtoFuncSetYear): + (JSC::dateProtoFuncGetYear): Updated for the gregorianDateTime change above. + +2009-11-09 Geoffrey Garen <ggaren@apple.com> + + Build fix: export a new symbol. + + * JavaScriptCore.exp: + * JavaScriptCore.vcproj/JavaScriptCore/JavaScriptCore.def: + +2009-11-09 Geoffrey Garen <ggaren@apple.com> + + Reviewed by Sam "Home Wrecker" Weinig. + + Added a tiny cache for Date parsing. + + SunSpider says 1.2% faster. + + * runtime/DateConversion.cpp: + (JSC::parseDate): Try to reuse the last parsed Date, if present. + + * runtime/JSGlobalData.cpp: + (JSC::JSGlobalData::resetDateCache): + * runtime/JSGlobalData.h: Added storage for last parsed Date. Refactored + this code to make resetting the date cache easier. + + * runtime/JSGlobalObject.h: + (JSC::DynamicGlobalObjectScope::DynamicGlobalObjectScope): Updated for + refactoring. + + * wtf/DateMath.cpp: + (JSC::parseDateFromNullTerminatedCharacters): + * wtf/DateMath.h: Changed ExecState to be first parameter, as is the JSC custom. + +2009-11-09 Oliver Hunt <oliver@apple.com> + + Reviewed by Gavin Barraclough. + + Can cache prototype lookups on uncacheable dictionaries. + https://bugs.webkit.org/show_bug.cgi?id=31198 + + Replace fromDictionaryTransition with flattenDictionaryObject and + flattenDictionaryStructure. This change is necessary as we need to + guarantee that our attempt to convert away from a dictionary structure + will definitely succeed, and in some cases this requires mutating the + object storage itself. + + * interpreter/Interpreter.cpp: + (JSC::Interpreter::tryCacheGetByID): + * jit/JITStubs.cpp: + (JSC::JITThunks::tryCacheGetByID): + (JSC::DEFINE_STUB_FUNCTION): + * runtime/BatchedTransitionOptimizer.h: + (JSC::BatchedTransitionOptimizer::~BatchedTransitionOptimizer): + * runtime/JSObject.h: + (JSC::JSObject::flattenDictionaryObject): + * runtime/Operations.h: + (JSC::normalizePrototypeChain): + * runtime/Structure.cpp: + (JSC::Structure::flattenDictionaryStructure): + (JSC::comparePropertyMapEntryIndices): + * runtime/Structure.h: + +2009-11-09 Laszlo Gombos <laszlo.1.gombos@nokia.com> + + Not reviewed, build fix. + + Remove extra character from r50701. + + * JavaScriptCore.pri: + +2009-11-09 Laszlo Gombos <laszlo.1.gombos@nokia.com> + + Not reviewed, build fix. + + Revert r50695 because it broke QtWebKit (clean builds). + + * JavaScriptCore.pri: + +2009-11-09 Norbert Leser <norbert.leser@nokia.com> + + Reviewed by Kenneth Rohde Christiansen. + + Prepended $$PWD to GENERATED_SOURCES_DIR to avoid potential ambiguities when included from WebCore.pro. + Some preprocessors consider this GENERATED_SOURCES_DIR relative to current invoking dir (e.g., ./WebCore), + and not the working dir of JavaCriptCore.pri (i.e., ../JavaScriptCore/). + + * JavaScriptCore.pri: + +2009-11-09 Laszlo Gombos <laszlo.1.gombos@nokia.com> + + Reviewed by Kenneth Rohde Christiansen. + + Use explicit parentheses to silence gcc 4.4 -Wparentheses warnings + https://bugs.webkit.org/show_bug.cgi?id=31040 + + * interpreter/Interpreter.cpp: + (JSC::Interpreter::privateExecute): + +2009-11-08 David Levin <levin@chromium.org> + + Reviewed by NOBODY (speculative snow leopard and windows build fixes). + + * wtf/DateMath.cpp: + (WTF::parseDateFromNullTerminatedCharacters): + (JSC::gregorianDateTimeToMS): + (JSC::msToGregorianDateTime): + (JSC::parseDateFromNullTerminatedCharacters): + * wtf/DateMath.h: + (JSC::GregorianDateTime::GregorianDateTime): + +2009-11-08 David Levin <levin@chromium.org> + + Reviewed by NOBODY (chromium build fix). + + Hopefully, the last build fix. + + Create better separation in DateMath about the JSC + and non-JSC portions. Also, only expose the non-JSC + version in the exports. + + * JavaScriptCore.exp: + * JavaScriptCore.vcproj/JavaScriptCore/JavaScriptCore.def: + * wtf/DateMath.cpp: + (WTF::parseDateFromNullTerminatedCharacters): + (JSC::getUTCOffset): + (JSC::gregorianDateTimeToMS): + (JSC::msToGregorianDateTime): + (JSC::parseDateFromNullTerminatedCharacters): + * wtf/DateMath.h: + (JSC::gmtoffset): + +2009-11-08 David Levin <levin@chromium.org> + + Reviewed by NOBODY (chromium build fix). + + For the change in DateMath. + + * config.h: + * wtf/DateMath.cpp: + +2009-11-06 Geoffrey Garen <ggaren@apple.com> + + Windows build fix: export some symbols. + + * JavaScriptCore.vcproj/JavaScriptCore/JavaScriptCore.def: + +2009-11-06 Geoffrey Garen <ggaren@apple.com> + + Build fix: updated export file. + + * JavaScriptCore.vcproj/JavaScriptCore/JavaScriptCore.def: + +2009-11-06 Geoffrey Garen <ggaren@apple.com> + + Build fix: added some #includes. + + * wtf/CurrentTime.h: + * wtf/DateMath.h: + +2009-11-06 Geoffrey Garen <ggaren@apple.com> + + Reviewed by Oliver Hunt. + + https://bugs.webkit.org/show_bug.cgi?id=31197 + Implemented a timezone cache not based on Mac OS X's notify_check API. + + If the VM calculates the local timezone offset from UTC, it caches the + result until the end of the current VM invocation. (We don't want to cache + forever, because the user's timezone may change over time.) + + This removes notify_* overhead on Mac, and, more significantly, removes + OS time and date call overhead on non-Mac platforms. + + ~8% speedup on Date microbenchmark on Mac. SunSpider reports maybe a tiny + speedup on Mac. (Speedup on non-Mac platforms should be even more noticeable.) + + * JavaScriptCore.exp: + + * interpreter/CachedCall.h: + (JSC::CachedCall::CachedCall): + * interpreter/Interpreter.cpp: + (JSC::Interpreter::execute): + * runtime/JSGlobalObject.h: + (JSC::DynamicGlobalObjectScope::DynamicGlobalObjectScope): Made the + DynamicGlobalObjectScope constructor responsible for checking whether a + dynamicGlobalObject has already been set. This eliminated some duplicate + client code, and allowed me to avoid adding even more duplicate client + code. Made DynamicGlobalObjectScope responsible for resetting the + local timezone cache upon first entry to the VM. + + * runtime/DateConstructor.cpp: + (JSC::constructDate): + (JSC::callDate): + (JSC::dateParse): + (JSC::dateUTC): + * runtime/DateConversion.cpp: + (JSC::parseDate): + * runtime/DateConversion.h: + * runtime/DateInstance.cpp: + (JSC::DateInstance::gregorianDateTime): + * runtime/DateInstance.h: + * runtime/DateInstanceCache.h: + * runtime/DatePrototype.cpp: + (JSC::setNewValueFromTimeArgs): + (JSC::setNewValueFromDateArgs): + (JSC::dateProtoFuncSetYear): + * runtime/InitializeThreading.cpp: + (JSC::initializeThreadingOnce): + * runtime/JSGlobalData.cpp: + (JSC::JSGlobalData::JSGlobalData): + * runtime/JSGlobalData.h: + * wtf/DateMath.cpp: + (WTF::getCurrentUTCTime): + (WTF::getCurrentUTCTimeWithMicroseconds): + (WTF::getLocalTime): + (JSC::getUTCOffset): Use the new cache. Also, see below. + (JSC::gregorianDateTimeToMS): + (JSC::msToGregorianDateTime): + (JSC::initializeDates): + (JSC::parseDateFromNullTerminatedCharacters): Simplified the way this function + accounts for the local timezone offset, to accomodate our new caching API, + and a (possibly misguided) caller in WebCore. Also, see below. + * wtf/DateMath.h: + (JSC::GregorianDateTime::GregorianDateTime): Moved most of the code in + DateMath.* into the JSC namespace. The code needed to move so it could + naturally interact with ExecState and JSGlobalData to support caching. + Logically, it seemed right to move it, too, since this code is not really + as low-level as the WTF namespace might imply -- it implements a set of + date parsing and conversion quirks that are finely tuned to the JavaScript + language. Also removed the Mac OS X notify_* infrastructure. + + * wtf/CurrentTime.h: + (WTF::currentTimeMS): + (WTF::getLocalTime): Moved the rest of the DateMath code here, and renamed + it to make it consistent with WTF's currentTime function. + +2009-11-06 Gabor Loki <loki@inf.u-szeged.hu> + + Unreviewed trivial buildfix after r50595. + + Rename the remaining rshiftPtr calls to rshift32 + + * jit/JITArithmetic.cpp: + (JSC::JIT::emit_op_rshift): + * jit/JITInlineMethods.h: + (JSC::JIT::emitFastArithImmToInt): + +2009-11-06 Gavin Barraclough <barraclough@apple.com> + + Reviewed by Oliver Hunt. + + Tidy up the shift methods on the macro-assembler interface. + + Currently behaviour of shifts of a magnitude > 0x1f is undefined. + Instead defined that all shifts are masked to this range. This makes a lot of + practical sense, both since having undefined behaviour is not particularly + desirable, and because this behaviour is commonly required (particularly since + it is required bt ECMA-262 for shifts). + + Update the ARM assemblers to provide this behaviour. Remove (now) redundant + masks from JITArithmetic, and remove rshiftPtr (this was used in case that + could be rewritten in a simpler form using rshift32, only optimized JSVALUE32 + on x86-64, which uses JSVALUE64!) + + * assembler/MacroAssembler.h: + * assembler/MacroAssemblerARM.h: + (JSC::MacroAssemblerARM::lshift32): + (JSC::MacroAssemblerARM::rshift32): + * assembler/MacroAssemblerARMv7.h: + (JSC::MacroAssemblerARMv7::lshift32): + (JSC::MacroAssemblerARMv7::rshift32): + * assembler/MacroAssemblerX86_64.h: + * jit/JITArithmetic.cpp: + (JSC::JIT::emit_op_lshift): + (JSC::JIT::emit_op_rshift): + +2009-11-05 Gavin Barraclough <barraclough@apple.com> + + Rubber Stamped by Oliver Hunt. + + Remove a magic number (1) from the JIT, instead compute the value with OBJECT_OFFSET. + + * jit/JITInlineMethods.h: + (JSC::JIT::emitPutJITStubArg): + (JSC::JIT::emitPutJITStubArgConstant): + (JSC::JIT::emitGetJITStubArg): + (JSC::JIT::emitPutJITStubArgFromVirtualRegister): + * jit/JITStubCall.h: + (JSC::JITStubCall::JITStubCall): + (JSC::JITStubCall::getArgument): + * jit/JITStubs.h: + +2009-11-05 Zoltan Herczeg <zherczeg@inf.u-szeged.hu> + + Reviewed by Gavin Barraclough. + + https://bugs.webkit.org/show_bug.cgi?id=31159 + Fix branchDouble behaviour on ARM THUMB2 JIT. + + The x86 branchDouble behaviour is reworked, and all JIT + ports should follow the x86 port. See bug 31104 and 31151 + + This patch contains a fix for the traditional ARM port + + * assembler/ARMAssembler.h: + (JSC::ARMAssembler::): + (JSC::ARMAssembler::fmrs_r): + (JSC::ARMAssembler::ftosid_r): + * assembler/MacroAssemblerARM.h: + (JSC::MacroAssemblerARM::): + (JSC::MacroAssemblerARM::branchDouble): + (JSC::MacroAssemblerARM::branchConvertDoubleToInt32): + +2009-11-05 Chris Jerdonek <chris.jerdonek@gmail.com> + + Reviewed by Eric Seidel. + + Removed the "this is part of the KDE project" comments from + all *.h, *.cpp, *.idl, and *.pm files. + + https://bugs.webkit.org/show_bug.cgi?id=31167 + + The maintenance and architecture page in the project wiki lists + this as a task. + + This change includes no changes or additions to test cases + since the change affects only comments. + + * wtf/wince/FastMallocWince.h: + +2009-11-05 Gabor Loki <loki@inf.u-szeged.hu> + + Reviewed by Gavin Barraclough. + + Use ARMv7 specific encoding for immediate constants on ARMv7 target + https://bugs.webkit.org/show_bug.cgi?id=31060 + + * assembler/ARMAssembler.cpp: + (JSC::ARMAssembler::getOp2): Use INVALID_IMM + (JSC::ARMAssembler::getImm): Use encodeComplexImm for complex immediate + (JSC::ARMAssembler::moveImm): Ditto. + (JSC::ARMAssembler::encodeComplexImm): Encode a constant by one or two + instructions or a PC relative load. + * assembler/ARMAssembler.h: Use INVALID_IMM if a constant cannot be + encoded as an immediate constant. + (JSC::ARMAssembler::): + (JSC::ARMAssembler::movw_r): 16-bit immediate load + (JSC::ARMAssembler::movt_r): High halfword 16-bit immediate load + (JSC::ARMAssembler::getImm16Op2): Encode immediate constant for + movw_r and mowt_r + +2009-11-04 Mark Mentovai <mark@chromium.org> + + Reviewed by Mark Rowe. + + Provide TARGETING_TIGER and TARGETING_LEOPARD as analogues to + BUILDING_ON_TIGER and BUILDING_ON_LEOPARD. The TARGETING_ macros + consider the deployment target; the BUILDING_ON_ macros consider the + headers being built against. + + * wtf/Platform.h: + +2009-11-04 Gavin Barraclough <barraclough@apple.com> + + Reviewed by Oliver Hunt. + + https://bugs.webkit.org/show_bug.cgi?id=31151 + Fix branchDouble behaviour on ARM THUMB2 JIT. + + The ARMv7 JIT is currently using ARMv7Assembler::ConditionEQ to branch + for DoubleEqualOrUnordered, however this is incorrect – ConditionEQ won't + branch on unordered operands. Similarly, DoubleLessThanOrUnordered & + DoubleLessThanOrEqualOrUnordered use ARMv7Assembler::ConditionLO & + ARMv7Assembler::ConditionLS, whereas they should be using + ARMv7Assembler::ConditionLT & ARMv7Assembler::ConditionLE. + + Fix these, and fill out the missing DoubleConditions. + + * assembler/MacroAssemblerARMv7.h: + (JSC::MacroAssemblerARMv7::): + (JSC::MacroAssemblerARMv7::branchDouble): + +2009-11-04 Gavin Barraclough <barraclough@apple.com> + + Rubber Stamped by Oliver Hunt. + + Enable native call optimizations on ARMv7. (Existing ARM_TRADITIONAL + implementation was generic, worked perfectly, just needed turning on). + + * jit/JITOpcodes.cpp: + * wtf/Platform.h: + +2009-11-04 Gavin Barraclough <barraclough@apple.com> + + Rubber Stamped by Mark Rowe, Oliver Hunt, and Sam Weinig. + + Add a missing assert to the ARMv7 JIT. + + * assembler/ARMv7Assembler.h: + (JSC::ARMThumbImmediate::ARMThumbImmediate): + +2009-11-04 Mark Rowe <mrowe@apple.com> + + Rubber-stamped by Oliver Hunt. + + Remove bogus op_ prefix on dumped version of three opcodes. + + * bytecode/CodeBlock.cpp: + (JSC::CodeBlock::dump): + +2009-11-04 Mark Rowe <mrowe@apple.com> + + Reviewed by Sam Weinig. + + Fix dumping of constants in bytecode so that they aren't printed as large positive register numbers. + + We do this by having the registerName function return information about the constant if the register + number corresponds to a constant. This requires that registerName, and several functions that call it, + be converted to member functions of CodeBlock so that the constant value can be retrieved. The + ExecState also needs to be threaded down through these functions so that it can be passed on to + constantName when needed. + + * bytecode/CodeBlock.cpp: + (JSC::constantName): + (JSC::CodeBlock::registerName): + (JSC::CodeBlock::printUnaryOp): + (JSC::CodeBlock::printBinaryOp): + (JSC::CodeBlock::printConditionalJump): + (JSC::CodeBlock::printGetByIdOp): + (JSC::CodeBlock::printPutByIdOp): + (JSC::CodeBlock::dump): + * bytecode/CodeBlock.h: + (JSC::CodeBlock::isConstantRegisterIndex): + +2009-11-04 Pavel Heimlich <tropikhajma@gmail.com> + + Reviewed by Alexey Proskuryakov. + + https://bugs.webkit.org/show_bug.cgi?id=30647 + Solaris build failure due to strnstr. + + * wtf/StringExtras.h: Enable strnstr on Solaris, too. + +2009-11-04 Gavin Barraclough <barraclough@apple.com> + + Reviewed by Oliver Hunt. + + https://bugs.webkit.org/show_bug.cgi?id=31104 + Refactor x86-specific behaviour out of the JIT. + + - Add explicit double branch conditions for ordered and unordered comparisons (presently the brehaviour is a mix). + - Refactor double to int conversion out into the MacroAssembler. + - Remove broken double to int conversion for !JSVALUE32_64 builds - this code was broken and slowing us down, fixing it showed it not to be an improvement. + - Remove exclusion of double to int conversion from (1 % X) cases in JSVALUE32_64 builds - if this was of benefit this is no longer the case; simplify. + + * assembler/MacroAssemblerARM.h: + (JSC::MacroAssemblerARM::): + * assembler/MacroAssemblerARMv7.h: + (JSC::MacroAssemblerARMv7::): + * assembler/MacroAssemblerX86Common.h: + (JSC::MacroAssemblerX86Common::): + (JSC::MacroAssemblerX86Common::convertInt32ToDouble): + (JSC::MacroAssemblerX86Common::branchDouble): + (JSC::MacroAssemblerX86Common::branchConvertDoubleToInt32): + * jit/JITArithmetic.cpp: + (JSC::JIT::emitBinaryDoubleOp): + (JSC::JIT::emit_op_div): + (JSC::JIT::emitSlow_op_jnless): + (JSC::JIT::emitSlow_op_jnlesseq): + * jit/JITOpcodes.cpp: + (JSC::JIT::emit_op_jfalse): + +2009-11-04 Mark Mentovai <mark@chromium.org> + + Reviewed by Eric Seidel. + + Remove BUILDING_ON_LEOPARD from JavaScriptCore.gyp. This is supposed + to be set as needed only in wtf/Platform.h. + + * JavaScriptCore.gyp/JavaScriptCore.gyp: + +2009-11-02 Oliver Hunt <oliver@apple.com> + + Reviewed by Gavin Barraclough. + + REGRESSION (r48573): JSC may incorrectly cache chain lookups with a dictionary at the head of the chain + https://bugs.webkit.org/show_bug.cgi?id=31045 + + Add guards to prevent caching of prototype chain lookups with dictionaries at the + head of the chain. Also add a few tighter assertions to cached prototype lookups + to catch this in future. + + * interpreter/Interpreter.cpp: + (JSC::Interpreter::tryCacheGetByID): + (JSC::Interpreter::privateExecute): + * jit/JITStubs.cpp: + (JSC::JITThunks::tryCacheGetByID): + +2009-11-02 Laszlo Gombos <laszlo.1.gombos@nokia.com> + + Reviewed by Darin Adler. + + PLATFORM(CF) should be set when building for Qt on Darwin + https://bugs.webkit.org/show_bug.cgi?id=23671 + + * wtf/Platform.h: Turn on CF support if both QT and DARWIN + platforms are defined. + +2009-11-02 Dmitry Titov <dimich@chromium.org> + + Reviewed by David Levin. + + Remove threadsafe refcounting from tasks used with WTF::MessageQueue. + https://bugs.webkit.org/show_bug.cgi?id=30612 + + * wtf/MessageQueue.h: + (WTF::MessageQueue::alwaysTruePredicate): + (WTF::MessageQueue::~MessageQueue): + (WTF::MessageQueue::append): + (WTF::MessageQueue::appendAndCheckEmpty): + (WTF::MessageQueue::prepend): + (WTF::MessageQueue::waitForMessage): + (WTF::MessageQueue::waitForMessageFilteredWithTimeout): + (WTF::MessageQueue::tryGetMessage): + (WTF::MessageQueue::removeIf): + The MessageQueue is changed to act as a queue of OwnPtr<DataType>. It takes ownership + of posted tasks and passes it to the new owner (in another thread) when the task is fetched. + All methods have arguments of type PassOwnPtr<DataType> and return the same type. + + * wtf/Threading.cpp: + (WTF::createThread): + Superficial change to trigger rebuild of JSC project on Windows, + workaround for https://bugs.webkit.org/show_bug.cgi?id=30890 + +2009-10-30 Geoffrey Garen <ggaren@apple.com> + + Reviewed by Oliver Hunt. + + Fixed failing layout test: restore a special case I accidentally deleted. + + * runtime/DatePrototype.cpp: + (JSC::setNewValueFromDateArgs): In the case of applying a change to a date + that is NaN, reset the date to 0 *and* then apply the change; don't just + reset the date to 0. + +2009-10-30 Geoffrey Garen <ggaren@apple.com> + + Windows build fix: update for object-to-pointer change. + + * runtime/DatePrototype.cpp: + (JSC::formatLocaleDate): + +2009-10-29 Geoffrey Garen <ggaren@apple.com> + + Reviewed by Darin Adler. + + https://bugs.webkit.org/show_bug.cgi?id=30942 + Use pointers instead of copies to pass GregorianDateTime objects around. + + SunSpider reports a shocking 4.5% speedup on date-format-xparb, and 1.3% + speedup on date-format-tofte. + + * runtime/DateInstance.cpp: + (JSC::DateInstance::gregorianDateTime): + * runtime/DateInstance.h: + * runtime/DatePrototype.cpp: + (JSC::formatLocaleDate): + (JSC::dateProtoFuncToString): + (JSC::dateProtoFuncToUTCString): + (JSC::dateProtoFuncToISOString): + (JSC::dateProtoFuncToDateString): + (JSC::dateProtoFuncToTimeString): + (JSC::dateProtoFuncGetFullYear): + (JSC::dateProtoFuncGetUTCFullYear): + (JSC::dateProtoFuncToGMTString): + (JSC::dateProtoFuncGetMonth): + (JSC::dateProtoFuncGetUTCMonth): + (JSC::dateProtoFuncGetDate): + (JSC::dateProtoFuncGetUTCDate): + (JSC::dateProtoFuncGetDay): + (JSC::dateProtoFuncGetUTCDay): + (JSC::dateProtoFuncGetHours): + (JSC::dateProtoFuncGetUTCHours): + (JSC::dateProtoFuncGetMinutes): + (JSC::dateProtoFuncGetUTCMinutes): + (JSC::dateProtoFuncGetSeconds): + (JSC::dateProtoFuncGetUTCSeconds): + (JSC::dateProtoFuncGetTimezoneOffset): + (JSC::setNewValueFromTimeArgs): + (JSC::setNewValueFromDateArgs): + (JSC::dateProtoFuncSetYear): + (JSC::dateProtoFuncGetYear): Renamed getGregorianDateTime to gregorianDateTime, + since it no longer has an out parameter. Uses 0 to indicate invalid dates. + +2009-10-30 Zoltan Horvath <zoltan@webkit.org> + + Reviewed by Darin Adler. + + Allow custom memory allocation control for JavaScriptCore's ListHashSet + https://bugs.webkit.org/show_bug.cgi?id=30853 + + Inherits ListHashSet class from FastAllocBase because it is + instantiated by 'new' in WebCore/rendering/RenderBlock.cpp:1813. + + * wtf/ListHashSet.h: + +2009-10-30 Oliver Hunt <oliver@apple.com> + + Reviewed by Gavin Barraclough. + + Regression: crash enumerating properties of an object with getters or setters + https://bugs.webkit.org/show_bug.cgi?id=30948 + + Add a guard to prevent us trying to cache property enumeration on + objects with getters or setters. + + * runtime/JSPropertyNameIterator.cpp: + (JSC::JSPropertyNameIterator::create): + +2009-10-30 Roland Steiner <rolandsteiner@chromium.org> + + Reviewed by Eric Seidel. + + Remove ENABLE_RUBY guards as discussed with Dave Hyatt and Maciej Stachowiak. + + Bug 28420 - Implement HTML5 <ruby> rendering + (https://bugs.webkit.org/show_bug.cgi?id=28420) + + No new tests (no functional change). + + * Configurations/FeatureDefines.xcconfig: + +2009-10-29 Oliver Hunt <oliver@apple.com> + + Reviewed by Maciej Stachowiak. + + REGRESSION (r50218-r50262): E*TRADE accounts page is missing content + https://bugs.webkit.org/show_bug.cgi?id=30947 + <rdar://problem/7348833> + + The logic for flagging that a structure has non-enumerable properties + was in addPropertyWithoutTransition, rather than in the core Structure::put + method. Despite this I was unable to produce a testcase that caused + the failure that etrade was experiencing, but the new assertion in + getEnumerablePropertyNames triggers on numerous layout tests without + the fix, so in effecti all for..in enumeration in any test ends up + doing the required consistency check. + + * runtime/Structure.cpp: + (JSC::Structure::addPropertyWithoutTransition): + (JSC::Structure::put): + (JSC::Structure::getEnumerablePropertyNames): + (JSC::Structure::checkConsistency): + +2009-10-29 Gabor Loki <loki@inf.u-szeged.hu> + + Reviewed by Gavin Barraclough. + + Add cacheFlush support for Thumb-2 on Linux + https://bugs.webkit.org/show_bug.cgi?id=30865 + + * jit/ExecutableAllocator.h: + (JSC::ExecutableAllocator::cacheFlush): + 2009-10-28 Gavin Barraclough <barraclough@apple.com> Reviewed by Oliver Hunt. diff --git a/JavaScriptCore/Configurations/FeatureDefines.xcconfig b/JavaScriptCore/Configurations/FeatureDefines.xcconfig index 42aa3cf..cd462d6 100644 --- a/JavaScriptCore/Configurations/FeatureDefines.xcconfig +++ b/JavaScriptCore/Configurations/FeatureDefines.xcconfig @@ -44,14 +44,13 @@ ENABLE_DATAGRID = ENABLE_DATAGRID; ENABLE_DATALIST = ENABLE_DATALIST; ENABLE_DOM_STORAGE = ENABLE_DOM_STORAGE; ENABLE_EVENTSOURCE = ENABLE_EVENTSOURCE; -ENABLE_FILTERS = ; +ENABLE_FILTERS = ENABLE_FILTERS; ENABLE_GEOLOCATION = ; ENABLE_ICONDATABASE = ENABLE_ICONDATABASE; ENABLE_JAVASCRIPT_DEBUGGER = ENABLE_JAVASCRIPT_DEBUGGER; ENABLE_MATHML = ; ENABLE_NOTIFICATIONS = ; ENABLE_OFFLINE_WEB_APPLICATIONS = ENABLE_OFFLINE_WEB_APPLICATIONS; -ENABLE_RUBY = ENABLE_RUBY; ENABLE_SHARED_WORKERS = ENABLE_SHARED_WORKERS; ENABLE_SVG = ENABLE_SVG; ENABLE_SVG_ANIMATION = ENABLE_SVG_ANIMATION; @@ -67,4 +66,4 @@ ENABLE_WORKERS = ENABLE_WORKERS; ENABLE_XPATH = ENABLE_XPATH; ENABLE_XSLT = ENABLE_XSLT; -FEATURE_DEFINES = $(ENABLE_3D_CANVAS) $(ENABLE_3D_RENDERING) $(ENABLE_CHANNEL_MESSAGING) $(ENABLE_DATABASE) $(ENABLE_DATAGRID) $(ENABLE_DATALIST) $(ENABLE_DOM_STORAGE) $(ENABLE_EVENTSOURCE) $(ENABLE_FILTERS) $(ENABLE_GEOLOCATION) $(ENABLE_ICONDATABASE) $(ENABLE_JAVASCRIPT_DEBUGGER) $(ENABLE_MATHML) $(ENABLE_NOTIFICATIONS) $(ENABLE_OFFLINE_WEB_APPLICATIONS) $(ENABLE_RUBY) $(ENABLE_SHARED_WORKERS) $(ENABLE_SVG) $(ENABLE_SVG_ANIMATION) $(ENABLE_SVG_AS_IMAGE) $(ENABLE_SVG_DOM_OBJC_BINDINGS) $(ENABLE_SVG_FONTS) $(ENABLE_SVG_FOREIGN_OBJECT) $(ENABLE_SVG_USE) $(ENABLE_VIDEO) $(ENABLE_WEB_SOCKETS) $(ENABLE_WML) $(ENABLE_WORKERS) $(ENABLE_XPATH) $(ENABLE_XSLT); +FEATURE_DEFINES = $(ENABLE_3D_CANVAS) $(ENABLE_3D_RENDERING) $(ENABLE_CHANNEL_MESSAGING) $(ENABLE_DATABASE) $(ENABLE_DATAGRID) $(ENABLE_DATALIST) $(ENABLE_DOM_STORAGE) $(ENABLE_EVENTSOURCE) $(ENABLE_FILTERS) $(ENABLE_GEOLOCATION) $(ENABLE_ICONDATABASE) $(ENABLE_JAVASCRIPT_DEBUGGER) $(ENABLE_MATHML) $(ENABLE_NOTIFICATIONS) $(ENABLE_OFFLINE_WEB_APPLICATIONS) $(ENABLE_SHARED_WORKERS) $(ENABLE_SVG) $(ENABLE_SVG_ANIMATION) $(ENABLE_SVG_AS_IMAGE) $(ENABLE_SVG_DOM_OBJC_BINDINGS) $(ENABLE_SVG_FONTS) $(ENABLE_SVG_FOREIGN_OBJECT) $(ENABLE_SVG_USE) $(ENABLE_VIDEO) $(ENABLE_WEB_SOCKETS) $(ENABLE_WML) $(ENABLE_WORKERS) $(ENABLE_XPATH) $(ENABLE_XSLT); diff --git a/JavaScriptCore/Configurations/Version.xcconfig b/JavaScriptCore/Configurations/Version.xcconfig index fabc009..b3bf41d 100644 --- a/JavaScriptCore/Configurations/Version.xcconfig +++ b/JavaScriptCore/Configurations/Version.xcconfig @@ -22,7 +22,7 @@ // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. MAJOR_VERSION = 532; -MINOR_VERSION = 4; +MINOR_VERSION = 6; TINY_VERSION = 0; FULL_VERSION = $(MAJOR_VERSION).$(MINOR_VERSION); diff --git a/JavaScriptCore/GNUmakefile.am b/JavaScriptCore/GNUmakefile.am index 5e50ba7..dba305a 100644 --- a/JavaScriptCore/GNUmakefile.am +++ b/JavaScriptCore/GNUmakefile.am @@ -236,8 +236,6 @@ javascriptcore_sources += \ JavaScriptCore/wtf/Deque.h \ JavaScriptCore/wtf/DisallowCType.h \ JavaScriptCore/wtf/Forward.h \ - JavaScriptCore/wtf/GOwnPtr.cpp \ - JavaScriptCore/wtf/GOwnPtr.h \ JavaScriptCore/wtf/GetPtr.h \ JavaScriptCore/wtf/HashCountedSet.h \ JavaScriptCore/wtf/HashFunctions.h \ @@ -288,6 +286,8 @@ javascriptcore_sources += \ JavaScriptCore/wtf/UnusedParam.h \ JavaScriptCore/wtf/Vector.h \ JavaScriptCore/wtf/VectorTraits.h \ + JavaScriptCore/wtf/gtk/GOwnPtr.cpp \ + JavaScriptCore/wtf/gtk/GOwnPtr.h \ JavaScriptCore/wtf/gtk/MainThreadGtk.cpp \ JavaScriptCore/wtf/gtk/ThreadingGtk.cpp \ JavaScriptCore/wtf/unicode/Collator.h \ @@ -342,6 +342,7 @@ javascriptcore_sources += \ JavaScriptCore/interpreter/RegisterFile.h \ JavaScriptCore/bytecompiler/BytecodeGenerator.cpp \ JavaScriptCore/bytecompiler/BytecodeGenerator.h \ + JavaScriptCore/bytecompiler/NodesCodegen.cpp \ JavaScriptCore/bytecompiler/LabelScope.h \ JavaScriptCore/debugger/Debugger.cpp \ JavaScriptCore/debugger/Debugger.h \ @@ -498,6 +499,7 @@ javascriptcore_sources += \ JavaScriptCore/runtime/Tracing.h \ JavaScriptCore/runtime/UString.cpp \ JavaScriptCore/runtime/UString.h \ + JavaScriptCore/runtime/WeakRandom.h \ JavaScriptCore/wtf/FastAllocBase.h \ JavaScriptCore/wtf/FastMalloc.cpp \ JavaScriptCore/wtf/FastMalloc.h \ diff --git a/JavaScriptCore/JavaScriptCore.exp b/JavaScriptCore/JavaScriptCore.exp index 12c96b3..1c91d36 100644 --- a/JavaScriptCore/JavaScriptCore.exp +++ b/JavaScriptCore/JavaScriptCore.exp @@ -111,6 +111,7 @@ __ZN3JSC12JSGlobalData12createLeakedEv __ZN3JSC12JSGlobalData12stopSamplingEv __ZN3JSC12JSGlobalData13startSamplingEv __ZN3JSC12JSGlobalData14dumpSampleDataEPNS_9ExecStateE +__ZN3JSC12JSGlobalData14resetDateCacheEv __ZN3JSC12JSGlobalData14sharedInstanceEv __ZN3JSC12JSGlobalData6createEb __ZN3JSC12JSGlobalDataD1Ev @@ -149,7 +150,7 @@ __ZN3JSC15JSWrapperObject12markChildrenERNS_9MarkStackE __ZN3JSC15createTypeErrorEPNS_9ExecStateEPKc __ZN3JSC15toInt32SlowCaseEdRb __ZN3JSC16InternalFunction4infoE -__ZN3JSC16InternalFunction4nameEPNS_12JSGlobalDataE +__ZN3JSC16InternalFunction4nameEPNS_9ExecStateE __ZN3JSC16InternalFunctionC2EPNS_12JSGlobalDataEN3WTF17NonNullPassRefPtrINS_9StructureEEERKNS_10IdentifierE __ZN3JSC16JSVariableObject14deletePropertyEPNS_9ExecStateERKNS_10IdentifierE __ZN3JSC16JSVariableObject14symbolTableGetERKNS_10IdentifierERNS_18PropertyDescriptorE @@ -178,6 +179,7 @@ __ZN3JSC23setUpStaticFunctionSlotEPNS_9ExecStateEPKNS_9HashEntryEPNS_8JSObjectER __ZN3JSC24createStackOverflowErrorEPNS_9ExecStateE __ZN3JSC25evaluateInGlobalCallFrameERKNS_7UStringERNS_7JSValueEPNS_14JSGlobalObjectE __ZN3JSC35createInterruptedExecutionExceptionEPNS_12JSGlobalDataE +__ZN3JSC3NaNE __ZN3JSC4Heap11objectCountEv __ZN3JSC4Heap14primaryHeapEndEv __ZN3JSC4Heap15recordExtraCostEm @@ -270,6 +272,7 @@ __ZN3JSC8JSObject23allocatePropertyStorageEmm __ZN3JSC8JSObject24getOwnPropertyDescriptorEPNS_9ExecStateERKNS_10IdentifierERNS_18PropertyDescriptorE __ZN3JSC8JSObject3putEPNS_9ExecStateERKNS_10IdentifierENS_7JSValueERNS_15PutPropertySlotE __ZN3JSC8JSObject3putEPNS_9ExecStateEjNS_7JSValueE +__ZN3JSC8JSString4RopeD1Ev __ZN3JSC8Profiler13stopProfilingEPNS_9ExecStateERKNS_7UStringE __ZN3JSC8Profiler14startProfilingEPNS_9ExecStateERKNS_7UStringE __ZN3JSC8Profiler8profilerEv @@ -326,9 +329,11 @@ __ZN3WTF21RefCountedLeakCounter9decrementEv __ZN3WTF21RefCountedLeakCounter9incrementEv __ZN3WTF21RefCountedLeakCounterC1EPKc __ZN3WTF21RefCountedLeakCounterD1Ev +__ZN3WTF23callOnMainThreadAndWaitEPFvPvES0_ __ZN3WTF23waitForThreadCompletionEjPPv __ZN3WTF27releaseFastMallocFreeMemoryEv __ZN3WTF28setMainThreadCallbacksPausedEb +__ZN3WTF32doubleToStringInJavaScriptFormatEdPcPj __ZN3WTF36lockAtomicallyInitializedStaticMutexEv __ZN3WTF37parseDateFromNullTerminatedCharactersEPKc __ZN3WTF38unlockAtomicallyInitializedStaticMutexEv @@ -350,7 +355,6 @@ __ZNK3JSC11Interpreter18retrieveLastCallerEPNS_9ExecStateERiRlRNS_7UStringERNS_7 __ZNK3JSC14JSGlobalObject14isDynamicScopeEv __ZNK3JSC16InternalFunction9classInfoEv __ZNK3JSC16JSVariableObject16isVariableObjectEv -__ZNK3JSC16JSVariableObject21getPropertyAttributesEPNS_9ExecStateERKNS_10IdentifierERj __ZNK3JSC17DebuggerCallFrame10thisObjectEv __ZNK3JSC17DebuggerCallFrame12functionNameEv __ZNK3JSC17DebuggerCallFrame22calculatedFunctionNameEv @@ -372,9 +376,9 @@ __ZNK3JSC6JSCell8toNumberEPNS_9ExecStateE __ZNK3JSC6JSCell8toObjectEPNS_9ExecStateE __ZNK3JSC6JSCell8toStringEPNS_9ExecStateE __ZNK3JSC6JSCell9classInfoEv -__ZNK3JSC6JSCell9getStringERNS_7UStringE -__ZNK3JSC6JSCell9getStringEv __ZNK3JSC6JSCell9getUInt32ERj +__ZNK3JSC6JSCell9getStringEPNS_9ExecStateE +__ZNK3JSC6JSCell9getStringEPNS_9ExecStateERNS_7UStringE __ZNK3JSC6JSCell9toBooleanEPNS_9ExecStateE __ZNK3JSC7ArgList8getSliceEiRS0_ __ZNK3JSC7JSValue16toObjectSlowCaseEPNS_9ExecStateE @@ -392,12 +396,12 @@ __ZNK3JSC8JSObject11hasPropertyEPNS_9ExecStateERKNS_10IdentifierE __ZNK3JSC8JSObject11hasPropertyEPNS_9ExecStateEj __ZNK3JSC8JSObject12defaultValueEPNS_9ExecStateENS_22PreferredPrimitiveTypeE __ZNK3JSC8JSObject12toThisObjectEPNS_9ExecStateE -__ZNK3JSC8JSObject21getPropertyAttributesEPNS_9ExecStateERKNS_10IdentifierERj __ZNK3JSC8JSObject8toNumberEPNS_9ExecStateE __ZNK3JSC8JSObject8toObjectEPNS_9ExecStateE __ZNK3JSC8JSObject8toStringEPNS_9ExecStateE __ZNK3JSC8JSObject9classNameEv __ZNK3JSC8JSObject9toBooleanEPNS_9ExecStateE +__ZNK3JSC8JSString11resolveRopeEPNS_9ExecStateE __ZNK3JSC9HashTable11createTableEPNS_12JSGlobalDataE __ZNK3JSC9HashTable11deleteTableEv __ZNK3WTF8Collator7collateEPKtmS2_m diff --git a/JavaScriptCore/JavaScriptCore.gyp/JavaScriptCore.gyp b/JavaScriptCore/JavaScriptCore.gyp/JavaScriptCore.gyp index cfad3cf..64e20a2 100644 --- a/JavaScriptCore/JavaScriptCore.gyp/JavaScriptCore.gyp +++ b/JavaScriptCore/JavaScriptCore.gyp/JavaScriptCore.gyp @@ -75,15 +75,10 @@ ], 'include_dirs': [ '../os-win32', - '<(chromium_src_dir)/webkit/build/JavaScriptCore', ], }], ['OS=="mac"', { 'defines': [ - # Ensure that only Leopard features are used when doing the - # Mac build. - 'BUILDING_ON_LEOPARD', - # Use USE_NEW_THEME on Mac. 'WTF_USE_NEW_THEME=1', ], @@ -119,9 +114,10 @@ # ... Then include what we want. ['include', '../wtf/'], # GLib/GTK, even though its name doesn't really indicate. - ['exclude', '/(GOwnPtr|glib/.*)\\.(cpp|h)$'], + ['exclude', '/(gtk|glib)/.*\\.(cpp|h)$'], ['exclude', '(Default|Gtk|Mac|None|Qt|Win|Wx)\\.(cpp|mm)$'], ['exclude', 'wtf/CurrentTime\\.cpp$'], + ['exclude', 'wtf/TC.*\\.(cpp|h)$'], ], 'direct_dependent_settings': { 'include_dirs': [ @@ -141,18 +137,6 @@ ['exclude', 'ThreadingPthreads\\.cpp$'], ['include', 'Thread(ing|Specific)Win\\.cpp$'] ], - 'include_dirs': [ - '<(chromium_src_dir)/webkit/build', - '../kjs', - '../API', - # These 3 do not seem to exist. - '../bindings', - '../bindings/c', - '../bindings/jni', - # FIXME: removed these - don't seem to exist - 'pending', - 'pending/wtf', - ], 'include_dirs!': [ '<(SHARED_INTERMEDIATE_DIR)/webkit', ], diff --git a/JavaScriptCore/JavaScriptCore.gypi b/JavaScriptCore/JavaScriptCore.gypi index 03c23c3..b4495b2 100644 --- a/JavaScriptCore/JavaScriptCore.gypi +++ b/JavaScriptCore/JavaScriptCore.gypi @@ -64,6 +64,7 @@ 'bytecode/StructureStubInfo.h', 'bytecompiler/BytecodeGenerator.cpp', 'bytecompiler/BytecodeGenerator.h', + 'bytecompiler/NodesCodegen.cpp', 'bytecompiler/Label.h', 'bytecompiler/LabelScope.h', 'bytecompiler/RegisterID.h', @@ -332,6 +333,7 @@ 'runtime/Tracing.h', 'runtime/UString.cpp', 'runtime/UString.h', + 'runtime/WeakRandom.h', 'wrec/CharacterClass.cpp', 'wrec/CharacterClass.h', 'wrec/CharacterClassConstructor.cpp', @@ -369,8 +371,8 @@ 'wtf/FastMalloc.h', 'wtf/Forward.h', 'wtf/GetPtr.h', - 'wtf/GOwnPtr.cpp', - 'wtf/GOwnPtr.h', + 'wtf/gtk/GOwnPtr.cpp', + 'wtf/gtk/GOwnPtr.h', 'wtf/gtk/MainThreadGtk.cpp', 'wtf/gtk/ThreadingGtk.cpp', 'wtf/HashCountedSet.h', diff --git a/JavaScriptCore/JavaScriptCore.order b/JavaScriptCore/JavaScriptCore.order index 3ae3ec6..d6f6caa 100644 --- a/JavaScriptCore/JavaScriptCore.order +++ b/JavaScriptCore/JavaScriptCore.order @@ -1625,7 +1625,6 @@ __ZN3JSC18EmptyStatementNode12emitBytecodeERNS_17BytecodeGeneratorEPNS_10Registe __ZN3JSCL27compareByStringPairForQSortEPKvS1_ __Z22jsc_pcre_ucp_othercasej __ZN3JSCL35objectProtoFuncPropertyIsEnumerableEPNS_9ExecStateEPNS_8JSObjectENS_7JSValueERKNS_7ArgListE -__ZNK3JSC8JSObject21getPropertyAttributesEPNS_9ExecStateERKNS_10IdentifierERj __ZN3WTF7HashMapIjN3JSC7JSValueENS_7IntHashIjEENS_10HashTraitsIjEENS5_IS2_EEE3setERKjRKS2_ __ZN3WTF9HashTableIjSt4pairIjN3JSC7JSValueEENS_18PairFirstExtractorIS4_EENS_7IntHashIjEENS_14PairHashTraitsINS_10HashTraitsIjEE __ZN3JSC12RegisterFile21releaseExcessCapacityEv @@ -1841,7 +1840,6 @@ __ZN3JSC6JSCell14toThisJSStringEPNS_9ExecStateE __ZNK3JSC6JSCell12toThisStringEPNS_9ExecStateE __ZN3JSCL27objectProtoFuncLookupSetterEPNS_9ExecStateEPNS_8JSObjectENS_7JSValueERKNS_7ArgListE __ZN3JSC8JSObject12lookupSetterEPNS_9ExecStateERKNS_10IdentifierE -__ZNK3JSC16JSVariableObject21getPropertyAttributesEPNS_9ExecStateERKNS_10IdentifierERj __ZN3JSC9ExecState22regExpConstructorTableEPS0_ __ZN3JSCL24regExpConstructorDollar7EPNS_9ExecStateERKNS_10IdentifierERKNS_12PropertySlotE __ZN3JSCL24regExpConstructorDollar8EPNS_9ExecStateERKNS_10IdentifierERKNS_12PropertySlotE diff --git a/JavaScriptCore/JavaScriptCore.pri b/JavaScriptCore/JavaScriptCore.pri index eb26664..aecf01c 100644 --- a/JavaScriptCore/JavaScriptCore.pri +++ b/JavaScriptCore/JavaScriptCore.pri @@ -9,6 +9,11 @@ CONFIG(debug, debug|release) { OBJECTS_DIR = obj/release } +symbian: { + # Need to guarantee this comes before system includes of /epoc32/include + MMP_RULES += "USERINCLUDE ../JavaScriptCore/profiler" +} + INCLUDEPATH = \ $$PWD \ $$PWD/.. \ @@ -54,28 +59,29 @@ win32-* { # Rules when JIT enabled (not disabled) !contains(DEFINES, ENABLE_JIT=0) { - linux-g++*:greaterThan(QT_GCC_MAJOR_VERSION,3):greaterThan(QT_GCC_MINOR_VERSION,0) { + linux*-g++*:greaterThan(QT_GCC_MAJOR_VERSION,3):greaterThan(QT_GCC_MINOR_VERSION,0) { QMAKE_CXXFLAGS += -fno-stack-protector QMAKE_CFLAGS += -fno-stack-protector } } wince* { - SOURCES += $$QT_SOURCE_TREE/src/3rdparty/ce-compat/ce_time.cpp + INCLUDEPATH += $$QT_SOURCE_TREE/src/3rdparty/ce-compat + SOURCES += $$QT_SOURCE_TREE/src/3rdparty/ce-compat/ce_time.c DEFINES += WINCEBASIC } include(pcre/pcre.pri) LUT_FILES += \ + runtime/ArrayPrototype.cpp \ runtime/DatePrototype.cpp \ runtime/JSONObject.cpp \ - runtime/NumberConstructor.cpp \ - runtime/StringPrototype.cpp \ - runtime/ArrayPrototype.cpp \ runtime/MathObject.cpp \ + runtime/NumberConstructor.cpp \ runtime/RegExpConstructor.cpp \ - runtime/RegExpObject.cpp + runtime/RegExpObject.cpp \ + runtime/StringPrototype.cpp KEYWORDLUT_FILES += \ parser/Keywords.table @@ -84,16 +90,6 @@ JSCBISON += \ parser/Grammar.y SOURCES += \ - wtf/Assertions.cpp \ - wtf/ByteArray.cpp \ - wtf/HashTable.cpp \ - wtf/MainThread.cpp \ - wtf/RandomNumber.cpp \ - wtf/RefCountedLeakCounter.cpp \ - wtf/TypeTraits.cpp \ - wtf/unicode/CollatorDefault.cpp \ - wtf/unicode/icu/CollatorICU.cpp \ - wtf/unicode/UTF8.cpp \ API/JSBase.cpp \ API/JSCallbackConstructor.cpp \ API/JSCallbackFunction.cpp \ @@ -104,60 +100,38 @@ SOURCES += \ API/JSStringRef.cpp \ API/JSValueRef.cpp \ API/OpaqueJSString.cpp \ - runtime/InitializeThreading.cpp \ - runtime/JSGlobalData.cpp \ - runtime/JSGlobalObject.cpp \ - runtime/JSStaticScopeObject.cpp \ - runtime/JSVariableObject.cpp \ - runtime/JSActivation.cpp \ - runtime/JSNotAnObject.cpp \ - runtime/JSONObject.cpp \ - runtime/LiteralParser.cpp \ - runtime/MarkStack.cpp \ - runtime/TimeoutChecker.cpp \ - bytecode/CodeBlock.cpp \ - bytecode/StructureStubInfo.cpp \ - bytecode/JumpTable.cpp \ assembler/ARMAssembler.cpp \ assembler/MacroAssemblerARM.cpp \ - jit/JIT.cpp \ - jit/JITCall.cpp \ + bytecode/CodeBlock.cpp \ + bytecode/JumpTable.cpp \ + bytecode/Opcode.cpp \ + bytecode/SamplingTool.cpp \ + bytecode/StructureStubInfo.cpp \ + bytecompiler/BytecodeGenerator.cpp \ + bytecompiler/NodesCodegen.cpp \ + debugger/DebuggerActivation.cpp \ + debugger/DebuggerCallFrame.cpp \ + debugger/Debugger.cpp \ + interpreter/CallFrame.cpp \ + interpreter/Interpreter.cpp \ + interpreter/RegisterFile.cpp \ + jit/ExecutableAllocator.cpp \ jit/JITArithmetic.cpp \ + jit/JITCall.cpp \ + jit/JIT.cpp \ jit/JITOpcodes.cpp \ jit/JITPropertyAccess.cpp \ - jit/ExecutableAllocator.cpp \ jit/JITStubs.cpp \ - bytecompiler/BytecodeGenerator.cpp \ - runtime/ExceptionHelpers.cpp \ - runtime/JSPropertyNameIterator.cpp \ - interpreter/Interpreter.cpp \ - bytecode/Opcode.cpp \ - bytecode/SamplingTool.cpp \ - yarr/RegexCompiler.cpp \ - yarr/RegexInterpreter.cpp \ - yarr/RegexJIT.cpp \ - interpreter/RegisterFile.cpp - -symbian { - SOURCES += jit/ExecutableAllocatorSymbian.cpp \ - runtime/MarkStackSymbian.cpp -} else { - win32-*|wince* { - SOURCES += jit/ExecutableAllocatorWin.cpp \ - runtime/MarkStackWin.cpp - } else { - SOURCES += jit/ExecutableAllocatorPosix.cpp \ - runtime/MarkStackPosix.cpp - } -} - -!contains(DEFINES, USE_SYSTEM_MALLOC) { - SOURCES += wtf/TCSystemAlloc.cpp -} - -# AllInOneFile.cpp helps gcc analize and optimize code -# Other compilers may be able to do this at link time -SOURCES += \ + parser/Lexer.cpp \ + parser/Nodes.cpp \ + parser/ParserArena.cpp \ + parser/Parser.cpp \ + profiler/HeavyProfile.cpp \ + profiler/Profile.cpp \ + profiler/ProfileGenerator.cpp \ + profiler/ProfileNode.cpp \ + profiler/Profiler.cpp \ + profiler/TreeProfile.cpp \ runtime/ArgList.cpp \ runtime/Arguments.cpp \ runtime/ArrayConstructor.cpp \ @@ -168,62 +142,64 @@ SOURCES += \ runtime/CallData.cpp \ runtime/Collector.cpp \ runtime/CommonIdentifiers.cpp \ + runtime/Completion.cpp \ runtime/ConstructData.cpp \ - wtf/CurrentTime.cpp \ runtime/DateConstructor.cpp \ runtime/DateConversion.cpp \ runtime/DateInstance.cpp \ runtime/DatePrototype.cpp \ - debugger/Debugger.cpp \ - debugger/DebuggerCallFrame.cpp \ - debugger/DebuggerActivation.cpp \ - wtf/dtoa.cpp \ - runtime/Error.cpp \ runtime/ErrorConstructor.cpp \ + runtime/Error.cpp \ runtime/ErrorInstance.cpp \ runtime/ErrorPrototype.cpp \ - interpreter/CallFrame.cpp \ + runtime/ExceptionHelpers.cpp \ runtime/Executable.cpp \ runtime/FunctionConstructor.cpp \ runtime/FunctionPrototype.cpp \ runtime/GetterSetter.cpp \ runtime/GlobalEvalFunction.cpp \ runtime/Identifier.cpp \ + runtime/InitializeThreading.cpp \ runtime/InternalFunction.cpp \ - runtime/Completion.cpp \ - runtime/JSArray.cpp \ + runtime/JSActivation.cpp \ runtime/JSAPIValueWrapper.cpp \ + runtime/JSArray.cpp \ runtime/JSByteArray.cpp \ runtime/JSCell.cpp \ runtime/JSFunction.cpp \ + runtime/JSGlobalData.cpp \ + runtime/JSGlobalObject.cpp \ runtime/JSGlobalObjectFunctions.cpp \ runtime/JSImmediate.cpp \ runtime/JSLock.cpp \ + runtime/JSNotAnObject.cpp \ runtime/JSNumberCell.cpp \ runtime/JSObject.cpp \ + runtime/JSONObject.cpp \ + runtime/JSPropertyNameIterator.cpp \ + runtime/JSStaticScopeObject.cpp \ runtime/JSString.cpp \ runtime/JSValue.cpp \ + runtime/JSVariableObject.cpp \ runtime/JSWrapperObject.cpp \ - parser/Lexer.cpp \ + runtime/LiteralParser.cpp \ runtime/Lookup.cpp \ + runtime/MarkStack.cpp \ runtime/MathObject.cpp \ runtime/NativeErrorConstructor.cpp \ runtime/NativeErrorPrototype.cpp \ - parser/Nodes.cpp \ runtime/NumberConstructor.cpp \ runtime/NumberObject.cpp \ runtime/NumberPrototype.cpp \ runtime/ObjectConstructor.cpp \ runtime/ObjectPrototype.cpp \ runtime/Operations.cpp \ - parser/Parser.cpp \ - parser/ParserArena.cpp \ runtime/PropertyDescriptor.cpp \ runtime/PropertyNameArray.cpp \ runtime/PropertySlot.cpp \ runtime/PrototypeFunction.cpp \ - runtime/RegExp.cpp \ runtime/RegExpConstructor.cpp \ + runtime/RegExp.cpp \ runtime/RegExpObject.cpp \ runtime/RegExpPrototype.cpp \ runtime/ScopeChain.cpp \ @@ -231,19 +207,46 @@ SOURCES += \ runtime/StringConstructor.cpp \ runtime/StringObject.cpp \ runtime/StringPrototype.cpp \ - runtime/Structure.cpp \ runtime/StructureChain.cpp \ + runtime/Structure.cpp \ + runtime/TimeoutChecker.cpp \ runtime/UString.cpp \ - profiler/HeavyProfile.cpp \ - profiler/Profile.cpp \ - profiler/ProfileGenerator.cpp \ - profiler/ProfileNode.cpp \ - profiler/Profiler.cpp \ - profiler/TreeProfile.cpp \ + wtf/Assertions.cpp \ + wtf/ByteArray.cpp \ + wtf/CurrentTime.cpp \ wtf/DateMath.cpp \ + wtf/dtoa.cpp \ wtf/FastMalloc.cpp \ + wtf/HashTable.cpp \ + wtf/MainThread.cpp \ + wtf/qt/MainThreadQt.cpp \ + wtf/RandomNumber.cpp \ + wtf/RefCountedLeakCounter.cpp \ wtf/Threading.cpp \ - wtf/qt/MainThreadQt.cpp + wtf/TypeTraits.cpp \ + wtf/unicode/CollatorDefault.cpp \ + wtf/unicode/icu/CollatorICU.cpp \ + wtf/unicode/UTF8.cpp \ + yarr/RegexCompiler.cpp \ + yarr/RegexInterpreter.cpp \ + yarr/RegexJIT.cpp + +symbian { + SOURCES += jit/ExecutableAllocatorSymbian.cpp \ + runtime/MarkStackSymbian.cpp +} else { + win32-*|wince* { + SOURCES += jit/ExecutableAllocatorWin.cpp \ + runtime/MarkStackWin.cpp + } else { + SOURCES += jit/ExecutableAllocatorPosix.cpp \ + runtime/MarkStackPosix.cpp + } +} + +!contains(DEFINES, USE_SYSTEM_MALLOC) { + SOURCES += wtf/TCSystemAlloc.cpp +} !contains(DEFINES, ENABLE_SINGLE_THREADED=1) { SOURCES += wtf/qt/ThreadingQt.cpp diff --git a/JavaScriptCore/JavaScriptCore.pro b/JavaScriptCore/JavaScriptCore.pro index a1affd4..0d6becb 100644 --- a/JavaScriptCore/JavaScriptCore.pro +++ b/JavaScriptCore/JavaScriptCore.pro @@ -52,17 +52,11 @@ win32-g++ { QMAKE_LIBDIR_POST += $$split(TMPPATH,";") } -DEFINES += WTF_USE_JAVASCRIPTCORE_BINDINGS=1 - DEFINES += WTF_CHANGES=1 include(JavaScriptCore.pri) QMAKE_EXTRA_TARGETS += generated_files -lessThan(QT_MINOR_VERSION, 4) { - DEFINES += QT_BEGIN_NAMESPACE="" QT_END_NAMESPACE="" -} - *-g++*:QMAKE_CXXFLAGS_RELEASE -= -O2 *-g++*:QMAKE_CXXFLAGS_RELEASE += -O3 diff --git a/JavaScriptCore/JavaScriptCore.vcproj/JavaScriptCore/JavaScriptCore.def b/JavaScriptCore/JavaScriptCore.vcproj/JavaScriptCore/JavaScriptCore.def index 01f84f1..aae8118 100644 --- a/JavaScriptCore/JavaScriptCore.vcproj/JavaScriptCore/JavaScriptCore.def +++ b/JavaScriptCore/JavaScriptCore.vcproj/JavaScriptCore/JavaScriptCore.def @@ -1,6 +1,5 @@ -LIBRARY "JavaScriptCore" - EXPORTS + ??0Collator@WTF@@QAE@PBD@Z ??0DateInstance@JSC@@QAE@PAVExecState@1@N@Z ??0DropAllLocks@JSLock@JSC@@QAE@W4JSLockBehavior@2@@Z @@ -27,12 +26,14 @@ EXPORTS ??1JSGlobalObject@JSC@@UAE@XZ ??1Mutex@WTF@@QAE@XZ ??1RefCountedLeakCounter@WTF@@QAE@XZ + ??1Rope@JSString@JSC@@QAE@XZ ??1Structure@JSC@@QAE@XZ ??1ThreadCondition@WTF@@QAE@XZ ??2JSCell@JSC@@SAPAXIPAVExecState@1@@Z ??2JSGlobalObject@JSC@@SAPAXIPAVJSGlobalData@1@@Z ??4UString@JSC@@QAEAAV01@PBD@Z ??8JSC@@YA_NABVUString@0@0@Z + ?NaN@JSC@@3NB ?UTF8String@UString@JSC@@QBE?AVCString@2@_N@Z ?add@Identifier@JSC@@SA?AV?$PassRefPtr@URep@UString@JSC@@@WTF@@PAVExecState@2@PBD@Z ?add@PropertyNameArray@JSC@@QAEXPAURep@UString@2@@Z @@ -52,6 +53,7 @@ EXPORTS ?calculatedFunctionName@DebuggerCallFrame@JSC@@QBE?AVUString@2@XZ ?call@JSC@@YA?AVJSValue@1@PAVExecState@1@V21@W4CallType@1@ABTCallData@1@1ABVArgList@1@@Z ?callOnMainThread@WTF@@YAXP6AXPAX@Z0@Z + ?callOnMainThreadAndWait@WTF@@YAXP6AXPAX@Z0@Z ?changePrototypeTransition@Structure@JSC@@SA?AV?$PassRefPtr@VStructure@JSC@@@WTF@@PAV12@VJSValue@2@@Z ?checkSameIdentifierTable@Identifier@JSC@@CAXPAVExecState@2@PAURep@UString@2@@Z ?checkSameIdentifierTable@Identifier@JSC@@CAXPAVJSGlobalData@2@PAURep@UString@2@@Z @@ -114,6 +116,7 @@ EXPORTS ?detachThread@WTF@@YAXI@Z ?didTimeOut@TimeoutChecker@JSC@@QAE_NPAVExecState@2@@Z ?dumpSampleData@JSGlobalData@JSC@@QAEXPAVExecState@2@@Z + ?doubleToStringInJavaScriptFormat@WTF@@YAXNQADPAI@Z ?enumerable@PropertyDescriptor@JSC@@QBE_NXZ ?equal@Identifier@JSC@@SA_NPBURep@UString@2@PBD@Z ?equal@JSC@@YA_NPBURep@UString@1@0@Z @@ -152,13 +155,11 @@ EXPORTS ?getPrimitiveNumber@JSCell@JSC@@UAE_NPAVExecState@2@AANAAVJSValue@2@@Z ?getPrimitiveNumber@JSObject@JSC@@UAE_NPAVExecState@2@AANAAVJSValue@2@@Z ?getPrimitiveNumber@JSString@JSC@@EAE_NPAVExecState@2@AANAAVJSValue@2@@Z - ?getPropertyAttributes@JSObject@JSC@@UBE_NPAVExecState@2@ABVIdentifier@2@AAI@Z - ?getPropertyAttributes@JSVariableObject@JSC@@UBE_NPAVExecState@2@ABVIdentifier@2@AAI@Z ?getPropertyDescriptor@JSObject@JSC@@QAE_NPAVExecState@2@ABVIdentifier@2@AAVPropertyDescriptor@2@@Z ?getPropertyNames@JSObject@JSC@@UAEXPAVExecState@2@AAVPropertyNameArray@2@@Z ?getSlice@ArgList@JSC@@QBEXHAAV12@@Z - ?getString@JSCell@JSC@@QBE?AVUString@2@XZ - ?getString@JSCell@JSC@@QBE_NAAVUString@2@@Z + ?getString@JSCell@JSC@@QBE?AVUString@2@PAVExecState@2@@Z + ?getString@JSCell@JSC@@QBE_NPAVExecState@2@AAVUString@2@@Z ?getUInt32@JSCell@JSC@@UBE_NAAI@Z ?getter@PropertyDescriptor@JSC@@QBE?AVJSValue@2@XZ ?globalExec@JSGlobalObject@JSC@@UAEPAVExecState@2@XZ @@ -197,7 +198,7 @@ EXPORTS ?markChildren@JSObject@JSC@@UAEXAAVMarkStack@2@@Z ?markChildren@JSWrapperObject@JSC@@EAEXAAVMarkStack@2@@Z ?materializePropertyMap@Structure@JSC@@AAEXXZ - ?name@InternalFunction@JSC@@QAEABVUString@2@PAVJSGlobalData@2@@Z + ?name@InternalFunction@JSC@@QAEABVUString@2@PAVExecState@2@@Z ?nonInlineNaN@JSC@@YANXZ ?objectCount@Heap@JSC@@QAEIXZ ?objectProtoFuncToString@JSC@@YI?AVJSValue@1@PAVExecState@1@PAVJSObject@1@V21@ABVArgList@1@@Z @@ -227,6 +228,8 @@ EXPORTS ?releaseStack@MarkStack@JSC@@CAXPAXI@Z ?reset@ParserArena@JSC@@QAEXXZ ?reset@TimeoutChecker@JSC@@QAEXXZ + ?resetDateCache@JSGlobalData@JSC@@QAEXXZ + ?resolveRope@JSString@JSC@@ABEXPAVExecState@2@@Z ?restoreAll@Profile@JSC@@QAEXXZ ?retrieveCaller@Interpreter@JSC@@QBE?AVJSValue@2@PAVExecState@2@PAVInternalFunction@2@@Z ?retrieveLastCaller@Interpreter@JSC@@QBEXPAVExecState@2@AAH1AAVUString@2@AAVJSValue@2@@Z diff --git a/JavaScriptCore/JavaScriptCore.vcproj/JavaScriptCore/JavaScriptCore.vcproj b/JavaScriptCore/JavaScriptCore.vcproj/JavaScriptCore/JavaScriptCore.vcproj index bf25a85..5b7f9ad 100644 --- a/JavaScriptCore/JavaScriptCore.vcproj/JavaScriptCore/JavaScriptCore.vcproj +++ b/JavaScriptCore/JavaScriptCore.vcproj/JavaScriptCore/JavaScriptCore.vcproj @@ -668,6 +668,14 @@ RelativePath="..\..\runtime\DateInstanceCache.h"
>
</File>
+ <File
+ RelativePath="..\..\wtf\DateMath.cpp"
+ >
+ </File>
+ <File
+ RelativePath="..\..\wtf\DateMath.h"
+ >
+ </File>
<File
RelativePath="..\..\runtime\DatePrototype.cpp"
>
@@ -1204,6 +1212,10 @@ RelativePath="..\..\runtime\UString.h"
>
</File>
+ <File
+ RelativePath="..\..\runtime\WeakRandom.h"
+ >
+ </File>
<Filter
Name="DerivedSources"
>
@@ -1537,6 +1549,10 @@ >
</File>
<File
+ RelativePath="..\..\bytecompiler\NodesCodegen.cpp"
+ >
+ </File>
+ <File
RelativePath="..\..\bytecompiler\Label.h"
>
</File>
diff --git a/JavaScriptCore/JavaScriptCore.vcproj/JavaScriptCore/JavaScriptCoreGenerated.make b/JavaScriptCore/JavaScriptCore.vcproj/JavaScriptCore/JavaScriptCoreGenerated.make index 9fd7ad4..92479bb 100644 --- a/JavaScriptCore/JavaScriptCore.vcproj/JavaScriptCore/JavaScriptCoreGenerated.make +++ b/JavaScriptCore/JavaScriptCore.vcproj/JavaScriptCore/JavaScriptCoreGenerated.make @@ -7,6 +7,7 @@ all: xcopy /y /d "..\..\API\JavaScript.h" "$(WEBKITOUTPUTDIR)\include\JavaScriptCore" xcopy /y /d "..\..\API\JSBase.h" "$(WEBKITOUTPUTDIR)\include\JavaScriptCore" xcopy /y /d "..\..\API\JSContextRef.h" "$(WEBKITOUTPUTDIR)\include\JavaScriptCore" + xcopy /y /d "..\..\API\JSContextRefPrivate.h" "$(WEBKITOUTPUTDIR)\include\JavaScriptCore" xcopy /y /d "..\..\API\JSObjectRef.h" "$(WEBKITOUTPUTDIR)\include\JavaScriptCore" xcopy /y /d "..\..\API\JSStringRef.h" "$(WEBKITOUTPUTDIR)\include\JavaScriptCore" xcopy /y /d "..\..\API\JSStringRefCF.h" "$(WEBKITOUTPUTDIR)\include\JavaScriptCore" diff --git a/JavaScriptCore/JavaScriptCore.vcproj/WTF/WTF.vcproj b/JavaScriptCore/JavaScriptCore.vcproj/WTF/WTF.vcproj index 67c004c..78f507a 100644 --- a/JavaScriptCore/JavaScriptCore.vcproj/WTF/WTF.vcproj +++ b/JavaScriptCore/JavaScriptCore.vcproj/WTF/WTF.vcproj @@ -285,14 +285,6 @@ >
</File>
<File
- RelativePath="..\..\wtf\DateMath.cpp"
- >
- </File>
- <File
- RelativePath="..\..\wtf\DateMath.h"
- >
- </File>
- <File
RelativePath="..\..\wtf\Deque.h"
>
</File>
diff --git a/JavaScriptCore/JavaScriptCore.vcproj/jsc/jscCommon.vsprops b/JavaScriptCore/JavaScriptCore.vcproj/jsc/jscCommon.vsprops index 7e8a193..1a8f8b6 100644 --- a/JavaScriptCore/JavaScriptCore.vcproj/jsc/jscCommon.vsprops +++ b/JavaScriptCore/JavaScriptCore.vcproj/jsc/jscCommon.vsprops @@ -1,25 +1,25 @@ -<?xml version="1.0" encoding="Windows-1252"?> -<VisualStudioPropertySheet - ProjectType="Visual C++" - Version="8.00" - Name="jscCommon" - > - <Tool - Name="VCCLCompilerTool" - AdditionalIncludeDirectories=""$(WebKitOutputDir)\include";"$(WebKitOutputDir)\obj\JavaScriptCore\$(ConfigurationName)\DerivedSources\";../../;"../../os-win32/";../../pcre/;../../assembler/;../../wrec/;../../parser/;../../runtime/;../../VM/;../../bytecode/;../../interpreter/;../../wtf/;../../debugger/;../../bytecompiler/;../../profiler;"$(WebKitLibrariesDir)\include\icu";"$(WebKitLibrariesDir)\include\pthreads";../../../icu/include;"$(WebKitLibrariesDir)\include";../../jit/" - PreprocessorDefinitions="__STD_C" - /> - <Tool - Name="VCLinkerTool" - AdditionalDependencies="JavaScriptCore$(WebKitDLLConfigSuffix).lib WTF$(WebKitConfigSuffix).lib icuin$(LibraryConfigSuffix).lib icuuc$(LibraryConfigSuffix).lib winmm.lib pthreadVC2$(LibraryConfigSuffix).lib user32.lib" - SubSystem="1" - /> - <Tool - Name="VCPostBuildEventTool" - CommandLine="if exist "$(WebKitOutputDir)\buildfailed" del "$(WebKitOutputDir)\buildfailed"

mkdir 2>NUL "$(WebKitOutputDir)\bin"
if exist "$(WebKitLibrariesDir)\bin\icudt40.dll" xcopy /y /d "$(WebKitLibrariesDir)\bin\icudt40.dll" "$(WebKitOutputDir)\bin"
if exist "$(WebKitLibrariesDir)\bin\icudt40$(LibraryConfigSuffix).dll" xcopy /y /d "$(WebKitLibrariesDir)\bin\icudt40$(LibraryConfigSuffix).dll" "$(WebKitOutputDir)\bin"
if exist "$(WebKitLibrariesDir)\bin\icuin40$(LibraryConfigSuffix).dll" xcopy /y /d "$(WebKitLibrariesDir)\bin\icuin40$(LibraryConfigSuffix).dll" "$(WebKitOutputDir)\bin"
if exist "$(WebKitLibrariesDir)\bin\icuuc40$(LibraryConfigSuffix).dll" xcopy /y /d "$(WebKitLibrariesDir)\bin\icuuc40$(LibraryConfigSuffix).dll" "$(WebKitOutputDir)\bin"
if exist "$(WebKitLibrariesDir)\bin\CoreFoundation$(LibraryConfigSuffix).dll" xcopy /y /d "$(WebKitLibrariesDir)\bin\CoreFoundation$(LibraryConfigSuffix).dll" "$(WebKitOutputDir)\bin"
if exist "$(WebKitLibrariesDir)\bin\CoreFoundation.resources" xcopy /y /d /e /i "$(WebKitLibrariesDir)\bin\CoreFoundation.resources" "$(WebKitOutputDir)\bin\CoreFoundation.resources"
if exist "$(WebKitLibrariesDir)\bin\pthreadVC2$(LibraryConfigSuffix).dll" xcopy /y /d "$(WebKitLibrariesDir)\bin\pthreadVC2$(LibraryConfigSuffix).dll" "$(WebKitOutputDir)\bin"
if exist "$(WebKitLibrariesDir)\bin\objc$(LibraryConfigSuffix).dll" xcopy /y /d "$(WebKitLibrariesDir)\bin\objc$(LibraryConfigSuffix).dll" "$(WebKitOutputDir)\bin"
if exist "$(WebKitLibrariesDir)\bin\ASL$(LibraryConfigSuffix).dll" xcopy /y /d "$(WebKitLibrariesDir)\bin\ASL$(LibraryConfigSuffix).dll" "$(WebKitOutputDir)\bin"

cmd /c
" - /> - <Tool - Name="VCPreBuildEventTool" - CommandLine="%SystemDrive%\cygwin\bin\which.exe bash
if errorlevel 1 set PATH=%SystemDrive%\cygwin\bin;%PATH%
cmd /c
if exist "$(WebKitOutputDir)\buildfailed" grep XX$(ProjectName)XX "$(WebKitOutputDir)\buildfailed"
if errorlevel 1 exit 1
echo XX$(ProjectName)XX > "$(WebKitOutputDir)\buildfailed"
" - /> -</VisualStudioPropertySheet> +<?xml version="1.0" encoding="Windows-1252"?>
+<VisualStudioPropertySheet
+ ProjectType="Visual C++"
+ Version="8.00"
+ Name="jscCommon"
+ >
+ <Tool
+ Name="VCCLCompilerTool"
+ AdditionalIncludeDirectories=""$(WebKitOutputDir)\include";"$(WebKitOutputDir)\obj\JavaScriptCore\$(ConfigurationName)\DerivedSources\";../../;"../../os-win32/";../../pcre/;../../assembler/;../../wrec/;../../parser/;../../runtime/;../../VM/;../../bytecode/;../../interpreter/;../../wtf/;../../debugger/;../../bytecompiler/;../../profiler;"$(WebKitLibrariesDir)\include\icu";"$(WebKitLibrariesDir)\include\pthreads";../../../icu/include;"$(WebKitLibrariesDir)\include";../../jit/"
+ PreprocessorDefinitions="__STD_C"
+ />
+ <Tool
+ Name="VCLinkerTool"
+ AdditionalDependencies="JavaScriptCore$(WebKitDLLConfigSuffix).lib WTF$(WebKitConfigSuffix).lib icuin$(LibraryConfigSuffix).lib icuuc$(LibraryConfigSuffix).lib winmm.lib pthreadVC2$(LibraryConfigSuffix).lib user32.lib"
+ SubSystem="1"
+ />
+ <Tool
+ Name="VCPostBuildEventTool"
+ CommandLine="if exist "$(WebKitOutputDir)\buildfailed" del "$(WebKitOutputDir)\buildfailed"

mkdir 2>NUL "$(WebKitOutputDir)\bin"
if exist "$(WebKitLibrariesDir)\bin\icudt40.dll" xcopy /y /d "$(WebKitLibrariesDir)\bin\icudt40.dll" "$(WebKitOutputDir)\bin"
if exist "$(WebKitLibrariesDir)\bin\icudt40$(LibraryConfigSuffix).dll" xcopy /y /d "$(WebKitLibrariesDir)\bin\icudt40$(LibraryConfigSuffix).dll" "$(WebKitOutputDir)\bin"
if exist "$(WebKitLibrariesDir)\bin\icuin40$(LibraryConfigSuffix).dll" xcopy /y /d "$(WebKitLibrariesDir)\bin\icuin40$(LibraryConfigSuffix).dll" "$(WebKitOutputDir)\bin"
if exist "$(WebKitLibrariesDir)\bin\icuuc40$(LibraryConfigSuffix).dll" xcopy /y /d "$(WebKitLibrariesDir)\bin\icuuc40$(LibraryConfigSuffix).dll" "$(WebKitOutputDir)\bin"
if exist "$(WebKitLibrariesDir)\bin\icudt42.dll" xcopy /y /d "$(WebKitLibrariesDir)\bin\icudt42.dll" "$(WebKitOutputDir)\bin"
if exist "$(WebKitLibrariesDir)\bin\icudt42$(LibraryConfigSuffix).dll" xcopy /y /d "$(WebKitLibrariesDir)\bin\icudt42$(LibraryConfigSuffix).dll" "$(WebKitOutputDir)\bin"
if exist "$(WebKitLibrariesDir)\bin\icuin42$(LibraryConfigSuffix).dll" xcopy /y /d "$(WebKitLibrariesDir)\bin\icuin42$(LibraryConfigSuffix).dll" "$(WebKitOutputDir)\bin"
if exist "$(WebKitLibrariesDir)\bin\icuuc42$(LibraryConfigSuffix).dll" xcopy /y /d "$(WebKitLibrariesDir)\bin\icuuc42$(LibraryConfigSuffix).dll" "$(WebKitOutputDir)\bin"
if exist "$(WebKitLibrariesDir)\bin\CoreFoundation$(LibraryConfigSuffix).dll" xcopy /y /d "$(WebKitLibrariesDir)\bin\CoreFoundation$(LibraryConfigSuffix).dll" "$(WebKitOutputDir)\bin"
if exist "$(WebKitLibrariesDir)\bin\CoreFoundation.resources" xcopy /y /d /e /i "$(WebKitLibrariesDir)\bin\CoreFoundation.resources" "$(WebKitOutputDir)\bin\CoreFoundation.resources"
if exist "$(WebKitLibrariesDir)\bin\pthreadVC2$(LibraryConfigSuffix).dll" xcopy /y /d "$(WebKitLibrariesDir)\bin\pthreadVC2$(LibraryConfigSuffix).dll" "$(WebKitOutputDir)\bin"
if exist "$(WebKitLibrariesDir)\bin\objc$(LibraryConfigSuffix).dll" xcopy /y /d "$(WebKitLibrariesDir)\bin\objc$(LibraryConfigSuffix).dll" "$(WebKitOutputDir)\bin"
if exist "$(WebKitLibrariesDir)\bin\ASL$(LibraryConfigSuffix).dll" xcopy /y /d "$(WebKitLibrariesDir)\bin\ASL$(LibraryConfigSuffix).dll" "$(WebKitOutputDir)\bin"

cmd /c
"
+ />
+ <Tool
+ Name="VCPreBuildEventTool"
+ CommandLine="%SystemDrive%\cygwin\bin\which.exe bash
if errorlevel 1 set PATH=%SystemDrive%\cygwin\bin;%PATH%
cmd /c
if exist "$(WebKitOutputDir)\buildfailed" grep XX$(ProjectName)XX "$(WebKitOutputDir)\buildfailed"
if errorlevel 1 exit 1
echo XX$(ProjectName)XX > "$(WebKitOutputDir)\buildfailed"
"
+ />
+</VisualStudioPropertySheet>
diff --git a/JavaScriptCore/JavaScriptCore.xcodeproj/project.pbxproj b/JavaScriptCore/JavaScriptCore.xcodeproj/project.pbxproj index 48cf396..e766243 100644 --- a/JavaScriptCore/JavaScriptCore.xcodeproj/project.pbxproj +++ b/JavaScriptCore/JavaScriptCore.xcodeproj/project.pbxproj @@ -51,6 +51,7 @@ 140D17D70E8AD4A9000CD17D /* JSBasePrivate.h in Headers */ = {isa = PBXBuildFile; fileRef = 140D17D60E8AD4A9000CD17D /* JSBasePrivate.h */; settings = {ATTRIBUTES = (Private, ); }; }; 141211310A48794D00480255 /* JavaScriptCore.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 932F5BD90822A1C700736975 /* JavaScriptCore.framework */; }; 141211340A48795800480255 /* minidom.c in Sources */ = {isa = PBXBuildFile; fileRef = 141211020A48780900480255 /* minidom.c */; }; + 1420BE7B10AA6DDB00F455D2 /* WeakRandom.h in Headers */ = {isa = PBXBuildFile; fileRef = 1420BE7A10AA6DDB00F455D2 /* WeakRandom.h */; settings = {ATTRIBUTES = (Private, ); }; }; 1421359B0A677F4F00A8195E /* JSBase.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 1421359A0A677F4F00A8195E /* JSBase.cpp */; }; 14280823107EC02C0013E7B2 /* Debugger.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F692A8580255597D01FF60F7 /* Debugger.cpp */; }; 1428082D107EC0570013E7B2 /* CallData.cpp in Sources */ = {isa = PBXBuildFile; fileRef = BCA62DFE0E2826230004F30D /* CallData.cpp */; }; @@ -187,6 +188,7 @@ 5D6A566B0F05995500266145 /* Threading.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 5D6A566A0F05995500266145 /* Threading.cpp */; }; 5DE6E5B30E1728EC00180407 /* create_hash_table in Headers */ = {isa = PBXBuildFile; fileRef = F692A8540255597D01FF60F7 /* create_hash_table */; settings = {ATTRIBUTES = (); }; }; 6507D29E0E871E5E00D7D896 /* JSTypeInfo.h in Headers */ = {isa = PBXBuildFile; fileRef = 6507D2970E871E4A00D7D896 /* JSTypeInfo.h */; settings = {ATTRIBUTES = (Private, ); }; }; + 655EB29B10CE2581001A990E /* NodesCodegen.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 655EB29A10CE2581001A990E /* NodesCodegen.cpp */; }; 65DFC93308EA173A00F7300B /* HashTable.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 65DFC92D08EA173A00F7300B /* HashTable.cpp */; }; 65FDE49C0BDD1D4A00E80111 /* Assertions.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 65E217B808E7EECC0023E5F6 /* Assertions.cpp */; settings = {COMPILER_FLAGS = "-Wno-missing-format-attribute"; }; }; 7E2ADD8E0E79AAD500D50C51 /* CharacterClassConstructor.h in Headers */ = {isa = PBXBuildFile; fileRef = 7E2ADD8D0E79AAD500D50C51 /* CharacterClassConstructor.h */; }; @@ -283,6 +285,7 @@ A7A1F7AC0F252B3C00E184E2 /* ByteArray.cpp in Sources */ = {isa = PBXBuildFile; fileRef = A7A1F7AA0F252B3C00E184E2 /* ByteArray.cpp */; }; A7A1F7AD0F252B3C00E184E2 /* ByteArray.h in Headers */ = {isa = PBXBuildFile; fileRef = A7A1F7AB0F252B3C00E184E2 /* ByteArray.h */; settings = {ATTRIBUTES = (Private, ); }; }; A7B48F490EE8936F00DCBDB6 /* ExecutableAllocator.cpp in Sources */ = {isa = PBXBuildFile; fileRef = A7B48DB60EE74CFC00DCBDB6 /* ExecutableAllocator.cpp */; }; + A7C2217810C7479400F97913 /* JSZombie.cpp in Sources */ = {isa = PBXBuildFile; fileRef = A7C2216B10C7469C00F97913 /* JSZombie.cpp */; }; A7C530E4102A3813005BC741 /* MarkStackPosix.cpp in Sources */ = {isa = PBXBuildFile; fileRef = A7C530E3102A3813005BC741 /* MarkStackPosix.cpp */; }; A7D649AA1015224E009B2E1B /* PossiblyNull.h in Headers */ = {isa = PBXBuildFile; fileRef = A7D649A91015224E009B2E1B /* PossiblyNull.h */; settings = {ATTRIBUTES = (Private, ); }; }; A7E2EA6B0FB460CF00601F06 /* LiteralParser.h in Headers */ = {isa = PBXBuildFile; fileRef = A7E2EA690FB460CF00601F06 /* LiteralParser.h */; }; @@ -561,6 +564,7 @@ 1412110D0A48788700480255 /* minidom.js */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.javascript; name = minidom.js; path = tests/minidom.js; sourceTree = "<group>"; }; 141211200A48793C00480255 /* minidom */ = {isa = PBXFileReference; explicitFileType = "compiled.mach-o.executable"; includeInIndex = 0; path = minidom; sourceTree = BUILT_PRODUCTS_DIR; }; 1419D32C0CEA7CDE00FF507A /* RefCounted.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RefCounted.h; sourceTree = "<group>"; }; + 1420BE7A10AA6DDB00F455D2 /* WeakRandom.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = WeakRandom.h; sourceTree = "<group>"; }; 1421359A0A677F4F00A8195E /* JSBase.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = JSBase.cpp; sourceTree = "<group>"; }; 142711380A460BBB0080EEEA /* JSBase.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = JSBase.h; sourceTree = "<group>"; }; 1429D77B0ED20D7300B89619 /* Interpreter.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = Interpreter.h; sourceTree = "<group>"; }; @@ -678,6 +682,7 @@ 6541BD6F08E80A17002CBEE7 /* TCSpinLock.h */ = {isa = PBXFileReference; fileEncoding = 30; indentWidth = 4; lastKnownFileType = sourcecode.c.h; path = TCSpinLock.h; sourceTree = "<group>"; tabWidth = 8; }; 6541BD7008E80A17002CBEE7 /* TCSystemAlloc.cpp */ = {isa = PBXFileReference; fileEncoding = 30; indentWidth = 4; lastKnownFileType = sourcecode.cpp.cpp; path = TCSystemAlloc.cpp; sourceTree = "<group>"; tabWidth = 8; }; 6541BD7108E80A17002CBEE7 /* TCSystemAlloc.h */ = {isa = PBXFileReference; fileEncoding = 30; indentWidth = 4; lastKnownFileType = sourcecode.c.h; path = TCSystemAlloc.h; sourceTree = "<group>"; tabWidth = 8; }; + 655EB29A10CE2581001A990E /* NodesCodegen.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = NodesCodegen.cpp; sourceTree = "<group>"; }; 6560A4CF04B3B3E7008AE952 /* CoreFoundation.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = CoreFoundation.framework; path = /System/Library/Frameworks/CoreFoundation.framework; sourceTree = "<absolute>"; }; 65621E6B089E859700760F35 /* PropertySlot.cpp */ = {isa = PBXFileReference; fileEncoding = 30; indentWidth = 4; lastKnownFileType = sourcecode.cpp.cpp; path = PropertySlot.cpp; sourceTree = "<group>"; tabWidth = 8; }; 65621E6C089E859700760F35 /* PropertySlot.h */ = {isa = PBXFileReference; fileEncoding = 30; indentWidth = 4; lastKnownFileType = sourcecode.c.h; path = PropertySlot.h; sourceTree = "<group>"; tabWidth = 8; }; @@ -835,6 +840,8 @@ A7A1F7AB0F252B3C00E184E2 /* ByteArray.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ByteArray.h; sourceTree = "<group>"; }; A7B48DB50EE74CFC00DCBDB6 /* ExecutableAllocator.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ExecutableAllocator.h; sourceTree = "<group>"; }; A7B48DB60EE74CFC00DCBDB6 /* ExecutableAllocator.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = ExecutableAllocator.cpp; sourceTree = "<group>"; }; + A7C2216810C745E000F97913 /* JSZombie.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = JSZombie.h; sourceTree = "<group>"; }; + A7C2216B10C7469C00F97913 /* JSZombie.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = JSZombie.cpp; sourceTree = "<group>"; }; A7C530E3102A3813005BC741 /* MarkStackPosix.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = MarkStackPosix.cpp; sourceTree = "<group>"; }; A7D649A91015224E009B2E1B /* PossiblyNull.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = PossiblyNull.h; sourceTree = "<group>"; }; A7E2EA690FB460CF00601F06 /* LiteralParser.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = LiteralParser.h; sourceTree = "<group>"; }; @@ -1385,6 +1392,7 @@ isa = PBXGroup; children = ( 969A07200ED1CE3300F1F681 /* BytecodeGenerator.cpp */, + 655EB29A10CE2581001A990E /* NodesCodegen.cpp */, 969A07210ED1CE3300F1F681 /* BytecodeGenerator.h */, 969A07270ED1CE6900F1F681 /* Label.h */, 960097A50EBABB58007A7297 /* LabelScope.h */, @@ -1593,6 +1601,9 @@ 5D53726E0E1C54880021E549 /* Tracing.h */, F692A8850255597D01FF60F7 /* UString.cpp */, F692A8860255597D01FF60F7 /* UString.h */, + 1420BE7A10AA6DDB00F455D2 /* WeakRandom.h */, + A7C2216810C745E000F97913 /* JSZombie.h */, + A7C2216B10C7469C00F97913 /* JSZombie.cpp */, ); path = runtime; sourceTree = "<group>"; @@ -2002,6 +2013,7 @@ BC87CDB910712AD4000614CF /* JSONObject.lut.h in Headers */, 148CD1D8108CF902008163C6 /* JSContextRefPrivate.h in Headers */, 14A1563210966365006FA260 /* DateInstanceCache.h in Headers */, + 1420BE7B10AA6DDB00F455D2 /* WeakRandom.h in Headers */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -2184,7 +2196,7 @@ ); runOnlyForDeploymentPostprocessing = 0; shellPath = /bin/sh; - shellScript = "# Touch Info.plist to let Xcode know it needs to copy it into the built product\ntouch \"$SRCROOT/Info.plist\"\n"; + shellScript = "# Touch Info.plist to let Xcode know it needs to copy it into the built product\nif [[ \"${CONFIGURATION}\" != \"Production\" ]]; then\n touch \"$SRCROOT/Info.plist\";\nfi;\n"; }; 5D35DEE10C7C140B008648B2 /* Generate DTrace header */ = { isa = PBXShellScriptBuildPhase; @@ -2444,6 +2456,8 @@ 1429DA820ED2482900B89619 /* WRECFunctors.cpp in Sources */, 1429DAE10ED2645B00B89619 /* WRECGenerator.cpp in Sources */, 1429DAC00ED263E700B89619 /* WRECParser.cpp in Sources */, + A7C2217810C7479400F97913 /* JSZombie.cpp in Sources */, + 655EB29B10CE2581001A990E /* NodesCodegen.cpp in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; diff --git a/JavaScriptCore/JavaScriptCoreSources.bkl b/JavaScriptCore/JavaScriptCoreSources.bkl index 1852d12..1f36c1c 100644 --- a/JavaScriptCore/JavaScriptCoreSources.bkl +++ b/JavaScriptCore/JavaScriptCoreSources.bkl @@ -43,6 +43,7 @@ Source files for JSCore. </set> <set append="1" var="JSCORE_BYTECOMPILER_SOURCES"> bytecompiler/BytecodeGenerator.cpp + bytecompiler/NodesCodegen.cpp </set> <set append="1" var="JSCORE_DEBUGGER_SOURCES"> debugger/Debugger.cpp @@ -89,6 +90,7 @@ Source files for JSCore. runtime/DateConstructor.cpp runtime/DateConversion.cpp runtime/DateInstance.cpp + wtf/DateMath.cpp runtime/DatePrototype.cpp runtime/Error.cpp runtime/ErrorConstructor.cpp @@ -173,7 +175,6 @@ Source files for JSCore. wtf/Assertions.cpp wtf/ByteArray.cpp wtf/CurrentTime.cpp - wtf/DateMath.cpp wtf/FastMalloc.cpp wtf/HashTable.cpp wtf/MainThread.cpp diff --git a/JavaScriptCore/assembler/ARMAssembler.cpp b/JavaScriptCore/assembler/ARMAssembler.cpp index 1324586..81c3222 100644 --- a/JavaScriptCore/assembler/ARMAssembler.cpp +++ b/JavaScriptCore/assembler/ARMAssembler.cpp @@ -118,7 +118,7 @@ ARMWord ARMAssembler::getOp2(ARMWord imm) if ((imm & 0x00ffffff) == 0) return OP2_IMM | (imm >> 24) | (rol << 8); - return 0; + return INVALID_IMM; } int ARMAssembler::genInt(int reg, ARMWord imm, bool positive) @@ -236,25 +236,18 @@ ARMWord ARMAssembler::getImm(ARMWord imm, int tmpReg, bool invert) // Do it by 1 instruction tmp = getOp2(imm); - if (tmp) + if (tmp != INVALID_IMM) return tmp; tmp = getOp2(~imm); - if (tmp) { + if (tmp != INVALID_IMM) { if (invert) return tmp | OP2_INV_IMM; mvn_r(tmpReg, tmp); return tmpReg; } - // Do it by 2 instruction - if (genInt(tmpReg, imm, true)) - return tmpReg; - if (genInt(tmpReg, ~imm, false)) - return tmpReg; - - ldr_imm(tmpReg, imm); - return tmpReg; + return encodeComplexImm(imm, tmpReg); } void ARMAssembler::moveImm(ARMWord imm, int dest) @@ -263,24 +256,43 @@ void ARMAssembler::moveImm(ARMWord imm, int dest) // Do it by 1 instruction tmp = getOp2(imm); - if (tmp) { + if (tmp != INVALID_IMM) { mov_r(dest, tmp); return; } tmp = getOp2(~imm); - if (tmp) { + if (tmp != INVALID_IMM) { mvn_r(dest, tmp); return; } + encodeComplexImm(imm, dest); +} + +ARMWord ARMAssembler::encodeComplexImm(ARMWord imm, int dest) +{ + ARMWord tmp; + +#if ARM_ARCH_VERSION >= 7 + tmp = getImm16Op2(imm); + if (tmp != INVALID_IMM) { + movw_r(dest, tmp); + return dest; + } + movw_r(dest, getImm16Op2(imm & 0xffff)); + movt_r(dest, getImm16Op2(imm >> 16)); + return dest; +#else // Do it by 2 instruction if (genInt(dest, imm, true)) - return; + return dest; if (genInt(dest, ~imm, false)) - return; + return dest; ldr_imm(dest, imm); + return dest; +#endif } // Memory load/store helpers diff --git a/JavaScriptCore/assembler/ARMAssembler.h b/JavaScriptCore/assembler/ARMAssembler.h index 9f9a450..712473e 100644 --- a/JavaScriptCore/assembler/ARMAssembler.h +++ b/JavaScriptCore/assembler/ARMAssembler.h @@ -121,6 +121,7 @@ namespace JSC { MUL = 0x00000090, MULL = 0x00c00090, FADDD = 0x0e300b00, + FDIVD = 0x0e800b00, FSUBD = 0x0e300b40, FMULD = 0x0e200b00, FCMPD = 0x0eb40b40, @@ -133,12 +134,18 @@ namespace JSC { B = 0x0a000000, BL = 0x0b000000, FMSR = 0x0e000a10, + FMRS = 0x0e100a10, FSITOD = 0x0eb80bc0, + FTOSID = 0x0ebd0b40, FMSTAT = 0x0ef1fa10, #if ARM_ARCH_VERSION >= 5 CLZ = 0x016f0f10, BKPT = 0xe120070, #endif +#if ARM_ARCH_VERSION >= 7 + MOVW = 0x03000000, + MOVT = 0x03400000, +#endif }; enum { @@ -175,6 +182,8 @@ namespace JSC { padForAlign32 = 0xee120070, }; + static const ARMWord INVALID_IMM = 0xf0000000; + class JmpSrc { friend class ARMAssembler; public: @@ -333,6 +342,20 @@ namespace JSC { emitInst(static_cast<ARMWord>(cc) | MOV, rd, ARMRegisters::r0, op2); } +#if ARM_ARCH_VERSION >= 7 + void movw_r(int rd, ARMWord op2, Condition cc = AL) + { + ASSERT((op2 | 0xf0fff) == 0xf0fff); + m_buffer.putInt(static_cast<ARMWord>(cc) | MOVW | RD(rd) | op2); + } + + void movt_r(int rd, ARMWord op2, Condition cc = AL) + { + ASSERT((op2 | 0xf0fff) == 0xf0fff); + m_buffer.putInt(static_cast<ARMWord>(cc) | MOVT | RD(rd) | op2); + } +#endif + void movs_r(int rd, ARMWord op2, Condition cc = AL) { emitInst(static_cast<ARMWord>(cc) | MOV | SET_CC, rd, ARMRegisters::r0, op2); @@ -378,6 +401,11 @@ namespace JSC { emitInst(static_cast<ARMWord>(cc) | FADDD, dd, dn, dm); } + void fdivd_r(int dd, int dn, int dm, Condition cc = AL) + { + emitInst(static_cast<ARMWord>(cc) | FDIVD, dd, dn, dm); + } + void fsubd_r(int dd, int dn, int dm, Condition cc = AL) { emitInst(static_cast<ARMWord>(cc) | FSUBD, dd, dn, dm); @@ -482,11 +510,21 @@ namespace JSC { emitInst(static_cast<ARMWord>(cc) | FMSR, rn, dd, 0); } + void fmrs_r(int rd, int dn, Condition cc = AL) + { + emitInst(static_cast<ARMWord>(cc) | FMRS, rd, dn, 0); + } + void fsitod_r(int dd, int dm, Condition cc = AL) { emitInst(static_cast<ARMWord>(cc) | FSITOD, dd, 0, dm); } + void ftosid_r(int fd, int dm, Condition cc = AL) + { + emitInst(static_cast<ARMWord>(cc) | FTOSID, fd, 0, dm); + } + void fmstat(Condition cc = AL) { m_buffer.putInt(static_cast<ARMWord>(cc) | FMSTAT); @@ -708,8 +746,18 @@ namespace JSC { } static ARMWord getOp2(ARMWord imm); + +#if ARM_ARCH_VERSION >= 7 + static ARMWord getImm16Op2(ARMWord imm) + { + if (imm <= 0xffff) + return (imm & 0xf000) << 4 | (imm & 0xfff); + return INVALID_IMM; + } +#endif ARMWord getImm(ARMWord imm, int tmpReg, bool invert = false); void moveImm(ARMWord imm, int dest); + ARMWord encodeComplexImm(ARMWord imm, int dest); // Memory load/store helpers diff --git a/JavaScriptCore/assembler/ARMv7Assembler.h b/JavaScriptCore/assembler/ARMv7Assembler.h index 02ce2e9..e253a53 100644 --- a/JavaScriptCore/assembler/ARMv7Assembler.h +++ b/JavaScriptCore/assembler/ARMv7Assembler.h @@ -236,6 +236,11 @@ class ARMThumbImmediate { ARMThumbImmediate(ThumbImmediateType type, uint16_t value) : m_type(TypeUInt16) { + // Make sure this constructor is only reached with type TypeUInt16; + // this extra parameter makes the code a little clearer by making it + // explicit at call sites which type is being constructed + ASSERT_UNUSED(type, type == TypeUInt16); + m_value.asInt = value; } diff --git a/JavaScriptCore/assembler/MacroAssembler.h b/JavaScriptCore/assembler/MacroAssembler.h index 2743ab4..858707d 100644 --- a/JavaScriptCore/assembler/MacroAssembler.h +++ b/JavaScriptCore/assembler/MacroAssembler.h @@ -179,16 +179,6 @@ public: or32(imm, dest); } - void rshiftPtr(RegisterID shift_amount, RegisterID dest) - { - rshift32(shift_amount, dest); - } - - void rshiftPtr(Imm32 imm, RegisterID dest) - { - rshift32(imm, dest); - } - void subPtr(RegisterID src, RegisterID dest) { sub32(src, dest); diff --git a/JavaScriptCore/assembler/MacroAssemblerARM.h b/JavaScriptCore/assembler/MacroAssemblerARM.h index 7a72b06..24e2e11 100644 --- a/JavaScriptCore/assembler/MacroAssemblerARM.h +++ b/JavaScriptCore/assembler/MacroAssemblerARM.h @@ -38,6 +38,9 @@ namespace JSC { class MacroAssemblerARM : public AbstractMacroAssembler<ARMAssembler> { + static const int DoubleConditionMask = 0x0f; + static const int DoubleConditionBitSpecial = 0x10; + COMPILE_ASSERT(!(DoubleConditionBitSpecial & DoubleConditionMask), DoubleConditionBitSpecial_should_not_interfere_with_ARMAssembler_Condition_codes); public: enum Condition { Equal = ARMAssembler::EQ, @@ -57,11 +60,20 @@ public: }; enum DoubleCondition { + // These conditions will only evaluate to true if the comparison is ordered - i.e. neither operand is NaN. DoubleEqual = ARMAssembler::EQ, + DoubleNotEqual = ARMAssembler::NE | DoubleConditionBitSpecial, DoubleGreaterThan = ARMAssembler::GT, DoubleGreaterThanOrEqual = ARMAssembler::GE, - DoubleLessThan = ARMAssembler::LT, - DoubleLessThanOrEqual = ARMAssembler::LE, + DoubleLessThan = ARMAssembler::CC, + DoubleLessThanOrEqual = ARMAssembler::LS, + // If either operand is NaN, these conditions always evaluate to true. + DoubleEqualOrUnordered = ARMAssembler::EQ | DoubleConditionBitSpecial, + DoubleNotEqualOrUnordered = ARMAssembler::NE, + DoubleGreaterThanOrUnordered = ARMAssembler::HI, + DoubleGreaterThanOrEqualOrUnordered = ARMAssembler::CS, + DoubleLessThanOrUnordered = ARMAssembler::LT, + DoubleLessThanOrEqualOrUnordered = ARMAssembler::LE, }; static const RegisterID stackPointerRegister = ARMRegisters::sp; @@ -106,14 +118,18 @@ public: m_assembler.ands_r(dest, dest, w); } - void lshift32(Imm32 imm, RegisterID dest) + void lshift32(RegisterID shift_amount, RegisterID dest) { - m_assembler.movs_r(dest, m_assembler.lsl(dest, imm.m_value & 0x1f)); + ARMWord w = ARMAssembler::getOp2(0x1f); + ASSERT(w != ARMAssembler::INVALID_IMM); + m_assembler.and_r(ARMRegisters::S0, shift_amount, w); + + m_assembler.movs_r(dest, m_assembler.lsl_r(dest, ARMRegisters::S0)); } - void lshift32(RegisterID shift_amount, RegisterID dest) + void lshift32(Imm32 imm, RegisterID dest) { - m_assembler.movs_r(dest, m_assembler.lsl_r(dest, shift_amount)); + m_assembler.movs_r(dest, m_assembler.lsl(dest, imm.m_value & 0x1f)); } void mul32(RegisterID src, RegisterID dest) @@ -131,6 +147,11 @@ public: m_assembler.muls_r(dest, src, ARMRegisters::S0); } + void neg32(RegisterID srcDest) + { + m_assembler.rsbs_r(srcDest, srcDest, ARMAssembler::getOp2(0)); + } + void not32(RegisterID dest) { m_assembler.mvns_r(dest, dest); @@ -148,7 +169,11 @@ public: void rshift32(RegisterID shift_amount, RegisterID dest) { - m_assembler.movs_r(dest, m_assembler.asr_r(dest, shift_amount)); + ARMWord w = ARMAssembler::getOp2(0x1f); + ASSERT(w != ARMAssembler::INVALID_IMM); + m_assembler.and_r(ARMRegisters::S0, shift_amount, w); + + m_assembler.movs_r(dest, m_assembler.asr_r(dest, ARMRegisters::S0)); } void rshift32(Imm32 imm, RegisterID dest) @@ -505,6 +530,13 @@ public: return Jump(m_assembler.jmp(ARMCondition(cond))); } + Jump branchOr32(Condition cond, RegisterID src, RegisterID dest) + { + ASSERT((cond == Signed) || (cond == Zero) || (cond == NonZero)); + or32(src, dest); + return Jump(m_assembler.jmp(ARMCondition(cond))); + } + void breakpoint() { m_assembler.bkpt(0); @@ -548,6 +580,25 @@ public: m_assembler.mov_r(dest, ARMAssembler::getOp2(1), ARMCondition(cond)); } + void set8(Condition cond, RegisterID left, RegisterID right, RegisterID dest) + { + // ARM doesn't have byte registers + set32(cond, left, right, dest); + } + + void set8(Condition cond, Address left, RegisterID right, RegisterID dest) + { + // ARM doesn't have byte registers + load32(left, ARMRegisters::S1); + set32(cond, ARMRegisters::S1, right, dest); + } + + void set8(Condition cond, RegisterID left, Imm32 right, RegisterID dest) + { + // ARM doesn't have byte registers + set32(cond, left, right, dest); + } + void setTest32(Condition cond, Address address, Imm32 mask, RegisterID dest) { load32(address, ARMRegisters::S1); @@ -559,6 +610,12 @@ public: m_assembler.mov_r(dest, ARMAssembler::getOp2(1), ARMCondition(cond)); } + void setTest8(Condition cond, Address address, Imm32 mask, RegisterID dest) + { + // ARM doesn't have byte registers + setTest32(cond, address, mask, dest); + } + void add32(Imm32 imm, RegisterID src, RegisterID dest) { m_assembler.add_r(dest, src, m_assembler.getImm(imm.m_value, ARMRegisters::S0)); @@ -666,6 +723,12 @@ public: m_assembler.doubleTransfer(true, dest, address.base, address.offset); } + void loadDouble(void* address, FPRegisterID dest) + { + m_assembler.ldr_un_imm(ARMRegisters::S0, (ARMWord)address); + m_assembler.fdtr_u(true, dest, ARMRegisters::S0, 0); + } + void storeDouble(FPRegisterID src, ImplicitAddress address) { m_assembler.doubleTransfer(false, src, address.base, address.offset); @@ -682,6 +745,18 @@ public: addDouble(ARMRegisters::SD0, dest); } + void divDouble(FPRegisterID src, FPRegisterID dest) + { + m_assembler.fdivd_r(dest, dest, src); + } + + void divDouble(Address src, FPRegisterID dest) + { + ASSERT_NOT_REACHED(); // Untested + loadDouble(src, ARMRegisters::SD0); + divDouble(ARMRegisters::SD0, dest); + } + void subDouble(FPRegisterID src, FPRegisterID dest) { m_assembler.fsubd_r(dest, dest, src); @@ -710,11 +785,30 @@ public: m_assembler.fsitod_r(dest, dest); } + void convertInt32ToDouble(Address src, FPRegisterID dest) + { + ASSERT_NOT_REACHED(); // Untested + // flds does not worth the effort here + load32(src, ARMRegisters::S1); + convertInt32ToDouble(ARMRegisters::S1, dest); + } + + void convertInt32ToDouble(AbsoluteAddress src, FPRegisterID dest) + { + ASSERT_NOT_REACHED(); // Untested + // flds does not worth the effort here + m_assembler.ldr_un_imm(ARMRegisters::S1, (ARMWord)src.m_ptr); + m_assembler.dtr_u(true, ARMRegisters::S1, ARMRegisters::S1, 0); + convertInt32ToDouble(ARMRegisters::S1, dest); + } + Jump branchDouble(DoubleCondition cond, FPRegisterID left, FPRegisterID right) { m_assembler.fcmpd_r(left, right); m_assembler.fmstat(); - return Jump(m_assembler.jmp(static_cast<ARMAssembler::Condition>(cond))); + if (cond & DoubleConditionBitSpecial) + m_assembler.cmp_r(ARMRegisters::S0, ARMRegisters::S0, ARMAssembler::VS); + return Jump(m_assembler.jmp(static_cast<ARMAssembler::Condition>(cond & ~DoubleConditionMask))); } // Truncates 'src' to an integer, and places the resulting 'dest'. @@ -729,6 +823,29 @@ public: return jump(); } + // Convert 'src' to an integer, and places the resulting 'dest'. + // If the result is not representable as a 32 bit value, branch. + // May also branch for some values that are representable in 32 bits + // (specifically, in this case, 0). + void branchConvertDoubleToInt32(FPRegisterID src, RegisterID dest, JumpList& failureCases, FPRegisterID fpTemp) + { + m_assembler.ftosid_r(ARMRegisters::SD0, src); + m_assembler.fmrs_r(dest, ARMRegisters::SD0); + + // Convert the integer result back to float & compare to the original value - if not equal or unordered (NaN) then jump. + m_assembler.fsitod_r(ARMRegisters::SD0, ARMRegisters::SD0); + failureCases.append(branchDouble(DoubleNotEqualOrUnordered, src, ARMRegisters::SD0)); + + // If the result is zero, it might have been -0.0, and 0.0 equals to -0.0 + failureCases.append(branchTest32(Zero, dest)); + } + + void zeroDouble(FPRegisterID srcDest) + { + m_assembler.mov_r(ARMRegisters::S0, ARMAssembler::getOp2(0)); + convertInt32ToDouble(ARMRegisters::S0, srcDest); + } + protected: ARMAssembler::Condition ARMCondition(Condition cond) { diff --git a/JavaScriptCore/assembler/MacroAssemblerARMv7.h b/JavaScriptCore/assembler/MacroAssemblerARMv7.h index c479517..532a9cf 100644 --- a/JavaScriptCore/assembler/MacroAssemblerARMv7.h +++ b/JavaScriptCore/assembler/MacroAssemblerARMv7.h @@ -93,13 +93,21 @@ public: Zero = ARMv7Assembler::ConditionEQ, NonZero = ARMv7Assembler::ConditionNE }; - enum DoubleCondition { + // These conditions will only evaluate to true if the comparison is ordered - i.e. neither operand is NaN. DoubleEqual = ARMv7Assembler::ConditionEQ, + DoubleNotEqual = ARMv7Assembler::ConditionVC, // Not the right flag! check for this & handle differently. DoubleGreaterThan = ARMv7Assembler::ConditionGT, DoubleGreaterThanOrEqual = ARMv7Assembler::ConditionGE, DoubleLessThan = ARMv7Assembler::ConditionLO, DoubleLessThanOrEqual = ARMv7Assembler::ConditionLS, + // If either operand is NaN, these conditions always evaluate to true. + DoubleEqualOrUnordered = ARMv7Assembler::ConditionVS, // Not the right flag! check for this & handle differently. + DoubleNotEqualOrUnordered = ARMv7Assembler::ConditionNE, + DoubleGreaterThanOrUnordered = ARMv7Assembler::ConditionHI, + DoubleGreaterThanOrEqualOrUnordered = ARMv7Assembler::ConditionHS, + DoubleLessThanOrUnordered = ARMv7Assembler::ConditionLT, + DoubleLessThanOrEqualOrUnordered = ARMv7Assembler::ConditionLE, }; static const RegisterID stackPointerRegister = ARMRegisters::sp; @@ -189,14 +197,19 @@ public: } } - void lshift32(Imm32 imm, RegisterID dest) + void lshift32(RegisterID shift_amount, RegisterID dest) { - m_assembler.lsl(dest, dest, imm.m_value); + // Clamp the shift to the range 0..31 + ARMThumbImmediate armImm = ARMThumbImmediate::makeEncodedImm(0x1f); + ASSERT(armImm.isValid()); + m_assembler.ARM_and(dataTempRegister, shift_amount, armImm); + + m_assembler.lsl(dest, dest, dataTempRegister); } - void lshift32(RegisterID shift_amount, RegisterID dest) + void lshift32(Imm32 imm, RegisterID dest) { - m_assembler.lsl(dest, dest, shift_amount); + m_assembler.lsl(dest, dest, imm.m_value & 0x1f); } void mul32(RegisterID src, RegisterID dest) @@ -233,12 +246,17 @@ public: void rshift32(RegisterID shift_amount, RegisterID dest) { - m_assembler.asr(dest, dest, shift_amount); + // Clamp the shift to the range 0..31 + ARMThumbImmediate armImm = ARMThumbImmediate::makeEncodedImm(0x1f); + ASSERT(armImm.isValid()); + m_assembler.ARM_and(dataTempRegister, shift_amount, armImm); + + m_assembler.asr(dest, dest, dataTempRegister); } void rshift32(Imm32 imm, RegisterID dest) { - m_assembler.asr(dest, dest, imm.m_value); + m_assembler.asr(dest, dest, imm.m_value & 0x1f); } void sub32(RegisterID src, RegisterID dest) @@ -531,6 +549,23 @@ public: { m_assembler.vcmp_F64(left, right); m_assembler.vmrs_APSR_nzcv_FPSCR(); + + if (cond == DoubleNotEqual) { + // ConditionNE jumps if NotEqual *or* unordered - force the unordered cases not to jump. + Jump unordered = makeBranch(ARMv7Assembler::ConditionVS); + Jump result = makeBranch(ARMv7Assembler::ConditionNE); + unordered.link(this); + return result; + } + if (cond == DoubleEqualOrUnordered) { + Jump unordered = makeBranch(ARMv7Assembler::ConditionVS); + Jump notEqual = makeBranch(ARMv7Assembler::ConditionNE); + unordered.link(this); + // We get here if either unordered, or equal. + Jump result = makeJump(); + notEqual.link(this); + return result; + } return makeBranch(cond); } diff --git a/JavaScriptCore/assembler/MacroAssemblerX86Common.h b/JavaScriptCore/assembler/MacroAssemblerX86Common.h index 5ebefa7..96d21f1 100644 --- a/JavaScriptCore/assembler/MacroAssemblerX86Common.h +++ b/JavaScriptCore/assembler/MacroAssemblerX86Common.h @@ -36,6 +36,10 @@ namespace JSC { class MacroAssemblerX86Common : public AbstractMacroAssembler<X86Assembler> { + static const int DoubleConditionBitInvert = 0x10; + static const int DoubleConditionBitSpecial = 0x20; + static const int DoubleConditionBits = DoubleConditionBitInvert | DoubleConditionBitSpecial; + public: enum Condition { @@ -56,13 +60,24 @@ public: }; enum DoubleCondition { - DoubleEqual = X86Assembler::ConditionE, + // These conditions will only evaluate to true if the comparison is ordered - i.e. neither operand is NaN. + DoubleEqual = X86Assembler::ConditionE | DoubleConditionBitSpecial, DoubleNotEqual = X86Assembler::ConditionNE, DoubleGreaterThan = X86Assembler::ConditionA, DoubleGreaterThanOrEqual = X86Assembler::ConditionAE, - DoubleLessThan = X86Assembler::ConditionB, - DoubleLessThanOrEqual = X86Assembler::ConditionBE, + DoubleLessThan = X86Assembler::ConditionA | DoubleConditionBitInvert, + DoubleLessThanOrEqual = X86Assembler::ConditionAE | DoubleConditionBitInvert, + // If either operand is NaN, these conditions always evaluate to true. + DoubleEqualOrUnordered = X86Assembler::ConditionE, + DoubleNotEqualOrUnordered = X86Assembler::ConditionNE | DoubleConditionBitSpecial, + DoubleGreaterThanOrUnordered = X86Assembler::ConditionB | DoubleConditionBitInvert, + DoubleGreaterThanOrEqualOrUnordered = X86Assembler::ConditionBE | DoubleConditionBitInvert, + DoubleLessThanOrUnordered = X86Assembler::ConditionB, + DoubleLessThanOrEqualOrUnordered = X86Assembler::ConditionBE, }; + COMPILE_ASSERT( + !((X86Assembler::ConditionE | X86Assembler::ConditionNE | X86Assembler::ConditionA | X86Assembler::ConditionAE | X86Assembler::ConditionB | X86Assembler::ConditionBE) & DoubleConditionBits), + DoubleConditionBits_should_not_interfere_with_X86Assembler_Condition_codes); static const RegisterID stackPointerRegister = X86Registers::esp; @@ -416,20 +431,35 @@ public: void convertInt32ToDouble(Address src, FPRegisterID dest) { + ASSERT(isSSE2Present()); m_assembler.cvtsi2sd_mr(src.offset, src.base, dest); } Jump branchDouble(DoubleCondition cond, FPRegisterID left, FPRegisterID right) { ASSERT(isSSE2Present()); - m_assembler.ucomisd_rr(right, left); - return Jump(m_assembler.jCC(x86Condition(cond))); - } - Jump branchDouble(DoubleCondition cond, FPRegisterID left, Address right) - { - m_assembler.ucomisd_mr(right.offset, right.base, left); - return Jump(m_assembler.jCC(x86Condition(cond))); + if (cond & DoubleConditionBitInvert) + m_assembler.ucomisd_rr(left, right); + else + m_assembler.ucomisd_rr(right, left); + + if (cond == DoubleEqual) { + Jump isUnordered(m_assembler.jp()); + Jump result = Jump(m_assembler.je()); + isUnordered.link(this); + return result; + } else if (cond == DoubleNotEqualOrUnordered) { + Jump isUnordered(m_assembler.jp()); + Jump isEqual(m_assembler.je()); + isUnordered.link(this); + Jump result = jump(); + isEqual.link(this); + return result; + } + + ASSERT(!(cond & DoubleConditionBitSpecial)); + return Jump(m_assembler.jCC(static_cast<X86Assembler::Condition>(cond & ~DoubleConditionBits))); } // Truncates 'src' to an integer, and places the resulting 'dest'. @@ -443,6 +473,25 @@ public: return branch32(Equal, dest, Imm32(0x80000000)); } + // Convert 'src' to an integer, and places the resulting 'dest'. + // If the result is not representable as a 32 bit value, branch. + // May also branch for some values that are representable in 32 bits + // (specifically, in this case, 0). + void branchConvertDoubleToInt32(FPRegisterID src, RegisterID dest, JumpList& failureCases, FPRegisterID fpTemp) + { + ASSERT(isSSE2Present()); + m_assembler.cvttsd2si_rr(src, dest); + + // If the result is zero, it might have been -0.0, and the double comparison won't catch this! + failureCases.append(branchTest32(Zero, dest)); + + // Convert the integer result back to float & compare to the original value - if not equal or unordered (NaN) then jump. + convertInt32ToDouble(dest, fpTemp); + m_assembler.ucomisd_rr(fpTemp, src); + failureCases.append(m_assembler.jp()); + failureCases.append(m_assembler.jne()); + } + void zeroDouble(FPRegisterID srcDest) { ASSERT(isSSE2Present()); @@ -509,7 +558,8 @@ public: void swap(RegisterID reg1, RegisterID reg2) { - m_assembler.xchgq_rr(reg1, reg2); + if (reg1 != reg2) + m_assembler.xchgq_rr(reg1, reg2); } void signExtend32ToPtr(RegisterID src, RegisterID dest) @@ -889,11 +939,6 @@ protected: return static_cast<X86Assembler::Condition>(cond); } - X86Assembler::Condition x86Condition(DoubleCondition cond) - { - return static_cast<X86Assembler::Condition>(cond); - } - private: // Only MacroAssemblerX86 should be using the following method; SSE2 is always available on // x86_64, and clients & subclasses of MacroAssembler should be using 'supportsFloatingPoint()'. diff --git a/JavaScriptCore/assembler/MacroAssemblerX86_64.h b/JavaScriptCore/assembler/MacroAssemblerX86_64.h index 0f95fe6..7828cf5 100644 --- a/JavaScriptCore/assembler/MacroAssemblerX86_64.h +++ b/JavaScriptCore/assembler/MacroAssemblerX86_64.h @@ -192,33 +192,6 @@ public: m_assembler.orq_ir(imm.m_value, dest); } - void rshiftPtr(RegisterID shift_amount, RegisterID dest) - { - // On x86 we can only shift by ecx; if asked to shift by another register we'll - // need rejig the shift amount into ecx first, and restore the registers afterwards. - if (shift_amount != X86Registers::ecx) { - swap(shift_amount, X86Registers::ecx); - - // E.g. transform "shll %eax, %eax" -> "xchgl %eax, %ecx; shll %ecx, %ecx; xchgl %eax, %ecx" - if (dest == shift_amount) - m_assembler.sarq_CLr(X86Registers::ecx); - // E.g. transform "shll %eax, %ecx" -> "xchgl %eax, %ecx; shll %ecx, %eax; xchgl %eax, %ecx" - else if (dest == X86Registers::ecx) - m_assembler.sarq_CLr(shift_amount); - // E.g. transform "shll %eax, %ebx" -> "xchgl %eax, %ecx; shll %ecx, %ebx; xchgl %eax, %ecx" - else - m_assembler.sarq_CLr(dest); - - swap(shift_amount, X86Registers::ecx); - } else - m_assembler.sarq_CLr(dest); - } - - void rshiftPtr(Imm32 imm, RegisterID dest) - { - m_assembler.sarq_i8r(imm.m_value, dest); - } - void subPtr(RegisterID src, RegisterID dest) { m_assembler.subq_rr(src, dest); diff --git a/JavaScriptCore/bytecode/CodeBlock.cpp b/JavaScriptCore/bytecode/CodeBlock.cpp index c915934..13bed8c 100644 --- a/JavaScriptCore/bytecode/CodeBlock.cpp +++ b/JavaScriptCore/bytecode/CodeBlock.cpp @@ -71,17 +71,9 @@ static UString valueToSourceString(ExecState* exec, JSValue val) return val.toString(exec); } -static CString registerName(int r) -{ - if (r == missingThisObjectMarker()) - return "<null>"; - - return (UString("r") + UString::from(r)).UTF8String(); -} - static CString constantName(ExecState* exec, int k, JSValue value) { - return (valueToSourceString(exec, value) + "(@k" + UString::from(k) + ")").UTF8String(); + return (valueToSourceString(exec, value) + "(@k" + UString::from(k - FirstConstantRegisterIndex) + ")").UTF8String(); } static CString idName(int id0, const Identifier& ident) @@ -89,6 +81,17 @@ static CString idName(int id0, const Identifier& ident) return (ident.ustring() + "(@id" + UString::from(id0) +")").UTF8String(); } +CString CodeBlock::registerName(ExecState* exec, int r) const +{ + if (r == missingThisObjectMarker()) + return "<null>"; + + if (isConstantRegisterIndex(r)) + return constantName(exec, r, getConstant(r)); + + return (UString("r") + UString::from(r)).UTF8String(); +} + static UString regexpToSourceString(RegExp* regExp) { UString pattern = UString("/") + regExp->pattern() + "/"; @@ -135,44 +138,44 @@ NEVER_INLINE static const char* debugHookName(int debugHookID) return ""; } -static void printUnaryOp(int location, Vector<Instruction>::const_iterator& it, const char* op) +void CodeBlock::printUnaryOp(ExecState* exec, int location, Vector<Instruction>::const_iterator& it, const char* op) const { int r0 = (++it)->u.operand; int r1 = (++it)->u.operand; - printf("[%4d] %s\t\t %s, %s\n", location, op, registerName(r0).c_str(), registerName(r1).c_str()); + printf("[%4d] %s\t\t %s, %s\n", location, op, registerName(exec, r0).c_str(), registerName(exec, r1).c_str()); } -static void printBinaryOp(int location, Vector<Instruction>::const_iterator& it, const char* op) +void CodeBlock::printBinaryOp(ExecState* exec, int location, Vector<Instruction>::const_iterator& it, const char* op) const { int r0 = (++it)->u.operand; int r1 = (++it)->u.operand; int r2 = (++it)->u.operand; - printf("[%4d] %s\t\t %s, %s, %s\n", location, op, registerName(r0).c_str(), registerName(r1).c_str(), registerName(r2).c_str()); + printf("[%4d] %s\t\t %s, %s, %s\n", location, op, registerName(exec, r0).c_str(), registerName(exec, r1).c_str(), registerName(exec, r2).c_str()); } -static void printConditionalJump(const Vector<Instruction>::const_iterator&, Vector<Instruction>::const_iterator& it, int location, const char* op) +void CodeBlock::printConditionalJump(ExecState* exec, const Vector<Instruction>::const_iterator&, Vector<Instruction>::const_iterator& it, int location, const char* op) const { int r0 = (++it)->u.operand; int offset = (++it)->u.operand; - printf("[%4d] %s\t\t %s, %d(->%d)\n", location, op, registerName(r0).c_str(), offset, location + offset); + printf("[%4d] %s\t\t %s, %d(->%d)\n", location, op, registerName(exec, r0).c_str(), offset, location + offset); } -static void printGetByIdOp(int location, Vector<Instruction>::const_iterator& it, const Vector<Identifier>& m_identifiers, const char* op) +void CodeBlock::printGetByIdOp(ExecState* exec, int location, Vector<Instruction>::const_iterator& it, const char* op) const { int r0 = (++it)->u.operand; int r1 = (++it)->u.operand; int id0 = (++it)->u.operand; - printf("[%4d] %s\t %s, %s, %s\n", location, op, registerName(r0).c_str(), registerName(r1).c_str(), idName(id0, m_identifiers[id0]).c_str()); + printf("[%4d] %s\t %s, %s, %s\n", location, op, registerName(exec, r0).c_str(), registerName(exec, r1).c_str(), idName(id0, m_identifiers[id0]).c_str()); it += 4; } -static void printPutByIdOp(int location, Vector<Instruction>::const_iterator& it, const Vector<Identifier>& m_identifiers, const char* op) +void CodeBlock::printPutByIdOp(ExecState* exec, int location, Vector<Instruction>::const_iterator& it, const char* op) const { int r0 = (++it)->u.operand; int id0 = (++it)->u.operand; int r1 = (++it)->u.operand; - printf("[%4d] %s\t %s, %s, %s\n", location, op, registerName(r0).c_str(), idName(id0, m_identifiers[id0]).c_str(), registerName(r1).c_str()); + printf("[%4d] %s\t %s, %s, %s\n", location, op, registerName(exec, r0).c_str(), idName(id0, m_identifiers[id0]).c_str(), registerName(exec, r1).c_str()); it += 4; } @@ -357,7 +360,7 @@ void CodeBlock::dump(ExecState* exec) const unsigned registerIndex = m_numVars; size_t i = 0; do { - printf(" r%u = %s\n", registerIndex, valueToSourceString(exec, m_constantRegisters[i].jsValue()).ascii()); + printf(" k%u = %s\n", registerIndex, valueToSourceString(exec, m_constantRegisters[i].jsValue()).ascii()); ++i; ++registerIndex; } while (i < m_constantRegisters.size()); @@ -481,7 +484,7 @@ void CodeBlock::dump(ExecState* exec, const Vector<Instruction>::const_iterator& } case op_enter_with_activation: { int r0 = (++it)->u.operand; - printf("[%4d] enter_with_activation %s\n", location, registerName(r0).c_str()); + printf("[%4d] enter_with_activation %s\n", location, registerName(exec, r0).c_str()); break; } case op_create_arguments: { @@ -494,148 +497,148 @@ void CodeBlock::dump(ExecState* exec, const Vector<Instruction>::const_iterator& } case op_convert_this: { int r0 = (++it)->u.operand; - printf("[%4d] convert_this %s\n", location, registerName(r0).c_str()); + printf("[%4d] convert_this %s\n", location, registerName(exec, r0).c_str()); break; } case op_new_object: { int r0 = (++it)->u.operand; - printf("[%4d] new_object\t %s\n", location, registerName(r0).c_str()); + printf("[%4d] new_object\t %s\n", location, registerName(exec, r0).c_str()); break; } case op_new_array: { int dst = (++it)->u.operand; int argv = (++it)->u.operand; int argc = (++it)->u.operand; - printf("[%4d] new_array\t %s, %s, %d\n", location, registerName(dst).c_str(), registerName(argv).c_str(), argc); + printf("[%4d] new_array\t %s, %s, %d\n", location, registerName(exec, dst).c_str(), registerName(exec, argv).c_str(), argc); break; } case op_new_regexp: { int r0 = (++it)->u.operand; int re0 = (++it)->u.operand; - printf("[%4d] new_regexp\t %s, %s\n", location, registerName(r0).c_str(), regexpName(re0, regexp(re0)).c_str()); + printf("[%4d] new_regexp\t %s, %s\n", location, registerName(exec, r0).c_str(), regexpName(re0, regexp(re0)).c_str()); break; } case op_mov: { int r0 = (++it)->u.operand; int r1 = (++it)->u.operand; - printf("[%4d] mov\t\t %s, %s\n", location, registerName(r0).c_str(), registerName(r1).c_str()); + printf("[%4d] mov\t\t %s, %s\n", location, registerName(exec, r0).c_str(), registerName(exec, r1).c_str()); break; } case op_not: { - printUnaryOp(location, it, "not"); + printUnaryOp(exec, location, it, "not"); break; } case op_eq: { - printBinaryOp(location, it, "eq"); + printBinaryOp(exec, location, it, "eq"); break; } case op_eq_null: { - printUnaryOp(location, it, "eq_null"); + printUnaryOp(exec, location, it, "eq_null"); break; } case op_neq: { - printBinaryOp(location, it, "neq"); + printBinaryOp(exec, location, it, "neq"); break; } case op_neq_null: { - printUnaryOp(location, it, "neq_null"); + printUnaryOp(exec, location, it, "neq_null"); break; } case op_stricteq: { - printBinaryOp(location, it, "stricteq"); + printBinaryOp(exec, location, it, "stricteq"); break; } case op_nstricteq: { - printBinaryOp(location, it, "nstricteq"); + printBinaryOp(exec, location, it, "nstricteq"); break; } case op_less: { - printBinaryOp(location, it, "less"); + printBinaryOp(exec, location, it, "less"); break; } case op_lesseq: { - printBinaryOp(location, it, "lesseq"); + printBinaryOp(exec, location, it, "lesseq"); break; } case op_pre_inc: { int r0 = (++it)->u.operand; - printf("[%4d] pre_inc\t\t %s\n", location, registerName(r0).c_str()); + printf("[%4d] pre_inc\t\t %s\n", location, registerName(exec, r0).c_str()); break; } case op_pre_dec: { int r0 = (++it)->u.operand; - printf("[%4d] pre_dec\t\t %s\n", location, registerName(r0).c_str()); + printf("[%4d] pre_dec\t\t %s\n", location, registerName(exec, r0).c_str()); break; } case op_post_inc: { - printUnaryOp(location, it, "post_inc"); + printUnaryOp(exec, location, it, "post_inc"); break; } case op_post_dec: { - printUnaryOp(location, it, "post_dec"); + printUnaryOp(exec, location, it, "post_dec"); break; } case op_to_jsnumber: { - printUnaryOp(location, it, "to_jsnumber"); + printUnaryOp(exec, location, it, "to_jsnumber"); break; } case op_negate: { - printUnaryOp(location, it, "negate"); + printUnaryOp(exec, location, it, "negate"); break; } case op_add: { - printBinaryOp(location, it, "add"); + printBinaryOp(exec, location, it, "add"); ++it; break; } case op_mul: { - printBinaryOp(location, it, "mul"); + printBinaryOp(exec, location, it, "mul"); ++it; break; } case op_div: { - printBinaryOp(location, it, "div"); + printBinaryOp(exec, location, it, "div"); ++it; break; } case op_mod: { - printBinaryOp(location, it, "mod"); + printBinaryOp(exec, location, it, "mod"); break; } case op_sub: { - printBinaryOp(location, it, "sub"); + printBinaryOp(exec, location, it, "sub"); ++it; break; } case op_lshift: { - printBinaryOp(location, it, "lshift"); + printBinaryOp(exec, location, it, "lshift"); break; } case op_rshift: { - printBinaryOp(location, it, "rshift"); + printBinaryOp(exec, location, it, "rshift"); break; } case op_urshift: { - printBinaryOp(location, it, "urshift"); + printBinaryOp(exec, location, it, "urshift"); break; } case op_bitand: { - printBinaryOp(location, it, "bitand"); + printBinaryOp(exec, location, it, "bitand"); ++it; break; } case op_bitxor: { - printBinaryOp(location, it, "bitxor"); + printBinaryOp(exec, location, it, "bitxor"); ++it; break; } case op_bitor: { - printBinaryOp(location, it, "bitor"); + printBinaryOp(exec, location, it, "bitor"); ++it; break; } case op_bitnot: { - printUnaryOp(location, it, "bitnot"); + printUnaryOp(exec, location, it, "bitnot"); break; } case op_instanceof: { @@ -643,59 +646,59 @@ void CodeBlock::dump(ExecState* exec, const Vector<Instruction>::const_iterator& int r1 = (++it)->u.operand; int r2 = (++it)->u.operand; int r3 = (++it)->u.operand; - printf("[%4d] instanceof\t\t %s, %s, %s, %s\n", location, registerName(r0).c_str(), registerName(r1).c_str(), registerName(r2).c_str(), registerName(r3).c_str()); + printf("[%4d] instanceof\t\t %s, %s, %s, %s\n", location, registerName(exec, r0).c_str(), registerName(exec, r1).c_str(), registerName(exec, r2).c_str(), registerName(exec, r3).c_str()); break; } case op_typeof: { - printUnaryOp(location, it, "typeof"); + printUnaryOp(exec, location, it, "typeof"); break; } case op_is_undefined: { - printUnaryOp(location, it, "is_undefined"); + printUnaryOp(exec, location, it, "is_undefined"); break; } case op_is_boolean: { - printUnaryOp(location, it, "is_boolean"); + printUnaryOp(exec, location, it, "is_boolean"); break; } case op_is_number: { - printUnaryOp(location, it, "is_number"); + printUnaryOp(exec, location, it, "is_number"); break; } case op_is_string: { - printUnaryOp(location, it, "is_string"); + printUnaryOp(exec, location, it, "is_string"); break; } case op_is_object: { - printUnaryOp(location, it, "is_object"); + printUnaryOp(exec, location, it, "is_object"); break; } case op_is_function: { - printUnaryOp(location, it, "is_function"); + printUnaryOp(exec, location, it, "is_function"); break; } case op_in: { - printBinaryOp(location, it, "in"); + printBinaryOp(exec, location, it, "in"); break; } case op_resolve: { int r0 = (++it)->u.operand; int id0 = (++it)->u.operand; - printf("[%4d] resolve\t\t %s, %s\n", location, registerName(r0).c_str(), idName(id0, m_identifiers[id0]).c_str()); + printf("[%4d] resolve\t\t %s, %s\n", location, registerName(exec, r0).c_str(), idName(id0, m_identifiers[id0]).c_str()); break; } case op_resolve_skip: { int r0 = (++it)->u.operand; int id0 = (++it)->u.operand; int skipLevels = (++it)->u.operand; - printf("[%4d] resolve_skip\t %s, %s, %d\n", location, registerName(r0).c_str(), idName(id0, m_identifiers[id0]).c_str(), skipLevels); + printf("[%4d] resolve_skip\t %s, %s, %d\n", location, registerName(exec, r0).c_str(), idName(id0, m_identifiers[id0]).c_str(), skipLevels); break; } case op_resolve_global: { int r0 = (++it)->u.operand; JSValue scope = JSValue((++it)->u.jsCell); int id0 = (++it)->u.operand; - printf("[%4d] resolve_global\t %s, %s, %s\n", location, registerName(r0).c_str(), valueToSourceString(exec, scope).ascii(), idName(id0, m_identifiers[id0]).c_str()); + printf("[%4d] resolve_global\t %s, %s, %s\n", location, registerName(exec, r0).c_str(), valueToSourceString(exec, scope).ascii(), idName(id0, m_identifiers[id0]).c_str()); it += 2; break; } @@ -703,125 +706,125 @@ void CodeBlock::dump(ExecState* exec, const Vector<Instruction>::const_iterator& int r0 = (++it)->u.operand; int index = (++it)->u.operand; int skipLevels = (++it)->u.operand; - printf("[%4d] get_scoped_var\t %s, %d, %d\n", location, registerName(r0).c_str(), index, skipLevels); + printf("[%4d] get_scoped_var\t %s, %d, %d\n", location, registerName(exec, r0).c_str(), index, skipLevels); break; } case op_put_scoped_var: { int index = (++it)->u.operand; int skipLevels = (++it)->u.operand; int r0 = (++it)->u.operand; - printf("[%4d] put_scoped_var\t %d, %d, %s\n", location, index, skipLevels, registerName(r0).c_str()); + printf("[%4d] put_scoped_var\t %d, %d, %s\n", location, index, skipLevels, registerName(exec, r0).c_str()); break; } case op_get_global_var: { int r0 = (++it)->u.operand; JSValue scope = JSValue((++it)->u.jsCell); int index = (++it)->u.operand; - printf("[%4d] get_global_var\t %s, %s, %d\n", location, registerName(r0).c_str(), valueToSourceString(exec, scope).ascii(), index); + printf("[%4d] get_global_var\t %s, %s, %d\n", location, registerName(exec, r0).c_str(), valueToSourceString(exec, scope).ascii(), index); break; } case op_put_global_var: { JSValue scope = JSValue((++it)->u.jsCell); int index = (++it)->u.operand; int r0 = (++it)->u.operand; - printf("[%4d] put_global_var\t %s, %d, %s\n", location, valueToSourceString(exec, scope).ascii(), index, registerName(r0).c_str()); + printf("[%4d] put_global_var\t %s, %d, %s\n", location, valueToSourceString(exec, scope).ascii(), index, registerName(exec, r0).c_str()); break; } case op_resolve_base: { int r0 = (++it)->u.operand; int id0 = (++it)->u.operand; - printf("[%4d] resolve_base\t %s, %s\n", location, registerName(r0).c_str(), idName(id0, m_identifiers[id0]).c_str()); + printf("[%4d] resolve_base\t %s, %s\n", location, registerName(exec, r0).c_str(), idName(id0, m_identifiers[id0]).c_str()); break; } case op_resolve_with_base: { int r0 = (++it)->u.operand; int r1 = (++it)->u.operand; int id0 = (++it)->u.operand; - printf("[%4d] resolve_with_base %s, %s, %s\n", location, registerName(r0).c_str(), registerName(r1).c_str(), idName(id0, m_identifiers[id0]).c_str()); + printf("[%4d] resolve_with_base %s, %s, %s\n", location, registerName(exec, r0).c_str(), registerName(exec, r1).c_str(), idName(id0, m_identifiers[id0]).c_str()); break; } case op_get_by_id: { - printGetByIdOp(location, it, m_identifiers, "get_by_id"); + printGetByIdOp(exec, location, it, "get_by_id"); break; } case op_get_by_id_self: { - printGetByIdOp(location, it, m_identifiers, "get_by_id_self"); + printGetByIdOp(exec, location, it, "get_by_id_self"); break; } case op_get_by_id_self_list: { - printGetByIdOp(location, it, m_identifiers, "get_by_id_self_list"); + printGetByIdOp(exec, location, it, "get_by_id_self_list"); break; } case op_get_by_id_proto: { - printGetByIdOp(location, it, m_identifiers, "get_by_id_proto"); + printGetByIdOp(exec, location, it, "get_by_id_proto"); break; } case op_get_by_id_proto_list: { - printGetByIdOp(location, it, m_identifiers, "op_get_by_id_proto_list"); + printGetByIdOp(exec, location, it, "op_get_by_id_proto_list"); break; } case op_get_by_id_chain: { - printGetByIdOp(location, it, m_identifiers, "get_by_id_chain"); + printGetByIdOp(exec, location, it, "get_by_id_chain"); break; } case op_get_by_id_generic: { - printGetByIdOp(location, it, m_identifiers, "get_by_id_generic"); + printGetByIdOp(exec, location, it, "get_by_id_generic"); break; } case op_get_array_length: { - printGetByIdOp(location, it, m_identifiers, "get_array_length"); + printGetByIdOp(exec, location, it, "get_array_length"); break; } case op_get_string_length: { - printGetByIdOp(location, it, m_identifiers, "get_string_length"); + printGetByIdOp(exec, location, it, "get_string_length"); break; } case op_put_by_id: { - printPutByIdOp(location, it, m_identifiers, "put_by_id"); + printPutByIdOp(exec, location, it, "put_by_id"); break; } case op_put_by_id_replace: { - printPutByIdOp(location, it, m_identifiers, "put_by_id_replace"); + printPutByIdOp(exec, location, it, "put_by_id_replace"); break; } case op_put_by_id_transition: { - printPutByIdOp(location, it, m_identifiers, "put_by_id_transition"); + printPutByIdOp(exec, location, it, "put_by_id_transition"); break; } case op_put_by_id_generic: { - printPutByIdOp(location, it, m_identifiers, "put_by_id_generic"); + printPutByIdOp(exec, location, it, "put_by_id_generic"); break; } case op_put_getter: { int r0 = (++it)->u.operand; int id0 = (++it)->u.operand; int r1 = (++it)->u.operand; - printf("[%4d] put_getter\t %s, %s, %s\n", location, registerName(r0).c_str(), idName(id0, m_identifiers[id0]).c_str(), registerName(r1).c_str()); + printf("[%4d] put_getter\t %s, %s, %s\n", location, registerName(exec, r0).c_str(), idName(id0, m_identifiers[id0]).c_str(), registerName(exec, r1).c_str()); break; } case op_put_setter: { int r0 = (++it)->u.operand; int id0 = (++it)->u.operand; int r1 = (++it)->u.operand; - printf("[%4d] put_setter\t %s, %s, %s\n", location, registerName(r0).c_str(), idName(id0, m_identifiers[id0]).c_str(), registerName(r1).c_str()); + printf("[%4d] put_setter\t %s, %s, %s\n", location, registerName(exec, r0).c_str(), idName(id0, m_identifiers[id0]).c_str(), registerName(exec, r1).c_str()); break; } case op_method_check: { - printf("[%4d] op_method_check\n", location); + printf("[%4d] method_check\n", location); break; } case op_del_by_id: { int r0 = (++it)->u.operand; int r1 = (++it)->u.operand; int id0 = (++it)->u.operand; - printf("[%4d] del_by_id\t %s, %s, %s\n", location, registerName(r0).c_str(), registerName(r1).c_str(), idName(id0, m_identifiers[id0]).c_str()); + printf("[%4d] del_by_id\t %s, %s, %s\n", location, registerName(exec, r0).c_str(), registerName(exec, r1).c_str(), idName(id0, m_identifiers[id0]).c_str()); break; } case op_get_by_val: { int r0 = (++it)->u.operand; int r1 = (++it)->u.operand; int r2 = (++it)->u.operand; - printf("[%4d] get_by_val\t %s, %s, %s\n", location, registerName(r0).c_str(), registerName(r1).c_str(), registerName(r2).c_str()); + printf("[%4d] get_by_val\t %s, %s, %s\n", location, registerName(exec, r0).c_str(), registerName(exec, r1).c_str(), registerName(exec, r2).c_str()); break; } case op_get_by_pname: { @@ -831,28 +834,28 @@ void CodeBlock::dump(ExecState* exec, const Vector<Instruction>::const_iterator& int r3 = (++it)->u.operand; int r4 = (++it)->u.operand; int r5 = (++it)->u.operand; - printf("[%4d] get_by_pname\t %s, %s, %s, %s, %s, %s\n", location, registerName(r0).c_str(), registerName(r1).c_str(), registerName(r2).c_str(), registerName(r3).c_str(), registerName(r4).c_str(), registerName(r5).c_str()); + printf("[%4d] get_by_pname\t %s, %s, %s, %s, %s, %s\n", location, registerName(exec, r0).c_str(), registerName(exec, r1).c_str(), registerName(exec, r2).c_str(), registerName(exec, r3).c_str(), registerName(exec, r4).c_str(), registerName(exec, r5).c_str()); break; } case op_put_by_val: { int r0 = (++it)->u.operand; int r1 = (++it)->u.operand; int r2 = (++it)->u.operand; - printf("[%4d] put_by_val\t %s, %s, %s\n", location, registerName(r0).c_str(), registerName(r1).c_str(), registerName(r2).c_str()); + printf("[%4d] put_by_val\t %s, %s, %s\n", location, registerName(exec, r0).c_str(), registerName(exec, r1).c_str(), registerName(exec, r2).c_str()); break; } case op_del_by_val: { int r0 = (++it)->u.operand; int r1 = (++it)->u.operand; int r2 = (++it)->u.operand; - printf("[%4d] del_by_val\t %s, %s, %s\n", location, registerName(r0).c_str(), registerName(r1).c_str(), registerName(r2).c_str()); + printf("[%4d] del_by_val\t %s, %s, %s\n", location, registerName(exec, r0).c_str(), registerName(exec, r1).c_str(), registerName(exec, r2).c_str()); break; } case op_put_by_index: { int r0 = (++it)->u.operand; unsigned n0 = (++it)->u.operand; int r1 = (++it)->u.operand; - printf("[%4d] put_by_index\t %s, %u, %s\n", location, registerName(r0).c_str(), n0, registerName(r1).c_str()); + printf("[%4d] put_by_index\t %s, %u, %s\n", location, registerName(exec, r0).c_str(), n0, registerName(exec, r1).c_str()); break; } case op_jmp: { @@ -866,91 +869,102 @@ void CodeBlock::dump(ExecState* exec, const Vector<Instruction>::const_iterator& break; } case op_jtrue: { - printConditionalJump(begin, it, location, "jtrue"); + printConditionalJump(exec, begin, it, location, "jtrue"); break; } case op_loop_if_true: { - printConditionalJump(begin, it, location, "loop_if_true"); + printConditionalJump(exec, begin, it, location, "loop_if_true"); + break; + } + case op_loop_if_false: { + printConditionalJump(exec, begin, it, location, "loop_if_false"); break; } case op_jfalse: { - printConditionalJump(begin, it, location, "jfalse"); + printConditionalJump(exec, begin, it, location, "jfalse"); break; } case op_jeq_null: { - printConditionalJump(begin, it, location, "jeq_null"); + printConditionalJump(exec, begin, it, location, "jeq_null"); break; } case op_jneq_null: { - printConditionalJump(begin, it, location, "jneq_null"); + printConditionalJump(exec, begin, it, location, "jneq_null"); break; } case op_jneq_ptr: { int r0 = (++it)->u.operand; int r1 = (++it)->u.operand; int offset = (++it)->u.operand; - printf("[%4d] jneq_ptr\t\t %s, %s, %d(->%d)\n", location, registerName(r0).c_str(), registerName(r1).c_str(), offset, location + offset); + printf("[%4d] jneq_ptr\t\t %s, %s, %d(->%d)\n", location, registerName(exec, r0).c_str(), registerName(exec, r1).c_str(), offset, location + offset); break; } case op_jnless: { int r0 = (++it)->u.operand; int r1 = (++it)->u.operand; int offset = (++it)->u.operand; - printf("[%4d] jnless\t\t %s, %s, %d(->%d)\n", location, registerName(r0).c_str(), registerName(r1).c_str(), offset, location + offset); + printf("[%4d] jnless\t\t %s, %s, %d(->%d)\n", location, registerName(exec, r0).c_str(), registerName(exec, r1).c_str(), offset, location + offset); break; } case op_jnlesseq: { int r0 = (++it)->u.operand; int r1 = (++it)->u.operand; int offset = (++it)->u.operand; - printf("[%4d] jnlesseq\t\t %s, %s, %d(->%d)\n", location, registerName(r0).c_str(), registerName(r1).c_str(), offset, location + offset); + printf("[%4d] jnlesseq\t\t %s, %s, %d(->%d)\n", location, registerName(exec, r0).c_str(), registerName(exec, r1).c_str(), offset, location + offset); break; } case op_loop_if_less: { int r0 = (++it)->u.operand; int r1 = (++it)->u.operand; int offset = (++it)->u.operand; - printf("[%4d] loop_if_less\t %s, %s, %d(->%d)\n", location, registerName(r0).c_str(), registerName(r1).c_str(), offset, location + offset); + printf("[%4d] loop_if_less\t %s, %s, %d(->%d)\n", location, registerName(exec, r0).c_str(), registerName(exec, r1).c_str(), offset, location + offset); + break; + } + case op_jless: { + int r0 = (++it)->u.operand; + int r1 = (++it)->u.operand; + int offset = (++it)->u.operand; + printf("[%4d] jless\t\t %s, %s, %d(->%d)\n", location, registerName(exec, r0).c_str(), registerName(exec, r1).c_str(), offset, location + offset); break; } case op_loop_if_lesseq: { int r0 = (++it)->u.operand; int r1 = (++it)->u.operand; int offset = (++it)->u.operand; - printf("[%4d] loop_if_lesseq\t %s, %s, %d(->%d)\n", location, registerName(r0).c_str(), registerName(r1).c_str(), offset, location + offset); + printf("[%4d] loop_if_lesseq\t %s, %s, %d(->%d)\n", location, registerName(exec, r0).c_str(), registerName(exec, r1).c_str(), offset, location + offset); break; } case op_switch_imm: { int tableIndex = (++it)->u.operand; int defaultTarget = (++it)->u.operand; int scrutineeRegister = (++it)->u.operand; - printf("[%4d] switch_imm\t %d, %d(->%d), %s\n", location, tableIndex, defaultTarget, location + defaultTarget, registerName(scrutineeRegister).c_str()); + printf("[%4d] switch_imm\t %d, %d(->%d), %s\n", location, tableIndex, defaultTarget, location + defaultTarget, registerName(exec, scrutineeRegister).c_str()); break; } case op_switch_char: { int tableIndex = (++it)->u.operand; int defaultTarget = (++it)->u.operand; int scrutineeRegister = (++it)->u.operand; - printf("[%4d] switch_char\t %d, %d(->%d), %s\n", location, tableIndex, defaultTarget, location + defaultTarget, registerName(scrutineeRegister).c_str()); + printf("[%4d] switch_char\t %d, %d(->%d), %s\n", location, tableIndex, defaultTarget, location + defaultTarget, registerName(exec, scrutineeRegister).c_str()); break; } case op_switch_string: { int tableIndex = (++it)->u.operand; int defaultTarget = (++it)->u.operand; int scrutineeRegister = (++it)->u.operand; - printf("[%4d] switch_string\t %d, %d(->%d), %s\n", location, tableIndex, defaultTarget, location + defaultTarget, registerName(scrutineeRegister).c_str()); + printf("[%4d] switch_string\t %d, %d(->%d), %s\n", location, tableIndex, defaultTarget, location + defaultTarget, registerName(exec, scrutineeRegister).c_str()); break; } case op_new_func: { int r0 = (++it)->u.operand; int f0 = (++it)->u.operand; - printf("[%4d] new_func\t\t %s, f%d\n", location, registerName(r0).c_str(), f0); + printf("[%4d] new_func\t\t %s, f%d\n", location, registerName(exec, r0).c_str(), f0); break; } case op_new_func_exp: { int r0 = (++it)->u.operand; int f0 = (++it)->u.operand; - printf("[%4d] new_func_exp\t %s, f%d\n", location, registerName(r0).c_str(), f0); + printf("[%4d] new_func_exp\t %s, f%d\n", location, registerName(exec, r0).c_str(), f0); break; } case op_call: { @@ -958,7 +972,7 @@ void CodeBlock::dump(ExecState* exec, const Vector<Instruction>::const_iterator& int func = (++it)->u.operand; int argCount = (++it)->u.operand; int registerOffset = (++it)->u.operand; - printf("[%4d] call\t\t %s, %s, %d, %d\n", location, registerName(dst).c_str(), registerName(func).c_str(), argCount, registerOffset); + printf("[%4d] call\t\t %s, %s, %d, %d\n", location, registerName(exec, dst).c_str(), registerName(exec, func).c_str(), argCount, registerOffset); break; } case op_call_eval: { @@ -966,7 +980,7 @@ void CodeBlock::dump(ExecState* exec, const Vector<Instruction>::const_iterator& int func = (++it)->u.operand; int argCount = (++it)->u.operand; int registerOffset = (++it)->u.operand; - printf("[%4d] call_eval\t %s, %s, %d, %d\n", location, registerName(dst).c_str(), registerName(func).c_str(), argCount, registerOffset); + printf("[%4d] call_eval\t %s, %s, %d, %d\n", location, registerName(exec, dst).c_str(), registerName(exec, func).c_str(), argCount, registerOffset); break; } case op_call_varargs: { @@ -974,16 +988,16 @@ void CodeBlock::dump(ExecState* exec, const Vector<Instruction>::const_iterator& int func = (++it)->u.operand; int argCount = (++it)->u.operand; int registerOffset = (++it)->u.operand; - printf("[%4d] call_varargs\t %s, %s, %s, %d\n", location, registerName(dst).c_str(), registerName(func).c_str(), registerName(argCount).c_str(), registerOffset); + printf("[%4d] call_varargs\t %s, %s, %s, %d\n", location, registerName(exec, dst).c_str(), registerName(exec, func).c_str(), registerName(exec, argCount).c_str(), registerOffset); break; } case op_load_varargs: { - printUnaryOp(location, it, "load_varargs"); + printUnaryOp(exec, location, it, "load_varargs"); break; } case op_tear_off_activation: { int r0 = (++it)->u.operand; - printf("[%4d] tear_off_activation\t %s\n", location, registerName(r0).c_str()); + printf("[%4d] tear_off_activation\t %s\n", location, registerName(exec, r0).c_str()); break; } case op_tear_off_arguments: { @@ -992,7 +1006,7 @@ void CodeBlock::dump(ExecState* exec, const Vector<Instruction>::const_iterator& } case op_ret: { int r0 = (++it)->u.operand; - printf("[%4d] ret\t\t %s\n", location, registerName(r0).c_str()); + printf("[%4d] ret\t\t %s\n", location, registerName(exec, r0).c_str()); break; } case op_construct: { @@ -1002,26 +1016,26 @@ void CodeBlock::dump(ExecState* exec, const Vector<Instruction>::const_iterator& int registerOffset = (++it)->u.operand; int proto = (++it)->u.operand; int thisRegister = (++it)->u.operand; - printf("[%4d] construct\t %s, %s, %d, %d, %s, %s\n", location, registerName(dst).c_str(), registerName(func).c_str(), argCount, registerOffset, registerName(proto).c_str(), registerName(thisRegister).c_str()); + printf("[%4d] construct\t %s, %s, %d, %d, %s, %s\n", location, registerName(exec, dst).c_str(), registerName(exec, func).c_str(), argCount, registerOffset, registerName(exec, proto).c_str(), registerName(exec, thisRegister).c_str()); break; } case op_construct_verify: { int r0 = (++it)->u.operand; int r1 = (++it)->u.operand; - printf("[%4d] construct_verify\t %s, %s\n", location, registerName(r0).c_str(), registerName(r1).c_str()); + printf("[%4d] construct_verify\t %s, %s\n", location, registerName(exec, r0).c_str(), registerName(exec, r1).c_str()); break; } case op_strcat: { int r0 = (++it)->u.operand; int r1 = (++it)->u.operand; int count = (++it)->u.operand; - printf("[%4d] op_strcat\t %s, %s, %d\n", location, registerName(r0).c_str(), registerName(r1).c_str(), count); + printf("[%4d] strcat\t\t %s, %s, %d\n", location, registerName(exec, r0).c_str(), registerName(exec, r1).c_str(), count); break; } case op_to_primitive: { int r0 = (++it)->u.operand; int r1 = (++it)->u.operand; - printf("[%4d] op_to_primitive\t %s, %s\n", location, registerName(r0).c_str(), registerName(r1).c_str()); + printf("[%4d] to_primitive\t %s, %s\n", location, registerName(exec, r0).c_str(), registerName(exec, r1).c_str()); break; } case op_get_pnames: { @@ -1030,7 +1044,7 @@ void CodeBlock::dump(ExecState* exec, const Vector<Instruction>::const_iterator& int r2 = it[3].u.operand; int r3 = it[4].u.operand; int offset = it[5].u.operand; - printf("[%4d] get_pnames\t %s, %s, %s, %s, %d(->%d)\n", location, registerName(r0).c_str(), registerName(r1).c_str(), registerName(r2).c_str(), registerName(r3).c_str(), offset, location + offset); + printf("[%4d] get_pnames\t %s, %s, %s, %s, %d(->%d)\n", location, registerName(exec, r0).c_str(), registerName(exec, r1).c_str(), registerName(exec, r2).c_str(), registerName(exec, r3).c_str(), offset, location + offset); it += OPCODE_LENGTH(op_get_pnames) - 1; break; } @@ -1038,13 +1052,13 @@ void CodeBlock::dump(ExecState* exec, const Vector<Instruction>::const_iterator& int dest = it[1].u.operand; int iter = it[4].u.operand; int offset = it[5].u.operand; - printf("[%4d] next_pname\t %s, %s, %d(->%d)\n", location, registerName(dest).c_str(), registerName(iter).c_str(), offset, location + offset); + printf("[%4d] next_pname\t %s, %s, %d(->%d)\n", location, registerName(exec, dest).c_str(), registerName(exec, iter).c_str(), offset, location + offset); it += OPCODE_LENGTH(op_next_pname) - 1; break; } case op_push_scope: { int r0 = (++it)->u.operand; - printf("[%4d] push_scope\t %s\n", location, registerName(r0).c_str()); + printf("[%4d] push_scope\t %s\n", location, registerName(exec, r0).c_str()); break; } case op_pop_scope: { @@ -1055,7 +1069,7 @@ void CodeBlock::dump(ExecState* exec, const Vector<Instruction>::const_iterator& int r0 = (++it)->u.operand; int id0 = (++it)->u.operand; int r1 = (++it)->u.operand; - printf("[%4d] push_new_scope \t%s, %s, %s\n", location, registerName(r0).c_str(), idName(id0, m_identifiers[id0]).c_str(), registerName(r1).c_str()); + printf("[%4d] push_new_scope \t%s, %s, %s\n", location, registerName(exec, r0).c_str(), idName(id0, m_identifiers[id0]).c_str(), registerName(exec, r1).c_str()); break; } case op_jmp_scopes: { @@ -1066,30 +1080,30 @@ void CodeBlock::dump(ExecState* exec, const Vector<Instruction>::const_iterator& } case op_catch: { int r0 = (++it)->u.operand; - printf("[%4d] catch\t\t %s\n", location, registerName(r0).c_str()); + printf("[%4d] catch\t\t %s\n", location, registerName(exec, r0).c_str()); break; } case op_throw: { int r0 = (++it)->u.operand; - printf("[%4d] throw\t\t %s\n", location, registerName(r0).c_str()); + printf("[%4d] throw\t\t %s\n", location, registerName(exec, r0).c_str()); break; } case op_new_error: { int r0 = (++it)->u.operand; int errorType = (++it)->u.operand; int k0 = (++it)->u.operand; - printf("[%4d] new_error\t %s, %d, %s\n", location, registerName(r0).c_str(), errorType, constantName(exec, k0, getConstant(k0)).c_str()); + printf("[%4d] new_error\t %s, %d, %s\n", location, registerName(exec, r0).c_str(), errorType, constantName(exec, k0, getConstant(k0)).c_str()); break; } case op_jsr: { int retAddrDst = (++it)->u.operand; int offset = (++it)->u.operand; - printf("[%4d] jsr\t\t %s, %d(->%d)\n", location, registerName(retAddrDst).c_str(), offset, location + offset); + printf("[%4d] jsr\t\t %s, %d(->%d)\n", location, registerName(exec, retAddrDst).c_str(), offset, location + offset); break; } case op_sret: { int retAddrSrc = (++it)->u.operand; - printf("[%4d] sret\t\t %s\n", location, registerName(retAddrSrc).c_str()); + printf("[%4d] sret\t\t %s\n", location, registerName(exec, retAddrSrc).c_str()); break; } case op_debug: { @@ -1101,17 +1115,17 @@ void CodeBlock::dump(ExecState* exec, const Vector<Instruction>::const_iterator& } case op_profile_will_call: { int function = (++it)->u.operand; - printf("[%4d] profile_will_call %s\n", location, registerName(function).c_str()); + printf("[%4d] profile_will_call %s\n", location, registerName(exec, function).c_str()); break; } case op_profile_did_call: { int function = (++it)->u.operand; - printf("[%4d] profile_did_call\t %s\n", location, registerName(function).c_str()); + printf("[%4d] profile_did_call\t %s\n", location, registerName(exec, function).c_str()); break; } case op_end: { int r0 = (++it)->u.operand; - printf("[%4d] end\t\t %s\n", location, registerName(r0).c_str()); + printf("[%4d] end\t\t %s\n", location, registerName(exec, r0).c_str()); break; } } diff --git a/JavaScriptCore/bytecode/CodeBlock.h b/JavaScriptCore/bytecode/CodeBlock.h index 4ba58d7..eb874cc 100644 --- a/JavaScriptCore/bytecode/CodeBlock.h +++ b/JavaScriptCore/bytecode/CodeBlock.h @@ -438,7 +438,7 @@ namespace JSC { size_t numberOfConstantRegisters() const { return m_constantRegisters.size(); } void addConstantRegister(const Register& r) { return m_constantRegisters.append(r); } Register& constantRegister(int index) { return m_constantRegisters[index - FirstConstantRegisterIndex]; } - ALWAYS_INLINE bool isConstantRegisterIndex(int index) { return index >= FirstConstantRegisterIndex; } + ALWAYS_INLINE bool isConstantRegisterIndex(int index) const { return index >= FirstConstantRegisterIndex; } ALWAYS_INLINE JSValue getConstant(int index) const { return m_constantRegisters[index - FirstConstantRegisterIndex].jsValue(); } unsigned addFunctionDecl(NonNullPassRefPtr<FunctionExecutable> n) { unsigned size = m_functionDecls.size(); m_functionDecls.append(n); return size; } @@ -482,6 +482,13 @@ namespace JSC { private: #if !defined(NDEBUG) || ENABLE(OPCODE_SAMPLING) void dump(ExecState*, const Vector<Instruction>::const_iterator& begin, Vector<Instruction>::const_iterator&) const; + + CString registerName(ExecState*, int r) const; + void printUnaryOp(ExecState*, int location, Vector<Instruction>::const_iterator&, const char* op) const; + void printBinaryOp(ExecState*, int location, Vector<Instruction>::const_iterator&, const char* op) const; + void printConditionalJump(ExecState*, const Vector<Instruction>::const_iterator&, Vector<Instruction>::const_iterator&, int location, const char* op) const; + void printGetByIdOp(ExecState*, int location, Vector<Instruction>::const_iterator&, const char* op) const; + void printPutByIdOp(ExecState*, int location, Vector<Instruction>::const_iterator&, const char* op) const; #endif void reparseForExceptionInfoIfNecessary(CallFrame*); diff --git a/JavaScriptCore/bytecode/Opcode.h b/JavaScriptCore/bytecode/Opcode.h index 4facbef..e88f051 100644 --- a/JavaScriptCore/bytecode/Opcode.h +++ b/JavaScriptCore/bytecode/Opcode.h @@ -128,9 +128,11 @@ namespace JSC { macro(op_jneq_ptr, 4) \ macro(op_jnless, 4) \ macro(op_jnlesseq, 4) \ + macro(op_jless, 4) \ macro(op_jmp_scopes, 3) \ macro(op_loop, 2) \ macro(op_loop_if_true, 3) \ + macro(op_loop_if_false, 3) \ macro(op_loop_if_less, 4) \ macro(op_loop_if_lesseq, 4) \ macro(op_switch_imm, 4) \ diff --git a/JavaScriptCore/bytecompiler/BytecodeGenerator.cpp b/JavaScriptCore/bytecompiler/BytecodeGenerator.cpp index 04dae15..b0a0877 100644 --- a/JavaScriptCore/bytecompiler/BytecodeGenerator.cpp +++ b/JavaScriptCore/bytecompiler/BytecodeGenerator.cpp @@ -616,7 +616,7 @@ PassRefPtr<Label> BytecodeGenerator::emitJump(Label* target) PassRefPtr<Label> BytecodeGenerator::emitJumpIfTrue(RegisterID* cond, Label* target) { - if (m_lastOpcodeID == op_less && !target->isForward()) { + if (m_lastOpcodeID == op_less) { int dstIndex; int src1Index; int src2Index; @@ -627,7 +627,7 @@ PassRefPtr<Label> BytecodeGenerator::emitJumpIfTrue(RegisterID* cond, Label* tar rewindBinaryOp(); size_t begin = instructions().size(); - emitOpcode(op_loop_if_less); + emitOpcode(target->isForward() ? op_jless : op_loop_if_less); instructions().append(src1Index); instructions().append(src2Index); instructions().append(target->bind(begin, instructions().size())); @@ -692,9 +692,7 @@ PassRefPtr<Label> BytecodeGenerator::emitJumpIfTrue(RegisterID* cond, Label* tar PassRefPtr<Label> BytecodeGenerator::emitJumpIfFalse(RegisterID* cond, Label* target) { - ASSERT(target->isForward()); - - if (m_lastOpcodeID == op_less) { + if (m_lastOpcodeID == op_less && target->isForward()) { int dstIndex; int src1Index; int src2Index; @@ -711,7 +709,7 @@ PassRefPtr<Label> BytecodeGenerator::emitJumpIfFalse(RegisterID* cond, Label* ta instructions().append(target->bind(begin, instructions().size())); return target; } - } else if (m_lastOpcodeID == op_lesseq) { + } else if (m_lastOpcodeID == op_lesseq && target->isForward()) { int dstIndex; int src1Index; int src2Index; @@ -738,12 +736,12 @@ PassRefPtr<Label> BytecodeGenerator::emitJumpIfFalse(RegisterID* cond, Label* ta rewindUnaryOp(); size_t begin = instructions().size(); - emitOpcode(op_jtrue); + emitOpcode(target->isForward() ? op_jtrue : op_loop_if_true); instructions().append(srcIndex); instructions().append(target->bind(begin, instructions().size())); return target; } - } else if (m_lastOpcodeID == op_eq_null) { + } else if (m_lastOpcodeID == op_eq_null && target->isForward()) { int dstIndex; int srcIndex; @@ -758,7 +756,7 @@ PassRefPtr<Label> BytecodeGenerator::emitJumpIfFalse(RegisterID* cond, Label* ta instructions().append(target->bind(begin, instructions().size())); return target; } - } else if (m_lastOpcodeID == op_neq_null) { + } else if (m_lastOpcodeID == op_neq_null && target->isForward()) { int dstIndex; int srcIndex; @@ -776,7 +774,7 @@ PassRefPtr<Label> BytecodeGenerator::emitJumpIfFalse(RegisterID* cond, Label* ta } size_t begin = instructions().size(); - emitOpcode(op_jfalse); + emitOpcode(target->isForward() ? op_jfalse : op_loop_if_false); instructions().append(cond->index()); instructions().append(target->bind(begin, instructions().size())); return target; @@ -906,7 +904,7 @@ RegisterID* BytecodeGenerator::emitEqualityOp(OpcodeID opcodeID, RegisterID* dst && src1->isTemporary() && m_codeBlock->isConstantRegisterIndex(src2->index()) && m_codeBlock->constantRegister(src2->index()).jsValue().isString()) { - const UString& value = asString(m_codeBlock->constantRegister(src2->index()).jsValue())->value(); + const UString& value = asString(m_codeBlock->constantRegister(src2->index()).jsValue())->tryGetValue(); if (value == "undefined") { rewindUnaryOp(); emitOpcode(op_is_undefined); diff --git a/JavaScriptCore/bytecompiler/BytecodeGenerator.h b/JavaScriptCore/bytecompiler/BytecodeGenerator.h index 4648fb5..8b6a425 100644 --- a/JavaScriptCore/bytecompiler/BytecodeGenerator.h +++ b/JavaScriptCore/bytecompiler/BytecodeGenerator.h @@ -192,6 +192,19 @@ namespace JSC { return emitNode(0, n); } + void emitNodeInConditionContext(ExpressionNode* n, Label* trueTarget, Label* falseTarget, bool fallThroughMeansTrue) + { + if (!m_codeBlock->numberOfLineInfos() || m_codeBlock->lastLineInfo().lineNumber != n->lineNo()) { + LineInfo info = { instructions().size(), n->lineNo() }; + m_codeBlock->addLineInfo(info); + } + if (m_emitNodeDepth >= s_maxEmitNodeDepth) + emitThrowExpressionTooDeepException(); + ++m_emitNodeDepth; + n->emitBytecodeInConditionContext(*this, trueTarget, falseTarget, fallThroughMeansTrue); + --m_emitNodeDepth; + } + void emitExpressionInfo(unsigned divot, unsigned startOffset, unsigned endOffset) { divot -= m_codeBlock->sourceOffset(); diff --git a/JavaScriptCore/bytecompiler/NodesCodegen.cpp b/JavaScriptCore/bytecompiler/NodesCodegen.cpp new file mode 100644 index 0000000..80a7a2f --- /dev/null +++ b/JavaScriptCore/bytecompiler/NodesCodegen.cpp @@ -0,0 +1,2002 @@ +/* +* Copyright (C) 1999-2002 Harri Porten (porten@kde.org) +* Copyright (C) 2001 Peter Kelly (pmk@post.com) +* Copyright (C) 2003, 2004, 2005, 2006, 2007, 2008, 2009 Apple Inc. All rights reserved. +* Copyright (C) 2007 Cameron Zwarich (cwzwarich@uwaterloo.ca) +* Copyright (C) 2007 Maks Orlovich +* Copyright (C) 2007 Eric Seidel <eric@webkit.org> +* +* This library is free software; you can redistribute it and/or +* modify it under the terms of the GNU Library General Public +* License as published by the Free Software Foundation; either +* version 2 of the License, or (at your option) any later version. +* +* This library is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +* Library General Public License for more details. +* +* You should have received a copy of the GNU Library General Public License +* along with this library; see the file COPYING.LIB. If not, write to +* the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, +* Boston, MA 02110-1301, USA. +* +*/ + +#include "config.h" +#include "Nodes.h" +#include "NodeConstructors.h" + +#include "BytecodeGenerator.h" +#include "CallFrame.h" +#include "Debugger.h" +#include "JIT.h" +#include "JSFunction.h" +#include "JSGlobalObject.h" +#include "JSStaticScopeObject.h" +#include "LabelScope.h" +#include "Lexer.h" +#include "Operations.h" +#include "Parser.h" +#include "PropertyNameArray.h" +#include "RegExpObject.h" +#include "SamplingTool.h" +#include <wtf/Assertions.h> +#include <wtf/RefCountedLeakCounter.h> +#include <wtf/Threading.h> + +using namespace WTF; + +namespace JSC { + +/* + Details of the emitBytecode function. + + Return value: The register holding the production's value. + dst: An optional parameter specifying the most efficient destination at + which to store the production's value. The callee must honor dst. + + The dst argument provides for a crude form of copy propagation. For example, + + x = 1 + + becomes + + load r[x], 1 + + instead of + + load r0, 1 + mov r[x], r0 + + because the assignment node, "x =", passes r[x] as dst to the number node, "1". +*/ + +// ------------------------------ ThrowableExpressionData -------------------------------- + +static void substitute(UString& string, const UString& substring) +{ + int position = string.find("%s"); + ASSERT(position != -1); + UString newString = string.substr(0, position); + newString.append(substring); + newString.append(string.substr(position + 2)); + string = newString; +} + +RegisterID* ThrowableExpressionData::emitThrowError(BytecodeGenerator& generator, ErrorType type, const char* message) +{ + generator.emitExpressionInfo(divot(), startOffset(), endOffset()); + RegisterID* exception = generator.emitNewError(generator.newTemporary(), type, jsString(generator.globalData(), message)); + generator.emitThrow(exception); + return exception; +} + +RegisterID* ThrowableExpressionData::emitThrowError(BytecodeGenerator& generator, ErrorType type, const char* messageTemplate, const UString& label) +{ + UString message = messageTemplate; + substitute(message, label); + generator.emitExpressionInfo(divot(), startOffset(), endOffset()); + RegisterID* exception = generator.emitNewError(generator.newTemporary(), type, jsString(generator.globalData(), message)); + generator.emitThrow(exception); + return exception; +} + +inline RegisterID* ThrowableExpressionData::emitThrowError(BytecodeGenerator& generator, ErrorType type, const char* messageTemplate, const Identifier& label) +{ + return emitThrowError(generator, type, messageTemplate, label.ustring()); +} + +// ------------------------------ NullNode ------------------------------------- + +RegisterID* NullNode::emitBytecode(BytecodeGenerator& generator, RegisterID* dst) +{ + if (dst == generator.ignoredResult()) + return 0; + return generator.emitLoad(dst, jsNull()); +} + +// ------------------------------ BooleanNode ---------------------------------- + +RegisterID* BooleanNode::emitBytecode(BytecodeGenerator& generator, RegisterID* dst) +{ + if (dst == generator.ignoredResult()) + return 0; + return generator.emitLoad(dst, m_value); +} + +// ------------------------------ NumberNode ----------------------------------- + +RegisterID* NumberNode::emitBytecode(BytecodeGenerator& generator, RegisterID* dst) +{ + if (dst == generator.ignoredResult()) + return 0; + return generator.emitLoad(dst, m_value); +} + +// ------------------------------ StringNode ----------------------------------- + +RegisterID* StringNode::emitBytecode(BytecodeGenerator& generator, RegisterID* dst) +{ + if (dst == generator.ignoredResult()) + return 0; + return generator.emitLoad(dst, m_value); +} + +// ------------------------------ RegExpNode ----------------------------------- + +RegisterID* RegExpNode::emitBytecode(BytecodeGenerator& generator, RegisterID* dst) +{ + RefPtr<RegExp> regExp = RegExp::create(generator.globalData(), m_pattern.ustring(), m_flags.ustring()); + if (!regExp->isValid()) + return emitThrowError(generator, SyntaxError, "Invalid regular expression: %s", regExp->errorMessage()); + if (dst == generator.ignoredResult()) + return 0; + return generator.emitNewRegExp(generator.finalDestination(dst), regExp.get()); +} + +// ------------------------------ ThisNode ------------------------------------- + +RegisterID* ThisNode::emitBytecode(BytecodeGenerator& generator, RegisterID* dst) +{ + if (dst == generator.ignoredResult()) + return 0; + return generator.moveToDestinationIfNeeded(dst, generator.thisRegister()); +} + +// ------------------------------ ResolveNode ---------------------------------- + +bool ResolveNode::isPure(BytecodeGenerator& generator) const +{ + return generator.isLocal(m_ident); +} + +RegisterID* ResolveNode::emitBytecode(BytecodeGenerator& generator, RegisterID* dst) +{ + if (RegisterID* local = generator.registerFor(m_ident)) { + if (dst == generator.ignoredResult()) + return 0; + return generator.moveToDestinationIfNeeded(dst, local); + } + + generator.emitExpressionInfo(m_startOffset + m_ident.size(), m_ident.size(), 0); + return generator.emitResolve(generator.finalDestination(dst), m_ident); +} + +// ------------------------------ ArrayNode ------------------------------------ + +RegisterID* ArrayNode::emitBytecode(BytecodeGenerator& generator, RegisterID* dst) +{ + // FIXME: Should we put all of this code into emitNewArray? + + unsigned length = 0; + ElementNode* firstPutElement; + for (firstPutElement = m_element; firstPutElement; firstPutElement = firstPutElement->next()) { + if (firstPutElement->elision()) + break; + ++length; + } + + if (!firstPutElement && !m_elision) + return generator.emitNewArray(generator.finalDestination(dst), m_element); + + RefPtr<RegisterID> array = generator.emitNewArray(generator.tempDestination(dst), m_element); + + for (ElementNode* n = firstPutElement; n; n = n->next()) { + RegisterID* value = generator.emitNode(n->value()); + length += n->elision(); + generator.emitPutByIndex(array.get(), length++, value); + } + + if (m_elision) { + RegisterID* value = generator.emitLoad(0, jsNumber(generator.globalData(), m_elision + length)); + generator.emitPutById(array.get(), generator.propertyNames().length, value); + } + + return generator.moveToDestinationIfNeeded(dst, array.get()); +} + +bool ArrayNode::isSimpleArray() const +{ + if (m_elision || m_optional) + return false; + for (ElementNode* ptr = m_element; ptr; ptr = ptr->next()) { + if (ptr->elision()) + return false; + } + return true; +} + +ArgumentListNode* ArrayNode::toArgumentList(JSGlobalData* globalData) const +{ + ASSERT(!m_elision && !m_optional); + ElementNode* ptr = m_element; + if (!ptr) + return 0; + ArgumentListNode* head = new (globalData) ArgumentListNode(globalData, ptr->value()); + ArgumentListNode* tail = head; + ptr = ptr->next(); + for (; ptr; ptr = ptr->next()) { + ASSERT(!ptr->elision()); + tail = new (globalData) ArgumentListNode(globalData, tail, ptr->value()); + } + return head; +} + +// ------------------------------ ObjectLiteralNode ---------------------------- + +RegisterID* ObjectLiteralNode::emitBytecode(BytecodeGenerator& generator, RegisterID* dst) +{ + if (!m_list) { + if (dst == generator.ignoredResult()) + return 0; + return generator.emitNewObject(generator.finalDestination(dst)); + } + return generator.emitNode(dst, m_list); +} + +// ------------------------------ PropertyListNode ----------------------------- + +RegisterID* PropertyListNode::emitBytecode(BytecodeGenerator& generator, RegisterID* dst) +{ + RefPtr<RegisterID> newObj = generator.tempDestination(dst); + + generator.emitNewObject(newObj.get()); + + for (PropertyListNode* p = this; p; p = p->m_next) { + RegisterID* value = generator.emitNode(p->m_node->m_assign); + + switch (p->m_node->m_type) { + case PropertyNode::Constant: { + generator.emitPutById(newObj.get(), p->m_node->name(), value); + break; + } + case PropertyNode::Getter: { + generator.emitPutGetter(newObj.get(), p->m_node->name(), value); + break; + } + case PropertyNode::Setter: { + generator.emitPutSetter(newObj.get(), p->m_node->name(), value); + break; + } + default: + ASSERT_NOT_REACHED(); + } + } + + return generator.moveToDestinationIfNeeded(dst, newObj.get()); +} + +// ------------------------------ BracketAccessorNode -------------------------------- + +RegisterID* BracketAccessorNode::emitBytecode(BytecodeGenerator& generator, RegisterID* dst) +{ + RefPtr<RegisterID> base = generator.emitNodeForLeftHandSide(m_base, m_subscriptHasAssignments, m_subscript->isPure(generator)); + RegisterID* property = generator.emitNode(m_subscript); + generator.emitExpressionInfo(divot(), startOffset(), endOffset()); + return generator.emitGetByVal(generator.finalDestination(dst), base.get(), property); +} + +// ------------------------------ DotAccessorNode -------------------------------- + +RegisterID* DotAccessorNode::emitBytecode(BytecodeGenerator& generator, RegisterID* dst) +{ + RegisterID* base = generator.emitNode(m_base); + generator.emitExpressionInfo(divot(), startOffset(), endOffset()); + return generator.emitGetById(generator.finalDestination(dst), base, m_ident); +} + +// ------------------------------ ArgumentListNode ----------------------------- + +RegisterID* ArgumentListNode::emitBytecode(BytecodeGenerator& generator, RegisterID* dst) +{ + ASSERT(m_expr); + return generator.emitNode(dst, m_expr); +} + +// ------------------------------ NewExprNode ---------------------------------- + +RegisterID* NewExprNode::emitBytecode(BytecodeGenerator& generator, RegisterID* dst) +{ + RefPtr<RegisterID> func = generator.emitNode(m_expr); + return generator.emitConstruct(generator.finalDestination(dst), func.get(), m_args, divot(), startOffset(), endOffset()); +} + +// ------------------------------ EvalFunctionCallNode ---------------------------------- + +RegisterID* EvalFunctionCallNode::emitBytecode(BytecodeGenerator& generator, RegisterID* dst) +{ + RefPtr<RegisterID> func = generator.tempDestination(dst); + RefPtr<RegisterID> thisRegister = generator.newTemporary(); + generator.emitExpressionInfo(divot() - startOffset() + 4, 4, 0); + generator.emitResolveWithBase(thisRegister.get(), func.get(), generator.propertyNames().eval); + return generator.emitCallEval(generator.finalDestination(dst, func.get()), func.get(), thisRegister.get(), m_args, divot(), startOffset(), endOffset()); +} + +// ------------------------------ FunctionCallValueNode ---------------------------------- + +RegisterID* FunctionCallValueNode::emitBytecode(BytecodeGenerator& generator, RegisterID* dst) +{ + RefPtr<RegisterID> func = generator.emitNode(m_expr); + RefPtr<RegisterID> thisRegister = generator.emitLoad(generator.newTemporary(), jsNull()); + return generator.emitCall(generator.finalDestination(dst, func.get()), func.get(), thisRegister.get(), m_args, divot(), startOffset(), endOffset()); +} + +// ------------------------------ FunctionCallResolveNode ---------------------------------- + +RegisterID* FunctionCallResolveNode::emitBytecode(BytecodeGenerator& generator, RegisterID* dst) +{ + if (RefPtr<RegisterID> local = generator.registerFor(m_ident)) { + RefPtr<RegisterID> thisRegister = generator.emitLoad(generator.newTemporary(), jsNull()); + return generator.emitCall(generator.finalDestination(dst, thisRegister.get()), local.get(), thisRegister.get(), m_args, divot(), startOffset(), endOffset()); + } + + int index = 0; + size_t depth = 0; + JSObject* globalObject = 0; + if (generator.findScopedProperty(m_ident, index, depth, false, globalObject) && index != missingSymbolMarker()) { + RefPtr<RegisterID> func = generator.emitGetScopedVar(generator.newTemporary(), depth, index, globalObject); + RefPtr<RegisterID> thisRegister = generator.emitLoad(generator.newTemporary(), jsNull()); + return generator.emitCall(generator.finalDestination(dst, func.get()), func.get(), thisRegister.get(), m_args, divot(), startOffset(), endOffset()); + } + + RefPtr<RegisterID> func = generator.newTemporary(); + RefPtr<RegisterID> thisRegister = generator.newTemporary(); + int identifierStart = divot() - startOffset(); + generator.emitExpressionInfo(identifierStart + m_ident.size(), m_ident.size(), 0); + generator.emitResolveWithBase(thisRegister.get(), func.get(), m_ident); + return generator.emitCall(generator.finalDestination(dst, func.get()), func.get(), thisRegister.get(), m_args, divot(), startOffset(), endOffset()); +} + +// ------------------------------ FunctionCallBracketNode ---------------------------------- + +RegisterID* FunctionCallBracketNode::emitBytecode(BytecodeGenerator& generator, RegisterID* dst) +{ + RefPtr<RegisterID> base = generator.emitNode(m_base); + RegisterID* property = generator.emitNode(m_subscript); + generator.emitExpressionInfo(divot() - m_subexpressionDivotOffset, startOffset() - m_subexpressionDivotOffset, m_subexpressionEndOffset); + RefPtr<RegisterID> function = generator.emitGetByVal(generator.tempDestination(dst), base.get(), property); + RefPtr<RegisterID> thisRegister = generator.emitMove(generator.newTemporary(), base.get()); + return generator.emitCall(generator.finalDestination(dst, function.get()), function.get(), thisRegister.get(), m_args, divot(), startOffset(), endOffset()); +} + +// ------------------------------ FunctionCallDotNode ---------------------------------- + +RegisterID* FunctionCallDotNode::emitBytecode(BytecodeGenerator& generator, RegisterID* dst) +{ + RefPtr<RegisterID> function = generator.tempDestination(dst); + RefPtr<RegisterID> thisRegister = generator.newTemporary(); + generator.emitNode(thisRegister.get(), m_base); + generator.emitExpressionInfo(divot() - m_subexpressionDivotOffset, startOffset() - m_subexpressionDivotOffset, m_subexpressionEndOffset); + generator.emitMethodCheck(); + generator.emitGetById(function.get(), thisRegister.get(), m_ident); + return generator.emitCall(generator.finalDestination(dst, function.get()), function.get(), thisRegister.get(), m_args, divot(), startOffset(), endOffset()); +} + +RegisterID* CallFunctionCallDotNode::emitBytecode(BytecodeGenerator& generator, RegisterID* dst) +{ + RefPtr<Label> realCall = generator.newLabel(); + RefPtr<Label> end = generator.newLabel(); + RefPtr<RegisterID> base = generator.emitNode(m_base); + generator.emitExpressionInfo(divot() - m_subexpressionDivotOffset, startOffset() - m_subexpressionDivotOffset, m_subexpressionEndOffset); + RefPtr<RegisterID> function = generator.emitGetById(generator.tempDestination(dst), base.get(), m_ident); + RefPtr<RegisterID> finalDestination = generator.finalDestination(dst, function.get()); + generator.emitJumpIfNotFunctionCall(function.get(), realCall.get()); + { + RefPtr<RegisterID> realFunction = generator.emitMove(generator.tempDestination(dst), base.get()); + RefPtr<RegisterID> thisRegister = generator.newTemporary(); + ArgumentListNode* oldList = m_args->m_listNode; + if (m_args->m_listNode && m_args->m_listNode->m_expr) { + generator.emitNode(thisRegister.get(), m_args->m_listNode->m_expr); + m_args->m_listNode = m_args->m_listNode->m_next; + } else + generator.emitLoad(thisRegister.get(), jsNull()); + + generator.emitCall(finalDestination.get(), realFunction.get(), thisRegister.get(), m_args, divot(), startOffset(), endOffset()); + generator.emitJump(end.get()); + m_args->m_listNode = oldList; + } + generator.emitLabel(realCall.get()); + { + RefPtr<RegisterID> thisRegister = generator.emitMove(generator.newTemporary(), base.get()); + generator.emitCall(finalDestination.get(), function.get(), thisRegister.get(), m_args, divot(), startOffset(), endOffset()); + } + generator.emitLabel(end.get()); + return finalDestination.get(); +} + +static bool areTrivialApplyArguments(ArgumentsNode* args) +{ + return !args->m_listNode || !args->m_listNode->m_expr || !args->m_listNode->m_next + || (!args->m_listNode->m_next->m_next && args->m_listNode->m_next->m_expr->isSimpleArray()); +} + +RegisterID* ApplyFunctionCallDotNode::emitBytecode(BytecodeGenerator& generator, RegisterID* dst) +{ + // A few simple cases can be trivially handled as ordinary function calls. + // function.apply(), function.apply(arg) -> identical to function.call + // function.apply(thisArg, [arg0, arg1, ...]) -> can be trivially coerced into function.call(thisArg, arg0, arg1, ...) and saves object allocation + bool mayBeCall = areTrivialApplyArguments(m_args); + + RefPtr<Label> realCall = generator.newLabel(); + RefPtr<Label> end = generator.newLabel(); + RefPtr<RegisterID> base = generator.emitNode(m_base); + generator.emitExpressionInfo(divot() - m_subexpressionDivotOffset, startOffset() - m_subexpressionDivotOffset, m_subexpressionEndOffset); + RefPtr<RegisterID> function = generator.emitGetById(generator.tempDestination(dst), base.get(), m_ident); + RefPtr<RegisterID> finalDestination = generator.finalDestination(dst, function.get()); + generator.emitJumpIfNotFunctionApply(function.get(), realCall.get()); + { + if (mayBeCall) { + RefPtr<RegisterID> realFunction = generator.emitMove(generator.tempDestination(dst), base.get()); + RefPtr<RegisterID> thisRegister = generator.newTemporary(); + ArgumentListNode* oldList = m_args->m_listNode; + if (m_args->m_listNode && m_args->m_listNode->m_expr) { + generator.emitNode(thisRegister.get(), m_args->m_listNode->m_expr); + m_args->m_listNode = m_args->m_listNode->m_next; + if (m_args->m_listNode) { + ASSERT(m_args->m_listNode->m_expr->isSimpleArray()); + ASSERT(!m_args->m_listNode->m_next); + m_args->m_listNode = static_cast<ArrayNode*>(m_args->m_listNode->m_expr)->toArgumentList(generator.globalData()); + } + } else + generator.emitLoad(thisRegister.get(), jsNull()); + generator.emitCall(finalDestination.get(), realFunction.get(), thisRegister.get(), m_args, divot(), startOffset(), endOffset()); + m_args->m_listNode = oldList; + } else { + ASSERT(m_args->m_listNode && m_args->m_listNode->m_next); + RefPtr<RegisterID> realFunction = generator.emitMove(generator.newTemporary(), base.get()); + RefPtr<RegisterID> argsCountRegister = generator.newTemporary(); + RefPtr<RegisterID> thisRegister = generator.newTemporary(); + RefPtr<RegisterID> argsRegister = generator.newTemporary(); + generator.emitNode(thisRegister.get(), m_args->m_listNode->m_expr); + ArgumentListNode* args = m_args->m_listNode->m_next; + bool isArgumentsApply = false; + if (args->m_expr->isResolveNode()) { + ResolveNode* resolveNode = static_cast<ResolveNode*>(args->m_expr); + isArgumentsApply = generator.willResolveToArguments(resolveNode->identifier()); + if (isArgumentsApply) + generator.emitMove(argsRegister.get(), generator.uncheckedRegisterForArguments()); + } + if (!isArgumentsApply) + generator.emitNode(argsRegister.get(), args->m_expr); + while ((args = args->m_next)) + generator.emitNode(args->m_expr); + + generator.emitLoadVarargs(argsCountRegister.get(), argsRegister.get()); + generator.emitCallVarargs(finalDestination.get(), realFunction.get(), thisRegister.get(), argsCountRegister.get(), divot(), startOffset(), endOffset()); + } + generator.emitJump(end.get()); + } + generator.emitLabel(realCall.get()); + { + RefPtr<RegisterID> thisRegister = generator.emitMove(generator.newTemporary(), base.get()); + generator.emitCall(finalDestination.get(), function.get(), thisRegister.get(), m_args, divot(), startOffset(), endOffset()); + } + generator.emitLabel(end.get()); + return finalDestination.get(); +} + +// ------------------------------ PostfixResolveNode ---------------------------------- + +static RegisterID* emitPreIncOrDec(BytecodeGenerator& generator, RegisterID* srcDst, Operator oper) +{ + return (oper == OpPlusPlus) ? generator.emitPreInc(srcDst) : generator.emitPreDec(srcDst); +} + +static RegisterID* emitPostIncOrDec(BytecodeGenerator& generator, RegisterID* dst, RegisterID* srcDst, Operator oper) +{ + if (srcDst == dst) + return generator.emitToJSNumber(dst, srcDst); + return (oper == OpPlusPlus) ? generator.emitPostInc(dst, srcDst) : generator.emitPostDec(dst, srcDst); +} + +RegisterID* PostfixResolveNode::emitBytecode(BytecodeGenerator& generator, RegisterID* dst) +{ + if (RegisterID* local = generator.registerFor(m_ident)) { + if (generator.isLocalConstant(m_ident)) { + if (dst == generator.ignoredResult()) + return 0; + return generator.emitToJSNumber(generator.finalDestination(dst), local); + } + + if (dst == generator.ignoredResult()) + return emitPreIncOrDec(generator, local, m_operator); + return emitPostIncOrDec(generator, generator.finalDestination(dst), local, m_operator); + } + + int index = 0; + size_t depth = 0; + JSObject* globalObject = 0; + if (generator.findScopedProperty(m_ident, index, depth, true, globalObject) && index != missingSymbolMarker()) { + RefPtr<RegisterID> value = generator.emitGetScopedVar(generator.newTemporary(), depth, index, globalObject); + RegisterID* oldValue; + if (dst == generator.ignoredResult()) { + oldValue = 0; + emitPreIncOrDec(generator, value.get(), m_operator); + } else { + oldValue = emitPostIncOrDec(generator, generator.finalDestination(dst), value.get(), m_operator); + } + generator.emitPutScopedVar(depth, index, value.get(), globalObject); + return oldValue; + } + + generator.emitExpressionInfo(divot(), startOffset(), endOffset()); + RefPtr<RegisterID> value = generator.newTemporary(); + RefPtr<RegisterID> base = generator.emitResolveWithBase(generator.newTemporary(), value.get(), m_ident); + RegisterID* oldValue; + if (dst == generator.ignoredResult()) { + oldValue = 0; + emitPreIncOrDec(generator, value.get(), m_operator); + } else { + oldValue = emitPostIncOrDec(generator, generator.finalDestination(dst), value.get(), m_operator); + } + generator.emitPutById(base.get(), m_ident, value.get()); + return oldValue; +} + +// ------------------------------ PostfixBracketNode ---------------------------------- + +RegisterID* PostfixBracketNode::emitBytecode(BytecodeGenerator& generator, RegisterID* dst) +{ + RefPtr<RegisterID> base = generator.emitNode(m_base); + RefPtr<RegisterID> property = generator.emitNode(m_subscript); + + generator.emitExpressionInfo(divot() - m_subexpressionDivotOffset, startOffset() - m_subexpressionDivotOffset, m_subexpressionEndOffset); + RefPtr<RegisterID> value = generator.emitGetByVal(generator.newTemporary(), base.get(), property.get()); + RegisterID* oldValue; + if (dst == generator.ignoredResult()) { + oldValue = 0; + if (m_operator == OpPlusPlus) + generator.emitPreInc(value.get()); + else + generator.emitPreDec(value.get()); + } else { + oldValue = (m_operator == OpPlusPlus) ? generator.emitPostInc(generator.finalDestination(dst), value.get()) : generator.emitPostDec(generator.finalDestination(dst), value.get()); + } + generator.emitExpressionInfo(divot(), startOffset(), endOffset()); + generator.emitPutByVal(base.get(), property.get(), value.get()); + return oldValue; +} + +// ------------------------------ PostfixDotNode ---------------------------------- + +RegisterID* PostfixDotNode::emitBytecode(BytecodeGenerator& generator, RegisterID* dst) +{ + RefPtr<RegisterID> base = generator.emitNode(m_base); + + generator.emitExpressionInfo(divot() - m_subexpressionDivotOffset, startOffset() - m_subexpressionDivotOffset, m_subexpressionEndOffset); + RefPtr<RegisterID> value = generator.emitGetById(generator.newTemporary(), base.get(), m_ident); + RegisterID* oldValue; + if (dst == generator.ignoredResult()) { + oldValue = 0; + if (m_operator == OpPlusPlus) + generator.emitPreInc(value.get()); + else + generator.emitPreDec(value.get()); + } else { + oldValue = (m_operator == OpPlusPlus) ? generator.emitPostInc(generator.finalDestination(dst), value.get()) : generator.emitPostDec(generator.finalDestination(dst), value.get()); + } + generator.emitExpressionInfo(divot(), startOffset(), endOffset()); + generator.emitPutById(base.get(), m_ident, value.get()); + return oldValue; +} + +// ------------------------------ PostfixErrorNode ----------------------------------- + +RegisterID* PostfixErrorNode::emitBytecode(BytecodeGenerator& generator, RegisterID*) +{ + return emitThrowError(generator, ReferenceError, m_operator == OpPlusPlus + ? "Postfix ++ operator applied to value that is not a reference." + : "Postfix -- operator applied to value that is not a reference."); +} + +// ------------------------------ DeleteResolveNode ----------------------------------- + +RegisterID* DeleteResolveNode::emitBytecode(BytecodeGenerator& generator, RegisterID* dst) +{ + if (generator.registerFor(m_ident)) + return generator.emitLoad(generator.finalDestination(dst), false); + + generator.emitExpressionInfo(divot(), startOffset(), endOffset()); + RegisterID* base = generator.emitResolveBase(generator.tempDestination(dst), m_ident); + return generator.emitDeleteById(generator.finalDestination(dst, base), base, m_ident); +} + +// ------------------------------ DeleteBracketNode ----------------------------------- + +RegisterID* DeleteBracketNode::emitBytecode(BytecodeGenerator& generator, RegisterID* dst) +{ + RefPtr<RegisterID> r0 = generator.emitNode(m_base); + RegisterID* r1 = generator.emitNode(m_subscript); + + generator.emitExpressionInfo(divot(), startOffset(), endOffset()); + return generator.emitDeleteByVal(generator.finalDestination(dst), r0.get(), r1); +} + +// ------------------------------ DeleteDotNode ----------------------------------- + +RegisterID* DeleteDotNode::emitBytecode(BytecodeGenerator& generator, RegisterID* dst) +{ + RegisterID* r0 = generator.emitNode(m_base); + + generator.emitExpressionInfo(divot(), startOffset(), endOffset()); + return generator.emitDeleteById(generator.finalDestination(dst), r0, m_ident); +} + +// ------------------------------ DeleteValueNode ----------------------------------- + +RegisterID* DeleteValueNode::emitBytecode(BytecodeGenerator& generator, RegisterID* dst) +{ + generator.emitNode(generator.ignoredResult(), m_expr); + + // delete on a non-location expression ignores the value and returns true + return generator.emitLoad(generator.finalDestination(dst), true); +} + +// ------------------------------ VoidNode ------------------------------------- + +RegisterID* VoidNode::emitBytecode(BytecodeGenerator& generator, RegisterID* dst) +{ + if (dst == generator.ignoredResult()) { + generator.emitNode(generator.ignoredResult(), m_expr); + return 0; + } + RefPtr<RegisterID> r0 = generator.emitNode(m_expr); + return generator.emitLoad(dst, jsUndefined()); +} + +// ------------------------------ TypeOfValueNode ----------------------------------- + +RegisterID* TypeOfResolveNode::emitBytecode(BytecodeGenerator& generator, RegisterID* dst) +{ + if (RegisterID* local = generator.registerFor(m_ident)) { + if (dst == generator.ignoredResult()) + return 0; + return generator.emitTypeOf(generator.finalDestination(dst), local); + } + + RefPtr<RegisterID> scratch = generator.emitResolveBase(generator.tempDestination(dst), m_ident); + generator.emitGetById(scratch.get(), scratch.get(), m_ident); + if (dst == generator.ignoredResult()) + return 0; + return generator.emitTypeOf(generator.finalDestination(dst, scratch.get()), scratch.get()); +} + +// ------------------------------ TypeOfValueNode ----------------------------------- + +RegisterID* TypeOfValueNode::emitBytecode(BytecodeGenerator& generator, RegisterID* dst) +{ + if (dst == generator.ignoredResult()) { + generator.emitNode(generator.ignoredResult(), m_expr); + return 0; + } + RefPtr<RegisterID> src = generator.emitNode(m_expr); + return generator.emitTypeOf(generator.finalDestination(dst), src.get()); +} + +// ------------------------------ PrefixResolveNode ---------------------------------- + +RegisterID* PrefixResolveNode::emitBytecode(BytecodeGenerator& generator, RegisterID* dst) +{ + if (RegisterID* local = generator.registerFor(m_ident)) { + if (generator.isLocalConstant(m_ident)) { + if (dst == generator.ignoredResult()) + return 0; + RefPtr<RegisterID> r0 = generator.emitLoad(generator.finalDestination(dst), (m_operator == OpPlusPlus) ? 1.0 : -1.0); + return generator.emitBinaryOp(op_add, r0.get(), local, r0.get(), OperandTypes()); + } + + emitPreIncOrDec(generator, local, m_operator); + return generator.moveToDestinationIfNeeded(dst, local); + } + + int index = 0; + size_t depth = 0; + JSObject* globalObject = 0; + if (generator.findScopedProperty(m_ident, index, depth, false, globalObject) && index != missingSymbolMarker()) { + RefPtr<RegisterID> propDst = generator.emitGetScopedVar(generator.tempDestination(dst), depth, index, globalObject); + emitPreIncOrDec(generator, propDst.get(), m_operator); + generator.emitPutScopedVar(depth, index, propDst.get(), globalObject); + return generator.moveToDestinationIfNeeded(dst, propDst.get()); + } + + generator.emitExpressionInfo(divot(), startOffset(), endOffset()); + RefPtr<RegisterID> propDst = generator.tempDestination(dst); + RefPtr<RegisterID> base = generator.emitResolveWithBase(generator.newTemporary(), propDst.get(), m_ident); + emitPreIncOrDec(generator, propDst.get(), m_operator); + generator.emitPutById(base.get(), m_ident, propDst.get()); + return generator.moveToDestinationIfNeeded(dst, propDst.get()); +} + +// ------------------------------ PrefixBracketNode ---------------------------------- + +RegisterID* PrefixBracketNode::emitBytecode(BytecodeGenerator& generator, RegisterID* dst) +{ + RefPtr<RegisterID> base = generator.emitNode(m_base); + RefPtr<RegisterID> property = generator.emitNode(m_subscript); + RefPtr<RegisterID> propDst = generator.tempDestination(dst); + + generator.emitExpressionInfo(divot() + m_subexpressionDivotOffset, m_subexpressionStartOffset, endOffset() - m_subexpressionDivotOffset); + RegisterID* value = generator.emitGetByVal(propDst.get(), base.get(), property.get()); + if (m_operator == OpPlusPlus) + generator.emitPreInc(value); + else + generator.emitPreDec(value); + generator.emitExpressionInfo(divot(), startOffset(), endOffset()); + generator.emitPutByVal(base.get(), property.get(), value); + return generator.moveToDestinationIfNeeded(dst, propDst.get()); +} + +// ------------------------------ PrefixDotNode ---------------------------------- + +RegisterID* PrefixDotNode::emitBytecode(BytecodeGenerator& generator, RegisterID* dst) +{ + RefPtr<RegisterID> base = generator.emitNode(m_base); + RefPtr<RegisterID> propDst = generator.tempDestination(dst); + + generator.emitExpressionInfo(divot() + m_subexpressionDivotOffset, m_subexpressionStartOffset, endOffset() - m_subexpressionDivotOffset); + RegisterID* value = generator.emitGetById(propDst.get(), base.get(), m_ident); + if (m_operator == OpPlusPlus) + generator.emitPreInc(value); + else + generator.emitPreDec(value); + generator.emitExpressionInfo(divot(), startOffset(), endOffset()); + generator.emitPutById(base.get(), m_ident, value); + return generator.moveToDestinationIfNeeded(dst, propDst.get()); +} + +// ------------------------------ PrefixErrorNode ----------------------------------- + +RegisterID* PrefixErrorNode::emitBytecode(BytecodeGenerator& generator, RegisterID*) +{ + return emitThrowError(generator, ReferenceError, m_operator == OpPlusPlus + ? "Prefix ++ operator applied to value that is not a reference." + : "Prefix -- operator applied to value that is not a reference."); +} + +// ------------------------------ Unary Operation Nodes ----------------------------------- + +RegisterID* UnaryOpNode::emitBytecode(BytecodeGenerator& generator, RegisterID* dst) +{ + RegisterID* src = generator.emitNode(m_expr); + return generator.emitUnaryOp(opcodeID(), generator.finalDestination(dst), src); +} + + +// ------------------------------ LogicalNotNode ----------------------------------- + +void LogicalNotNode::emitBytecodeInConditionContext(BytecodeGenerator& generator, Label* trueTarget, Label* falseTarget, bool fallThroughMeansTrue) +{ + ASSERT(expr()->hasConditionContextCodegen()); + + // reverse the true and false targets + generator.emitNodeInConditionContext(expr(), falseTarget, trueTarget, !fallThroughMeansTrue); +} + + +// ------------------------------ Binary Operation Nodes ----------------------------------- + +// BinaryOpNode::emitStrcat: +// +// This node generates an op_strcat operation. This opcode can handle concatenation of three or +// more values, where we can determine a set of separate op_add operations would be operating on +// string values. +// +// This function expects to be operating on a graph of AST nodes looking something like this: +// +// (a)... (b) +// \ / +// (+) (c) +// \ / +// [d] ((+)) +// \ / +// [+=] +// +// The assignment operation is optional, if it exists the register holding the value on the +// lefthand side of the assignment should be passing as the optional 'lhs' argument. +// +// The method should be called on the node at the root of the tree of regular binary add +// operations (marked in the diagram with a double set of parentheses). This node must +// be performing a string concatenation (determined by statically detecting that at least +// one child must be a string). +// +// Since the minimum number of values being concatenated together is expected to be 3, if +// a lhs to a concatenating assignment is not provided then the root add should have at +// least one left child that is also an add that can be determined to be operating on strings. +// +RegisterID* BinaryOpNode::emitStrcat(BytecodeGenerator& generator, RegisterID* dst, RegisterID* lhs, ReadModifyResolveNode* emitExpressionInfoForMe) +{ + ASSERT(isAdd()); + ASSERT(resultDescriptor().definitelyIsString()); + + // Create a list of expressions for all the adds in the tree of nodes we can convert into + // a string concatenation. The rightmost node (c) is added first. The rightmost node is + // added first, and the leftmost child is never added, so the vector produced for the + // example above will be [ c, b ]. + Vector<ExpressionNode*, 16> reverseExpressionList; + reverseExpressionList.append(m_expr2); + + // Examine the left child of the add. So long as this is a string add, add its right-child + // to the list, and keep processing along the left fork. + ExpressionNode* leftMostAddChild = m_expr1; + while (leftMostAddChild->isAdd() && leftMostAddChild->resultDescriptor().definitelyIsString()) { + reverseExpressionList.append(static_cast<AddNode*>(leftMostAddChild)->m_expr2); + leftMostAddChild = static_cast<AddNode*>(leftMostAddChild)->m_expr1; + } + + Vector<RefPtr<RegisterID>, 16> temporaryRegisters; + + // If there is an assignment, allocate a temporary to hold the lhs after conversion. + // We could possibly avoid this (the lhs is converted last anyway, we could let the + // op_strcat node handle its conversion if required). + if (lhs) + temporaryRegisters.append(generator.newTemporary()); + + // Emit code for the leftmost node ((a) in the example). + temporaryRegisters.append(generator.newTemporary()); + RegisterID* leftMostAddChildTempRegister = temporaryRegisters.last().get(); + generator.emitNode(leftMostAddChildTempRegister, leftMostAddChild); + + // Note on ordering of conversions: + // + // We maintain the same ordering of conversions as we would see if the concatenations + // was performed as a sequence of adds (otherwise this optimization could change + // behaviour should an object have been provided a valueOf or toString method). + // + // Considering the above example, the sequnce of execution is: + // * evaluate operand (a) + // * evaluate operand (b) + // * convert (a) to primitive <- (this would be triggered by the first add) + // * convert (b) to primitive <- (ditto) + // * evaluate operand (c) + // * convert (c) to primitive <- (this would be triggered by the second add) + // And optionally, if there is an assignment: + // * convert (d) to primitive <- (this would be triggered by the assigning addition) + // + // As such we do not plant an op to convert the leftmost child now. Instead, use + // 'leftMostAddChildTempRegister' as a flag to trigger generation of the conversion + // once the second node has been generated. However, if the leftmost child is an + // immediate we can trivially determine that no conversion will be required. + // If this is the case + if (leftMostAddChild->isString()) + leftMostAddChildTempRegister = 0; + + while (reverseExpressionList.size()) { + ExpressionNode* node = reverseExpressionList.last(); + reverseExpressionList.removeLast(); + + // Emit the code for the current node. + temporaryRegisters.append(generator.newTemporary()); + generator.emitNode(temporaryRegisters.last().get(), node); + + // On the first iteration of this loop, when we first reach this point we have just + // generated the second node, which means it is time to convert the leftmost operand. + if (leftMostAddChildTempRegister) { + generator.emitToPrimitive(leftMostAddChildTempRegister, leftMostAddChildTempRegister); + leftMostAddChildTempRegister = 0; // Only do this once. + } + // Plant a conversion for this node, if necessary. + if (!node->isString()) + generator.emitToPrimitive(temporaryRegisters.last().get(), temporaryRegisters.last().get()); + } + ASSERT(temporaryRegisters.size() >= 3); + + // Certain read-modify nodes require expression info to be emitted *after* m_right has been generated. + // If this is required the node is passed as 'emitExpressionInfoForMe'; do so now. + if (emitExpressionInfoForMe) + generator.emitExpressionInfo(emitExpressionInfoForMe->divot(), emitExpressionInfoForMe->startOffset(), emitExpressionInfoForMe->endOffset()); + + // If there is an assignment convert the lhs now. This will also copy lhs to + // the temporary register we allocated for it. + if (lhs) + generator.emitToPrimitive(temporaryRegisters[0].get(), lhs); + + return generator.emitStrcat(generator.finalDestination(dst, temporaryRegisters[0].get()), temporaryRegisters[0].get(), temporaryRegisters.size()); +} + +RegisterID* BinaryOpNode::emitBytecode(BytecodeGenerator& generator, RegisterID* dst) +{ + OpcodeID opcodeID = this->opcodeID(); + + if (opcodeID == op_add && m_expr1->isAdd() && m_expr1->resultDescriptor().definitelyIsString()) + return emitStrcat(generator, dst); + + if (opcodeID == op_neq) { + if (m_expr1->isNull() || m_expr2->isNull()) { + RefPtr<RegisterID> src = generator.tempDestination(dst); + generator.emitNode(src.get(), m_expr1->isNull() ? m_expr2 : m_expr1); + return generator.emitUnaryOp(op_neq_null, generator.finalDestination(dst, src.get()), src.get()); + } + } + + RefPtr<RegisterID> src1 = generator.emitNodeForLeftHandSide(m_expr1, m_rightHasAssignments, m_expr2->isPure(generator)); + RegisterID* src2 = generator.emitNode(m_expr2); + return generator.emitBinaryOp(opcodeID, generator.finalDestination(dst, src1.get()), src1.get(), src2, OperandTypes(m_expr1->resultDescriptor(), m_expr2->resultDescriptor())); +} + +RegisterID* EqualNode::emitBytecode(BytecodeGenerator& generator, RegisterID* dst) +{ + if (m_expr1->isNull() || m_expr2->isNull()) { + RefPtr<RegisterID> src = generator.tempDestination(dst); + generator.emitNode(src.get(), m_expr1->isNull() ? m_expr2 : m_expr1); + return generator.emitUnaryOp(op_eq_null, generator.finalDestination(dst, src.get()), src.get()); + } + + RefPtr<RegisterID> src1 = generator.emitNodeForLeftHandSide(m_expr1, m_rightHasAssignments, m_expr2->isPure(generator)); + RegisterID* src2 = generator.emitNode(m_expr2); + return generator.emitEqualityOp(op_eq, generator.finalDestination(dst, src1.get()), src1.get(), src2); +} + +RegisterID* StrictEqualNode::emitBytecode(BytecodeGenerator& generator, RegisterID* dst) +{ + RefPtr<RegisterID> src1 = generator.emitNodeForLeftHandSide(m_expr1, m_rightHasAssignments, m_expr2->isPure(generator)); + RegisterID* src2 = generator.emitNode(m_expr2); + return generator.emitEqualityOp(op_stricteq, generator.finalDestination(dst, src1.get()), src1.get(), src2); +} + +RegisterID* ReverseBinaryOpNode::emitBytecode(BytecodeGenerator& generator, RegisterID* dst) +{ + RefPtr<RegisterID> src1 = generator.emitNodeForLeftHandSide(m_expr1, m_rightHasAssignments, m_expr2->isPure(generator)); + RegisterID* src2 = generator.emitNode(m_expr2); + return generator.emitBinaryOp(opcodeID(), generator.finalDestination(dst, src1.get()), src2, src1.get(), OperandTypes(m_expr2->resultDescriptor(), m_expr1->resultDescriptor())); +} + +RegisterID* ThrowableBinaryOpNode::emitBytecode(BytecodeGenerator& generator, RegisterID* dst) +{ + RefPtr<RegisterID> src1 = generator.emitNodeForLeftHandSide(m_expr1, m_rightHasAssignments, m_expr2->isPure(generator)); + RegisterID* src2 = generator.emitNode(m_expr2); + generator.emitExpressionInfo(divot(), startOffset(), endOffset()); + return generator.emitBinaryOp(opcodeID(), generator.finalDestination(dst, src1.get()), src1.get(), src2, OperandTypes(m_expr1->resultDescriptor(), m_expr2->resultDescriptor())); +} + +RegisterID* InstanceOfNode::emitBytecode(BytecodeGenerator& generator, RegisterID* dst) +{ + RefPtr<RegisterID> src1 = generator.emitNodeForLeftHandSide(m_expr1, m_rightHasAssignments, m_expr2->isPure(generator)); + RefPtr<RegisterID> src2 = generator.emitNode(m_expr2); + + generator.emitExpressionInfo(divot(), startOffset(), endOffset()); + generator.emitGetByIdExceptionInfo(op_instanceof); + RegisterID* src2Prototype = generator.emitGetById(generator.newTemporary(), src2.get(), generator.globalData()->propertyNames->prototype); + + generator.emitExpressionInfo(divot(), startOffset(), endOffset()); + return generator.emitInstanceOf(generator.finalDestination(dst, src1.get()), src1.get(), src2.get(), src2Prototype); +} + +// ------------------------------ LogicalOpNode ---------------------------- + +RegisterID* LogicalOpNode::emitBytecode(BytecodeGenerator& generator, RegisterID* dst) +{ + RefPtr<RegisterID> temp = generator.tempDestination(dst); + RefPtr<Label> target = generator.newLabel(); + + generator.emitNode(temp.get(), m_expr1); + if (m_operator == OpLogicalAnd) + generator.emitJumpIfFalse(temp.get(), target.get()); + else + generator.emitJumpIfTrue(temp.get(), target.get()); + generator.emitNode(temp.get(), m_expr2); + generator.emitLabel(target.get()); + + return generator.moveToDestinationIfNeeded(dst, temp.get()); +} + +void LogicalOpNode::emitBytecodeInConditionContext(BytecodeGenerator& generator, Label* trueTarget, Label* falseTarget, bool fallThroughMeansTrue) +{ + if (m_expr1->hasConditionContextCodegen()) { + RefPtr<Label> afterExpr1 = generator.newLabel(); + if (m_operator == OpLogicalAnd) + generator.emitNodeInConditionContext(m_expr1, afterExpr1.get(), falseTarget, true); + else + generator.emitNodeInConditionContext(m_expr1, trueTarget, afterExpr1.get(), false); + generator.emitLabel(afterExpr1.get()); + } else { + RegisterID* temp = generator.emitNode(m_expr1); + if (m_operator == OpLogicalAnd) + generator.emitJumpIfFalse(temp, falseTarget); + else + generator.emitJumpIfTrue(temp, trueTarget); + } + + if (m_expr2->hasConditionContextCodegen()) + generator.emitNodeInConditionContext(m_expr2, trueTarget, falseTarget, fallThroughMeansTrue); + else { + RegisterID* temp = generator.emitNode(m_expr2); + if (fallThroughMeansTrue) + generator.emitJumpIfFalse(temp, falseTarget); + else + generator.emitJumpIfTrue(temp, trueTarget); + } +} + +// ------------------------------ ConditionalNode ------------------------------ + +RegisterID* ConditionalNode::emitBytecode(BytecodeGenerator& generator, RegisterID* dst) +{ + RefPtr<RegisterID> newDst = generator.finalDestination(dst); + RefPtr<Label> beforeElse = generator.newLabel(); + RefPtr<Label> afterElse = generator.newLabel(); + + if (m_logical->hasConditionContextCodegen()) { + RefPtr<Label> beforeThen = generator.newLabel(); + generator.emitNodeInConditionContext(m_logical, beforeThen.get(), beforeElse.get(), true); + generator.emitLabel(beforeThen.get()); + } else { + RegisterID* cond = generator.emitNode(m_logical); + generator.emitJumpIfFalse(cond, beforeElse.get()); + } + + generator.emitNode(newDst.get(), m_expr1); + generator.emitJump(afterElse.get()); + + generator.emitLabel(beforeElse.get()); + generator.emitNode(newDst.get(), m_expr2); + + generator.emitLabel(afterElse.get()); + + return newDst.get(); +} + +// ------------------------------ ReadModifyResolveNode ----------------------------------- + +// FIXME: should this be moved to be a method on BytecodeGenerator? +static ALWAYS_INLINE RegisterID* emitReadModifyAssignment(BytecodeGenerator& generator, RegisterID* dst, RegisterID* src1, ExpressionNode* m_right, Operator oper, OperandTypes types, ReadModifyResolveNode* emitExpressionInfoForMe = 0) +{ + OpcodeID opcodeID; + switch (oper) { + case OpMultEq: + opcodeID = op_mul; + break; + case OpDivEq: + opcodeID = op_div; + break; + case OpPlusEq: + if (m_right->isAdd() && m_right->resultDescriptor().definitelyIsString()) + return static_cast<AddNode*>(m_right)->emitStrcat(generator, dst, src1, emitExpressionInfoForMe); + opcodeID = op_add; + break; + case OpMinusEq: + opcodeID = op_sub; + break; + case OpLShift: + opcodeID = op_lshift; + break; + case OpRShift: + opcodeID = op_rshift; + break; + case OpURShift: + opcodeID = op_urshift; + break; + case OpAndEq: + opcodeID = op_bitand; + break; + case OpXOrEq: + opcodeID = op_bitxor; + break; + case OpOrEq: + opcodeID = op_bitor; + break; + case OpModEq: + opcodeID = op_mod; + break; + default: + ASSERT_NOT_REACHED(); + return dst; + } + + RegisterID* src2 = generator.emitNode(m_right); + + // Certain read-modify nodes require expression info to be emitted *after* m_right has been generated. + // If this is required the node is passed as 'emitExpressionInfoForMe'; do so now. + if (emitExpressionInfoForMe) + generator.emitExpressionInfo(emitExpressionInfoForMe->divot(), emitExpressionInfoForMe->startOffset(), emitExpressionInfoForMe->endOffset()); + + return generator.emitBinaryOp(opcodeID, dst, src1, src2, types); +} + +RegisterID* ReadModifyResolveNode::emitBytecode(BytecodeGenerator& generator, RegisterID* dst) +{ + if (RegisterID* local = generator.registerFor(m_ident)) { + if (generator.isLocalConstant(m_ident)) { + return emitReadModifyAssignment(generator, generator.finalDestination(dst), local, m_right, m_operator, OperandTypes(ResultType::unknownType(), m_right->resultDescriptor())); + } + + if (generator.leftHandSideNeedsCopy(m_rightHasAssignments, m_right->isPure(generator))) { + RefPtr<RegisterID> result = generator.newTemporary(); + generator.emitMove(result.get(), local); + emitReadModifyAssignment(generator, result.get(), result.get(), m_right, m_operator, OperandTypes(ResultType::unknownType(), m_right->resultDescriptor())); + generator.emitMove(local, result.get()); + return generator.moveToDestinationIfNeeded(dst, result.get()); + } + + RegisterID* result = emitReadModifyAssignment(generator, local, local, m_right, m_operator, OperandTypes(ResultType::unknownType(), m_right->resultDescriptor())); + return generator.moveToDestinationIfNeeded(dst, result); + } + + int index = 0; + size_t depth = 0; + JSObject* globalObject = 0; + if (generator.findScopedProperty(m_ident, index, depth, true, globalObject) && index != missingSymbolMarker()) { + RefPtr<RegisterID> src1 = generator.emitGetScopedVar(generator.tempDestination(dst), depth, index, globalObject); + RegisterID* result = emitReadModifyAssignment(generator, generator.finalDestination(dst, src1.get()), src1.get(), m_right, m_operator, OperandTypes(ResultType::unknownType(), m_right->resultDescriptor())); + generator.emitPutScopedVar(depth, index, result, globalObject); + return result; + } + + RefPtr<RegisterID> src1 = generator.tempDestination(dst); + generator.emitExpressionInfo(divot() - startOffset() + m_ident.size(), m_ident.size(), 0); + RefPtr<RegisterID> base = generator.emitResolveWithBase(generator.newTemporary(), src1.get(), m_ident); + RegisterID* result = emitReadModifyAssignment(generator, generator.finalDestination(dst, src1.get()), src1.get(), m_right, m_operator, OperandTypes(ResultType::unknownType(), m_right->resultDescriptor()), this); + return generator.emitPutById(base.get(), m_ident, result); +} + +// ------------------------------ AssignResolveNode ----------------------------------- + +RegisterID* AssignResolveNode::emitBytecode(BytecodeGenerator& generator, RegisterID* dst) +{ + if (RegisterID* local = generator.registerFor(m_ident)) { + if (generator.isLocalConstant(m_ident)) + return generator.emitNode(dst, m_right); + + RegisterID* result = generator.emitNode(local, m_right); + return generator.moveToDestinationIfNeeded(dst, result); + } + + int index = 0; + size_t depth = 0; + JSObject* globalObject = 0; + if (generator.findScopedProperty(m_ident, index, depth, true, globalObject) && index != missingSymbolMarker()) { + if (dst == generator.ignoredResult()) + dst = 0; + RegisterID* value = generator.emitNode(dst, m_right); + generator.emitPutScopedVar(depth, index, value, globalObject); + return value; + } + + RefPtr<RegisterID> base = generator.emitResolveBase(generator.newTemporary(), m_ident); + if (dst == generator.ignoredResult()) + dst = 0; + RegisterID* value = generator.emitNode(dst, m_right); + generator.emitExpressionInfo(divot(), startOffset(), endOffset()); + return generator.emitPutById(base.get(), m_ident, value); +} + +// ------------------------------ AssignDotNode ----------------------------------- + +RegisterID* AssignDotNode::emitBytecode(BytecodeGenerator& generator, RegisterID* dst) +{ + RefPtr<RegisterID> base = generator.emitNodeForLeftHandSide(m_base, m_rightHasAssignments, m_right->isPure(generator)); + RefPtr<RegisterID> value = generator.destinationForAssignResult(dst); + RegisterID* result = generator.emitNode(value.get(), m_right); + generator.emitExpressionInfo(divot(), startOffset(), endOffset()); + generator.emitPutById(base.get(), m_ident, result); + return generator.moveToDestinationIfNeeded(dst, result); +} + +// ------------------------------ ReadModifyDotNode ----------------------------------- + +RegisterID* ReadModifyDotNode::emitBytecode(BytecodeGenerator& generator, RegisterID* dst) +{ + RefPtr<RegisterID> base = generator.emitNodeForLeftHandSide(m_base, m_rightHasAssignments, m_right->isPure(generator)); + + generator.emitExpressionInfo(divot() - m_subexpressionDivotOffset, startOffset() - m_subexpressionDivotOffset, m_subexpressionEndOffset); + RefPtr<RegisterID> value = generator.emitGetById(generator.tempDestination(dst), base.get(), m_ident); + RegisterID* updatedValue = emitReadModifyAssignment(generator, generator.finalDestination(dst, value.get()), value.get(), m_right, m_operator, OperandTypes(ResultType::unknownType(), m_right->resultDescriptor())); + + generator.emitExpressionInfo(divot(), startOffset(), endOffset()); + return generator.emitPutById(base.get(), m_ident, updatedValue); +} + +// ------------------------------ AssignErrorNode ----------------------------------- + +RegisterID* AssignErrorNode::emitBytecode(BytecodeGenerator& generator, RegisterID*) +{ + return emitThrowError(generator, ReferenceError, "Left side of assignment is not a reference."); +} + +// ------------------------------ AssignBracketNode ----------------------------------- + +RegisterID* AssignBracketNode::emitBytecode(BytecodeGenerator& generator, RegisterID* dst) +{ + RefPtr<RegisterID> base = generator.emitNodeForLeftHandSide(m_base, m_subscriptHasAssignments || m_rightHasAssignments, m_subscript->isPure(generator) && m_right->isPure(generator)); + RefPtr<RegisterID> property = generator.emitNodeForLeftHandSide(m_subscript, m_rightHasAssignments, m_right->isPure(generator)); + RefPtr<RegisterID> value = generator.destinationForAssignResult(dst); + RegisterID* result = generator.emitNode(value.get(), m_right); + + generator.emitExpressionInfo(divot(), startOffset(), endOffset()); + generator.emitPutByVal(base.get(), property.get(), result); + return generator.moveToDestinationIfNeeded(dst, result); +} + +// ------------------------------ ReadModifyBracketNode ----------------------------------- + +RegisterID* ReadModifyBracketNode::emitBytecode(BytecodeGenerator& generator, RegisterID* dst) +{ + RefPtr<RegisterID> base = generator.emitNodeForLeftHandSide(m_base, m_subscriptHasAssignments || m_rightHasAssignments, m_subscript->isPure(generator) && m_right->isPure(generator)); + RefPtr<RegisterID> property = generator.emitNodeForLeftHandSide(m_subscript, m_rightHasAssignments, m_right->isPure(generator)); + + generator.emitExpressionInfo(divot() - m_subexpressionDivotOffset, startOffset() - m_subexpressionDivotOffset, m_subexpressionEndOffset); + RefPtr<RegisterID> value = generator.emitGetByVal(generator.tempDestination(dst), base.get(), property.get()); + RegisterID* updatedValue = emitReadModifyAssignment(generator, generator.finalDestination(dst, value.get()), value.get(), m_right, m_operator, OperandTypes(ResultType::unknownType(), m_right->resultDescriptor())); + + generator.emitExpressionInfo(divot(), startOffset(), endOffset()); + generator.emitPutByVal(base.get(), property.get(), updatedValue); + + return updatedValue; +} + +// ------------------------------ CommaNode ------------------------------------ + +RegisterID* CommaNode::emitBytecode(BytecodeGenerator& generator, RegisterID* dst) +{ + ASSERT(m_expressions.size() > 1); + for (size_t i = 0; i < m_expressions.size() - 1; i++) + generator.emitNode(generator.ignoredResult(), m_expressions[i]); + return generator.emitNode(dst, m_expressions.last()); +} + +// ------------------------------ ConstDeclNode ------------------------------------ + +RegisterID* ConstDeclNode::emitCodeSingle(BytecodeGenerator& generator) +{ + if (RegisterID* local = generator.constRegisterFor(m_ident)) { + if (!m_init) + return local; + + return generator.emitNode(local, m_init); + } + + if (generator.codeType() != EvalCode) { + if (m_init) + return generator.emitNode(m_init); + else + return generator.emitResolve(generator.newTemporary(), m_ident); + } + // FIXME: While this code should only be hit in eval code, it will potentially + // assign to the wrong base if m_ident exists in an intervening dynamic scope. + RefPtr<RegisterID> base = generator.emitResolveBase(generator.newTemporary(), m_ident); + RegisterID* value = m_init ? generator.emitNode(m_init) : generator.emitLoad(0, jsUndefined()); + return generator.emitPutById(base.get(), m_ident, value); +} + +RegisterID* ConstDeclNode::emitBytecode(BytecodeGenerator& generator, RegisterID*) +{ + RegisterID* result = 0; + for (ConstDeclNode* n = this; n; n = n->m_next) + result = n->emitCodeSingle(generator); + + return result; +} + +// ------------------------------ ConstStatementNode ----------------------------- + +RegisterID* ConstStatementNode::emitBytecode(BytecodeGenerator& generator, RegisterID*) +{ + generator.emitDebugHook(WillExecuteStatement, firstLine(), lastLine()); + return generator.emitNode(m_next); +} + +// ------------------------------ SourceElements ------------------------------- + + +inline StatementNode* SourceElements::lastStatement() const +{ + size_t size = m_statements.size(); + return size ? m_statements[size - 1] : 0; +} + +inline void SourceElements::emitBytecode(BytecodeGenerator& generator, RegisterID* dst) +{ + size_t size = m_statements.size(); + for (size_t i = 0; i < size; ++i) + generator.emitNode(dst, m_statements[i]); +} + +// ------------------------------ BlockNode ------------------------------------ + +inline StatementNode* BlockNode::lastStatement() const +{ + return m_statements ? m_statements->lastStatement() : 0; +} + +RegisterID* BlockNode::emitBytecode(BytecodeGenerator& generator, RegisterID* dst) +{ + if (m_statements) + m_statements->emitBytecode(generator, dst); + return 0; +} + +// ------------------------------ EmptyStatementNode --------------------------- + +RegisterID* EmptyStatementNode::emitBytecode(BytecodeGenerator& generator, RegisterID* dst) +{ + generator.emitDebugHook(WillExecuteStatement, firstLine(), lastLine()); + return dst; +} + +// ------------------------------ DebuggerStatementNode --------------------------- + +RegisterID* DebuggerStatementNode::emitBytecode(BytecodeGenerator& generator, RegisterID* dst) +{ + generator.emitDebugHook(DidReachBreakpoint, firstLine(), lastLine()); + return dst; +} + +// ------------------------------ ExprStatementNode ---------------------------- + +RegisterID* ExprStatementNode::emitBytecode(BytecodeGenerator& generator, RegisterID* dst) +{ + ASSERT(m_expr); + generator.emitDebugHook(WillExecuteStatement, firstLine(), lastLine()); + return generator.emitNode(dst, m_expr); +} + +// ------------------------------ VarStatementNode ---------------------------- + +RegisterID* VarStatementNode::emitBytecode(BytecodeGenerator& generator, RegisterID*) +{ + ASSERT(m_expr); + generator.emitDebugHook(WillExecuteStatement, firstLine(), lastLine()); + return generator.emitNode(m_expr); +} + +// ------------------------------ IfNode --------------------------------------- + +RegisterID* IfNode::emitBytecode(BytecodeGenerator& generator, RegisterID* dst) +{ + generator.emitDebugHook(WillExecuteStatement, firstLine(), lastLine()); + + RefPtr<Label> afterThen = generator.newLabel(); + + if (m_condition->hasConditionContextCodegen()) { + RefPtr<Label> beforeThen = generator.newLabel(); + generator.emitNodeInConditionContext(m_condition, beforeThen.get(), afterThen.get(), true); + generator.emitLabel(beforeThen.get()); + } else { + RegisterID* cond = generator.emitNode(m_condition); + generator.emitJumpIfFalse(cond, afterThen.get()); + } + + generator.emitNode(dst, m_ifBlock); + generator.emitLabel(afterThen.get()); + + // FIXME: This should return the last statement executed so that it can be returned as a Completion. + return 0; +} + +// ------------------------------ IfElseNode --------------------------------------- + +RegisterID* IfElseNode::emitBytecode(BytecodeGenerator& generator, RegisterID* dst) +{ + generator.emitDebugHook(WillExecuteStatement, firstLine(), lastLine()); + + RefPtr<Label> beforeElse = generator.newLabel(); + RefPtr<Label> afterElse = generator.newLabel(); + + if (m_condition->hasConditionContextCodegen()) { + RefPtr<Label> beforeThen = generator.newLabel(); + generator.emitNodeInConditionContext(m_condition, beforeThen.get(), beforeElse.get(), true); + generator.emitLabel(beforeThen.get()); + } else { + RegisterID* cond = generator.emitNode(m_condition); + generator.emitJumpIfFalse(cond, beforeElse.get()); + } + + generator.emitNode(dst, m_ifBlock); + generator.emitJump(afterElse.get()); + + generator.emitLabel(beforeElse.get()); + + generator.emitNode(dst, m_elseBlock); + + generator.emitLabel(afterElse.get()); + + // FIXME: This should return the last statement executed so that it can be returned as a Completion. + return 0; +} + +// ------------------------------ DoWhileNode ---------------------------------- + +RegisterID* DoWhileNode::emitBytecode(BytecodeGenerator& generator, RegisterID* dst) +{ + RefPtr<LabelScope> scope = generator.newLabelScope(LabelScope::Loop); + + RefPtr<Label> topOfLoop = generator.newLabel(); + generator.emitLabel(topOfLoop.get()); + + generator.emitDebugHook(WillExecuteStatement, firstLine(), lastLine()); + + RefPtr<RegisterID> result = generator.emitNode(dst, m_statement); + + generator.emitLabel(scope->continueTarget()); + generator.emitDebugHook(WillExecuteStatement, m_expr->lineNo(), m_expr->lineNo()); + if (m_expr->hasConditionContextCodegen()) + generator.emitNodeInConditionContext(m_expr, topOfLoop.get(), scope->breakTarget(), false); + else { + RegisterID* cond = generator.emitNode(m_expr); + generator.emitJumpIfTrue(cond, topOfLoop.get()); + } + + generator.emitLabel(scope->breakTarget()); + return result.get(); +} + +// ------------------------------ WhileNode ------------------------------------ + +RegisterID* WhileNode::emitBytecode(BytecodeGenerator& generator, RegisterID* dst) +{ + RefPtr<LabelScope> scope = generator.newLabelScope(LabelScope::Loop); + + generator.emitJump(scope->continueTarget()); + + RefPtr<Label> topOfLoop = generator.newLabel(); + generator.emitLabel(topOfLoop.get()); + + generator.emitNode(dst, m_statement); + + generator.emitLabel(scope->continueTarget()); + generator.emitDebugHook(WillExecuteStatement, m_expr->lineNo(), m_expr->lineNo()); + + if (m_expr->hasConditionContextCodegen()) + generator.emitNodeInConditionContext(m_expr, topOfLoop.get(), scope->breakTarget(), false); + else { + RegisterID* cond = generator.emitNode(m_expr); + generator.emitJumpIfTrue(cond, topOfLoop.get()); + } + + generator.emitLabel(scope->breakTarget()); + + // FIXME: This should return the last statement executed so that it can be returned as a Completion + return 0; +} + +// ------------------------------ ForNode -------------------------------------- + +RegisterID* ForNode::emitBytecode(BytecodeGenerator& generator, RegisterID* dst) +{ + RefPtr<LabelScope> scope = generator.newLabelScope(LabelScope::Loop); + + generator.emitDebugHook(WillExecuteStatement, firstLine(), lastLine()); + + if (m_expr1) + generator.emitNode(generator.ignoredResult(), m_expr1); + + RefPtr<Label> condition = generator.newLabel(); + generator.emitJump(condition.get()); + + RefPtr<Label> topOfLoop = generator.newLabel(); + generator.emitLabel(topOfLoop.get()); + + RefPtr<RegisterID> result = generator.emitNode(dst, m_statement); + + generator.emitLabel(scope->continueTarget()); + generator.emitDebugHook(WillExecuteStatement, firstLine(), lastLine()); + if (m_expr3) + generator.emitNode(generator.ignoredResult(), m_expr3); + + generator.emitLabel(condition.get()); + if (m_expr2) { + if (m_expr2->hasConditionContextCodegen()) + generator.emitNodeInConditionContext(m_expr2, topOfLoop.get(), scope->breakTarget(), false); + else { + RegisterID* cond = generator.emitNode(m_expr2); + generator.emitJumpIfTrue(cond, topOfLoop.get()); + } + } else + generator.emitJump(topOfLoop.get()); + + generator.emitLabel(scope->breakTarget()); + return result.get(); +} + +// ------------------------------ ForInNode ------------------------------------ + +RegisterID* ForInNode::emitBytecode(BytecodeGenerator& generator, RegisterID* dst) +{ + RefPtr<LabelScope> scope = generator.newLabelScope(LabelScope::Loop); + + if (!m_lexpr->isLocation()) + return emitThrowError(generator, ReferenceError, "Left side of for-in statement is not a reference."); + + generator.emitDebugHook(WillExecuteStatement, firstLine(), lastLine()); + + if (m_init) + generator.emitNode(generator.ignoredResult(), m_init); + + RefPtr<RegisterID> base = generator.newTemporary(); + generator.emitNode(base.get(), m_expr); + RefPtr<RegisterID> i = generator.newTemporary(); + RefPtr<RegisterID> size = generator.newTemporary(); + RefPtr<RegisterID> expectedSubscript; + RefPtr<RegisterID> iter = generator.emitGetPropertyNames(generator.newTemporary(), base.get(), i.get(), size.get(), scope->breakTarget()); + generator.emitJump(scope->continueTarget()); + + RefPtr<Label> loopStart = generator.newLabel(); + generator.emitLabel(loopStart.get()); + + RegisterID* propertyName; + bool optimizedForinAccess = false; + if (m_lexpr->isResolveNode()) { + const Identifier& ident = static_cast<ResolveNode*>(m_lexpr)->identifier(); + propertyName = generator.registerFor(ident); + if (!propertyName) { + propertyName = generator.newTemporary(); + RefPtr<RegisterID> protect = propertyName; + RegisterID* base = generator.emitResolveBase(generator.newTemporary(), ident); + + generator.emitExpressionInfo(divot(), startOffset(), endOffset()); + generator.emitPutById(base, ident, propertyName); + } else { + expectedSubscript = generator.emitMove(generator.newTemporary(), propertyName); + generator.pushOptimisedForIn(expectedSubscript.get(), iter.get(), i.get(), propertyName); + optimizedForinAccess = true; + } + } else if (m_lexpr->isDotAccessorNode()) { + DotAccessorNode* assignNode = static_cast<DotAccessorNode*>(m_lexpr); + const Identifier& ident = assignNode->identifier(); + propertyName = generator.newTemporary(); + RefPtr<RegisterID> protect = propertyName; + RegisterID* base = generator.emitNode(assignNode->base()); + + generator.emitExpressionInfo(assignNode->divot(), assignNode->startOffset(), assignNode->endOffset()); + generator.emitPutById(base, ident, propertyName); + } else { + ASSERT(m_lexpr->isBracketAccessorNode()); + BracketAccessorNode* assignNode = static_cast<BracketAccessorNode*>(m_lexpr); + propertyName = generator.newTemporary(); + RefPtr<RegisterID> protect = propertyName; + RefPtr<RegisterID> base = generator.emitNode(assignNode->base()); + RegisterID* subscript = generator.emitNode(assignNode->subscript()); + + generator.emitExpressionInfo(assignNode->divot(), assignNode->startOffset(), assignNode->endOffset()); + generator.emitPutByVal(base.get(), subscript, propertyName); + } + + generator.emitNode(dst, m_statement); + + if (optimizedForinAccess) + generator.popOptimisedForIn(); + + generator.emitLabel(scope->continueTarget()); + generator.emitNextPropertyName(propertyName, base.get(), i.get(), size.get(), iter.get(), loopStart.get()); + generator.emitDebugHook(WillExecuteStatement, firstLine(), lastLine()); + generator.emitLabel(scope->breakTarget()); + return dst; +} + +// ------------------------------ ContinueNode --------------------------------- + +// ECMA 12.7 +RegisterID* ContinueNode::emitBytecode(BytecodeGenerator& generator, RegisterID* dst) +{ + generator.emitDebugHook(WillExecuteStatement, firstLine(), lastLine()); + + LabelScope* scope = generator.continueTarget(m_ident); + + if (!scope) + return m_ident.isEmpty() + ? emitThrowError(generator, SyntaxError, "Invalid continue statement.") + : emitThrowError(generator, SyntaxError, "Undefined label: '%s'.", m_ident); + + generator.emitJumpScopes(scope->continueTarget(), scope->scopeDepth()); + return dst; +} + +// ------------------------------ BreakNode ------------------------------------ + +// ECMA 12.8 +RegisterID* BreakNode::emitBytecode(BytecodeGenerator& generator, RegisterID* dst) +{ + generator.emitDebugHook(WillExecuteStatement, firstLine(), lastLine()); + + LabelScope* scope = generator.breakTarget(m_ident); + + if (!scope) + return m_ident.isEmpty() + ? emitThrowError(generator, SyntaxError, "Invalid break statement.") + : emitThrowError(generator, SyntaxError, "Undefined label: '%s'.", m_ident); + + generator.emitJumpScopes(scope->breakTarget(), scope->scopeDepth()); + return dst; +} + +// ------------------------------ ReturnNode ----------------------------------- + +RegisterID* ReturnNode::emitBytecode(BytecodeGenerator& generator, RegisterID* dst) +{ + generator.emitDebugHook(WillExecuteStatement, firstLine(), lastLine()); + if (generator.codeType() != FunctionCode) + return emitThrowError(generator, SyntaxError, "Invalid return statement."); + + if (dst == generator.ignoredResult()) + dst = 0; + RegisterID* r0 = m_value ? generator.emitNode(dst, m_value) : generator.emitLoad(dst, jsUndefined()); + RefPtr<RegisterID> returnRegister; + if (generator.scopeDepth()) { + RefPtr<Label> l0 = generator.newLabel(); + if (generator.hasFinaliser() && !r0->isTemporary()) { + returnRegister = generator.emitMove(generator.newTemporary(), r0); + r0 = returnRegister.get(); + } + generator.emitJumpScopes(l0.get(), 0); + generator.emitLabel(l0.get()); + } + generator.emitDebugHook(WillLeaveCallFrame, firstLine(), lastLine()); + return generator.emitReturn(r0); +} + +// ------------------------------ WithNode ------------------------------------- + +RegisterID* WithNode::emitBytecode(BytecodeGenerator& generator, RegisterID* dst) +{ + generator.emitDebugHook(WillExecuteStatement, firstLine(), lastLine()); + + RefPtr<RegisterID> scope = generator.newTemporary(); + generator.emitNode(scope.get(), m_expr); // scope must be protected until popped + generator.emitExpressionInfo(m_divot, m_expressionLength, 0); + generator.emitPushScope(scope.get()); + RegisterID* result = generator.emitNode(dst, m_statement); + generator.emitPopScope(); + return result; +} + +// ------------------------------ CaseClauseNode -------------------------------- + +inline void CaseClauseNode::emitBytecode(BytecodeGenerator& generator, RegisterID* dst) +{ + if (m_statements) + m_statements->emitBytecode(generator, dst); +} + +// ------------------------------ CaseBlockNode -------------------------------- + +enum SwitchKind { + SwitchUnset = 0, + SwitchNumber = 1, + SwitchString = 2, + SwitchNeither = 3 +}; + +static void processClauseList(ClauseListNode* list, Vector<ExpressionNode*, 8>& literalVector, SwitchKind& typeForTable, bool& singleCharacterSwitch, int32_t& min_num, int32_t& max_num) +{ + for (; list; list = list->getNext()) { + ExpressionNode* clauseExpression = list->getClause()->expr(); + literalVector.append(clauseExpression); + if (clauseExpression->isNumber()) { + double value = static_cast<NumberNode*>(clauseExpression)->value(); + int32_t intVal = static_cast<int32_t>(value); + if ((typeForTable & ~SwitchNumber) || (intVal != value)) { + typeForTable = SwitchNeither; + break; + } + if (intVal < min_num) + min_num = intVal; + if (intVal > max_num) + max_num = intVal; + typeForTable = SwitchNumber; + continue; + } + if (clauseExpression->isString()) { + if (typeForTable & ~SwitchString) { + typeForTable = SwitchNeither; + break; + } + const UString& value = static_cast<StringNode*>(clauseExpression)->value().ustring(); + if (singleCharacterSwitch &= value.size() == 1) { + int32_t intVal = value.rep()->data()[0]; + if (intVal < min_num) + min_num = intVal; + if (intVal > max_num) + max_num = intVal; + } + typeForTable = SwitchString; + continue; + } + typeForTable = SwitchNeither; + break; + } +} + +SwitchInfo::SwitchType CaseBlockNode::tryOptimizedSwitch(Vector<ExpressionNode*, 8>& literalVector, int32_t& min_num, int32_t& max_num) +{ + SwitchKind typeForTable = SwitchUnset; + bool singleCharacterSwitch = true; + + processClauseList(m_list1, literalVector, typeForTable, singleCharacterSwitch, min_num, max_num); + processClauseList(m_list2, literalVector, typeForTable, singleCharacterSwitch, min_num, max_num); + + if (typeForTable == SwitchUnset || typeForTable == SwitchNeither) + return SwitchInfo::SwitchNone; + + if (typeForTable == SwitchNumber) { + int32_t range = max_num - min_num; + if (min_num <= max_num && range <= 1000 && (range / literalVector.size()) < 10) + return SwitchInfo::SwitchImmediate; + return SwitchInfo::SwitchNone; + } + + ASSERT(typeForTable == SwitchString); + + if (singleCharacterSwitch) { + int32_t range = max_num - min_num; + if (min_num <= max_num && range <= 1000 && (range / literalVector.size()) < 10) + return SwitchInfo::SwitchCharacter; + } + + return SwitchInfo::SwitchString; +} + +RegisterID* CaseBlockNode::emitBytecodeForBlock(BytecodeGenerator& generator, RegisterID* switchExpression, RegisterID* dst) +{ + RefPtr<Label> defaultLabel; + Vector<RefPtr<Label>, 8> labelVector; + Vector<ExpressionNode*, 8> literalVector; + int32_t min_num = std::numeric_limits<int32_t>::max(); + int32_t max_num = std::numeric_limits<int32_t>::min(); + SwitchInfo::SwitchType switchType = tryOptimizedSwitch(literalVector, min_num, max_num); + + if (switchType != SwitchInfo::SwitchNone) { + // Prepare the various labels + for (uint32_t i = 0; i < literalVector.size(); i++) + labelVector.append(generator.newLabel()); + defaultLabel = generator.newLabel(); + generator.beginSwitch(switchExpression, switchType); + } else { + // Setup jumps + for (ClauseListNode* list = m_list1; list; list = list->getNext()) { + RefPtr<RegisterID> clauseVal = generator.newTemporary(); + generator.emitNode(clauseVal.get(), list->getClause()->expr()); + generator.emitBinaryOp(op_stricteq, clauseVal.get(), clauseVal.get(), switchExpression, OperandTypes()); + labelVector.append(generator.newLabel()); + generator.emitJumpIfTrue(clauseVal.get(), labelVector[labelVector.size() - 1].get()); + } + + for (ClauseListNode* list = m_list2; list; list = list->getNext()) { + RefPtr<RegisterID> clauseVal = generator.newTemporary(); + generator.emitNode(clauseVal.get(), list->getClause()->expr()); + generator.emitBinaryOp(op_stricteq, clauseVal.get(), clauseVal.get(), switchExpression, OperandTypes()); + labelVector.append(generator.newLabel()); + generator.emitJumpIfTrue(clauseVal.get(), labelVector[labelVector.size() - 1].get()); + } + defaultLabel = generator.newLabel(); + generator.emitJump(defaultLabel.get()); + } + + RegisterID* result = 0; + + size_t i = 0; + for (ClauseListNode* list = m_list1; list; list = list->getNext()) { + generator.emitLabel(labelVector[i++].get()); + list->getClause()->emitBytecode(generator, dst); + } + + if (m_defaultClause) { + generator.emitLabel(defaultLabel.get()); + m_defaultClause->emitBytecode(generator, dst); + } + + for (ClauseListNode* list = m_list2; list; list = list->getNext()) { + generator.emitLabel(labelVector[i++].get()); + list->getClause()->emitBytecode(generator, dst); + } + if (!m_defaultClause) + generator.emitLabel(defaultLabel.get()); + + ASSERT(i == labelVector.size()); + if (switchType != SwitchInfo::SwitchNone) { + ASSERT(labelVector.size() == literalVector.size()); + generator.endSwitch(labelVector.size(), labelVector.data(), literalVector.data(), defaultLabel.get(), min_num, max_num); + } + return result; +} + +// ------------------------------ SwitchNode ----------------------------------- + +RegisterID* SwitchNode::emitBytecode(BytecodeGenerator& generator, RegisterID* dst) +{ + generator.emitDebugHook(WillExecuteStatement, firstLine(), lastLine()); + + RefPtr<LabelScope> scope = generator.newLabelScope(LabelScope::Switch); + + RefPtr<RegisterID> r0 = generator.emitNode(m_expr); + RegisterID* r1 = m_block->emitBytecodeForBlock(generator, r0.get(), dst); + + generator.emitLabel(scope->breakTarget()); + return r1; +} + +// ------------------------------ LabelNode ------------------------------------ + +RegisterID* LabelNode::emitBytecode(BytecodeGenerator& generator, RegisterID* dst) +{ + generator.emitDebugHook(WillExecuteStatement, firstLine(), lastLine()); + + if (generator.breakTarget(m_name)) + return emitThrowError(generator, SyntaxError, "Duplicate label: %s.", m_name); + + RefPtr<LabelScope> scope = generator.newLabelScope(LabelScope::NamedLabel, &m_name); + RegisterID* r0 = generator.emitNode(dst, m_statement); + + generator.emitLabel(scope->breakTarget()); + return r0; +} + +// ------------------------------ ThrowNode ------------------------------------ + +RegisterID* ThrowNode::emitBytecode(BytecodeGenerator& generator, RegisterID* dst) +{ + generator.emitDebugHook(WillExecuteStatement, firstLine(), lastLine()); + + if (dst == generator.ignoredResult()) + dst = 0; + RefPtr<RegisterID> expr = generator.emitNode(m_expr); + generator.emitExpressionInfo(divot(), startOffset(), endOffset()); + generator.emitThrow(expr.get()); + return 0; +} + +// ------------------------------ TryNode -------------------------------------- + +RegisterID* TryNode::emitBytecode(BytecodeGenerator& generator, RegisterID* dst) +{ + // NOTE: The catch and finally blocks must be labeled explicitly, so the + // optimizer knows they may be jumped to from anywhere. + + generator.emitDebugHook(WillExecuteStatement, firstLine(), lastLine()); + + RefPtr<Label> tryStartLabel = generator.newLabel(); + RefPtr<Label> finallyStart; + RefPtr<RegisterID> finallyReturnAddr; + if (m_finallyBlock) { + finallyStart = generator.newLabel(); + finallyReturnAddr = generator.newTemporary(); + generator.pushFinallyContext(finallyStart.get(), finallyReturnAddr.get()); + } + + generator.emitLabel(tryStartLabel.get()); + generator.emitNode(dst, m_tryBlock); + + if (m_catchBlock) { + RefPtr<Label> catchEndLabel = generator.newLabel(); + + // Normal path: jump over the catch block. + generator.emitJump(catchEndLabel.get()); + + // Uncaught exception path: the catch block. + RefPtr<Label> here = generator.emitLabel(generator.newLabel().get()); + RefPtr<RegisterID> exceptionRegister = generator.emitCatch(generator.newTemporary(), tryStartLabel.get(), here.get()); + if (m_catchHasEval) { + RefPtr<RegisterID> dynamicScopeObject = generator.emitNewObject(generator.newTemporary()); + generator.emitPutById(dynamicScopeObject.get(), m_exceptionIdent, exceptionRegister.get()); + generator.emitMove(exceptionRegister.get(), dynamicScopeObject.get()); + generator.emitPushScope(exceptionRegister.get()); + } else + generator.emitPushNewScope(exceptionRegister.get(), m_exceptionIdent, exceptionRegister.get()); + generator.emitNode(dst, m_catchBlock); + generator.emitPopScope(); + generator.emitLabel(catchEndLabel.get()); + } + + if (m_finallyBlock) { + generator.popFinallyContext(); + // there may be important registers live at the time we jump + // to a finally block (such as for a return or throw) so we + // ref the highest register ever used as a conservative + // approach to not clobbering anything important + RefPtr<RegisterID> highestUsedRegister = generator.highestUsedRegister(); + RefPtr<Label> finallyEndLabel = generator.newLabel(); + + // Normal path: invoke the finally block, then jump over it. + generator.emitJumpSubroutine(finallyReturnAddr.get(), finallyStart.get()); + generator.emitJump(finallyEndLabel.get()); + + // Uncaught exception path: invoke the finally block, then re-throw the exception. + RefPtr<Label> here = generator.emitLabel(generator.newLabel().get()); + RefPtr<RegisterID> tempExceptionRegister = generator.emitCatch(generator.newTemporary(), tryStartLabel.get(), here.get()); + generator.emitJumpSubroutine(finallyReturnAddr.get(), finallyStart.get()); + generator.emitThrow(tempExceptionRegister.get()); + + // The finally block. + generator.emitLabel(finallyStart.get()); + generator.emitNode(dst, m_finallyBlock); + generator.emitSubroutineReturn(finallyReturnAddr.get()); + + generator.emitLabel(finallyEndLabel.get()); + } + + return dst; +} + +// ------------------------------ ScopeNode ----------------------------- + +inline void ScopeNode::emitStatementsBytecode(BytecodeGenerator& generator, RegisterID* dst) +{ + if (m_data->m_statements) + m_data->m_statements->emitBytecode(generator, dst); +} + +// ------------------------------ ProgramNode ----------------------------- + +RegisterID* ProgramNode::emitBytecode(BytecodeGenerator& generator, RegisterID*) +{ + generator.emitDebugHook(WillExecuteProgram, firstLine(), lastLine()); + + RefPtr<RegisterID> dstRegister = generator.newTemporary(); + generator.emitLoad(dstRegister.get(), jsUndefined()); + emitStatementsBytecode(generator, dstRegister.get()); + + generator.emitDebugHook(DidExecuteProgram, firstLine(), lastLine()); + generator.emitEnd(dstRegister.get()); + return 0; +} + +// ------------------------------ EvalNode ----------------------------- + +RegisterID* EvalNode::emitBytecode(BytecodeGenerator& generator, RegisterID*) +{ + generator.emitDebugHook(WillExecuteProgram, firstLine(), lastLine()); + + RefPtr<RegisterID> dstRegister = generator.newTemporary(); + generator.emitLoad(dstRegister.get(), jsUndefined()); + emitStatementsBytecode(generator, dstRegister.get()); + + generator.emitDebugHook(DidExecuteProgram, firstLine(), lastLine()); + generator.emitEnd(dstRegister.get()); + return 0; +} + +// ------------------------------ FunctionBodyNode ----------------------------- + +RegisterID* FunctionBodyNode::emitBytecode(BytecodeGenerator& generator, RegisterID*) +{ + generator.emitDebugHook(DidEnterCallFrame, firstLine(), lastLine()); + emitStatementsBytecode(generator, generator.ignoredResult()); + StatementNode* singleStatement = this->singleStatement(); + if (singleStatement && singleStatement->isBlock()) { + StatementNode* lastStatementInBlock = static_cast<BlockNode*>(singleStatement)->lastStatement(); + if (lastStatementInBlock && lastStatementInBlock->isReturnNode()) + return 0; + } + + RegisterID* r0 = generator.emitLoad(0, jsUndefined()); + generator.emitDebugHook(WillLeaveCallFrame, firstLine(), lastLine()); + generator.emitReturn(r0); + return 0; +} + +// ------------------------------ FuncDeclNode --------------------------------- + +RegisterID* FuncDeclNode::emitBytecode(BytecodeGenerator& generator, RegisterID* dst) +{ + if (dst == generator.ignoredResult()) + dst = 0; + return dst; +} + +// ------------------------------ FuncExprNode --------------------------------- + +RegisterID* FuncExprNode::emitBytecode(BytecodeGenerator& generator, RegisterID* dst) +{ + return generator.emitNewFunctionExpression(generator.finalDestination(dst), this); +} + +} // namespace JSC diff --git a/JavaScriptCore/config.h b/JavaScriptCore/config.h index 6681761..30757db 100644 --- a/JavaScriptCore/config.h +++ b/JavaScriptCore/config.h @@ -57,7 +57,6 @@ /* FIXME: if all platforms have these, do they really need #defines? */ #define HAVE_STDINT_H 1 -#define HAVE_STRING_H 1 #define WTF_CHANGES 1 @@ -74,3 +73,15 @@ #include <wtf/DisallowCType.h> #endif +#if PLATFORM(CHROMIUM) +#if !defined(WTF_USE_V8) +#define WTF_USE_V8 1 +#endif +#endif /* PLATFORM(CHROMIUM) */ + +#if !defined(WTF_USE_V8) +#define WTF_USE_V8 0 +#endif /* !defined(WTF_USE_V8) */ + +/* Using V8 implies not using JSC and vice versa */ +#define WTF_USE_JSC !WTF_USE_V8 diff --git a/JavaScriptCore/debugger/DebuggerActivation.cpp b/JavaScriptCore/debugger/DebuggerActivation.cpp index 5cc9a9f..d47db5b 100644 --- a/JavaScriptCore/debugger/DebuggerActivation.cpp +++ b/JavaScriptCore/debugger/DebuggerActivation.cpp @@ -76,9 +76,9 @@ void DebuggerActivation::getOwnPropertyNames(ExecState* exec, PropertyNameArray& m_activation->getPropertyNames(exec, propertyNames); } -bool DebuggerActivation::getPropertyAttributes(JSC::ExecState* exec, const Identifier& propertyName, unsigned& attributes) const +bool DebuggerActivation::getOwnPropertyDescriptor(ExecState* exec, const Identifier& propertyName, PropertyDescriptor& descriptor) { - return m_activation->getPropertyAttributes(exec, propertyName, attributes); + return m_activation->getOwnPropertyDescriptor(exec, propertyName, descriptor); } void DebuggerActivation::defineGetter(ExecState* exec, const Identifier& propertyName, JSObject* getterFunction, unsigned attributes) diff --git a/JavaScriptCore/debugger/DebuggerActivation.h b/JavaScriptCore/debugger/DebuggerActivation.h index 63cf635..373e62d 100644 --- a/JavaScriptCore/debugger/DebuggerActivation.h +++ b/JavaScriptCore/debugger/DebuggerActivation.h @@ -43,7 +43,7 @@ namespace JSC { virtual void putWithAttributes(ExecState*, const Identifier& propertyName, JSValue, unsigned attributes); virtual bool deleteProperty(ExecState*, const Identifier& propertyName); virtual void getOwnPropertyNames(ExecState*, PropertyNameArray&); - virtual bool getPropertyAttributes(ExecState*, const Identifier& propertyName, unsigned& attributes) const; + virtual bool getOwnPropertyDescriptor(ExecState*, const Identifier&, PropertyDescriptor&); virtual void defineGetter(ExecState*, const Identifier& propertyName, JSObject* getterFunction, unsigned attributes); virtual void defineSetter(ExecState*, const Identifier& propertyName, JSObject* setterFunction, unsigned attributes); virtual JSValue lookupGetter(ExecState*, const Identifier& propertyName); diff --git a/JavaScriptCore/debugger/DebuggerCallFrame.cpp b/JavaScriptCore/debugger/DebuggerCallFrame.cpp index c9d7cc6..c6b4223 100644 --- a/JavaScriptCore/debugger/DebuggerCallFrame.cpp +++ b/JavaScriptCore/debugger/DebuggerCallFrame.cpp @@ -44,7 +44,7 @@ const UString* DebuggerCallFrame::functionName() const JSFunction* function = asFunction(m_callFrame->callee()); if (!function) return 0; - return &function->name(&m_callFrame->globalData()); + return &function->name(m_callFrame); } UString DebuggerCallFrame::calculatedFunctionName() const @@ -55,7 +55,7 @@ UString DebuggerCallFrame::calculatedFunctionName() const JSFunction* function = asFunction(m_callFrame->callee()); if (!function) return 0; - return function->calculatedDisplayName(&m_callFrame->globalData()); + return function->calculatedDisplayName(m_callFrame); } DebuggerCallFrame::Type DebuggerCallFrame::type() const diff --git a/JavaScriptCore/interpreter/CachedCall.h b/JavaScriptCore/interpreter/CachedCall.h index e903b79..eb48a03 100644 --- a/JavaScriptCore/interpreter/CachedCall.h +++ b/JavaScriptCore/interpreter/CachedCall.h @@ -38,7 +38,7 @@ namespace JSC { : m_valid(false) , m_interpreter(callFrame->interpreter()) , m_exception(exception) - , m_globalObjectScope(callFrame, callFrame->globalData().dynamicGlobalObject ? callFrame->globalData().dynamicGlobalObject : function->scope().globalObject()) + , m_globalObjectScope(callFrame, function->scope().globalObject()) { ASSERT(!function->isHostFunction()); m_closure = m_interpreter->prepareForRepeatCall(function->jsExecutable(), callFrame, function, argCount, function->scope().node(), exception); @@ -52,7 +52,14 @@ namespace JSC { } void setThis(JSValue v) { m_closure.setArgument(0, v); } void setArgument(int n, JSValue v) { m_closure.setArgument(n + 1, v); } - CallFrame* newCallFrame() { return m_closure.newCallFrame; } + + CallFrame* newCallFrame(ExecState* exec) + { + CallFrame* callFrame = m_closure.newCallFrame; + callFrame->setScopeChain(exec->scopeChain()); + return callFrame; + } + ~CachedCall() { if (m_valid) diff --git a/JavaScriptCore/interpreter/CallFrame.h b/JavaScriptCore/interpreter/CallFrame.h index b4d49db..fff4c9b 100644 --- a/JavaScriptCore/interpreter/CallFrame.h +++ b/JavaScriptCore/interpreter/CallFrame.h @@ -39,7 +39,11 @@ namespace JSC { public: JSFunction* callee() const { return this[RegisterFile::Callee].function(); } CodeBlock* codeBlock() const { return this[RegisterFile::CodeBlock].Register::codeBlock(); } - ScopeChainNode* scopeChain() const { return this[RegisterFile::ScopeChain].Register::scopeChain(); } + ScopeChainNode* scopeChain() const + { + ASSERT(this[RegisterFile::ScopeChain].Register::scopeChain()); + return this[RegisterFile::ScopeChain].Register::scopeChain(); + } int argumentCount() const { return this[RegisterFile::ArgumentCount].i(); } JSValue thisValue(); @@ -66,6 +70,7 @@ namespace JSC { // or a pointer everywhere. JSGlobalData& globalData() const { + ASSERT(scopeChain()->globalData); return *scopeChain()->globalData; } diff --git a/JavaScriptCore/interpreter/Interpreter.cpp b/JavaScriptCore/interpreter/Interpreter.cpp index c77a0f1..ed8bf5b 100644 --- a/JavaScriptCore/interpreter/Interpreter.cpp +++ b/JavaScriptCore/interpreter/Interpreter.cpp @@ -300,7 +300,7 @@ NEVER_INLINE JSValue Interpreter::callEval(CallFrame* callFrame, RegisterFile* r if (!program.isString()) return program; - UString programSource = asString(program)->value(); + UString programSource = asString(program)->value(callFrame); LiteralParser preparser(callFrame, programSource, LiteralParser::NonStrictJSON); if (JSValue parsedObject = preparser.tryLiteralParse()) @@ -537,7 +537,7 @@ NEVER_INLINE HandlerInfo* Interpreter::throwException(CallFrame*& callFrame, JSV #if !ENABLE(JIT) if (isCallBytecode(codeBlock->instructions()[bytecodeOffset].u.opcode)) profiler->didExecute(callFrame, callFrame->r(codeBlock->instructions()[bytecodeOffset + 2].u.operand).jsValue()); - else if (codeBlock->instructions()[bytecodeOffset + 8].u.opcode == getOpcode(op_construct)) + else if (codeBlock->instructions().size() > (bytecodeOffset + 8) && codeBlock->instructions()[bytecodeOffset + 8].u.opcode == getOpcode(op_construct)) profiler->didExecute(callFrame, callFrame->r(codeBlock->instructions()[bytecodeOffset + 10].u.operand).jsValue()); #else int functionRegisterIndex; @@ -647,7 +647,7 @@ JSValue Interpreter::execute(FunctionExecutable* functionExecutable, CallFrame* return jsNull(); } - DynamicGlobalObjectScope globalObjectScope(callFrame, callFrame->globalData().dynamicGlobalObject ? callFrame->globalData().dynamicGlobalObject : scopeChain->globalObject); + DynamicGlobalObjectScope globalObjectScope(callFrame, scopeChain->globalObject); CallFrame* newCallFrame = CallFrame::create(oldEnd); size_t dst = 0; @@ -777,7 +777,7 @@ JSValue Interpreter::execute(EvalExecutable* eval, CallFrame* callFrame, JSObjec } } - DynamicGlobalObjectScope globalObjectScope(callFrame, callFrame->globalData().dynamicGlobalObject ? callFrame->globalData().dynamicGlobalObject : scopeChain->globalObject); + DynamicGlobalObjectScope globalObjectScope(callFrame, scopeChain->globalObject); EvalCodeBlock* codeBlock = &eval->bytecode(callFrame, scopeChain); @@ -1029,6 +1029,11 @@ NEVER_INLINE void Interpreter::tryCacheGetByID(CallFrame* callFrame, CodeBlock* return; } + if (structure->isDictionary()) { + vPC[0] = getOpcode(op_get_by_id_generic); + return; + } + if (slot.slotBase() == structure->prototypeForLookup(callFrame)) { ASSERT(slot.slotBase().isObject()); @@ -1037,7 +1042,9 @@ NEVER_INLINE void Interpreter::tryCacheGetByID(CallFrame* callFrame, CodeBlock* // Since we're accessing a prototype in a loop, it's a good bet that it // should not be treated as a dictionary. if (baseObject->structure()->isDictionary()) - baseObject->setStructure(Structure::fromDictionaryTransition(baseObject->structure())); + baseObject->flattenDictionaryObject(); + + ASSERT(!baseObject->structure()->isUncacheableDictionary()); vPC[0] = getOpcode(op_get_by_id_proto); vPC[5] = baseObject->structure(); @@ -1299,7 +1306,7 @@ JSValue Interpreter::privateExecute(ExecutionFlag flag, RegisterFile* registerFi int dst = vPC[1].u.operand; JSValue src1 = callFrame->r(vPC[2].u.operand).jsValue(); JSValue src2 = callFrame->r(vPC[3].u.operand).jsValue(); - callFrame->r(dst) = jsBoolean(JSValue::strictEqual(src1, src2)); + callFrame->r(dst) = jsBoolean(JSValue::strictEqual(callFrame, src1, src2)); vPC += OPCODE_LENGTH(op_stricteq); NEXT_INSTRUCTION(); @@ -1314,7 +1321,7 @@ JSValue Interpreter::privateExecute(ExecutionFlag flag, RegisterFile* registerFi int dst = vPC[1].u.operand; JSValue src1 = callFrame->r(vPC[2].u.operand).jsValue(); JSValue src2 = callFrame->r(vPC[3].u.operand).jsValue(); - callFrame->r(dst) = jsBoolean(!JSValue::strictEqual(src1, src2)); + callFrame->r(dst) = jsBoolean(!JSValue::strictEqual(callFrame, src1, src2)); vPC += OPCODE_LENGTH(op_nstricteq); NEXT_INSTRUCTION(); @@ -1488,7 +1495,7 @@ JSValue Interpreter::privateExecute(ExecutionFlag flag, RegisterFile* registerFi int dst = vPC[1].u.operand; JSValue src1 = callFrame->r(vPC[2].u.operand).jsValue(); JSValue src2 = callFrame->r(vPC[3].u.operand).jsValue(); - if (src1.isInt32() && src2.isInt32() && !(src1.asInt32() | src2.asInt32() & 0xc0000000)) // no overflow + if (src1.isInt32() && src2.isInt32() && !(src1.asInt32() | (src2.asInt32() & 0xc0000000))) // no overflow callFrame->r(dst) = jsNumber(callFrame, src1.asInt32() + src2.asInt32()); else { JSValue result = jsAdd(callFrame, src1, src2); @@ -1575,7 +1582,7 @@ JSValue Interpreter::privateExecute(ExecutionFlag flag, RegisterFile* registerFi int dst = vPC[1].u.operand; JSValue src1 = callFrame->r(vPC[2].u.operand).jsValue(); JSValue src2 = callFrame->r(vPC[3].u.operand).jsValue(); - if (src1.isInt32() && src2.isInt32() && !(src1.asInt32() | src2.asInt32() & 0xc0000000)) // no overflow + if (src1.isInt32() && src2.isInt32() && !(src1.asInt32() | (src2.asInt32() & 0xc0000000))) // no overflow callFrame->r(dst) = jsNumber(callFrame, src1.asInt32() - src2.asInt32()); else { JSValue result = jsNumber(callFrame, src1.toNumber(callFrame) - src2.toNumber(callFrame)); @@ -2134,6 +2141,7 @@ JSValue Interpreter::privateExecute(ExecutionFlag flag, RegisterFile* registerFi int offset = vPC[6].u.operand; ASSERT(protoObject->get(callFrame, callFrame->codeBlock()->identifier(vPC[3].u.operand)) == protoObject->getDirectOffset(offset)); + ASSERT(baseValue.get(callFrame, callFrame->codeBlock()->identifier(vPC[3].u.operand)) == protoObject->getDirectOffset(offset)); callFrame->r(dst) = JSValue(protoObject->getDirectOffset(offset)); vPC += OPCODE_LENGTH(op_get_by_id_proto); @@ -2189,6 +2197,7 @@ JSValue Interpreter::privateExecute(ExecutionFlag flag, RegisterFile* registerFi int offset = vPC[7].u.operand; ASSERT(baseObject->get(callFrame, callFrame->codeBlock()->identifier(vPC[3].u.operand)) == baseObject->getDirectOffset(offset)); + ASSERT(baseValue.get(callFrame, callFrame->codeBlock()->identifier(vPC[3].u.operand)) == baseObject->getDirectOffset(offset)); callFrame->r(dst) = JSValue(baseObject->getDirectOffset(offset)); vPC += OPCODE_LENGTH(op_get_by_id_chain); @@ -2256,7 +2265,7 @@ JSValue Interpreter::privateExecute(ExecutionFlag flag, RegisterFile* registerFi JSValue baseValue = callFrame->r(base).jsValue(); if (LIKELY(isJSString(globalData, baseValue))) { int dst = vPC[1].u.operand; - callFrame->r(dst) = jsNumber(callFrame, asString(baseValue)->value().size()); + callFrame->r(dst) = jsNumber(callFrame, asString(baseValue)->length()); vPC += OPCODE_LENGTH(op_get_string_length); NEXT_INSTRUCTION(); } @@ -2470,7 +2479,7 @@ JSValue Interpreter::privateExecute(ExecutionFlag flag, RegisterFile* registerFi else result = jsArray->JSArray::get(callFrame, i); } else if (isJSString(globalData, baseValue) && asString(baseValue)->canGetIndex(i)) - result = asString(baseValue)->getIndex(&callFrame->globalData(), i); + result = asString(baseValue)->getIndex(callFrame, i); else if (isJSByteArray(globalData, baseValue) && asByteArray(baseValue)->canAccessIndex(i)) result = asByteArray(baseValue)->getIndex(callFrame, i); else @@ -2638,6 +2647,26 @@ JSValue Interpreter::privateExecute(ExecutionFlag flag, RegisterFile* registerFi vPC += OPCODE_LENGTH(op_loop_if_true); NEXT_INSTRUCTION(); } + DEFINE_OPCODE(op_loop_if_false) { + /* loop_if_true cond(r) target(offset) + + Jumps to offset target from the current instruction, if and + only if register cond converts to boolean as false. + + Additionally this loop instruction may terminate JS execution is + the JS timeout is reached. + */ + int cond = vPC[1].u.operand; + int target = vPC[2].u.operand; + if (!callFrame->r(cond).jsValue().toBoolean(callFrame)) { + vPC += target; + CHECK_FOR_TIMEOUT(); + NEXT_INSTRUCTION(); + } + + vPC += OPCODE_LENGTH(op_loop_if_true); + NEXT_INSTRUCTION(); + } DEFINE_OPCODE(op_jtrue) { /* jtrue cond(r) target(offset) @@ -2698,7 +2727,7 @@ JSValue Interpreter::privateExecute(ExecutionFlag flag, RegisterFile* registerFi int target = vPC[2].u.operand; JSValue srcValue = callFrame->r(src).jsValue(); - if (!srcValue.isUndefinedOrNull() || (srcValue.isCell() && !srcValue.asCell()->structure()->typeInfo().masqueradesAsUndefined())) { + if (!srcValue.isUndefinedOrNull() && (!srcValue.isCell() || !srcValue.asCell()->structure()->typeInfo().masqueradesAsUndefined())) { vPC += target; NEXT_INSTRUCTION(); } @@ -2801,6 +2830,29 @@ JSValue Interpreter::privateExecute(ExecutionFlag flag, RegisterFile* registerFi vPC += OPCODE_LENGTH(op_jnless); NEXT_INSTRUCTION(); } + DEFINE_OPCODE(op_jless) { + /* jless src1(r) src2(r) target(offset) + + Checks whether register src1 is less than register src2, as + with the ECMAScript '<' operator, and then jumps to offset + target from the current instruction, if and only if the + result of the comparison is true. + */ + JSValue src1 = callFrame->r(vPC[1].u.operand).jsValue(); + JSValue src2 = callFrame->r(vPC[2].u.operand).jsValue(); + int target = vPC[3].u.operand; + + bool result = jsLess(callFrame, src1, src2); + CHECK_FOR_EXCEPTION(); + + if (result) { + vPC += target; + NEXT_INSTRUCTION(); + } + + vPC += OPCODE_LENGTH(op_jless); + NEXT_INSTRUCTION(); + } DEFINE_OPCODE(op_jnlesseq) { /* jnlesseq src1(r) src2(r) target(offset) @@ -2863,7 +2915,7 @@ JSValue Interpreter::privateExecute(ExecutionFlag flag, RegisterFile* registerFi if (!scrutinee.isString()) vPC += defaultOffset; else { - UString::Rep* value = asString(scrutinee)->value().rep(); + UString::Rep* value = asString(scrutinee)->value(callFrame).rep(); if (value->size() != 1) vPC += defaultOffset; else @@ -2886,7 +2938,7 @@ JSValue Interpreter::privateExecute(ExecutionFlag flag, RegisterFile* registerFi if (!scrutinee.isString()) vPC += defaultOffset; else - vPC += callFrame->codeBlock()->stringSwitchJumpTable(tableIndex).offsetForValue(asString(scrutinee)->value().rep(), defaultOffset); + vPC += callFrame->codeBlock()->stringSwitchJumpTable(tableIndex).offsetForValue(asString(scrutinee)->value(callFrame).rep(), defaultOffset); NEXT_INSTRUCTION(); } DEFINE_OPCODE(op_new_func) { @@ -3478,7 +3530,8 @@ JSValue Interpreter::privateExecute(ExecutionFlag flag, RegisterFile* registerFi int src = vPC[2].u.operand; int count = vPC[3].u.operand; - callFrame->r(dst) = concatenateStrings(callFrame, &callFrame->registers()[src], count); + callFrame->r(dst) = jsString(callFrame, &callFrame->registers()[src], count); + CHECK_FOR_EXCEPTION(); vPC += OPCODE_LENGTH(op_strcat); NEXT_INSTRUCTION(); diff --git a/JavaScriptCore/interpreter/Register.h b/JavaScriptCore/interpreter/Register.h index 76184ba..d0b0568 100644 --- a/JavaScriptCore/interpreter/Register.h +++ b/JavaScriptCore/interpreter/Register.h @@ -104,6 +104,9 @@ namespace JSC { ALWAYS_INLINE Register::Register(JSValue v) { +#if ENABLE(JSC_ZOMBIES) + ASSERT(!v.isZombie()); +#endif u.value = JSValue::encode(v); } diff --git a/JavaScriptCore/jit/ExecutableAllocator.h b/JavaScriptCore/jit/ExecutableAllocator.h index 1d15ef0..9ca62c8 100644 --- a/JavaScriptCore/jit/ExecutableAllocator.h +++ b/JavaScriptCore/jit/ExecutableAllocator.h @@ -189,6 +189,22 @@ public: sys_dcache_flush(code, size); sys_icache_invalidate(code, size); } +#elif PLATFORM(ARM_THUMB2) && PLATFORM(LINUX) + static void cacheFlush(void* code, size_t size) + { + asm volatile ( + "push {r7}\n" + "mov r0, %0\n" + "mov r1, %1\n" + "movw r7, #0x2\n" + "movt r7, #0xf\n" + "movs r2, #0x0\n" + "svc 0x0\n" + "pop {r7}\n" + : + : "r" (code), "r" (reinterpret_cast<char*>(code) + size) + : "r0", "r1", "r2"); + } #elif PLATFORM(SYMBIAN) static void cacheFlush(void* code, size_t size) { @@ -208,7 +224,7 @@ public: "pop {r7}\n" : : "r" (code), "r" (reinterpret_cast<char*>(code) + size) - : "r0", "r1"); + : "r0", "r1", "r2"); } #else #error "The cacheFlush support is missing on this platform." diff --git a/JavaScriptCore/jit/JIT.cpp b/JavaScriptCore/jit/JIT.cpp index 000e4b8..585486f 100644 --- a/JavaScriptCore/jit/JIT.cpp +++ b/JavaScriptCore/jit/JIT.cpp @@ -37,7 +37,6 @@ JSC::MacroAssemblerX86Common::SSE2CheckState JSC::MacroAssemblerX86Common::s_sse #include "CodeBlock.h" #include "Interpreter.h" #include "JITInlineMethods.h" -#include "JITStubs.h" #include "JITStubCall.h" #include "JSArray.h" #include "JSFunction.h" @@ -251,6 +250,7 @@ void JIT::privateCompileMainPass() DEFINE_OP(op_jneq_null) DEFINE_OP(op_jneq_ptr) DEFINE_OP(op_jnless) + DEFINE_OP(op_jless) DEFINE_OP(op_jnlesseq) DEFINE_OP(op_jsr) DEFINE_OP(op_jtrue) @@ -259,6 +259,7 @@ void JIT::privateCompileMainPass() DEFINE_OP(op_loop_if_less) DEFINE_OP(op_loop_if_lesseq) DEFINE_OP(op_loop_if_true) + DEFINE_OP(op_loop_if_false) DEFINE_OP(op_lshift) DEFINE_OP(op_method_check) DEFINE_OP(op_mod) @@ -390,11 +391,13 @@ void JIT::privateCompileSlowCases() DEFINE_SLOWCASE_OP(op_instanceof) DEFINE_SLOWCASE_OP(op_jfalse) DEFINE_SLOWCASE_OP(op_jnless) + DEFINE_SLOWCASE_OP(op_jless) DEFINE_SLOWCASE_OP(op_jnlesseq) DEFINE_SLOWCASE_OP(op_jtrue) DEFINE_SLOWCASE_OP(op_loop_if_less) DEFINE_SLOWCASE_OP(op_loop_if_lesseq) DEFINE_SLOWCASE_OP(op_loop_if_true) + DEFINE_SLOWCASE_OP(op_loop_if_false) DEFINE_SLOWCASE_OP(op_lshift) DEFINE_SLOWCASE_OP(op_method_check) DEFINE_SLOWCASE_OP(op_mod) diff --git a/JavaScriptCore/jit/JIT.h b/JavaScriptCore/jit/JIT.h index e19ea17..0b902b9 100644 --- a/JavaScriptCore/jit/JIT.h +++ b/JavaScriptCore/jit/JIT.h @@ -465,6 +465,47 @@ namespace JSC { static const int patchOffsetMethodCheckProtoObj = 11; static const int patchOffsetMethodCheckProtoStruct = 18; static const int patchOffsetMethodCheckPutFunction = 29; +#elif PLATFORM(ARM_TRADITIONAL) + // These architecture specific value are used to enable patching - see comment on op_put_by_id. + static const int patchOffsetPutByIdStructure = 4; + static const int patchOffsetPutByIdExternalLoad = 16; + static const int patchLengthPutByIdExternalLoad = 4; + static const int patchOffsetPutByIdPropertyMapOffset1 = 20; + static const int patchOffsetPutByIdPropertyMapOffset2 = 28; + // These architecture specific value are used to enable patching - see comment on op_get_by_id. + static const int patchOffsetGetByIdStructure = 4; + static const int patchOffsetGetByIdBranchToSlowCase = 16; + static const int patchOffsetGetByIdExternalLoad = 16; + static const int patchLengthGetByIdExternalLoad = 4; + static const int patchOffsetGetByIdPropertyMapOffset1 = 20; + static const int patchOffsetGetByIdPropertyMapOffset2 = 28; + static const int patchOffsetGetByIdPutResult = 36; +#if ENABLE(OPCODE_SAMPLING) + #error "OPCODE_SAMPLING is not yet supported" +#else + static const int patchOffsetGetByIdSlowCaseCall = 32; +#endif + static const int patchOffsetOpCallCompareToJump = 12; + + static const int patchOffsetMethodCheckProtoObj = 12; + static const int patchOffsetMethodCheckProtoStruct = 20; + static const int patchOffsetMethodCheckPutFunction = 32; + + // sequenceOpCall + static const int sequenceOpCallInstructionSpace = 12; + static const int sequenceOpCallConstantSpace = 2; + // sequenceMethodCheck + static const int sequenceMethodCheckInstructionSpace = 40; + static const int sequenceMethodCheckConstantSpace = 6; + // sequenceGetByIdHotPath + static const int sequenceGetByIdHotPathInstructionSpace = 36; + static const int sequenceGetByIdHotPathConstantSpace = 4; + // sequenceGetByIdSlowCase + static const int sequenceGetByIdSlowCaseInstructionSpace = 40; + static const int sequenceGetByIdSlowCaseConstantSpace = 2; + // sequencePutById + static const int sequencePutByIdInstructionSpace = 36; + static const int sequencePutByIdConstantSpace = 4; #else #error "JSVALUE32_64 not supported on this platform." #endif @@ -629,10 +670,7 @@ namespace JSC { static const int patchOffsetMethodCheckProtoObj = 12; static const int patchOffsetMethodCheckProtoStruct = 20; static const int patchOffsetMethodCheckPutFunction = 32; -#endif -#endif // USE(JSVALUE32_64) -#if PLATFORM(ARM_TRADITIONAL) // sequenceOpCall static const int sequenceOpCallInstructionSpace = 12; static const int sequenceOpCallConstantSpace = 2; @@ -649,6 +687,7 @@ namespace JSC { static const int sequencePutByIdInstructionSpace = 28; static const int sequencePutByIdConstantSpace = 3; #endif +#endif // USE(JSVALUE32_64) #if defined(ASSEMBLER_HAS_CONSTANT_POOL) && ASSEMBLER_HAS_CONSTANT_POOL #define BEGIN_UNINTERRUPTED_SEQUENCE(name) beginUninterruptedSequence(name ## InstructionSpace, name ## ConstantSpace) @@ -697,6 +736,7 @@ namespace JSC { void emit_op_jneq_null(Instruction*); void emit_op_jneq_ptr(Instruction*); void emit_op_jnless(Instruction*); + void emit_op_jless(Instruction*); void emit_op_jnlesseq(Instruction*); void emit_op_jsr(Instruction*); void emit_op_jtrue(Instruction*); @@ -705,6 +745,7 @@ namespace JSC { void emit_op_loop_if_less(Instruction*); void emit_op_loop_if_lesseq(Instruction*); void emit_op_loop_if_true(Instruction*); + void emit_op_loop_if_false(Instruction*); void emit_op_lshift(Instruction*); void emit_op_method_check(Instruction*); void emit_op_mod(Instruction*); @@ -779,11 +820,13 @@ namespace JSC { void emitSlow_op_instanceof(Instruction*, Vector<SlowCaseEntry>::iterator&); void emitSlow_op_jfalse(Instruction*, Vector<SlowCaseEntry>::iterator&); void emitSlow_op_jnless(Instruction*, Vector<SlowCaseEntry>::iterator&); + void emitSlow_op_jless(Instruction*, Vector<SlowCaseEntry>::iterator&); void emitSlow_op_jnlesseq(Instruction*, Vector<SlowCaseEntry>::iterator&); void emitSlow_op_jtrue(Instruction*, Vector<SlowCaseEntry>::iterator&); void emitSlow_op_loop_if_less(Instruction*, Vector<SlowCaseEntry>::iterator&); void emitSlow_op_loop_if_lesseq(Instruction*, Vector<SlowCaseEntry>::iterator&); void emitSlow_op_loop_if_true(Instruction*, Vector<SlowCaseEntry>::iterator&); + void emitSlow_op_loop_if_false(Instruction*, Vector<SlowCaseEntry>::iterator&); void emitSlow_op_lshift(Instruction*, Vector<SlowCaseEntry>::iterator&); void emitSlow_op_method_check(Instruction*, Vector<SlowCaseEntry>::iterator&); void emitSlow_op_mod(Instruction*, Vector<SlowCaseEntry>::iterator&); diff --git a/JavaScriptCore/jit/JITArithmetic.cpp b/JavaScriptCore/jit/JITArithmetic.cpp index 8cda482..70f7564 100644 --- a/JavaScriptCore/jit/JITArithmetic.cpp +++ b/JavaScriptCore/jit/JITArithmetic.cpp @@ -148,6 +148,69 @@ void JIT::emitSlow_op_jnless(Instruction* currentInstruction, Vector<SlowCaseEnt emitJumpSlowToHot(branchTest32(Zero, regT0), target); } +void JIT::emit_op_jless(Instruction* currentInstruction) +{ + unsigned op1 = currentInstruction[1].u.operand; + unsigned op2 = currentInstruction[2].u.operand; + unsigned target = currentInstruction[3].u.operand; + + JumpList notInt32Op1; + JumpList notInt32Op2; + + // Int32 less. + if (isOperandConstantImmediateInt(op1)) { + emitLoad(op2, regT3, regT2); + notInt32Op2.append(branch32(NotEqual, regT3, Imm32(JSValue::Int32Tag))); + addJump(branch32(GreaterThan, regT2, Imm32(getConstantOperand(op1).asInt32())), target); + } else if (isOperandConstantImmediateInt(op2)) { + emitLoad(op1, regT1, regT0); + notInt32Op1.append(branch32(NotEqual, regT1, Imm32(JSValue::Int32Tag))); + addJump(branch32(LessThan, regT0, Imm32(getConstantOperand(op2).asInt32())), target); + } else { + emitLoad2(op1, regT1, regT0, op2, regT3, regT2); + notInt32Op1.append(branch32(NotEqual, regT1, Imm32(JSValue::Int32Tag))); + notInt32Op2.append(branch32(NotEqual, regT3, Imm32(JSValue::Int32Tag))); + addJump(branch32(LessThan, regT0, regT2), target); + } + + if (!supportsFloatingPoint()) { + addSlowCase(notInt32Op1); + addSlowCase(notInt32Op2); + return; + } + Jump end = jump(); + + // Double less. + emitBinaryDoubleOp(op_jless, target, op1, op2, OperandTypes(), notInt32Op1, notInt32Op2, !isOperandConstantImmediateInt(op1), isOperandConstantImmediateInt(op1) || !isOperandConstantImmediateInt(op2)); + end.link(this); +} + +void JIT::emitSlow_op_jless(Instruction* currentInstruction, Vector<SlowCaseEntry>::iterator& iter) +{ + unsigned op1 = currentInstruction[1].u.operand; + unsigned op2 = currentInstruction[2].u.operand; + unsigned target = currentInstruction[3].u.operand; + + if (!supportsFloatingPoint()) { + if (!isOperandConstantImmediateInt(op1) && !isOperandConstantImmediateInt(op2)) + linkSlowCase(iter); // int32 check + linkSlowCase(iter); // int32 check + } else { + if (!isOperandConstantImmediateInt(op1)) { + linkSlowCase(iter); // double check + linkSlowCase(iter); // int32 check + } + if (isOperandConstantImmediateInt(op1) || !isOperandConstantImmediateInt(op2)) + linkSlowCase(iter); // double check + } + + JITStubCall stubCall(this, cti_op_jless); + stubCall.addArgument(op1); + stubCall.addArgument(op2); + stubCall.call(); + emitJumpSlowToHot(branchTest32(NonZero, regT0), target); +} + void JIT::emit_op_jnlesseq(Instruction* currentInstruction) { unsigned op1 = currentInstruction[1].u.operand; @@ -829,11 +892,15 @@ void JIT::emitBinaryDoubleOp(OpcodeID opcodeID, unsigned dst, unsigned op1, unsi break; case op_jnless: emitLoadDouble(op1, fpRegT2); - addJump(branchDouble(DoubleLessThanOrEqual, fpRegT0, fpRegT2), dst); + addJump(branchDouble(DoubleLessThanOrEqualOrUnordered, fpRegT0, fpRegT2), dst); + break; + case op_jless: + emitLoadDouble(op1, fpRegT2); + addJump(branchDouble(DoubleLessThan, fpRegT2, fpRegT0), dst); break; case op_jnlesseq: emitLoadDouble(op1, fpRegT2); - addJump(branchDouble(DoubleLessThan, fpRegT0, fpRegT2), dst); + addJump(branchDouble(DoubleLessThanOrUnordered, fpRegT0, fpRegT2), dst); break; default: ASSERT_NOT_REACHED(); @@ -882,11 +949,15 @@ void JIT::emitBinaryDoubleOp(OpcodeID opcodeID, unsigned dst, unsigned op1, unsi break; case op_jnless: emitLoadDouble(op2, fpRegT1); - addJump(branchDouble(DoubleLessThanOrEqual, fpRegT1, fpRegT0), dst); + addJump(branchDouble(DoubleLessThanOrEqualOrUnordered, fpRegT1, fpRegT0), dst); + break; + case op_jless: + emitLoadDouble(op2, fpRegT1); + addJump(branchDouble(DoubleLessThan, fpRegT0, fpRegT1), dst); break; case op_jnlesseq: emitLoadDouble(op2, fpRegT1); - addJump(branchDouble(DoubleLessThan, fpRegT1, fpRegT0), dst); + addJump(branchDouble(DoubleLessThanOrUnordered, fpRegT1, fpRegT0), dst); break; default: ASSERT_NOT_REACHED(); @@ -1000,20 +1071,11 @@ void JIT::emit_op_div(Instruction* currentInstruction) divDouble(fpRegT1, fpRegT0); JumpList doubleResult; - if (!isOperandConstantImmediateInt(op1) || getConstantOperand(op1).asInt32() > 1) { - m_assembler.cvttsd2si_rr(fpRegT0, regT0); - convertInt32ToDouble(regT0, fpRegT1); - m_assembler.ucomisd_rr(fpRegT1, fpRegT0); - - doubleResult.append(m_assembler.jne()); - doubleResult.append(m_assembler.jp()); - - doubleResult.append(branchTest32(Zero, regT0)); + branchConvertDoubleToInt32(fpRegT0, regT0, doubleResult, fpRegT1); - // Int32 result. - emitStoreInt32(dst, regT0, (op1 == dst || op2 == dst)); - end.append(jump()); - } + // Int32 result. + emitStoreInt32(dst, regT0, (op1 == dst || op2 == dst)); + end.append(jump()); // Double result. doubleResult.link(this); @@ -1152,13 +1214,8 @@ void JIT::emit_op_lshift(Instruction* currentInstruction) emitJumpSlowCaseIfNotImmediateInteger(regT2); emitFastArithImmToInt(regT0); emitFastArithImmToInt(regT2); -#if !PLATFORM(X86) - // Mask with 0x1f as per ecma-262 11.7.2 step 7. - // On 32-bit x86 this is not necessary, since the shift anount is implicitly masked in the instruction. - and32(Imm32(0x1f), regT2); -#endif lshift32(regT2, regT0); -#if !USE(JSVALUE64) +#if USE(JSVALUE32) addSlowCase(branchAdd32(Overflow, regT0, regT0)); signExtend32ToPtr(regT0, regT0); #endif @@ -1203,11 +1260,7 @@ void JIT::emit_op_rshift(Instruction* currentInstruction) emitGetVirtualRegister(op1, regT0); emitJumpSlowCaseIfNotImmediateInteger(regT0); // Mask with 0x1f as per ecma-262 11.7.2 step 7. -#if USE(JSVALUE64) rshift32(Imm32(getConstantOperandImmediateInt(op2) & 0x1f), regT0); -#else - rshiftPtr(Imm32(getConstantOperandImmediateInt(op2) & 0x1f), regT0); -#endif } else { emitGetVirtualRegisters(op1, regT0, op2, regT2); if (supportsFloatingPointTruncate()) { @@ -1234,15 +1287,9 @@ void JIT::emit_op_rshift(Instruction* currentInstruction) emitJumpSlowCaseIfNotImmediateInteger(regT2); } emitFastArithImmToInt(regT2); -#if !PLATFORM(X86) - // Mask with 0x1f as per ecma-262 11.7.2 step 7. - // On 32-bit x86 this is not necessary, since the shift anount is implicitly masked in the instruction. - and32(Imm32(0x1f), regT2); -#endif -#if USE(JSVALUE64) rshift32(regT2, regT0); -#else - rshiftPtr(regT2, regT0); +#if USE(JSVALUE32) + signExtend32ToPtr(regT0, regT0); #endif } #if USE(JSVALUE64) @@ -1365,7 +1412,7 @@ void JIT::emitSlow_op_jnless(Instruction* currentInstruction, Vector<SlowCaseEnt move(Imm32(op2imm), regT1); convertInt32ToDouble(regT1, fpRegT1); - emitJumpSlowToHot(branchDouble(DoubleLessThanOrEqual, fpRegT1, fpRegT0), target); + emitJumpSlowToHot(branchDouble(DoubleLessThanOrEqualOrUnordered, fpRegT1, fpRegT0), target); emitJumpSlowToHot(jump(), OPCODE_LENGTH(op_jnless)); @@ -1406,7 +1453,7 @@ void JIT::emitSlow_op_jnless(Instruction* currentInstruction, Vector<SlowCaseEnt move(Imm32(op1imm), regT0); convertInt32ToDouble(regT0, fpRegT0); - emitJumpSlowToHot(branchDouble(DoubleLessThanOrEqual, fpRegT1, fpRegT0), target); + emitJumpSlowToHot(branchDouble(DoubleLessThanOrEqualOrUnordered, fpRegT1, fpRegT0), target); emitJumpSlowToHot(jump(), OPCODE_LENGTH(op_jnless)); @@ -1452,7 +1499,7 @@ void JIT::emitSlow_op_jnless(Instruction* currentInstruction, Vector<SlowCaseEnt loadDouble(Address(regT1, OBJECT_OFFSETOF(JSNumberCell, m_value)), fpRegT1); #endif - emitJumpSlowToHot(branchDouble(DoubleLessThanOrEqual, fpRegT1, fpRegT0), target); + emitJumpSlowToHot(branchDouble(DoubleLessThanOrEqualOrUnordered, fpRegT1, fpRegT0), target); emitJumpSlowToHot(jump(), OPCODE_LENGTH(op_jnless)); @@ -1479,6 +1526,191 @@ void JIT::emitSlow_op_jnless(Instruction* currentInstruction, Vector<SlowCaseEnt } } +void JIT::emit_op_jless(Instruction* currentInstruction) +{ + unsigned op1 = currentInstruction[1].u.operand; + unsigned op2 = currentInstruction[2].u.operand; + unsigned target = currentInstruction[3].u.operand; + + // We generate inline code for the following cases in the fast path: + // - int immediate to constant int immediate + // - constant int immediate to int immediate + // - int immediate to int immediate + + if (isOperandConstantImmediateInt(op2)) { + emitGetVirtualRegister(op1, regT0); + emitJumpSlowCaseIfNotImmediateInteger(regT0); +#if USE(JSVALUE64) + int32_t op2imm = getConstantOperandImmediateInt(op2); +#else + int32_t op2imm = static_cast<int32_t>(JSImmediate::rawValue(getConstantOperand(op2))); +#endif + addJump(branch32(LessThan, regT0, Imm32(op2imm)), target); + } else if (isOperandConstantImmediateInt(op1)) { + emitGetVirtualRegister(op2, regT1); + emitJumpSlowCaseIfNotImmediateInteger(regT1); +#if USE(JSVALUE64) + int32_t op1imm = getConstantOperandImmediateInt(op1); +#else + int32_t op1imm = static_cast<int32_t>(JSImmediate::rawValue(getConstantOperand(op1))); +#endif + addJump(branch32(GreaterThan, regT1, Imm32(op1imm)), target); + } else { + emitGetVirtualRegisters(op1, regT0, op2, regT1); + emitJumpSlowCaseIfNotImmediateInteger(regT0); + emitJumpSlowCaseIfNotImmediateInteger(regT1); + + addJump(branch32(LessThan, regT0, regT1), target); + } +} + +void JIT::emitSlow_op_jless(Instruction* currentInstruction, Vector<SlowCaseEntry>::iterator& iter) +{ + unsigned op1 = currentInstruction[1].u.operand; + unsigned op2 = currentInstruction[2].u.operand; + unsigned target = currentInstruction[3].u.operand; + + // We generate inline code for the following cases in the slow path: + // - floating-point number to constant int immediate + // - constant int immediate to floating-point number + // - floating-point number to floating-point number. + + if (isOperandConstantImmediateInt(op2)) { + linkSlowCase(iter); + + if (supportsFloatingPoint()) { +#if USE(JSVALUE64) + Jump fail1 = emitJumpIfNotImmediateNumber(regT0); + addPtr(tagTypeNumberRegister, regT0); + movePtrToDouble(regT0, fpRegT0); +#else + Jump fail1; + if (!m_codeBlock->isKnownNotImmediate(op1)) + fail1 = emitJumpIfNotJSCell(regT0); + + Jump fail2 = checkStructure(regT0, m_globalData->numberStructure.get()); + loadDouble(Address(regT0, OBJECT_OFFSETOF(JSNumberCell, m_value)), fpRegT0); +#endif + + int32_t op2imm = getConstantOperand(op2).asInt32(); + + move(Imm32(op2imm), regT1); + convertInt32ToDouble(regT1, fpRegT1); + + emitJumpSlowToHot(branchDouble(DoubleLessThan, fpRegT0, fpRegT1), target); + + emitJumpSlowToHot(jump(), OPCODE_LENGTH(op_jnless)); + +#if USE(JSVALUE64) + fail1.link(this); +#else + if (!m_codeBlock->isKnownNotImmediate(op1)) + fail1.link(this); + fail2.link(this); +#endif + } + + JITStubCall stubCall(this, cti_op_jless); + stubCall.addArgument(regT0); + stubCall.addArgument(op2, regT2); + stubCall.call(); + emitJumpSlowToHot(branchTest32(NonZero, regT0), target); + + } else if (isOperandConstantImmediateInt(op1)) { + linkSlowCase(iter); + + if (supportsFloatingPoint()) { +#if USE(JSVALUE64) + Jump fail1 = emitJumpIfNotImmediateNumber(regT1); + addPtr(tagTypeNumberRegister, regT1); + movePtrToDouble(regT1, fpRegT1); +#else + Jump fail1; + if (!m_codeBlock->isKnownNotImmediate(op2)) + fail1 = emitJumpIfNotJSCell(regT1); + + Jump fail2 = checkStructure(regT1, m_globalData->numberStructure.get()); + loadDouble(Address(regT1, OBJECT_OFFSETOF(JSNumberCell, m_value)), fpRegT1); +#endif + + int32_t op1imm = getConstantOperand(op1).asInt32(); + + move(Imm32(op1imm), regT0); + convertInt32ToDouble(regT0, fpRegT0); + + emitJumpSlowToHot(branchDouble(DoubleLessThan, fpRegT0, fpRegT1), target); + + emitJumpSlowToHot(jump(), OPCODE_LENGTH(op_jnless)); + +#if USE(JSVALUE64) + fail1.link(this); +#else + if (!m_codeBlock->isKnownNotImmediate(op2)) + fail1.link(this); + fail2.link(this); +#endif + } + + JITStubCall stubCall(this, cti_op_jless); + stubCall.addArgument(op1, regT2); + stubCall.addArgument(regT1); + stubCall.call(); + emitJumpSlowToHot(branchTest32(NonZero, regT0), target); + + } else { + linkSlowCase(iter); + + if (supportsFloatingPoint()) { +#if USE(JSVALUE64) + Jump fail1 = emitJumpIfNotImmediateNumber(regT0); + Jump fail2 = emitJumpIfNotImmediateNumber(regT1); + Jump fail3 = emitJumpIfImmediateInteger(regT1); + addPtr(tagTypeNumberRegister, regT0); + addPtr(tagTypeNumberRegister, regT1); + movePtrToDouble(regT0, fpRegT0); + movePtrToDouble(regT1, fpRegT1); +#else + Jump fail1; + if (!m_codeBlock->isKnownNotImmediate(op1)) + fail1 = emitJumpIfNotJSCell(regT0); + + Jump fail2; + if (!m_codeBlock->isKnownNotImmediate(op2)) + fail2 = emitJumpIfNotJSCell(regT1); + + Jump fail3 = checkStructure(regT0, m_globalData->numberStructure.get()); + Jump fail4 = checkStructure(regT1, m_globalData->numberStructure.get()); + loadDouble(Address(regT0, OBJECT_OFFSETOF(JSNumberCell, m_value)), fpRegT0); + loadDouble(Address(regT1, OBJECT_OFFSETOF(JSNumberCell, m_value)), fpRegT1); +#endif + + emitJumpSlowToHot(branchDouble(DoubleLessThan, fpRegT0, fpRegT1), target); + + emitJumpSlowToHot(jump(), OPCODE_LENGTH(op_jnless)); + +#if USE(JSVALUE64) + fail1.link(this); + fail2.link(this); + fail3.link(this); +#else + if (!m_codeBlock->isKnownNotImmediate(op1)) + fail1.link(this); + if (!m_codeBlock->isKnownNotImmediate(op2)) + fail2.link(this); + fail3.link(this); + fail4.link(this); +#endif + } + + linkSlowCase(iter); + JITStubCall stubCall(this, cti_op_jless); + stubCall.addArgument(regT0); + stubCall.addArgument(regT1); + stubCall.call(); + emitJumpSlowToHot(branchTest32(NonZero, regT0), target); + } +} + void JIT::emit_op_jnlesseq(Instruction* currentInstruction) { unsigned op1 = currentInstruction[1].u.operand; @@ -1550,7 +1782,7 @@ void JIT::emitSlow_op_jnlesseq(Instruction* currentInstruction, Vector<SlowCaseE move(Imm32(op2imm), regT1); convertInt32ToDouble(regT1, fpRegT1); - emitJumpSlowToHot(branchDouble(DoubleLessThan, fpRegT1, fpRegT0), target); + emitJumpSlowToHot(branchDouble(DoubleLessThanOrUnordered, fpRegT1, fpRegT0), target); emitJumpSlowToHot(jump(), OPCODE_LENGTH(op_jnlesseq)); @@ -1591,7 +1823,7 @@ void JIT::emitSlow_op_jnlesseq(Instruction* currentInstruction, Vector<SlowCaseE move(Imm32(op1imm), regT0); convertInt32ToDouble(regT0, fpRegT0); - emitJumpSlowToHot(branchDouble(DoubleLessThan, fpRegT1, fpRegT0), target); + emitJumpSlowToHot(branchDouble(DoubleLessThanOrUnordered, fpRegT1, fpRegT0), target); emitJumpSlowToHot(jump(), OPCODE_LENGTH(op_jnlesseq)); @@ -1637,7 +1869,7 @@ void JIT::emitSlow_op_jnlesseq(Instruction* currentInstruction, Vector<SlowCaseE loadDouble(Address(regT1, OBJECT_OFFSETOF(JSNumberCell, m_value)), fpRegT1); #endif - emitJumpSlowToHot(branchDouble(DoubleLessThan, fpRegT1, fpRegT0), target); + emitJumpSlowToHot(branchDouble(DoubleLessThanOrUnordered, fpRegT1, fpRegT0), target); emitJumpSlowToHot(jump(), OPCODE_LENGTH(op_jnlesseq)); @@ -2160,27 +2392,10 @@ void JIT::emit_op_div(Instruction* currentInstruction) } divDouble(fpRegT1, fpRegT0); - JumpList doubleResult; - Jump end; - bool attemptIntConversion = (!isOperandConstantImmediateInt(op1) || getConstantOperand(op1).asInt32() > 1) && isOperandConstantImmediateInt(op2); - if (attemptIntConversion) { - m_assembler.cvttsd2si_rr(fpRegT0, regT0); - doubleResult.append(branchTest32(Zero, regT0)); - m_assembler.ucomisd_rr(fpRegT1, fpRegT0); - - doubleResult.append(m_assembler.jne()); - doubleResult.append(m_assembler.jp()); - emitFastArithIntToImmNoCheck(regT0, regT0); - end = jump(); - } - // Double result. - doubleResult.link(this); moveDoubleToPtr(fpRegT0, regT0); subPtr(tagTypeNumberRegister, regT0); - if (attemptIntConversion) - end.link(this); emitPutVirtualRegister(dst, regT0); } diff --git a/JavaScriptCore/jit/JITCall.cpp b/JavaScriptCore/jit/JITCall.cpp index f7fcc0a..179aad7 100644 --- a/JavaScriptCore/jit/JITCall.cpp +++ b/JavaScriptCore/jit/JITCall.cpp @@ -315,7 +315,13 @@ void JIT::compileOpCall(OpcodeID opcodeID, Instruction* instruction, unsigned ca emitLoad(callee, regT1, regT0); DataLabelPtr addressOfLinkedFunctionCheck; + + BEGIN_UNINTERRUPTED_SEQUENCE(sequenceOpCall); + Jump jumpToSlow = branchPtrWithPatch(NotEqual, regT0, addressOfLinkedFunctionCheck, ImmPtr(0)); + + END_UNINTERRUPTED_SEQUENCE(sequenceOpCall); + addSlowCase(jumpToSlow); ASSERT(differenceBetween(addressOfLinkedFunctionCheck, jumpToSlow) == patchOffsetOpCallCompareToJump); m_callStructureStubCompilationInfo[callLinkInfoIndex].hotPathBegin = addressOfLinkedFunctionCheck; diff --git a/JavaScriptCore/jit/JITInlineMethods.h b/JavaScriptCore/jit/JITInlineMethods.h index 93d6ce7..1ce6889 100644 --- a/JavaScriptCore/jit/JITInlineMethods.h +++ b/JavaScriptCore/jit/JITInlineMethods.h @@ -37,7 +37,7 @@ namespace JSC { // puts an arg onto the stack, as an arg to a context threaded function. ALWAYS_INLINE void JIT::emitPutJITStubArg(RegisterID src, unsigned argumentNumber) { - unsigned argumentStackOffset = (argumentNumber * (sizeof(JSValue) / sizeof(void*))) + 1; + unsigned argumentStackOffset = (argumentNumber * (sizeof(JSValue) / sizeof(void*))) + JITSTACKFRAME_ARGS_INDEX; poke(src, argumentStackOffset); } @@ -45,7 +45,7 @@ ALWAYS_INLINE void JIT::emitPutJITStubArg(RegisterID src, unsigned argumentNumbe ALWAYS_INLINE void JIT::emitPutJITStubArgConstant(unsigned value, unsigned argumentNumber) { - unsigned argumentStackOffset = (argumentNumber * (sizeof(JSValue) / sizeof(void*))) + 1; + unsigned argumentStackOffset = (argumentNumber * (sizeof(JSValue) / sizeof(void*))) + JITSTACKFRAME_ARGS_INDEX; poke(Imm32(value), argumentStackOffset); } @@ -53,7 +53,7 @@ ALWAYS_INLINE void JIT::emitPutJITStubArgConstant(unsigned value, unsigned argum ALWAYS_INLINE void JIT::emitPutJITStubArgConstant(void* value, unsigned argumentNumber) { - unsigned argumentStackOffset = (argumentNumber * (sizeof(JSValue) / sizeof(void*))) + 1; + unsigned argumentStackOffset = (argumentNumber * (sizeof(JSValue) / sizeof(void*))) + JITSTACKFRAME_ARGS_INDEX; poke(ImmPtr(value), argumentStackOffset); } @@ -61,7 +61,7 @@ ALWAYS_INLINE void JIT::emitPutJITStubArgConstant(void* value, unsigned argument ALWAYS_INLINE void JIT::emitGetJITStubArg(unsigned argumentNumber, RegisterID dst) { - unsigned argumentStackOffset = (argumentNumber * (sizeof(JSValue) / sizeof(void*))) + 1; + unsigned argumentStackOffset = (argumentNumber * (sizeof(JSValue) / sizeof(void*))) + JITSTACKFRAME_ARGS_INDEX; peek(dst, argumentStackOffset); } @@ -585,7 +585,7 @@ ALWAYS_INLINE bool JIT::getOperandConstantImmediateInt(unsigned op1, unsigned op ALWAYS_INLINE void JIT::emitPutJITStubArg(RegisterID tag, RegisterID payload, unsigned argumentNumber) { - unsigned argumentStackOffset = (argumentNumber * (sizeof(JSValue) / sizeof(void*))) + 1; + unsigned argumentStackOffset = (argumentNumber * (sizeof(JSValue) / sizeof(void*))) + JITSTACKFRAME_ARGS_INDEX; poke(payload, argumentStackOffset); poke(tag, argumentStackOffset + 1); } @@ -594,7 +594,7 @@ ALWAYS_INLINE void JIT::emitPutJITStubArg(RegisterID tag, RegisterID payload, un ALWAYS_INLINE void JIT::emitPutJITStubArgFromVirtualRegister(unsigned src, unsigned argumentNumber, RegisterID scratch1, RegisterID scratch2) { - unsigned argumentStackOffset = (argumentNumber * (sizeof(JSValue) / sizeof(void*))) + 1; + unsigned argumentStackOffset = (argumentNumber * (sizeof(JSValue) / sizeof(void*))) + JITSTACKFRAME_ARGS_INDEX; if (m_codeBlock->isConstantRegisterIndex(src)) { JSValue constant = m_codeBlock->getConstant(src); poke(Imm32(constant.payload()), argumentStackOffset); @@ -817,7 +817,7 @@ ALWAYS_INLINE void JIT::emitFastArithImmToInt(RegisterID reg) #if USE(JSVALUE64) UNUSED_PARAM(reg); #else - rshiftPtr(Imm32(JSImmediate::IntegerPayloadShift), reg); + rshift32(Imm32(JSImmediate::IntegerPayloadShift), reg); #endif } @@ -846,7 +846,7 @@ ALWAYS_INLINE void JIT::emitTagAsBoolImmediate(RegisterID reg) // get arg puts an arg from the SF register array onto the stack, as an arg to a context threaded function. ALWAYS_INLINE void JIT::emitPutJITStubArgFromVirtualRegister(unsigned src, unsigned argumentNumber, RegisterID scratch) { - unsigned argumentStackOffset = (argumentNumber * (sizeof(JSValue) / sizeof(void*))) + 1; + unsigned argumentStackOffset = (argumentNumber * (sizeof(JSValue) / sizeof(void*))) + JITSTACKFRAME_ARGS_INDEX; if (m_codeBlock->isConstantRegisterIndex(src)) { JSValue value = m_codeBlock->getConstant(src); poke(ImmPtr(JSValue::encode(value)), argumentStackOffset); diff --git a/JavaScriptCore/jit/JITOpcodes.cpp b/JavaScriptCore/jit/JITOpcodes.cpp index 14736cf..77fec28 100644 --- a/JavaScriptCore/jit/JITOpcodes.cpp +++ b/JavaScriptCore/jit/JITOpcodes.cpp @@ -52,8 +52,7 @@ void JIT::privateCompileCTIMachineTrampolines(RefPtr<ExecutablePool>* executable Jump string_failureCases2 = branchPtr(NotEqual, Address(regT0), ImmPtr(m_globalData->jsStringVPtr)); // Checks out okay! - get the length from the Ustring. - loadPtr(Address(regT0, OBJECT_OFFSETOF(JSString, m_value) + OBJECT_OFFSETOF(UString, m_rep)), regT2); - load32(Address(regT2, OBJECT_OFFSETOF(UString::Rep, len)), regT2); + load32(Address(regT0, OBJECT_OFFSETOF(JSString, m_stringLength)), regT2); Jump string_failureCases3 = branch32(Above, regT2, Imm32(INT_MAX)); move(regT2, regT0); @@ -138,7 +137,7 @@ void JIT::privateCompileCTIMachineTrampolines(RefPtr<ExecutablePool>* executable loadPtr(Address(regT2, OBJECT_OFFSETOF(FunctionExecutable, m_jitCode)), regT0); jump(regT0); -#if PLATFORM(X86) +#if PLATFORM(X86) || PLATFORM(ARM_TRADITIONAL) Label nativeCallThunk = align(); preserveReturnAddressAfterCall(regT0); emitPutToCallFrameHeader(regT0, RegisterFile::ReturnPC); // Push return address @@ -149,6 +148,7 @@ void JIT::privateCompileCTIMachineTrampolines(RefPtr<ExecutablePool>* executable emitGetFromCallFrameHeaderPtr(RegisterFile::ScopeChain, regT1, regT1); emitPutToCallFrameHeader(regT1, RegisterFile::ScopeChain); +#if PLATFORM(X86) emitGetFromCallFrameHeader32(RegisterFile::ArgumentCount, regT0); /* We have two structs that we use to describe the stackframe we set up for our @@ -248,6 +248,66 @@ void JIT::privateCompileCTIMachineTrampolines(RefPtr<ExecutablePool>* executable // so pull them off now addPtr(Imm32(NativeCallFrameSize - sizeof(NativeFunctionCalleeSignature)), stackPointerRegister); +#elif PLATFORM(ARM_TRADITIONAL) + emitGetFromCallFrameHeader32(RegisterFile::ArgumentCount, regT0); + + // Allocate stack space for our arglist + COMPILE_ASSERT((sizeof(ArgList) & 0x7) == 0 && sizeof(JSValue) == 8 && sizeof(Register) == 8, ArgList_should_by_8byte_aligned); + subPtr(Imm32(sizeof(ArgList)), stackPointerRegister); + + // Set up arguments + subPtr(Imm32(1), regT0); // Don't include 'this' in argcount + + // Push argcount + storePtr(regT0, Address(stackPointerRegister, OBJECT_OFFSETOF(ArgList, m_argCount))); + + // Calculate the start of the callframe header, and store in regT1 + move(callFrameRegister, regT1); + sub32(Imm32(RegisterFile::CallFrameHeaderSize * (int32_t)sizeof(Register)), regT1); + + // Calculate start of arguments as callframe header - sizeof(Register) * argcount (regT1) + mul32(Imm32(sizeof(Register)), regT0, regT0); + subPtr(regT0, regT1); + + // push pointer to arguments + storePtr(regT1, Address(stackPointerRegister, OBJECT_OFFSETOF(ArgList, m_args))); + + // Argument passing method: + // r0 - points to return value + // r1 - callFrame + // r2 - callee + // stack: this(JSValue) and a pointer to ArgList + + move(stackPointerRegister, regT3); + subPtr(Imm32(8), stackPointerRegister); + move(stackPointerRegister, regT0); + subPtr(Imm32(8 + 4 + 4 /* padding */), stackPointerRegister); + + // Setup arg4: + storePtr(regT3, Address(stackPointerRegister, 8)); + + // Setup arg3 + // regT1 currently points to the first argument, regT1-sizeof(Register) points to 'this' + load32(Address(regT1, -(int32_t)sizeof(void*) * 2), regT3); + storePtr(regT3, Address(stackPointerRegister, 0)); + load32(Address(regT1, -(int32_t)sizeof(void*)), regT3); + storePtr(regT3, Address(stackPointerRegister, 4)); + + // Setup arg2: + emitGetFromCallFrameHeaderPtr(RegisterFile::Callee, regT2); + + // Setup arg1: + move(callFrameRegister, regT1); + + call(Address(regT2, OBJECT_OFFSETOF(JSFunction, m_data))); + + // Load return value + load32(Address(stackPointerRegister, 16), regT0); + load32(Address(stackPointerRegister, 20), regT1); + + addPtr(Imm32(sizeof(ArgList) + 16 + 8), stackPointerRegister); +#endif + // Check for an exception move(ImmPtr(&globalData->exception), regT2); Jump sawException = branch32(NotEqual, tagFor(0, regT2), Imm32(JSValue::EmptyValueTag)); @@ -394,7 +454,7 @@ void JIT::emitSlow_op_loop_if_less(Instruction* currentInstruction, Vector<SlowC linkSlowCase(iter); // int32 check linkSlowCase(iter); // int32 check - JITStubCall stubCall(this, cti_op_loop_if_less); + JITStubCall stubCall(this, cti_op_jless); stubCall.addArgument(op1); stubCall.addArgument(op2); stubCall.call(); @@ -683,6 +743,50 @@ void JIT::emitSlow_op_loop_if_true(Instruction* currentInstruction, Vector<SlowC emitJumpSlowToHot(branchTest32(NonZero, regT0), target); } +void JIT::emit_op_loop_if_false(Instruction* currentInstruction) +{ + unsigned cond = currentInstruction[1].u.operand; + unsigned target = currentInstruction[2].u.operand; + + emitTimeoutCheck(); + + emitLoad(cond, regT1, regT0); + + Jump isTrue = branch32(Equal, regT1, Imm32(JSValue::TrueTag)); + addJump(branch32(Equal, regT1, Imm32(JSValue::FalseTag)), target); + + Jump isNotInteger = branch32(NotEqual, regT1, Imm32(JSValue::Int32Tag)); + Jump isTrue2 = branch32(NotEqual, regT0, Imm32(0)); + addJump(jump(), target); + + if (supportsFloatingPoint()) { + isNotInteger.link(this); + + addSlowCase(branch32(Above, regT1, Imm32(JSValue::LowestTag))); + + zeroDouble(fpRegT0); + emitLoadDouble(cond, fpRegT1); + addJump(branchDouble(DoubleEqualOrUnordered, fpRegT0, fpRegT1), target); + } else + addSlowCase(isNotInteger); + + isTrue.link(this); + isTrue2.link(this); +} + +void JIT::emitSlow_op_loop_if_false(Instruction* currentInstruction, Vector<SlowCaseEntry>::iterator& iter) +{ + unsigned cond = currentInstruction[1].u.operand; + unsigned target = currentInstruction[2].u.operand; + + linkSlowCase(iter); + + JITStubCall stubCall(this, cti_op_jtrue); + stubCall.addArgument(cond); + stubCall.call(); + emitJumpSlowToHot(branchTest32(Zero, regT0), target); +} + void JIT::emit_op_resolve_base(Instruction* currentInstruction) { JITStubCall stubCall(this, cti_op_resolve_base); @@ -786,7 +890,7 @@ void JIT::emit_op_jfalse(Instruction* currentInstruction) zeroDouble(fpRegT0); emitLoadDouble(cond, fpRegT1); - addJump(branchDouble(DoubleEqual, fpRegT0, fpRegT1), target); + addJump(branchDouble(DoubleEqualOrUnordered, fpRegT0, fpRegT1), target); } else addSlowCase(isNotInteger); @@ -1532,8 +1636,7 @@ void JIT::privateCompileCTIMachineTrampolines(RefPtr<ExecutablePool>* executable Jump string_failureCases2 = branchPtr(NotEqual, Address(regT0), ImmPtr(m_globalData->jsStringVPtr)); // Checks out okay! - get the length from the Ustring. - loadPtr(Address(regT0, OBJECT_OFFSETOF(JSString, m_value) + OBJECT_OFFSETOF(UString, m_rep)), regT0); - load32(Address(regT0, OBJECT_OFFSETOF(UString::Rep, len)), regT0); + load32(Address(regT0, OBJECT_OFFSETOF(JSString, m_stringLength)), regT0); Jump string_failureCases3 = branch32(Above, regT0, Imm32(JSImmediate::maxImmediateInt)); @@ -1756,7 +1859,7 @@ void JIT::privateCompileCTIMachineTrampolines(RefPtr<ExecutablePool>* executable // so pull them off now addPtr(Imm32(NativeCallFrameSize - sizeof(NativeFunctionCalleeSignature)), stackPointerRegister); -#elif PLATFORM(ARM_TRADITIONAL) +#elif PLATFORM(ARM) emitGetFromCallFrameHeader32(RegisterFile::ArgumentCount, regT0); // Allocate stack space for our arglist @@ -1790,7 +1893,7 @@ void JIT::privateCompileCTIMachineTrampolines(RefPtr<ExecutablePool>* executable move(callFrameRegister, regT0); // Setup arg4: This is a plain hack - move(stackPointerRegister, ARMRegisters::S0); + move(stackPointerRegister, ARMRegisters::r3); call(Address(regT1, OBJECT_OFFSETOF(JSFunction, m_data))); @@ -2196,6 +2299,25 @@ void JIT::emit_op_loop_if_true(Instruction* currentInstruction) isZero.link(this); }; + +void JIT::emit_op_loop_if_false(Instruction* currentInstruction) +{ + emitTimeoutCheck(); + + + unsigned target = currentInstruction[2].u.operand; + emitGetVirtualRegister(currentInstruction[1].u.operand, regT0); + + addJump(branchPtr(Equal, regT0, ImmPtr(JSValue::encode(jsNumber(m_globalData, 0)))), target); + Jump isNonZero = emitJumpIfImmediateInteger(regT0); + + addJump(branchPtr(Equal, regT0, ImmPtr(JSValue::encode(jsBoolean(false)))), target); + addSlowCase(branchPtr(NotEqual, regT0, ImmPtr(JSValue::encode(jsBoolean(true))))); + + isNonZero.link(this); + RECORD_JUMP_TARGET(target); +}; + void JIT::emit_op_resolve_base(Instruction* currentInstruction) { JITStubCall stubCall(this, cti_op_resolve_base); @@ -2882,14 +3004,14 @@ void JIT::emitSlow_op_loop_if_less(Instruction* currentInstruction, Vector<SlowC unsigned target = currentInstruction[3].u.operand; if (isOperandConstantImmediateInt(op2)) { linkSlowCase(iter); - JITStubCall stubCall(this, cti_op_loop_if_less); + JITStubCall stubCall(this, cti_op_jless); stubCall.addArgument(regT0); stubCall.addArgument(op2, regT2); stubCall.call(); emitJumpSlowToHot(branchTest32(NonZero, regT0), target); } else if (isOperandConstantImmediateInt(op1)) { linkSlowCase(iter); - JITStubCall stubCall(this, cti_op_loop_if_less); + JITStubCall stubCall(this, cti_op_jless); stubCall.addArgument(op1, regT2); stubCall.addArgument(regT0); stubCall.call(); @@ -2897,7 +3019,7 @@ void JIT::emitSlow_op_loop_if_less(Instruction* currentInstruction, Vector<SlowC } else { linkSlowCase(iter); linkSlowCase(iter); - JITStubCall stubCall(this, cti_op_loop_if_less); + JITStubCall stubCall(this, cti_op_jless); stubCall.addArgument(regT0); stubCall.addArgument(regT1); stubCall.call(); @@ -2954,6 +3076,15 @@ void JIT::emitSlow_op_loop_if_true(Instruction* currentInstruction, Vector<SlowC emitJumpSlowToHot(branchTest32(NonZero, regT0), currentInstruction[2].u.operand); } +void JIT::emitSlow_op_loop_if_false(Instruction* currentInstruction, Vector<SlowCaseEntry>::iterator& iter) +{ + linkSlowCase(iter); + JITStubCall stubCall(this, cti_op_jtrue); + stubCall.addArgument(regT0); + stubCall.call(); + emitJumpSlowToHot(branchTest32(Zero, regT0), currentInstruction[2].u.operand); // inverted! +} + void JIT::emitSlow_op_not(Instruction* currentInstruction, Vector<SlowCaseEntry>::iterator& iter) { linkSlowCase(iter); diff --git a/JavaScriptCore/jit/JITPropertyAccess.cpp b/JavaScriptCore/jit/JITPropertyAccess.cpp index bf367a6..e2995a1 100644 --- a/JavaScriptCore/jit/JITPropertyAccess.cpp +++ b/JavaScriptCore/jit/JITPropertyAccess.cpp @@ -212,12 +212,17 @@ void JIT::emit_op_method_check(Instruction* currentInstruction) emitLoad(base, regT1, regT0); emitJumpSlowCaseIfNotJSCell(base, regT1); + BEGIN_UNINTERRUPTED_SEQUENCE(sequenceMethodCheck); + Jump structureCheck = branchPtrWithPatch(NotEqual, Address(regT0, OBJECT_OFFSETOF(JSCell, m_structure)), info.structureToCompare, ImmPtr(reinterpret_cast<void*>(patchGetByIdDefaultStructure))); DataLabelPtr protoStructureToCompare, protoObj = moveWithPatch(ImmPtr(0), regT2); Jump protoStructureCheck = branchPtrWithPatch(NotEqual, Address(regT2, OBJECT_OFFSETOF(JSCell, m_structure)), protoStructureToCompare, ImmPtr(reinterpret_cast<void*>(patchGetByIdDefaultStructure))); // This will be relinked to load the function without doing a load. DataLabelPtr putFunction = moveWithPatch(ImmPtr(0), regT0); + + END_UNINTERRUPTED_SEQUENCE(sequenceMethodCheck); + move(Imm32(JSValue::CellTag), regT1); Jump match = jump(); @@ -374,6 +379,9 @@ void JIT::compileGetByIdHotPath() // Additionally, for get_by_id we need patch the offset of the branch to the slow case (we patch this to jump // to array-length / prototype access tranpolines, and finally we also the the property-map access offset as a label // to jump back to if one of these trampolies finds a match. + + BEGIN_UNINTERRUPTED_SEQUENCE(sequenceGetByIdHotPath); + Label hotPathBegin(this); m_propertyAccessCompilationInfo[m_propertyAccessInstructionIndex].hotPathBegin = hotPathBegin; m_propertyAccessInstructionIndex++; @@ -396,6 +404,8 @@ void JIT::compileGetByIdHotPath() Label putResult(this); ASSERT(differenceBetween(hotPathBegin, putResult) == patchOffsetGetByIdPutResult); + + END_UNINTERRUPTED_SEQUENCE(sequenceGetByIdHotPath); } void JIT::emitSlow_op_get_by_id(Instruction* currentInstruction, Vector<SlowCaseEntry>::iterator& iter) @@ -417,13 +427,18 @@ void JIT::compileGetByIdSlowCase(int dst, int base, Identifier* ident, Vector<Sl linkSlowCaseIfNotJSCell(iter, base); linkSlowCase(iter); - Label coldPathBegin(this); + BEGIN_UNINTERRUPTED_SEQUENCE(sequenceGetByIdSlowCase); +#ifndef NDEBUG + Label coldPathBegin(this); +#endif JITStubCall stubCall(this, isMethodCheck ? cti_op_get_by_id_method_check : cti_op_get_by_id); stubCall.addArgument(regT1, regT0); stubCall.addArgument(ImmPtr(ident)); Call call = stubCall.call(dst); + END_UNINTERRUPTED_SEQUENCE(sequenceGetByIdSlowCase); + ASSERT(differenceBetween(coldPathBegin, call) == patchOffsetGetByIdSlowCaseCall); // Track the location of the call; this will be used to recover patch information. @@ -444,6 +459,8 @@ void JIT::emit_op_put_by_id(Instruction* currentInstruction) emitJumpSlowCaseIfNotJSCell(base, regT1); + BEGIN_UNINTERRUPTED_SEQUENCE(sequencePutById); + Label hotPathBegin(this); m_propertyAccessCompilationInfo[m_propertyAccessInstructionIndex].hotPathBegin = hotPathBegin; m_propertyAccessInstructionIndex++; @@ -461,6 +478,9 @@ void JIT::emit_op_put_by_id(Instruction* currentInstruction) DataLabel32 displacementLabel1 = storePtrWithAddressOffsetPatch(regT2, Address(regT0, patchGetByIdDefaultOffset)); // payload DataLabel32 displacementLabel2 = storePtrWithAddressOffsetPatch(regT3, Address(regT0, patchGetByIdDefaultOffset)); // tag + + END_UNINTERRUPTED_SEQUENCE(sequencePutById); + ASSERT(differenceBetween(hotPathBegin, displacementLabel1) == patchOffsetPutByIdPropertyMapOffset1); ASSERT(differenceBetween(hotPathBegin, displacementLabel2) == patchOffsetPutByIdPropertyMapOffset2); } diff --git a/JavaScriptCore/jit/JITStubCall.h b/JavaScriptCore/jit/JITStubCall.h index c5ed9e3..cfbd7dc 100644 --- a/JavaScriptCore/jit/JITStubCall.h +++ b/JavaScriptCore/jit/JITStubCall.h @@ -38,7 +38,7 @@ namespace JSC { : m_jit(jit) , m_stub(stub) , m_returnType(Cell) - , m_stackIndex(stackIndexStart) + , m_stackIndex(JITSTACKFRAME_ARGS_INDEX) { } @@ -46,7 +46,7 @@ namespace JSC { : m_jit(jit) , m_stub(stub) , m_returnType(Cell) - , m_stackIndex(stackIndexStart) + , m_stackIndex(JITSTACKFRAME_ARGS_INDEX) { } @@ -54,7 +54,7 @@ namespace JSC { : m_jit(jit) , m_stub(stub) , m_returnType(VoidPtr) - , m_stackIndex(stackIndexStart) + , m_stackIndex(JITSTACKFRAME_ARGS_INDEX) { } @@ -62,7 +62,7 @@ namespace JSC { : m_jit(jit) , m_stub(stub) , m_returnType(Int) - , m_stackIndex(stackIndexStart) + , m_stackIndex(JITSTACKFRAME_ARGS_INDEX) { } @@ -70,7 +70,7 @@ namespace JSC { : m_jit(jit) , m_stub(stub) , m_returnType(Int) - , m_stackIndex(stackIndexStart) + , m_stackIndex(JITSTACKFRAME_ARGS_INDEX) { } @@ -78,7 +78,7 @@ namespace JSC { : m_jit(jit) , m_stub(stub) , m_returnType(Void) - , m_stackIndex(stackIndexStart) + , m_stackIndex(JITSTACKFRAME_ARGS_INDEX) { } @@ -87,7 +87,7 @@ namespace JSC { : m_jit(jit) , m_stub(stub) , m_returnType(Value) - , m_stackIndex(stackIndexStart) + , m_stackIndex(JITSTACKFRAME_ARGS_INDEX) { } #endif @@ -145,7 +145,7 @@ namespace JSC { void getArgument(size_t argumentNumber, JIT::RegisterID tag, JIT::RegisterID payload) { - size_t stackIndex = stackIndexStart + (argumentNumber * stackIndexStep); + size_t stackIndex = JITSTACKFRAME_ARGS_INDEX + (argumentNumber * stackIndexStep); m_jit->peek(payload, stackIndex); m_jit->peek(tag, stackIndex + 1); } @@ -222,7 +222,6 @@ namespace JSC { private: static const size_t stackIndexStep = sizeof(EncodedJSValue) == 2 * sizeof(void*) ? 2 : 1; - static const size_t stackIndexStart = 1; // Index 0 is reserved for restoreArgumentReference(). JIT* m_jit; FunctionPtr m_stub; diff --git a/JavaScriptCore/jit/JITStubs.cpp b/JavaScriptCore/jit/JITStubs.cpp index c999618..8dd7a97 100644 --- a/JavaScriptCore/jit/JITStubs.cpp +++ b/JavaScriptCore/jit/JITStubs.cpp @@ -56,6 +56,7 @@ #include "RegExpPrototype.h" #include "Register.h" #include "SamplingTool.h" +#include <wtf/StdLibExtras.h> #include <stdarg.h> #include <stdio.h> @@ -81,6 +82,19 @@ namespace JSC { #define SYMBOL_STRING_RELOCATION(name) SYMBOL_STRING(name) #endif +#if PLATFORM(DARWIN) + // Mach-O platform +#define HIDE_SYMBOL(name) ".private_extern _" #name +#elif PLATFORM(AIX) + // IBM's own file format +#define HIDE_SYMBOL(name) ".lglobl " #name +#elif PLATFORM(LINUX) || PLATFORM(FREEBSD) || PLATFORM(OPENBSD) || PLATFORM(SOLARIS) || (PLATFORM(HPUX) && PLATFORM(IA64)) || PLATFORM(SYMBIAN) || PLATFORM(NETBSD) + // ELF platform +#define HIDE_SYMBOL(name) ".hidden " #name +#else +#define HIDE_SYMBOL(name) +#endif + #if USE(JSVALUE32_64) #if COMPILER(GCC) && PLATFORM(X86) @@ -93,7 +107,9 @@ COMPILE_ASSERT(offsetof(struct JITStackFrame, callFrame) == 0x58, JITStackFrame_ COMPILE_ASSERT(offsetof(struct JITStackFrame, code) == 0x50, JITStackFrame_code_offset_matches_ctiTrampoline); asm volatile ( +".text\n" ".globl " SYMBOL_STRING(ctiTrampoline) "\n" +HIDE_SYMBOL(ctiTrampoline) "\n" SYMBOL_STRING(ctiTrampoline) ":" "\n" "pushl %ebp" "\n" "movl %esp, %ebp" "\n" @@ -114,6 +130,7 @@ SYMBOL_STRING(ctiTrampoline) ":" "\n" asm volatile ( ".globl " SYMBOL_STRING(ctiVMThrowTrampoline) "\n" +HIDE_SYMBOL(ctiVMThrowTrampoline) "\n" SYMBOL_STRING(ctiVMThrowTrampoline) ":" "\n" #if !USE(JIT_STUB_ARGUMENT_VA_LIST) "movl %esp, %ecx" "\n" @@ -129,6 +146,7 @@ SYMBOL_STRING(ctiVMThrowTrampoline) ":" "\n" asm volatile ( ".globl " SYMBOL_STRING(ctiOpThrowNotCaught) "\n" +HIDE_SYMBOL(ctiOpThrowNotCaught) "\n" SYMBOL_STRING(ctiOpThrowNotCaught) ":" "\n" "addl $0x3c, %esp" "\n" "popl %ebx" "\n" @@ -153,6 +171,7 @@ COMPILE_ASSERT(offsetof(struct JITStackFrame, code) == 0x80, JITStackFrame_code_ asm volatile ( ".globl " SYMBOL_STRING(ctiTrampoline) "\n" +HIDE_SYMBOL(ctiTrampoline) "\n" SYMBOL_STRING(ctiTrampoline) ":" "\n" "pushq %rbp" "\n" "movq %rsp, %rbp" "\n" @@ -179,6 +198,7 @@ SYMBOL_STRING(ctiTrampoline) ":" "\n" asm volatile ( ".globl " SYMBOL_STRING(ctiVMThrowTrampoline) "\n" +HIDE_SYMBOL(ctiVMThrowTrampoline) "\n" SYMBOL_STRING(ctiVMThrowTrampoline) ":" "\n" "movq %rsp, %rdi" "\n" "call " SYMBOL_STRING_RELOCATION(cti_vm_throw) "\n" @@ -194,6 +214,7 @@ SYMBOL_STRING(ctiVMThrowTrampoline) ":" "\n" asm volatile ( ".globl " SYMBOL_STRING(ctiOpThrowNotCaught) "\n" +HIDE_SYMBOL(ctiOpThrowNotCaught) "\n" SYMBOL_STRING(ctiOpThrowNotCaught) ":" "\n" "addq $0x48, %rsp" "\n" "popq %rbx" "\n" @@ -215,6 +236,7 @@ asm volatile ( ".text" "\n" ".align 2" "\n" ".globl " SYMBOL_STRING(ctiTrampoline) "\n" +HIDE_SYMBOL(ctiTrampoline) "\n" ".thumb" "\n" ".thumb_func " THUMB_FUNC_PARAM(ctiTrampoline) "\n" SYMBOL_STRING(ctiTrampoline) ":" "\n" @@ -241,6 +263,7 @@ asm volatile ( ".text" "\n" ".align 2" "\n" ".globl " SYMBOL_STRING(ctiVMThrowTrampoline) "\n" +HIDE_SYMBOL(ctiVMThrowTrampoline) "\n" ".thumb" "\n" ".thumb_func " THUMB_FUNC_PARAM(ctiVMThrowTrampoline) "\n" SYMBOL_STRING(ctiVMThrowTrampoline) ":" "\n" @@ -269,6 +292,40 @@ SYMBOL_STRING(ctiOpThrowNotCaught) ":" "\n" "bx lr" "\n" ); +#elif COMPILER(GCC) && PLATFORM(ARM_TRADITIONAL) + +asm volatile ( +".globl " SYMBOL_STRING(ctiTrampoline) "\n" +SYMBOL_STRING(ctiTrampoline) ":" "\n" + "stmdb sp!, {r1-r3}" "\n" + "stmdb sp!, {r4-r8, lr}" "\n" + "sub sp, sp, #68" "\n" + "mov r4, r2" "\n" + "mov r5, #512" "\n" + // r0 contains the code + "mov lr, pc" "\n" + "mov pc, r0" "\n" + "add sp, sp, #68" "\n" + "ldmia sp!, {r4-r8, lr}" "\n" + "add sp, sp, #12" "\n" + "mov pc, lr" "\n" +); + +asm volatile ( +".globl " SYMBOL_STRING(ctiVMThrowTrampoline) "\n" +SYMBOL_STRING(ctiVMThrowTrampoline) ":" "\n" + "mov r0, sp" "\n" + "bl " SYMBOL_STRING(cti_vm_throw) "\n" + +// Both has the same return sequence +".globl " SYMBOL_STRING(ctiOpThrowNotCaught) "\n" +SYMBOL_STRING(ctiOpThrowNotCaught) ":" "\n" + "add sp, sp, #68" "\n" + "ldmia sp!, {r4-r8, lr}" "\n" + "add sp, sp, #12" "\n" + "mov pc, lr" "\n" +); + #elif COMPILER(MSVC) #if USE(JIT_STUB_ARGUMENT_VA_LIST) @@ -346,7 +403,9 @@ COMPILE_ASSERT(offsetof(struct JITStackFrame, code) == 0x30, JITStackFrame_code_ COMPILE_ASSERT(offsetof(struct JITStackFrame, savedEBX) == 0x1c, JITStackFrame_stub_argument_space_matches_ctiTrampoline); asm volatile ( +".text\n" ".globl " SYMBOL_STRING(ctiTrampoline) "\n" +HIDE_SYMBOL(ctiTrampoline) "\n" SYMBOL_STRING(ctiTrampoline) ":" "\n" "pushl %ebp" "\n" "movl %esp, %ebp" "\n" @@ -367,6 +426,7 @@ SYMBOL_STRING(ctiTrampoline) ":" "\n" asm volatile ( ".globl " SYMBOL_STRING(ctiVMThrowTrampoline) "\n" +HIDE_SYMBOL(ctiVMThrowTrampoline) "\n" SYMBOL_STRING(ctiVMThrowTrampoline) ":" "\n" #if !USE(JIT_STUB_ARGUMENT_VA_LIST) "movl %esp, %ecx" "\n" @@ -382,6 +442,7 @@ SYMBOL_STRING(ctiVMThrowTrampoline) ":" "\n" asm volatile ( ".globl " SYMBOL_STRING(ctiOpThrowNotCaught) "\n" +HIDE_SYMBOL(ctiOpThrowNotCaught) "\n" SYMBOL_STRING(ctiOpThrowNotCaught) ":" "\n" "addl $0x1c, %esp" "\n" "popl %ebx" "\n" @@ -404,7 +465,9 @@ COMPILE_ASSERT(offsetof(struct JITStackFrame, code) == 0x48, JITStackFrame_code_ COMPILE_ASSERT(offsetof(struct JITStackFrame, savedRBX) == 0x78, JITStackFrame_stub_argument_space_matches_ctiTrampoline); asm volatile ( +".text\n" ".globl " SYMBOL_STRING(ctiTrampoline) "\n" +HIDE_SYMBOL(ctiTrampoline) "\n" SYMBOL_STRING(ctiTrampoline) ":" "\n" "pushq %rbp" "\n" "movq %rsp, %rbp" "\n" @@ -438,6 +501,7 @@ SYMBOL_STRING(ctiTrampoline) ":" "\n" asm volatile ( ".globl " SYMBOL_STRING(ctiVMThrowTrampoline) "\n" +HIDE_SYMBOL(ctiVMThrowTrampoline) "\n" SYMBOL_STRING(ctiVMThrowTrampoline) ":" "\n" "movq %rsp, %rdi" "\n" "call " SYMBOL_STRING_RELOCATION(cti_vm_throw) "\n" @@ -453,6 +517,7 @@ SYMBOL_STRING(ctiVMThrowTrampoline) ":" "\n" asm volatile ( ".globl " SYMBOL_STRING(ctiOpThrowNotCaught) "\n" +HIDE_SYMBOL(ctiOpThrowNotCaught) "\n" SYMBOL_STRING(ctiOpThrowNotCaught) ":" "\n" "addq $0x78, %rsp" "\n" "popq %rbx" "\n" @@ -474,6 +539,7 @@ asm volatile ( ".text" "\n" ".align 2" "\n" ".globl " SYMBOL_STRING(ctiTrampoline) "\n" +HIDE_SYMBOL(ctiTrampoline) "\n" ".thumb" "\n" ".thumb_func " THUMB_FUNC_PARAM(ctiTrampoline) "\n" SYMBOL_STRING(ctiTrampoline) ":" "\n" @@ -500,6 +566,7 @@ asm volatile ( ".text" "\n" ".align 2" "\n" ".globl " SYMBOL_STRING(ctiVMThrowTrampoline) "\n" +HIDE_SYMBOL(ctiVMThrowTrampoline) "\n" ".thumb" "\n" ".thumb_func " THUMB_FUNC_PARAM(ctiVMThrowTrampoline) "\n" SYMBOL_STRING(ctiVMThrowTrampoline) ":" "\n" @@ -517,6 +584,7 @@ asm volatile ( ".text" "\n" ".align 2" "\n" ".globl " SYMBOL_STRING(ctiOpThrowNotCaught) "\n" +HIDE_SYMBOL(ctiOpThrowNotCaught) "\n" ".thumb" "\n" ".thumb_func " THUMB_FUNC_PARAM(ctiOpThrowNotCaught) "\n" SYMBOL_STRING(ctiOpThrowNotCaught) ":" "\n" @@ -531,7 +599,9 @@ SYMBOL_STRING(ctiOpThrowNotCaught) ":" "\n" #elif COMPILER(GCC) && PLATFORM(ARM_TRADITIONAL) asm volatile ( +".text\n" ".globl " SYMBOL_STRING(ctiTrampoline) "\n" +HIDE_SYMBOL(ctiTrampoline) "\n" SYMBOL_STRING(ctiTrampoline) ":" "\n" "stmdb sp!, {r1-r3}" "\n" "stmdb sp!, {r4-r8, lr}" "\n" @@ -548,12 +618,14 @@ SYMBOL_STRING(ctiTrampoline) ":" "\n" asm volatile ( ".globl " SYMBOL_STRING(ctiVMThrowTrampoline) "\n" +HIDE_SYMBOL(ctiVMThrowTrampoline) "\n" SYMBOL_STRING(ctiVMThrowTrampoline) ":" "\n" "mov r0, sp" "\n" "bl " SYMBOL_STRING_RELOCATION(cti_vm_throw) "\n" // Both has the same return sequence ".globl " SYMBOL_STRING(ctiOpThrowNotCaught) "\n" +HIDE_SYMBOL(ctiOpThrowNotCaught) "\n" SYMBOL_STRING(ctiOpThrowNotCaught) ":" "\n" "add sp, sp, #36" "\n" "ldmia sp!, {r4-r8, lr}" "\n" @@ -758,6 +830,11 @@ NEVER_INLINE void JITThunks::tryCacheGetByID(CallFrame* callFrame, CodeBlock* co return; } + if (structure->isDictionary()) { + ctiPatchCallByReturnAddress(codeBlock, returnAddress, FunctionPtr(cti_op_get_by_id_generic)); + return; + } + if (slot.slotBase() == structure->prototypeForLookup(callFrame)) { ASSERT(slot.slotBase().isObject()); @@ -766,10 +843,12 @@ NEVER_INLINE void JITThunks::tryCacheGetByID(CallFrame* callFrame, CodeBlock* co // Since we're accessing a prototype in a loop, it's a good bet that it // should not be treated as a dictionary. if (slotBaseObject->structure()->isDictionary()) - slotBaseObject->setStructure(Structure::fromDictionaryTransition(slotBaseObject->structure())); + slotBaseObject->flattenDictionaryObject(); stubInfo->initGetByIdProto(structure, slotBaseObject->structure()); + ASSERT(!structure->isDictionary()); + ASSERT(!slotBaseObject->structure()->isDictionary()); JIT::compileGetByIdProto(callFrame->scopeChain()->globalData, callFrame, codeBlock, stubInfo, structure, slotBaseObject->structure(), slot.cachedOffset(), returnAddress); return; } @@ -888,6 +967,7 @@ static NEVER_INLINE void throwStackOverflowError(CallFrame* callFrame, JSGlobalD ".text" "\n" \ ".align 2" "\n" \ ".globl " SYMBOL_STRING(cti_##op) "\n" \ + HIDE_SYMBOL(cti_##op) "\n" \ ".thumb" "\n" \ ".thumb_func " THUMB_FUNC_PARAM(cti_##op) "\n" \ SYMBOL_STRING(cti_##op) ":" "\n" \ @@ -900,6 +980,14 @@ static NEVER_INLINE void throwStackOverflowError(CallFrame* callFrame, JSGlobalD #elif PLATFORM(ARM_TRADITIONAL) && COMPILER(GCC) +#if USE(JSVALUE32_64) +#define THUNK_RETURN_ADDRESS_OFFSET 64 +#else +#define THUNK_RETURN_ADDRESS_OFFSET 32 +#endif + +COMPILE_ASSERT(offsetof(struct JITStackFrame, thunkReturnAddress) == THUNK_RETURN_ADDRESS_OFFSET, JITStackFrame_thunkReturnAddress_offset_mismatch); + #define DEFINE_STUB_FUNCTION(rtype, op) \ extern "C" { \ rtype JITStubThunked_##op(STUB_ARGS_DECLARATION); \ @@ -907,9 +995,9 @@ static NEVER_INLINE void throwStackOverflowError(CallFrame* callFrame, JSGlobalD asm volatile ( \ ".globl " SYMBOL_STRING(cti_##op) "\n" \ SYMBOL_STRING(cti_##op) ":" "\n" \ - "str lr, [sp, #32]" "\n" \ + "str lr, [sp, #" STRINGIZE_VALUE_OF(THUNK_RETURN_ADDRESS_OFFSET) "]" "\n" \ "bl " SYMBOL_STRING(JITStubThunked_##op) "\n" \ - "ldr lr, [sp, #32]" "\n" \ + "ldr lr, [sp, #" STRINGIZE_VALUE_OF(THUNK_RETURN_ADDRESS_OFFSET) "]" "\n" \ "mov pc, lr" "\n" \ ); \ rtype JITStubThunked_##op(STUB_ARGS_DECLARATION) @@ -945,38 +1033,19 @@ DEFINE_STUB_FUNCTION(EncodedJSValue, op_add) JSValue v1 = stackFrame.args[0].jsValue(); JSValue v2 = stackFrame.args[1].jsValue(); - - double left; - double right = 0.0; - - bool rightIsNumber = v2.getNumber(right); - if (rightIsNumber && v1.getNumber(left)) - return JSValue::encode(jsNumber(stackFrame.globalData, left + right)); - CallFrame* callFrame = stackFrame.callFrame; - bool leftIsString = v1.isString(); - if (leftIsString && v2.isString()) { - RefPtr<UString::Rep> value = concatenate(asString(v1)->value().rep(), asString(v2)->value().rep()); - if (UNLIKELY(!value)) { - throwOutOfMemoryError(callFrame); - VM_THROW_EXCEPTION(); - } - - return JSValue::encode(jsString(stackFrame.globalData, value.release())); + if (v1.isString()) { + JSValue result = v2.isString() + ? jsString(callFrame, asString(v1), asString(v2)) + : jsString(callFrame, asString(v1), v2.toString(callFrame)); + CHECK_FOR_EXCEPTION_AT_END(); + return JSValue::encode(result); } - if (rightIsNumber & leftIsString) { - RefPtr<UString::Rep> value = v2.isInt32() ? - concatenate(asString(v1)->value().rep(), v2.asInt32()) : - concatenate(asString(v1)->value().rep(), right); - - if (UNLIKELY(!value)) { - throwOutOfMemoryError(callFrame); - VM_THROW_EXCEPTION(); - } - return JSValue::encode(jsString(stackFrame.globalData, value.release())); - } + double left = 0.0, right; + if (v1.getNumber(left) && v2.getNumber(right)) + return JSValue::encode(jsNumber(stackFrame.globalData, left + right)); // All other cases are pretty uncommon JSValue result = jsAddSlowCase(callFrame, v1, v2); @@ -1025,19 +1094,6 @@ DEFINE_STUB_FUNCTION(void, register_file_check) throwStackOverflowError(oldCallFrame, stackFrame.globalData, ReturnAddressPtr(oldCallFrame->returnPC()), STUB_RETURN_ADDRESS); } -DEFINE_STUB_FUNCTION(int, op_loop_if_less) -{ - STUB_INIT_STACK_FRAME(stackFrame); - - JSValue src1 = stackFrame.args[0].jsValue(); - JSValue src2 = stackFrame.args[1].jsValue(); - CallFrame* callFrame = stackFrame.callFrame; - - bool result = jsLess(callFrame, src1, src2); - CHECK_FOR_EXCEPTION_AT_END(); - return result; -} - DEFINE_STUB_FUNCTION(int, op_loop_if_lesseq) { STUB_INIT_STACK_FRAME(stackFrame); @@ -1176,7 +1232,7 @@ DEFINE_STUB_FUNCTION(EncodedJSValue, op_get_by_id_method_check) // Since we're accessing a prototype in a loop, it's a good bet that it // should not be treated as a dictionary. if (slotBaseObject->structure()->isDictionary()) - slotBaseObject->setStructure(Structure::fromDictionaryTransition(slotBaseObject->structure())); + slotBaseObject->flattenDictionaryObject(); // The result fetched should always be the callee! ASSERT(result == JSValue(callee)); @@ -1311,7 +1367,7 @@ DEFINE_STUB_FUNCTION(EncodedJSValue, op_get_by_id_proto_list) CHECK_FOR_EXCEPTION(); - if (!baseValue.isCell() || !slot.isCacheable() || asCell(baseValue)->structure()->isUncacheableDictionary()) { + if (!baseValue.isCell() || !slot.isCacheable() || asCell(baseValue)->structure()->isDictionary()) { ctiPatchCallByReturnAddress(callFrame->codeBlock(), STUB_RETURN_ADDRESS, FunctionPtr(cti_op_get_by_id_proto_fail)); return JSValue::encode(result); } @@ -1326,10 +1382,11 @@ DEFINE_STUB_FUNCTION(EncodedJSValue, op_get_by_id_proto_list) if (slot.slotBase() == baseValue) ctiPatchCallByReturnAddress(codeBlock, STUB_RETURN_ADDRESS, FunctionPtr(cti_op_get_by_id_proto_fail)); else if (slot.slotBase() == asCell(baseValue)->structure()->prototypeForLookup(callFrame)) { + ASSERT(!asCell(baseValue)->structure()->isDictionary()); // Since we're accessing a prototype in a loop, it's a good bet that it // should not be treated as a dictionary. if (slotBaseObject->structure()->isDictionary()) - slotBaseObject->setStructure(Structure::fromDictionaryTransition(slotBaseObject->structure())); + slotBaseObject->flattenDictionaryObject(); int listIndex; PolymorphicAccessStructureList* prototypeStructureList = getPolymorphicAccessStructureListSlot(stubInfo, listIndex); @@ -1339,6 +1396,7 @@ DEFINE_STUB_FUNCTION(EncodedJSValue, op_get_by_id_proto_list) if (listIndex == (POLYMORPHIC_LIST_CACHE_SIZE - 1)) ctiPatchCallByReturnAddress(codeBlock, STUB_RETURN_ADDRESS, FunctionPtr(cti_op_get_by_id_proto_list_full)); } else if (size_t count = normalizePrototypeChain(callFrame, baseValue, slot.slotBase())) { + ASSERT(!asCell(baseValue)->structure()->isDictionary()); int listIndex; PolymorphicAccessStructureList* prototypeStructureList = getPolymorphicAccessStructureListSlot(stubInfo, listIndex); @@ -1807,7 +1865,7 @@ DEFINE_STUB_FUNCTION(EncodedJSValue, op_get_by_val) } else if (isJSString(globalData, baseValue) && asString(baseValue)->canGetIndex(i)) { // All fast byte array accesses are safe from exceptions so return immediately to avoid exception checks. ctiPatchCallByReturnAddress(callFrame->codeBlock(), STUB_RETURN_ADDRESS, FunctionPtr(cti_op_get_by_val_string)); - result = asString(baseValue)->getIndex(stackFrame.globalData, i); + result = asString(baseValue)->getIndex(callFrame, i); } else if (isJSByteArray(globalData, baseValue) && asByteArray(baseValue)->canAccessIndex(i)) { // All fast byte array accesses are safe from exceptions so return immediately to avoid exception checks. ctiPatchCallByReturnAddress(callFrame->codeBlock(), STUB_RETURN_ADDRESS, FunctionPtr(cti_op_get_by_val_byte_array)); @@ -1838,7 +1896,7 @@ DEFINE_STUB_FUNCTION(EncodedJSValue, op_get_by_val_string) if (LIKELY(subscript.isUInt32())) { uint32_t i = subscript.asUInt32(); if (isJSString(globalData, baseValue) && asString(baseValue)->canGetIndex(i)) - result = asString(baseValue)->getIndex(stackFrame.globalData, i); + result = asString(baseValue)->getIndex(callFrame, i); else { result = baseValue.get(callFrame, i); if (!isJSString(globalData, baseValue)) @@ -2003,19 +2061,6 @@ DEFINE_STUB_FUNCTION(EncodedJSValue, op_lesseq) return JSValue::encode(result); } -DEFINE_STUB_FUNCTION(int, op_loop_if_true) -{ - STUB_INIT_STACK_FRAME(stackFrame); - - JSValue src1 = stackFrame.args[0].jsValue(); - - CallFrame* callFrame = stackFrame.callFrame; - - bool result = src1.toBoolean(callFrame); - CHECK_FOR_EXCEPTION_AT_END(); - return result; -} - DEFINE_STUB_FUNCTION(int, op_load_varargs) { STUB_INIT_STACK_FRAME(stackFrame); @@ -2354,20 +2399,20 @@ DEFINE_STUB_FUNCTION(int, op_eq) if (cell1->isString()) { if (src2.isInt32()) - return static_cast<JSString*>(cell1)->value().toDouble() == src2.asInt32(); + return static_cast<JSString*>(cell1)->value(stackFrame.callFrame).toDouble() == src2.asInt32(); if (src2.isDouble()) - return static_cast<JSString*>(cell1)->value().toDouble() == src2.asDouble(); + return static_cast<JSString*>(cell1)->value(stackFrame.callFrame).toDouble() == src2.asDouble(); if (src2.isTrue()) - return static_cast<JSString*>(cell1)->value().toDouble() == 1.0; + return static_cast<JSString*>(cell1)->value(stackFrame.callFrame).toDouble() == 1.0; if (src2.isFalse()) - return static_cast<JSString*>(cell1)->value().toDouble() == 0.0; + return static_cast<JSString*>(cell1)->value(stackFrame.callFrame).toDouble() == 0.0; JSCell* cell2 = asCell(src2); if (cell2->isString()) - return static_cast<JSString*>(cell1)->value() == static_cast<JSString*>(cell2)->value(); + return static_cast<JSString*>(cell1)->value(stackFrame.callFrame) == static_cast<JSString*>(cell2)->value(stackFrame.callFrame); src2 = asObject(cell2)->toPrimitive(stackFrame.callFrame); CHECK_FOR_EXCEPTION(); @@ -2390,7 +2435,7 @@ DEFINE_STUB_FUNCTION(int, op_eq_strings) ASSERT(string1->isString()); ASSERT(string2->isString()); - return string1->value() == string2->value(); + return string1->value(stackFrame.callFrame) == string2->value(stackFrame.callFrame); } #else // USE(JSVALUE32_64) @@ -2688,7 +2733,7 @@ DEFINE_STUB_FUNCTION(int, has_property) JSObject* base = stackFrame.args[0].jsObject(); JSString* property = stackFrame.args[1].jsString(); - return base->hasProperty(stackFrame.callFrame, Identifier(stackFrame.callFrame, property->value())); + return base->hasProperty(stackFrame.callFrame, Identifier(stackFrame.callFrame, property->value(stackFrame.callFrame))); } DEFINE_STUB_FUNCTION(JSObject*, op_push_scope) @@ -2765,7 +2810,7 @@ DEFINE_STUB_FUNCTION(EncodedJSValue, op_stricteq) JSValue src1 = stackFrame.args[0].jsValue(); JSValue src2 = stackFrame.args[1].jsValue(); - return JSValue::encode(jsBoolean(JSValue::strictEqual(src1, src2))); + return JSValue::encode(jsBoolean(JSValue::strictEqual(stackFrame.callFrame, src1, src2))); } DEFINE_STUB_FUNCTION(EncodedJSValue, op_to_primitive) @@ -2779,7 +2824,9 @@ DEFINE_STUB_FUNCTION(EncodedJSValue, op_strcat) { STUB_INIT_STACK_FRAME(stackFrame); - return JSValue::encode(concatenateStrings(stackFrame.callFrame, &stackFrame.callFrame->registers()[stackFrame.args[0].int32()], stackFrame.args[1].int32())); + JSValue result = jsString(stackFrame.callFrame, &stackFrame.callFrame->registers()[stackFrame.args[0].int32()], stackFrame.args[1].int32()); + CHECK_FOR_EXCEPTION_AT_END(); + return JSValue::encode(result); } DEFINE_STUB_FUNCTION(EncodedJSValue, op_nstricteq) @@ -2789,7 +2836,7 @@ DEFINE_STUB_FUNCTION(EncodedJSValue, op_nstricteq) JSValue src1 = stackFrame.args[0].jsValue(); JSValue src2 = stackFrame.args[1].jsValue(); - return JSValue::encode(jsBoolean(!JSValue::strictEqual(src1, src2))); + return JSValue::encode(jsBoolean(!JSValue::strictEqual(stackFrame.callFrame, src1, src2))); } DEFINE_STUB_FUNCTION(EncodedJSValue, op_to_jsnumber) @@ -2898,7 +2945,7 @@ DEFINE_STUB_FUNCTION(void*, op_switch_char) void* result = codeBlock->characterSwitchJumpTable(tableIndex).ctiDefault.executableAddress(); if (scrutinee.isString()) { - UString::Rep* value = asString(scrutinee)->value().rep(); + UString::Rep* value = asString(scrutinee)->value(callFrame).rep(); if (value->size() == 1) result = codeBlock->characterSwitchJumpTable(tableIndex).ctiForValue(value->data()[0]).executableAddress(); } @@ -2918,7 +2965,7 @@ DEFINE_STUB_FUNCTION(void*, op_switch_string) void* result = codeBlock->stringSwitchJumpTable(tableIndex).ctiDefault.executableAddress(); if (scrutinee.isString()) { - UString::Rep* value = asString(scrutinee)->value().rep(); + UString::Rep* value = asString(scrutinee)->value(callFrame).rep(); result = codeBlock->stringSwitchJumpTable(tableIndex).ctiForValue(value).executableAddress(); } diff --git a/JavaScriptCore/jit/JITStubs.h b/JavaScriptCore/jit/JITStubs.h index 69776cb..f71dc9a 100644 --- a/JavaScriptCore/jit/JITStubs.h +++ b/JavaScriptCore/jit/JITStubs.h @@ -187,6 +187,8 @@ namespace JSC { #error "JITStackFrame not defined for this platform." #endif +#define JITSTACKFRAME_ARGS_INDEX (OBJECT_OFFSETOF(JITStackFrame, args) / sizeof(void*)) + #if USE(JIT_STUB_ARGUMENT_VA_LIST) #define STUB_ARGS_DECLARATION void* args, ... #define STUB_ARGS (reinterpret_cast<void**>(vl_args) - 1) @@ -333,9 +335,7 @@ extern "C" { int JIT_STUB cti_op_jlesseq(STUB_ARGS_DECLARATION); int JIT_STUB cti_op_jtrue(STUB_ARGS_DECLARATION); int JIT_STUB cti_op_load_varargs(STUB_ARGS_DECLARATION); - int JIT_STUB cti_op_loop_if_less(STUB_ARGS_DECLARATION); int JIT_STUB cti_op_loop_if_lesseq(STUB_ARGS_DECLARATION); - int JIT_STUB cti_op_loop_if_true(STUB_ARGS_DECLARATION); int JIT_STUB cti_timeout_check(STUB_ARGS_DECLARATION); int JIT_STUB cti_has_property(STUB_ARGS_DECLARATION); void JIT_STUB cti_op_create_arguments(STUB_ARGS_DECLARATION); diff --git a/JavaScriptCore/jsc.pro b/JavaScriptCore/jsc.pro index c6efabc..9bcf08b 100644 --- a/JavaScriptCore/jsc.pro +++ b/JavaScriptCore/jsc.pro @@ -23,13 +23,13 @@ CONFIG(debug, debug|release) { OBJECTS_DIR_WTR = $$OBJECTS_DIR$${QMAKE_DIR_SEP} include($$PWD/JavaScriptCore.pri) -lessThan(QT_MINOR_VERSION, 4) { - DEFINES += QT_BEGIN_NAMESPACE="" QT_END_NAMESPACE="" -} - *-g++*:QMAKE_CXXFLAGS_RELEASE -= -O2 *-g++*:QMAKE_CXXFLAGS_RELEASE += -O3 symbian { TARGET.CAPABILITY = ReadUserData WriteUserData NetworkServices } + +mac { + LIBS_PRIVATE += -framework AppKit +}
\ No newline at end of file diff --git a/JavaScriptCore/parser/Lexer.cpp b/JavaScriptCore/parser/Lexer.cpp index df30838..9148230 100644 --- a/JavaScriptCore/parser/Lexer.cpp +++ b/JavaScriptCore/parser/Lexer.cpp @@ -39,10 +39,7 @@ using namespace Unicode; // We can't specify the namespace in yacc's C output, so do it here instead. using namespace JSC; -#ifndef KDE_USE_FINAL #include "Grammar.h" -#endif - #include "Lookup.h" #include "Lexer.lut.h" diff --git a/JavaScriptCore/parser/Nodes.cpp b/JavaScriptCore/parser/Nodes.cpp index 45009dc..4b97e9a 100644 --- a/JavaScriptCore/parser/Nodes.cpp +++ b/JavaScriptCore/parser/Nodes.cpp @@ -49,63 +49,6 @@ using namespace WTF; namespace JSC { -/* - Details of the emitBytecode function. - - Return value: The register holding the production's value. - dst: An optional parameter specifying the most efficient destination at - which to store the production's value. The callee must honor dst. - - The dst argument provides for a crude form of copy propagation. For example, - - x = 1 - - becomes - - load r[x], 1 - - instead of - - load r0, 1 - mov r[x], r0 - - because the assignment node, "x =", passes r[x] as dst to the number node, "1". -*/ - -// ------------------------------ ThrowableExpressionData -------------------------------- - -static void substitute(UString& string, const UString& substring) -{ - int position = string.find("%s"); - ASSERT(position != -1); - UString newString = string.substr(0, position); - newString.append(substring); - newString.append(string.substr(position + 2)); - string = newString; -} - -RegisterID* ThrowableExpressionData::emitThrowError(BytecodeGenerator& generator, ErrorType type, const char* message) -{ - generator.emitExpressionInfo(divot(), startOffset(), endOffset()); - RegisterID* exception = generator.emitNewError(generator.newTemporary(), type, jsString(generator.globalData(), message)); - generator.emitThrow(exception); - return exception; -} - -RegisterID* ThrowableExpressionData::emitThrowError(BytecodeGenerator& generator, ErrorType type, const char* messageTemplate, const UString& label) -{ - UString message = messageTemplate; - substitute(message, label); - generator.emitExpressionInfo(divot(), startOffset(), endOffset()); - RegisterID* exception = generator.emitNewError(generator.newTemporary(), type, jsString(generator.globalData(), message)); - generator.emitThrow(exception); - return exception; -} - -inline RegisterID* ThrowableExpressionData::emitThrowError(BytecodeGenerator& generator, ErrorType type, const char* messageTemplate, const Identifier& label) -{ - return emitThrowError(generator, type, messageTemplate, label.ustring()); -} // ------------------------------ StatementNode -------------------------------- @@ -130,1753 +73,6 @@ inline StatementNode* SourceElements::singleStatement() const return size == 1 ? m_statements[0] : 0; } -inline StatementNode* SourceElements::lastStatement() const -{ - size_t size = m_statements.size(); - return size ? m_statements[size - 1] : 0; -} - -// ------------------------------ NullNode ------------------------------------- - -RegisterID* NullNode::emitBytecode(BytecodeGenerator& generator, RegisterID* dst) -{ - if (dst == generator.ignoredResult()) - return 0; - return generator.emitLoad(dst, jsNull()); -} - -// ------------------------------ BooleanNode ---------------------------------- - -RegisterID* BooleanNode::emitBytecode(BytecodeGenerator& generator, RegisterID* dst) -{ - if (dst == generator.ignoredResult()) - return 0; - return generator.emitLoad(dst, m_value); -} - -// ------------------------------ NumberNode ----------------------------------- - -RegisterID* NumberNode::emitBytecode(BytecodeGenerator& generator, RegisterID* dst) -{ - if (dst == generator.ignoredResult()) - return 0; - return generator.emitLoad(dst, m_value); -} - -// ------------------------------ StringNode ----------------------------------- - -RegisterID* StringNode::emitBytecode(BytecodeGenerator& generator, RegisterID* dst) -{ - if (dst == generator.ignoredResult()) - return 0; - return generator.emitLoad(dst, m_value); -} - -// ------------------------------ RegExpNode ----------------------------------- - -RegisterID* RegExpNode::emitBytecode(BytecodeGenerator& generator, RegisterID* dst) -{ - RefPtr<RegExp> regExp = RegExp::create(generator.globalData(), m_pattern.ustring(), m_flags.ustring()); - if (!regExp->isValid()) - return emitThrowError(generator, SyntaxError, "Invalid regular expression: %s", regExp->errorMessage()); - if (dst == generator.ignoredResult()) - return 0; - return generator.emitNewRegExp(generator.finalDestination(dst), regExp.get()); -} - -// ------------------------------ ThisNode ------------------------------------- - -RegisterID* ThisNode::emitBytecode(BytecodeGenerator& generator, RegisterID* dst) -{ - if (dst == generator.ignoredResult()) - return 0; - return generator.moveToDestinationIfNeeded(dst, generator.thisRegister()); -} - -// ------------------------------ ResolveNode ---------------------------------- - -bool ResolveNode::isPure(BytecodeGenerator& generator) const -{ - return generator.isLocal(m_ident); -} - -RegisterID* ResolveNode::emitBytecode(BytecodeGenerator& generator, RegisterID* dst) -{ - if (RegisterID* local = generator.registerFor(m_ident)) { - if (dst == generator.ignoredResult()) - return 0; - return generator.moveToDestinationIfNeeded(dst, local); - } - - generator.emitExpressionInfo(m_startOffset + m_ident.size(), m_ident.size(), 0); - return generator.emitResolve(generator.finalDestination(dst), m_ident); -} - -// ------------------------------ ArrayNode ------------------------------------ - -RegisterID* ArrayNode::emitBytecode(BytecodeGenerator& generator, RegisterID* dst) -{ - // FIXME: Should we put all of this code into emitNewArray? - - unsigned length = 0; - ElementNode* firstPutElement; - for (firstPutElement = m_element; firstPutElement; firstPutElement = firstPutElement->next()) { - if (firstPutElement->elision()) - break; - ++length; - } - - if (!firstPutElement && !m_elision) - return generator.emitNewArray(generator.finalDestination(dst), m_element); - - RefPtr<RegisterID> array = generator.emitNewArray(generator.tempDestination(dst), m_element); - - for (ElementNode* n = firstPutElement; n; n = n->next()) { - RegisterID* value = generator.emitNode(n->value()); - length += n->elision(); - generator.emitPutByIndex(array.get(), length++, value); - } - - if (m_elision) { - RegisterID* value = generator.emitLoad(0, jsNumber(generator.globalData(), m_elision + length)); - generator.emitPutById(array.get(), generator.propertyNames().length, value); - } - - return generator.moveToDestinationIfNeeded(dst, array.get()); -} - -bool ArrayNode::isSimpleArray() const -{ - if (m_elision || m_optional) - return false; - for (ElementNode* ptr = m_element; ptr; ptr = ptr->next()) { - if (ptr->elision()) - return false; - } - return true; -} - -ArgumentListNode* ArrayNode::toArgumentList(JSGlobalData* globalData) const -{ - ASSERT(!m_elision && !m_optional); - ElementNode* ptr = m_element; - if (!ptr) - return 0; - ArgumentListNode* head = new (globalData) ArgumentListNode(globalData, ptr->value()); - ArgumentListNode* tail = head; - ptr = ptr->next(); - for (; ptr; ptr = ptr->next()) { - ASSERT(!ptr->elision()); - tail = new (globalData) ArgumentListNode(globalData, tail, ptr->value()); - } - return head; -} - -// ------------------------------ ObjectLiteralNode ---------------------------- - -RegisterID* ObjectLiteralNode::emitBytecode(BytecodeGenerator& generator, RegisterID* dst) -{ - if (!m_list) { - if (dst == generator.ignoredResult()) - return 0; - return generator.emitNewObject(generator.finalDestination(dst)); - } - return generator.emitNode(dst, m_list); -} - -// ------------------------------ PropertyListNode ----------------------------- - -RegisterID* PropertyListNode::emitBytecode(BytecodeGenerator& generator, RegisterID* dst) -{ - RefPtr<RegisterID> newObj = generator.tempDestination(dst); - - generator.emitNewObject(newObj.get()); - - for (PropertyListNode* p = this; p; p = p->m_next) { - RegisterID* value = generator.emitNode(p->m_node->m_assign); - - switch (p->m_node->m_type) { - case PropertyNode::Constant: { - generator.emitPutById(newObj.get(), p->m_node->name(), value); - break; - } - case PropertyNode::Getter: { - generator.emitPutGetter(newObj.get(), p->m_node->name(), value); - break; - } - case PropertyNode::Setter: { - generator.emitPutSetter(newObj.get(), p->m_node->name(), value); - break; - } - default: - ASSERT_NOT_REACHED(); - } - } - - return generator.moveToDestinationIfNeeded(dst, newObj.get()); -} - -// ------------------------------ BracketAccessorNode -------------------------------- - -RegisterID* BracketAccessorNode::emitBytecode(BytecodeGenerator& generator, RegisterID* dst) -{ - RefPtr<RegisterID> base = generator.emitNodeForLeftHandSide(m_base, m_subscriptHasAssignments, m_subscript->isPure(generator)); - RegisterID* property = generator.emitNode(m_subscript); - generator.emitExpressionInfo(divot(), startOffset(), endOffset()); - return generator.emitGetByVal(generator.finalDestination(dst), base.get(), property); -} - -// ------------------------------ DotAccessorNode -------------------------------- - -RegisterID* DotAccessorNode::emitBytecode(BytecodeGenerator& generator, RegisterID* dst) -{ - RegisterID* base = generator.emitNode(m_base); - generator.emitExpressionInfo(divot(), startOffset(), endOffset()); - return generator.emitGetById(generator.finalDestination(dst), base, m_ident); -} - -// ------------------------------ ArgumentListNode ----------------------------- - -RegisterID* ArgumentListNode::emitBytecode(BytecodeGenerator& generator, RegisterID* dst) -{ - ASSERT(m_expr); - return generator.emitNode(dst, m_expr); -} - -// ------------------------------ NewExprNode ---------------------------------- - -RegisterID* NewExprNode::emitBytecode(BytecodeGenerator& generator, RegisterID* dst) -{ - RefPtr<RegisterID> func = generator.emitNode(m_expr); - return generator.emitConstruct(generator.finalDestination(dst), func.get(), m_args, divot(), startOffset(), endOffset()); -} - -// ------------------------------ EvalFunctionCallNode ---------------------------------- - -RegisterID* EvalFunctionCallNode::emitBytecode(BytecodeGenerator& generator, RegisterID* dst) -{ - RefPtr<RegisterID> func = generator.tempDestination(dst); - RefPtr<RegisterID> thisRegister = generator.newTemporary(); - generator.emitExpressionInfo(divot() - startOffset() + 4, 4, 0); - generator.emitResolveWithBase(thisRegister.get(), func.get(), generator.propertyNames().eval); - return generator.emitCallEval(generator.finalDestination(dst, func.get()), func.get(), thisRegister.get(), m_args, divot(), startOffset(), endOffset()); -} - -// ------------------------------ FunctionCallValueNode ---------------------------------- - -RegisterID* FunctionCallValueNode::emitBytecode(BytecodeGenerator& generator, RegisterID* dst) -{ - RefPtr<RegisterID> func = generator.emitNode(m_expr); - RefPtr<RegisterID> thisRegister = generator.emitLoad(generator.newTemporary(), jsNull()); - return generator.emitCall(generator.finalDestination(dst, func.get()), func.get(), thisRegister.get(), m_args, divot(), startOffset(), endOffset()); -} - -// ------------------------------ FunctionCallResolveNode ---------------------------------- - -RegisterID* FunctionCallResolveNode::emitBytecode(BytecodeGenerator& generator, RegisterID* dst) -{ - if (RefPtr<RegisterID> local = generator.registerFor(m_ident)) { - RefPtr<RegisterID> thisRegister = generator.emitLoad(generator.newTemporary(), jsNull()); - return generator.emitCall(generator.finalDestination(dst, thisRegister.get()), local.get(), thisRegister.get(), m_args, divot(), startOffset(), endOffset()); - } - - int index = 0; - size_t depth = 0; - JSObject* globalObject = 0; - if (generator.findScopedProperty(m_ident, index, depth, false, globalObject) && index != missingSymbolMarker()) { - RefPtr<RegisterID> func = generator.emitGetScopedVar(generator.newTemporary(), depth, index, globalObject); - RefPtr<RegisterID> thisRegister = generator.emitLoad(generator.newTemporary(), jsNull()); - return generator.emitCall(generator.finalDestination(dst, func.get()), func.get(), thisRegister.get(), m_args, divot(), startOffset(), endOffset()); - } - - RefPtr<RegisterID> func = generator.newTemporary(); - RefPtr<RegisterID> thisRegister = generator.newTemporary(); - int identifierStart = divot() - startOffset(); - generator.emitExpressionInfo(identifierStart + m_ident.size(), m_ident.size(), 0); - generator.emitResolveWithBase(thisRegister.get(), func.get(), m_ident); - return generator.emitCall(generator.finalDestination(dst, func.get()), func.get(), thisRegister.get(), m_args, divot(), startOffset(), endOffset()); -} - -// ------------------------------ FunctionCallBracketNode ---------------------------------- - -RegisterID* FunctionCallBracketNode::emitBytecode(BytecodeGenerator& generator, RegisterID* dst) -{ - RefPtr<RegisterID> base = generator.emitNode(m_base); - RegisterID* property = generator.emitNode(m_subscript); - generator.emitExpressionInfo(divot() - m_subexpressionDivotOffset, startOffset() - m_subexpressionDivotOffset, m_subexpressionEndOffset); - RefPtr<RegisterID> function = generator.emitGetByVal(generator.tempDestination(dst), base.get(), property); - RefPtr<RegisterID> thisRegister = generator.emitMove(generator.newTemporary(), base.get()); - return generator.emitCall(generator.finalDestination(dst, function.get()), function.get(), thisRegister.get(), m_args, divot(), startOffset(), endOffset()); -} - -// ------------------------------ FunctionCallDotNode ---------------------------------- - -RegisterID* FunctionCallDotNode::emitBytecode(BytecodeGenerator& generator, RegisterID* dst) -{ - RefPtr<RegisterID> function = generator.tempDestination(dst); - RefPtr<RegisterID> thisRegister = generator.newTemporary(); - generator.emitNode(thisRegister.get(), m_base); - generator.emitExpressionInfo(divot() - m_subexpressionDivotOffset, startOffset() - m_subexpressionDivotOffset, m_subexpressionEndOffset); - generator.emitMethodCheck(); - generator.emitGetById(function.get(), thisRegister.get(), m_ident); - return generator.emitCall(generator.finalDestination(dst, function.get()), function.get(), thisRegister.get(), m_args, divot(), startOffset(), endOffset()); -} - -RegisterID* CallFunctionCallDotNode::emitBytecode(BytecodeGenerator& generator, RegisterID* dst) -{ - RefPtr<Label> realCall = generator.newLabel(); - RefPtr<Label> end = generator.newLabel(); - RefPtr<RegisterID> base = generator.emitNode(m_base); - generator.emitExpressionInfo(divot() - m_subexpressionDivotOffset, startOffset() - m_subexpressionDivotOffset, m_subexpressionEndOffset); - RefPtr<RegisterID> function = generator.emitGetById(generator.tempDestination(dst), base.get(), m_ident); - RefPtr<RegisterID> finalDestination = generator.finalDestination(dst, function.get()); - generator.emitJumpIfNotFunctionCall(function.get(), realCall.get()); - { - RefPtr<RegisterID> realFunction = generator.emitMove(generator.tempDestination(dst), base.get()); - RefPtr<RegisterID> thisRegister = generator.newTemporary(); - ArgumentListNode* oldList = m_args->m_listNode; - if (m_args->m_listNode && m_args->m_listNode->m_expr) { - generator.emitNode(thisRegister.get(), m_args->m_listNode->m_expr); - m_args->m_listNode = m_args->m_listNode->m_next; - } else - generator.emitLoad(thisRegister.get(), jsNull()); - - generator.emitCall(finalDestination.get(), realFunction.get(), thisRegister.get(), m_args, divot(), startOffset(), endOffset()); - generator.emitJump(end.get()); - m_args->m_listNode = oldList; - } - generator.emitLabel(realCall.get()); - { - RefPtr<RegisterID> thisRegister = generator.emitMove(generator.newTemporary(), base.get()); - generator.emitCall(finalDestination.get(), function.get(), thisRegister.get(), m_args, divot(), startOffset(), endOffset()); - } - generator.emitLabel(end.get()); - return finalDestination.get(); -} - -static bool areTrivialApplyArguments(ArgumentsNode* args) -{ - return !args->m_listNode || !args->m_listNode->m_expr || !args->m_listNode->m_next - || (!args->m_listNode->m_next->m_next && args->m_listNode->m_next->m_expr->isSimpleArray()); -} - -RegisterID* ApplyFunctionCallDotNode::emitBytecode(BytecodeGenerator& generator, RegisterID* dst) -{ - // A few simple cases can be trivially handled as ordinary function calls. - // function.apply(), function.apply(arg) -> identical to function.call - // function.apply(thisArg, [arg0, arg1, ...]) -> can be trivially coerced into function.call(thisArg, arg0, arg1, ...) and saves object allocation - bool mayBeCall = areTrivialApplyArguments(m_args); - - RefPtr<Label> realCall = generator.newLabel(); - RefPtr<Label> end = generator.newLabel(); - RefPtr<RegisterID> base = generator.emitNode(m_base); - generator.emitExpressionInfo(divot() - m_subexpressionDivotOffset, startOffset() - m_subexpressionDivotOffset, m_subexpressionEndOffset); - RefPtr<RegisterID> function = generator.emitGetById(generator.tempDestination(dst), base.get(), m_ident); - RefPtr<RegisterID> finalDestination = generator.finalDestination(dst, function.get()); - generator.emitJumpIfNotFunctionApply(function.get(), realCall.get()); - { - if (mayBeCall) { - RefPtr<RegisterID> realFunction = generator.emitMove(generator.tempDestination(dst), base.get()); - RefPtr<RegisterID> thisRegister = generator.newTemporary(); - ArgumentListNode* oldList = m_args->m_listNode; - if (m_args->m_listNode && m_args->m_listNode->m_expr) { - generator.emitNode(thisRegister.get(), m_args->m_listNode->m_expr); - m_args->m_listNode = m_args->m_listNode->m_next; - if (m_args->m_listNode) { - ASSERT(m_args->m_listNode->m_expr->isSimpleArray()); - ASSERT(!m_args->m_listNode->m_next); - m_args->m_listNode = static_cast<ArrayNode*>(m_args->m_listNode->m_expr)->toArgumentList(generator.globalData()); - } - } else - generator.emitLoad(thisRegister.get(), jsNull()); - generator.emitCall(finalDestination.get(), realFunction.get(), thisRegister.get(), m_args, divot(), startOffset(), endOffset()); - m_args->m_listNode = oldList; - } else { - ASSERT(m_args->m_listNode && m_args->m_listNode->m_next); - RefPtr<RegisterID> realFunction = generator.emitMove(generator.newTemporary(), base.get()); - RefPtr<RegisterID> argsCountRegister = generator.newTemporary(); - RefPtr<RegisterID> thisRegister = generator.newTemporary(); - RefPtr<RegisterID> argsRegister = generator.newTemporary(); - generator.emitNode(thisRegister.get(), m_args->m_listNode->m_expr); - ArgumentListNode* args = m_args->m_listNode->m_next; - bool isArgumentsApply = false; - if (args->m_expr->isResolveNode()) { - ResolveNode* resolveNode = static_cast<ResolveNode*>(args->m_expr); - isArgumentsApply = generator.willResolveToArguments(resolveNode->identifier()); - if (isArgumentsApply) - generator.emitMove(argsRegister.get(), generator.uncheckedRegisterForArguments()); - } - if (!isArgumentsApply) - generator.emitNode(argsRegister.get(), args->m_expr); - while ((args = args->m_next)) - generator.emitNode(args->m_expr); - - generator.emitLoadVarargs(argsCountRegister.get(), argsRegister.get()); - generator.emitCallVarargs(finalDestination.get(), realFunction.get(), thisRegister.get(), argsCountRegister.get(), divot(), startOffset(), endOffset()); - } - generator.emitJump(end.get()); - } - generator.emitLabel(realCall.get()); - { - RefPtr<RegisterID> thisRegister = generator.emitMove(generator.newTemporary(), base.get()); - generator.emitCall(finalDestination.get(), function.get(), thisRegister.get(), m_args, divot(), startOffset(), endOffset()); - } - generator.emitLabel(end.get()); - return finalDestination.get(); -} - -// ------------------------------ PostfixResolveNode ---------------------------------- - -static RegisterID* emitPreIncOrDec(BytecodeGenerator& generator, RegisterID* srcDst, Operator oper) -{ - return (oper == OpPlusPlus) ? generator.emitPreInc(srcDst) : generator.emitPreDec(srcDst); -} - -static RegisterID* emitPostIncOrDec(BytecodeGenerator& generator, RegisterID* dst, RegisterID* srcDst, Operator oper) -{ - if (srcDst == dst) - return generator.emitToJSNumber(dst, srcDst); - return (oper == OpPlusPlus) ? generator.emitPostInc(dst, srcDst) : generator.emitPostDec(dst, srcDst); -} - -RegisterID* PostfixResolveNode::emitBytecode(BytecodeGenerator& generator, RegisterID* dst) -{ - if (RegisterID* local = generator.registerFor(m_ident)) { - if (generator.isLocalConstant(m_ident)) { - if (dst == generator.ignoredResult()) - return 0; - return generator.emitToJSNumber(generator.finalDestination(dst), local); - } - - if (dst == generator.ignoredResult()) - return emitPreIncOrDec(generator, local, m_operator); - return emitPostIncOrDec(generator, generator.finalDestination(dst), local, m_operator); - } - - int index = 0; - size_t depth = 0; - JSObject* globalObject = 0; - if (generator.findScopedProperty(m_ident, index, depth, true, globalObject) && index != missingSymbolMarker()) { - RefPtr<RegisterID> value = generator.emitGetScopedVar(generator.newTemporary(), depth, index, globalObject); - RegisterID* oldValue; - if (dst == generator.ignoredResult()) { - oldValue = 0; - emitPreIncOrDec(generator, value.get(), m_operator); - } else { - oldValue = emitPostIncOrDec(generator, generator.finalDestination(dst), value.get(), m_operator); - } - generator.emitPutScopedVar(depth, index, value.get(), globalObject); - return oldValue; - } - - generator.emitExpressionInfo(divot(), startOffset(), endOffset()); - RefPtr<RegisterID> value = generator.newTemporary(); - RefPtr<RegisterID> base = generator.emitResolveWithBase(generator.newTemporary(), value.get(), m_ident); - RegisterID* oldValue; - if (dst == generator.ignoredResult()) { - oldValue = 0; - emitPreIncOrDec(generator, value.get(), m_operator); - } else { - oldValue = emitPostIncOrDec(generator, generator.finalDestination(dst), value.get(), m_operator); - } - generator.emitPutById(base.get(), m_ident, value.get()); - return oldValue; -} - -// ------------------------------ PostfixBracketNode ---------------------------------- - -RegisterID* PostfixBracketNode::emitBytecode(BytecodeGenerator& generator, RegisterID* dst) -{ - RefPtr<RegisterID> base = generator.emitNode(m_base); - RefPtr<RegisterID> property = generator.emitNode(m_subscript); - - generator.emitExpressionInfo(divot() - m_subexpressionDivotOffset, startOffset() - m_subexpressionDivotOffset, m_subexpressionEndOffset); - RefPtr<RegisterID> value = generator.emitGetByVal(generator.newTemporary(), base.get(), property.get()); - RegisterID* oldValue; - if (dst == generator.ignoredResult()) { - oldValue = 0; - if (m_operator == OpPlusPlus) - generator.emitPreInc(value.get()); - else - generator.emitPreDec(value.get()); - } else { - oldValue = (m_operator == OpPlusPlus) ? generator.emitPostInc(generator.finalDestination(dst), value.get()) : generator.emitPostDec(generator.finalDestination(dst), value.get()); - } - generator.emitExpressionInfo(divot(), startOffset(), endOffset()); - generator.emitPutByVal(base.get(), property.get(), value.get()); - return oldValue; -} - -// ------------------------------ PostfixDotNode ---------------------------------- - -RegisterID* PostfixDotNode::emitBytecode(BytecodeGenerator& generator, RegisterID* dst) -{ - RefPtr<RegisterID> base = generator.emitNode(m_base); - - generator.emitExpressionInfo(divot() - m_subexpressionDivotOffset, startOffset() - m_subexpressionDivotOffset, m_subexpressionEndOffset); - RefPtr<RegisterID> value = generator.emitGetById(generator.newTemporary(), base.get(), m_ident); - RegisterID* oldValue; - if (dst == generator.ignoredResult()) { - oldValue = 0; - if (m_operator == OpPlusPlus) - generator.emitPreInc(value.get()); - else - generator.emitPreDec(value.get()); - } else { - oldValue = (m_operator == OpPlusPlus) ? generator.emitPostInc(generator.finalDestination(dst), value.get()) : generator.emitPostDec(generator.finalDestination(dst), value.get()); - } - generator.emitExpressionInfo(divot(), startOffset(), endOffset()); - generator.emitPutById(base.get(), m_ident, value.get()); - return oldValue; -} - -// ------------------------------ PostfixErrorNode ----------------------------------- - -RegisterID* PostfixErrorNode::emitBytecode(BytecodeGenerator& generator, RegisterID*) -{ - return emitThrowError(generator, ReferenceError, m_operator == OpPlusPlus - ? "Postfix ++ operator applied to value that is not a reference." - : "Postfix -- operator applied to value that is not a reference."); -} - -// ------------------------------ DeleteResolveNode ----------------------------------- - -RegisterID* DeleteResolveNode::emitBytecode(BytecodeGenerator& generator, RegisterID* dst) -{ - if (generator.registerFor(m_ident)) - return generator.emitLoad(generator.finalDestination(dst), false); - - generator.emitExpressionInfo(divot(), startOffset(), endOffset()); - RegisterID* base = generator.emitResolveBase(generator.tempDestination(dst), m_ident); - return generator.emitDeleteById(generator.finalDestination(dst, base), base, m_ident); -} - -// ------------------------------ DeleteBracketNode ----------------------------------- - -RegisterID* DeleteBracketNode::emitBytecode(BytecodeGenerator& generator, RegisterID* dst) -{ - RefPtr<RegisterID> r0 = generator.emitNode(m_base); - RegisterID* r1 = generator.emitNode(m_subscript); - - generator.emitExpressionInfo(divot(), startOffset(), endOffset()); - return generator.emitDeleteByVal(generator.finalDestination(dst), r0.get(), r1); -} - -// ------------------------------ DeleteDotNode ----------------------------------- - -RegisterID* DeleteDotNode::emitBytecode(BytecodeGenerator& generator, RegisterID* dst) -{ - RegisterID* r0 = generator.emitNode(m_base); - - generator.emitExpressionInfo(divot(), startOffset(), endOffset()); - return generator.emitDeleteById(generator.finalDestination(dst), r0, m_ident); -} - -// ------------------------------ DeleteValueNode ----------------------------------- - -RegisterID* DeleteValueNode::emitBytecode(BytecodeGenerator& generator, RegisterID* dst) -{ - generator.emitNode(generator.ignoredResult(), m_expr); - - // delete on a non-location expression ignores the value and returns true - return generator.emitLoad(generator.finalDestination(dst), true); -} - -// ------------------------------ VoidNode ------------------------------------- - -RegisterID* VoidNode::emitBytecode(BytecodeGenerator& generator, RegisterID* dst) -{ - if (dst == generator.ignoredResult()) { - generator.emitNode(generator.ignoredResult(), m_expr); - return 0; - } - RefPtr<RegisterID> r0 = generator.emitNode(m_expr); - return generator.emitLoad(dst, jsUndefined()); -} - -// ------------------------------ TypeOfValueNode ----------------------------------- - -RegisterID* TypeOfResolveNode::emitBytecode(BytecodeGenerator& generator, RegisterID* dst) -{ - if (RegisterID* local = generator.registerFor(m_ident)) { - if (dst == generator.ignoredResult()) - return 0; - return generator.emitTypeOf(generator.finalDestination(dst), local); - } - - RefPtr<RegisterID> scratch = generator.emitResolveBase(generator.tempDestination(dst), m_ident); - generator.emitGetById(scratch.get(), scratch.get(), m_ident); - if (dst == generator.ignoredResult()) - return 0; - return generator.emitTypeOf(generator.finalDestination(dst, scratch.get()), scratch.get()); -} - -// ------------------------------ TypeOfValueNode ----------------------------------- - -RegisterID* TypeOfValueNode::emitBytecode(BytecodeGenerator& generator, RegisterID* dst) -{ - if (dst == generator.ignoredResult()) { - generator.emitNode(generator.ignoredResult(), m_expr); - return 0; - } - RefPtr<RegisterID> src = generator.emitNode(m_expr); - return generator.emitTypeOf(generator.finalDestination(dst), src.get()); -} - -// ------------------------------ PrefixResolveNode ---------------------------------- - -RegisterID* PrefixResolveNode::emitBytecode(BytecodeGenerator& generator, RegisterID* dst) -{ - if (RegisterID* local = generator.registerFor(m_ident)) { - if (generator.isLocalConstant(m_ident)) { - if (dst == generator.ignoredResult()) - return 0; - RefPtr<RegisterID> r0 = generator.emitLoad(generator.finalDestination(dst), (m_operator == OpPlusPlus) ? 1.0 : -1.0); - return generator.emitBinaryOp(op_add, r0.get(), local, r0.get(), OperandTypes()); - } - - emitPreIncOrDec(generator, local, m_operator); - return generator.moveToDestinationIfNeeded(dst, local); - } - - int index = 0; - size_t depth = 0; - JSObject* globalObject = 0; - if (generator.findScopedProperty(m_ident, index, depth, false, globalObject) && index != missingSymbolMarker()) { - RefPtr<RegisterID> propDst = generator.emitGetScopedVar(generator.tempDestination(dst), depth, index, globalObject); - emitPreIncOrDec(generator, propDst.get(), m_operator); - generator.emitPutScopedVar(depth, index, propDst.get(), globalObject); - return generator.moveToDestinationIfNeeded(dst, propDst.get()); - } - - generator.emitExpressionInfo(divot(), startOffset(), endOffset()); - RefPtr<RegisterID> propDst = generator.tempDestination(dst); - RefPtr<RegisterID> base = generator.emitResolveWithBase(generator.newTemporary(), propDst.get(), m_ident); - emitPreIncOrDec(generator, propDst.get(), m_operator); - generator.emitPutById(base.get(), m_ident, propDst.get()); - return generator.moveToDestinationIfNeeded(dst, propDst.get()); -} - -// ------------------------------ PrefixBracketNode ---------------------------------- - -RegisterID* PrefixBracketNode::emitBytecode(BytecodeGenerator& generator, RegisterID* dst) -{ - RefPtr<RegisterID> base = generator.emitNode(m_base); - RefPtr<RegisterID> property = generator.emitNode(m_subscript); - RefPtr<RegisterID> propDst = generator.tempDestination(dst); - - generator.emitExpressionInfo(divot() + m_subexpressionDivotOffset, m_subexpressionStartOffset, endOffset() - m_subexpressionDivotOffset); - RegisterID* value = generator.emitGetByVal(propDst.get(), base.get(), property.get()); - if (m_operator == OpPlusPlus) - generator.emitPreInc(value); - else - generator.emitPreDec(value); - generator.emitExpressionInfo(divot(), startOffset(), endOffset()); - generator.emitPutByVal(base.get(), property.get(), value); - return generator.moveToDestinationIfNeeded(dst, propDst.get()); -} - -// ------------------------------ PrefixDotNode ---------------------------------- - -RegisterID* PrefixDotNode::emitBytecode(BytecodeGenerator& generator, RegisterID* dst) -{ - RefPtr<RegisterID> base = generator.emitNode(m_base); - RefPtr<RegisterID> propDst = generator.tempDestination(dst); - - generator.emitExpressionInfo(divot() + m_subexpressionDivotOffset, m_subexpressionStartOffset, endOffset() - m_subexpressionDivotOffset); - RegisterID* value = generator.emitGetById(propDst.get(), base.get(), m_ident); - if (m_operator == OpPlusPlus) - generator.emitPreInc(value); - else - generator.emitPreDec(value); - generator.emitExpressionInfo(divot(), startOffset(), endOffset()); - generator.emitPutById(base.get(), m_ident, value); - return generator.moveToDestinationIfNeeded(dst, propDst.get()); -} - -// ------------------------------ PrefixErrorNode ----------------------------------- - -RegisterID* PrefixErrorNode::emitBytecode(BytecodeGenerator& generator, RegisterID*) -{ - return emitThrowError(generator, ReferenceError, m_operator == OpPlusPlus - ? "Prefix ++ operator applied to value that is not a reference." - : "Prefix -- operator applied to value that is not a reference."); -} - -// ------------------------------ Unary Operation Nodes ----------------------------------- - -RegisterID* UnaryOpNode::emitBytecode(BytecodeGenerator& generator, RegisterID* dst) -{ - RegisterID* src = generator.emitNode(m_expr); - return generator.emitUnaryOp(opcodeID(), generator.finalDestination(dst), src); -} - -// ------------------------------ Binary Operation Nodes ----------------------------------- - -// BinaryOpNode::emitStrcat: -// -// This node generates an op_strcat operation. This opcode can handle concatenation of three or -// more values, where we can determine a set of separate op_add operations would be operating on -// string values. -// -// This function expects to be operating on a graph of AST nodes looking something like this: -// -// (a)... (b) -// \ / -// (+) (c) -// \ / -// [d] ((+)) -// \ / -// [+=] -// -// The assignment operation is optional, if it exists the register holding the value on the -// lefthand side of the assignment should be passing as the optional 'lhs' argument. -// -// The method should be called on the node at the root of the tree of regular binary add -// operations (marked in the diagram with a double set of parentheses). This node must -// be performing a string concatenation (determined by statically detecting that at least -// one child must be a string). -// -// Since the minimum number of values being concatenated together is expected to be 3, if -// a lhs to a concatenating assignment is not provided then the root add should have at -// least one left child that is also an add that can be determined to be operating on strings. -// -RegisterID* BinaryOpNode::emitStrcat(BytecodeGenerator& generator, RegisterID* dst, RegisterID* lhs, ReadModifyResolveNode* emitExpressionInfoForMe) -{ - ASSERT(isAdd()); - ASSERT(resultDescriptor().definitelyIsString()); - - // Create a list of expressions for all the adds in the tree of nodes we can convert into - // a string concatenation. The rightmost node (c) is added first. The rightmost node is - // added first, and the leftmost child is never added, so the vector produced for the - // example above will be [ c, b ]. - Vector<ExpressionNode*, 16> reverseExpressionList; - reverseExpressionList.append(m_expr2); - - // Examine the left child of the add. So long as this is a string add, add its right-child - // to the list, and keep processing along the left fork. - ExpressionNode* leftMostAddChild = m_expr1; - while (leftMostAddChild->isAdd() && leftMostAddChild->resultDescriptor().definitelyIsString()) { - reverseExpressionList.append(static_cast<AddNode*>(leftMostAddChild)->m_expr2); - leftMostAddChild = static_cast<AddNode*>(leftMostAddChild)->m_expr1; - } - - Vector<RefPtr<RegisterID>, 16> temporaryRegisters; - - // If there is an assignment, allocate a temporary to hold the lhs after conversion. - // We could possibly avoid this (the lhs is converted last anyway, we could let the - // op_strcat node handle its conversion if required). - if (lhs) - temporaryRegisters.append(generator.newTemporary()); - - // Emit code for the leftmost node ((a) in the example). - temporaryRegisters.append(generator.newTemporary()); - RegisterID* leftMostAddChildTempRegister = temporaryRegisters.last().get(); - generator.emitNode(leftMostAddChildTempRegister, leftMostAddChild); - - // Note on ordering of conversions: - // - // We maintain the same ordering of conversions as we would see if the concatenations - // was performed as a sequence of adds (otherwise this optimization could change - // behaviour should an object have been provided a valueOf or toString method). - // - // Considering the above example, the sequnce of execution is: - // * evaluate operand (a) - // * evaluate operand (b) - // * convert (a) to primitive <- (this would be triggered by the first add) - // * convert (b) to primitive <- (ditto) - // * evaluate operand (c) - // * convert (c) to primitive <- (this would be triggered by the second add) - // And optionally, if there is an assignment: - // * convert (d) to primitive <- (this would be triggered by the assigning addition) - // - // As such we do not plant an op to convert the leftmost child now. Instead, use - // 'leftMostAddChildTempRegister' as a flag to trigger generation of the conversion - // once the second node has been generated. However, if the leftmost child is an - // immediate we can trivially determine that no conversion will be required. - // If this is the case - if (leftMostAddChild->isString()) - leftMostAddChildTempRegister = 0; - - while (reverseExpressionList.size()) { - ExpressionNode* node = reverseExpressionList.last(); - reverseExpressionList.removeLast(); - - // Emit the code for the current node. - temporaryRegisters.append(generator.newTemporary()); - generator.emitNode(temporaryRegisters.last().get(), node); - - // On the first iteration of this loop, when we first reach this point we have just - // generated the second node, which means it is time to convert the leftmost operand. - if (leftMostAddChildTempRegister) { - generator.emitToPrimitive(leftMostAddChildTempRegister, leftMostAddChildTempRegister); - leftMostAddChildTempRegister = 0; // Only do this once. - } - // Plant a conversion for this node, if necessary. - if (!node->isString()) - generator.emitToPrimitive(temporaryRegisters.last().get(), temporaryRegisters.last().get()); - } - ASSERT(temporaryRegisters.size() >= 3); - - // Certain read-modify nodes require expression info to be emitted *after* m_right has been generated. - // If this is required the node is passed as 'emitExpressionInfoForMe'; do so now. - if (emitExpressionInfoForMe) - generator.emitExpressionInfo(emitExpressionInfoForMe->divot(), emitExpressionInfoForMe->startOffset(), emitExpressionInfoForMe->endOffset()); - - // If there is an assignment convert the lhs now. This will also copy lhs to - // the temporary register we allocated for it. - if (lhs) - generator.emitToPrimitive(temporaryRegisters[0].get(), lhs); - - return generator.emitStrcat(generator.finalDestination(dst, temporaryRegisters[0].get()), temporaryRegisters[0].get(), temporaryRegisters.size()); -} - -RegisterID* BinaryOpNode::emitBytecode(BytecodeGenerator& generator, RegisterID* dst) -{ - OpcodeID opcodeID = this->opcodeID(); - - if (opcodeID == op_add && m_expr1->isAdd() && m_expr1->resultDescriptor().definitelyIsString()) - return emitStrcat(generator, dst); - - if (opcodeID == op_neq) { - if (m_expr1->isNull() || m_expr2->isNull()) { - RefPtr<RegisterID> src = generator.tempDestination(dst); - generator.emitNode(src.get(), m_expr1->isNull() ? m_expr2 : m_expr1); - return generator.emitUnaryOp(op_neq_null, generator.finalDestination(dst, src.get()), src.get()); - } - } - - RefPtr<RegisterID> src1 = generator.emitNodeForLeftHandSide(m_expr1, m_rightHasAssignments, m_expr2->isPure(generator)); - RegisterID* src2 = generator.emitNode(m_expr2); - return generator.emitBinaryOp(opcodeID, generator.finalDestination(dst, src1.get()), src1.get(), src2, OperandTypes(m_expr1->resultDescriptor(), m_expr2->resultDescriptor())); -} - -RegisterID* EqualNode::emitBytecode(BytecodeGenerator& generator, RegisterID* dst) -{ - if (m_expr1->isNull() || m_expr2->isNull()) { - RefPtr<RegisterID> src = generator.tempDestination(dst); - generator.emitNode(src.get(), m_expr1->isNull() ? m_expr2 : m_expr1); - return generator.emitUnaryOp(op_eq_null, generator.finalDestination(dst, src.get()), src.get()); - } - - RefPtr<RegisterID> src1 = generator.emitNodeForLeftHandSide(m_expr1, m_rightHasAssignments, m_expr2->isPure(generator)); - RegisterID* src2 = generator.emitNode(m_expr2); - return generator.emitEqualityOp(op_eq, generator.finalDestination(dst, src1.get()), src1.get(), src2); -} - -RegisterID* StrictEqualNode::emitBytecode(BytecodeGenerator& generator, RegisterID* dst) -{ - RefPtr<RegisterID> src1 = generator.emitNodeForLeftHandSide(m_expr1, m_rightHasAssignments, m_expr2->isPure(generator)); - RegisterID* src2 = generator.emitNode(m_expr2); - return generator.emitEqualityOp(op_stricteq, generator.finalDestination(dst, src1.get()), src1.get(), src2); -} - -RegisterID* ReverseBinaryOpNode::emitBytecode(BytecodeGenerator& generator, RegisterID* dst) -{ - RefPtr<RegisterID> src1 = generator.emitNodeForLeftHandSide(m_expr1, m_rightHasAssignments, m_expr2->isPure(generator)); - RegisterID* src2 = generator.emitNode(m_expr2); - return generator.emitBinaryOp(opcodeID(), generator.finalDestination(dst, src1.get()), src2, src1.get(), OperandTypes(m_expr2->resultDescriptor(), m_expr1->resultDescriptor())); -} - -RegisterID* ThrowableBinaryOpNode::emitBytecode(BytecodeGenerator& generator, RegisterID* dst) -{ - RefPtr<RegisterID> src1 = generator.emitNodeForLeftHandSide(m_expr1, m_rightHasAssignments, m_expr2->isPure(generator)); - RegisterID* src2 = generator.emitNode(m_expr2); - generator.emitExpressionInfo(divot(), startOffset(), endOffset()); - return generator.emitBinaryOp(opcodeID(), generator.finalDestination(dst, src1.get()), src1.get(), src2, OperandTypes(m_expr1->resultDescriptor(), m_expr2->resultDescriptor())); -} - -RegisterID* InstanceOfNode::emitBytecode(BytecodeGenerator& generator, RegisterID* dst) -{ - RefPtr<RegisterID> src1 = generator.emitNodeForLeftHandSide(m_expr1, m_rightHasAssignments, m_expr2->isPure(generator)); - RefPtr<RegisterID> src2 = generator.emitNode(m_expr2); - - generator.emitExpressionInfo(divot(), startOffset(), endOffset()); - generator.emitGetByIdExceptionInfo(op_instanceof); - RegisterID* src2Prototype = generator.emitGetById(generator.newTemporary(), src2.get(), generator.globalData()->propertyNames->prototype); - - generator.emitExpressionInfo(divot(), startOffset(), endOffset()); - return generator.emitInstanceOf(generator.finalDestination(dst, src1.get()), src1.get(), src2.get(), src2Prototype); -} - -// ------------------------------ LogicalOpNode ---------------------------- - -RegisterID* LogicalOpNode::emitBytecode(BytecodeGenerator& generator, RegisterID* dst) -{ - RefPtr<RegisterID> temp = generator.tempDestination(dst); - RefPtr<Label> target = generator.newLabel(); - - generator.emitNode(temp.get(), m_expr1); - if (m_operator == OpLogicalAnd) - generator.emitJumpIfFalse(temp.get(), target.get()); - else - generator.emitJumpIfTrue(temp.get(), target.get()); - generator.emitNode(temp.get(), m_expr2); - generator.emitLabel(target.get()); - - return generator.moveToDestinationIfNeeded(dst, temp.get()); -} - -// ------------------------------ ConditionalNode ------------------------------ - -RegisterID* ConditionalNode::emitBytecode(BytecodeGenerator& generator, RegisterID* dst) -{ - RefPtr<RegisterID> newDst = generator.finalDestination(dst); - RefPtr<Label> beforeElse = generator.newLabel(); - RefPtr<Label> afterElse = generator.newLabel(); - - RegisterID* cond = generator.emitNode(m_logical); - generator.emitJumpIfFalse(cond, beforeElse.get()); - - generator.emitNode(newDst.get(), m_expr1); - generator.emitJump(afterElse.get()); - - generator.emitLabel(beforeElse.get()); - generator.emitNode(newDst.get(), m_expr2); - - generator.emitLabel(afterElse.get()); - - return newDst.get(); -} - -// ------------------------------ ReadModifyResolveNode ----------------------------------- - -// FIXME: should this be moved to be a method on BytecodeGenerator? -static ALWAYS_INLINE RegisterID* emitReadModifyAssignment(BytecodeGenerator& generator, RegisterID* dst, RegisterID* src1, ExpressionNode* m_right, Operator oper, OperandTypes types, ReadModifyResolveNode* emitExpressionInfoForMe = 0) -{ - OpcodeID opcodeID; - switch (oper) { - case OpMultEq: - opcodeID = op_mul; - break; - case OpDivEq: - opcodeID = op_div; - break; - case OpPlusEq: - if (m_right->isAdd() && m_right->resultDescriptor().definitelyIsString()) - return static_cast<AddNode*>(m_right)->emitStrcat(generator, dst, src1, emitExpressionInfoForMe); - opcodeID = op_add; - break; - case OpMinusEq: - opcodeID = op_sub; - break; - case OpLShift: - opcodeID = op_lshift; - break; - case OpRShift: - opcodeID = op_rshift; - break; - case OpURShift: - opcodeID = op_urshift; - break; - case OpAndEq: - opcodeID = op_bitand; - break; - case OpXOrEq: - opcodeID = op_bitxor; - break; - case OpOrEq: - opcodeID = op_bitor; - break; - case OpModEq: - opcodeID = op_mod; - break; - default: - ASSERT_NOT_REACHED(); - return dst; - } - - RegisterID* src2 = generator.emitNode(m_right); - - // Certain read-modify nodes require expression info to be emitted *after* m_right has been generated. - // If this is required the node is passed as 'emitExpressionInfoForMe'; do so now. - if (emitExpressionInfoForMe) - generator.emitExpressionInfo(emitExpressionInfoForMe->divot(), emitExpressionInfoForMe->startOffset(), emitExpressionInfoForMe->endOffset()); - - return generator.emitBinaryOp(opcodeID, dst, src1, src2, types); -} - -RegisterID* ReadModifyResolveNode::emitBytecode(BytecodeGenerator& generator, RegisterID* dst) -{ - if (RegisterID* local = generator.registerFor(m_ident)) { - if (generator.isLocalConstant(m_ident)) { - return emitReadModifyAssignment(generator, generator.finalDestination(dst), local, m_right, m_operator, OperandTypes(ResultType::unknownType(), m_right->resultDescriptor())); - } - - if (generator.leftHandSideNeedsCopy(m_rightHasAssignments, m_right->isPure(generator))) { - RefPtr<RegisterID> result = generator.newTemporary(); - generator.emitMove(result.get(), local); - emitReadModifyAssignment(generator, result.get(), result.get(), m_right, m_operator, OperandTypes(ResultType::unknownType(), m_right->resultDescriptor())); - generator.emitMove(local, result.get()); - return generator.moveToDestinationIfNeeded(dst, result.get()); - } - - RegisterID* result = emitReadModifyAssignment(generator, local, local, m_right, m_operator, OperandTypes(ResultType::unknownType(), m_right->resultDescriptor())); - return generator.moveToDestinationIfNeeded(dst, result); - } - - int index = 0; - size_t depth = 0; - JSObject* globalObject = 0; - if (generator.findScopedProperty(m_ident, index, depth, true, globalObject) && index != missingSymbolMarker()) { - RefPtr<RegisterID> src1 = generator.emitGetScopedVar(generator.tempDestination(dst), depth, index, globalObject); - RegisterID* result = emitReadModifyAssignment(generator, generator.finalDestination(dst, src1.get()), src1.get(), m_right, m_operator, OperandTypes(ResultType::unknownType(), m_right->resultDescriptor())); - generator.emitPutScopedVar(depth, index, result, globalObject); - return result; - } - - RefPtr<RegisterID> src1 = generator.tempDestination(dst); - generator.emitExpressionInfo(divot() - startOffset() + m_ident.size(), m_ident.size(), 0); - RefPtr<RegisterID> base = generator.emitResolveWithBase(generator.newTemporary(), src1.get(), m_ident); - RegisterID* result = emitReadModifyAssignment(generator, generator.finalDestination(dst, src1.get()), src1.get(), m_right, m_operator, OperandTypes(ResultType::unknownType(), m_right->resultDescriptor()), this); - return generator.emitPutById(base.get(), m_ident, result); -} - -// ------------------------------ AssignResolveNode ----------------------------------- - -RegisterID* AssignResolveNode::emitBytecode(BytecodeGenerator& generator, RegisterID* dst) -{ - if (RegisterID* local = generator.registerFor(m_ident)) { - if (generator.isLocalConstant(m_ident)) - return generator.emitNode(dst, m_right); - - RegisterID* result = generator.emitNode(local, m_right); - return generator.moveToDestinationIfNeeded(dst, result); - } - - int index = 0; - size_t depth = 0; - JSObject* globalObject = 0; - if (generator.findScopedProperty(m_ident, index, depth, true, globalObject) && index != missingSymbolMarker()) { - if (dst == generator.ignoredResult()) - dst = 0; - RegisterID* value = generator.emitNode(dst, m_right); - generator.emitPutScopedVar(depth, index, value, globalObject); - return value; - } - - RefPtr<RegisterID> base = generator.emitResolveBase(generator.newTemporary(), m_ident); - if (dst == generator.ignoredResult()) - dst = 0; - RegisterID* value = generator.emitNode(dst, m_right); - generator.emitExpressionInfo(divot(), startOffset(), endOffset()); - return generator.emitPutById(base.get(), m_ident, value); -} - -// ------------------------------ AssignDotNode ----------------------------------- - -RegisterID* AssignDotNode::emitBytecode(BytecodeGenerator& generator, RegisterID* dst) -{ - RefPtr<RegisterID> base = generator.emitNodeForLeftHandSide(m_base, m_rightHasAssignments, m_right->isPure(generator)); - RefPtr<RegisterID> value = generator.destinationForAssignResult(dst); - RegisterID* result = generator.emitNode(value.get(), m_right); - generator.emitExpressionInfo(divot(), startOffset(), endOffset()); - generator.emitPutById(base.get(), m_ident, result); - return generator.moveToDestinationIfNeeded(dst, result); -} - -// ------------------------------ ReadModifyDotNode ----------------------------------- - -RegisterID* ReadModifyDotNode::emitBytecode(BytecodeGenerator& generator, RegisterID* dst) -{ - RefPtr<RegisterID> base = generator.emitNodeForLeftHandSide(m_base, m_rightHasAssignments, m_right->isPure(generator)); - - generator.emitExpressionInfo(divot() - m_subexpressionDivotOffset, startOffset() - m_subexpressionDivotOffset, m_subexpressionEndOffset); - RefPtr<RegisterID> value = generator.emitGetById(generator.tempDestination(dst), base.get(), m_ident); - RegisterID* updatedValue = emitReadModifyAssignment(generator, generator.finalDestination(dst, value.get()), value.get(), m_right, m_operator, OperandTypes(ResultType::unknownType(), m_right->resultDescriptor())); - - generator.emitExpressionInfo(divot(), startOffset(), endOffset()); - return generator.emitPutById(base.get(), m_ident, updatedValue); -} - -// ------------------------------ AssignErrorNode ----------------------------------- - -RegisterID* AssignErrorNode::emitBytecode(BytecodeGenerator& generator, RegisterID*) -{ - return emitThrowError(generator, ReferenceError, "Left side of assignment is not a reference."); -} - -// ------------------------------ AssignBracketNode ----------------------------------- - -RegisterID* AssignBracketNode::emitBytecode(BytecodeGenerator& generator, RegisterID* dst) -{ - RefPtr<RegisterID> base = generator.emitNodeForLeftHandSide(m_base, m_subscriptHasAssignments || m_rightHasAssignments, m_subscript->isPure(generator) && m_right->isPure(generator)); - RefPtr<RegisterID> property = generator.emitNodeForLeftHandSide(m_subscript, m_rightHasAssignments, m_right->isPure(generator)); - RefPtr<RegisterID> value = generator.destinationForAssignResult(dst); - RegisterID* result = generator.emitNode(value.get(), m_right); - - generator.emitExpressionInfo(divot(), startOffset(), endOffset()); - generator.emitPutByVal(base.get(), property.get(), result); - return generator.moveToDestinationIfNeeded(dst, result); -} - -// ------------------------------ ReadModifyBracketNode ----------------------------------- - -RegisterID* ReadModifyBracketNode::emitBytecode(BytecodeGenerator& generator, RegisterID* dst) -{ - RefPtr<RegisterID> base = generator.emitNodeForLeftHandSide(m_base, m_subscriptHasAssignments || m_rightHasAssignments, m_subscript->isPure(generator) && m_right->isPure(generator)); - RefPtr<RegisterID> property = generator.emitNodeForLeftHandSide(m_subscript, m_rightHasAssignments, m_right->isPure(generator)); - - generator.emitExpressionInfo(divot() - m_subexpressionDivotOffset, startOffset() - m_subexpressionDivotOffset, m_subexpressionEndOffset); - RefPtr<RegisterID> value = generator.emitGetByVal(generator.tempDestination(dst), base.get(), property.get()); - RegisterID* updatedValue = emitReadModifyAssignment(generator, generator.finalDestination(dst, value.get()), value.get(), m_right, m_operator, OperandTypes(ResultType::unknownType(), m_right->resultDescriptor())); - - generator.emitExpressionInfo(divot(), startOffset(), endOffset()); - generator.emitPutByVal(base.get(), property.get(), updatedValue); - - return updatedValue; -} - -// ------------------------------ CommaNode ------------------------------------ - -RegisterID* CommaNode::emitBytecode(BytecodeGenerator& generator, RegisterID* dst) -{ - ASSERT(m_expressions.size() > 1); - for (size_t i = 0; i < m_expressions.size() - 1; i++) - generator.emitNode(generator.ignoredResult(), m_expressions[i]); - return generator.emitNode(dst, m_expressions.last()); -} - -// ------------------------------ ConstDeclNode ------------------------------------ - -RegisterID* ConstDeclNode::emitCodeSingle(BytecodeGenerator& generator) -{ - if (RegisterID* local = generator.constRegisterFor(m_ident)) { - if (!m_init) - return local; - - return generator.emitNode(local, m_init); - } - - if (generator.codeType() != EvalCode) { - if (m_init) - return generator.emitNode(m_init); - else - return generator.emitResolve(generator.newTemporary(), m_ident); - } - // FIXME: While this code should only be hit in eval code, it will potentially - // assign to the wrong base if m_ident exists in an intervening dynamic scope. - RefPtr<RegisterID> base = generator.emitResolveBase(generator.newTemporary(), m_ident); - RegisterID* value = m_init ? generator.emitNode(m_init) : generator.emitLoad(0, jsUndefined()); - return generator.emitPutById(base.get(), m_ident, value); -} - -RegisterID* ConstDeclNode::emitBytecode(BytecodeGenerator& generator, RegisterID*) -{ - RegisterID* result = 0; - for (ConstDeclNode* n = this; n; n = n->m_next) - result = n->emitCodeSingle(generator); - - return result; -} - -// ------------------------------ ConstStatementNode ----------------------------- - -RegisterID* ConstStatementNode::emitBytecode(BytecodeGenerator& generator, RegisterID*) -{ - generator.emitDebugHook(WillExecuteStatement, firstLine(), lastLine()); - return generator.emitNode(m_next); -} - -// ------------------------------ SourceElements ------------------------------- - -inline void SourceElements::emitBytecode(BytecodeGenerator& generator, RegisterID* dst) -{ - size_t size = m_statements.size(); - for (size_t i = 0; i < size; ++i) - generator.emitNode(dst, m_statements[i]); -} - -// ------------------------------ BlockNode ------------------------------------ - -inline StatementNode* BlockNode::lastStatement() const -{ - return m_statements ? m_statements->lastStatement() : 0; -} - -RegisterID* BlockNode::emitBytecode(BytecodeGenerator& generator, RegisterID* dst) -{ - if (m_statements) - m_statements->emitBytecode(generator, dst); - return 0; -} - -// ------------------------------ EmptyStatementNode --------------------------- - -RegisterID* EmptyStatementNode::emitBytecode(BytecodeGenerator& generator, RegisterID* dst) -{ - generator.emitDebugHook(WillExecuteStatement, firstLine(), lastLine()); - return dst; -} - -// ------------------------------ DebuggerStatementNode --------------------------- - -RegisterID* DebuggerStatementNode::emitBytecode(BytecodeGenerator& generator, RegisterID* dst) -{ - generator.emitDebugHook(DidReachBreakpoint, firstLine(), lastLine()); - return dst; -} - -// ------------------------------ ExprStatementNode ---------------------------- - -RegisterID* ExprStatementNode::emitBytecode(BytecodeGenerator& generator, RegisterID* dst) -{ - ASSERT(m_expr); - generator.emitDebugHook(WillExecuteStatement, firstLine(), lastLine()); - return generator.emitNode(dst, m_expr); -} - -// ------------------------------ VarStatementNode ---------------------------- - -RegisterID* VarStatementNode::emitBytecode(BytecodeGenerator& generator, RegisterID*) -{ - ASSERT(m_expr); - generator.emitDebugHook(WillExecuteStatement, firstLine(), lastLine()); - return generator.emitNode(m_expr); -} - -// ------------------------------ IfNode --------------------------------------- - -RegisterID* IfNode::emitBytecode(BytecodeGenerator& generator, RegisterID* dst) -{ - generator.emitDebugHook(WillExecuteStatement, firstLine(), lastLine()); - - RefPtr<Label> afterThen = generator.newLabel(); - - RegisterID* cond = generator.emitNode(m_condition); - generator.emitJumpIfFalse(cond, afterThen.get()); - - generator.emitNode(dst, m_ifBlock); - generator.emitLabel(afterThen.get()); - - // FIXME: This should return the last statement executed so that it can be returned as a Completion. - return 0; -} - -// ------------------------------ IfElseNode --------------------------------------- - -RegisterID* IfElseNode::emitBytecode(BytecodeGenerator& generator, RegisterID* dst) -{ - generator.emitDebugHook(WillExecuteStatement, firstLine(), lastLine()); - - RefPtr<Label> beforeElse = generator.newLabel(); - RefPtr<Label> afterElse = generator.newLabel(); - - RegisterID* cond = generator.emitNode(m_condition); - generator.emitJumpIfFalse(cond, beforeElse.get()); - - generator.emitNode(dst, m_ifBlock); - generator.emitJump(afterElse.get()); - - generator.emitLabel(beforeElse.get()); - - generator.emitNode(dst, m_elseBlock); - - generator.emitLabel(afterElse.get()); - - // FIXME: This should return the last statement executed so that it can be returned as a Completion. - return 0; -} - -// ------------------------------ DoWhileNode ---------------------------------- - -RegisterID* DoWhileNode::emitBytecode(BytecodeGenerator& generator, RegisterID* dst) -{ - RefPtr<LabelScope> scope = generator.newLabelScope(LabelScope::Loop); - - RefPtr<Label> topOfLoop = generator.newLabel(); - generator.emitLabel(topOfLoop.get()); - - generator.emitDebugHook(WillExecuteStatement, firstLine(), lastLine()); - - RefPtr<RegisterID> result = generator.emitNode(dst, m_statement); - - generator.emitLabel(scope->continueTarget()); - generator.emitDebugHook(WillExecuteStatement, m_expr->lineNo(), m_expr->lineNo()); - RegisterID* cond = generator.emitNode(m_expr); - generator.emitJumpIfTrue(cond, topOfLoop.get()); - - generator.emitLabel(scope->breakTarget()); - return result.get(); -} - -// ------------------------------ WhileNode ------------------------------------ - -RegisterID* WhileNode::emitBytecode(BytecodeGenerator& generator, RegisterID* dst) -{ - RefPtr<LabelScope> scope = generator.newLabelScope(LabelScope::Loop); - - generator.emitJump(scope->continueTarget()); - - RefPtr<Label> topOfLoop = generator.newLabel(); - generator.emitLabel(topOfLoop.get()); - - generator.emitNode(dst, m_statement); - - generator.emitLabel(scope->continueTarget()); - generator.emitDebugHook(WillExecuteStatement, m_expr->lineNo(), m_expr->lineNo()); - RegisterID* cond = generator.emitNode(m_expr); - generator.emitJumpIfTrue(cond, topOfLoop.get()); - - generator.emitLabel(scope->breakTarget()); - - // FIXME: This should return the last statement executed so that it can be returned as a Completion - return 0; -} - -// ------------------------------ ForNode -------------------------------------- - -RegisterID* ForNode::emitBytecode(BytecodeGenerator& generator, RegisterID* dst) -{ - RefPtr<LabelScope> scope = generator.newLabelScope(LabelScope::Loop); - - generator.emitDebugHook(WillExecuteStatement, firstLine(), lastLine()); - - if (m_expr1) - generator.emitNode(generator.ignoredResult(), m_expr1); - - RefPtr<Label> condition = generator.newLabel(); - generator.emitJump(condition.get()); - - RefPtr<Label> topOfLoop = generator.newLabel(); - generator.emitLabel(topOfLoop.get()); - - RefPtr<RegisterID> result = generator.emitNode(dst, m_statement); - - generator.emitLabel(scope->continueTarget()); - generator.emitDebugHook(WillExecuteStatement, firstLine(), lastLine()); - if (m_expr3) - generator.emitNode(generator.ignoredResult(), m_expr3); - - generator.emitLabel(condition.get()); - if (m_expr2) { - RegisterID* cond = generator.emitNode(m_expr2); - generator.emitJumpIfTrue(cond, topOfLoop.get()); - } else - generator.emitJump(topOfLoop.get()); - - generator.emitLabel(scope->breakTarget()); - return result.get(); -} - -// ------------------------------ ForInNode ------------------------------------ - -RegisterID* ForInNode::emitBytecode(BytecodeGenerator& generator, RegisterID* dst) -{ - RefPtr<LabelScope> scope = generator.newLabelScope(LabelScope::Loop); - - if (!m_lexpr->isLocation()) - return emitThrowError(generator, ReferenceError, "Left side of for-in statement is not a reference."); - - generator.emitDebugHook(WillExecuteStatement, firstLine(), lastLine()); - - if (m_init) - generator.emitNode(generator.ignoredResult(), m_init); - - RefPtr<RegisterID> base = generator.newTemporary(); - generator.emitNode(base.get(), m_expr); - RefPtr<RegisterID> i = generator.newTemporary(); - RefPtr<RegisterID> size = generator.newTemporary(); - RefPtr<RegisterID> expectedSubscript; - RefPtr<RegisterID> iter = generator.emitGetPropertyNames(generator.newTemporary(), base.get(), i.get(), size.get(), scope->breakTarget()); - generator.emitJump(scope->continueTarget()); - - RefPtr<Label> loopStart = generator.newLabel(); - generator.emitLabel(loopStart.get()); - - RegisterID* propertyName; - bool optimizedForinAccess = false; - if (m_lexpr->isResolveNode()) { - const Identifier& ident = static_cast<ResolveNode*>(m_lexpr)->identifier(); - propertyName = generator.registerFor(ident); - if (!propertyName) { - propertyName = generator.newTemporary(); - RefPtr<RegisterID> protect = propertyName; - RegisterID* base = generator.emitResolveBase(generator.newTemporary(), ident); - - generator.emitExpressionInfo(divot(), startOffset(), endOffset()); - generator.emitPutById(base, ident, propertyName); - } else { - expectedSubscript = generator.emitMove(generator.newTemporary(), propertyName); - generator.pushOptimisedForIn(expectedSubscript.get(), iter.get(), i.get(), propertyName); - optimizedForinAccess = true; - } - } else if (m_lexpr->isDotAccessorNode()) { - DotAccessorNode* assignNode = static_cast<DotAccessorNode*>(m_lexpr); - const Identifier& ident = assignNode->identifier(); - propertyName = generator.newTemporary(); - RefPtr<RegisterID> protect = propertyName; - RegisterID* base = generator.emitNode(assignNode->base()); - - generator.emitExpressionInfo(assignNode->divot(), assignNode->startOffset(), assignNode->endOffset()); - generator.emitPutById(base, ident, propertyName); - } else { - ASSERT(m_lexpr->isBracketAccessorNode()); - BracketAccessorNode* assignNode = static_cast<BracketAccessorNode*>(m_lexpr); - propertyName = generator.newTemporary(); - RefPtr<RegisterID> protect = propertyName; - RefPtr<RegisterID> base = generator.emitNode(assignNode->base()); - RegisterID* subscript = generator.emitNode(assignNode->subscript()); - - generator.emitExpressionInfo(assignNode->divot(), assignNode->startOffset(), assignNode->endOffset()); - generator.emitPutByVal(base.get(), subscript, propertyName); - } - - generator.emitNode(dst, m_statement); - - if (optimizedForinAccess) - generator.popOptimisedForIn(); - - generator.emitLabel(scope->continueTarget()); - generator.emitNextPropertyName(propertyName, base.get(), i.get(), size.get(), iter.get(), loopStart.get()); - generator.emitDebugHook(WillExecuteStatement, firstLine(), lastLine()); - generator.emitLabel(scope->breakTarget()); - return dst; -} - -// ------------------------------ ContinueNode --------------------------------- - -// ECMA 12.7 -RegisterID* ContinueNode::emitBytecode(BytecodeGenerator& generator, RegisterID* dst) -{ - generator.emitDebugHook(WillExecuteStatement, firstLine(), lastLine()); - - LabelScope* scope = generator.continueTarget(m_ident); - - if (!scope) - return m_ident.isEmpty() - ? emitThrowError(generator, SyntaxError, "Invalid continue statement.") - : emitThrowError(generator, SyntaxError, "Undefined label: '%s'.", m_ident); - - generator.emitJumpScopes(scope->continueTarget(), scope->scopeDepth()); - return dst; -} - -// ------------------------------ BreakNode ------------------------------------ - -// ECMA 12.8 -RegisterID* BreakNode::emitBytecode(BytecodeGenerator& generator, RegisterID* dst) -{ - generator.emitDebugHook(WillExecuteStatement, firstLine(), lastLine()); - - LabelScope* scope = generator.breakTarget(m_ident); - - if (!scope) - return m_ident.isEmpty() - ? emitThrowError(generator, SyntaxError, "Invalid break statement.") - : emitThrowError(generator, SyntaxError, "Undefined label: '%s'.", m_ident); - - generator.emitJumpScopes(scope->breakTarget(), scope->scopeDepth()); - return dst; -} - -// ------------------------------ ReturnNode ----------------------------------- - -RegisterID* ReturnNode::emitBytecode(BytecodeGenerator& generator, RegisterID* dst) -{ - generator.emitDebugHook(WillExecuteStatement, firstLine(), lastLine()); - if (generator.codeType() != FunctionCode) - return emitThrowError(generator, SyntaxError, "Invalid return statement."); - - if (dst == generator.ignoredResult()) - dst = 0; - RegisterID* r0 = m_value ? generator.emitNode(dst, m_value) : generator.emitLoad(dst, jsUndefined()); - RefPtr<RegisterID> returnRegister; - if (generator.scopeDepth()) { - RefPtr<Label> l0 = generator.newLabel(); - if (generator.hasFinaliser() && !r0->isTemporary()) { - returnRegister = generator.emitMove(generator.newTemporary(), r0); - r0 = returnRegister.get(); - } - generator.emitJumpScopes(l0.get(), 0); - generator.emitLabel(l0.get()); - } - generator.emitDebugHook(WillLeaveCallFrame, firstLine(), lastLine()); - return generator.emitReturn(r0); -} - -// ------------------------------ WithNode ------------------------------------- - -RegisterID* WithNode::emitBytecode(BytecodeGenerator& generator, RegisterID* dst) -{ - generator.emitDebugHook(WillExecuteStatement, firstLine(), lastLine()); - - RefPtr<RegisterID> scope = generator.newTemporary(); - generator.emitNode(scope.get(), m_expr); // scope must be protected until popped - generator.emitExpressionInfo(m_divot, m_expressionLength, 0); - generator.emitPushScope(scope.get()); - RegisterID* result = generator.emitNode(dst, m_statement); - generator.emitPopScope(); - return result; -} - -// ------------------------------ CaseClauseNode -------------------------------- - -inline void CaseClauseNode::emitBytecode(BytecodeGenerator& generator, RegisterID* dst) -{ - if (m_statements) - m_statements->emitBytecode(generator, dst); -} - -// ------------------------------ CaseBlockNode -------------------------------- - -enum SwitchKind { - SwitchUnset = 0, - SwitchNumber = 1, - SwitchString = 2, - SwitchNeither = 3 -}; - -static void processClauseList(ClauseListNode* list, Vector<ExpressionNode*, 8>& literalVector, SwitchKind& typeForTable, bool& singleCharacterSwitch, int32_t& min_num, int32_t& max_num) -{ - for (; list; list = list->getNext()) { - ExpressionNode* clauseExpression = list->getClause()->expr(); - literalVector.append(clauseExpression); - if (clauseExpression->isNumber()) { - double value = static_cast<NumberNode*>(clauseExpression)->value(); - int32_t intVal = static_cast<int32_t>(value); - if ((typeForTable & ~SwitchNumber) || (intVal != value)) { - typeForTable = SwitchNeither; - break; - } - if (intVal < min_num) - min_num = intVal; - if (intVal > max_num) - max_num = intVal; - typeForTable = SwitchNumber; - continue; - } - if (clauseExpression->isString()) { - if (typeForTable & ~SwitchString) { - typeForTable = SwitchNeither; - break; - } - const UString& value = static_cast<StringNode*>(clauseExpression)->value().ustring(); - if (singleCharacterSwitch &= value.size() == 1) { - int32_t intVal = value.rep()->data()[0]; - if (intVal < min_num) - min_num = intVal; - if (intVal > max_num) - max_num = intVal; - } - typeForTable = SwitchString; - continue; - } - typeForTable = SwitchNeither; - break; - } -} - -SwitchInfo::SwitchType CaseBlockNode::tryOptimizedSwitch(Vector<ExpressionNode*, 8>& literalVector, int32_t& min_num, int32_t& max_num) -{ - SwitchKind typeForTable = SwitchUnset; - bool singleCharacterSwitch = true; - - processClauseList(m_list1, literalVector, typeForTable, singleCharacterSwitch, min_num, max_num); - processClauseList(m_list2, literalVector, typeForTable, singleCharacterSwitch, min_num, max_num); - - if (typeForTable == SwitchUnset || typeForTable == SwitchNeither) - return SwitchInfo::SwitchNone; - - if (typeForTable == SwitchNumber) { - int32_t range = max_num - min_num; - if (min_num <= max_num && range <= 1000 && (range / literalVector.size()) < 10) - return SwitchInfo::SwitchImmediate; - return SwitchInfo::SwitchNone; - } - - ASSERT(typeForTable == SwitchString); - - if (singleCharacterSwitch) { - int32_t range = max_num - min_num; - if (min_num <= max_num && range <= 1000 && (range / literalVector.size()) < 10) - return SwitchInfo::SwitchCharacter; - } - - return SwitchInfo::SwitchString; -} - -RegisterID* CaseBlockNode::emitBytecodeForBlock(BytecodeGenerator& generator, RegisterID* switchExpression, RegisterID* dst) -{ - RefPtr<Label> defaultLabel; - Vector<RefPtr<Label>, 8> labelVector; - Vector<ExpressionNode*, 8> literalVector; - int32_t min_num = std::numeric_limits<int32_t>::max(); - int32_t max_num = std::numeric_limits<int32_t>::min(); - SwitchInfo::SwitchType switchType = tryOptimizedSwitch(literalVector, min_num, max_num); - - if (switchType != SwitchInfo::SwitchNone) { - // Prepare the various labels - for (uint32_t i = 0; i < literalVector.size(); i++) - labelVector.append(generator.newLabel()); - defaultLabel = generator.newLabel(); - generator.beginSwitch(switchExpression, switchType); - } else { - // Setup jumps - for (ClauseListNode* list = m_list1; list; list = list->getNext()) { - RefPtr<RegisterID> clauseVal = generator.newTemporary(); - generator.emitNode(clauseVal.get(), list->getClause()->expr()); - generator.emitBinaryOp(op_stricteq, clauseVal.get(), clauseVal.get(), switchExpression, OperandTypes()); - labelVector.append(generator.newLabel()); - generator.emitJumpIfTrue(clauseVal.get(), labelVector[labelVector.size() - 1].get()); - } - - for (ClauseListNode* list = m_list2; list; list = list->getNext()) { - RefPtr<RegisterID> clauseVal = generator.newTemporary(); - generator.emitNode(clauseVal.get(), list->getClause()->expr()); - generator.emitBinaryOp(op_stricteq, clauseVal.get(), clauseVal.get(), switchExpression, OperandTypes()); - labelVector.append(generator.newLabel()); - generator.emitJumpIfTrue(clauseVal.get(), labelVector[labelVector.size() - 1].get()); - } - defaultLabel = generator.newLabel(); - generator.emitJump(defaultLabel.get()); - } - - RegisterID* result = 0; - - size_t i = 0; - for (ClauseListNode* list = m_list1; list; list = list->getNext()) { - generator.emitLabel(labelVector[i++].get()); - list->getClause()->emitBytecode(generator, dst); - } - - if (m_defaultClause) { - generator.emitLabel(defaultLabel.get()); - m_defaultClause->emitBytecode(generator, dst); - } - - for (ClauseListNode* list = m_list2; list; list = list->getNext()) { - generator.emitLabel(labelVector[i++].get()); - list->getClause()->emitBytecode(generator, dst); - } - if (!m_defaultClause) - generator.emitLabel(defaultLabel.get()); - - ASSERT(i == labelVector.size()); - if (switchType != SwitchInfo::SwitchNone) { - ASSERT(labelVector.size() == literalVector.size()); - generator.endSwitch(labelVector.size(), labelVector.data(), literalVector.data(), defaultLabel.get(), min_num, max_num); - } - return result; -} - -// ------------------------------ SwitchNode ----------------------------------- - -RegisterID* SwitchNode::emitBytecode(BytecodeGenerator& generator, RegisterID* dst) -{ - generator.emitDebugHook(WillExecuteStatement, firstLine(), lastLine()); - - RefPtr<LabelScope> scope = generator.newLabelScope(LabelScope::Switch); - - RefPtr<RegisterID> r0 = generator.emitNode(m_expr); - RegisterID* r1 = m_block->emitBytecodeForBlock(generator, r0.get(), dst); - - generator.emitLabel(scope->breakTarget()); - return r1; -} - -// ------------------------------ LabelNode ------------------------------------ - -RegisterID* LabelNode::emitBytecode(BytecodeGenerator& generator, RegisterID* dst) -{ - generator.emitDebugHook(WillExecuteStatement, firstLine(), lastLine()); - - if (generator.breakTarget(m_name)) - return emitThrowError(generator, SyntaxError, "Duplicate label: %s.", m_name); - - RefPtr<LabelScope> scope = generator.newLabelScope(LabelScope::NamedLabel, &m_name); - RegisterID* r0 = generator.emitNode(dst, m_statement); - - generator.emitLabel(scope->breakTarget()); - return r0; -} - -// ------------------------------ ThrowNode ------------------------------------ - -RegisterID* ThrowNode::emitBytecode(BytecodeGenerator& generator, RegisterID* dst) -{ - generator.emitDebugHook(WillExecuteStatement, firstLine(), lastLine()); - - if (dst == generator.ignoredResult()) - dst = 0; - RefPtr<RegisterID> expr = generator.emitNode(m_expr); - generator.emitExpressionInfo(divot(), startOffset(), endOffset()); - generator.emitThrow(expr.get()); - return 0; -} - -// ------------------------------ TryNode -------------------------------------- - -RegisterID* TryNode::emitBytecode(BytecodeGenerator& generator, RegisterID* dst) -{ - // NOTE: The catch and finally blocks must be labeled explicitly, so the - // optimizer knows they may be jumped to from anywhere. - - generator.emitDebugHook(WillExecuteStatement, firstLine(), lastLine()); - - RefPtr<Label> tryStartLabel = generator.newLabel(); - RefPtr<Label> finallyStart; - RefPtr<RegisterID> finallyReturnAddr; - if (m_finallyBlock) { - finallyStart = generator.newLabel(); - finallyReturnAddr = generator.newTemporary(); - generator.pushFinallyContext(finallyStart.get(), finallyReturnAddr.get()); - } - - generator.emitLabel(tryStartLabel.get()); - generator.emitNode(dst, m_tryBlock); - - if (m_catchBlock) { - RefPtr<Label> catchEndLabel = generator.newLabel(); - - // Normal path: jump over the catch block. - generator.emitJump(catchEndLabel.get()); - - // Uncaught exception path: the catch block. - RefPtr<Label> here = generator.emitLabel(generator.newLabel().get()); - RefPtr<RegisterID> exceptionRegister = generator.emitCatch(generator.newTemporary(), tryStartLabel.get(), here.get()); - if (m_catchHasEval) { - RefPtr<RegisterID> dynamicScopeObject = generator.emitNewObject(generator.newTemporary()); - generator.emitPutById(dynamicScopeObject.get(), m_exceptionIdent, exceptionRegister.get()); - generator.emitMove(exceptionRegister.get(), dynamicScopeObject.get()); - generator.emitPushScope(exceptionRegister.get()); - } else - generator.emitPushNewScope(exceptionRegister.get(), m_exceptionIdent, exceptionRegister.get()); - generator.emitNode(dst, m_catchBlock); - generator.emitPopScope(); - generator.emitLabel(catchEndLabel.get()); - } - - if (m_finallyBlock) { - generator.popFinallyContext(); - // there may be important registers live at the time we jump - // to a finally block (such as for a return or throw) so we - // ref the highest register ever used as a conservative - // approach to not clobbering anything important - RefPtr<RegisterID> highestUsedRegister = generator.highestUsedRegister(); - RefPtr<Label> finallyEndLabel = generator.newLabel(); - - // Normal path: invoke the finally block, then jump over it. - generator.emitJumpSubroutine(finallyReturnAddr.get(), finallyStart.get()); - generator.emitJump(finallyEndLabel.get()); - - // Uncaught exception path: invoke the finally block, then re-throw the exception. - RefPtr<Label> here = generator.emitLabel(generator.newLabel().get()); - RefPtr<RegisterID> tempExceptionRegister = generator.emitCatch(generator.newTemporary(), tryStartLabel.get(), here.get()); - generator.emitJumpSubroutine(finallyReturnAddr.get(), finallyStart.get()); - generator.emitThrow(tempExceptionRegister.get()); - - // The finally block. - generator.emitLabel(finallyStart.get()); - generator.emitNode(dst, m_finallyBlock); - generator.emitSubroutineReturn(finallyReturnAddr.get()); - - generator.emitLabel(finallyEndLabel.get()); - } - - return dst; -} - // -----------------------------ScopeNodeData --------------------------- ScopeNodeData::ScopeNodeData(ParserArena& arena, SourceElements* statements, VarStack* varStack, FunctionStack* funcStack, int numConstants) @@ -1908,12 +104,6 @@ ScopeNode::ScopeNode(JSGlobalData* globalData, const SourceCode& source, SourceE { } -inline void ScopeNode::emitStatementsBytecode(BytecodeGenerator& generator, RegisterID* dst) -{ - if (m_data->m_statements) - m_data->m_statements->emitBytecode(generator, dst); -} - StatementNode* ScopeNode::singleStatement() const { return m_data->m_statements ? m_data->m_statements->singleStatement() : 0; @@ -1937,19 +127,6 @@ PassRefPtr<ProgramNode> ProgramNode::create(JSGlobalData* globalData, SourceElem return node.release(); } -RegisterID* ProgramNode::emitBytecode(BytecodeGenerator& generator, RegisterID*) -{ - generator.emitDebugHook(WillExecuteProgram, firstLine(), lastLine()); - - RefPtr<RegisterID> dstRegister = generator.newTemporary(); - generator.emitLoad(dstRegister.get(), jsUndefined()); - emitStatementsBytecode(generator, dstRegister.get()); - - generator.emitDebugHook(DidExecuteProgram, firstLine(), lastLine()); - generator.emitEnd(dstRegister.get()); - return 0; -} - // ------------------------------ EvalNode ----------------------------- inline EvalNode::EvalNode(JSGlobalData* globalData, SourceElements* children, VarStack* varStack, FunctionStack* funcStack, const SourceCode& source, CodeFeatures features, int numConstants) @@ -1968,19 +145,6 @@ PassRefPtr<EvalNode> EvalNode::create(JSGlobalData* globalData, SourceElements* return node.release(); } -RegisterID* EvalNode::emitBytecode(BytecodeGenerator& generator, RegisterID*) -{ - generator.emitDebugHook(WillExecuteProgram, firstLine(), lastLine()); - - RefPtr<RegisterID> dstRegister = generator.newTemporary(); - generator.emitLoad(dstRegister.get(), jsUndefined()); - emitStatementsBytecode(generator, dstRegister.get()); - - generator.emitDebugHook(DidExecuteProgram, firstLine(), lastLine()); - generator.emitEnd(dstRegister.get()); - return 0; -} - // ------------------------------ FunctionBodyNode ----------------------------- FunctionParameters::FunctionParameters(ParameterNode* firstParameter) @@ -2028,37 +192,4 @@ PassRefPtr<FunctionBodyNode> FunctionBodyNode::create(JSGlobalData* globalData, return node.release(); } -RegisterID* FunctionBodyNode::emitBytecode(BytecodeGenerator& generator, RegisterID*) -{ - generator.emitDebugHook(DidEnterCallFrame, firstLine(), lastLine()); - emitStatementsBytecode(generator, generator.ignoredResult()); - StatementNode* singleStatement = this->singleStatement(); - if (singleStatement && singleStatement->isBlock()) { - StatementNode* lastStatementInBlock = static_cast<BlockNode*>(singleStatement)->lastStatement(); - if (lastStatementInBlock && lastStatementInBlock->isReturnNode()) - return 0; - } - - RegisterID* r0 = generator.emitLoad(0, jsUndefined()); - generator.emitDebugHook(WillLeaveCallFrame, firstLine(), lastLine()); - generator.emitReturn(r0); - return 0; -} - -// ------------------------------ FuncDeclNode --------------------------------- - -RegisterID* FuncDeclNode::emitBytecode(BytecodeGenerator& generator, RegisterID* dst) -{ - if (dst == generator.ignoredResult()) - dst = 0; - return dst; -} - -// ------------------------------ FuncExprNode --------------------------------- - -RegisterID* FuncExprNode::emitBytecode(BytecodeGenerator& generator, RegisterID* dst) -{ - return generator.emitNewFunctionExpression(generator.finalDestination(dst), this); -} - } // namespace JSC diff --git a/JavaScriptCore/parser/Nodes.h b/JavaScriptCore/parser/Nodes.h index be84c4a..c216ea8 100644 --- a/JavaScriptCore/parser/Nodes.h +++ b/JavaScriptCore/parser/Nodes.h @@ -40,6 +40,7 @@ namespace JSC { class ArgumentListNode; class BytecodeGenerator; class FunctionBodyNode; + class Label; class PropertyListNode; class ReadModifyResolveNode; class RegisterID; @@ -151,6 +152,9 @@ namespace JSC { virtual bool isCommaNode() const { return false; } virtual bool isSimpleArray() const { return false; } virtual bool isAdd() const { return false; } + virtual bool hasConditionContextCodegen() const { return false; } + + virtual void emitBytecodeInConditionContext(BytecodeGenerator&, Label*, Label*, bool) { ASSERT_NOT_REACHED(); } virtual ExpressionNode* stripUnaryPlus() { return this; } @@ -757,6 +761,7 @@ namespace JSC { protected: ExpressionNode* expr() { return m_expr; } + const ExpressionNode* expr() const { return m_expr; } private: virtual RegisterID* emitBytecode(BytecodeGenerator&, RegisterID* = 0); @@ -788,6 +793,9 @@ namespace JSC { class LogicalNotNode : public UnaryOpNode { public: LogicalNotNode(JSGlobalData*, ExpressionNode*); + private: + void emitBytecodeInConditionContext(BytecodeGenerator&, Label* trueTarget, Label* falseTarget, bool fallThroughMeansTrue); + virtual bool hasConditionContextCodegen() const { return expr()->hasConditionContextCodegen(); } }; class BinaryOpNode : public ExpressionNode { @@ -952,6 +960,8 @@ namespace JSC { private: virtual RegisterID* emitBytecode(BytecodeGenerator&, RegisterID* = 0); + void emitBytecodeInConditionContext(BytecodeGenerator&, Label* trueTarget, Label* falseTarget, bool fallThroughMeansTrue); + virtual bool hasConditionContextCodegen() const { return true; } ExpressionNode* m_expr1; ExpressionNode* m_expr2; diff --git a/JavaScriptCore/pcre/pcre_exec.cpp b/JavaScriptCore/pcre/pcre_exec.cpp index 16619d4..8ca2eb4 100644 --- a/JavaScriptCore/pcre/pcre_exec.cpp +++ b/JavaScriptCore/pcre/pcre_exec.cpp @@ -2164,14 +2164,14 @@ void Histogram::add(const JSRegExp* re, double elapsedTime) HistogramTimeLogger::HistogramTimeLogger(const JSRegExp* re) : m_re(re) - , m_startTime(getCurrentUTCTimeWithMicroseconds()) + , m_startTime(currentTimeMS()) { } HistogramTimeLogger::~HistogramTimeLogger() { static Histogram histogram; - histogram.add(m_re, getCurrentUTCTimeWithMicroseconds() - m_startTime); + histogram.add(m_re, currentTimeMS() - m_startTime); } #endif diff --git a/JavaScriptCore/profiler/ProfileGenerator.cpp b/JavaScriptCore/profiler/ProfileGenerator.cpp index dc68ecb..17d37d7 100644 --- a/JavaScriptCore/profiler/ProfileGenerator.cpp +++ b/JavaScriptCore/profiler/ProfileGenerator.cpp @@ -63,7 +63,7 @@ void ProfileGenerator::addParentForConsoleStart(ExecState* exec) JSValue function; exec->interpreter()->retrieveLastCaller(exec, lineNumber, sourceID, sourceURL, function); - m_currentNode = ProfileNode::create(Profiler::createCallIdentifier(&exec->globalData(), function ? function.toThisObject(exec) : 0, sourceURL, lineNumber), m_head.get(), m_head.get()); + m_currentNode = ProfileNode::create(Profiler::createCallIdentifier(exec, function ? function.toThisObject(exec) : 0, sourceURL, lineNumber), m_head.get(), m_head.get()); m_head->insertNode(m_currentNode.get()); } diff --git a/JavaScriptCore/profiler/ProfileNode.cpp b/JavaScriptCore/profiler/ProfileNode.cpp index 19050aa..02fb7c9 100644 --- a/JavaScriptCore/profiler/ProfileNode.cpp +++ b/JavaScriptCore/profiler/ProfileNode.cpp @@ -37,6 +37,8 @@ #include <windows.h> #endif +using namespace WTF; + namespace JSC { static double getCount() @@ -49,7 +51,7 @@ static double getCount() QueryPerformanceCounter(&counter); return static_cast<double>(counter.QuadPart) / frequency.QuadPart; #else - return WTF::getCurrentUTCTimeWithMicroseconds(); + return currentTimeMS(); #endif } diff --git a/JavaScriptCore/profiler/Profiler.cpp b/JavaScriptCore/profiler/Profiler.cpp index 6f72e08..5585d2e 100644 --- a/JavaScriptCore/profiler/Profiler.cpp +++ b/JavaScriptCore/profiler/Profiler.cpp @@ -46,7 +46,7 @@ static const char* GlobalCodeExecution = "(program)"; static const char* AnonymousFunction = "(anonymous function)"; static unsigned ProfilesUID = 0; -static CallIdentifier createCallIdentifierFromFunctionImp(JSGlobalData*, JSFunction*); +static CallIdentifier createCallIdentifierFromFunctionImp(ExecState*, JSFunction*); Profiler* Profiler::s_sharedProfiler = 0; Profiler* Profiler::s_sharedEnabledProfilerReference = 0; @@ -109,14 +109,14 @@ void Profiler::willExecute(ExecState* exec, JSValue function) { ASSERT(!m_currentProfiles.isEmpty()); - dispatchFunctionToProfiles(m_currentProfiles, &ProfileGenerator::willExecute, createCallIdentifier(&exec->globalData(), function, "", 0), exec->lexicalGlobalObject()->profileGroup()); + dispatchFunctionToProfiles(m_currentProfiles, &ProfileGenerator::willExecute, createCallIdentifier(exec, function, "", 0), exec->lexicalGlobalObject()->profileGroup()); } void Profiler::willExecute(ExecState* exec, const UString& sourceURL, int startingLineNumber) { ASSERT(!m_currentProfiles.isEmpty()); - CallIdentifier callIdentifier = createCallIdentifier(&exec->globalData(), JSValue(), sourceURL, startingLineNumber); + CallIdentifier callIdentifier = createCallIdentifier(exec, JSValue(), sourceURL, startingLineNumber); dispatchFunctionToProfiles(m_currentProfiles, &ProfileGenerator::willExecute, callIdentifier, exec->lexicalGlobalObject()->profileGroup()); } @@ -125,17 +125,17 @@ void Profiler::didExecute(ExecState* exec, JSValue function) { ASSERT(!m_currentProfiles.isEmpty()); - dispatchFunctionToProfiles(m_currentProfiles, &ProfileGenerator::didExecute, createCallIdentifier(&exec->globalData(), function, "", 0), exec->lexicalGlobalObject()->profileGroup()); + dispatchFunctionToProfiles(m_currentProfiles, &ProfileGenerator::didExecute, createCallIdentifier(exec, function, "", 0), exec->lexicalGlobalObject()->profileGroup()); } void Profiler::didExecute(ExecState* exec, const UString& sourceURL, int startingLineNumber) { ASSERT(!m_currentProfiles.isEmpty()); - dispatchFunctionToProfiles(m_currentProfiles, &ProfileGenerator::didExecute, createCallIdentifier(&exec->globalData(), JSValue(), sourceURL, startingLineNumber), exec->lexicalGlobalObject()->profileGroup()); + dispatchFunctionToProfiles(m_currentProfiles, &ProfileGenerator::didExecute, createCallIdentifier(exec, JSValue(), sourceURL, startingLineNumber), exec->lexicalGlobalObject()->profileGroup()); } -CallIdentifier Profiler::createCallIdentifier(JSGlobalData* globalData, JSValue functionValue, const UString& defaultSourceURL, int defaultLineNumber) +CallIdentifier Profiler::createCallIdentifier(ExecState* exec, JSValue functionValue, const UString& defaultSourceURL, int defaultLineNumber) { if (!functionValue) return CallIdentifier(GlobalCodeExecution, defaultSourceURL, defaultLineNumber); @@ -144,17 +144,17 @@ CallIdentifier Profiler::createCallIdentifier(JSGlobalData* globalData, JSValue if (asObject(functionValue)->inherits(&JSFunction::info)) { JSFunction* function = asFunction(functionValue); if (!function->executable()->isHostFunction()) - return createCallIdentifierFromFunctionImp(globalData, function); + return createCallIdentifierFromFunctionImp(exec, function); } if (asObject(functionValue)->inherits(&InternalFunction::info)) - return CallIdentifier(static_cast<InternalFunction*>(asObject(functionValue))->name(globalData), defaultSourceURL, defaultLineNumber); + return CallIdentifier(static_cast<InternalFunction*>(asObject(functionValue))->name(exec), defaultSourceURL, defaultLineNumber); return CallIdentifier("(" + asObject(functionValue)->className() + " object)", defaultSourceURL, defaultLineNumber); } -CallIdentifier createCallIdentifierFromFunctionImp(JSGlobalData* globalData, JSFunction* function) +CallIdentifier createCallIdentifierFromFunctionImp(ExecState* exec, JSFunction* function) { ASSERT(!function->isHostFunction()); - const UString& name = function->calculatedDisplayName(globalData); + const UString& name = function->calculatedDisplayName(exec); return CallIdentifier(name.isEmpty() ? AnonymousFunction : name, function->jsExecutable()->sourceURL(), function->jsExecutable()->lineNo()); } diff --git a/JavaScriptCore/profiler/Profiler.h b/JavaScriptCore/profiler/Profiler.h index 21621bf..4b8b4a0 100644 --- a/JavaScriptCore/profiler/Profiler.h +++ b/JavaScriptCore/profiler/Profiler.h @@ -52,7 +52,7 @@ namespace JSC { } static Profiler* profiler(); - static CallIdentifier createCallIdentifier(JSGlobalData*, JSValue, const UString& sourceURL, int lineNumber); + static CallIdentifier createCallIdentifier(ExecState* exec, JSValue, const UString& sourceURL, int lineNumber); void startProfiling(ExecState*, const UString& title); PassRefPtr<Profile> stopProfiling(ExecState*, const UString& title); diff --git a/JavaScriptCore/runtime/ArgList.h b/JavaScriptCore/runtime/ArgList.h index 3227770..8e1fdbe 100644 --- a/JavaScriptCore/runtime/ArgList.h +++ b/JavaScriptCore/runtime/ArgList.h @@ -104,7 +104,11 @@ namespace JSC { void append(JSValue v) { ASSERT(!m_isReadOnly); - + +#if ENABLE(JSC_ZOMBIES) + ASSERT(!v.isZombie()); +#endif + if (m_isUsingInlineBuffer && m_size < inlineCapacity) { m_vector.uncheckedAppend(v); ++m_size; @@ -187,6 +191,10 @@ namespace JSC { : m_args(args) , m_argCount(argCount) { +#if ENABLE(JSC_ZOMBIES) + for (size_t i = 0; i < argCount; i++) + ASSERT(!m_args[i].isZombie()); +#endif } ArgList(Register* args, int argCount) diff --git a/JavaScriptCore/runtime/ArrayPrototype.cpp b/JavaScriptCore/runtime/ArrayPrototype.cpp index 7a89447..5b359e7 100644 --- a/JavaScriptCore/runtime/ArrayPrototype.cpp +++ b/JavaScriptCore/runtime/ArrayPrototype.cpp @@ -745,8 +745,8 @@ JSValue JSC_HOST_CALL arrayProtoFuncEvery(ExecState* exec, JSObject*, JSValue th cachedCall.setArgument(0, array->getIndex(k)); cachedCall.setArgument(1, jsNumber(exec, k)); cachedCall.setArgument(2, thisObj); - - if (!cachedCall.call().toBoolean(exec)) + JSValue result = cachedCall.call(); + if (!result.toBoolean(cachedCall.newCallFrame(exec))) return jsBoolean(false); } } @@ -846,8 +846,8 @@ JSValue JSC_HOST_CALL arrayProtoFuncSome(ExecState* exec, JSObject*, JSValue thi cachedCall.setArgument(0, array->getIndex(k)); cachedCall.setArgument(1, jsNumber(exec, k)); cachedCall.setArgument(2, thisObj); - - if (cachedCall.call().toBoolean(exec)) + JSValue result = cachedCall.call(); + if (result.toBoolean(cachedCall.newCallFrame(exec))) return jsBoolean(true); } } @@ -1034,7 +1034,7 @@ JSValue JSC_HOST_CALL arrayProtoFuncIndexOf(ExecState* exec, JSObject*, JSValue JSValue e = getProperty(exec, thisObj, index); if (!e) continue; - if (JSValue::strictEqual(searchElement, e)) + if (JSValue::strictEqual(exec, searchElement, e)) return jsNumber(exec, index); } @@ -1065,7 +1065,7 @@ JSValue JSC_HOST_CALL arrayProtoFuncLastIndexOf(ExecState* exec, JSObject*, JSVa JSValue e = getProperty(exec, thisObj, index); if (!e) continue; - if (JSValue::strictEqual(searchElement, e)) + if (JSValue::strictEqual(exec, searchElement, e)) return jsNumber(exec, index); } diff --git a/JavaScriptCore/runtime/BatchedTransitionOptimizer.h b/JavaScriptCore/runtime/BatchedTransitionOptimizer.h index 929a5e7..74089a5 100644 --- a/JavaScriptCore/runtime/BatchedTransitionOptimizer.h +++ b/JavaScriptCore/runtime/BatchedTransitionOptimizer.h @@ -43,7 +43,7 @@ namespace JSC { ~BatchedTransitionOptimizer() { - m_object->setStructure(Structure::fromDictionaryTransition(m_object->structure())); + m_object->flattenDictionaryObject(); } private: diff --git a/JavaScriptCore/runtime/Collector.cpp b/JavaScriptCore/runtime/Collector.cpp index 1b238d9..1630a58 100644 --- a/JavaScriptCore/runtime/Collector.cpp +++ b/JavaScriptCore/runtime/Collector.cpp @@ -32,6 +32,7 @@ #include "JSONObject.h" #include "JSString.h" #include "JSValue.h" +#include "JSZombie.h" #include "MarkStack.h" #include "Nodes.h" #include "Tracing.h" @@ -118,11 +119,7 @@ static RHeap* userChunk = 0; #if PLATFORM(DARWIN) typedef mach_port_t PlatformThread; #elif PLATFORM(WIN_OS) -struct PlatformThread { - PlatformThread(DWORD _id, HANDLE _handle) : id(_id), handle(_handle) {} - DWORD id; - HANDLE handle; -}; +typedef HANDLE PlatformThread; #endif class Heap::Thread { @@ -198,9 +195,11 @@ void Heap::destroy() sweep<PrimaryHeap>(); // No need to sweep number heap, because the JSNumber destructor doesn't do anything. - +#if ENABLE(JSC_ZOMBIES) + ASSERT(primaryHeap.numLiveObjects == primaryHeap.numZombies); +#else ASSERT(!primaryHeap.numLiveObjects); - +#endif freeBlocks(&primaryHeap); freeBlocks(&numberHeap); @@ -646,8 +645,7 @@ static inline PlatformThread getCurrentPlatformThread() #if PLATFORM(DARWIN) return pthread_mach_thread_np(pthread_self()); #elif PLATFORM(WIN_OS) - HANDLE threadHandle = pthread_getw32threadhandle_np(pthread_self()); - return PlatformThread(GetCurrentThreadId(), threadHandle); + return pthread_getw32threadhandle_np(pthread_self()); #endif } @@ -811,7 +809,7 @@ static inline void suspendThread(const PlatformThread& platformThread) #if PLATFORM(DARWIN) thread_suspend(platformThread); #elif PLATFORM(WIN_OS) - SuspendThread(platformThread.handle); + SuspendThread(platformThread); #else #error Need a way to suspend threads on this platform #endif @@ -822,7 +820,7 @@ static inline void resumeThread(const PlatformThread& platformThread) #if PLATFORM(DARWIN) thread_resume(platformThread); #elif PLATFORM(WIN_OS) - ResumeThread(platformThread.handle); + ResumeThread(platformThread); #else #error Need a way to resume threads on this platform #endif @@ -886,7 +884,7 @@ static size_t getPlatformThreadRegisters(const PlatformThread& platformThread, P #elif PLATFORM(WIN_OS) && PLATFORM(X86) regs.ContextFlags = CONTEXT_INTEGER | CONTEXT_CONTROL | CONTEXT_SEGMENTS; - GetThreadContext(platformThread.handle, ®s); + GetThreadContext(platformThread, ®s); return sizeof(CONTEXT); #else #error Need a way to get thread registers on this platform @@ -1041,17 +1039,26 @@ template <HeapType heapType> size_t Heap::sweep() // assumes the object has a valid vptr.) if (cell->u.freeCell.zeroIfFree == 0) continue; - +#if ENABLE(JSC_ZOMBIES) + if (!imp->isZombie()) { + const ClassInfo* info = imp->classInfo(); + imp->~JSCell(); + new (imp) JSZombie(info, JSZombie::leakedZombieStructure()); + heap.numZombies++; + } +#else imp->~JSCell(); +#endif } - - --usedCells; --numLiveObjects; +#if !ENABLE(JSC_ZOMBIES) + --usedCells; // put cell on the free list cell->u.freeCell.zeroIfFree = 0; cell->u.freeCell.next = freeList - (cell + 1); freeList = cell; +#endif } } } else { @@ -1064,8 +1071,18 @@ template <HeapType heapType> size_t Heap::sweep() if (!curBlock->marked.get(i >> HeapConstants<heapType>::bitmapShift)) { if (heapType != NumberHeap) { JSCell* imp = reinterpret_cast<JSCell*>(cell); +#if ENABLE(JSC_ZOMBIES) + if (!imp->isZombie()) { + const ClassInfo* info = imp->classInfo(); + imp->~JSCell(); + new (imp) JSZombie(info, JSZombie::leakedZombieStructure()); + heap.numZombies++; + } +#else imp->~JSCell(); +#endif } +#if !ENABLE(JSC_ZOMBIES) --usedCells; --numLiveObjects; @@ -1073,6 +1090,7 @@ template <HeapType heapType> size_t Heap::sweep() cell->u.freeCell.zeroIfFree = 0; cell->u.freeCell.next = freeList - (cell + 1); freeList = cell; +#endif } } } diff --git a/JavaScriptCore/runtime/Collector.h b/JavaScriptCore/runtime/Collector.h index 9ca9d18..9128701 100644 --- a/JavaScriptCore/runtime/Collector.h +++ b/JavaScriptCore/runtime/Collector.h @@ -60,6 +60,9 @@ namespace JSC { size_t numLiveObjects; size_t numLiveObjectsAtLastCollect; size_t extraCost; +#if ENABLE(JSC_ZOMBIES) + size_t numZombies; +#endif OperationInProgress operationInProgress; }; diff --git a/JavaScriptCore/runtime/DateConstructor.cpp b/JavaScriptCore/runtime/DateConstructor.cpp index 9908fef..61ec4c5 100644 --- a/JavaScriptCore/runtime/DateConstructor.cpp +++ b/JavaScriptCore/runtime/DateConstructor.cpp @@ -77,14 +77,14 @@ JSObject* constructDate(ExecState* exec, const ArgList& args) double value; if (numArgs == 0) // new Date() ECMA 15.9.3.3 - value = getCurrentUTCTime(); + value = jsCurrentTime(); else if (numArgs == 1) { if (args.at(0).inherits(&DateInstance::info)) value = asDateInstance(args.at(0))->internalNumber(); else { JSValue primitive = args.at(0).toPrimitive(exec); if (primitive.isString()) - value = parseDate(primitive.getString()); + value = parseDate(exec, primitive.getString(exec)); else value = primitive.toNumber(exec); } @@ -108,7 +108,7 @@ JSObject* constructDate(ExecState* exec, const ArgList& args) t.second = args.at(5).toInt32(exec); t.isDST = -1; double ms = (numArgs >= 7) ? args.at(6).toNumber(exec) : 0; - value = gregorianDateTimeToMS(t, ms, false); + value = gregorianDateTimeToMS(exec, t, ms, false); } } @@ -132,8 +132,8 @@ static JSValue JSC_HOST_CALL callDate(ExecState* exec, JSObject*, JSValue, const time_t localTime = time(0); tm localTM; getLocalTime(&localTime, &localTM); - GregorianDateTime ts(localTM); - return jsNontrivialString(exec, formatDate(ts) + " " + formatTime(ts, false)); + GregorianDateTime ts(exec, localTM); + return jsNontrivialString(exec, formatDate(ts) + " " + formatTime(ts)); } CallType DateConstructor::getCallData(CallData& callData) @@ -144,12 +144,12 @@ CallType DateConstructor::getCallData(CallData& callData) static JSValue JSC_HOST_CALL dateParse(ExecState* exec, JSObject*, JSValue, const ArgList& args) { - return jsNumber(exec, parseDate(args.at(0).toString(exec))); + return jsNumber(exec, parseDate(exec, args.at(0).toString(exec))); } static JSValue JSC_HOST_CALL dateNow(ExecState* exec, JSObject*, JSValue, const ArgList&) { - return jsNumber(exec, getCurrentUTCTime()); + return jsNumber(exec, jsCurrentTime()); } static JSValue JSC_HOST_CALL dateUTC(ExecState* exec, JSObject*, JSValue, const ArgList& args) @@ -173,7 +173,7 @@ static JSValue JSC_HOST_CALL dateUTC(ExecState* exec, JSObject*, JSValue, const t.minute = args.at(4).toInt32(exec); t.second = args.at(5).toInt32(exec); double ms = (n >= 7) ? args.at(6).toNumber(exec) : 0; - return jsNumber(exec, gregorianDateTimeToMS(t, ms, true)); + return jsNumber(exec, gregorianDateTimeToMS(exec, t, ms, true)); } } // namespace JSC diff --git a/JavaScriptCore/runtime/DateConversion.cpp b/JavaScriptCore/runtime/DateConversion.cpp index a725478..b9d0e02 100644 --- a/JavaScriptCore/runtime/DateConversion.cpp +++ b/JavaScriptCore/runtime/DateConversion.cpp @@ -43,6 +43,7 @@ #include "config.h" #include "DateConversion.h" +#include "CallFrame.h" #include "UString.h" #include <wtf/DateMath.h> #include <wtf/StringExtras.h> @@ -51,9 +52,14 @@ using namespace WTF; namespace JSC { -double parseDate(const UString &date) +double parseDate(ExecState* exec, const UString &date) { - return parseDateFromNullTerminatedCharacters(date.UTF8String().c_str()); + if (date == exec->globalData().cachedDateString) + return exec->globalData().cachedDateStringValue; + double value = parseDateFromNullTerminatedCharacters(exec, date.UTF8String().c_str()); + exec->globalData().cachedDateString = date; + exec->globalData().cachedDateStringValue = value; + return value; } UString formatDate(const GregorianDateTime &t) @@ -74,28 +80,31 @@ UString formatDateUTCVariant(const GregorianDateTime &t) return buffer; } -UString formatTime(const GregorianDateTime &t, bool utc) +UString formatTime(const GregorianDateTime &t) { char buffer[100]; - if (utc) { - snprintf(buffer, sizeof(buffer), "%02d:%02d:%02d GMT", t.hour, t.minute, t.second); - } else { - int offset = abs(gmtoffset(t)); - char timeZoneName[70]; - struct tm gtm = t; - strftime(timeZoneName, sizeof(timeZoneName), "%Z", >m); + int offset = abs(gmtoffset(t)); + char timeZoneName[70]; + struct tm gtm = t; + strftime(timeZoneName, sizeof(timeZoneName), "%Z", >m); - if (timeZoneName[0]) { - snprintf(buffer, sizeof(buffer), "%02d:%02d:%02d GMT%c%02d%02d (%s)", - t.hour, t.minute, t.second, - gmtoffset(t) < 0 ? '-' : '+', offset / (60*60), (offset / 60) % 60, timeZoneName); - } else { - snprintf(buffer, sizeof(buffer), "%02d:%02d:%02d GMT%c%02d%02d", - t.hour, t.minute, t.second, - gmtoffset(t) < 0 ? '-' : '+', offset / (60*60), (offset / 60) % 60); - } + if (timeZoneName[0]) { + snprintf(buffer, sizeof(buffer), "%02d:%02d:%02d GMT%c%02d%02d (%s)", + t.hour, t.minute, t.second, + gmtoffset(t) < 0 ? '-' : '+', offset / (60*60), (offset / 60) % 60, timeZoneName); + } else { + snprintf(buffer, sizeof(buffer), "%02d:%02d:%02d GMT%c%02d%02d", + t.hour, t.minute, t.second, + gmtoffset(t) < 0 ? '-' : '+', offset / (60*60), (offset / 60) % 60); } return UString(buffer); } +UString formatTimeUTC(const GregorianDateTime &t) +{ + char buffer[100]; + snprintf(buffer, sizeof(buffer), "%02d:%02d:%02d GMT", t.hour, t.minute, t.second); + return UString(buffer); +} + } // namespace JSC diff --git a/JavaScriptCore/runtime/DateConversion.h b/JavaScriptCore/runtime/DateConversion.h index 0d12815..dc980d3 100644 --- a/JavaScriptCore/runtime/DateConversion.h +++ b/JavaScriptCore/runtime/DateConversion.h @@ -42,18 +42,17 @@ #ifndef DateConversion_h #define DateConversion_h -namespace WTF { - struct GregorianDateTime; -} - namespace JSC { +class ExecState; class UString; +struct GregorianDateTime; -double parseDate(const UString&); -UString formatDate(const WTF::GregorianDateTime&); -UString formatDateUTCVariant(const WTF::GregorianDateTime&); -UString formatTime(const WTF::GregorianDateTime&, bool inputIsUTC); +double parseDate(ExecState* exec, const UString&); +UString formatDate(const GregorianDateTime&); +UString formatDateUTCVariant(const GregorianDateTime&); +UString formatTime(const GregorianDateTime&); +UString formatTimeUTC(const GregorianDateTime&); } // namespace JSC diff --git a/JavaScriptCore/runtime/DateInstance.cpp b/JavaScriptCore/runtime/DateInstance.cpp index d4c9ef7..77a92be 100644 --- a/JavaScriptCore/runtime/DateInstance.cpp +++ b/JavaScriptCore/runtime/DateInstance.cpp @@ -46,30 +46,36 @@ DateInstance::DateInstance(ExecState* exec, double time) setInternalValue(jsNumber(exec, timeClip(time))); } -bool DateInstance::getGregorianDateTime(ExecState* exec, bool outputIsUTC, GregorianDateTime& t) const +const GregorianDateTime* DateInstance::calculateGregorianDateTime(ExecState* exec) const { double milli = internalNumber(); if (isnan(milli)) - return false; + return 0; if (!m_data) m_data = exec->globalData().dateInstanceCache.add(milli); - if (outputIsUTC) { - if (m_data->m_gregorianDateTimeUTCCachedForMS != milli) { - WTF::msToGregorianDateTime(internalNumber(), true, m_data->m_cachedGregorianDateTimeUTC); - m_data->m_gregorianDateTimeUTCCachedForMS = milli; - } - t.copyFrom(m_data->m_cachedGregorianDateTimeUTC); - } else { - if (m_data->m_gregorianDateTimeCachedForMS != milli) { - WTF::msToGregorianDateTime(internalNumber(), false, m_data->m_cachedGregorianDateTime); - m_data->m_gregorianDateTimeCachedForMS = milli; - } - t.copyFrom(m_data->m_cachedGregorianDateTime); + if (m_data->m_gregorianDateTimeCachedForMS != milli) { + msToGregorianDateTime(exec, milli, false, m_data->m_cachedGregorianDateTime); + m_data->m_gregorianDateTimeCachedForMS = milli; } + return &m_data->m_cachedGregorianDateTime; +} + +const GregorianDateTime* DateInstance::calculateGregorianDateTimeUTC(ExecState* exec) const +{ + double milli = internalNumber(); + if (isnan(milli)) + return 0; - return true; + if (!m_data) + m_data = exec->globalData().dateInstanceCache.add(milli); + + if (m_data->m_gregorianDateTimeUTCCachedForMS != milli) { + msToGregorianDateTime(exec, milli, true, m_data->m_cachedGregorianDateTimeUTC); + m_data->m_gregorianDateTimeUTCCachedForMS = milli; + } + return &m_data->m_cachedGregorianDateTimeUTC; } } // namespace JSC diff --git a/JavaScriptCore/runtime/DateInstance.h b/JavaScriptCore/runtime/DateInstance.h index 38b321c..44b7521 100644 --- a/JavaScriptCore/runtime/DateInstance.h +++ b/JavaScriptCore/runtime/DateInstance.h @@ -38,7 +38,19 @@ namespace JSC { static JS_EXPORTDATA const ClassInfo info; - bool getGregorianDateTime(ExecState*, bool outputIsUTC, WTF::GregorianDateTime&) const; + const GregorianDateTime* gregorianDateTime(ExecState* exec) const + { + if (m_data && m_data->m_gregorianDateTimeCachedForMS == internalNumber()) + return &m_data->m_cachedGregorianDateTime; + return calculateGregorianDateTime(exec); + } + + const GregorianDateTime* gregorianDateTimeUTC(ExecState* exec) const + { + if (m_data && m_data->m_gregorianDateTimeUTCCachedForMS == internalNumber()) + return &m_data->m_cachedGregorianDateTimeUTC; + return calculateGregorianDateTimeUTC(exec); + } static PassRefPtr<Structure> createStructure(JSValue prototype) { @@ -49,6 +61,8 @@ namespace JSC { static const unsigned StructureFlags = OverridesMarkChildren | JSWrapperObject::StructureFlags; private: + const GregorianDateTime* calculateGregorianDateTime(ExecState*) const; + const GregorianDateTime* calculateGregorianDateTimeUTC(ExecState*) const; virtual const ClassInfo* classInfo() const { return &info; } mutable RefPtr<DateInstanceData> m_data; diff --git a/JavaScriptCore/runtime/DateInstanceCache.h b/JavaScriptCore/runtime/DateInstanceCache.h index b626c1d..d208580 100644 --- a/JavaScriptCore/runtime/DateInstanceCache.h +++ b/JavaScriptCore/runtime/DateInstanceCache.h @@ -40,9 +40,9 @@ namespace JSC { static PassRefPtr<DateInstanceData> create() { return adoptRef(new DateInstanceData); } double m_gregorianDateTimeCachedForMS; - WTF::GregorianDateTime m_cachedGregorianDateTime; + GregorianDateTime m_cachedGregorianDateTime; double m_gregorianDateTimeUTCCachedForMS; - WTF::GregorianDateTime m_cachedGregorianDateTimeUTC; + GregorianDateTime m_cachedGregorianDateTimeUTC; private: DateInstanceData() @@ -56,6 +56,11 @@ namespace JSC { public: DateInstanceCache() { + reset(); + } + + void reset() + { for (size_t i = 0; i < cacheSize; ++i) m_cache[i].key = NaN; } @@ -72,7 +77,7 @@ namespace JSC { } private: - static const size_t cacheSize = 64; + static const size_t cacheSize = 16; struct CacheEntry { double key; diff --git a/JavaScriptCore/runtime/DatePrototype.cpp b/JavaScriptCore/runtime/DatePrototype.cpp index 3f3e1f9..7a2e802 100644 --- a/JavaScriptCore/runtime/DatePrototype.cpp +++ b/JavaScriptCore/runtime/DatePrototype.cpp @@ -28,7 +28,6 @@ #include "JSString.h" #include "ObjectPrototype.h" #include "DateInstance.h" -#include <float.h> #if !PLATFORM(MAC) && HAVE(LANGINFO_H) #include <langinfo.h> @@ -253,11 +252,10 @@ static JSCell* formatLocaleDate(ExecState* exec, const GregorianDateTime& gdt, L static JSCell* formatLocaleDate(ExecState* exec, DateInstance* dateObject, double, LocaleDateTimeFormat format, const ArgList&) { - GregorianDateTime gregorianDateTime; - const bool outputIsUTC = false; - if (!dateObject->getGregorianDateTime(exec, outputIsUTC, gregorianDateTime)) + const GregorianDateTime* gregorianDateTime = dateObject->gregorianDateTime(exec); + if (!gregorianDateTime) return jsNontrivialString(exec, "Invalid Date"); - return formatLocaleDate(exec, gregorianDateTime, format); + return formatLocaleDate(exec, *gregorianDateTime, format); } #endif // !PLATFORM(MAC) @@ -420,14 +418,12 @@ JSValue JSC_HOST_CALL dateProtoFuncToString(ExecState* exec, JSObject*, JSValue if (!thisValue.inherits(&DateInstance::info)) return throwError(exec, TypeError); - const bool outputIsUTC = false; - DateInstance* thisDateObj = asDateInstance(thisValue); - GregorianDateTime t; - if (!thisDateObj->getGregorianDateTime(exec, outputIsUTC, t)) + const GregorianDateTime* gregorianDateTime = thisDateObj->gregorianDateTime(exec); + if (!gregorianDateTime) return jsNontrivialString(exec, "Invalid Date"); - return jsNontrivialString(exec, formatDate(t) + " " + formatTime(t, outputIsUTC)); + return jsNontrivialString(exec, formatDate(*gregorianDateTime) + " " + formatTime(*gregorianDateTime)); } JSValue JSC_HOST_CALL dateProtoFuncToUTCString(ExecState* exec, JSObject*, JSValue thisValue, const ArgList&) @@ -435,14 +431,12 @@ JSValue JSC_HOST_CALL dateProtoFuncToUTCString(ExecState* exec, JSObject*, JSVal if (!thisValue.inherits(&DateInstance::info)) return throwError(exec, TypeError); - const bool outputIsUTC = true; - DateInstance* thisDateObj = asDateInstance(thisValue); - GregorianDateTime t; - if (!thisDateObj->getGregorianDateTime(exec, outputIsUTC, t)) + const GregorianDateTime* gregorianDateTime = thisDateObj->gregorianDateTimeUTC(exec); + if (!gregorianDateTime) return jsNontrivialString(exec, "Invalid Date"); - return jsNontrivialString(exec, formatDateUTCVariant(t) + " " + formatTime(t, outputIsUTC)); + return jsNontrivialString(exec, formatDateUTCVariant(*gregorianDateTime) + " " + formatTimeUTC(*gregorianDateTime)); } JSValue JSC_HOST_CALL dateProtoFuncToISOString(ExecState* exec, JSObject*, JSValue thisValue, const ArgList&) @@ -450,17 +444,15 @@ JSValue JSC_HOST_CALL dateProtoFuncToISOString(ExecState* exec, JSObject*, JSVal if (!thisValue.inherits(&DateInstance::info)) return throwError(exec, TypeError); - const bool outputIsUTC = true; - DateInstance* thisDateObj = asDateInstance(thisValue); - GregorianDateTime t; - if (!thisDateObj->getGregorianDateTime(exec, outputIsUTC, t)) + const GregorianDateTime* gregorianDateTime = thisDateObj->gregorianDateTimeUTC(exec); + if (!gregorianDateTime) return jsNontrivialString(exec, "Invalid Date"); // Maximum amount of space we need in buffer: 6 (max. digits in year) + 2 * 5 (2 characters each for month, day, hour, minute, second) + 4 (. + 3 digits for milliseconds) // 6 for formatting and one for null termination = 27. We add one extra character to allow us to force null termination. char buffer[28]; - snprintf(buffer, sizeof(buffer) - 1, "%04d-%02d-%02dT%02d:%02d:%02d.%03dZ", 1900 + t.year, t.month + 1, t.monthDay, t.hour, t.minute, t.second, static_cast<int>(fmod(thisDateObj->internalNumber(), 1000))); + snprintf(buffer, sizeof(buffer) - 1, "%04d-%02d-%02dT%02d:%02d:%02d.%03dZ", 1900 + gregorianDateTime->year, gregorianDateTime->month + 1, gregorianDateTime->monthDay, gregorianDateTime->hour, gregorianDateTime->minute, gregorianDateTime->second, static_cast<int>(fmod(thisDateObj->internalNumber(), 1000))); buffer[sizeof(buffer) - 1] = 0; return jsNontrivialString(exec, buffer); } @@ -470,14 +462,12 @@ JSValue JSC_HOST_CALL dateProtoFuncToDateString(ExecState* exec, JSObject*, JSVa if (!thisValue.inherits(&DateInstance::info)) return throwError(exec, TypeError); - const bool outputIsUTC = false; - DateInstance* thisDateObj = asDateInstance(thisValue); - GregorianDateTime t; - if (!thisDateObj->getGregorianDateTime(exec, outputIsUTC, t)) + const GregorianDateTime* gregorianDateTime = thisDateObj->gregorianDateTime(exec); + if (!gregorianDateTime) return jsNontrivialString(exec, "Invalid Date"); - return jsNontrivialString(exec, formatDate(t)); + return jsNontrivialString(exec, formatDate(*gregorianDateTime)); } JSValue JSC_HOST_CALL dateProtoFuncToTimeString(ExecState* exec, JSObject*, JSValue thisValue, const ArgList&) @@ -485,14 +475,12 @@ JSValue JSC_HOST_CALL dateProtoFuncToTimeString(ExecState* exec, JSObject*, JSVa if (!thisValue.inherits(&DateInstance::info)) return throwError(exec, TypeError); - const bool outputIsUTC = false; - DateInstance* thisDateObj = asDateInstance(thisValue); - GregorianDateTime t; - if (!thisDateObj->getGregorianDateTime(exec, outputIsUTC, t)) + const GregorianDateTime* gregorianDateTime = thisDateObj->gregorianDateTime(exec); + if (!gregorianDateTime) return jsNontrivialString(exec, "Invalid Date"); - return jsNontrivialString(exec, formatTime(t, outputIsUTC)); + return jsNontrivialString(exec, formatTime(*gregorianDateTime)); } JSValue JSC_HOST_CALL dateProtoFuncToLocaleString(ExecState* exec, JSObject*, JSValue thisValue, const ArgList& args) @@ -535,14 +523,12 @@ JSValue JSC_HOST_CALL dateProtoFuncGetFullYear(ExecState* exec, JSObject*, JSVal if (!thisValue.inherits(&DateInstance::info)) return throwError(exec, TypeError); - const bool outputIsUTC = false; - DateInstance* thisDateObj = asDateInstance(thisValue); - GregorianDateTime t; - if (!thisDateObj->getGregorianDateTime(exec, outputIsUTC, t)) + const GregorianDateTime* gregorianDateTime = thisDateObj->gregorianDateTime(exec); + if (!gregorianDateTime) return jsNaN(exec); - return jsNumber(exec, 1900 + t.year); + return jsNumber(exec, 1900 + gregorianDateTime->year); } JSValue JSC_HOST_CALL dateProtoFuncGetUTCFullYear(ExecState* exec, JSObject*, JSValue thisValue, const ArgList&) @@ -550,14 +536,12 @@ JSValue JSC_HOST_CALL dateProtoFuncGetUTCFullYear(ExecState* exec, JSObject*, JS if (!thisValue.inherits(&DateInstance::info)) return throwError(exec, TypeError); - const bool outputIsUTC = true; - DateInstance* thisDateObj = asDateInstance(thisValue); - GregorianDateTime t; - if (!thisDateObj->getGregorianDateTime(exec, outputIsUTC, t)) + const GregorianDateTime* gregorianDateTime = thisDateObj->gregorianDateTimeUTC(exec); + if (!gregorianDateTime) return jsNaN(exec); - return jsNumber(exec, 1900 + t.year); + return jsNumber(exec, 1900 + gregorianDateTime->year); } JSValue JSC_HOST_CALL dateProtoFuncToGMTString(ExecState* exec, JSObject*, JSValue thisValue, const ArgList&) @@ -565,14 +549,12 @@ JSValue JSC_HOST_CALL dateProtoFuncToGMTString(ExecState* exec, JSObject*, JSVal if (!thisValue.inherits(&DateInstance::info)) return throwError(exec, TypeError); - const bool outputIsUTC = true; - DateInstance* thisDateObj = asDateInstance(thisValue); - GregorianDateTime t; - if (!thisDateObj->getGregorianDateTime(exec, outputIsUTC, t)) + const GregorianDateTime* gregorianDateTime = thisDateObj->gregorianDateTimeUTC(exec); + if (!gregorianDateTime) return jsNontrivialString(exec, "Invalid Date"); - return jsNontrivialString(exec, formatDateUTCVariant(t) + " " + formatTime(t, outputIsUTC)); + return jsNontrivialString(exec, formatDateUTCVariant(*gregorianDateTime) + " " + formatTimeUTC(*gregorianDateTime)); } JSValue JSC_HOST_CALL dateProtoFuncGetMonth(ExecState* exec, JSObject*, JSValue thisValue, const ArgList&) @@ -580,14 +562,12 @@ JSValue JSC_HOST_CALL dateProtoFuncGetMonth(ExecState* exec, JSObject*, JSValue if (!thisValue.inherits(&DateInstance::info)) return throwError(exec, TypeError); - const bool outputIsUTC = false; - DateInstance* thisDateObj = asDateInstance(thisValue); - GregorianDateTime t; - if (!thisDateObj->getGregorianDateTime(exec, outputIsUTC, t)) + const GregorianDateTime* gregorianDateTime = thisDateObj->gregorianDateTime(exec); + if (!gregorianDateTime) return jsNaN(exec); - return jsNumber(exec, t.month); + return jsNumber(exec, gregorianDateTime->month); } JSValue JSC_HOST_CALL dateProtoFuncGetUTCMonth(ExecState* exec, JSObject*, JSValue thisValue, const ArgList&) @@ -595,14 +575,12 @@ JSValue JSC_HOST_CALL dateProtoFuncGetUTCMonth(ExecState* exec, JSObject*, JSVal if (!thisValue.inherits(&DateInstance::info)) return throwError(exec, TypeError); - const bool outputIsUTC = true; - DateInstance* thisDateObj = asDateInstance(thisValue); - GregorianDateTime t; - if (!thisDateObj->getGregorianDateTime(exec, outputIsUTC, t)) + const GregorianDateTime* gregorianDateTime = thisDateObj->gregorianDateTimeUTC(exec); + if (!gregorianDateTime) return jsNaN(exec); - return jsNumber(exec, t.month); + return jsNumber(exec, gregorianDateTime->month); } JSValue JSC_HOST_CALL dateProtoFuncGetDate(ExecState* exec, JSObject*, JSValue thisValue, const ArgList&) @@ -610,14 +588,12 @@ JSValue JSC_HOST_CALL dateProtoFuncGetDate(ExecState* exec, JSObject*, JSValue t if (!thisValue.inherits(&DateInstance::info)) return throwError(exec, TypeError); - const bool outputIsUTC = false; - DateInstance* thisDateObj = asDateInstance(thisValue); - GregorianDateTime t; - if (!thisDateObj->getGregorianDateTime(exec, outputIsUTC, t)) + const GregorianDateTime* gregorianDateTime = thisDateObj->gregorianDateTime(exec); + if (!gregorianDateTime) return jsNaN(exec); - return jsNumber(exec, t.monthDay); + return jsNumber(exec, gregorianDateTime->monthDay); } JSValue JSC_HOST_CALL dateProtoFuncGetUTCDate(ExecState* exec, JSObject*, JSValue thisValue, const ArgList&) @@ -625,14 +601,12 @@ JSValue JSC_HOST_CALL dateProtoFuncGetUTCDate(ExecState* exec, JSObject*, JSValu if (!thisValue.inherits(&DateInstance::info)) return throwError(exec, TypeError); - const bool outputIsUTC = true; - DateInstance* thisDateObj = asDateInstance(thisValue); - GregorianDateTime t; - if (!thisDateObj->getGregorianDateTime(exec, outputIsUTC, t)) + const GregorianDateTime* gregorianDateTime = thisDateObj->gregorianDateTimeUTC(exec); + if (!gregorianDateTime) return jsNaN(exec); - return jsNumber(exec, t.monthDay); + return jsNumber(exec, gregorianDateTime->monthDay); } JSValue JSC_HOST_CALL dateProtoFuncGetDay(ExecState* exec, JSObject*, JSValue thisValue, const ArgList&) @@ -640,14 +614,12 @@ JSValue JSC_HOST_CALL dateProtoFuncGetDay(ExecState* exec, JSObject*, JSValue th if (!thisValue.inherits(&DateInstance::info)) return throwError(exec, TypeError); - const bool outputIsUTC = false; - DateInstance* thisDateObj = asDateInstance(thisValue); - GregorianDateTime t; - if (!thisDateObj->getGregorianDateTime(exec, outputIsUTC, t)) + const GregorianDateTime* gregorianDateTime = thisDateObj->gregorianDateTime(exec); + if (!gregorianDateTime) return jsNaN(exec); - return jsNumber(exec, t.weekDay); + return jsNumber(exec, gregorianDateTime->weekDay); } JSValue JSC_HOST_CALL dateProtoFuncGetUTCDay(ExecState* exec, JSObject*, JSValue thisValue, const ArgList&) @@ -655,14 +627,12 @@ JSValue JSC_HOST_CALL dateProtoFuncGetUTCDay(ExecState* exec, JSObject*, JSValue if (!thisValue.inherits(&DateInstance::info)) return throwError(exec, TypeError); - const bool outputIsUTC = true; - DateInstance* thisDateObj = asDateInstance(thisValue); - GregorianDateTime t; - if (!thisDateObj->getGregorianDateTime(exec, outputIsUTC, t)) + const GregorianDateTime* gregorianDateTime = thisDateObj->gregorianDateTimeUTC(exec); + if (!gregorianDateTime) return jsNaN(exec); - return jsNumber(exec, t.weekDay); + return jsNumber(exec, gregorianDateTime->weekDay); } JSValue JSC_HOST_CALL dateProtoFuncGetHours(ExecState* exec, JSObject*, JSValue thisValue, const ArgList&) @@ -670,14 +640,12 @@ JSValue JSC_HOST_CALL dateProtoFuncGetHours(ExecState* exec, JSObject*, JSValue if (!thisValue.inherits(&DateInstance::info)) return throwError(exec, TypeError); - const bool outputIsUTC = false; - DateInstance* thisDateObj = asDateInstance(thisValue); - GregorianDateTime t; - if (!thisDateObj->getGregorianDateTime(exec, outputIsUTC, t)) + const GregorianDateTime* gregorianDateTime = thisDateObj->gregorianDateTime(exec); + if (!gregorianDateTime) return jsNaN(exec); - return jsNumber(exec, t.hour); + return jsNumber(exec, gregorianDateTime->hour); } JSValue JSC_HOST_CALL dateProtoFuncGetUTCHours(ExecState* exec, JSObject*, JSValue thisValue, const ArgList&) @@ -685,14 +653,12 @@ JSValue JSC_HOST_CALL dateProtoFuncGetUTCHours(ExecState* exec, JSObject*, JSVal if (!thisValue.inherits(&DateInstance::info)) return throwError(exec, TypeError); - const bool outputIsUTC = true; - DateInstance* thisDateObj = asDateInstance(thisValue); - GregorianDateTime t; - if (!thisDateObj->getGregorianDateTime(exec, outputIsUTC, t)) + const GregorianDateTime* gregorianDateTime = thisDateObj->gregorianDateTimeUTC(exec); + if (!gregorianDateTime) return jsNaN(exec); - return jsNumber(exec, t.hour); + return jsNumber(exec, gregorianDateTime->hour); } JSValue JSC_HOST_CALL dateProtoFuncGetMinutes(ExecState* exec, JSObject*, JSValue thisValue, const ArgList&) @@ -700,14 +666,12 @@ JSValue JSC_HOST_CALL dateProtoFuncGetMinutes(ExecState* exec, JSObject*, JSValu if (!thisValue.inherits(&DateInstance::info)) return throwError(exec, TypeError); - const bool outputIsUTC = false; - DateInstance* thisDateObj = asDateInstance(thisValue); - GregorianDateTime t; - if (!thisDateObj->getGregorianDateTime(exec, outputIsUTC, t)) + const GregorianDateTime* gregorianDateTime = thisDateObj->gregorianDateTime(exec); + if (!gregorianDateTime) return jsNaN(exec); - return jsNumber(exec, t.minute); + return jsNumber(exec, gregorianDateTime->minute); } JSValue JSC_HOST_CALL dateProtoFuncGetUTCMinutes(ExecState* exec, JSObject*, JSValue thisValue, const ArgList&) @@ -715,14 +679,12 @@ JSValue JSC_HOST_CALL dateProtoFuncGetUTCMinutes(ExecState* exec, JSObject*, JSV if (!thisValue.inherits(&DateInstance::info)) return throwError(exec, TypeError); - const bool outputIsUTC = true; - DateInstance* thisDateObj = asDateInstance(thisValue); - GregorianDateTime t; - if (!thisDateObj->getGregorianDateTime(exec, outputIsUTC, t)) + const GregorianDateTime* gregorianDateTime = thisDateObj->gregorianDateTimeUTC(exec); + if (!gregorianDateTime) return jsNaN(exec); - return jsNumber(exec, t.minute); + return jsNumber(exec, gregorianDateTime->minute); } JSValue JSC_HOST_CALL dateProtoFuncGetSeconds(ExecState* exec, JSObject*, JSValue thisValue, const ArgList&) @@ -730,14 +692,12 @@ JSValue JSC_HOST_CALL dateProtoFuncGetSeconds(ExecState* exec, JSObject*, JSValu if (!thisValue.inherits(&DateInstance::info)) return throwError(exec, TypeError); - const bool outputIsUTC = false; - DateInstance* thisDateObj = asDateInstance(thisValue); - GregorianDateTime t; - if (!thisDateObj->getGregorianDateTime(exec, outputIsUTC, t)) + const GregorianDateTime* gregorianDateTime = thisDateObj->gregorianDateTime(exec); + if (!gregorianDateTime) return jsNaN(exec); - return jsNumber(exec, t.second); + return jsNumber(exec, gregorianDateTime->second); } JSValue JSC_HOST_CALL dateProtoFuncGetUTCSeconds(ExecState* exec, JSObject*, JSValue thisValue, const ArgList&) @@ -745,14 +705,12 @@ JSValue JSC_HOST_CALL dateProtoFuncGetUTCSeconds(ExecState* exec, JSObject*, JSV if (!thisValue.inherits(&DateInstance::info)) return throwError(exec, TypeError); - const bool outputIsUTC = true; - DateInstance* thisDateObj = asDateInstance(thisValue); - GregorianDateTime t; - if (!thisDateObj->getGregorianDateTime(exec, outputIsUTC, t)) + const GregorianDateTime* gregorianDateTime = thisDateObj->gregorianDateTimeUTC(exec); + if (!gregorianDateTime) return jsNaN(exec); - return jsNumber(exec, t.second); + return jsNumber(exec, gregorianDateTime->second); } JSValue JSC_HOST_CALL dateProtoFuncGetMilliSeconds(ExecState* exec, JSObject*, JSValue thisValue, const ArgList&) @@ -790,14 +748,12 @@ JSValue JSC_HOST_CALL dateProtoFuncGetTimezoneOffset(ExecState* exec, JSObject*, if (!thisValue.inherits(&DateInstance::info)) return throwError(exec, TypeError); - const bool outputIsUTC = false; - DateInstance* thisDateObj = asDateInstance(thisValue); - GregorianDateTime t; - if (!thisDateObj->getGregorianDateTime(exec, outputIsUTC, t)) + const GregorianDateTime* gregorianDateTime = thisDateObj->gregorianDateTime(exec); + if (!gregorianDateTime) return jsNaN(exec); - return jsNumber(exec, -gmtoffset(t) / minutesPerHour); + return jsNumber(exec, -gregorianDateTime->utcOffset / minutesPerHour); } JSValue JSC_HOST_CALL dateProtoFuncSetTime(ExecState* exec, JSObject*, JSValue thisValue, const ArgList& args) @@ -830,16 +786,21 @@ static JSValue setNewValueFromTimeArgs(ExecState* exec, JSValue thisValue, const double secs = floor(milli / msPerSecond); double ms = milli - secs * msPerSecond; - GregorianDateTime t; - thisDateObj->getGregorianDateTime(exec, inputIsUTC, t); + const GregorianDateTime* other = inputIsUTC + ? thisDateObj->gregorianDateTimeUTC(exec) + : thisDateObj->gregorianDateTime(exec); + if (!other) + return jsNaN(exec); - if (!fillStructuresUsingTimeArgs(exec, args, numArgsToUse, &ms, &t)) { + GregorianDateTime gregorianDateTime; + gregorianDateTime.copyFrom(*other); + if (!fillStructuresUsingTimeArgs(exec, args, numArgsToUse, &ms, &gregorianDateTime)) { JSValue result = jsNaN(exec); thisDateObj->setInternalValue(result); return result; } - JSValue result = jsNumber(exec, gregorianDateTimeToMS(t, ms, inputIsUTC)); + JSValue result = jsNumber(exec, gregorianDateTimeToMS(exec, gregorianDateTime, ms, inputIsUTC)); thisDateObj->setInternalValue(result); return result; } @@ -857,26 +818,28 @@ static JSValue setNewValueFromDateArgs(ExecState* exec, JSValue thisValue, const } double milli = thisDateObj->internalNumber(); - double ms = 0; - - GregorianDateTime t; - if (numArgsToUse == 3 && isnan(milli)) - // Based on ECMA 262 15.9.5.40 - .41 (set[UTC]FullYear) - // the time must be reset to +0 if it is NaN. - WTF::msToGregorianDateTime(0, true, t); - else { - double secs = floor(milli / msPerSecond); - ms = milli - secs * msPerSecond; - thisDateObj->getGregorianDateTime(exec, inputIsUTC, t); + double ms = 0; + + GregorianDateTime gregorianDateTime; + if (numArgsToUse == 3 && isnan(milli)) + msToGregorianDateTime(exec, 0, true, gregorianDateTime); + else { + ms = milli - floor(milli / msPerSecond) * msPerSecond; + const GregorianDateTime* other = inputIsUTC + ? thisDateObj->gregorianDateTimeUTC(exec) + : thisDateObj->gregorianDateTime(exec); + if (!other) + return jsNaN(exec); + gregorianDateTime.copyFrom(*other); } - if (!fillStructuresUsingDateArgs(exec, args, numArgsToUse, &ms, &t)) { + if (!fillStructuresUsingDateArgs(exec, args, numArgsToUse, &ms, &gregorianDateTime)) { JSValue result = jsNaN(exec); thisDateObj->setInternalValue(result); return result; } - JSValue result = jsNumber(exec, gregorianDateTimeToMS(t, ms, inputIsUTC)); + JSValue result = jsNumber(exec, gregorianDateTimeToMS(exec, gregorianDateTime, ms, inputIsUTC)); thisDateObj->setInternalValue(result); return result; } @@ -970,8 +933,6 @@ JSValue JSC_HOST_CALL dateProtoFuncSetYear(ExecState* exec, JSObject*, JSValue t if (!thisValue.inherits(&DateInstance::info)) return throwError(exec, TypeError); - const bool outputIsUTC = false; - DateInstance* thisDateObj = asDateInstance(thisValue); if (args.isEmpty()) { JSValue result = jsNaN(exec); @@ -982,15 +943,16 @@ JSValue JSC_HOST_CALL dateProtoFuncSetYear(ExecState* exec, JSObject*, JSValue t double milli = thisDateObj->internalNumber(); double ms = 0; - GregorianDateTime t; + GregorianDateTime gregorianDateTime; if (isnan(milli)) // Based on ECMA 262 B.2.5 (setYear) // the time must be reset to +0 if it is NaN. - WTF::msToGregorianDateTime(0, true, t); + msToGregorianDateTime(exec, 0, true, gregorianDateTime); else { double secs = floor(milli / msPerSecond); ms = milli - secs * msPerSecond; - thisDateObj->getGregorianDateTime(exec, outputIsUTC, t); + if (const GregorianDateTime* other = thisDateObj->gregorianDateTime(exec)) + gregorianDateTime.copyFrom(*other); } bool ok = true; @@ -1001,8 +963,8 @@ JSValue JSC_HOST_CALL dateProtoFuncSetYear(ExecState* exec, JSObject*, JSValue t return result; } - t.year = (year > 99 || year < 0) ? year - 1900 : year; - JSValue result = jsNumber(exec, gregorianDateTimeToMS(t, ms, outputIsUTC)); + gregorianDateTime.year = (year > 99 || year < 0) ? year - 1900 : year; + JSValue result = jsNumber(exec, gregorianDateTimeToMS(exec, gregorianDateTime, ms, false)); thisDateObj->setInternalValue(result); return result; } @@ -1012,16 +974,14 @@ JSValue JSC_HOST_CALL dateProtoFuncGetYear(ExecState* exec, JSObject*, JSValue t if (!thisValue.inherits(&DateInstance::info)) return throwError(exec, TypeError); - const bool outputIsUTC = false; - DateInstance* thisDateObj = asDateInstance(thisValue); - GregorianDateTime t; - if (!thisDateObj->getGregorianDateTime(exec, outputIsUTC, t)) + const GregorianDateTime* gregorianDateTime = thisDateObj->gregorianDateTime(exec); + if (!gregorianDateTime) return jsNaN(exec); // NOTE: IE returns the full year even in getYear. - return jsNumber(exec, t.year); + return jsNumber(exec, gregorianDateTime->year); } JSValue JSC_HOST_CALL dateProtoFuncToJSON(ExecState* exec, JSObject*, JSValue thisValue, const ArgList&) diff --git a/JavaScriptCore/runtime/FunctionPrototype.cpp b/JavaScriptCore/runtime/FunctionPrototype.cpp index 45f17b1..a3a7479 100644 --- a/JavaScriptCore/runtime/FunctionPrototype.cpp +++ b/JavaScriptCore/runtime/FunctionPrototype.cpp @@ -90,13 +90,13 @@ JSValue JSC_HOST_CALL functionProtoFuncToString(ExecState* exec, JSObject*, JSVa FunctionExecutable* executable = function->jsExecutable(); UString sourceString = executable->source().toString(); insertSemicolonIfNeeded(sourceString); - return jsString(exec, "function " + function->name(&exec->globalData()) + "(" + executable->paramString() + ") " + sourceString); + return jsString(exec, "function " + function->name(exec) + "(" + executable->paramString() + ") " + sourceString); } } if (thisValue.inherits(&InternalFunction::info)) { InternalFunction* function = asInternalFunction(thisValue); - return jsString(exec, "function " + function->name(&exec->globalData()) + "() {\n [native code]\n}"); + return jsString(exec, "function " + function->name(exec) + "() {\n [native code]\n}"); } return throwError(exec, TypeError); diff --git a/JavaScriptCore/runtime/InitializeThreading.cpp b/JavaScriptCore/runtime/InitializeThreading.cpp index fea89f8..aad1af8 100644 --- a/JavaScriptCore/runtime/InitializeThreading.cpp +++ b/JavaScriptCore/runtime/InitializeThreading.cpp @@ -51,7 +51,7 @@ static void initializeThreadingOnce() initializeUString(); #if ENABLE(JSC_MULTIPLE_THREADS) s_dtoaP5Mutex = new Mutex; - WTF::initializeDates(); + initializeDates(); #endif } diff --git a/JavaScriptCore/runtime/InternalFunction.cpp b/JavaScriptCore/runtime/InternalFunction.cpp index 2ba2984..c48d628 100644 --- a/JavaScriptCore/runtime/InternalFunction.cpp +++ b/JavaScriptCore/runtime/InternalFunction.cpp @@ -43,29 +43,29 @@ InternalFunction::InternalFunction(JSGlobalData* globalData, NonNullPassRefPtr<S putDirect(globalData->propertyNames->name, jsString(globalData, name.ustring()), DontDelete | ReadOnly | DontEnum); } -const UString& InternalFunction::name(JSGlobalData* globalData) +const UString& InternalFunction::name(ExecState* exec) { - return asString(getDirect(globalData->propertyNames->name))->value(); + return asString(getDirect(exec->globalData().propertyNames->name))->value(exec); } -const UString InternalFunction::displayName(JSGlobalData* globalData) +const UString InternalFunction::displayName(ExecState* exec) { - JSValue displayName = getDirect(globalData->propertyNames->displayName); + JSValue displayName = getDirect(exec->globalData().propertyNames->displayName); - if (displayName && isJSString(globalData, displayName)) - return asString(displayName)->value(); + if (displayName && isJSString(&exec->globalData(), displayName)) + return asString(displayName)->value(exec); return UString::null(); } -const UString InternalFunction::calculatedDisplayName(JSGlobalData* globalData) +const UString InternalFunction::calculatedDisplayName(ExecState* exec) { - const UString explicitName = displayName(globalData); + const UString explicitName = displayName(exec); if (!explicitName.isEmpty()) return explicitName; - return name(globalData); + return name(exec); } } // namespace JSC diff --git a/JavaScriptCore/runtime/InternalFunction.h b/JavaScriptCore/runtime/InternalFunction.h index de9a1d6..fa1e5aa 100644 --- a/JavaScriptCore/runtime/InternalFunction.h +++ b/JavaScriptCore/runtime/InternalFunction.h @@ -36,9 +36,9 @@ namespace JSC { virtual const ClassInfo* classInfo() const; static JS_EXPORTDATA const ClassInfo info; - const UString& name(JSGlobalData*); - const UString displayName(JSGlobalData*); - const UString calculatedDisplayName(JSGlobalData*); + const UString& name(ExecState*); + const UString displayName(ExecState*); + const UString calculatedDisplayName(ExecState*); static PassRefPtr<Structure> createStructure(JSValue proto) { diff --git a/JavaScriptCore/runtime/JSArray.cpp b/JavaScriptCore/runtime/JSArray.cpp index fd9e7b2..b16d3fa 100644 --- a/JavaScriptCore/runtime/JSArray.cpp +++ b/JavaScriptCore/runtime/JSArray.cpp @@ -785,7 +785,7 @@ struct AVLTreeAbstractorForArrayCompare { m_cachedCall->setThis(m_globalThisValue); m_cachedCall->setArgument(0, va); m_cachedCall->setArgument(1, vb); - compareResult = m_cachedCall->call().toNumber(m_cachedCall->newCallFrame()); + compareResult = m_cachedCall->call().toNumber(m_cachedCall->newCallFrame(m_exec)); } else { MarkedArgumentBuffer arguments; arguments.append(va); diff --git a/JavaScriptCore/runtime/JSCell.cpp b/JavaScriptCore/runtime/JSCell.cpp index fae056e..17410e2 100644 --- a/JavaScriptCore/runtime/JSCell.cpp +++ b/JavaScriptCore/runtime/JSCell.cpp @@ -86,17 +86,17 @@ bool JSCell::getUInt32(uint32_t&) const return false; } -bool JSCell::getString(UString&stringValue) const +bool JSCell::getString(ExecState* exec, UString&stringValue) const { if (!isString()) return false; - stringValue = static_cast<const JSString*>(this)->value(); + stringValue = static_cast<const JSString*>(this)->value(exec); return true; } -UString JSCell::getString() const +UString JSCell::getString(ExecState* exec) const { - return isString() ? static_cast<const JSString*>(this)->value() : UString(); + return isString() ? static_cast<const JSString*>(this)->value(exec) : UString(); } JSObject* JSCell::getObject() diff --git a/JavaScriptCore/runtime/JSCell.h b/JavaScriptCore/runtime/JSCell.h index 722ae33..c8ba2b8 100644 --- a/JavaScriptCore/runtime/JSCell.h +++ b/JavaScriptCore/runtime/JSCell.h @@ -42,6 +42,7 @@ namespace JSC { friend class JSString; friend class JSValue; friend class JSAPIValueWrapper; + friend class JSZombie; friend struct VPtrSet; private: @@ -64,8 +65,8 @@ namespace JSC { Structure* structure() const; // Extracting the value. - bool getString(UString&) const; - UString getString() const; // null string if not a string + bool getString(ExecState* exec, UString&) const; + UString getString(ExecState* exec) const; // null string if not a string JSObject* getObject(); // NULL if not an object const JSObject* getObject() const; // NULL if not an object @@ -90,6 +91,9 @@ namespace JSC { void* operator new(size_t, void* placementNewDestination) { return placementNewDestination; } virtual void markChildren(MarkStack&); +#if ENABLE(JSC_ZOMBIES) + virtual bool isZombie() const { return false; } +#endif // Object operations, with the toObject operation included. virtual const ClassInfo* classInfo() const; @@ -175,14 +179,14 @@ namespace JSC { return isCell() && asCell()->isObject(); } - inline bool JSValue::getString(UString& s) const + inline bool JSValue::getString(ExecState* exec, UString& s) const { - return isCell() && asCell()->getString(s); + return isCell() && asCell()->getString(exec, s); } - inline UString JSValue::getString() const + inline UString JSValue::getString(ExecState* exec) const { - return isCell() ? asCell()->getString() : UString(); + return isCell() ? asCell()->getString(exec) : UString(); } inline JSObject* JSValue::getObject() const @@ -342,7 +346,13 @@ namespace JSC { { return cellBlock(c)->heap; } - + +#if ENABLE(JSC_ZOMBIES) + inline bool JSValue::isZombie() const + { + return isCell() && asCell() && asCell()->isZombie(); + } +#endif } // namespace JSC #endif // JSCell_h diff --git a/JavaScriptCore/runtime/JSGlobalData.cpp b/JavaScriptCore/runtime/JSGlobalData.cpp index 1221ef2..234449f 100644 --- a/JavaScriptCore/runtime/JSGlobalData.cpp +++ b/JavaScriptCore/runtime/JSGlobalData.cpp @@ -82,26 +82,28 @@ struct VPtrSet { VPtrSet::VPtrSet() { - // Bizarrely, calling fastMalloc here is faster than allocating space on the stack. - void* storage = fastMalloc(sizeof(CollectorBlock)); + CollectorCell cell; + void* storage = &cell; + COMPILE_ASSERT(sizeof(JSArray) <= sizeof(CollectorCell), sizeof_JSArray_must_be_less_than_CollectorCell); JSCell* jsArray = new (storage) JSArray(JSArray::createStructure(jsNull())); jsArrayVPtr = jsArray->vptr(); jsArray->~JSCell(); + COMPILE_ASSERT(sizeof(JSByteArray) <= sizeof(CollectorCell), sizeof_JSByteArray_must_be_less_than_CollectorCell); JSCell* jsByteArray = new (storage) JSByteArray(JSByteArray::VPtrStealingHack); jsByteArrayVPtr = jsByteArray->vptr(); jsByteArray->~JSCell(); + COMPILE_ASSERT(sizeof(JSString) <= sizeof(CollectorCell), sizeof_JSString_must_be_less_than_CollectorCell); JSCell* jsString = new (storage) JSString(JSString::VPtrStealingHack); jsStringVPtr = jsString->vptr(); jsString->~JSCell(); + COMPILE_ASSERT(sizeof(JSFunction) <= sizeof(CollectorCell), sizeof_JSFunction_must_be_less_than_CollectorCell); JSCell* jsFunction = new (storage) JSFunction(JSFunction::createStructure(jsNull())); jsFunctionVPtr = jsFunction->vptr(); jsFunction->~JSCell(); - - fastFree(storage); } JSGlobalData::JSGlobalData(bool isShared, const VPtrSet& vptrSet) @@ -147,6 +149,8 @@ JSGlobalData::JSGlobalData(bool isShared, const VPtrSet& vptrSet) , functionCodeBlockBeingReparsed(0) , firstStringifierToMark(0) , markStack(vptrSet.jsArrayVPtr) + , cachedUTCOffset(NaN) + , weakRandom(static_cast<int>(currentTime())) #ifndef NDEBUG , mainThreadOnly(false) #endif @@ -251,6 +255,14 @@ JSGlobalData::ClientData::~ClientData() { } +void JSGlobalData::resetDateCache() +{ + cachedUTCOffset = NaN; + dstOffsetCache.reset(); + cachedDateString = UString(); + dateInstanceCache.reset(); +} + void JSGlobalData::startSampling() { interpreter->startSampling(); diff --git a/JavaScriptCore/runtime/JSGlobalData.h b/JavaScriptCore/runtime/JSGlobalData.h index d2aa2da..f0c1b5c 100644 --- a/JavaScriptCore/runtime/JSGlobalData.h +++ b/JavaScriptCore/runtime/JSGlobalData.h @@ -38,6 +38,7 @@ #include "NumericStrings.h" #include "SmallStrings.h" #include "TimeoutChecker.h" +#include "WeakRandom.h" #include <wtf/Forward.h> #include <wtf/HashMap.h> #include <wtf/RefCounted.h> @@ -63,6 +64,26 @@ namespace JSC { struct Instruction; struct VPtrSet; + struct DSTOffsetCache { + DSTOffsetCache() + { + reset(); + } + + void reset() + { + offset = 0.0; + start = 0.0; + end = -1.0; + increment = 0.0; + } + + double offset; + double start; + double end; + double increment; + }; + class JSGlobalData : public RefCounted<JSGlobalData> { public: struct ClientData { @@ -153,10 +174,20 @@ namespace JSC { MarkStack markStack; + double cachedUTCOffset; + DSTOffsetCache dstOffsetCache; + + UString cachedDateString; + double cachedDateStringValue; + + WeakRandom weakRandom; + #ifndef NDEBUG bool mainThreadOnly; #endif + void resetDateCache(); + void startSampling(); void stopSampling(); void dumpSampleData(ExecState* exec); @@ -165,7 +196,7 @@ namespace JSC { static JSGlobalData*& sharedInstanceInternal(); void createNativeThunk(); }; - + } // namespace JSC #endif // JSGlobalData_h diff --git a/JavaScriptCore/runtime/JSGlobalObject.h b/JavaScriptCore/runtime/JSGlobalObject.h index 720d3a5..9e4ef49 100644 --- a/JavaScriptCore/runtime/JSGlobalObject.h +++ b/JavaScriptCore/runtime/JSGlobalObject.h @@ -442,7 +442,13 @@ namespace JSC { : m_dynamicGlobalObjectSlot(callFrame->globalData().dynamicGlobalObject) , m_savedDynamicGlobalObject(m_dynamicGlobalObjectSlot) { - m_dynamicGlobalObjectSlot = dynamicGlobalObject; + if (!m_dynamicGlobalObjectSlot) { + m_dynamicGlobalObjectSlot = dynamicGlobalObject; + + // Reset the date cache between JS invocations to force the VM + // to observe time zone changes. + callFrame->globalData().resetDateCache(); + } } ~DynamicGlobalObjectScope() diff --git a/JavaScriptCore/runtime/JSNumberCell.h b/JavaScriptCore/runtime/JSNumberCell.h index 309488f..e9e2470 100644 --- a/JavaScriptCore/runtime/JSNumberCell.h +++ b/JavaScriptCore/runtime/JSNumberCell.h @@ -109,6 +109,11 @@ namespace JSC { return static_cast<JSNumberCell*>(v.asCell()); } + ALWAYS_INLINE JSValue::JSValue(EncodeAsDoubleTag, ExecState* exec, double d) + { + *this = jsNumberCell(exec, d); + } + inline JSValue::JSValue(ExecState* exec, double d) { JSValue v = JSImmediate::from(d); @@ -193,6 +198,11 @@ namespace JSC { #endif // USE(JSVALUE32) #if USE(JSVALUE64) + ALWAYS_INLINE JSValue::JSValue(EncodeAsDoubleTag, ExecState*, double d) + { + *this = JSImmediate::fromNumberOutsideIntegerRange(d); + } + inline JSValue::JSValue(ExecState*, double d) { JSValue v = JSImmediate::from(d); diff --git a/JavaScriptCore/runtime/JSONObject.cpp b/JavaScriptCore/runtime/JSONObject.cpp index 297d457..cc7f6d9 100644 --- a/JavaScriptCore/runtime/JSONObject.cpp +++ b/JavaScriptCore/runtime/JSONObject.cpp @@ -70,7 +70,23 @@ public: void markAggregate(MarkStack&); private: - typedef UString StringBuilder; + class StringBuilder : public Vector<UChar> { + public: + using Vector<UChar>::append; + + inline void append(const char* str) + { + size_t len = strlen(str); + reserveCapacity(size() + len); + for (size_t i = 0; i < len; i++) + Vector<UChar>::append(str[i]); + } + + inline void append(const UString& str) + { + append(str.data(), str.size()); + } + }; class Holder { public: @@ -156,7 +172,7 @@ static inline UString gap(ExecState* exec, JSValue space) } // If the space value is a string, use it as the gap string, otherwise use no gap string. - UString spaces = space.getString(); + UString spaces = space.getString(exec); if (spaces.size() > maxGapLength) { spaces = spaces.substr(0, maxGapLength); } @@ -213,7 +229,7 @@ Stringifier::Stringifier(ExecState* exec, JSValue replacer, JSValue space) break; UString propertyName; - if (name.getString(propertyName)) { + if (name.getString(exec, propertyName)) { m_arrayReplacerPropertyNames.add(Identifier(exec, propertyName)); continue; } @@ -269,7 +285,9 @@ JSValue Stringifier::stringify(JSValue value) if (m_exec->hadException()) return jsNull(); - return jsString(m_exec, result); + result.shrinkToFit(); + size_t length = result.size(); + return jsString(m_exec, UString(result.releaseBuffer(), length, false)); } void Stringifier::appendQuotedString(StringBuilder& builder, const UString& value) @@ -389,7 +407,7 @@ Stringifier::StringifyResult Stringifier::appendStringifiedValue(StringBuilder& } UString stringValue; - if (value.getString(stringValue)) { + if (value.getString(m_exec, stringValue)) { appendQuotedString(builder, stringValue); return StringifySucceeded; } @@ -586,7 +604,7 @@ bool Stringifier::Holder::appendNextProperty(Stringifier& stringifier, StringBui // This only occurs when get an undefined value for an object property. // In this case we don't want the separator and property name that we // already appended, so roll back. - builder = builder.substr(0, rollBackPoint); + builder.resize(rollBackPoint); break; } diff --git a/JavaScriptCore/runtime/JSObject.cpp b/JavaScriptCore/runtime/JSObject.cpp index 6932ded..ed9fdc2 100644 --- a/JavaScriptCore/runtime/JSObject.cpp +++ b/JavaScriptCore/runtime/JSObject.cpp @@ -406,26 +406,10 @@ bool JSObject::hasInstance(ExecState* exec, JSValue value, JSValue proto) bool JSObject::propertyIsEnumerable(ExecState* exec, const Identifier& propertyName) const { - unsigned attributes; - if (!getPropertyAttributes(exec, propertyName, attributes)) + PropertyDescriptor descriptor; + if (!const_cast<JSObject*>(this)->getOwnPropertyDescriptor(exec, propertyName, descriptor)) return false; - return !(attributes & DontEnum); -} - -bool JSObject::getPropertyAttributes(ExecState* exec, const Identifier& propertyName, unsigned& attributes) const -{ - JSCell* specificValue; - if (m_structure->get(propertyName, attributes, specificValue) != WTF::notFound) - return true; - - // Look in the static hashtable of properties - const HashEntry* entry = findPropertyHashEntry(exec, propertyName); - if (entry) { - attributes = entry->attributes(); - return true; - } - - return false; + return descriptor.enumerable(); } bool JSObject::getPropertySpecificValue(ExecState*, const Identifier& propertyName, JSCell*& specificValue) const @@ -522,12 +506,12 @@ void JSObject::removeDirect(const Identifier& propertyName) void JSObject::putDirectFunction(ExecState* exec, InternalFunction* function, unsigned attr) { - putDirectFunction(Identifier(exec, function->name(&exec->globalData())), function, attr); + putDirectFunction(Identifier(exec, function->name(exec)), function, attr); } void JSObject::putDirectFunctionWithoutTransition(ExecState* exec, InternalFunction* function, unsigned attr) { - putDirectFunctionWithoutTransition(Identifier(exec, function->name(&exec->globalData())), function, attr); + putDirectFunctionWithoutTransition(Identifier(exec, function->name(exec)), function, attr); } NEVER_INLINE void JSObject::fillGetterPropertySlot(PropertySlot& slot, JSValue* location) @@ -599,7 +583,7 @@ bool JSObject::defineOwnProperty(ExecState* exec, const Identifier& propertyName if (descriptor.isEmpty()) return true; - if (current.equalTo(descriptor)) + if (current.equalTo(exec, descriptor)) return true; // Filter out invalid changes @@ -645,7 +629,7 @@ bool JSObject::defineOwnProperty(ExecState* exec, const Identifier& propertyName return false; } if (!current.writable()) { - if (descriptor.value() || !JSValue::strictEqual(current.value(), descriptor.value())) { + if (descriptor.value() || !JSValue::strictEqual(exec, current.value(), descriptor.value())) { if (throwException) throwError(exec, TypeError, "Attempting to change value of a readonly property."); return false; @@ -667,12 +651,12 @@ bool JSObject::defineOwnProperty(ExecState* exec, const Identifier& propertyName // Changing the accessor functions of an existing accessor property ASSERT(descriptor.isAccessorDescriptor()); if (!current.configurable()) { - if (descriptor.setterPresent() && !(current.setter() && JSValue::strictEqual(current.setter(), descriptor.setter()))) { + if (descriptor.setterPresent() && !(current.setter() && JSValue::strictEqual(exec, current.setter(), descriptor.setter()))) { if (throwException) throwError(exec, TypeError, "Attempting to change the setter of an unconfigurable property."); return false; } - if (descriptor.getterPresent() && !(current.getter() && JSValue::strictEqual(current.getter(), descriptor.getter()))) { + if (descriptor.getterPresent() && !(current.getter() && JSValue::strictEqual(exec, current.getter(), descriptor.getter()))) { if (throwException) throwError(exec, TypeError, "Attempting to change the getter of an unconfigurable property."); return false; diff --git a/JavaScriptCore/runtime/JSObject.h b/JavaScriptCore/runtime/JSObject.h index 5a89c40..a5da267 100644 --- a/JavaScriptCore/runtime/JSObject.h +++ b/JavaScriptCore/runtime/JSObject.h @@ -135,7 +135,6 @@ namespace JSC { virtual JSObject* toThisObject(ExecState*) const; virtual JSObject* unwrappedObject(); - virtual bool getPropertyAttributes(ExecState*, const Identifier& propertyName, unsigned& attributes) const; bool getPropertySpecificValue(ExecState* exec, const Identifier& propertyName, JSCell*& specificFunction) const; // This get function only looks at the property map. @@ -210,6 +209,11 @@ namespace JSC { return Structure::create(prototype, TypeInfo(ObjectType, StructureFlags)); } + void flattenDictionaryObject() + { + m_structure->flattenDictionaryStructure(this); + } + protected: static const unsigned StructureFlags = 0; @@ -229,7 +233,7 @@ namespace JSC { using JSCell::isGetterSetter; using JSCell::toObject; void getObject(); - void getString(); + void getString(ExecState* exec); void isObject(); void isString(); #if USE(JSVALUE32) diff --git a/JavaScriptCore/runtime/JSPropertyNameIterator.cpp b/JavaScriptCore/runtime/JSPropertyNameIterator.cpp index 6fd0344..d3dcb83 100644 --- a/JavaScriptCore/runtime/JSPropertyNameIterator.cpp +++ b/JavaScriptCore/runtime/JSPropertyNameIterator.cpp @@ -45,7 +45,8 @@ JSPropertyNameIterator* JSPropertyNameIterator::create(ExecState* exec, JSObject o->getPropertyNames(exec, propertyNames); size_t numCacheableSlots = 0; if (!o->structure()->hasNonEnumerableProperties() && !o->structure()->hasAnonymousSlots() && - !o->structure()->isUncacheableDictionary() && !o->structure()->typeInfo().overridesGetPropertyNames()) + !o->structure()->hasGetterSetterProperties() && !o->structure()->isUncacheableDictionary() && + !o->structure()->typeInfo().overridesGetPropertyNames()) numCacheableSlots = o->structure()->propertyStorageSize(); JSPropertyNameIterator* jsPropertyNameIterator = new (exec) JSPropertyNameIterator(exec, propertyNames.data(), numCacheableSlots); @@ -76,7 +77,7 @@ JSValue JSPropertyNameIterator::get(ExecState* exec, JSObject* base, size_t i) if (m_cachedStructure == base->structure() && m_cachedPrototypeChain == base->structure()->prototypeChain(exec)) return identifier; - if (!base->hasProperty(exec, Identifier(exec, asString(identifier)->value()))) + if (!base->hasProperty(exec, Identifier(exec, asString(identifier)->value(exec)))) return JSValue(); return identifier; } diff --git a/JavaScriptCore/runtime/JSString.cpp b/JavaScriptCore/runtime/JSString.cpp index 20ba868..28ae6f7 100644 --- a/JavaScriptCore/runtime/JSString.cpp +++ b/JavaScriptCore/runtime/JSString.cpp @@ -25,41 +25,160 @@ #include "JSGlobalObject.h" #include "JSObject.h" +#include "Operations.h" #include "StringObject.h" #include "StringPrototype.h" namespace JSC { +void JSString::Rope::destructNonRecursive() +{ + Vector<Rope*, 32> workQueue; + Rope* rope = this; + + while (true) { + unsigned length = rope->ropeLength(); + for (unsigned i = 0; i < length; ++i) { + Fiber& fiber = rope->fibers(i); + if (fiber.isString()) + fiber.string()->deref(); + else { + Rope* nextRope = fiber.rope(); + if (nextRope->hasOneRef()) + workQueue.append(nextRope); + else + nextRope->deref(); + } + } + if (rope != this) + fastFree(rope); + + if (workQueue.isEmpty()) + return; + + rope = workQueue.last(); + workQueue.removeLast(); + } +} + +JSString::Rope::~Rope() +{ + destructNonRecursive(); +} + +#define ROPE_COPY_CHARS_INLINE_CUTOFF 20 + +static inline void copyChars(UChar* destination, const UChar* source, unsigned numCharacters) +{ +#ifdef ROPE_COPY_CHARS_INLINE_CUTOFF + if (numCharacters <= ROPE_COPY_CHARS_INLINE_CUTOFF) { + for (unsigned i = 0; i < numCharacters; ++i) + destination[i] = source[i]; + return; + } +#endif + memcpy(destination, source, numCharacters * sizeof(UChar)); +} + +// Overview: this methods converts a JSString from holding a string in rope form +// down to a simple UString representation. It does so by building up the string +// backwards, since we want to avoid recursion, we expect that the tree structure +// representing the rope is likely imbalanced with more nodes down the left side +// (since appending to the string is likely more common) - and as such resolving +// in this fashion should minimize work queue size. (If we built the queue forwards +// we would likely have to place all of the constituent UString::Reps into the +// Vector before performing any concatenation, but by working backwards we likely +// only fill the queue with the number of substrings at any given level in a +// rope-of-ropes.) +void JSString::resolveRope(ExecState* exec) const +{ + ASSERT(isRope()); + + // Allocate the buffer to hold the final string, position initially points to the end. + UChar* buffer; + if (!tryFastMalloc(m_stringLength * sizeof(UChar)).getValue(buffer)) { + for (unsigned i = 0; i < m_ropeLength; ++i) + m_fibers[i].deref(); + m_ropeLength = 0; + ASSERT(!isRope()); + ASSERT(m_value == UString()); + + throwOutOfMemoryError(exec); + return; + } + UChar* position = buffer + m_stringLength; + + // Start with the current Rope. + Vector<Rope::Fiber, 32> workQueue; + Rope::Fiber currentFiber; + for (unsigned i = 0; i < (m_ropeLength - 1); ++i) + workQueue.append(m_fibers[i]); + currentFiber = m_fibers[m_ropeLength - 1]; + while (true) { + if (currentFiber.isRope()) { + Rope* rope = currentFiber.rope(); + // Copy the contents of the current rope into the workQueue, with the last item in 'currentFiber' + // (we will be working backwards over the rope). + unsigned ropeLengthMinusOne = rope->ropeLength() - 1; + for (unsigned i = 0; i < ropeLengthMinusOne; ++i) + workQueue.append(rope->fibers(i)); + currentFiber = rope->fibers(ropeLengthMinusOne); + } else { + UString::Rep* string = currentFiber.string(); + unsigned length = string->len; + position -= length; + copyChars(position, string->data(), length); + + // Was this the last item in the work queue? + if (workQueue.isEmpty()) { + // Create a string from the UChar buffer, clear the rope RefPtr. + ASSERT(buffer == position); + m_value = UString::Rep::create(buffer, m_stringLength); + for (unsigned i = 0; i < m_ropeLength; ++i) + m_fibers[i].deref(); + m_ropeLength = 0; + + ASSERT(!isRope()); + return; + } + + // No! - set the next item up to process. + currentFiber = workQueue.last(); + workQueue.removeLast(); + } + } +} + JSValue JSString::toPrimitive(ExecState*, PreferredPrimitiveType) const { return const_cast<JSString*>(this); } -bool JSString::getPrimitiveNumber(ExecState*, double& number, JSValue& value) +bool JSString::getPrimitiveNumber(ExecState* exec, double& number, JSValue& result) { - value = this; - number = m_value.toDouble(); + result = this; + number = value(exec).toDouble(); return false; } bool JSString::toBoolean(ExecState*) const { - return !m_value.isEmpty(); + return m_stringLength; } -double JSString::toNumber(ExecState*) const +double JSString::toNumber(ExecState* exec) const { - return m_value.toDouble(); + return value(exec).toDouble(); } -UString JSString::toString(ExecState*) const +UString JSString::toString(ExecState* exec) const { - return m_value; + return value(exec); } -UString JSString::toThisString(ExecState*) const +UString JSString::toThisString(ExecState* exec) const { - return m_value; + return value(exec); } JSString* JSString::toThisJSString(ExecState*) @@ -106,14 +225,14 @@ bool JSString::getOwnPropertySlot(ExecState* exec, const Identifier& propertyNam bool JSString::getStringPropertyDescriptor(ExecState* exec, const Identifier& propertyName, PropertyDescriptor& descriptor) { if (propertyName == exec->propertyNames().length) { - descriptor.setDescriptor(jsNumber(exec, m_value.size()), DontEnum | DontDelete | ReadOnly); + descriptor.setDescriptor(jsNumber(exec, m_stringLength), DontEnum | DontDelete | ReadOnly); return true; } bool isStrictUInt32; unsigned i = propertyName.toStrictUInt32(&isStrictUInt32); - if (isStrictUInt32 && i < static_cast<unsigned>(m_value.size())) { - descriptor.setDescriptor(jsSingleCharacterSubstring(exec, m_value, i), DontDelete | ReadOnly); + if (isStrictUInt32 && i < m_stringLength) { + descriptor.setDescriptor(jsSingleCharacterSubstring(exec, value(exec), i), DontDelete | ReadOnly); return true; } diff --git a/JavaScriptCore/runtime/JSString.h b/JavaScriptCore/runtime/JSString.h index 39dfe75..1867b17 100644 --- a/JavaScriptCore/runtime/JSString.h +++ b/JavaScriptCore/runtime/JSString.h @@ -60,13 +60,110 @@ namespace JSC { JSString* jsOwnedString(ExecState*, const UString&); class JSString : public JSCell { + public: friend class JIT; friend struct VPtrSet; - public: + // A Rope is a string composed of a set of substrings. + class Rope : public RefCounted<Rope> { + public: + // A Rope is composed from a set of smaller strings called Fibers. + // Each Fiber in a rope is either UString::Rep or another Rope. + class Fiber { + public: + Fiber() {} + Fiber(UString::Rep* string) : m_value(reinterpret_cast<intptr_t>(string)) {} + Fiber(Rope* rope) : m_value(reinterpret_cast<intptr_t>(rope) | 1) {} + + void deref() + { + if (isRope()) + rope()->deref(); + else + string()->deref(); + } + + Fiber& ref() + { + if (isString()) + string()->ref(); + else + rope()->ref(); + return *this; + } + + unsigned refAndGetLength() + { + if (isString()) { + UString::Rep* rep = string(); + return rep->ref()->len; + } else { + Rope* r = rope(); + r->ref(); + return r->stringLength(); + } + } + + bool isRope() { return m_value & 1; } + Rope* rope() { return reinterpret_cast<Rope*>(m_value & ~1); } + bool isString() { return !isRope(); } + UString::Rep* string() { return reinterpret_cast<UString::Rep*>(m_value); } + + private: + intptr_t m_value; + }; + + // Creates a Rope comprising of 'ropeLength' Fibers. + // The Rope is constructed in an uninitialized state - initialize must be called for each Fiber in the Rope. + static PassRefPtr<Rope> createOrNull(unsigned ropeLength) + { + void* allocation; + if (tryFastMalloc(sizeof(Rope) + (ropeLength - 1) * sizeof(Fiber)).getValue(allocation)) + return adoptRef(new (allocation) Rope(ropeLength)); + return 0; + } + + ~Rope(); + void destructNonRecursive(); + + void append(unsigned &index, Fiber& fiber) + { + m_fibers[index++] = fiber; + m_stringLength += fiber.refAndGetLength(); + } + void append(unsigned &index, const UString& string) + { + UString::Rep* rep = string.rep(); + m_fibers[index++] = Fiber(rep); + m_stringLength += rep->ref()->len; + } + void append(unsigned& index, JSString* jsString) + { + if (jsString->isRope()) { + for (unsigned i = 0; i < jsString->m_ropeLength; ++i) + append(index, jsString->m_fibers[i]); + } else + append(index, jsString->string()); + } + + unsigned ropeLength() { return m_ropeLength; } + unsigned stringLength() { return m_stringLength; } + Fiber& fibers(unsigned index) { return m_fibers[index]; } + + private: + Rope(unsigned ropeLength) : m_ropeLength(ropeLength), m_stringLength(0) {} + void* operator new(size_t, void* inPlace) { return inPlace; } + + unsigned m_ropeLength; + unsigned m_stringLength; + Fiber m_fibers[1]; + }; + JSString(JSGlobalData* globalData, const UString& value) : JSCell(globalData->stringStructure.get()) + , m_stringLength(value.size()) , m_value(value) + , m_ropeLength(0) { Heap::heap(this)->reportExtraMemoryCost(value.cost()); } @@ -74,23 +171,106 @@ namespace JSC { enum HasOtherOwnerType { HasOtherOwner }; JSString(JSGlobalData* globalData, const UString& value, HasOtherOwnerType) : JSCell(globalData->stringStructure.get()) + , m_stringLength(value.size()) , m_value(value) + , m_ropeLength(0) { } JSString(JSGlobalData* globalData, PassRefPtr<UString::Rep> value, HasOtherOwnerType) : JSCell(globalData->stringStructure.get()) + , m_stringLength(value->size()) , m_value(value) + , m_ropeLength(0) { } - - const UString& value() const { return m_value; } + JSString(JSGlobalData* globalData, PassRefPtr<JSString::Rope> rope) + : JSCell(globalData->stringStructure.get()) + , m_stringLength(rope->stringLength()) + , m_ropeLength(1) + { + m_fibers[0] = rope.releaseRef(); + } + // This constructor constructs a new string by concatenating s1 & s2. + // This should only be called with ropeLength <= 3. + JSString(JSGlobalData* globalData, unsigned ropeLength, JSString* s1, JSString* s2) + : JSCell(globalData->stringStructure.get()) + , m_stringLength(s1->length() + s2->length()) + , m_ropeLength(ropeLength) + { + ASSERT(ropeLength <= s_maxInternalRopeLength); + unsigned index = 0; + appendStringInConstruct(index, s1); + appendStringInConstruct(index, s2); + ASSERT(ropeLength == index); + } + // This constructor constructs a new string by concatenating s1 & s2. + // This should only be called with ropeLength <= 3. + JSString(JSGlobalData* globalData, unsigned ropeLength, JSString* s1, const UString& u2) + : JSCell(globalData->stringStructure.get()) + , m_stringLength(s1->length() + u2.size()) + , m_ropeLength(ropeLength) + { + ASSERT(ropeLength <= s_maxInternalRopeLength); + unsigned index = 0; + appendStringInConstruct(index, s1); + appendStringInConstruct(index, u2); + ASSERT(ropeLength == index); + } + // This constructor constructs a new string by concatenating s1 & s2. + // This should only be called with ropeLength <= 3. + JSString(JSGlobalData* globalData, unsigned ropeLength, const UString& u1, JSString* s2) + : JSCell(globalData->stringStructure.get()) + , m_stringLength(u1.size() + s2->length()) + , m_ropeLength(ropeLength) + { + ASSERT(ropeLength <= s_maxInternalRopeLength); + unsigned index = 0; + appendStringInConstruct(index, u1); + appendStringInConstruct(index, s2); + ASSERT(ropeLength == index); + } + // This constructor constructs a new string by concatenating v1, v2 & v3. + // This should only be called with ropeLength <= 3 ... which since every + // value must require a ropeLength of at least one implies that the length + // for each value must be exactly 1! + JSString(ExecState* exec, JSValue v1, JSValue v2, JSValue v3) + : JSCell(exec->globalData().stringStructure.get()) + , m_stringLength(0) + , m_ropeLength(s_maxInternalRopeLength) + { + unsigned index = 0; + appendValueInConstructAndIncrementLength(exec, index, v1); + appendValueInConstructAndIncrementLength(exec, index, v2); + appendValueInConstructAndIncrementLength(exec, index, v3); + ASSERT(index == s_maxInternalRopeLength); + } + + ~JSString() + { + for (unsigned i = 0; i < m_ropeLength; ++i) + m_fibers[i].deref(); + } + + const UString& value(ExecState* exec) const + { + if (isRope()) + resolveRope(exec); + return m_value; + } + const UString tryGetValue() const + { + if (isRope()) + UString(); + return m_value; + } + unsigned length() { return m_stringLength; } bool getStringPropertySlot(ExecState*, const Identifier& propertyName, PropertySlot&); bool getStringPropertySlot(ExecState*, unsigned propertyName, PropertySlot&); bool getStringPropertyDescriptor(ExecState*, const Identifier& propertyName, PropertyDescriptor&); - bool canGetIndex(unsigned i) { return i < static_cast<unsigned>(m_value.size()); } - JSString* getIndex(JSGlobalData*, unsigned); + bool canGetIndex(unsigned i) { return i < m_stringLength; } + JSString* getIndex(ExecState*, unsigned); static PassRefPtr<Structure> createStructure(JSValue proto) { return Structure::create(proto, TypeInfo(StringType, OverridesGetOwnPropertySlot | NeedsThisConversion)); } @@ -98,7 +278,39 @@ namespace JSC { enum VPtrStealingHackType { VPtrStealingHack }; JSString(VPtrStealingHackType) : JSCell(0) + , m_ropeLength(0) + { + } + + void resolveRope(ExecState*) const; + + void appendStringInConstruct(unsigned& index, const UString& string) + { + m_fibers[index++] = Rope::Fiber(string.rep()->ref()); + } + + void appendStringInConstruct(unsigned& index, JSString* jsString) + { + if (jsString->isRope()) { + for (unsigned i = 0; i < jsString->m_ropeLength; ++i) + m_fibers[index++] = jsString->m_fibers[i].ref(); + } else + appendStringInConstruct(index, jsString->string()); + } + + void appendValueInConstructAndIncrementLength(ExecState* exec, unsigned& index, JSValue v) { + if (v.isString()) { + ASSERT(asCell(v)->isString()); + JSString* s = static_cast<JSString*>(asCell(v)); + ASSERT(s->ropeLength() == 1); + appendStringInConstruct(index, s); + m_stringLength += s->length(); + } else { + UString u(v.toString(exec)); + m_fibers[index++] = Rope::Fiber(u.rep()->ref()); + m_stringLength += u.size(); + } } virtual JSValue toPrimitive(ExecState*, PreferredPrimitiveType) const; @@ -117,7 +329,22 @@ namespace JSC { virtual bool getOwnPropertySlot(ExecState*, unsigned propertyName, PropertySlot&); virtual bool getOwnPropertyDescriptor(ExecState*, const Identifier&, PropertyDescriptor&); - UString m_value; + static const unsigned s_maxInternalRopeLength = 3; + + // A string is represented either by a UString or a Rope. + unsigned m_stringLength; + mutable UString m_value; + mutable unsigned m_ropeLength; + mutable Rope::Fiber m_fibers[s_maxInternalRopeLength]; + + bool isRope() const { return m_ropeLength; } + UString& string() { ASSERT(!isRope()); return m_value; } + unsigned ropeLength() { return m_ropeLength ? m_ropeLength : 1; } + + friend JSValue jsString(ExecState* exec, JSString* s1, JSString* s2); + friend JSValue jsString(ExecState* exec, const UString& u1, JSString* s2); + friend JSValue jsString(ExecState* exec, JSString* s1, const UString& u2); + friend JSValue jsString(ExecState* exec, Register* strings, unsigned count); }; JSString* asString(JSValue); @@ -146,7 +373,7 @@ namespace JSC { UChar c = s.data()[offset]; if (c <= 0xFF) return globalData->smallStrings.singleCharacterString(globalData, c); - return new (globalData) JSString(globalData, UString::Rep::create(s.rep(), offset, 1)); + return new (globalData) JSString(globalData, UString(UString::Rep::create(s.rep(), offset, 1))); } inline JSString* jsNontrivialString(JSGlobalData* globalData, const char* s) @@ -163,10 +390,10 @@ namespace JSC { return new (globalData) JSString(globalData, s); } - inline JSString* JSString::getIndex(JSGlobalData* globalData, unsigned i) + inline JSString* JSString::getIndex(ExecState* exec, unsigned i) { ASSERT(canGetIndex(i)); - return jsSingleCharacterSubstring(globalData, m_value, i); + return jsSingleCharacterSubstring(&exec->globalData(), value(exec), i); } inline JSString* jsString(JSGlobalData* globalData, const UString& s) @@ -181,7 +408,7 @@ namespace JSC { } return new (globalData) JSString(globalData, s); } - + inline JSString* jsSubstring(JSGlobalData* globalData, const UString& s, unsigned offset, unsigned length) { ASSERT(offset <= static_cast<unsigned>(s.size())); @@ -194,7 +421,7 @@ namespace JSC { if (c <= 0xFF) return globalData->smallStrings.singleCharacterString(globalData, c); } - return new (globalData) JSString(globalData, UString::Rep::create(s.rep(), offset, length)); + return new (globalData) JSString(globalData, UString(UString::Rep::create(s.rep(), offset, length))); } inline JSString* jsOwnedString(JSGlobalData* globalData, const UString& s) @@ -222,14 +449,14 @@ namespace JSC { ALWAYS_INLINE bool JSString::getStringPropertySlot(ExecState* exec, const Identifier& propertyName, PropertySlot& slot) { if (propertyName == exec->propertyNames().length) { - slot.setValue(jsNumber(exec, m_value.size())); + slot.setValue(jsNumber(exec, m_stringLength)); return true; } bool isStrictUInt32; unsigned i = propertyName.toStrictUInt32(&isStrictUInt32); - if (isStrictUInt32 && i < static_cast<unsigned>(m_value.size())) { - slot.setValue(jsSingleCharacterSubstring(exec, m_value, i)); + if (isStrictUInt32 && i < m_stringLength) { + slot.setValue(jsSingleCharacterSubstring(exec, value(exec), i)); return true; } @@ -238,8 +465,8 @@ namespace JSC { ALWAYS_INLINE bool JSString::getStringPropertySlot(ExecState* exec, unsigned propertyName, PropertySlot& slot) { - if (propertyName < static_cast<unsigned>(m_value.size())) { - slot.setValue(jsSingleCharacterSubstring(exec, m_value, propertyName)); + if (propertyName < m_stringLength) { + slot.setValue(jsSingleCharacterSubstring(exec, value(exec), propertyName)); return true; } @@ -258,7 +485,7 @@ namespace JSC { inline UString JSValue::toString(ExecState* exec) const { if (isString()) - return static_cast<JSString*>(asCell())->value(); + return static_cast<JSString*>(asCell())->value(exec); if (isInt32()) return exec->globalData().numericStrings.add(asInt32()); if (isDouble()) diff --git a/JavaScriptCore/runtime/JSValue.h b/JavaScriptCore/runtime/JSValue.h index 1063cdc..fa5b5c0 100644 --- a/JavaScriptCore/runtime/JSValue.h +++ b/JavaScriptCore/runtime/JSValue.h @@ -80,6 +80,7 @@ namespace JSC { enum JSUndefinedTag { JSUndefined }; enum JSTrueTag { JSTrue }; enum JSFalseTag { JSFalse }; + enum EncodeAsDoubleTag { EncodeAsDouble }; JSValue(); JSValue(JSNullTag); @@ -90,6 +91,7 @@ namespace JSC { JSValue(const JSCell* ptr); // Numbers + JSValue(EncodeAsDoubleTag, ExecState*, double); JSValue(ExecState*, double); JSValue(ExecState*, char); JSValue(ExecState*, unsigned char); @@ -135,8 +137,8 @@ namespace JSC { bool getBoolean() const; // false if not a boolean bool getNumber(double&) const; double uncheckedGetNumber() const; - bool getString(UString&) const; - UString getString() const; // null string if not a string + bool getString(ExecState* exec, UString&) const; + UString getString(ExecState* exec) const; // null string if not a string JSObject* getObject() const; // 0 if not an object CallType getCallData(CallData&); @@ -166,6 +168,10 @@ namespace JSC { uint32_t toUInt32(ExecState*) const; uint32_t toUInt32(ExecState*, bool& ok) const; +#if ENABLE(JSC_ZOMBIES) + bool isZombie() const; +#endif + // Floating point conversions (this is a convenience method for webcore; // signle precision float is not a representation used in JS or JSC). float toFloat(ExecState* exec) const { return static_cast<float>(toNumber(exec)); } @@ -186,9 +192,9 @@ namespace JSC { static bool equal(ExecState* exec, JSValue v1, JSValue v2); static bool equalSlowCase(ExecState* exec, JSValue v1, JSValue v2); static bool equalSlowCaseInline(ExecState* exec, JSValue v1, JSValue v2); - static bool strictEqual(JSValue v1, JSValue v2); - static bool strictEqualSlowCase(JSValue v1, JSValue v2); - static bool strictEqualSlowCaseInline(JSValue v1, JSValue v2); + static bool strictEqual(ExecState* exec, JSValue v1, JSValue v2); + static bool strictEqualSlowCase(ExecState* exec, JSValue v1, JSValue v2); + static bool strictEqualSlowCaseInline(ExecState* exec, JSValue v1, JSValue v2); JSValue getJSNumber(); // JSValue() if this is not a JSNumber or number object @@ -279,6 +285,11 @@ namespace JSC { return b ? JSValue(JSValue::JSTrue) : JSValue(JSValue::JSFalse); } + ALWAYS_INLINE JSValue jsDoubleNumber(ExecState* exec, double d) + { + return JSValue(JSValue::EncodeAsDouble, exec, d); + } + ALWAYS_INLINE JSValue jsNumber(ExecState* exec, double d) { return JSValue(exec, d); @@ -431,6 +442,9 @@ namespace JSC { { JSValue v; v.u.asEncodedJSValue = encodedJSValue; +#if ENABLE(JSC_ZOMBIES) + ASSERT(!v.isZombie()); +#endif return v; } @@ -477,6 +491,9 @@ namespace JSC { else u.asBits.tag = EmptyValueTag; u.asBits.payload = reinterpret_cast<int32_t>(ptr); +#if ENABLE(JSC_ZOMBIES) + ASSERT(!isZombie()); +#endif } inline JSValue::JSValue(const JSCell* ptr) @@ -486,6 +503,9 @@ namespace JSC { else u.asBits.tag = EmptyValueTag; u.asBits.payload = reinterpret_cast<int32_t>(const_cast<JSCell*>(ptr)); +#if ENABLE(JSC_ZOMBIES) + ASSERT(!isZombie()); +#endif } inline JSValue::operator bool() const @@ -583,6 +603,11 @@ namespace JSC { return reinterpret_cast<JSCell*>(u.asBits.payload); } + ALWAYS_INLINE JSValue::JSValue(EncodeAsDoubleTag, ExecState*, double d) + { + u.asDouble = d; + } + inline JSValue::JSValue(ExecState* exec, double d) { const int32_t asInt32 = static_cast<int32_t>(d); @@ -781,11 +806,17 @@ namespace JSC { inline JSValue::JSValue(JSCell* ptr) : m_ptr(ptr) { +#if ENABLE(JSC_ZOMBIES) + ASSERT(!isZombie()); +#endif } inline JSValue::JSValue(const JSCell* ptr) : m_ptr(const_cast<JSCell*>(ptr)) { +#if ENABLE(JSC_ZOMBIES) + ASSERT(!isZombie()); +#endif } inline JSValue::operator bool() const diff --git a/JavaScriptCore/runtime/JSVariableObject.cpp b/JavaScriptCore/runtime/JSVariableObject.cpp index 6586393..3aa9e62 100644 --- a/JavaScriptCore/runtime/JSVariableObject.cpp +++ b/JavaScriptCore/runtime/JSVariableObject.cpp @@ -53,16 +53,6 @@ void JSVariableObject::getOwnPropertyNames(ExecState* exec, PropertyNameArray& p JSObject::getOwnPropertyNames(exec, propertyNames); } -bool JSVariableObject::getPropertyAttributes(ExecState* exec, const Identifier& propertyName, unsigned& attributes) const -{ - SymbolTableEntry entry = symbolTable().get(propertyName.ustring().rep()); - if (!entry.isNull()) { - attributes = entry.getAttributes() | DontDelete; - return true; - } - return JSObject::getPropertyAttributes(exec, propertyName, attributes); -} - bool JSVariableObject::isVariableObject() const { return true; diff --git a/JavaScriptCore/runtime/JSVariableObject.h b/JavaScriptCore/runtime/JSVariableObject.h index d8b1479..15d6ff5 100644 --- a/JavaScriptCore/runtime/JSVariableObject.h +++ b/JavaScriptCore/runtime/JSVariableObject.h @@ -54,8 +54,6 @@ namespace JSC { virtual bool isVariableObject() const; virtual bool isDynamicScope() const = 0; - virtual bool getPropertyAttributes(ExecState*, const Identifier& propertyName, unsigned& attributes) const; - Register& registerAt(int index) const { return d->registers[index]; } static PassRefPtr<Structure> createStructure(JSValue prototype) diff --git a/JavaScriptCore/runtime/JSZombie.cpp b/JavaScriptCore/runtime/JSZombie.cpp new file mode 100644 index 0000000..072d29b --- /dev/null +++ b/JavaScriptCore/runtime/JSZombie.cpp @@ -0,0 +1,48 @@ +/* + * Copyright (C) 2009 Apple Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "config.h" +#include "JSZombie.h" +#include "ClassInfo.h" + +#if ENABLE(JSC_ZOMBIES) + +namespace JSC { + +const ClassInfo JSZombie::s_info = { "Zombie", 0, 0, 0 }; + +Structure* JSZombie::leakedZombieStructure() { + static Structure* structure = 0; + if (!structure) { + Structure::startIgnoringLeaks(); + structure = Structure::create(jsNull(), TypeInfo(UnspecifiedType)).releaseRef(); + Structure::stopIgnoringLeaks(); + } + return structure; +} + +} + +#endif // ENABLE(JSC_ZOMBIES) diff --git a/JavaScriptCore/runtime/JSZombie.h b/JavaScriptCore/runtime/JSZombie.h new file mode 100644 index 0000000..8b33ea6 --- /dev/null +++ b/JavaScriptCore/runtime/JSZombie.h @@ -0,0 +1,78 @@ +/* + * Copyright (C) 2009 Apple Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef JSZombie_h +#define JSZombie_h + +#include "JSCell.h" + +#if ENABLE(JSC_ZOMBIES) +namespace JSC { + +class JSZombie : public JSCell { +public: + JSZombie(const ClassInfo* oldInfo, Structure* structure) + : JSCell(structure) + , m_oldInfo(oldInfo) + { + } + virtual bool isZombie() const { return true; } + virtual const ClassInfo* classInfo() const { return &s_info; } + static Structure* leakedZombieStructure(); + + virtual bool isGetterSetter() const { ASSERT_NOT_REACHED(); return false; } + virtual bool isAPIValueWrapper() const { ASSERT_NOT_REACHED(); return false; } + virtual bool isPropertyNameIterator() const { ASSERT_NOT_REACHED(); return false; } + virtual CallType getCallData(CallData&) { ASSERT_NOT_REACHED(); return CallTypeNone; } + virtual ConstructType getConstructData(ConstructData&) { ASSERT_NOT_REACHED(); return ConstructTypeNone; } + virtual bool getUInt32(uint32_t&) const { ASSERT_NOT_REACHED(); return false; } + virtual JSValue toPrimitive(ExecState*, PreferredPrimitiveType) const { ASSERT_NOT_REACHED(); return jsNull(); } + virtual bool getPrimitiveNumber(ExecState*, double&, JSValue&) { ASSERT_NOT_REACHED(); return false; } + virtual bool toBoolean(ExecState*) const { ASSERT_NOT_REACHED(); return false; } + virtual double toNumber(ExecState*) const { ASSERT_NOT_REACHED(); return 0.0; } + virtual UString toString(ExecState*) const { ASSERT_NOT_REACHED(); return ""; } + virtual JSObject* toObject(ExecState*) const { ASSERT_NOT_REACHED(); return 0; } + virtual void markChildren(MarkStack&) { ASSERT_NOT_REACHED(); } + virtual void put(ExecState*, const Identifier&, JSValue, PutPropertySlot&) { ASSERT_NOT_REACHED(); } + virtual void put(ExecState*, unsigned, JSValue) { ASSERT_NOT_REACHED(); } + virtual bool deleteProperty(ExecState*, const Identifier&) { ASSERT_NOT_REACHED(); return false; } + virtual bool deleteProperty(ExecState*, unsigned) { ASSERT_NOT_REACHED(); return false; } + virtual JSObject* toThisObject(ExecState*) const { ASSERT_NOT_REACHED(); return 0; } + virtual UString toThisString(ExecState*) const { ASSERT_NOT_REACHED(); return ""; } + virtual JSString* toThisJSString(ExecState*) { ASSERT_NOT_REACHED(); return 0; } + virtual JSValue getJSNumber() { ASSERT_NOT_REACHED(); return jsNull(); } + virtual bool getOwnPropertySlot(ExecState*, const Identifier&, PropertySlot&) { ASSERT_NOT_REACHED(); return false; } + virtual bool getOwnPropertySlot(ExecState*, unsigned, PropertySlot&) { ASSERT_NOT_REACHED(); return false; } + + static const ClassInfo s_info; +private: + const ClassInfo* m_oldInfo; +}; + +} + +#endif // ENABLE(JSC_ZOMBIES) + +#endif // JSZombie_h diff --git a/JavaScriptCore/runtime/MarkStack.h b/JavaScriptCore/runtime/MarkStack.h index ea09f54..a114ae0 100644 --- a/JavaScriptCore/runtime/MarkStack.h +++ b/JavaScriptCore/runtime/MarkStack.h @@ -153,7 +153,7 @@ namespace JSC { ASSERT(0 == (size % MarkStack::pageSize())); if (size == m_allocated) return; -#if PLATFORM(WIN) || PLATFORM(SYMBIAN) +#if PLATFORM(WIN_OS) || PLATFORM(SYMBIAN) // We cannot release a part of a region with VirtualFree. To get around this, // we'll release the entire region and reallocate the size that we want. releaseStack(m_data, m_allocated); diff --git a/JavaScriptCore/runtime/MathObject.cpp b/JavaScriptCore/runtime/MathObject.cpp index e8b7b97..98ff3ba 100644 --- a/JavaScriptCore/runtime/MathObject.cpp +++ b/JavaScriptCore/runtime/MathObject.cpp @@ -96,7 +96,6 @@ MathObject::MathObject(ExecState* exec, NonNullPassRefPtr<Structure> structure) putDirectWithoutTransition(Identifier(exec, "PI"), jsNumber(exec, piDouble), DontDelete | DontEnum | ReadOnly); putDirectWithoutTransition(Identifier(exec, "SQRT1_2"), jsNumber(exec, sqrt(0.5)), DontDelete | DontEnum | ReadOnly); putDirectWithoutTransition(Identifier(exec, "SQRT2"), jsNumber(exec, sqrt(2.0)), DontDelete | DontEnum | ReadOnly); - WTF::initializeWeakRandomNumberGenerator(); } // ECMA 15.8 @@ -120,22 +119,22 @@ JSValue JSC_HOST_CALL mathProtoFuncAbs(ExecState* exec, JSObject*, JSValue, cons JSValue JSC_HOST_CALL mathProtoFuncACos(ExecState* exec, JSObject*, JSValue, const ArgList& args) { - return jsNumber(exec, acos(args.at(0).toNumber(exec))); + return jsDoubleNumber(exec, acos(args.at(0).toNumber(exec))); } JSValue JSC_HOST_CALL mathProtoFuncASin(ExecState* exec, JSObject*, JSValue, const ArgList& args) { - return jsNumber(exec, asin(args.at(0).toNumber(exec))); + return jsDoubleNumber(exec, asin(args.at(0).toNumber(exec))); } JSValue JSC_HOST_CALL mathProtoFuncATan(ExecState* exec, JSObject*, JSValue, const ArgList& args) { - return jsNumber(exec, atan(args.at(0).toNumber(exec))); + return jsDoubleNumber(exec, atan(args.at(0).toNumber(exec))); } JSValue JSC_HOST_CALL mathProtoFuncATan2(ExecState* exec, JSObject*, JSValue, const ArgList& args) { - return jsNumber(exec, atan2(args.at(0).toNumber(exec), args.at(1).toNumber(exec))); + return jsDoubleNumber(exec, atan2(args.at(0).toNumber(exec), args.at(1).toNumber(exec))); } JSValue JSC_HOST_CALL mathProtoFuncCeil(ExecState* exec, JSObject*, JSValue, const ArgList& args) @@ -145,12 +144,12 @@ JSValue JSC_HOST_CALL mathProtoFuncCeil(ExecState* exec, JSObject*, JSValue, con JSValue JSC_HOST_CALL mathProtoFuncCos(ExecState* exec, JSObject*, JSValue, const ArgList& args) { - return jsNumber(exec, cos(args.at(0).toNumber(exec))); + return jsDoubleNumber(exec, cos(args.at(0).toNumber(exec))); } JSValue JSC_HOST_CALL mathProtoFuncExp(ExecState* exec, JSObject*, JSValue, const ArgList& args) { - return jsNumber(exec, exp(args.at(0).toNumber(exec))); + return jsDoubleNumber(exec, exp(args.at(0).toNumber(exec))); } JSValue JSC_HOST_CALL mathProtoFuncFloor(ExecState* exec, JSObject*, JSValue, const ArgList& args) @@ -160,7 +159,7 @@ JSValue JSC_HOST_CALL mathProtoFuncFloor(ExecState* exec, JSObject*, JSValue, co JSValue JSC_HOST_CALL mathProtoFuncLog(ExecState* exec, JSObject*, JSValue, const ArgList& args) { - return jsNumber(exec, log(args.at(0).toNumber(exec))); + return jsDoubleNumber(exec, log(args.at(0).toNumber(exec))); } JSValue JSC_HOST_CALL mathProtoFuncMax(ExecState* exec, JSObject*, JSValue, const ArgList& args) @@ -211,7 +210,7 @@ JSValue JSC_HOST_CALL mathProtoFuncPow(ExecState* exec, JSObject*, JSValue, cons JSValue JSC_HOST_CALL mathProtoFuncRandom(ExecState* exec, JSObject*, JSValue, const ArgList&) { - return jsNumber(exec, WTF::weakRandomNumber()); + return jsDoubleNumber(exec, exec->globalData().weakRandom.get()); } JSValue JSC_HOST_CALL mathProtoFuncRound(ExecState* exec, JSObject*, JSValue, const ArgList& args) @@ -224,17 +223,17 @@ JSValue JSC_HOST_CALL mathProtoFuncRound(ExecState* exec, JSObject*, JSValue, co JSValue JSC_HOST_CALL mathProtoFuncSin(ExecState* exec, JSObject*, JSValue, const ArgList& args) { - return jsNumber(exec, sin(args.at(0).toNumber(exec))); + return jsDoubleNumber(exec, sin(args.at(0).toNumber(exec))); } JSValue JSC_HOST_CALL mathProtoFuncSqrt(ExecState* exec, JSObject*, JSValue, const ArgList& args) { - return jsNumber(exec, sqrt(args.at(0).toNumber(exec))); + return jsDoubleNumber(exec, sqrt(args.at(0).toNumber(exec))); } JSValue JSC_HOST_CALL mathProtoFuncTan(ExecState* exec, JSObject*, JSValue, const ArgList& args) { - return jsNumber(exec, tan(args.at(0).toNumber(exec))); + return jsDoubleNumber(exec, tan(args.at(0).toNumber(exec))); } } // namespace JSC diff --git a/JavaScriptCore/runtime/NativeErrorConstructor.cpp b/JavaScriptCore/runtime/NativeErrorConstructor.cpp index c655fae..403fc7e 100644 --- a/JavaScriptCore/runtime/NativeErrorConstructor.cpp +++ b/JavaScriptCore/runtime/NativeErrorConstructor.cpp @@ -33,7 +33,7 @@ ASSERT_CLASS_FITS_IN_CELL(NativeErrorConstructor); const ClassInfo NativeErrorConstructor::info = { "Function", &InternalFunction::info, 0, 0 }; NativeErrorConstructor::NativeErrorConstructor(ExecState* exec, NonNullPassRefPtr<Structure> structure, NativeErrorPrototype* nativeErrorPrototype) - : InternalFunction(&exec->globalData(), structure, Identifier(exec, nativeErrorPrototype->getDirect(exec->propertyNames().name).getString())) + : InternalFunction(&exec->globalData(), structure, Identifier(exec, nativeErrorPrototype->getDirect(exec->propertyNames().name).getString(exec))) , m_errorStructure(ErrorInstance::createStructure(nativeErrorPrototype)) { putDirect(exec->propertyNames().length, jsNumber(exec, 1), DontDelete | ReadOnly | DontEnum); // ECMA 15.11.7.5 diff --git a/JavaScriptCore/runtime/ObjectConstructor.cpp b/JavaScriptCore/runtime/ObjectConstructor.cpp index 837d5a6..693efc3 100644 --- a/JavaScriptCore/runtime/ObjectConstructor.cpp +++ b/JavaScriptCore/runtime/ObjectConstructor.cpp @@ -148,14 +148,14 @@ static bool toPropertyDescriptor(ExecState* exec, JSValue in, PropertyDescriptor } JSObject* description = asObject(in); - PropertySlot enumerableSlot; + PropertySlot enumerableSlot(description); if (description->getPropertySlot(exec, exec->propertyNames().enumerable, enumerableSlot)) { desc.setEnumerable(enumerableSlot.getValue(exec, exec->propertyNames().enumerable).toBoolean(exec)); if (exec->hadException()) return false; } - PropertySlot configurableSlot; + PropertySlot configurableSlot(description); if (description->getPropertySlot(exec, exec->propertyNames().configurable, configurableSlot)) { desc.setConfigurable(configurableSlot.getValue(exec, exec->propertyNames().configurable).toBoolean(exec)); if (exec->hadException()) @@ -163,21 +163,21 @@ static bool toPropertyDescriptor(ExecState* exec, JSValue in, PropertyDescriptor } JSValue value; - PropertySlot valueSlot; + PropertySlot valueSlot(description); if (description->getPropertySlot(exec, exec->propertyNames().value, valueSlot)) { desc.setValue(valueSlot.getValue(exec, exec->propertyNames().value)); if (exec->hadException()) return false; } - PropertySlot writableSlot; + PropertySlot writableSlot(description); if (description->getPropertySlot(exec, exec->propertyNames().writable, writableSlot)) { desc.setWritable(writableSlot.getValue(exec, exec->propertyNames().writable).toBoolean(exec)); if (exec->hadException()) return false; } - PropertySlot getSlot; + PropertySlot getSlot(description); if (description->getPropertySlot(exec, exec->propertyNames().get, getSlot)) { JSValue get = getSlot.getValue(exec, exec->propertyNames().get); if (exec->hadException()) @@ -193,7 +193,7 @@ static bool toPropertyDescriptor(ExecState* exec, JSValue in, PropertyDescriptor desc.setGetter(get); } - PropertySlot setSlot; + PropertySlot setSlot(description); if (description->getPropertySlot(exec, exec->propertyNames().set, setSlot)) { JSValue set = setSlot.getValue(exec, exec->propertyNames().set); if (exec->hadException()) diff --git a/JavaScriptCore/runtime/Operations.cpp b/JavaScriptCore/runtime/Operations.cpp index 093bbec..0e1887c 100644 --- a/JavaScriptCore/runtime/Operations.cpp +++ b/JavaScriptCore/runtime/Operations.cpp @@ -29,10 +29,6 @@ #include <stdio.h> #include <wtf/MathExtras.h> -#if HAVE(FLOAT_H) -#include <float.h> -#endif - namespace JSC { bool JSValue::equalSlowCase(ExecState* exec, JSValue v1, JSValue v2) @@ -40,9 +36,9 @@ bool JSValue::equalSlowCase(ExecState* exec, JSValue v1, JSValue v2) return equalSlowCaseInline(exec, v1, v2); } -bool JSValue::strictEqualSlowCase(JSValue v1, JSValue v2) +bool JSValue::strictEqualSlowCase(ExecState* exec, JSValue v1, JSValue v2) { - return strictEqualSlowCaseInline(v1, v2); + return strictEqualSlowCaseInline(exec, v1, v2); } NEVER_INLINE JSValue throwOutOfMemoryError(ExecState* exec) @@ -58,12 +54,13 @@ NEVER_INLINE JSValue jsAddSlowCase(CallFrame* callFrame, JSValue v1, JSValue v2) JSValue p1 = v1.toPrimitive(callFrame); JSValue p2 = v2.toPrimitive(callFrame); - if (p1.isString() || p2.isString()) { - RefPtr<UString::Rep> value = concatenate(p1.toString(callFrame).rep(), p2.toString(callFrame).rep()); - if (!value) - return throwOutOfMemoryError(callFrame); - return jsString(callFrame, value.release()); + if (p1.isString()) { + return p2.isString() + ? jsString(callFrame, asString(p1), asString(p2)) + : jsString(callFrame, asString(p1), p2.toString(callFrame)); } + if (p2.isString()) + return jsString(callFrame, p1.toString(callFrame), asString(p2)); return jsNumber(callFrame, p1.toNumber(callFrame) + p2.toNumber(callFrame)); } diff --git a/JavaScriptCore/runtime/Operations.h b/JavaScriptCore/runtime/Operations.h index 1aa68b3..0289ac2 100644 --- a/JavaScriptCore/runtime/Operations.h +++ b/JavaScriptCore/runtime/Operations.h @@ -35,6 +35,99 @@ namespace JSC { bool jsIsObjectType(JSValue); bool jsIsFunctionType(JSValue); + ALWAYS_INLINE JSValue jsString(ExecState* exec, JSString* s1, JSString* s2) + { + if (!s1->length()) + return s2; + if (!s2->length()) + return s1; + + unsigned ropeLength = s1->ropeLength() + s2->ropeLength(); + JSGlobalData* globalData = &exec->globalData(); + + if (ropeLength <= JSString::s_maxInternalRopeLength) + return new (globalData) JSString(globalData, ropeLength, s1, s2); + + unsigned index = 0; + RefPtr<JSString::Rope> rope = JSString::Rope::createOrNull(ropeLength); + if (UNLIKELY(!rope)) + return throwOutOfMemoryError(exec); + rope->append(index, s1); + rope->append(index, s2); + ASSERT(index == ropeLength); + return new (globalData) JSString(globalData, rope.release()); + } + + ALWAYS_INLINE JSValue jsString(ExecState* exec, const UString& u1, JSString* s2) + { + unsigned ropeLength = 1 + s2->ropeLength(); + JSGlobalData* globalData = &exec->globalData(); + + if (ropeLength <= JSString::s_maxInternalRopeLength) + return new (globalData) JSString(globalData, ropeLength, u1, s2); + + unsigned index = 0; + RefPtr<JSString::Rope> rope = JSString::Rope::createOrNull(ropeLength); + if (UNLIKELY(!rope)) + return throwOutOfMemoryError(exec); + rope->append(index, u1); + rope->append(index, s2); + ASSERT(index == ropeLength); + return new (globalData) JSString(globalData, rope.release()); + } + + ALWAYS_INLINE JSValue jsString(ExecState* exec, JSString* s1, const UString& u2) + { + unsigned ropeLength = s1->ropeLength() + 1; + JSGlobalData* globalData = &exec->globalData(); + + if (ropeLength <= JSString::s_maxInternalRopeLength) + return new (globalData) JSString(globalData, ropeLength, s1, u2); + + unsigned index = 0; + RefPtr<JSString::Rope> rope = JSString::Rope::createOrNull(ropeLength); + if (UNLIKELY(!rope)) + return throwOutOfMemoryError(exec); + rope->append(index, s1); + rope->append(index, u2); + ASSERT(index == ropeLength); + return new (globalData) JSString(globalData, rope.release()); + } + + ALWAYS_INLINE JSValue jsString(ExecState* exec, Register* strings, unsigned count) + { + ASSERT(count >= 3); + + unsigned ropeLength = 0; + for (unsigned i = 0; i < count; ++i) { + JSValue v = strings[i].jsValue(); + if (LIKELY(v.isString())) + ropeLength += asString(v)->ropeLength(); + else + ++ropeLength; + } + + JSGlobalData* globalData = &exec->globalData(); + if (ropeLength == 3) + return new (globalData) JSString(exec, strings[0].jsValue(), strings[1].jsValue(), strings[2].jsValue()); + + RefPtr<JSString::Rope> rope = JSString::Rope::createOrNull(ropeLength); + if (UNLIKELY(!rope)) + return throwOutOfMemoryError(exec); + + unsigned index = 0; + for (unsigned i = 0; i < count; ++i) { + JSValue v = strings[i].jsValue(); + if (LIKELY(v.isString())) + rope->append(index, asString(v)); + else + rope->append(index, v.toString(exec)); + } + + ASSERT(index == ropeLength); + return new (globalData) JSString(globalData, rope.release()); + } + // ECMA 11.9.3 inline bool JSValue::equal(ExecState* exec, JSValue v1, JSValue v2) { @@ -53,7 +146,7 @@ namespace JSC { bool s1 = v1.isString(); bool s2 = v2.isString(); if (s1 && s2) - return asString(v1)->value() == asString(v2)->value(); + return asString(v1)->value(exec) == asString(v2)->value(exec); if (v1.isUndefinedOrNull()) { if (v2.isUndefinedOrNull()) @@ -110,17 +203,17 @@ namespace JSC { } // ECMA 11.9.3 - ALWAYS_INLINE bool JSValue::strictEqualSlowCaseInline(JSValue v1, JSValue v2) + ALWAYS_INLINE bool JSValue::strictEqualSlowCaseInline(ExecState* exec, JSValue v1, JSValue v2) { ASSERT(v1.isCell() && v2.isCell()); if (v1.asCell()->isString() && v2.asCell()->isString()) - return asString(v1)->value() == asString(v2)->value(); + return asString(v1)->value(exec) == asString(v2)->value(exec); return v1 == v2; } - inline bool JSValue::strictEqual(JSValue v1, JSValue v2) + inline bool JSValue::strictEqual(ExecState* exec, JSValue v1, JSValue v2) { if (v1.isInt32() && v2.isInt32()) return v1 == v2; @@ -131,7 +224,7 @@ namespace JSC { if (!v1.isCell() || !v2.isCell()) return v1 == v2; - return strictEqualSlowCaseInline(v1, v2); + return strictEqualSlowCaseInline(exec, v1, v2); } inline bool jsLess(CallFrame* callFrame, JSValue v1, JSValue v2) @@ -146,7 +239,7 @@ namespace JSC { JSGlobalData* globalData = &callFrame->globalData(); if (isJSString(globalData, v1) && isJSString(globalData, v2)) - return asString(v1)->value() < asString(v2)->value(); + return asString(v1)->value(callFrame) < asString(v2)->value(callFrame); JSValue p1; JSValue p2; @@ -156,7 +249,7 @@ namespace JSC { if (wasNotString1 | wasNotString2) return n1 < n2; - return asString(p1)->value() < asString(p2)->value(); + return asString(p1)->value(callFrame) < asString(p2)->value(callFrame); } inline bool jsLessEq(CallFrame* callFrame, JSValue v1, JSValue v2) @@ -171,7 +264,7 @@ namespace JSC { JSGlobalData* globalData = &callFrame->globalData(); if (isJSString(globalData, v1) && isJSString(globalData, v2)) - return !(asString(v2)->value() < asString(v1)->value()); + return !(asString(v2)->value(callFrame) < asString(v1)->value(callFrame)); JSValue p1; JSValue p2; @@ -181,7 +274,7 @@ namespace JSC { if (wasNotString1 | wasNotString2) return n1 <= n2; - return !(asString(p2)->value() < asString(p1)->value()); + return !(asString(p2)->value(callFrame) < asString(p1)->value(callFrame)); } // Fast-path choices here are based on frequency data from SunSpider: @@ -195,29 +288,14 @@ namespace JSC { ALWAYS_INLINE JSValue jsAdd(CallFrame* callFrame, JSValue v1, JSValue v2) { - double left; - double right = 0.0; - - bool rightIsNumber = v2.getNumber(right); - if (rightIsNumber && v1.getNumber(left)) + double left = 0.0, right; + if (v1.getNumber(left), v2.getNumber(right)) return jsNumber(callFrame, left + right); - bool leftIsString = v1.isString(); - if (leftIsString && v2.isString()) { - RefPtr<UString::Rep> value = concatenate(asString(v1)->value().rep(), asString(v2)->value().rep()); - if (!value) - return throwOutOfMemoryError(callFrame); - return jsString(callFrame, value.release()); - } - - if (rightIsNumber & leftIsString) { - RefPtr<UString::Rep> value = v2.isInt32() ? - concatenate(asString(v1)->value().rep(), v2.asInt32()) : - concatenate(asString(v1)->value().rep(), right); - - if (!value) - return throwOutOfMemoryError(callFrame); - return jsString(callFrame, value.release()); + if (v1.isString()) { + return v2.isString() + ? jsString(callFrame, asString(v1), asString(v2)) + : jsString(callFrame, asString(v1), v2.toString(callFrame)); } // All other cases are pretty uncommon @@ -243,7 +321,7 @@ namespace JSC { // Since we're accessing a prototype in a loop, it's a good bet that it // should not be treated as a dictionary. if (cell->structure()->isDictionary()) - asObject(cell)->setStructure(Structure::fromDictionaryTransition(cell->structure())); + asObject(cell)->flattenDictionaryObject(); ++count; } @@ -265,7 +343,7 @@ namespace JSC { // Since we're accessing a prototype in a loop, it's a good bet that it // should not be treated as a dictionary. if (base->structure()->isDictionary()) - asObject(base)->setStructure(Structure::fromDictionaryTransition(base->structure())); + asObject(base)->flattenDictionaryObject(); ++count; } @@ -293,52 +371,6 @@ namespace JSC { ASSERT_NOT_REACHED(); return JSValue(); } - - ALWAYS_INLINE JSValue concatenateStrings(CallFrame* callFrame, Register* strings, unsigned count) - { - ASSERT(count >= 3); - - // Estimate the amount of space required to hold the entire string. If all - // arguments are strings, we can easily calculate the exact amount of space - // required. For any other arguments, for now let's assume they may require - // 11 UChars of storage. This is enouch to hold any int, and likely is also - // reasonable for the other immediates. We may want to come back and tune - // this value at some point. - unsigned bufferSize = 0; - for (unsigned i = 0; i < count; ++i) { - JSValue v = strings[i].jsValue(); - if (LIKELY(v.isString())) - bufferSize += asString(v)->value().size(); - else - bufferSize += 11; - } - - // Allocate an output string to store the result. - // If the first argument is a String, and if it has the capacity (or can grow - // its capacity) to hold the entire result then use this as a base to concatenate - // onto. Otherwise, allocate a new empty output buffer. - JSValue firstValue = strings[0].jsValue(); - RefPtr<UString::Rep> resultRep; - if (firstValue.isString() && (resultRep = asString(firstValue)->value().rep())->reserveCapacity(bufferSize)) { - // We're going to concatenate onto the first string - remove it from the list of items to be appended. - ++strings; - --count; - } else - resultRep = UString::Rep::createEmptyBuffer(bufferSize); - UString result(resultRep); - - // Loop over the operands, writing them into the output buffer. - for (unsigned i = 0; i < count; ++i) { - JSValue v = strings[i].jsValue(); - if (LIKELY(v.isString())) - result.append(asString(v)->value()); - else - result.append(v.toString(callFrame)); - } - - return jsString(callFrame, result); - } - } // namespace JSC #endif // Operations_h diff --git a/JavaScriptCore/runtime/PropertyDescriptor.cpp b/JavaScriptCore/runtime/PropertyDescriptor.cpp index 4db814f..558ae28 100644 --- a/JavaScriptCore/runtime/PropertyDescriptor.cpp +++ b/JavaScriptCore/runtime/PropertyDescriptor.cpp @@ -153,15 +153,15 @@ void PropertyDescriptor::setGetter(JSValue getter) m_attributes &= ~ReadOnly; } -bool PropertyDescriptor::equalTo(const PropertyDescriptor& other) const +bool PropertyDescriptor::equalTo(ExecState* exec, const PropertyDescriptor& other) const { if (!other.m_value == m_value || !other.m_getter == m_getter || !other.m_setter == m_setter) return false; - return (!m_value || JSValue::strictEqual(other.m_value, m_value)) && - (!m_getter || JSValue::strictEqual(other.m_getter, m_getter)) && - (!m_setter || JSValue::strictEqual(other.m_setter, m_setter)) && + return (!m_value || JSValue::strictEqual(exec, other.m_value, m_value)) && + (!m_getter || JSValue::strictEqual(exec, other.m_getter, m_getter)) && + (!m_setter || JSValue::strictEqual(exec, other.m_setter, m_setter)) && attributesEqual(other); } diff --git a/JavaScriptCore/runtime/PropertyDescriptor.h b/JavaScriptCore/runtime/PropertyDescriptor.h index 40bec86..ff9f160 100644 --- a/JavaScriptCore/runtime/PropertyDescriptor.h +++ b/JavaScriptCore/runtime/PropertyDescriptor.h @@ -61,7 +61,7 @@ namespace JSC { bool configurablePresent() const { return m_seenAttributes & ConfigurablePresent; } bool setterPresent() const { return m_setter; } bool getterPresent() const { return m_getter; } - bool equalTo(const PropertyDescriptor& other) const; + bool equalTo(ExecState* exec, const PropertyDescriptor& other) const; bool attributesEqual(const PropertyDescriptor& other) const; unsigned attributesWithOverride(const PropertyDescriptor& other) const; private: diff --git a/JavaScriptCore/runtime/RegExpConstructor.cpp b/JavaScriptCore/runtime/RegExpConstructor.cpp index c609e08..e5c6909 100644 --- a/JavaScriptCore/runtime/RegExpConstructor.cpp +++ b/JavaScriptCore/runtime/RegExpConstructor.cpp @@ -132,6 +132,8 @@ void RegExpMatchesArray::fillArrayInstance(ExecState* exec) int start = d->lastOvector()[2 * i]; if (start >= 0) JSArray::put(exec, i, jsSubstring(exec, d->lastInput, start, d->lastOvector()[2 * i + 1] - start)); + else + JSArray::put(exec, i, jsUndefined()); } PutPropertySlot slot; diff --git a/JavaScriptCore/runtime/StringObject.cpp b/JavaScriptCore/runtime/StringObject.cpp index 7216d3a..f23a20d 100644 --- a/JavaScriptCore/runtime/StringObject.cpp +++ b/JavaScriptCore/runtime/StringObject.cpp @@ -79,12 +79,16 @@ bool StringObject::deleteProperty(ExecState* exec, const Identifier& propertyNam { if (propertyName == exec->propertyNames().length) return false; + bool isStrictUInt32; + unsigned i = propertyName.toStrictUInt32(&isStrictUInt32); + if (isStrictUInt32 && internalValue()->canGetIndex(i)) + return false; return JSObject::deleteProperty(exec, propertyName); } void StringObject::getOwnPropertyNames(ExecState* exec, PropertyNameArray& propertyNames) { - int size = internalValue()->value().size(); + int size = internalValue()->length(); for (int i = 0; i < size; ++i) propertyNames.add(Identifier(exec, UString::from(i))); return JSObject::getOwnPropertyNames(exec, propertyNames); diff --git a/JavaScriptCore/runtime/StringPrototype.cpp b/JavaScriptCore/runtime/StringPrototype.cpp index a0713b8..32f9e6b 100644 --- a/JavaScriptCore/runtime/StringPrototype.cpp +++ b/JavaScriptCore/runtime/StringPrototype.cpp @@ -224,7 +224,7 @@ static inline int localeCompare(const UString& a, const UString& b) JSValue JSC_HOST_CALL stringProtoFuncReplace(ExecState* exec, JSObject*, JSValue thisValue, const ArgList& args) { JSString* sourceVal = thisValue.toThisJSString(exec); - const UString& source = sourceVal->value(); + const UString& source = sourceVal->value(exec); JSValue pattern = args.at(0); @@ -281,7 +281,8 @@ JSValue JSC_HOST_CALL stringProtoFuncReplace(ExecState* exec, JSObject*, JSValue cachedCall.setArgument(i++, sourceVal); cachedCall.setThis(exec->globalThisValue()); - replacements.append(cachedCall.call().toString(cachedCall.newCallFrame())); + JSValue result = cachedCall.call(); + replacements.append(result.toString(cachedCall.newCallFrame(exec))); if (exec->hadException()) break; @@ -469,6 +470,11 @@ JSValue JSC_HOST_CALL stringProtoFuncLastIndexOf(ExecState* exec, JSObject*, JSV dpos = 0; else if (!(dpos <= len)) // true for NaN dpos = len; +#if PLATFORM(SYMBIAN) + // Work around for broken NaN compare operator + else if (isnan(dpos)) + dpos = len; +#endif return jsNumber(exec, s.rfind(u2, static_cast<int>(dpos))); } @@ -691,7 +697,7 @@ JSValue JSC_HOST_CALL stringProtoFuncSubstring(ExecState* exec, JSObject*, JSVal JSValue JSC_HOST_CALL stringProtoFuncToLowerCase(ExecState* exec, JSObject*, JSValue thisValue, const ArgList&) { JSString* sVal = thisValue.toThisJSString(exec); - const UString& s = sVal->value(); + const UString& s = sVal->value(exec); int sSize = s.size(); if (!sSize) @@ -725,7 +731,7 @@ JSValue JSC_HOST_CALL stringProtoFuncToLowerCase(ExecState* exec, JSObject*, JSV JSValue JSC_HOST_CALL stringProtoFuncToUpperCase(ExecState* exec, JSObject*, JSValue thisValue, const ArgList&) { JSString* sVal = thisValue.toThisJSString(exec); - const UString& s = sVal->value(); + const UString& s = sVal->value(exec); int sSize = s.size(); if (!sSize) diff --git a/JavaScriptCore/runtime/Structure.cpp b/JavaScriptCore/runtime/Structure.cpp index 65b62f9..e4c9ac3 100644 --- a/JavaScriptCore/runtime/Structure.cpp +++ b/JavaScriptCore/runtime/Structure.cpp @@ -77,6 +77,8 @@ static HashSet<Structure*>& ignoreSet = *(new HashSet<Structure*>); static HashSet<Structure*>& liveStructureSet = *(new HashSet<Structure*>); #endif +static int comparePropertyMapEntryIndices(const void* a, const void* b); + void Structure::dumpStatistics() { #if DUMP_STRUCTURE_ID_STATISTICS @@ -534,20 +536,47 @@ PassRefPtr<Structure> Structure::toUncacheableDictionaryTransition(Structure* st return toDictionaryTransition(structure, UncachedDictionaryKind); } -PassRefPtr<Structure> Structure::fromDictionaryTransition(Structure* structure) +PassRefPtr<Structure> Structure::flattenDictionaryStructure(JSObject* object) { - ASSERT(structure->isDictionary()); - - // Since dictionary Structures are not shared, and no opcodes specialize - // for them, we don't need to allocate a new Structure when transitioning - // to non-dictionary status. - - // FIMXE: We can make this more efficient by canonicalizing the Structure (draining the - // deleted offsets vector) before transitioning from dictionary. - if (!structure->m_propertyTable || !structure->m_propertyTable->deletedOffsets || structure->m_propertyTable->deletedOffsets->isEmpty()) - structure->m_dictionaryKind = NoneDictionaryKind; + ASSERT(isDictionary()); + if (isUncacheableDictionary()) { + ASSERT(m_propertyTable); + Vector<PropertyMapEntry*> sortedPropertyEntries(m_propertyTable->keyCount); + PropertyMapEntry** p = sortedPropertyEntries.data(); + unsigned entryCount = m_propertyTable->keyCount + m_propertyTable->deletedSentinelCount; + for (unsigned i = 1; i <= entryCount; i++) { + if (m_propertyTable->entries()[i].key) + *p++ = &m_propertyTable->entries()[i]; + } + size_t propertyCount = p - sortedPropertyEntries.data(); + qsort(sortedPropertyEntries.data(), propertyCount, sizeof(PropertyMapEntry*), comparePropertyMapEntryIndices); + sortedPropertyEntries.resize(propertyCount); + + // We now have the properties currently defined on this object + // in the order that they are expected to be in, but we need to + // reorder the storage, so we have to copy the current values out + Vector<JSValue> values(propertyCount); + unsigned anonymousSlotCount = m_propertyTable->anonymousSlotCount; + for (unsigned i = 0; i < propertyCount; i++) { + PropertyMapEntry* entry = sortedPropertyEntries[i]; + values[i] = object->getDirectOffset(entry->offset); + // Update property table to have the new property offsets + entry->offset = anonymousSlotCount + i; + entry->index = i; + } + + // Copy the original property values into their final locations + for (unsigned i = 0; i < propertyCount; i++) + object->putDirectOffset(anonymousSlotCount + i, values[i]); + + if (m_propertyTable->deletedOffsets) { + delete m_propertyTable->deletedOffsets; + m_propertyTable->deletedOffsets = 0; + } + } - return structure; + m_dictionaryKind = NoneDictionaryKind; + return this; } size_t Structure::addPropertyWithoutTransition(const Identifier& propertyName, unsigned attributes, JSCell* specificValue) @@ -556,8 +585,6 @@ size_t Structure::addPropertyWithoutTransition(const Identifier& propertyName, u materializePropertyMapIfNecessary(); m_isPinnedPropertyTable = true; - if (attributes & DontEnum) - m_hasNonEnumerableProperties = true; size_t offset = put(propertyName, attributes, specificValue); if (propertyStorageSize() > propertyStorageCapacity()) @@ -739,6 +766,9 @@ size_t Structure::put(const Identifier& propertyName, unsigned attributes, JSCel checkConsistency(); + if (attributes & DontEnum) + m_hasNonEnumerableProperties = true; + UString::Rep* rep = propertyName._ustring.rep(); if (!m_propertyTable) @@ -1003,7 +1033,7 @@ void Structure::rehashPropertyMapHashTable(unsigned newTableSize) checkConsistency(); } -static int comparePropertyMapEntryIndices(const void* a, const void* b) +int comparePropertyMapEntryIndices(const void* a, const void* b) { unsigned ia = static_cast<PropertyMapEntry* const*>(a)[0]->index; unsigned ib = static_cast<PropertyMapEntry* const*>(b)[0]->index; @@ -1025,6 +1055,7 @@ void Structure::getEnumerablePropertyNames(PropertyNameArray& propertyNames) int i = 0; unsigned entryCount = m_propertyTable->keyCount + m_propertyTable->deletedSentinelCount; for (unsigned k = 1; k <= entryCount; k++) { + ASSERT(m_hasNonEnumerableProperties || !(m_propertyTable->entries()[k].attributes & DontEnum)); if (m_propertyTable->entries()[k].key && !(m_propertyTable->entries()[k].attributes & DontEnum)) { PropertyMapEntry* value = &m_propertyTable->entries()[k]; int j; @@ -1112,6 +1143,7 @@ void Structure::checkConsistency() unsigned nonEmptyEntryCount = 0; for (unsigned c = 1; c <= m_propertyTable->keyCount + m_propertyTable->deletedSentinelCount; ++c) { + ASSERT(m_hasNonEnumerableProperties || !(m_propertyTable->entries()[c].attributes & DontEnum)); UString::Rep* rep = m_propertyTable->entries()[c].key; if (!rep) continue; diff --git a/JavaScriptCore/runtime/Structure.h b/JavaScriptCore/runtime/Structure.h index f355c53..c6b7d91 100644 --- a/JavaScriptCore/runtime/Structure.h +++ b/JavaScriptCore/runtime/Structure.h @@ -74,7 +74,8 @@ namespace JSC { static PassRefPtr<Structure> getterSetterTransition(Structure*); static PassRefPtr<Structure> toCacheableDictionaryTransition(Structure*); static PassRefPtr<Structure> toUncacheableDictionaryTransition(Structure*); - static PassRefPtr<Structure> fromDictionaryTransition(Structure*); + + PassRefPtr<Structure> flattenDictionaryStructure(JSObject*); ~Structure(); @@ -306,7 +307,7 @@ namespace JSC { TransitionTable* transitionTable = new TransitionTable; setTransitionTable(transitionTable); if (existingTransition) - add(make_pair(existingTransition->m_nameInPrevious.get(), existingTransition->m_attributesInPrevious), existingTransition, existingTransition->m_specificValueInPrevious); + add(std::make_pair(existingTransition->m_nameInPrevious.get(), existingTransition->m_attributesInPrevious), existingTransition, existingTransition->m_specificValueInPrevious); } } // namespace JSC diff --git a/JavaScriptCore/runtime/UString.cpp b/JavaScriptCore/runtime/UString.cpp index e66ca93..50d23c4 100644 --- a/JavaScriptCore/runtime/UString.cpp +++ b/JavaScriptCore/runtime/UString.cpp @@ -30,12 +30,12 @@ #include "Identifier.h" #include "Operations.h" #include <ctype.h> -#include <float.h> #include <limits.h> #include <limits> #include <math.h> #include <stdio.h> #include <stdlib.h> +#include <string.h> #include <wtf/ASCIICType.h> #include <wtf/Assertions.h> #include <wtf/MathExtras.h> @@ -44,9 +44,6 @@ #include <wtf/unicode/UTF8.h> #include <wtf/StringExtras.h> -#if HAVE(STRING_H) -#include <string.h> -#endif #if HAVE(STRINGS_H) #include <strings.h> #endif @@ -578,11 +575,33 @@ static PassRefPtr<UString::Rep> createRep(const char* c) } +static inline PassRefPtr<UString::Rep> createRep(const char* c, int length) +{ + if (!c) + return &UString::Rep::null(); + + if (!length) + return &UString::Rep::empty(); + + UChar* d; + if (!allocChars(length).getValue(d)) + return &UString::Rep::null(); + + for (int i = 0; i < length; i++) + d[i] = static_cast<unsigned char>(c[i]); // use unsigned char to zero-extend instead of sign-extend + return UString::Rep::create(d, length); +} + UString::UString(const char* c) : m_rep(createRep(c)) { } +UString::UString(const char* c, int length) + : m_rep(createRep(c, length)) +{ +} + UString::UString(const UChar* c, int length) { if (length == 0) @@ -1025,69 +1044,10 @@ UString UString::from(long l) UString UString::from(double d) { - // avoid ever printing -NaN, in JS conceptually there is only one NaN value - if (isnan(d)) - return "NaN"; - if (!d) - return "0"; // -0 -> "0" - - char buf[80]; - int decimalPoint; - int sign; - - char result[80]; - WTF::dtoa(result, d, 0, &decimalPoint, &sign, NULL); - int length = static_cast<int>(strlen(result)); - - int i = 0; - if (sign) - buf[i++] = '-'; - - if (decimalPoint <= 0 && decimalPoint > -6) { - buf[i++] = '0'; - buf[i++] = '.'; - for (int j = decimalPoint; j < 0; j++) - buf[i++] = '0'; - strcpy(buf + i, result); - } else if (decimalPoint <= 21 && decimalPoint > 0) { - if (length <= decimalPoint) { - strcpy(buf + i, result); - i += length; - for (int j = 0; j < decimalPoint - length; j++) - buf[i++] = '0'; - buf[i] = '\0'; - } else { - strncpy(buf + i, result, decimalPoint); - i += decimalPoint; - buf[i++] = '.'; - strcpy(buf + i, result + decimalPoint); - } - } else if (result[0] < '0' || result[0] > '9') - strcpy(buf + i, result); - else { - buf[i++] = result[0]; - if (length > 1) { - buf[i++] = '.'; - strcpy(buf + i, result + 1); - i += length - 1; - } - - buf[i++] = 'e'; - buf[i++] = (decimalPoint >= 0) ? '+' : '-'; - // decimalPoint can't be more than 3 digits decimal given the - // nature of float representation - int exponential = decimalPoint - 1; - if (exponential < 0) - exponential = -exponential; - if (exponential >= 100) - buf[i++] = static_cast<char>('0' + exponential / 100); - if (exponential >= 10) - buf[i++] = static_cast<char>('0' + (exponential % 100) / 10); - buf[i++] = static_cast<char>('0' + exponential % 10); - buf[i++] = '\0'; - } - - return UString(buf); + DtoaBuffer buffer; + unsigned length; + doubleToStringInJavaScriptFormat(d, buffer, &length); + return UString(buffer, length); } UString UString::spliceSubstringsWithSeparators(const Range* substringRanges, int rangeCount, const UString* separators, int separatorCount) const diff --git a/JavaScriptCore/runtime/UString.h b/JavaScriptCore/runtime/UString.h index c4dad2a..9b046f2 100644 --- a/JavaScriptCore/runtime/UString.h +++ b/JavaScriptCore/runtime/UString.h @@ -235,7 +235,10 @@ namespace JSC { public: UString(); + // Constructor for null-terminated ASCII string. UString(const char*); + // Constructor for non-null-terminated ASCII string. + UString(const char*, int length); UString(const UChar*, int length); UString(UChar*, int length, bool copy); diff --git a/JavaScriptCore/runtime/WeakRandom.h b/JavaScriptCore/runtime/WeakRandom.h new file mode 100644 index 0000000..ff3995e --- /dev/null +++ b/JavaScriptCore/runtime/WeakRandom.h @@ -0,0 +1,86 @@ +/* + * Copyright (C) 2009 Apple Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * + * Copyright (c) 2009 Ian C. Bullard + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, + * copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following + * conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES + * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + */ + +#ifndef WeakRandom_h +#define WeakRandom_h + +#include <limits.h> +#include <wtf/StdLibExtras.h> + +namespace JSC { + +class WeakRandom { +public: + WeakRandom(unsigned seed) + : m_low(seed ^ 0x49616E42) + , m_high(seed) + { + } + + double get() + { + return advance() / (UINT_MAX + 1.0); + } + +private: + unsigned advance() + { + m_high = (m_high << 16) + (m_high >> 16); + m_high += m_low; + m_low += m_high; + return m_high; + } + + unsigned m_low; + unsigned m_high; +}; + +} // namespace JSC + +#endif // WeakRandom_h diff --git a/JavaScriptCore/wscript b/JavaScriptCore/wscript index 7a5ba1b..356950f 100644 --- a/JavaScriptCore/wscript +++ b/JavaScriptCore/wscript @@ -29,7 +29,7 @@ import commands from settings import * -jscore_excludes = ['jsc.cpp', 'ucptable.cpp', 'GOwnPtr.cpp'] +jscore_excludes = ['jsc.cpp', 'ucptable.cpp'] jscore_excludes.extend(get_excludes(jscore_dir, ['*CF.cpp', '*Symbian.cpp'])) sources = [] diff --git a/JavaScriptCore/wtf/CurrentTime.h b/JavaScriptCore/wtf/CurrentTime.h index 31f1ec8..472770e 100644 --- a/JavaScriptCore/wtf/CurrentTime.h +++ b/JavaScriptCore/wtf/CurrentTime.h @@ -32,13 +32,32 @@ #ifndef CurrentTime_h #define CurrentTime_h +#include <time.h> + namespace WTF { - // Returns the current system (UTC) time in seconds, starting January 1, 1970. - // Precision varies depending on a platform but usually is as good or better + // Returns the current UTC time in seconds, counted from January 1, 1970. + // Precision varies depending on platform but is usually as good or better // than a millisecond. double currentTime(); + // Same thing, in milliseconds. + inline double currentTimeMS() + { + return currentTime() * 1000.0; + } + + inline void getLocalTime(const time_t* localTime, struct tm* localTM) + { + #if COMPILER(MSVC7) || COMPILER(MINGW) || PLATFORM(WINCE) + *localTM = *localtime(localTime); + #elif COMPILER(MSVC) + localtime_s(localTM, localTime); + #else + localtime_r(localTime, localTM); + #endif + } + } // namespace WTF using WTF::currentTime; diff --git a/JavaScriptCore/wtf/DateMath.cpp b/JavaScriptCore/wtf/DateMath.cpp index 2110432..187fa63 100644 --- a/JavaScriptCore/wtf/DateMath.cpp +++ b/JavaScriptCore/wtf/DateMath.cpp @@ -39,6 +39,33 @@ * other provisions required by the MPL or the GPL, as the case may be. * If you do not delete the provisions above, a recipient may use your * version of this file under any of the LGPL, the MPL or the GPL. + + * Copyright 2006-2008 the V8 project authors. All rights reserved. + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following + * disclaimer in the documentation and/or other materials provided + * with the distribution. + * * Neither the name of Google Inc. nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #include "config.h" @@ -61,10 +88,6 @@ #include <errno.h> #endif -#if PLATFORM(DARWIN) -#include <notify.h> -#endif - #if PLATFORM(WINCE) extern "C" size_t strftime(char * const s, const size_t maxsize, const char * const format, const struct tm * const t); extern "C" struct tm * localtime(const time_t *timer); @@ -78,8 +101,14 @@ extern "C" struct tm * localtime(const time_t *timer); #include <sys/timeb.h> #endif +#if USE(JSC) +#include "CallFrame.h" +#endif + #define NaN std::numeric_limits<double>::quiet_NaN() +using namespace WTF; + namespace WTF { /* Constants */ @@ -293,28 +322,6 @@ static int dateToDayInYear(int year, int month, int day) return yearday + monthday + day - 1; } -double getCurrentUTCTime() -{ - return floor(getCurrentUTCTimeWithMicroseconds()); -} - -// Returns current time in milliseconds since 1 Jan 1970. -double getCurrentUTCTimeWithMicroseconds() -{ - return currentTime() * 1000.0; -} - -void getLocalTime(const time_t* localTime, struct tm* localTM) -{ -#if COMPILER(MSVC7) || COMPILER(MINGW) || PLATFORM(WINCE) - *localTM = *localtime(localTime); -#elif COMPILER(MSVC) - localtime_s(localTM, localTime); -#else - localtime_r(localTime, localTM); -#endif -} - // There is a hard limit at 2038 that we currently do not have a workaround // for (rdar://problem/5052975). static inline int maximumYearForDST() @@ -328,7 +335,7 @@ static inline int minimumYearForDST() // greater than the max year minus 27 (2010), we want to use the max year // minus 27 instead, to ensure there is a range of 28 years that all years // can map to. - return std::min(msToYear(getCurrentUTCTime()), maximumYearForDST() - 27) ; + return std::min(msToYear(jsCurrentTime()), maximumYearForDST() - 27) ; } /* @@ -399,44 +406,10 @@ static int32_t calculateUTCOffset() return static_cast<int32_t>(utcOffset * 1000); } -#if PLATFORM(DARWIN) -static int32_t s_cachedUTCOffset; // In milliseconds. An assumption here is that access to an int32_t variable is atomic on platforms that take this code path. -static bool s_haveCachedUTCOffset; -static int s_notificationToken; -#endif - -/* - * Get the difference in milliseconds between this time zone and UTC (GMT) - * NOT including DST. - */ -double getUTCOffset() -{ -#if PLATFORM(DARWIN) - if (s_haveCachedUTCOffset) { - int notified; - uint32_t status = notify_check(s_notificationToken, ¬ified); - if (status == NOTIFY_STATUS_OK && !notified) - return s_cachedUTCOffset; - } -#endif - - int32_t utcOffset = calculateUTCOffset(); - -#if PLATFORM(DARWIN) - // Theoretically, it is possible that several threads will be executing this code at once, in which case we will have a race condition, - // and a newer value may be overwritten. In practice, time zones don't change that often. - s_cachedUTCOffset = utcOffset; -#endif - - return utcOffset; -} - /* - * Get the DST offset for the time passed in. Takes - * seconds (not milliseconds) and cannot handle dates before 1970 - * on some OS' + * Get the DST offset for the time passed in. */ -static double getDSTOffsetSimple(double localTimeSeconds, double utcOffset) +static double calculateDSTOffsetSimple(double localTimeSeconds, double utcOffset) { if (localTimeSeconds > maxUnixTime) localTimeSeconds = maxUnixTime; @@ -465,9 +438,9 @@ static double getDSTOffsetSimple(double localTimeSeconds, double utcOffset) } // Get the DST offset, given a time in UTC -static double getDSTOffset(double ms, double utcOffset) +static double calculateDSTOffset(double ms, double utcOffset) { - // On Mac OS X, the call to localtime (see getDSTOffsetSimple) will return historically accurate + // On Mac OS X, the call to localtime (see calculateDSTOffsetSimple) will return historically accurate // DST information (e.g. New Zealand did not have DST from 1946 to 1974) however the JavaScript // standard explicitly dictates that historical information should not be considered when // determining DST. For this reason we shift away from years that localtime can handle but would @@ -483,65 +456,18 @@ static double getDSTOffset(double ms, double utcOffset) ms = (day * msPerDay) + msToMilliseconds(ms); } - return getDSTOffsetSimple(ms / msPerSecond, utcOffset); -} - -double gregorianDateTimeToMS(const GregorianDateTime& t, double milliSeconds, bool inputIsUTC) -{ - int day = dateToDayInYear(t.year + 1900, t.month, t.monthDay); - double ms = timeToMS(t.hour, t.minute, t.second, milliSeconds); - double result = (day * msPerDay) + ms; - - if (!inputIsUTC) { // convert to UTC - double utcOffset = getUTCOffset(); - result -= utcOffset; - result -= getDSTOffset(result, utcOffset); - } - - return result; -} - -// input is UTC -void msToGregorianDateTime(double ms, bool outputIsUTC, GregorianDateTime& tm) -{ - double dstOff = 0.0; - double utcOff = 0.0; - if (!outputIsUTC) { - utcOff = getUTCOffset(); - dstOff = getDSTOffset(ms, utcOff); - ms += dstOff + utcOff; - } - - const int year = msToYear(ms); - tm.second = msToSeconds(ms); - tm.minute = msToMinutes(ms); - tm.hour = msToHours(ms); - tm.weekDay = msToWeekDay(ms); - tm.yearDay = dayInYear(ms, year); - tm.monthDay = dayInMonthFromDayInYear(tm.yearDay, isLeapYear(year)); - tm.month = monthFromDayInYear(tm.yearDay, isLeapYear(year)); - tm.year = year - 1900; - tm.isDST = dstOff != 0.0; - tm.utcOffset = static_cast<long>((dstOff + utcOff) / msPerSecond); - tm.timeZone = NULL; + return calculateDSTOffsetSimple(ms / msPerSecond, utcOffset); } void initializeDates() { #ifndef NDEBUG static bool alreadyInitialized; - ASSERT(!alreadyInitialized++); + ASSERT(!alreadyInitialized); + alreadyInitialized = true; #endif equivalentYearForDST(2000); // Need to call once to initialize a static used in this function. -#if PLATFORM(DARWIN) - // Register for a notification whenever the time zone changes. - uint32_t status = notify_register_check("com.apple.system.timezone", &s_notificationToken); - if (status == NOTIFY_STATUS_OK) { - s_cachedUTCOffset = calculateUTCOffset(); - s_haveCachedUTCOffset = true; - } -#endif } static inline double ymdhmsToSeconds(long year, int mon, int day, int hour, int minute, int second) @@ -622,8 +548,12 @@ static bool parseLong(const char* string, char** stopPosition, int base, long* r return true; } -double parseDateFromNullTerminatedCharacters(const char* dateString) +// Odd case where 'exec' is allowed to be 0, to accomodate a caller in WebCore. +static double parseDateFromNullTerminatedCharacters(const char* dateString, bool& haveTZ, int& offset) { + haveTZ = false; + offset = 0; + // This parses a date in the form: // Tuesday, 09-Nov-99 23:12:40 GMT // or @@ -824,9 +754,6 @@ double parseDateFromNullTerminatedCharacters(const char* dateString) } } - bool haveTZ = false; - int offset = 0; - // Don't fail if the time zone is missing. // Some websites omit the time zone (4275206). if (*dateString) { @@ -889,23 +816,25 @@ double parseDateFromNullTerminatedCharacters(const char* dateString) else year += 1900; } + + return ymdhmsToSeconds(year, month + 1, day, hour, minute, second) * msPerSecond; +} + +double parseDateFromNullTerminatedCharacters(const char* dateString) +{ + bool haveTZ; + int offset; + double ms = parseDateFromNullTerminatedCharacters(dateString, haveTZ, offset); + if (isnan(ms)) + return NaN; // fall back to local timezone if (!haveTZ) { - GregorianDateTime t; - t.monthDay = day; - t.month = month; - t.year = year - 1900; - t.isDST = -1; - t.second = second; - t.minute = minute; - t.hour = hour; - - // Use our gregorianDateTimeToMS() rather than mktime() as the latter can't handle the full year range. - return gregorianDateTimeToMS(t, 0, false); + double utcOffset = calculateUTCOffset(); + double dstOffset = calculateDSTOffset(ms, utcOffset); + offset = static_cast<int>((utcOffset + dstOffset) / msPerMinute); } - - return (ymdhmsToSeconds(year, month + 1, day, hour, minute, second) - (offset * 60.0)) * msPerSecond; + return ms - (offset * msPerMinute); } double timeClip(double t) @@ -916,6 +845,143 @@ double timeClip(double t) return NaN; return trunc(t); } +} // namespace WTF +#if USE(JSC) +namespace JSC { + +// Get the DST offset for the time passed in. +// +// NOTE: The implementation relies on the fact that no time zones have +// more than one daylight savings offset change per month. +// If this function is called with NaN it returns NaN. +static double getDSTOffset(ExecState* exec, double ms, double utcOffset) +{ + DSTOffsetCache& cache = exec->globalData().dstOffsetCache; + double start = cache.start; + double end = cache.end; + + if (start <= ms) { + // If the time fits in the cached interval, return the cached offset. + if (ms <= end) return cache.offset; + + // Compute a possible new interval end. + double newEnd = end + cache.increment; + + if (ms <= newEnd) { + double endOffset = calculateDSTOffset(newEnd, utcOffset); + if (cache.offset == endOffset) { + // If the offset at the end of the new interval still matches + // the offset in the cache, we grow the cached time interval + // and return the offset. + cache.end = newEnd; + cache.increment = msPerMonth; + return endOffset; + } else { + double offset = calculateDSTOffset(ms, utcOffset); + if (offset == endOffset) { + // The offset at the given time is equal to the offset at the + // new end of the interval, so that means that we've just skipped + // the point in time where the DST offset change occurred. Updated + // the interval to reflect this and reset the increment. + cache.start = ms; + cache.end = newEnd; + cache.increment = msPerMonth; + } else { + // The interval contains a DST offset change and the given time is + // before it. Adjust the increment to avoid a linear search for + // the offset change point and change the end of the interval. + cache.increment /= 3; + cache.end = ms; + } + // Update the offset in the cache and return it. + cache.offset = offset; + return offset; + } + } + } -} // namespace WTF + // Compute the DST offset for the time and shrink the cache interval + // to only contain the time. This allows fast repeated DST offset + // computations for the same time. + double offset = calculateDSTOffset(ms, utcOffset); + cache.offset = offset; + cache.start = ms; + cache.end = ms; + cache.increment = msPerMonth; + return offset; +} + +/* + * Get the difference in milliseconds between this time zone and UTC (GMT) + * NOT including DST. + */ +double getUTCOffset(ExecState* exec) +{ + double utcOffset = exec->globalData().cachedUTCOffset; + if (!isnan(utcOffset)) + return utcOffset; + exec->globalData().cachedUTCOffset = calculateUTCOffset(); + return exec->globalData().cachedUTCOffset; +} + +double gregorianDateTimeToMS(ExecState* exec, const GregorianDateTime& t, double milliSeconds, bool inputIsUTC) +{ + int day = dateToDayInYear(t.year + 1900, t.month, t.monthDay); + double ms = timeToMS(t.hour, t.minute, t.second, milliSeconds); + double result = (day * WTF::msPerDay) + ms; + + if (!inputIsUTC) { // convert to UTC + double utcOffset = getUTCOffset(exec); + result -= utcOffset; + result -= getDSTOffset(exec, result, utcOffset); + } + + return result; +} + +// input is UTC +void msToGregorianDateTime(ExecState* exec, double ms, bool outputIsUTC, GregorianDateTime& tm) +{ + double dstOff = 0.0; + double utcOff = 0.0; + if (!outputIsUTC) { + utcOff = getUTCOffset(exec); + dstOff = getDSTOffset(exec, ms, utcOff); + ms += dstOff + utcOff; + } + + const int year = msToYear(ms); + tm.second = msToSeconds(ms); + tm.minute = msToMinutes(ms); + tm.hour = msToHours(ms); + tm.weekDay = msToWeekDay(ms); + tm.yearDay = dayInYear(ms, year); + tm.monthDay = dayInMonthFromDayInYear(tm.yearDay, isLeapYear(year)); + tm.month = monthFromDayInYear(tm.yearDay, isLeapYear(year)); + tm.year = year - 1900; + tm.isDST = dstOff != 0.0; + tm.utcOffset = static_cast<long>((dstOff + utcOff) / WTF::msPerSecond); + tm.timeZone = NULL; +} + +double parseDateFromNullTerminatedCharacters(ExecState* exec, const char* dateString) +{ + ASSERT(exec); + bool haveTZ; + int offset; + double ms = WTF::parseDateFromNullTerminatedCharacters(dateString, haveTZ, offset); + if (isnan(ms)) + return NaN; + + // fall back to local timezone + if (!haveTZ) { + double utcOffset = getUTCOffset(exec); + double dstOffset = getDSTOffset(exec, ms, utcOffset); + offset = static_cast<int>((utcOffset + dstOffset) / WTF::msPerMinute); + } + return ms - (offset * WTF::msPerMinute); +} + +} // namespace JSC +#endif // USE(JSC) diff --git a/JavaScriptCore/wtf/DateMath.h b/JavaScriptCore/wtf/DateMath.h index 6110f76..6d4ac1e 100644 --- a/JavaScriptCore/wtf/DateMath.h +++ b/JavaScriptCore/wtf/DateMath.h @@ -42,27 +42,27 @@ #ifndef DateMath_h #define DateMath_h -#include <time.h> +#include <math.h> #include <string.h> +#include <time.h> +#include <wtf/CurrentTime.h> #include <wtf/Noncopyable.h> +#include <wtf/UnusedParam.h> namespace WTF { - -struct GregorianDateTime; - void initializeDates(); -void msToGregorianDateTime(double, bool outputIsUTC, GregorianDateTime&); -double gregorianDateTimeToMS(const GregorianDateTime&, double, bool inputIsUTC); -double getUTCOffset(); int equivalentYearForDST(int year); -double getCurrentUTCTime(); -double getCurrentUTCTimeWithMicroseconds(); -void getLocalTime(const time_t*, tm*); // Not really math related, but this is currently the only shared place to put these. -double parseDateFromNullTerminatedCharacters(const char*); +double parseDateFromNullTerminatedCharacters(const char* dateString); double timeClip(double); +inline double jsCurrentTime() +{ + // JavaScript doesn't recognize fractions of a millisecond. + return floor(WTF::currentTimeMS()); +} + const char * const weekdayName[7] = { "Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun" }; const char * const monthName[12] = { "Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec" }; @@ -74,9 +74,22 @@ const double msPerSecond = 1000.0; const double msPerMinute = 60.0 * 1000.0; const double msPerHour = 60.0 * 60.0 * 1000.0; const double msPerDay = 24.0 * 60.0 * 60.0 * 1000.0; +const double msPerMonth = 2592000000.0; -// Intentionally overridding the default tm of the system -// Tee members of tm differ on various operating systems. +} // namespace WTF + +#if USE(JSC) +namespace JSC { +class ExecState; +struct GregorianDateTime; + +void msToGregorianDateTime(ExecState*, double, bool outputIsUTC, GregorianDateTime&); +double gregorianDateTimeToMS(ExecState*, const GregorianDateTime&, double, bool inputIsUTC); +double getUTCOffset(ExecState*); +double parseDateFromNullTerminatedCharacters(ExecState*, const char* dateString); + +// Intentionally overridding the default tm of the system. +// The members of tm differ on various operating systems. struct GregorianDateTime : Noncopyable { GregorianDateTime() : second(0) @@ -98,7 +111,7 @@ struct GregorianDateTime : Noncopyable { delete [] timeZone; } - GregorianDateTime(const tm& inTm) + GregorianDateTime(ExecState* exec, const tm& inTm) : second(inTm.tm_sec) , minute(inTm.tm_min) , hour(inTm.tm_hour) @@ -109,10 +122,11 @@ struct GregorianDateTime : Noncopyable { , year(inTm.tm_year) , isDST(inTm.tm_isdst) { + UNUSED_PARAM(exec); #if HAVE(TM_GMTOFF) utcOffset = static_cast<int>(inTm.tm_gmtoff); #else - utcOffset = static_cast<int>(getUTCOffset() / msPerSecond + (isDST ? secondsPerHour : 0)); + utcOffset = static_cast<int>(getUTCOffset(exec) / WTF::msPerSecond + (isDST ? WTF::secondsPerHour : 0)); #endif #if HAVE(TM_ZONE) @@ -186,7 +200,7 @@ static inline int gmtoffset(const GregorianDateTime& t) { return t.utcOffset; } - -} // namespace WTF +} // namespace JSC +#endif // USE(JSC) #endif // DateMath_h diff --git a/JavaScriptCore/wtf/FastMalloc.h b/JavaScriptCore/wtf/FastMalloc.h index 541b05d..abe4a58 100644 --- a/JavaScriptCore/wtf/FastMalloc.h +++ b/JavaScriptCore/wtf/FastMalloc.h @@ -26,19 +26,13 @@ #include <stdlib.h> #include <new> -#if COMPILER(GCC) -#define WTF_FAST_MALLOC_EXPORT __attribute__((visibility("default"))) -#else -#define WTF_FAST_MALLOC_EXPORT -#endif - namespace WTF { // These functions call CRASH() if an allocation fails. - void* fastMalloc(size_t) WTF_FAST_MALLOC_EXPORT; + void* fastMalloc(size_t); void* fastZeroedMalloc(size_t); - void* fastCalloc(size_t numElements, size_t elementSize) WTF_FAST_MALLOC_EXPORT; - void* fastRealloc(void*, size_t) WTF_FAST_MALLOC_EXPORT; + void* fastCalloc(size_t numElements, size_t elementSize); + void* fastRealloc(void*, size_t); struct TryMallocReturnValue { TryMallocReturnValue(void* data) @@ -77,7 +71,7 @@ namespace WTF { TryMallocReturnValue tryFastCalloc(size_t n_elements, size_t element_size); TryMallocReturnValue tryFastRealloc(void* p, size_t n); - void fastFree(void*) WTF_FAST_MALLOC_EXPORT; + void fastFree(void*); #ifndef NDEBUG void fastMallocForbid(); @@ -222,14 +216,21 @@ using WTF::fastMallocAllow; // We musn't customize the global operator new and delete for the Qt port. #if !PLATFORM(QT) -WTF_PRIVATE_INLINE void* operator new(size_t size) { return fastMalloc(size); } +#if COMPILER(MSVC) +#pragma warning(push) +#pragma warning(disable: 4290) // Disable the C++ exception specification ignored warning. +#endif +WTF_PRIVATE_INLINE void* operator new(size_t size) throw (std::bad_alloc) { return fastMalloc(size); } WTF_PRIVATE_INLINE void* operator new(size_t size, const std::nothrow_t&) throw() { return fastMalloc(size); } -WTF_PRIVATE_INLINE void operator delete(void* p) { fastFree(p); } +WTF_PRIVATE_INLINE void operator delete(void* p) throw() { fastFree(p); } WTF_PRIVATE_INLINE void operator delete(void* p, const std::nothrow_t&) throw() { fastFree(p); } -WTF_PRIVATE_INLINE void* operator new[](size_t size) { return fastMalloc(size); } +WTF_PRIVATE_INLINE void* operator new[](size_t size) throw (std::bad_alloc) { return fastMalloc(size); } WTF_PRIVATE_INLINE void* operator new[](size_t size, const std::nothrow_t&) throw() { return fastMalloc(size); } -WTF_PRIVATE_INLINE void operator delete[](void* p) { fastFree(p); } +WTF_PRIVATE_INLINE void operator delete[](void* p) throw() { fastFree(p); } WTF_PRIVATE_INLINE void operator delete[](void* p, const std::nothrow_t&) throw() { fastFree(p); } +#if COMPILER(MSVC) +#pragma warning(pop) +#endif #endif diff --git a/JavaScriptCore/wtf/GOwnPtr.h b/JavaScriptCore/wtf/GOwnPtr.h deleted file mode 100644 index 4993348..0000000 --- a/JavaScriptCore/wtf/GOwnPtr.h +++ /dev/null @@ -1,98 +0,0 @@ -/* - * Copyright (C) 2006, 2007 Apple Inc. All rights reserved. - * Copyright (C) 2008 Collabora Ltd. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Library General Public - * License as published by the Free Software Foundation; either - * version 2 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Library General Public License for more details. - * - * You should have received a copy of the GNU Library General Public License - * along with this library; see the file COPYING.LIB. If not, write to - * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, - * Boston, MA 02110-1301, USA. - * - */ - -#ifndef GOwnPtr_h -#define GOwnPtr_h - -#include <algorithm> -#include <glib.h> -#include <wtf/Assertions.h> -#include <wtf/Noncopyable.h> - -namespace WTF { - template <typename T> inline void freeOwnedGPtr(T* ptr) { g_free(reinterpret_cast<void*>(ptr)); } - template<> void freeOwnedGPtr<GError>(GError*); - template<> void freeOwnedGPtr<GList>(GList*); - template<> void freeOwnedGPtr<GCond>(GCond*); - template<> void freeOwnedGPtr<GMutex>(GMutex*); - template<> void freeOwnedGPtr<GPatternSpec>(GPatternSpec*); - template<> void freeOwnedGPtr<GDir>(GDir*); - template<> void freeOwnedGPtr<GHashTable>(GHashTable*); - - template <typename T> class GOwnPtr : public Noncopyable { - public: - explicit GOwnPtr(T* ptr = 0) : m_ptr(ptr) { } - ~GOwnPtr() { freeOwnedGPtr(m_ptr); } - - T* get() const { return m_ptr; } - T* release() { T* ptr = m_ptr; m_ptr = 0; return ptr; } - T*& outPtr() { ASSERT(!m_ptr); return m_ptr; } - - void set(T* ptr) { ASSERT(!ptr || m_ptr != ptr); freeOwnedGPtr(m_ptr); m_ptr = ptr; } - void clear() { freeOwnedGPtr(m_ptr); m_ptr = 0; } - - T& operator*() const { ASSERT(m_ptr); return *m_ptr; } - T* operator->() const { ASSERT(m_ptr); return m_ptr; } - - bool operator!() const { return !m_ptr; } - - // This conversion operator allows implicit conversion to bool but not to other integer types. - typedef T* GOwnPtr::*UnspecifiedBoolType; - operator UnspecifiedBoolType() const { return m_ptr ? &GOwnPtr::m_ptr : 0; } - - void swap(GOwnPtr& o) { std::swap(m_ptr, o.m_ptr); } - - private: - T* m_ptr; - }; - - template <typename T> inline void swap(GOwnPtr<T>& a, GOwnPtr<T>& b) { a.swap(b); } - - template <typename T, typename U> inline bool operator==(const GOwnPtr<T>& a, U* b) - { - return a.get() == b; - } - - template <typename T, typename U> inline bool operator==(T* a, const GOwnPtr<U>& b) - { - return a == b.get(); - } - - template <typename T, typename U> inline bool operator!=(const GOwnPtr<T>& a, U* b) - { - return a.get() != b; - } - - template <typename T, typename U> inline bool operator!=(T* a, const GOwnPtr<U>& b) - { - return a != b.get(); - } - - template <typename T> inline typename GOwnPtr<T>::PtrType getPtr(const GOwnPtr<T>& p) - { - return p.get(); - } - -} // namespace WTF - -using WTF::GOwnPtr; - -#endif // GOwnPtr_h diff --git a/JavaScriptCore/wtf/HashMap.h b/JavaScriptCore/wtf/HashMap.h index 3de5ee6..ece3812 100644 --- a/JavaScriptCore/wtf/HashMap.h +++ b/JavaScriptCore/wtf/HashMap.h @@ -83,6 +83,23 @@ namespace WTF { MappedType take(const KeyType&); // efficient combination of get with remove + // An alternate version of find() that finds the object by hashing and comparing + // with some other type, to avoid the cost of type conversion. HashTranslator + // must have the following function members: + // static unsigned hash(const T&); + // static bool equal(const ValueType&, const T&); + template<typename T, typename HashTranslator> iterator find(const T&); + template<typename T, typename HashTranslator> const_iterator find(const T&) const; + template<typename T, typename HashTranslator> bool contains(const T&) const; + + // An alternate version of add() that finds the object by hashing and comparing + // with some other type, to avoid the cost of type conversion if the object is already + // in the table. HashTranslator must have the following function members: + // static unsigned hash(const T&); + // static bool equal(const ValueType&, const T&); + // static translate(ValueType&, const T&, unsigned hashCode); + template<typename T, typename HashTranslator> pair<iterator, bool> add(const T&, const MappedType&); + private: pair<iterator, bool> inlineAdd(const KeyType&, const MappedType&); @@ -107,6 +124,19 @@ namespace WTF { } }; + template<typename ValueType, typename ValueTraits, typename T, typename Translator> + struct HashMapTranslatorAdapter { + typedef typename ValueType::first_type KeyType; + typedef typename ValueType::second_type MappedType; + + static unsigned hash(const T& key) { return Translator::hash(key); } + static bool equal(const KeyType& a, const T& b) { return Translator::equal(a, b); } + static void translate(ValueType& location, const T& key, const MappedType&, unsigned hashCode) + { + Translator::translate(location.first, key, hashCode); + } + }; + template<typename T, typename U, typename V, typename W, typename X> inline void HashMap<T, U, V, W, X>::swap(HashMap& other) { @@ -174,6 +204,33 @@ namespace WTF { } template<typename T, typename U, typename V, typename W, typename X> + template<typename TYPE, typename HashTranslator> + inline typename HashMap<T, U, V, W, X>::iterator + HashMap<T, U, V, W, X>::find(const TYPE& value) + { + typedef HashMapTranslatorAdapter<ValueType, ValueTraits, TYPE, HashTranslator> Adapter; + return m_impl.template find<TYPE, Adapter>(value); + } + + template<typename T, typename U, typename V, typename W, typename X> + template<typename TYPE, typename HashTranslator> + inline typename HashMap<T, U, V, W, X>::const_iterator + HashMap<T, U, V, W, X>::find(const TYPE& value) const + { + typedef HashMapTranslatorAdapter<ValueType, ValueTraits, TYPE, HashTranslator> Adapter; + return m_impl.template find<TYPE, Adapter>(value); + } + + template<typename T, typename U, typename V, typename W, typename X> + template<typename TYPE, typename HashTranslator> + inline bool + HashMap<T, U, V, W, X>::contains(const TYPE& value) const + { + typedef HashMapTranslatorAdapter<ValueType, ValueTraits, TYPE, HashTranslator> Adapter; + return m_impl.template contains<TYPE, Adapter>(value); + } + + template<typename T, typename U, typename V, typename W, typename X> inline pair<typename HashMap<T, U, V, W, X>::iterator, bool> HashMap<T, U, V, W, X>::inlineAdd(const KeyType& key, const MappedType& mapped) { @@ -194,6 +251,15 @@ namespace WTF { } template<typename T, typename U, typename V, typename W, typename X> + template<typename TYPE, typename HashTranslator> + pair<typename HashMap<T, U, V, W, X>::iterator, bool> + HashMap<T, U, V, W, X>::add(const TYPE& key, const MappedType& value) + { + typedef HashMapTranslatorAdapter<ValueType, ValueTraits, TYPE, HashTranslator> Adapter; + return m_impl.template addPassingHashCode<TYPE, MappedType, Adapter>(key, value); + } + + template<typename T, typename U, typename V, typename W, typename X> pair<typename HashMap<T, U, V, W, X>::iterator, bool> HashMap<T, U, V, W, X>::add(const KeyType& key, const MappedType& mapped) { diff --git a/JavaScriptCore/wtf/HashSet.h b/JavaScriptCore/wtf/HashSet.h index f4e2cf7..0d7b4bb 100644 --- a/JavaScriptCore/wtf/HashSet.h +++ b/JavaScriptCore/wtf/HashSet.h @@ -81,7 +81,7 @@ namespace WTF { // An alternate version of add() that finds the object by hashing and comparing // with some other type, to avoid the cost of type conversion if the object is already - // in the table. HashTranslator must have the following methods: + // in the table. HashTranslator must have the following function members: // static unsigned hash(const T&); // static bool equal(const ValueType&, const T&); // static translate(ValueType&, const T&, unsigned hashCode); diff --git a/JavaScriptCore/wtf/ListHashSet.h b/JavaScriptCore/wtf/ListHashSet.h index 38cc998..54ed36b 100644 --- a/JavaScriptCore/wtf/ListHashSet.h +++ b/JavaScriptCore/wtf/ListHashSet.h @@ -51,7 +51,7 @@ namespace WTF { template<typename ValueArg> struct ListHashSetNodeAllocator; template<typename ValueArg, typename HashArg> struct ListHashSetNodeHashFunctions; - template<typename ValueArg, typename HashArg = typename DefaultHash<ValueArg>::Hash> class ListHashSet { + template<typename ValueArg, typename HashArg = typename DefaultHash<ValueArg>::Hash> class ListHashSet : public FastAllocBase { private: typedef ListHashSetNode<ValueArg> Node; typedef ListHashSetNodeAllocator<ValueArg> NodeAllocator; diff --git a/JavaScriptCore/wtf/MainThread.cpp b/JavaScriptCore/wtf/MainThread.cpp index e999094..40a4ae5 100644 --- a/JavaScriptCore/wtf/MainThread.cpp +++ b/JavaScriptCore/wtf/MainThread.cpp @@ -39,10 +39,12 @@ namespace WTF { struct FunctionWithContext { MainThreadFunction* function; void* context; + ThreadCondition* syncFlag; - FunctionWithContext(MainThreadFunction* function = 0, void* context = 0) + FunctionWithContext(MainThreadFunction* function = 0, void* context = 0, ThreadCondition* syncFlag = 0) : function(function) , context(context) + , syncFlag(syncFlag) { } }; @@ -92,6 +94,8 @@ void dispatchFunctionsFromMainThread() } invocation.function(invocation.context); + if (invocation.syncFlag) + invocation.syncFlag->signal(); // If we are running accumulated functions for too long so UI may become unresponsive, we need to // yield so the user input can be processed. Otherwise user may not be able to even close the window. @@ -117,6 +121,24 @@ void callOnMainThread(MainThreadFunction* function, void* context) scheduleDispatchFunctionsOnMainThread(); } +void callOnMainThreadAndWait(MainThreadFunction* function, void* context) +{ + ASSERT(function); + + if (isMainThread()) { + function(context); + return; + } + + ThreadCondition syncFlag; + Mutex& functionQueueMutex = mainThreadFunctionQueueMutex(); + MutexLocker locker(functionQueueMutex); + functionQueue().append(FunctionWithContext(function, context, &syncFlag)); + if (functionQueue().size() == 1) + scheduleDispatchFunctionsOnMainThread(); + syncFlag.wait(functionQueueMutex); +} + void setMainThreadCallbacksPaused(bool paused) { ASSERT(isMainThread()); diff --git a/JavaScriptCore/wtf/MainThread.h b/JavaScriptCore/wtf/MainThread.h index 01ce804..8c0275b 100644 --- a/JavaScriptCore/wtf/MainThread.h +++ b/JavaScriptCore/wtf/MainThread.h @@ -38,6 +38,9 @@ typedef void MainThreadFunction(void*); void callOnMainThread(MainThreadFunction*, void* context); +// Blocks the thread until the call finishes on the main thread. Misusing this can easily cause deadlocks. +void callOnMainThreadAndWait(MainThreadFunction*, void* context); + void setMainThreadCallbacksPaused(bool paused); // Must be called from the main thread (Darwin is an exception to this rule). @@ -52,6 +55,7 @@ void dispatchFunctionsFromMainThread(); } // namespace WTF using WTF::callOnMainThread; +using WTF::callOnMainThreadAndWait; using WTF::setMainThreadCallbacksPaused; #endif // MainThread_h diff --git a/JavaScriptCore/wtf/MathExtras.h b/JavaScriptCore/wtf/MathExtras.h index 556230e..ee2110e 100644 --- a/JavaScriptCore/wtf/MathExtras.h +++ b/JavaScriptCore/wtf/MathExtras.h @@ -26,6 +26,7 @@ #ifndef WTF_MathExtras_h #define WTF_MathExtras_h +#include <float.h> #include <math.h> #include <stdlib.h> @@ -43,11 +44,6 @@ #include <stdlib.h> #endif #include <limits> - -#if HAVE(FLOAT_H) -#include <float.h> -#endif - #endif #ifndef M_PI diff --git a/JavaScriptCore/wtf/MessageQueue.h b/JavaScriptCore/wtf/MessageQueue.h index 9c9a4a78..48bd10a 100644 --- a/JavaScriptCore/wtf/MessageQueue.h +++ b/JavaScriptCore/wtf/MessageQueue.h @@ -44,23 +44,27 @@ namespace WTF { MessageQueueMessageReceived, // A message was successfully received and returned. }; + // The queue takes ownership of messages and transfer it to the new owner + // when messages are fetched from the queue. + // Essentially, MessageQueue acts as a queue of OwnPtr<DataType>. template<typename DataType> class MessageQueue : public Noncopyable { public: MessageQueue() : m_killed(false) { } - - void append(const DataType&); - bool appendAndCheckEmpty(const DataType&); - void prepend(const DataType&); - bool waitForMessage(DataType&); + ~MessageQueue(); + + void append(PassOwnPtr<DataType>); + bool appendAndCheckEmpty(PassOwnPtr<DataType>); + void prepend(PassOwnPtr<DataType>); + + PassOwnPtr<DataType> waitForMessage(); + PassOwnPtr<DataType> tryGetMessage(); template<typename Predicate> - MessageQueueWaitResult waitForMessageFilteredWithTimeout(DataType&, Predicate&, double absoluteTime); + PassOwnPtr<DataType> waitForMessageFilteredWithTimeout(MessageQueueWaitResult&, Predicate&, double absoluteTime); template<typename Predicate> void removeIf(Predicate&); - bool tryGetMessage(DataType&); - void kill(); bool killed() const; @@ -70,86 +74,98 @@ namespace WTF { static double infiniteTime() { return std::numeric_limits<double>::max(); } private: - static bool alwaysTruePredicate(DataType&) { return true; } + static bool alwaysTruePredicate(DataType*) { return true; } mutable Mutex m_mutex; ThreadCondition m_condition; - Deque<DataType> m_queue; + Deque<DataType*> m_queue; bool m_killed; }; template<typename DataType> - inline void MessageQueue<DataType>::append(const DataType& message) + MessageQueue<DataType>::~MessageQueue() + { + deleteAllValues(m_queue); + } + + template<typename DataType> + inline void MessageQueue<DataType>::append(PassOwnPtr<DataType> message) { MutexLocker lock(m_mutex); - m_queue.append(message); + m_queue.append(message.release()); m_condition.signal(); } // Returns true if the queue was empty before the item was added. template<typename DataType> - inline bool MessageQueue<DataType>::appendAndCheckEmpty(const DataType& message) + inline bool MessageQueue<DataType>::appendAndCheckEmpty(PassOwnPtr<DataType> message) { MutexLocker lock(m_mutex); bool wasEmpty = m_queue.isEmpty(); - m_queue.append(message); + m_queue.append(message.release()); m_condition.signal(); return wasEmpty; } template<typename DataType> - inline void MessageQueue<DataType>::prepend(const DataType& message) + inline void MessageQueue<DataType>::prepend(PassOwnPtr<DataType> message) { MutexLocker lock(m_mutex); - m_queue.prepend(message); + m_queue.prepend(message.release()); m_condition.signal(); } template<typename DataType> - inline bool MessageQueue<DataType>::waitForMessage(DataType& result) + inline PassOwnPtr<DataType> MessageQueue<DataType>::waitForMessage() { - MessageQueueWaitResult exitReason = waitForMessageFilteredWithTimeout(result, MessageQueue<DataType>::alwaysTruePredicate, infiniteTime()); + MessageQueueWaitResult exitReason; + PassOwnPtr<DataType> result = waitForMessageFilteredWithTimeout(exitReason, MessageQueue<DataType>::alwaysTruePredicate, infiniteTime()); ASSERT(exitReason == MessageQueueTerminated || exitReason == MessageQueueMessageReceived); - return exitReason == MessageQueueMessageReceived; + return result; } template<typename DataType> template<typename Predicate> - inline MessageQueueWaitResult MessageQueue<DataType>::waitForMessageFilteredWithTimeout(DataType& result, Predicate& predicate, double absoluteTime) + inline PassOwnPtr<DataType> MessageQueue<DataType>::waitForMessageFilteredWithTimeout(MessageQueueWaitResult& result, Predicate& predicate, double absoluteTime) { MutexLocker lock(m_mutex); bool timedOut = false; - DequeConstIterator<DataType> found = m_queue.end(); + DequeConstIterator<DataType*> found = m_queue.end(); while (!m_killed && !timedOut && (found = m_queue.findIf(predicate)) == m_queue.end()) timedOut = !m_condition.timedWait(m_mutex, absoluteTime); ASSERT(!timedOut || absoluteTime != infiniteTime()); - if (m_killed) - return MessageQueueTerminated; + if (m_killed) { + result = MessageQueueTerminated; + return 0; + } - if (timedOut) - return MessageQueueTimeout; + if (timedOut) { + result = MessageQueueTimeout; + return 0; + } ASSERT(found != m_queue.end()); - result = *found; + DataType* message = *found; m_queue.remove(found); - return MessageQueueMessageReceived; + result = MessageQueueMessageReceived; + return message; } template<typename DataType> - inline bool MessageQueue<DataType>::tryGetMessage(DataType& result) + inline PassOwnPtr<DataType> MessageQueue<DataType>::tryGetMessage() { MutexLocker lock(m_mutex); if (m_killed) - return false; + return 0; if (m_queue.isEmpty()) - return false; + return 0; - result = m_queue.first(); + DataType* message = m_queue.first(); m_queue.removeFirst(); - return true; + return message; } template<typename DataType> @@ -157,9 +173,15 @@ namespace WTF { inline void MessageQueue<DataType>::removeIf(Predicate& predicate) { MutexLocker lock(m_mutex); - DequeConstIterator<DataType> found = m_queue.end(); - while ((found = m_queue.findIf(predicate)) != m_queue.end()) { + // See bug 31657 for why this loop looks so weird + while (true) { + DequeConstIterator<DataType*> found = m_queue.findIf(predicate); + if (found == m_queue.end()) + break; + + DataType* message = *found; m_queue.remove(found); + delete message; } } diff --git a/JavaScriptCore/wtf/Platform.h b/JavaScriptCore/wtf/Platform.h index 20c030b..091ba4f 100644 --- a/JavaScriptCore/wtf/Platform.h +++ b/JavaScriptCore/wtf/Platform.h @@ -47,6 +47,11 @@ #elif !defined(MAC_OS_X_VERSION_10_6) || MAC_OS_X_VERSION_MAX_ALLOWED < MAC_OS_X_VERSION_10_6 #define BUILDING_ON_LEOPARD 1 #endif +#if !defined(MAC_OS_X_VERSION_10_5) || MAC_OS_X_VERSION_MIN_REQUIRED < MAC_OS_X_VERSION_10_5 +#define TARGETING_TIGER 1 +#elif !defined(MAC_OS_X_VERSION_10_6) || MAC_OS_X_VERSION_MIN_REQUIRED < MAC_OS_X_VERSION_10_6 +#define TARGETING_LEOPARD 1 +#endif #include <TargetConditionals.h> #endif @@ -136,19 +141,15 @@ /* PLATFORM(CHROMIUM) */ /* PLATFORM(QT) */ +/* PLATFORM(WX) */ /* PLATFORM(GTK) */ +/* PLATFORM(HAIKU) */ /* PLATFORM(MAC) */ /* PLATFORM(WIN) */ #if defined(BUILDING_CHROMIUM__) #define WTF_PLATFORM_CHROMIUM 1 #elif defined(BUILDING_QT__) #define WTF_PLATFORM_QT 1 - -/* PLATFORM(KDE) */ -#if defined(BUILDING_KDE__) -#define WTF_PLATFORM_KDE 1 -#endif - #elif defined(BUILDING_WX__) #define WTF_PLATFORM_WX 1 #elif defined(BUILDING_GTK__) @@ -224,6 +225,16 @@ #define WTF_PLATFORM_BIG_ENDIAN 1 #endif +/* PLATFORM(SPARC32) */ +#if defined(__sparc) && !defined(__arch64__) || defined(__sparcv8) +#define WTF_PLATFORM_SPARC32 1 +#define WTF_PLATFORM_BIG_ENDIAN 1 +#endif + +#if PLATFORM(SPARC32) || PLATFORM(SPARC64) +#define WTF_PLATFORM_SPARC +#endif + /* PLATFORM(PPC64) */ #if defined(__ppc64__) \ || defined(__PPC64__) @@ -354,6 +365,16 @@ #define WTF_PLATFORM_X86_64 1 #endif +/* PLATFORM(IA64) */ +#if defined(__ia64__) +#define WTF_PLATFORM_IA64 1 +#endif + +/* PLATFORM(ALPHA) */ +#if defined(__alpha__) +#define WTF_PLATFORM_ALPHA 1 +#endif + /* PLATFORM(SH4) */ #if defined(__SH4__) #define WTF_PLATFORM_SH4 1 @@ -377,6 +398,8 @@ # if Q_BYTE_ORDER == Q_BIG_EDIAN # define WTF_PLATFORM_BIG_ENDIAN 1 # endif + +# include <ce_time.h> #endif /* Compiler */ @@ -423,7 +446,7 @@ #define WTF_COMPILER_WINSCW 1 #endif -#if (PLATFORM(IPHONE) || PLATFORM(MAC) || PLATFORM(WIN)) && !defined(ENABLE_JSC_MULTIPLE_THREADS) +#if (PLATFORM(IPHONE) || PLATFORM(MAC) || PLATFORM(WIN) || (PLATFORM(QT) && PLATFORM(DARWIN))) && !defined(ENABLE_JSC_MULTIPLE_THREADS) #define ENABLE_JSC_MULTIPLE_THREADS 1 #endif @@ -459,8 +482,7 @@ #endif /* PLATFORM(WINCE) && !PLATFORM(QT) */ -/* for Unicode, KDE uses Qt */ -#if PLATFORM(KDE) || PLATFORM(QT) +#if PLATFORM(QT) #define WTF_USE_QT4_UNICODE 1 #elif PLATFORM(WINCE) #define WTF_USE_WINCE_UNICODE 1 @@ -493,6 +515,10 @@ #define HAVE_PTHREAD_RWLOCK 1 #endif +#if PLATFORM(QT) && PLATFORM(DARWIN) +#define WTF_PLATFORM_CF 1 +#endif + #if PLATFORM(IPHONE) #define ENABLE_CONTEXT_MENUS 0 #define ENABLE_DRAG_SUPPORT 0 @@ -520,7 +546,10 @@ // This prevents unnecessary invals. #define ENABLE_TEXT_CARET 1 #define ENABLE_JAVASCRIPT_DEBUGGER 0 +<<<<<<< HEAD:JavaScriptCore/wtf/Platform.h #define ENABLE_ORIENTATION_EVENTS 1 +======= +>>>>>>> webkit.org at r51976:JavaScriptCore/wtf/Platform.h #endif #if PLATFORM(WIN) @@ -529,6 +558,9 @@ #if PLATFORM(WX) #define ENABLE_ASSEMBLER 1 +#if PLATFORM(DARWIN) +#define WTF_PLATFORM_CF 1 +#endif #endif #if PLATFORM(GTK) @@ -577,7 +609,7 @@ #define HAVE_SYS_TIME_H 1 #define HAVE_SYS_TIMEB_H 1 -#if !defined(BUILDING_ON_TIGER) && !defined(BUILDING_ON_LEOPARD) && !PLATFORM(IPHONE) +#if !defined(BUILDING_ON_TIGER) && !defined(BUILDING_ON_LEOPARD) && !PLATFORM(IPHONE) && !PLATFORM(QT) #define HAVE_MADV_FREE_REUSE 1 #define HAVE_MADV_FREE 1 #define HAVE_PTHREAD_SETNAME_NP 1 @@ -589,7 +621,6 @@ #elif PLATFORM(WIN_OS) -#define HAVE_FLOAT_H 1 #if PLATFORM(WINCE) #define HAVE_ERRNO_H 0 #else @@ -741,7 +772,7 @@ #endif #if !defined(WTF_USE_JSVALUE64) && !defined(WTF_USE_JSVALUE32) && !defined(WTF_USE_JSVALUE32_64) -#if PLATFORM(X86_64) && (PLATFORM(DARWIN) || PLATFORM(LINUX) || PLATFORM(WIN_OS)) +#if (PLATFORM(X86_64) && (PLATFORM(UNIX) || PLATFORM(WIN_OS))) || PLATFORM(IA64) || PLATFORM(ALPHA) #define WTF_USE_JSVALUE64 1 #elif PLATFORM(ARM) || PLATFORM(PPC64) #define WTF_USE_JSVALUE32 1 @@ -769,14 +800,18 @@ on MinGW. See https://bugs.webkit.org/show_bug.cgi?id=29268 */ #define WTF_USE_JIT_STUB_ARGUMENT_VA_LIST 1 #elif PLATFORM(ARM_THUMB2) && PLATFORM(IPHONE) #define ENABLE_JIT 1 - #define ENABLE_JIT_OPTIMIZE_NATIVE_CALL 0 /* The JIT is tested & working on x86 Windows */ #elif PLATFORM(X86) && PLATFORM(WIN) #define ENABLE_JIT 1 #endif #if PLATFORM(QT) -#if PLATFORM(X86) && PLATFORM(WIN_OS) && COMPILER(MINGW) && GCC_VERSION >= 40100 +#if PLATFORM(X86_64) && PLATFORM(DARWIN) + #define ENABLE_JIT 1 +#elif PLATFORM(X86) && PLATFORM(DARWIN) + #define ENABLE_JIT 1 + #define WTF_USE_JIT_STUB_ARGUMENT_VA_LIST 1 +#elif PLATFORM(X86) && PLATFORM(WIN_OS) && COMPILER(MINGW) && GCC_VERSION >= 40100 #define ENABLE_JIT 1 #define WTF_USE_JIT_STUB_ARGUMENT_VA_LIST 1 #elif PLATFORM(X86) && PLATFORM(WIN_OS) && COMPILER(MSVC) @@ -787,9 +822,6 @@ on MinGW. See https://bugs.webkit.org/show_bug.cgi?id=29268 */ #define WTF_USE_JIT_STUB_ARGUMENT_VA_LIST 1 #elif PLATFORM(ARM_TRADITIONAL) && PLATFORM(LINUX) #define ENABLE_JIT 1 - #if PLATFORM(ARM_THUMB2) - #define ENABLE_JIT_OPTIMIZE_NATIVE_CALL 0 - #endif #endif #endif /* PLATFORM(QT) */ @@ -894,6 +926,10 @@ on MinGW. See https://bugs.webkit.org/show_bug.cgi?id=29268 */ #define WTF_USE_ACCELERATED_COMPOSITING 1 #endif +#if PLATFORM(WIN) +#define WTF_USE_ACCELERATED_COMPOSITING 0 +#endif + #if COMPILER(GCC) #define WARN_UNUSED_RETURN __attribute__ ((warn_unused_result)) #else @@ -907,4 +943,6 @@ on MinGW. See https://bugs.webkit.org/show_bug.cgi?id=29268 */ /* Set up a define for a common error that is intended to cause a build error -- thus the space after Error. */ #define WTF_PLATFORM_CFNETWORK Error USE_macro_should_be_used_with_CFNETWORK +#define ENABLE_JSC_ZOMBIES 0 + #endif /* WTF_Platform_h */ diff --git a/JavaScriptCore/wtf/PtrAndFlags.h b/JavaScriptCore/wtf/PtrAndFlags.h index 5d0bd2a..1e1bee0 100644 --- a/JavaScriptCore/wtf/PtrAndFlags.h +++ b/JavaScriptCore/wtf/PtrAndFlags.h @@ -68,7 +68,7 @@ namespace WTF { PtrAndFlags(T* ptr) { PtrAndFlagsBase<T, FlagEnum>::m_ptrAndFlags = 0; - set(ptr); + PtrAndFlagsBase<T, FlagEnum>::set(ptr); } }; } // namespace WTF diff --git a/JavaScriptCore/wtf/StdLibExtras.h b/JavaScriptCore/wtf/StdLibExtras.h index c9b5742..dd90c85 100644 --- a/JavaScriptCore/wtf/StdLibExtras.h +++ b/JavaScriptCore/wtf/StdLibExtras.h @@ -48,6 +48,10 @@ // NULL can cause compiler problems, especially in cases of multiple inheritance. #define OBJECT_OFFSETOF(class, field) (reinterpret_cast<ptrdiff_t>(&(reinterpret_cast<class*>(0x4000)->field)) - 0x4000) +// STRINGIZE: Can convert any value to quoted string, even expandable macros +#define STRINGIZE(exp) #exp +#define STRINGIZE_VALUE_OF(exp) STRINGIZE(exp) + namespace WTF { /* diff --git a/JavaScriptCore/wtf/StringExtras.h b/JavaScriptCore/wtf/StringExtras.h index 1120d65..50efe35 100644 --- a/JavaScriptCore/wtf/StringExtras.h +++ b/JavaScriptCore/wtf/StringExtras.h @@ -85,7 +85,7 @@ inline int strcasecmp(const char* s1, const char* s2) #endif -#if PLATFORM(WIN_OS) || PLATFORM(LINUX) +#if PLATFORM(WIN_OS) || PLATFORM(LINUX) || PLATFORM(SOLARIS) inline char* strnstr(const char* buffer, const char* target, size_t bufferLength) { diff --git a/JavaScriptCore/wtf/Threading.h b/JavaScriptCore/wtf/Threading.h index b24e7c8..2a2e9c4 100644 --- a/JavaScriptCore/wtf/Threading.h +++ b/JavaScriptCore/wtf/Threading.h @@ -86,7 +86,7 @@ #if USE(PTHREADS) #include <pthread.h> #elif PLATFORM(GTK) -#include <wtf/GOwnPtr.h> +#include <wtf/gtk/GOwnPtr.h> typedef struct _GMutex GMutex; typedef struct _GCond GCond; #endif diff --git a/JavaScriptCore/wtf/ThreadingPthreads.cpp b/JavaScriptCore/wtf/ThreadingPthreads.cpp index 6cad5e3..fe7b26a 100644 --- a/JavaScriptCore/wtf/ThreadingPthreads.cpp +++ b/JavaScriptCore/wtf/ThreadingPthreads.cpp @@ -56,7 +56,7 @@ typedef HashMap<ThreadIdentifier, pthread_t> ThreadMap; static Mutex* atomicallyInitializedStaticMutex; #if !PLATFORM(DARWIN) || PLATFORM(CHROMIUM) -static ThreadIdentifier mainThreadIdentifier; // The thread that was the first to call initializeThreading(), which must be the main thread. +static pthread_t mainThread; // The thread that was the first to call initializeThreading(), which must be the main thread. #endif static Mutex& threadMapMutex() @@ -72,7 +72,7 @@ void initializeThreading() threadMapMutex(); initializeRandomNumberGenerator(); #if !PLATFORM(DARWIN) || PLATFORM(CHROMIUM) - mainThreadIdentifier = currentThread(); + mainThread = pthread_self(); #endif initializeMainThread(); } @@ -232,7 +232,7 @@ bool isMainThread() #if PLATFORM(DARWIN) && !PLATFORM(CHROMIUM) return pthread_main_np(); #else - return currentThread() == mainThreadIdentifier; + return pthread_equal(pthread_self(), mainThread); #endif } diff --git a/JavaScriptCore/wtf/TypeTraits.h b/JavaScriptCore/wtf/TypeTraits.h index 6ce6a3e..9e75e7a 100644 --- a/JavaScriptCore/wtf/TypeTraits.h +++ b/JavaScriptCore/wtf/TypeTraits.h @@ -155,7 +155,7 @@ namespace WTF { typedef IntegralConstant<bool, true> true_type; typedef IntegralConstant<bool, false> false_type; -#if defined(_MSC_VER) && (_MSC_VER >= 1400) +#if defined(_MSC_VER) && (_MSC_VER >= 1400) && !defined(__INTEL_COMPILER) // VC8 (VS2005) and later have built-in compiler support for HasTrivialConstructor / HasTrivialDestructor, // but for some unexplained reason it doesn't work on built-in types. template <typename T> struct HasTrivialConstructor : public IntegralConstant<bool, __has_trivial_constructor(T)>{ }; diff --git a/JavaScriptCore/wtf/Vector.h b/JavaScriptCore/wtf/Vector.h index e1fc5b4..decc9c9 100644 --- a/JavaScriptCore/wtf/Vector.h +++ b/JavaScriptCore/wtf/Vector.h @@ -71,7 +71,7 @@ namespace WTF { } template <bool needsDestruction, typename T> - class VectorDestructor; + struct VectorDestructor; template<typename T> struct VectorDestructor<false, T> @@ -90,7 +90,7 @@ namespace WTF { }; template <bool needsInitialization, bool canInitializeWithMemset, typename T> - class VectorInitializer; + struct VectorInitializer; template<bool ignore, typename T> struct VectorInitializer<false, ignore, T> @@ -118,7 +118,7 @@ namespace WTF { }; template <bool canMoveWithMemcpy, typename T> - class VectorMover; + struct VectorMover; template<typename T> struct VectorMover<false, T> @@ -162,7 +162,7 @@ namespace WTF { }; template <bool canCopyWithMemcpy, typename T> - class VectorCopier; + struct VectorCopier; template<typename T> struct VectorCopier<false, T> @@ -187,7 +187,7 @@ namespace WTF { }; template <bool canFillWithMemset, typename T> - class VectorFiller; + struct VectorFiller; template<typename T> struct VectorFiller<false, T> @@ -212,7 +212,7 @@ namespace WTF { }; template<bool canCompareWithMemcmp, typename T> - class VectorComparer; + struct VectorComparer; template<typename T> struct VectorComparer<false, T> diff --git a/JavaScriptCore/wtf/VectorTraits.h b/JavaScriptCore/wtf/VectorTraits.h index eb4c279..bf77878 100644 --- a/JavaScriptCore/wtf/VectorTraits.h +++ b/JavaScriptCore/wtf/VectorTraits.h @@ -32,7 +32,7 @@ using std::pair; namespace WTF { template<bool isPod, typename T> - class VectorTraitsBase; + struct VectorTraitsBase; template<typename T> struct VectorTraitsBase<false, T> diff --git a/JavaScriptCore/wtf/dtoa.cpp b/JavaScriptCore/wtf/dtoa.cpp index d75c17a..edcb82b 100644 --- a/JavaScriptCore/wtf/dtoa.cpp +++ b/JavaScriptCore/wtf/dtoa.cpp @@ -140,7 +140,6 @@ #else #define NO_ERRNO #endif -#include <float.h> #include <math.h> #include <stdint.h> #include <stdlib.h> @@ -148,6 +147,7 @@ #include <wtf/AlwaysInline.h> #include <wtf/Assertions.h> #include <wtf/FastMalloc.h> +#include <wtf/MathExtras.h> #include <wtf/Vector.h> #include <wtf/Threading.h> @@ -1869,7 +1869,7 @@ static ALWAYS_INLINE int quorem(BigInt& b, BigInt& S) * calculation. */ -void dtoa(char* result, double dd, int ndigits, int* decpt, int* sign, char** rve) +void dtoa(DtoaBuffer result, double dd, int ndigits, int* decpt, int* sign, char** rve) { /* Arguments ndigits, decpt, sign are similar to those @@ -1908,16 +1908,23 @@ void dtoa(char* result, double dd, int ndigits, int* decpt, int* sign, char** rv { /* Infinity or NaN */ *decpt = 9999; - if (!word1(&u) && !(word0(&u) & 0xfffff)) + if (!word1(&u) && !(word0(&u) & 0xfffff)) { strcpy(result, "Infinity"); - else + if (rve) + *rve = result + 8; + } else { strcpy(result, "NaN"); + if (rve) + *rve = result + 3; + } return; } if (!dval(&u)) { *decpt = 1; result[0] = '0'; result[1] = '\0'; + if (rve) + *rve = result + 1; return; } @@ -2376,4 +2383,83 @@ ret: *rve = s; } +static ALWAYS_INLINE void append(char*& next, const char* src, unsigned size) +{ + for (unsigned i = 0; i < size; ++i) + *next++ = *src++; +} + +void doubleToStringInJavaScriptFormat(double d, DtoaBuffer buffer, unsigned* resultLength) +{ + ASSERT(buffer); + + // avoid ever printing -NaN, in JS conceptually there is only one NaN value + if (isnan(d)) { + append(buffer, "NaN", 3); + if (resultLength) + *resultLength = 3; + return; + } + // -0 -> "0" + if (!d) { + buffer[0] = '0'; + if (resultLength) + *resultLength = 1; + return; + } + + int decimalPoint; + int sign; + + DtoaBuffer result; + char* resultEnd = 0; + WTF::dtoa(result, d, 0, &decimalPoint, &sign, &resultEnd); + int length = resultEnd - result; + + char* next = buffer; + if (sign) + *next++ = '-'; + + if (decimalPoint <= 0 && decimalPoint > -6) { + *next++ = '0'; + *next++ = '.'; + for (int j = decimalPoint; j < 0; j++) + *next++ = '0'; + append(next, result, length); + } else if (decimalPoint <= 21 && decimalPoint > 0) { + if (length <= decimalPoint) { + append(next, result, length); + for (int j = 0; j < decimalPoint - length; j++) + *next++ = '0'; + } else { + append(next, result, decimalPoint); + *next++ = '.'; + append(next, result + decimalPoint, length - decimalPoint); + } + } else if (result[0] < '0' || result[0] > '9') + append(next, result, length); + else { + *next++ = result[0]; + if (length > 1) { + *next++ = '.'; + append(next, result + 1, length - 1); + } + + *next++ = 'e'; + *next++ = (decimalPoint >= 0) ? '+' : '-'; + // decimalPoint can't be more than 3 digits decimal given the + // nature of float representation + int exponential = decimalPoint - 1; + if (exponential < 0) + exponential = -exponential; + if (exponential >= 100) + *next++ = static_cast<char>('0' + exponential / 100); + if (exponential >= 10) + *next++ = static_cast<char>('0' + (exponential % 100) / 10); + *next++ = static_cast<char>('0' + exponential % 10); + } + if (resultLength) + *resultLength = next - buffer; +} + } // namespace WTF diff --git a/JavaScriptCore/wtf/dtoa.h b/JavaScriptCore/wtf/dtoa.h index cbec7c7..6127f53 100644 --- a/JavaScriptCore/wtf/dtoa.h +++ b/JavaScriptCore/wtf/dtoa.h @@ -30,8 +30,18 @@ namespace WTF { extern WTF::Mutex* s_dtoaP5Mutex; double strtod(const char* s00, char** se); - void dtoa(char* result, double d, int ndigits, int* decpt, int* sign, char** rve); + + typedef char DtoaBuffer[80]; + void dtoa(DtoaBuffer result, double d, int ndigits, int* decpt, int* sign, char** rve); + + // dtoa() for ECMA-262 'ToString Applied to the Number Type.' + // The *resultLength will have the length of the resultant string in bufer. + // The resultant string isn't terminated by 0. + void doubleToStringInJavaScriptFormat(double, DtoaBuffer, unsigned* resultLength); } // namespace WTF +using WTF::DtoaBuffer; +using WTF::doubleToStringInJavaScriptFormat; + #endif // WTF_dtoa_h diff --git a/JavaScriptCore/wtf/GOwnPtr.cpp b/JavaScriptCore/wtf/gtk/GOwnPtr.cpp index 432885f..8999962 100644 --- a/JavaScriptCore/wtf/GOwnPtr.cpp +++ b/JavaScriptCore/wtf/gtk/GOwnPtr.cpp @@ -19,6 +19,8 @@ #include "config.h" #include "GOwnPtr.h" +#include <glib.h> + namespace WTF { template <> void freeOwnedGPtr<GError>(GError* ptr) diff --git a/JavaScriptCore/wtf/gtk/GOwnPtr.h b/JavaScriptCore/wtf/gtk/GOwnPtr.h new file mode 100644 index 0000000..ad2c30e --- /dev/null +++ b/JavaScriptCore/wtf/gtk/GOwnPtr.h @@ -0,0 +1,146 @@ +/* + * Copyright (C) 2006, 2007 Apple Inc. All rights reserved. + * Copyright (C) 2008 Collabora Ltd. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public License + * along with this library; see the file COPYING.LIB. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. + * + */ + +#ifndef GOwnPtr_h +#define GOwnPtr_h + +#include <algorithm> +#include <wtf/Assertions.h> +#include <wtf/Noncopyable.h> + +// Forward delcarations at this point avoid the need to include GLib includes +// in WTF headers. +typedef struct _GError GError; +typedef struct _GList GList; +typedef struct _GCond GCond; +typedef struct _GMutex GMutex; +typedef struct _GPatternSpec GPatternSpec; +typedef struct _GDir GDir; +typedef struct _GHashTable GHashTable; +extern "C" void g_free(void*); + +namespace WTF { + +template <typename T> inline void freeOwnedGPtr(T* ptr); +template<> void freeOwnedGPtr<GError>(GError*); +template<> void freeOwnedGPtr<GList>(GList*); +template<> void freeOwnedGPtr<GCond>(GCond*); +template<> void freeOwnedGPtr<GMutex>(GMutex*); +template<> void freeOwnedGPtr<GPatternSpec>(GPatternSpec*); +template<> void freeOwnedGPtr<GDir>(GDir*); +template<> void freeOwnedGPtr<GHashTable>(GHashTable*); + +template <typename T> class GOwnPtr : public Noncopyable { +public: + explicit GOwnPtr(T* ptr = 0) : m_ptr(ptr) { } + ~GOwnPtr() { freeOwnedGPtr(m_ptr); } + + T* get() const { return m_ptr; } + T* release() + { + T* ptr = m_ptr; + m_ptr = 0; + return ptr; + } + + T*& outPtr() + { + ASSERT(!m_ptr); + return m_ptr; + } + + void set(T* ptr) + { + ASSERT(!ptr || m_ptr != ptr); + freeOwnedGPtr(m_ptr); + m_ptr = ptr; + } + + void clear() + { + freeOwnedGPtr(m_ptr); + m_ptr = 0; + } + + T& operator*() const + { + ASSERT(m_ptr); + return *m_ptr; + } + + T* operator->() const + { + ASSERT(m_ptr); + return m_ptr; + } + + bool operator!() const { return !m_ptr; } + + // This conversion operator allows implicit conversion to bool but not to other integer types. + typedef T* GOwnPtr::*UnspecifiedBoolType; + operator UnspecifiedBoolType() const { return m_ptr ? &GOwnPtr::m_ptr : 0; } + + void swap(GOwnPtr& o) { std::swap(m_ptr, o.m_ptr); } + +private: + T* m_ptr; +}; + +template <typename T> inline void swap(GOwnPtr<T>& a, GOwnPtr<T>& b) +{ + a.swap(b); +} + +template <typename T, typename U> inline bool operator==(const GOwnPtr<T>& a, U* b) +{ + return a.get() == b; +} + +template <typename T, typename U> inline bool operator==(T* a, const GOwnPtr<U>& b) +{ + return a == b.get(); +} + +template <typename T, typename U> inline bool operator!=(const GOwnPtr<T>& a, U* b) +{ + return a.get() != b; +} + +template <typename T, typename U> inline bool operator!=(T* a, const GOwnPtr<U>& b) +{ + return a != b.get(); +} + +template <typename T> inline typename GOwnPtr<T>::PtrType getPtr(const GOwnPtr<T>& p) +{ + return p.get(); +} + +template <typename T> inline void freeOwnedGPtr(T* ptr) +{ + g_free(ptr); +} + +} // namespace WTF + +using WTF::GOwnPtr; + +#endif // GOwnPtr_h diff --git a/JavaScriptCore/wtf/unicode/UTF8.cpp b/JavaScriptCore/wtf/unicode/UTF8.cpp index 9e713fe..21d5856 100644 --- a/JavaScriptCore/wtf/unicode/UTF8.cpp +++ b/JavaScriptCore/wtf/unicode/UTF8.cpp @@ -23,6 +23,7 @@ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ +#include "config.h" #include "UTF8.h" namespace WTF { diff --git a/JavaScriptCore/wtf/unicode/glib/UnicodeGLib.cpp b/JavaScriptCore/wtf/unicode/glib/UnicodeGLib.cpp index a779b36..e20c376 100644 --- a/JavaScriptCore/wtf/unicode/glib/UnicodeGLib.cpp +++ b/JavaScriptCore/wtf/unicode/glib/UnicodeGLib.cpp @@ -19,6 +19,7 @@ * */ +#include "config.h" #include "UnicodeGLib.h" namespace WTF { diff --git a/JavaScriptCore/wtf/unicode/glib/UnicodeGLib.h b/JavaScriptCore/wtf/unicode/glib/UnicodeGLib.h index c03d3ec..d72e707 100644 --- a/JavaScriptCore/wtf/unicode/glib/UnicodeGLib.h +++ b/JavaScriptCore/wtf/unicode/glib/UnicodeGLib.h @@ -26,7 +26,7 @@ #define UnicodeGLib_h #include "UnicodeMacrosFromICU.h" -#include <wtf/GOwnPtr.h> +#include <wtf/gtk/GOwnPtr.h> #include <glib.h> #include <pango/pango.h> @@ -152,6 +152,11 @@ inline bool isArabicChar(UChar32 c) return c >= 0x0600 && c <= 0x06FF; } +inline bool isAlphanumeric(UChar32 c) +{ + return g_unichar_isalnum(c); +} + inline bool isFormatChar(UChar32 c) { return g_unichar_type(c) == G_UNICODE_FORMAT; diff --git a/JavaScriptCore/wtf/unicode/icu/UnicodeIcu.h b/JavaScriptCore/wtf/unicode/icu/UnicodeIcu.h index 35c6fbf..a2a5c0a 100644 --- a/JavaScriptCore/wtf/unicode/icu/UnicodeIcu.h +++ b/JavaScriptCore/wtf/unicode/icu/UnicodeIcu.h @@ -164,6 +164,11 @@ inline bool isArabicChar(UChar32 c) return ublock_getCode(c) == UBLOCK_ARABIC; } +inline bool isAlphanumeric(UChar32 c) +{ + return u_isalnum(c); +} + inline bool isSeparatorSpace(UChar32 c) { return u_charType(c) == U_SPACE_SEPARATOR; diff --git a/JavaScriptCore/wtf/unicode/qt4/UnicodeQt4.h b/JavaScriptCore/wtf/unicode/qt4/UnicodeQt4.h index bdf2028..cfd482d 100644 --- a/JavaScriptCore/wtf/unicode/qt4/UnicodeQt4.h +++ b/JavaScriptCore/wtf/unicode/qt4/UnicodeQt4.h @@ -30,7 +30,6 @@ #include <stdint.h> -#if QT_VERSION >= 0x040300 QT_BEGIN_NAMESPACE namespace QUnicodeTables { struct Properties { @@ -55,7 +54,6 @@ namespace QUnicodeTables { Q_CORE_EXPORT const Properties * QT_FASTCALL properties(ushort ucs2); } QT_END_NAMESPACE -#endif // ugly hack to make UChar compatible with JSChar in API/JSStringRef.h #if defined(Q_OS_WIN) || COMPILER(WINSCW) @@ -186,8 +184,6 @@ enum CharCategory { }; -#if QT_VERSION >= 0x040300 - // FIXME: handle surrogates correctly in all methods inline UChar32 toLower(UChar32 ch) @@ -406,138 +402,6 @@ inline CharCategory category(UChar32 c) return (CharCategory) U_MASK(QChar::category(c)); } -#else - -inline UChar32 toLower(UChar32 ch) -{ - if (ch > 0xffff) - return ch; - return QChar((unsigned short)ch).toLower().unicode(); -} - -inline int toLower(UChar* result, int resultLength, const UChar* src, int srcLength, bool* error) -{ - *error = false; - if (resultLength < srcLength) { - *error = true; - return srcLength; - } - for (int i = 0; i < srcLength; ++i) - result[i] = QChar(src[i]).toLower().unicode(); - return srcLength; -} - -inline UChar32 toUpper(UChar32 ch) -{ - if (ch > 0xffff) - return ch; - return QChar((unsigned short)ch).toUpper().unicode(); -} - -inline int toUpper(UChar* result, int resultLength, const UChar* src, int srcLength, bool* error) -{ - *error = false; - if (resultLength < srcLength) { - *error = true; - return srcLength; - } - for (int i = 0; i < srcLength; ++i) - result[i] = QChar(src[i]).toUpper().unicode(); - return srcLength; -} - -inline int toTitleCase(UChar32 c) -{ - if (c > 0xffff) - return c; - return QChar((unsigned short)c).toUpper().unicode(); -} - -inline UChar32 foldCase(UChar32 c) -{ - if (c > 0xffff) - return c; - return QChar((unsigned short)c).toLower().unicode(); -} - -inline int foldCase(UChar* result, int resultLength, const UChar* src, int srcLength, bool* error) -{ - return toLower(result, resultLength, src, srcLength, error); -} - -inline bool isPrintableChar(UChar32 c) -{ - return (c & 0xffff0000) == 0 && QChar((unsigned short)c).isPrint(); -} - -inline bool isArabicChar(UChar32 c) -{ - return c >= 0x0600 && c <= 0x06FF; -} - -inline bool isSeparatorSpace(UChar32 c) -{ - return (c & 0xffff0000) == 0 && QChar((unsigned short)c).category() == QChar::Separator_Space; -} - -inline bool isPunct(UChar32 c) -{ - return (c & 0xffff0000) == 0 && QChar((unsigned short)c).isPunct(); -} - -inline bool isLower(UChar32 c) -{ - return (c & 0xffff0000) == 0 && QChar((unsigned short)c).category() == QChar::Letter_Lowercase; -} - -inline UChar32 mirroredChar(UChar32 c) -{ - if (c > 0xffff) - return c; - return QChar(c).mirroredChar().unicode(); -} - -inline uint8_t combiningClass(UChar32 c) -{ - if (c > 0xffff) - return 0; - return QChar((unsigned short)c).combiningClass(); -} - -inline DecompositionType decompositionType(UChar32 c) -{ - if (c > 0xffff) - return DecompositionNone; - return (DecompositionType)QChar(c).decompositionTag(); -} - -inline int umemcasecmp(const UChar* a, const UChar* b, int len) -{ - for (int i = 0; i < len; ++i) { - QChar c1 = QChar(a[i]).toLower(); - QChar c2 = QChar(b[i]).toLower(); - if (c1 != c2) - return c1.unicode() - c2.unicode(); - } - return 0; -} - -inline Direction direction(UChar32 c) -{ - if (c > 0xffff) - return LeftToRight; - return (Direction)QChar(c).direction(); -} - -inline CharCategory category(UChar32 c) -{ - if (c > 0xffff) - return NoCategory; - return (CharCategory) U_MASK(QChar(c).category()); -} - -#endif - } } #endif // WTF_UNICODE_QT4_H diff --git a/JavaScriptCore/wtf/unicode/wince/UnicodeWince.cpp b/JavaScriptCore/wtf/unicode/wince/UnicodeWince.cpp index 966f2a1..2df44f8 100644 --- a/JavaScriptCore/wtf/unicode/wince/UnicodeWince.cpp +++ b/JavaScriptCore/wtf/unicode/wince/UnicodeWince.cpp @@ -19,6 +19,7 @@ * Boston, MA 02110-1301, USA. */ +#include "config.h" #include "UnicodeWince.h" #include <wchar.h> diff --git a/JavaScriptCore/wtf/wince/FastMallocWince.h b/JavaScriptCore/wtf/wince/FastMallocWince.h index 93d9f75..37174f0 100644 --- a/JavaScriptCore/wtf/wince/FastMallocWince.h +++ b/JavaScriptCore/wtf/wince/FastMallocWince.h @@ -1,5 +1,4 @@ /* - * This file is part of the KDE libraries * Copyright (C) 2005, 2006, 2007, 2008 Apple Inc. All rights reserved. * Copyright (C) 2007-2009 Torch Mobile, Inc. All rights reserved * diff --git a/JavaScriptCore/yarr/RegexPattern.h b/JavaScriptCore/yarr/RegexPattern.h index a451131..dd7512d 100644 --- a/JavaScriptCore/yarr/RegexPattern.h +++ b/JavaScriptCore/yarr/RegexPattern.h @@ -137,7 +137,7 @@ struct PatternTerm { PatternTerm(unsigned spatternId) : type(TypeBackReference) - , invertOrCapture(invertOrCapture) + , invertOrCapture(false) { subpatternId = spatternId; quantityType = QuantifierFixedCount; |