diff options
author | Andrei Popescu <andreip@google.com> | 2010-02-26 13:31:12 +0000 |
---|---|---|
committer | Andrei Popescu <andreip@google.com> | 2010-02-26 13:31:12 +0000 |
commit | 402d937239b0e2fd11bf2f4fe972ad78aa9fd481 (patch) | |
tree | b9d769439a27fa48d7171e1a669e98f519591b94 | |
parent | 2007755a32dfa1ac843f501dec4fb872f8bbcc52 (diff) | |
download | android_external_v8-402d937239b0e2fd11bf2f4fe972ad78aa9fd481.tar.gz android_external_v8-402d937239b0e2fd11bf2f4fe972ad78aa9fd481.tar.bz2 android_external_v8-402d937239b0e2fd11bf2f4fe972ad78aa9fd481.zip |
update V8 to TOT snapshot branch
179 files changed, 12760 insertions, 10813 deletions
@@ -23,3 +23,4 @@ Rene Rebe <rene@exactcode.de> Ryan Dahl <coldredlemur@gmail.com> Patrick Gansterer <paroga@paroga.com> Subrato K De <subratokde@codeaurora.org> +Dineel D Sule <dsule@codeaurora.org> diff --git a/Android.v8common.mk b/Android.v8common.mk index 6d8034be..ded5f61f 100644 --- a/Android.v8common.mk +++ b/Android.v8common.mk @@ -38,6 +38,7 @@ V8_LOCAL_SRC_FILES := \ src/interpreter-irregexp.cc \ src/jsregexp.cc \ src/jump-target.cc \ + src/liveedit.cc \ src/log.cc \ src/log-utils.cc \ src/mark-compact.cc \ @@ -1,3 +1,25 @@ +2010-02-23: Version 2.1.2 + + Fix a crash bug caused by wrong assert. + + Fix a bug with register names on 64-bit V8 (issue 615). + + Performance improvements on all platforms. + +2010-02-19: Version 2.1.1 + + [ES5] Implemented Object.defineProperty. + + Improved profiler support. + + Added SetPrototype method in the public V8 API. + + Added GetScriptOrigin and GetScriptLineNumber methods to Function + objects in the API. + + Performance improvements on all platforms. + + 2010-02-03: Version 2.1.0 Values are now always wrapped in objects when used as a receiver. diff --git a/include/v8.h b/include/v8.h index 19a41f49..c911cc7a 100644 --- a/include/v8.h +++ b/include/v8.h @@ -534,51 +534,76 @@ class V8EXPORT ScriptOrigin { class V8EXPORT Script { public: - /** - * Compiles the specified script. The ScriptOrigin* and ScriptData* - * parameters are owned by the caller of Script::Compile. No - * references to these objects are kept after compilation finishes. - * - * The script object returned is context independent; when run it - * will use the currently entered context. - */ - static Local<Script> New(Handle<String> source, - ScriptOrigin* origin = NULL, - ScriptData* pre_data = NULL); + /** + * Compiles the specified script (context-independent). + * + * \param source Script source code. + * \param origin Script origin, owned by caller, no references are kept + * when New() returns + * \param pre_data Pre-parsing data, as obtained by ScriptData::PreCompile() + * using pre_data speeds compilation if it's done multiple times. + * Owned by caller, no references are kept when New() returns. + * \param script_data Arbitrary data associated with script. Using + * this has same effect as calling SetData(), but allows data to be + * available to compile event handlers. + * \return Compiled script object (context independent; when run it + * will use the currently entered context). + */ + static Local<Script> New(Handle<String> source, + ScriptOrigin* origin = NULL, + ScriptData* pre_data = NULL, + Handle<String> script_data = Handle<String>()); - /** - * Compiles the specified script using the specified file name - * object (typically a string) as the script's origin. - * - * The script object returned is context independent; when run it - * will use the currently entered context. - */ - static Local<Script> New(Handle<String> source, - Handle<Value> file_name); - - /** - * Compiles the specified script. The ScriptOrigin* and ScriptData* - * parameters are owned by the caller of Script::Compile. No - * references to these objects are kept after compilation finishes. + /** + * Compiles the specified script using the specified file name + * object (typically a string) as the script's origin. + * + * \param source Script source code. + * \patam file_name file name object (typically a string) to be used + * as the script's origin. + * \return Compiled script object (context independent; when run it + * will use the currently entered context). + */ + static Local<Script> New(Handle<String> source, + Handle<Value> file_name); + + /** + * Compiles the specified script (bound to current context). * - * The script object returned is bound to the context that was active - * when this function was called. When run it will always use this - * context. + * \param source Script source code. + * \param origin Script origin, owned by caller, no references are kept + * when Compile() returns + * \param pre_data Pre-parsing data, as obtained by ScriptData::PreCompile() + * using pre_data speeds compilation if it's done multiple times. + * Owned by caller, no references are kept when Compile() returns. + * \param script_data Arbitrary data associated with script. Using + * this has same effect as calling SetData(), but makes data available + * earlier (i.e. to compile event handlers). + * \return Compiled script object, bound to the context that was active + * when this function was called. When run it will always use this + * context. */ static Local<Script> Compile(Handle<String> source, ScriptOrigin* origin = NULL, - ScriptData* pre_data = NULL); + ScriptData* pre_data = NULL, + Handle<String> script_data = Handle<String>()); /** * Compiles the specified script using the specified file name * object (typically a string) as the script's origin. * - * The script object returned is bound to the context that was active - * when this function was called. When run it will always use this - * context. + * \param source Script source code. + * \param file_name File name to use as script's origin + * \param script_data Arbitrary data associated with script. Using + * this has same effect as calling SetData(), but makes data available + * earlier (i.e. to compile event handlers). + * \return Compiled script object, bound to the context that was active + * when this function was called. When run it will always use this + * context. */ static Local<Script> Compile(Handle<String> source, - Handle<Value> file_name); + Handle<Value> file_name, + Handle<String> script_data = Handle<String>()); /** * Runs the script returning the resulting value. If the script is @@ -1197,6 +1222,13 @@ class V8EXPORT Object : public Value { Local<Value> GetPrototype(); /** + * Set the prototype object. This does not skip objects marked to + * be skipped by __proto__ and it does not consult the security + * handler. + */ + bool SetPrototype(Handle<Value> prototype); + + /** * Finds an instance of the given function template in the prototype * chain. */ @@ -1354,7 +1386,15 @@ class V8EXPORT Function : public Object { Local<Value> Call(Handle<Object> recv, int argc, Handle<Value> argv[]); void SetName(Handle<String> name); Handle<Value> GetName() const; + + /** + * Returns zero based line number of function body and + * kLineOffsetNotFound if no information available. + */ + int GetScriptLineNumber() const; + ScriptOrigin GetScriptOrigin() const; static inline Function* Cast(Value* obj); + static const int kLineOffsetNotFound; private: Function(); static void CheckCast(Value* obj); @@ -2309,22 +2349,30 @@ class V8EXPORT V8 { static bool IsProfilerPaused(); /** - * Resumes specified profiler modules. + * Resumes specified profiler modules. Can be called several times to + * mark the opening of a profiler events block with the given tag. + * * "ResumeProfiler" is equivalent to "ResumeProfilerEx(PROFILER_MODULE_CPU)". * See ProfilerModules enum. * * \param flags Flags specifying profiler modules. + * \param tag Profile tag. */ - static void ResumeProfilerEx(int flags); + static void ResumeProfilerEx(int flags, int tag = 0); /** - * Pauses specified profiler modules. + * Pauses specified profiler modules. Each call to "PauseProfilerEx" closes + * a block of profiler events opened by a call to "ResumeProfilerEx" with the + * same tag value. There is no need for blocks to be properly nested. + * The profiler is paused when the last opened block is closed. + * * "PauseProfiler" is equivalent to "PauseProfilerEx(PROFILER_MODULE_CPU)". * See ProfilerModules enum. * * \param flags Flags specifying profiler modules. + * \param tag Profile tag. */ - static void PauseProfilerEx(int flags); + static void PauseProfilerEx(int flags, int tag = 0); /** * Returns active (resumed) profiler modules. diff --git a/samples/lineprocessor.cc b/samples/lineprocessor.cc index 505dabf9..61517d36 100644 --- a/samples/lineprocessor.cc +++ b/samples/lineprocessor.cc @@ -152,7 +152,7 @@ int RunMain(int argc, char* argv[]) { } else if (strcmp(str, "--main-cycle-in-js") == 0) { cycle_type = CycleInJs; } else if (strcmp(str, "-p") == 0 && i + 1 < argc) { - port_number = atoi(argv[i + 1]); + port_number = atoi(argv[i + 1]); // NOLINT i++; } else if (strncmp(str, "--", 2) == 0) { printf("Warning: unknown flag %s.\nTry --help for options\n", str); diff --git a/src/SConscript b/src/SConscript index 864b4e78..3b227c8b 100755 --- a/src/SConscript +++ b/src/SConscript @@ -57,7 +57,6 @@ SOURCES = { disassembler.cc execution.cc factory.cc - fast-codegen.cc flags.cc frame-element.cc frames.cc @@ -72,6 +71,7 @@ SOURCES = { interpreter-irregexp.cc jsregexp.cc jump-target.cc + liveedit.cc log-utils.cc log.cc mark-compact.cc @@ -108,6 +108,7 @@ SOURCES = { zone.cc """), 'arch:arm': Split(""" + fast-codegen.cc arm/builtins-arm.cc arm/codegen-arm.cc arm/constants-arm.cc @@ -132,6 +133,7 @@ SOURCES = { arm/assembler-thumb2.cc """), 'arch:mips': Split(""" + fast-codegen.cc mips/assembler-mips.cc mips/builtins-mips.cc mips/codegen-mips.cc @@ -168,6 +170,7 @@ SOURCES = { ia32/virtual-frame-ia32.cc """), 'arch:x64': Split(""" + fast-codegen.cc x64/assembler-x64.cc x64/builtins-x64.cc x64/codegen-x64.cc diff --git a/src/accessors.cc b/src/accessors.cc index 5a029285..b05719ed 100644 --- a/src/accessors.cc +++ b/src/accessors.cc @@ -647,42 +647,9 @@ Object* Accessors::ObjectGetPrototype(Object* receiver, void*) { Object* Accessors::ObjectSetPrototype(JSObject* receiver, Object* value, void*) { - // Before we can set the prototype we need to be sure - // prototype cycles are prevented. - // It is sufficient to validate that the receiver is not in the new prototype - // chain. - - // Silently ignore the change if value is not a JSObject or null. - // SpiderMonkey behaves this way. - if (!value->IsJSObject() && !value->IsNull()) return value; - - for (Object* pt = value; pt != Heap::null_value(); pt = pt->GetPrototype()) { - if (JSObject::cast(pt) == receiver) { - // Cycle detected. - HandleScope scope; - return Top::Throw(*Factory::NewError("cyclic_proto", - HandleVector<Object>(NULL, 0))); - } - } - - // Find the first object in the chain whose prototype object is not - // hidden and set the new prototype on that object. - JSObject* current = receiver; - Object* current_proto = receiver->GetPrototype(); - while (current_proto->IsJSObject() && - JSObject::cast(current_proto)->map()->is_hidden_prototype()) { - current = JSObject::cast(current_proto); - current_proto = current_proto->GetPrototype(); - } - - // Set the new prototype of the object. - Object* new_map = current->map()->CopyDropTransitions(); - if (new_map->IsFailure()) return new_map; - Map::cast(new_map)->set_prototype(value); - current->set_map(Map::cast(new_map)); - + const bool skip_hidden_prototypes = true; // To be consistent with other Set functions, return the value. - return value; + return receiver->SetPrototype(value, skip_hidden_prototypes); } @@ -1107,7 +1107,8 @@ ScriptData* ScriptData::New(unsigned* data, int length) { Local<Script> Script::New(v8::Handle<String> source, v8::ScriptOrigin* origin, - v8::ScriptData* script_data) { + v8::ScriptData* pre_data, + v8::Handle<String> script_data) { ON_BAILOUT("v8::Script::New()", return Local<Script>()); LOG_API("Script::New"); ENTER_V8; @@ -1127,13 +1128,13 @@ Local<Script> Script::New(v8::Handle<String> source, } } EXCEPTION_PREAMBLE(); - i::ScriptDataImpl* pre_data = static_cast<i::ScriptDataImpl*>(script_data); + i::ScriptDataImpl* pre_data_impl = static_cast<i::ScriptDataImpl*>(pre_data); // We assert that the pre-data is sane, even though we can actually // handle it if it turns out not to be in release mode. - ASSERT(pre_data == NULL || pre_data->SanityCheck()); + ASSERT(pre_data_impl == NULL || pre_data_impl->SanityCheck()); // If the pre-data isn't sane we simply ignore it - if (pre_data != NULL && !pre_data->SanityCheck()) { - pre_data = NULL; + if (pre_data_impl != NULL && !pre_data_impl->SanityCheck()) { + pre_data_impl = NULL; } i::Handle<i::JSFunction> boilerplate = i::Compiler::Compile(str, @@ -1141,7 +1142,8 @@ Local<Script> Script::New(v8::Handle<String> source, line_offset, column_offset, NULL, - pre_data, + pre_data_impl, + Utils::OpenHandle(*script_data), i::NOT_NATIVES_CODE); has_pending_exception = boilerplate.is_null(); EXCEPTION_BAILOUT_CHECK(Local<Script>()); @@ -1158,11 +1160,12 @@ Local<Script> Script::New(v8::Handle<String> source, Local<Script> Script::Compile(v8::Handle<String> source, v8::ScriptOrigin* origin, - v8::ScriptData* script_data) { + v8::ScriptData* pre_data, + v8::Handle<String> script_data) { ON_BAILOUT("v8::Script::Compile()", return Local<Script>()); LOG_API("Script::Compile"); ENTER_V8; - Local<Script> generic = New(source, origin, script_data); + Local<Script> generic = New(source, origin, pre_data, script_data); if (generic.IsEmpty()) return generic; i::Handle<i::JSFunction> boilerplate = Utils::OpenHandle(*generic); @@ -1174,9 +1177,10 @@ Local<Script> Script::Compile(v8::Handle<String> source, Local<Script> Script::Compile(v8::Handle<String> source, - v8::Handle<Value> file_name) { + v8::Handle<Value> file_name, + v8::Handle<String> script_data) { ScriptOrigin origin(file_name); - return Compile(source, &origin); + return Compile(source, &origin, 0, script_data); } @@ -2035,6 +2039,19 @@ Local<Value> v8::Object::GetPrototype() { } +bool v8::Object::SetPrototype(Handle<Value> value) { + ON_BAILOUT("v8::Object::SetPrototype()", return false); + ENTER_V8; + i::Handle<i::JSObject> self = Utils::OpenHandle(this); + i::Handle<i::Object> value_obj = Utils::OpenHandle(*value); + EXCEPTION_PREAMBLE(); + i::Handle<i::Object> result = i::SetPrototype(self, value_obj); + has_pending_exception = result.is_null(); + EXCEPTION_BAILOUT_CHECK(false); + return true; +} + + Local<Object> v8::Object::FindInstanceInPrototypeChain( v8::Handle<FunctionTemplate> tmpl) { ON_BAILOUT("v8::Object::FindInstanceInPrototypeChain()", @@ -2197,7 +2214,7 @@ Local<Value> v8::Object::GetRealNamedPropertyInPrototypeChain( i::Handle<i::String> key_obj = Utils::OpenHandle(*key); i::LookupResult lookup; self_obj->LookupRealNamedPropertyInPrototypes(*key_obj, &lookup); - if (lookup.IsValid()) { + if (lookup.IsProperty()) { PropertyAttributes attributes; i::Handle<i::Object> result(self_obj->GetProperty(*self_obj, &lookup, @@ -2216,7 +2233,7 @@ Local<Value> v8::Object::GetRealNamedProperty(Handle<String> key) { i::Handle<i::String> key_obj = Utils::OpenHandle(*key); i::LookupResult lookup; self_obj->LookupRealNamedProperty(*key_obj, &lookup); - if (lookup.IsValid()) { + if (lookup.IsProperty()) { PropertyAttributes attributes; i::Handle<i::Object> result(self_obj->GetProperty(*self_obj, &lookup, @@ -2448,6 +2465,99 @@ Handle<Value> Function::GetName() const { } +ScriptOrigin Function::GetScriptOrigin() const { + i::Handle<i::JSFunction> func = Utils::OpenHandle(this); + if (func->shared()->script()->IsScript()) { + i::Handle<i::Script> script(i::Script::cast(func->shared()->script())); + v8::ScriptOrigin origin( + Utils::ToLocal(i::Handle<i::Object>(script->name())), + v8::Integer::New(script->line_offset()->value()), + v8::Integer::New(script->column_offset()->value())); + return origin; + } + return v8::ScriptOrigin(Handle<Value>()); +} + + +const int Function::kLineOffsetNotFound = -1; + + +int Function::GetScriptLineNumber() const { + i::Handle<i::JSFunction> func = Utils::OpenHandle(this); + if (func->shared()->script()->IsScript()) { + i::Handle<i::Script> script(i::Script::cast(func->shared()->script())); + return i::GetScriptLineNumber(script, func->shared()->start_position()); + } + return kLineOffsetNotFound; +} + + +namespace { + +// Tracks string usage to help make better decisions when +// externalizing strings. +// +// Implementation note: internally this class only tracks fresh +// strings and keeps a single use counter for them. +class StringTracker { + public: + // Records that the given string's characters were copied to some + // external buffer. If this happens often we should honor + // externalization requests for the string. + static void RecordWrite(i::Handle<i::String> string) { + i::Address address = reinterpret_cast<i::Address>(*string); + i::Address top = i::Heap::NewSpaceTop(); + if (IsFreshString(address, top)) { + IncrementUseCount(top); + } + } + + // Estimates freshness and use frequency of the given string based + // on how close it is to the new space top and the recorded usage + // history. + static inline bool IsFreshUnusedString(i::Handle<i::String> string) { + i::Address address = reinterpret_cast<i::Address>(*string); + i::Address top = i::Heap::NewSpaceTop(); + return IsFreshString(address, top) && IsUseCountLow(top); + } + + private: + static inline bool IsFreshString(i::Address string, i::Address top) { + return top - kFreshnessLimit <= string && string <= top; + } + + static inline bool IsUseCountLow(i::Address top) { + if (last_top_ != top) return true; + return use_count_ < kUseLimit; + } + + static inline void IncrementUseCount(i::Address top) { + if (last_top_ != top) { + use_count_ = 0; + last_top_ = top; + } + ++use_count_; + } + + // How close to the new space top a fresh string has to be. + static const int kFreshnessLimit = 1024; + + // The number of uses required to consider a string useful. + static const int kUseLimit = 32; + + // Single use counter shared by all fresh strings. + static int use_count_; + + // Last new space top when the use count above was valid. + static i::Address last_top_; +}; + +int StringTracker::use_count_ = 0; +i::Address StringTracker::last_top_ = NULL; + +} // namespace + + int String::Length() const { if (IsDeadCheck("v8::String::Length()")) return 0; return Utils::OpenHandle(this)->length(); @@ -2465,6 +2575,7 @@ int String::WriteUtf8(char* buffer, int capacity) const { LOG_API("String::WriteUtf8"); ENTER_V8; i::Handle<i::String> str = Utils::OpenHandle(this); + StringTracker::RecordWrite(str); write_input_buffer.Reset(0, *str); int len = str->length(); // Encode the first K - 3 bytes directly into the buffer since we @@ -2508,6 +2619,7 @@ int String::WriteAscii(char* buffer, int start, int length) const { ENTER_V8; ASSERT(start >= 0 && length >= -1); i::Handle<i::String> str = Utils::OpenHandle(this); + StringTracker::RecordWrite(str); // Flatten the string for efficiency. This applies whether we are // using StringInputBuffer or Get(i) to access the characters. str->TryFlattenIfNotFlat(); @@ -2534,6 +2646,7 @@ int String::Write(uint16_t* buffer, int start, int length) const { ENTER_V8; ASSERT(start >= 0 && length >= -1); i::Handle<i::String> str = Utils::OpenHandle(this); + StringTracker::RecordWrite(str); int end = length; if ( (length == -1) || (length > str->length() - start) ) end = str->length() - start; @@ -3102,6 +3215,7 @@ bool v8::String::MakeExternal(v8::String::ExternalStringResource* resource) { if (this->IsExternal()) return false; // Already an external string. ENTER_V8; i::Handle<i::String> obj = Utils::OpenHandle(this); + if (StringTracker::IsFreshUnusedString(obj)) return false; bool result = obj->MakeExternal(resource); if (result && !obj->IsSymbol()) { i::ExternalStringTable::AddString(*obj); @@ -3127,6 +3241,7 @@ bool v8::String::MakeExternal( if (this->IsExternal()) return false; // Already an external string. ENTER_V8; i::Handle<i::String> obj = Utils::OpenHandle(this); + if (StringTracker::IsFreshUnusedString(obj)) return false; bool result = obj->MakeExternal(resource); if (result && !obj->IsSymbol()) { i::ExternalStringTable::AddString(*obj); @@ -3138,6 +3253,7 @@ bool v8::String::MakeExternal( bool v8::String::CanMakeExternal() { if (IsDeadCheck("v8::String::CanMakeExternal()")) return false; i::Handle<i::String> obj = Utils::OpenHandle(this); + if (StringTracker::IsFreshUnusedString(obj)) return false; int size = obj->Size(); // Byte size of the original string. if (size < i::ExternalString::kSize) return false; @@ -3361,14 +3477,14 @@ void V8::SetGlobalGCEpilogueCallback(GCCallback callback) { void V8::PauseProfiler() { #ifdef ENABLE_LOGGING_AND_PROFILING - i::Logger::PauseProfiler(PROFILER_MODULE_CPU); + PauseProfilerEx(PROFILER_MODULE_CPU); #endif } void V8::ResumeProfiler() { #ifdef ENABLE_LOGGING_AND_PROFILING - i::Logger::ResumeProfiler(PROFILER_MODULE_CPU); + ResumeProfilerEx(PROFILER_MODULE_CPU); #endif } @@ -3382,7 +3498,7 @@ bool V8::IsProfilerPaused() { } -void V8::ResumeProfilerEx(int flags) { +void V8::ResumeProfilerEx(int flags, int tag) { #ifdef ENABLE_LOGGING_AND_PROFILING if (flags & PROFILER_MODULE_HEAP_SNAPSHOT) { // Snapshot mode: resume modules, perform GC, then pause only @@ -3392,19 +3508,19 @@ void V8::ResumeProfilerEx(int flags) { // Reset snapshot flag and CPU module flags. flags &= ~(PROFILER_MODULE_HEAP_SNAPSHOT | PROFILER_MODULE_CPU); const int current_flags = i::Logger::GetActiveProfilerModules(); - i::Logger::ResumeProfiler(flags); + i::Logger::ResumeProfiler(flags, tag); i::Heap::CollectAllGarbage(false); - i::Logger::PauseProfiler(~current_flags & flags); + i::Logger::PauseProfiler(~current_flags & flags, tag); } else { - i::Logger::ResumeProfiler(flags); + i::Logger::ResumeProfiler(flags, tag); } #endif } -void V8::PauseProfilerEx(int flags) { +void V8::PauseProfilerEx(int flags, int tag) { #ifdef ENABLE_LOGGING_AND_PROFILING - i::Logger::PauseProfiler(flags); + i::Logger::PauseProfiler(flags, tag); #endif } diff --git a/src/arm/assembler-arm.cc b/src/arm/assembler-arm.cc index cf167f0f..1b3bcb0b 100644 --- a/src/arm/assembler-arm.cc +++ b/src/arm/assembler-arm.cc @@ -47,10 +47,29 @@ unsigned CpuFeatures::supported_ = 0; unsigned CpuFeatures::enabled_ = 0; unsigned CpuFeatures::found_by_runtime_probing_ = 0; + +#ifdef __arm__ +static uint64_t CpuFeaturesImpliedByCompiler() { + uint64_t answer = 0; +#ifdef CAN_USE_ARMV7_INSTRUCTIONS + answer |= 1u << ARMv7; +#endif // def CAN_USE_ARMV7_INSTRUCTIONS + // If the compiler is allowed to use VFP then we can use VFP too in our code + // generation even when generating snapshots. This won't work for cross + // compilation. +#if defined(__VFP_FP__) && !defined(__SOFTFP__) + answer |= 1u << VFP3; +#endif // defined(__VFP_FP__) && !defined(__SOFTFP__) +#ifdef CAN_USE_VFP_INSTRUCTIONS + answer |= 1u << VFP3; +#endif // def CAN_USE_VFP_INSTRUCTIONS + return answer; +} +#endif // def __arm__ + + void CpuFeatures::Probe() { - // If the compiler is allowed to use vfp then we can use vfp too in our - // code generation. -#if !defined(__arm__) +#ifndef __arm__ // For the simulator=arm build, use VFP when FLAG_enable_vfp3 is enabled. if (FLAG_enable_vfp3) { supported_ |= 1u << VFP3; @@ -59,10 +78,10 @@ void CpuFeatures::Probe() { if (FLAG_enable_armv7) { supported_ |= 1u << ARMv7; } -#else +#else // def __arm__ if (Serializer::enabled()) { - supported_ |= 1u << VFP3; - //supported_ |= OS::CpuFeaturesImpliedByPlatform(); + supported_ |= OS::CpuFeaturesImpliedByPlatform(); + supported_ |= CpuFeaturesImpliedByCompiler(); return; // No features if we might serialize. } @@ -77,7 +96,7 @@ void CpuFeatures::Probe() { supported_ |= 1u << ARMv7; found_by_runtime_probing_ |= 1u << ARMv7; } -#endif +#endif // def __arm__ } @@ -621,7 +640,7 @@ static bool MustUseIp(RelocInfo::Mode rmode) { if (!Serializer::enabled()) { Serializer::TooLateToEnableNow(); } -#endif +#endif // def DEBUG return Serializer::enabled(); } else if (rmode == RelocInfo::NONE) { return false; @@ -1226,14 +1245,16 @@ void Assembler::swpb(Register dst, // Exception-generating instructions and debugging support. void Assembler::stop(const char* msg) { -#if !defined(__arm__) +#ifndef __arm__ // The simulator handles these special instructions and stops execution. emit(15 << 28 | ((intptr_t) msg)); -#else - // Just issue a simple break instruction for now. Alternatively we could use - // the swi(0x9f0001) instruction on Linux. +#else // def __arm__ +#ifdef CAN_USE_ARMV5_INSTRUCTIONS bkpt(0); -#endif +#else // ndef CAN_USE_ARMV5_INSTRUCTIONS + swi(0x9f0001); +#endif // ndef CAN_USE_ARMV5_INSTRUCTIONS +#endif // def __arm__ } diff --git a/src/arm/assembler-thumb2-inl.h b/src/arm/assembler-thumb2-inl.h index 3808ef00..9e0fc2f7 100644 --- a/src/arm/assembler-thumb2-inl.h +++ b/src/arm/assembler-thumb2-inl.h @@ -174,20 +174,6 @@ Operand::Operand(const ExternalReference& f) { } -Operand::Operand(Object** opp) { - rm_ = no_reg; - imm32_ = reinterpret_cast<int32_t>(opp); - rmode_ = RelocInfo::NONE; -} - - -Operand::Operand(Context** cpp) { - rm_ = no_reg; - imm32_ = reinterpret_cast<int32_t>(cpp); - rmode_ = RelocInfo::NONE; -} - - Operand::Operand(Smi* value) { rm_ = no_reg; imm32_ = reinterpret_cast<intptr_t>(value); @@ -229,14 +215,24 @@ void Assembler::emit(Instr x) { Address Assembler::target_address_address_at(Address pc) { - Instr instr = Memory::int32_at(pc); - // Verify that the instruction at pc is a ldr<cond> <Rd>, [pc +/- offset_12]. + Address target_pc = pc; + Instr instr = Memory::int32_at(target_pc); + // If we have a bx instruction, the instruction before the bx is + // what we need to patch. + static const int32_t kBxInstMask = 0x0ffffff0; + static const int32_t kBxInstPattern = 0x012fff10; + if ((instr & kBxInstMask) == kBxInstPattern) { + target_pc -= kInstrSize; + instr = Memory::int32_at(target_pc); + } + // Verify that the instruction to patch is a + // ldr<cond> <Rd>, [pc +/- offset_12]. ASSERT((instr & 0x0f7f0000) == 0x051f0000); int offset = instr & 0xfff; // offset_12 is unsigned if ((instr & (1 << 23)) == 0) offset = -offset; // U bit defines offset sign // Verify that the constant pool comes after the instruction referencing it. ASSERT(offset >= -4); - return pc + offset + 8; + return target_pc + offset + 8; } diff --git a/src/arm/assembler-thumb2.cc b/src/arm/assembler-thumb2.cc index 6c2b9032..e31c4291 100644 --- a/src/arm/assembler-thumb2.cc +++ b/src/arm/assembler-thumb2.cc @@ -30,9 +30,9 @@ // ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED // OF THE POSSIBILITY OF SUCH DAMAGE. -// The original source code covered by the above license above has been modified -// significantly by Google Inc. -// Copyright 2006-2008 the V8 project authors. All rights reserved. +// The original source code covered by the above license above has been +// modified significantly by Google Inc. +// Copyright 2010 the V8 project authors. All rights reserved. #include "v8.h" @@ -51,9 +51,14 @@ void CpuFeatures::Probe() { // If the compiler is allowed to use vfp then we can use vfp too in our // code generation. #if !defined(__arm__) - // For the simulator=arm build, always use VFP since the arm simulator has - // VFP support. - supported_ |= 1u << VFP3; + // For the simulator=arm build, use VFP when FLAG_enable_vfp3 is enabled. + if (FLAG_enable_vfp3) { + supported_ |= 1u << VFP3; + } + // For the simulator=arm build, use ARMv7 when FLAG_enable_armv7 is enabled + if (FLAG_enable_armv7) { + supported_ |= 1u << ARMv7; + } #else if (Serializer::enabled()) { supported_ |= OS::CpuFeaturesImpliedByPlatform(); @@ -66,6 +71,11 @@ void CpuFeatures::Probe() { supported_ |= 1u << VFP3; found_by_runtime_probing_ |= 1u << VFP3; } + + if (OS::ArmCpuHasFeature(ARMv7)) { + supported_ |= 1u << ARMv7; + found_by_runtime_probing_ |= 1u << ARMv7; + } #endif } @@ -83,9 +93,9 @@ Register r4 = { 4 }; Register r5 = { 5 }; Register r6 = { 6 }; Register r7 = { 7 }; -Register r8 = { 8 }; +Register r8 = { 8 }; // Used as context register. Register r9 = { 9 }; -Register r10 = { 10 }; +Register r10 = { 10 }; // Used as roots register. Register fp = { 11 }; Register ip = { 12 }; Register sp = { 13 }; @@ -264,9 +274,9 @@ MemOperand::MemOperand(Register rn, Register rm, // ----------------------------------------------------------------------------- -// Implementation of Assembler +// Implementation of Assembler. -// Instruction encoding bits +// Instruction encoding bits. enum { H = 1 << 5, // halfword (or byte) S6 = 1 << 6, // signed (or unsigned) @@ -299,14 +309,14 @@ enum { B26 = 1 << 26, B27 = 1 << 27, - // Instruction bit masks + // Instruction bit masks. RdMask = 15 << 12, // in str instruction CondMask = 15 << 28, CoprocessorMask = 15 << 8, OpCodeMask = 15 << 21, // in data-processing instructions Imm24Mask = (1 << 24) - 1, Off12Mask = (1 << 12) - 1, - // Reserved condition + // Reserved condition. nv = 15 << 28 }; @@ -327,13 +337,13 @@ const Instr kMovLrPc = al | 13*B21 | pc.code() | lr.code() * B12; // ldr pc, [pc, #XXX] const Instr kLdrPCPattern = al | B26 | L | pc.code() * B16; -// spare_buffer_ +// Spare buffer. static const int kMinimalBufferSize = 4*KB; static byte* spare_buffer_ = NULL; Assembler::Assembler(void* buffer, int buffer_size) { if (buffer == NULL) { - // do our own buffer management + // Do our own buffer management. if (buffer_size <= kMinimalBufferSize) { buffer_size = kMinimalBufferSize; @@ -351,14 +361,14 @@ Assembler::Assembler(void* buffer, int buffer_size) { own_buffer_ = true; } else { - // use externally provided buffer instead + // Use externally provided buffer instead. ASSERT(buffer_size > 0); buffer_ = static_cast<byte*>(buffer); buffer_size_ = buffer_size; own_buffer_ = false; } - // setup buffer pointers + // Setup buffer pointers. ASSERT(buffer_ != NULL); pc_ = buffer_; reloc_info_writer.Reposition(buffer_ + buffer_size, pc_); @@ -386,11 +396,11 @@ Assembler::~Assembler() { void Assembler::GetCode(CodeDesc* desc) { - // emit constant pool if necessary + // Emit constant pool if necessary. CheckConstPool(true, false); ASSERT(num_prinfo_ == 0); - // setup desc + // Setup code descriptor. desc->buffer = buffer_; desc->buffer_size = buffer_size_; desc->instr_size = pc_offset(); @@ -539,7 +549,7 @@ void Assembler::bind_to(Label* L, int pos) { void Assembler::link_to(Label* L, Label* appendix) { if (appendix->is_linked()) { if (L->is_linked()) { - // append appendix to L's list + // Append appendix to L's list. int fixup_pos; int link = L->pos(); do { @@ -549,7 +559,7 @@ void Assembler::link_to(Label* L, Label* appendix) { ASSERT(link == kEndOfChain); target_at_put(fixup_pos, appendix->pos()); } else { - // L is empty, simply use appendix + // L is empty, simply use appendix. *L = *appendix; } } @@ -575,12 +585,12 @@ void Assembler::next(Label* L) { } -// Low-level code emission routines depending on the addressing mode +// Low-level code emission routines depending on the addressing mode. static bool fits_shifter(uint32_t imm32, uint32_t* rotate_imm, uint32_t* immed_8, Instr* instr) { - // imm32 must be unsigned + // imm32 must be unsigned. for (int rot = 0; rot < 16; rot++) { uint32_t imm8 = (imm32 << 2*rot) | (imm32 >> (32 - 2*rot)); if ((imm8 <= 0xff)) { @@ -589,7 +599,7 @@ static bool fits_shifter(uint32_t imm32, return true; } } - // if the opcode is mov or mvn and if ~imm32 fits, change the opcode + // If the opcode is mov or mvn and if ~imm32 fits, change the opcode. if (instr != NULL && (*instr & 0xd*B21) == 0xd*B21) { if (fits_shifter(~imm32, rotate_imm, immed_8, NULL)) { *instr ^= 0x2*B21; @@ -626,7 +636,7 @@ void Assembler::addrmod1(Instr instr, CheckBuffer(); ASSERT((instr & ~(CondMask | OpCodeMask | S)) == 0); if (!x.rm_.is_valid()) { - // immediate + // Immediate. uint32_t rotate_imm; uint32_t immed_8; if (MustUseIp(x.rmode_) || @@ -634,7 +644,7 @@ void Assembler::addrmod1(Instr instr, // The immediate operand cannot be encoded as a shifter operand, so load // it first to register ip and change the original instruction to use ip. // However, if the original instruction is a 'mov rd, x' (not setting the - // condition code), then replace it with a 'ldr rd, [pc]' + // condition code), then replace it with a 'ldr rd, [pc]'. RecordRelocInfo(x.rmode_, x.imm32_); CHECK(!rn.is(ip)); // rn should never be ip, or will be trashed Condition cond = static_cast<Condition>(instr & CondMask); @@ -648,16 +658,16 @@ void Assembler::addrmod1(Instr instr, } instr |= I | rotate_imm*B8 | immed_8; } else if (!x.rs_.is_valid()) { - // immediate shift + // Immediate shift. instr |= x.shift_imm_*B7 | x.shift_op_ | x.rm_.code(); } else { - // register shift + // Register shift. ASSERT(!rn.is(pc) && !rd.is(pc) && !x.rm_.is(pc) && !x.rs_.is(pc)); instr |= x.rs_.code()*B8 | x.shift_op_ | B4 | x.rm_.code(); } emit(instr | rn.code()*B16 | rd.code()*B12); if (rn.is(pc) || x.rm_.is(pc)) - // block constant pool emission for one instruction after reading pc + // Block constant pool emission for one instruction after reading pc. BlockConstPoolBefore(pc_offset() + kInstrSize); } @@ -666,15 +676,15 @@ void Assembler::addrmod2(Instr instr, Register rd, const MemOperand& x) { ASSERT((instr & ~(CondMask | B | L)) == B26); int am = x.am_; if (!x.rm_.is_valid()) { - // immediate offset + // Immediate offset. int offset_12 = x.offset_; if (offset_12 < 0) { offset_12 = -offset_12; am ^= U; } if (!is_uint12(offset_12)) { - // immediate offset cannot be encoded, load it first to register ip - // rn (and rd in a load) should never be ip, or will be trashed + // Immediate offset cannot be encoded, load it first to register ip + // rn (and rd in a load) should never be ip, or will be trashed. ASSERT(!x.rn_.is(ip) && ((instr & L) == L || !rd.is(ip))); mov(ip, Operand(x.offset_), LeaveCC, static_cast<Condition>(instr & CondMask)); @@ -684,9 +694,9 @@ void Assembler::addrmod2(Instr instr, Register rd, const MemOperand& x) { ASSERT(offset_12 >= 0); // no masking needed instr |= offset_12; } else { - // register offset (shift_imm_ and shift_op_ are 0) or scaled + // Register offset (shift_imm_ and shift_op_ are 0) or scaled // register offset the constructors make sure than both shift_imm_ - // and shift_op_ are initialized + // and shift_op_ are initialized. ASSERT(!x.rm_.is(pc)); instr |= B25 | x.shift_imm_*B7 | x.shift_op_ | x.rm_.code(); } @@ -700,15 +710,15 @@ void Assembler::addrmod3(Instr instr, Register rd, const MemOperand& x) { ASSERT(x.rn_.is_valid()); int am = x.am_; if (!x.rm_.is_valid()) { - // immediate offset + // Immediate offset. int offset_8 = x.offset_; if (offset_8 < 0) { offset_8 = -offset_8; am ^= U; } if (!is_uint8(offset_8)) { - // immediate offset cannot be encoded, load it first to register ip - // rn (and rd in a load) should never be ip, or will be trashed + // Immediate offset cannot be encoded, load it first to register ip + // rn (and rd in a load) should never be ip, or will be trashed. ASSERT(!x.rn_.is(ip) && ((instr & L) == L || !rd.is(ip))); mov(ip, Operand(x.offset_), LeaveCC, static_cast<Condition>(instr & CondMask)); @@ -718,15 +728,15 @@ void Assembler::addrmod3(Instr instr, Register rd, const MemOperand& x) { ASSERT(offset_8 >= 0); // no masking needed instr |= B | (offset_8 >> 4)*B8 | (offset_8 & 0xf); } else if (x.shift_imm_ != 0) { - // scaled register offset not supported, load index first - // rn (and rd in a load) should never be ip, or will be trashed + // Scaled register offset not supported, load index first + // rn (and rd in a load) should never be ip, or will be trashed. ASSERT(!x.rn_.is(ip) && ((instr & L) == L || !rd.is(ip))); mov(ip, Operand(x.rm_, x.shift_op_, x.shift_imm_), LeaveCC, static_cast<Condition>(instr & CondMask)); addrmod3(instr, rd, MemOperand(x.rn_, ip, x.am_)); return; } else { - // register offset + // Register offset. ASSERT((am & (P|W)) == P || !x.rm_.is(pc)); // no pc index with writeback instr |= x.rm_.code(); } @@ -744,7 +754,7 @@ void Assembler::addrmod4(Instr instr, Register rn, RegList rl) { void Assembler::addrmod5(Instr instr, CRegister crd, const MemOperand& x) { - // unindexed addressing is not encoded by this function + // Unindexed addressing is not encoded by this function. ASSERT_EQ((B27 | B26), (instr & ~(CondMask | CoprocessorMask | P | U | N | W | L))); ASSERT(x.rn_.is_valid() && !x.rm_.is_valid()); @@ -759,7 +769,7 @@ void Assembler::addrmod5(Instr instr, CRegister crd, const MemOperand& x) { ASSERT(is_uint8(offset_8)); // unsigned word offset must fit in a byte ASSERT((am & (P|W)) == P || !x.rn_.is(pc)); // no pc base with writeback - // post-indexed addressing requires W == 1; different than in addrmod2/3 + // Post-indexed addressing requires W == 1; different than in addrmod2/3. if ((am & P) == 0) am |= W; @@ -782,7 +792,7 @@ int Assembler::branch_offset(Label* L, bool jump_elimination_allowed) { } // Block the emission of the constant pool, since the branch instruction must - // be emitted at the pc offset recorded by the label + // be emitted at the pc offset recorded by the label. BlockConstPoolBefore(pc_offset() + kInstrSize); return target_pos - (pc_offset() + kPcLoadDelta); } @@ -804,7 +814,7 @@ void Assembler::label_at_put(Label* L, int at_offset) { } -// Branch instructions +// Branch instructions. void Assembler::b(int branch_offset, Condition cond) { ASSERT((branch_offset & 3) == 0); int imm24 = branch_offset >> 2; @@ -812,7 +822,7 @@ void Assembler::b(int branch_offset, Condition cond) { emit(cond | B27 | B25 | (imm24 & Imm24Mask)); if (cond == al) - // dead code is a good location to emit the constant pool + // Dead code is a good location to emit the constant pool. CheckConstPool(false, false); } @@ -849,7 +859,22 @@ void Assembler::bx(Register target, Condition cond) { // v5 and above, plus v4t } -// Data-processing instructions +// Data-processing instructions. + +// UBFX <Rd>,<Rn>,#<lsb>,#<width - 1> +// Instruction details available in ARM DDI 0406A, A8-464. +// cond(31-28) | 01111(27-23)| 1(22) | 1(21) | widthm1(20-16) | +// Rd(15-12) | lsb(11-7) | 101(6-4) | Rn(3-0) +void Assembler::ubfx(Register dst, Register src1, const Operand& src2, + const Operand& src3, Condition cond) { + ASSERT(!src2.rm_.is_valid() && !src3.rm_.is_valid()); + ASSERT(static_cast<uint32_t>(src2.imm32_) <= 0x1f); + ASSERT(static_cast<uint32_t>(src3.imm32_) <= 0x1f); + emit(cond | 0x3F*B21 | src3.imm32_*B16 | + dst.code()*B12 | src2.imm32_*B7 | 0x5*B4 | src1.code()); +} + + void Assembler::and_(Register dst, Register src1, const Operand& src2, SBit s, Condition cond) { addrmod1(cond | 0*B21 | s, src1, dst, src2); @@ -886,7 +911,7 @@ void Assembler::add(Register dst, Register src1, const Operand& src2, if (FLAG_push_pop_elimination && last_bound_pos_ <= (pc_offset() - pattern_size) && reloc_info_writer.last_pc() <= (pc_ - pattern_size) && - // pattern + // Pattern. instr_at(pc_ - 1 * kInstrSize) == kPopInstruction && (instr_at(pc_ - 2 * kInstrSize) & ~RdMask) == kPushRegPattern) { pc_ -= 2 * kInstrSize; @@ -960,7 +985,7 @@ void Assembler::mvn(Register dst, const Operand& src, SBit s, Condition cond) { } -// Multiply instructions +// Multiply instructions. void Assembler::mla(Register dst, Register src1, Register src2, Register srcA, SBit s, Condition cond) { ASSERT(!dst.is(pc) && !src1.is(pc) && !src2.is(pc) && !srcA.is(pc)); @@ -1029,7 +1054,7 @@ void Assembler::umull(Register dstL, } -// Miscellaneous arithmetic instructions +// Miscellaneous arithmetic instructions. void Assembler::clz(Register dst, Register src, Condition cond) { // v5 and above. ASSERT(!dst.is(pc) && !src.is(pc)); @@ -1038,7 +1063,7 @@ void Assembler::clz(Register dst, Register src, Condition cond) { } -// Status register access instructions +// Status register access instructions. void Assembler::mrs(Register dst, SRegister s, Condition cond) { ASSERT(!dst.is(pc)); emit(cond | B24 | s | 15*B16 | dst.code()*B12); @@ -1050,12 +1075,12 @@ void Assembler::msr(SRegisterFieldMask fields, const Operand& src, ASSERT(fields >= B16 && fields < B20); // at least one field set Instr instr; if (!src.rm_.is_valid()) { - // immediate + // Immediate. uint32_t rotate_imm; uint32_t immed_8; if (MustUseIp(src.rmode_) || !fits_shifter(src.imm32_, &rotate_imm, &immed_8, NULL)) { - // immediate operand cannot be encoded, load it first to register ip + // Immediate operand cannot be encoded, load it first to register ip. RecordRelocInfo(src.rmode_, src.imm32_); ldr(ip, MemOperand(pc, 0), cond); msr(fields, Operand(ip), cond); @@ -1070,7 +1095,7 @@ void Assembler::msr(SRegisterFieldMask fields, const Operand& src, } -// Load/Store instructions +// Load/Store instructions. void Assembler::ldr(Register dst, const MemOperand& src, Condition cond) { if (dst.is(pc)) { WriteRecordedPositions(); @@ -1085,7 +1110,7 @@ void Assembler::ldr(Register dst, const MemOperand& src, Condition cond) { if (FLAG_push_pop_elimination && last_bound_pos_ <= (pc_offset() - pattern_size) && reloc_info_writer.last_pc() <= (pc_ - pattern_size) && - // pattern + // Pattern. instr_at(pc_ - 1 * kInstrSize) == (kPopRegPattern | dst.code() * B12) && instr_at(pc_ - 2 * kInstrSize) == (kPushRegPattern | dst.code() * B12)) { pc_ -= 2 * kInstrSize; @@ -1106,6 +1131,7 @@ void Assembler::str(Register src, const MemOperand& dst, Condition cond) { if (FLAG_push_pop_elimination && last_bound_pos_ <= (pc_offset() - pattern_size) && reloc_info_writer.last_pc() <= (pc_ - pattern_size) && + // Pattern. instr_at(pc_ - 1 * kInstrSize) == (kPushRegPattern | src.code() * B12) && instr_at(pc_ - 2 * kInstrSize) == kPopInstruction) { pc_ -= 2 * kInstrSize; @@ -1147,17 +1173,17 @@ void Assembler::ldrsh(Register dst, const MemOperand& src, Condition cond) { } -// Load/Store multiple instructions +// Load/Store multiple instructions. void Assembler::ldm(BlockAddrMode am, Register base, RegList dst, Condition cond) { - // ABI stack constraint: ldmxx base, {..sp..} base != sp is not restartable + // ABI stack constraint: ldmxx base, {..sp..} base != sp is not restartable. ASSERT(base.is(sp) || (dst & sp.bit()) == 0); addrmod4(cond | B27 | am | L, base, dst); - // emit the constant pool after a function return implemented by ldm ..{..pc} + // Emit the constant pool after a function return implemented by ldm ..{..pc}. if (cond == al && (dst & pc.bit()) != 0) { // There is a slight chance that the ldm instruction was actually a call, // in which case it would be wrong to return into the constant pool; we @@ -1177,7 +1203,7 @@ void Assembler::stm(BlockAddrMode am, } -// Semaphore instructions +// Semaphore instructions. void Assembler::swp(Register dst, Register src, Register base, Condition cond) { ASSERT(!dst.is(pc) && !src.is(pc) && !base.is(pc)); ASSERT(!dst.is(base) && !src.is(base)); @@ -1197,7 +1223,7 @@ void Assembler::swpb(Register dst, } -// Exception-generating instructions and debugging support +// Exception-generating instructions and debugging support. void Assembler::stop(const char* msg) { #if !defined(__arm__) // The simulator handles these special instructions and stops execution. @@ -1222,7 +1248,7 @@ void Assembler::swi(uint32_t imm24, Condition cond) { } -// Coprocessor instructions +// Coprocessor instructions. void Assembler::cdp(Coprocessor coproc, int opcode_1, CRegister crd, @@ -1307,7 +1333,7 @@ void Assembler::ldc(Coprocessor coproc, int option, LFlag l, Condition cond) { - // unindexed addressing + // Unindexed addressing. ASSERT(is_uint8(option)); emit(cond | B27 | B26 | U | l | L | rn.code()*B16 | crd.code()*B12 | coproc*B8 | (option & 255)); @@ -1346,7 +1372,7 @@ void Assembler::stc(Coprocessor coproc, int option, LFlag l, Condition cond) { - // unindexed addressing + // Unindexed addressing. ASSERT(is_uint8(option)); emit(cond | B27 | B26 | U | l | rn.code()*B16 | crd.code()*B12 | coproc*B8 | (option & 255)); @@ -1371,6 +1397,36 @@ void Assembler::stc2(Coprocessor coproc, // Support for VFP. +void Assembler::vldr(const DwVfpRegister dst, + const Register base, + int offset, + const Condition cond) { + // Ddst = MEM(Rbase + offset). + // Instruction details available in ARM DDI 0406A, A8-628. + // cond(31-28) | 1101(27-24)| 1001(23-20) | Rbase(19-16) | + // Vdst(15-12) | 1011(11-8) | offset + ASSERT(CpuFeatures::IsEnabled(VFP3)); + ASSERT(offset % 4 == 0); + emit(cond | 0xD9*B20 | base.code()*B16 | dst.code()*B12 | + 0xB*B8 | ((offset / 4) & 255)); +} + + +void Assembler::vstr(const DwVfpRegister src, + const Register base, + int offset, + const Condition cond) { + // MEM(Rbase + offset) = Dsrc. + // Instruction details available in ARM DDI 0406A, A8-786. + // cond(31-28) | 1101(27-24)| 1000(23-20) | | Rbase(19-16) | + // Vsrc(15-12) | 1011(11-8) | (offset/4) + ASSERT(CpuFeatures::IsEnabled(VFP3)); + ASSERT(offset % 4 == 0); + emit(cond | 0xD8*B20 | base.code()*B16 | src.code()*B12 | + 0xB*B8 | ((offset / 4) & 255)); +} + + void Assembler::vmov(const DwVfpRegister dst, const Register src1, const Register src2, @@ -1434,7 +1490,7 @@ void Assembler::vcvt(const DwVfpRegister dst, const Condition cond) { // Dd = Sm (integer in Sm converted to IEEE 64-bit doubles in Dd). // Instruction details available in ARM DDI 0406A, A8-576. - // cond(31-28) | 11101(27-23)| D=?(22) | 11(21-20) | 1(19) |opc2=000(18-16) | + // cond(31-28) | 11101(27-23)| D=?(22) | 11(21-20) | 1(19) | opc2=000(18-16) | // Vd(15-12) | 101(11-9) | sz(8)=1 | op(7)=1 | 1(6) | M=?(5) | 0(4) | Vm(3-0) ASSERT(CpuFeatures::IsEnabled(VFP3)); emit(cond | 0xE*B24 | B23 | 0x3*B20 | B19 | @@ -1541,14 +1597,14 @@ void Assembler::vmrs(Register dst, Condition cond) { } -// Pseudo instructions +// Pseudo instructions. void Assembler::lea(Register dst, const MemOperand& x, SBit s, Condition cond) { int am = x.am_; if (!x.rm_.is_valid()) { - // immediate offset + // Immediate offset. if ((am & P) == 0) // post indexing mov(dst, Operand(x.rn_), s, cond); else if ((am & U) == 0) // negative indexing @@ -1582,7 +1638,7 @@ void Assembler::BlockConstPoolFor(int instructions) { } -// Debugging +// Debugging. void Assembler::RecordJSReturn() { WriteRecordedPositions(); CheckBuffer(); @@ -1635,7 +1691,7 @@ void Assembler::WriteRecordedPositions() { void Assembler::GrowBuffer() { if (!own_buffer_) FATAL("external code buffer is too small"); - // compute new buffer size + // Compute new buffer size. CodeDesc desc; // the new buffer if (buffer_size_ < 4*KB) { desc.buffer_size = 4*KB; @@ -1646,20 +1702,20 @@ void Assembler::GrowBuffer() { } CHECK_GT(desc.buffer_size, 0); // no overflow - // setup new buffer + // Setup new buffer. desc.buffer = NewArray<byte>(desc.buffer_size); desc.instr_size = pc_offset(); desc.reloc_size = (buffer_ + buffer_size_) - reloc_info_writer.pos(); - // copy the data + // Copy the data. int pc_delta = desc.buffer - buffer_; int rc_delta = (desc.buffer + desc.buffer_size) - (buffer_ + buffer_size_); memmove(desc.buffer, buffer_, desc.instr_size); memmove(reloc_info_writer.pos() + rc_delta, reloc_info_writer.pos(), desc.reloc_size); - // switch buffers + // Switch buffers. DeleteArray(buffer_); buffer_ = desc.buffer; buffer_size_ = desc.buffer_size; @@ -1667,11 +1723,11 @@ void Assembler::GrowBuffer() { reloc_info_writer.Reposition(reloc_info_writer.pos() + rc_delta, reloc_info_writer.last_pc() + pc_delta); - // none of our relocation types are pc relative pointing outside the code + // None of our relocation types are pc relative pointing outside the code // buffer nor pc absolute pointing inside the code buffer, so there is no need - // to relocate any emitted relocation entries + // to relocate any emitted relocation entries. - // relocate pending relocation entries + // Relocate pending relocation entries. for (int i = 0; i < num_prinfo_; i++) { RelocInfo& rinfo = prinfo_[i]; ASSERT(rinfo.rmode() != RelocInfo::COMMENT && @@ -1686,16 +1742,16 @@ void Assembler::GrowBuffer() { void Assembler::RecordRelocInfo(RelocInfo::Mode rmode, intptr_t data) { RelocInfo rinfo(pc_, rmode, data); // we do not try to reuse pool constants if (rmode >= RelocInfo::JS_RETURN && rmode <= RelocInfo::STATEMENT_POSITION) { - // Adjust code for new modes + // Adjust code for new modes. ASSERT(RelocInfo::IsJSReturn(rmode) || RelocInfo::IsComment(rmode) || RelocInfo::IsPosition(rmode)); - // these modes do not need an entry in the constant pool + // These modes do not need an entry in the constant pool. } else { ASSERT(num_prinfo_ < kMaxNumPRInfo); prinfo_[num_prinfo_++] = rinfo; // Make sure the constant pool is not emitted in place of the next - // instruction for which we just recorded relocation info + // instruction for which we just recorded relocation info. BlockConstPoolBefore(pc_offset() + kInstrSize); } if (rinfo.rmode() != RelocInfo::NONE) { @@ -1722,7 +1778,7 @@ void Assembler::CheckConstPool(bool force_emit, bool require_jump) { // blocked for a specific range. next_buffer_check_ = pc_offset() + kCheckConstInterval; - // There is nothing to do if there are no pending relocation info entries + // There is nothing to do if there are no pending relocation info entries. if (num_prinfo_ == 0) return; // We emit a constant pool at regular intervals of about kDistBetweenPools @@ -1748,10 +1804,11 @@ void Assembler::CheckConstPool(bool force_emit, bool require_jump) { // no_const_pool_before_, which is checked here. Also, recursive calls to // CheckConstPool are blocked by no_const_pool_before_. if (pc_offset() < no_const_pool_before_) { - // Emission is currently blocked; make sure we try again as soon as possible + // Emission is currently blocked; make sure we try again as soon as + // possible. next_buffer_check_ = no_const_pool_before_; - // Something is wrong if emission is forced and blocked at the same time + // Something is wrong if emission is forced and blocked at the same time. ASSERT(!force_emit); return; } @@ -1765,23 +1822,23 @@ void Assembler::CheckConstPool(bool force_emit, bool require_jump) { jump_instr + kInstrSize + num_prinfo_*(kInstrSize + kMaxRelocSize); while (buffer_space() <= (max_needed_space + kGap)) GrowBuffer(); - // Block recursive calls to CheckConstPool + // Block recursive calls to CheckConstPool. BlockConstPoolBefore(pc_offset() + jump_instr + kInstrSize + num_prinfo_*kInstrSize); // Don't bother to check for the emit calls below. next_buffer_check_ = no_const_pool_before_; - // Emit jump over constant pool if necessary + // Emit jump over constant pool if necessary. Label after_pool; if (require_jump) b(&after_pool); RecordComment("[ Constant Pool"); - // Put down constant pool marker - // "Undefined instruction" as specified by A3.1 Instruction set encoding + // Put down constant pool marker "Undefined instruction" as specified by + // A3.1 Instruction set encoding. emit(0x03000000 | num_prinfo_); - // Emit constant pool entries + // Emit constant pool entries. for (int i = 0; i < num_prinfo_; i++) { RelocInfo& rinfo = prinfo_[i]; ASSERT(rinfo.rmode() != RelocInfo::COMMENT && @@ -1789,8 +1846,8 @@ void Assembler::CheckConstPool(bool force_emit, bool require_jump) { rinfo.rmode() != RelocInfo::STATEMENT_POSITION); Instr instr = instr_at(rinfo.pc()); - // Instruction to patch must be a ldr/str [pc, #offset] - // P and U set, B and W clear, Rn == pc, offset12 still 0 + // Instruction to patch must be a ldr/str [pc, #offset]. + // P and U set, B and W clear, Rn == pc, offset12 still 0. ASSERT((instr & (7*B25 | P | U | B | W | 15*B16 | Off12Mask)) == (2*B25 | P | U | pc.code()*B16)); int delta = pc_ - rinfo.pc() - 8; diff --git a/src/arm/assembler-thumb2.h b/src/arm/assembler-thumb2.h index 9998e638..869ac461 100644 --- a/src/arm/assembler-thumb2.h +++ b/src/arm/assembler-thumb2.h @@ -30,9 +30,9 @@ // ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED // OF THE POSSIBILITY OF SUCH DAMAGE. -// The original source code covered by the above license above has been modified -// significantly by Google Inc. -// Copyright 2006-2008 the V8 project authors. All rights reserved. +// The original source code covered by the above license above has been +// modified significantly by Google Inc. +// Copyright 2010 the V8 project authors. All rights reserved. // A light-weight ARM Assembler // Generates user mode instructions for the ARM architecture up to version 5 @@ -250,7 +250,7 @@ enum Coprocessor { }; -// Condition field in instructions +// Condition field in instructions. enum Condition { eq = 0 << 28, // Z set equal. ne = 1 << 28, // Z clear not equal. @@ -398,8 +398,6 @@ class Operand BASE_EMBEDDED { RelocInfo::Mode rmode = RelocInfo::NONE)); INLINE(explicit Operand(const ExternalReference& f)); INLINE(explicit Operand(const char* s)); - INLINE(explicit Operand(Object** opp)); - INLINE(explicit Operand(Context** cpp)); explicit Operand(Handle<Object> handle); INLINE(explicit Operand(Smi* value)); @@ -630,6 +628,9 @@ class Assembler : public Malloced { void blx(Label* L) { blx(branch_offset(L, false)); } // v5 and above // Data-processing instructions + void ubfx(Register dst, Register src1, const Operand& src2, + const Operand& src3, Condition cond = al); + void and_(Register dst, Register src1, const Operand& src2, SBit s = LeaveCC, Condition cond = al); @@ -796,6 +797,14 @@ class Assembler : public Malloced { // However, some simple modifications can allow // these APIs to support D16 to D31. + void vldr(const DwVfpRegister dst, + const Register base, + int offset, // Offset must be a multiple of 4. + const Condition cond = al); + void vstr(const DwVfpRegister src, + const Register base, + int offset, // Offset must be a multiple of 4. + const Condition cond = al); void vmov(const DwVfpRegister dst, const Register src1, const Register src2, diff --git a/src/arm/builtins-arm.cc b/src/arm/builtins-arm.cc index ae7dae3b..edb1b0ae 100644 --- a/src/arm/builtins-arm.cc +++ b/src/arm/builtins-arm.cc @@ -499,7 +499,10 @@ void Builtins::Generate_JSConstructCall(MacroAssembler* masm) { // r0: number of arguments // r1: called object __ bind(&non_function_call); - + // CALL_NON_FUNCTION expects the non-function constructor as receiver + // (instead of the original receiver from the call site). The receiver is + // stack element argc. + __ str(r1, MemOperand(sp, r0, LSL, kPointerSizeLog2)); // Set expected number of arguments to zero (not changing r0). __ mov(r2, Operand(0)); __ GetBuiltinEntry(r3, Builtins::CALL_NON_FUNCTION_AS_CONSTRUCTOR); @@ -904,7 +907,7 @@ void Builtins::Generate_JSConstructEntryTrampoline(MacroAssembler* masm) { void Builtins::Generate_FunctionCall(MacroAssembler* masm) { // 1. Make sure we have at least one argument. - // r0: actual number of argument + // r0: actual number of arguments { Label done; __ tst(r0, Operand(r0)); __ b(ne, &done); @@ -914,40 +917,31 @@ void Builtins::Generate_FunctionCall(MacroAssembler* masm) { __ bind(&done); } - // 2. Get the function to call from the stack. - // r0: actual number of argument - { Label done, non_function, function; - __ ldr(r1, MemOperand(sp, r0, LSL, kPointerSizeLog2)); - __ tst(r1, Operand(kSmiTagMask)); - __ b(eq, &non_function); - __ CompareObjectType(r1, r2, r2, JS_FUNCTION_TYPE); - __ b(eq, &function); - - // Non-function called: Clear the function to force exception. - __ bind(&non_function); - __ mov(r1, Operand(0)); - __ b(&done); - - // Change the context eagerly because it will be used below to get the - // right global object. - __ bind(&function); - __ ldr(cp, FieldMemOperand(r1, JSFunction::kContextOffset)); - - __ bind(&done); - } + // 2. Get the function to call (passed as receiver) from the stack, check + // if it is a function. + // r0: actual number of arguments + Label non_function; + __ ldr(r1, MemOperand(sp, r0, LSL, kPointerSizeLog2)); + __ tst(r1, Operand(kSmiTagMask)); + __ b(eq, &non_function); + __ CompareObjectType(r1, r2, r2, JS_FUNCTION_TYPE); + __ b(ne, &non_function); - // 3. Make sure first argument is an object; convert if necessary. + // 3a. Patch the first argument if necessary when calling a function. // r0: actual number of arguments // r1: function - { Label call_to_object, use_global_receiver, patch_receiver, done; + Label shift_arguments; + { Label convert_to_object, use_global_receiver, patch_receiver; + // Change context eagerly in case we need the global receiver. + __ ldr(cp, FieldMemOperand(r1, JSFunction::kContextOffset)); + __ add(r2, sp, Operand(r0, LSL, kPointerSizeLog2)); __ ldr(r2, MemOperand(r2, -kPointerSize)); - // r0: actual number of arguments // r1: function // r2: first argument __ tst(r2, Operand(kSmiTagMask)); - __ b(eq, &call_to_object); + __ b(eq, &convert_to_object); __ LoadRoot(r3, Heap::kNullValueRootIndex); __ cmp(r2, r3); @@ -957,31 +951,28 @@ void Builtins::Generate_FunctionCall(MacroAssembler* masm) { __ b(eq, &use_global_receiver); __ CompareObjectType(r2, r3, r3, FIRST_JS_OBJECT_TYPE); - __ b(lt, &call_to_object); + __ b(lt, &convert_to_object); __ cmp(r3, Operand(LAST_JS_OBJECT_TYPE)); - __ b(le, &done); - - __ bind(&call_to_object); - __ EnterInternalFrame(); + __ b(le, &shift_arguments); - // Store number of arguments and function across the call into the runtime. - __ mov(r0, Operand(r0, LSL, kSmiTagSize)); + __ bind(&convert_to_object); + __ EnterInternalFrame(); // In order to preserve argument count. + __ mov(r0, Operand(r0, LSL, kSmiTagSize)); // Smi-tagged. __ push(r0); - __ push(r1); __ push(r2); __ InvokeBuiltin(Builtins::TO_OBJECT, CALL_JS); __ mov(r2, r0); - // Restore number of arguments and function. - __ pop(r1); __ pop(r0); __ mov(r0, Operand(r0, ASR, kSmiTagSize)); - __ LeaveInternalFrame(); - __ b(&patch_receiver); + // Restore the function to r1. + __ ldr(r1, MemOperand(sp, r0, LSL, kPointerSizeLog2)); + __ jmp(&patch_receiver); - // Use the global receiver object from the called function as the receiver. + // Use the global receiver object from the called function as the + // receiver. __ bind(&use_global_receiver); const int kGlobalIndex = Context::kHeaderSize + Context::GLOBAL_INDEX * kPointerSize; @@ -994,16 +985,30 @@ void Builtins::Generate_FunctionCall(MacroAssembler* masm) { __ add(r3, sp, Operand(r0, LSL, kPointerSizeLog2)); __ str(r2, MemOperand(r3, -kPointerSize)); - __ bind(&done); + __ jmp(&shift_arguments); } - // 4. Shift stuff one slot down the stack - // r0: actual number of arguments (including call() receiver) + // 3b. Patch the first argument when calling a non-function. The + // CALL_NON_FUNCTION builtin expects the non-function callee as + // receiver, so overwrite the first argument which will ultimately + // become the receiver. + // r0: actual number of arguments + // r1: function + __ bind(&non_function); + __ add(r2, sp, Operand(r0, LSL, kPointerSizeLog2)); + __ str(r1, MemOperand(r2, -kPointerSize)); + // Clear r1 to indicate a non-function being called. + __ mov(r1, Operand(0)); + + // 4. Shift arguments and return address one slot down on the stack + // (overwriting the original receiver). Adjust argument count to make + // the original first argument the new receiver. + // r0: actual number of arguments // r1: function + __ bind(&shift_arguments); { Label loop; // Calculate the copy start address (destination). Copy end address is sp. __ add(r2, sp, Operand(r0, LSL, kPointerSizeLog2)); - __ add(r2, r2, Operand(kPointerSize)); // copy receiver too __ bind(&loop); __ ldr(ip, MemOperand(r2, -kPointerSize)); @@ -1011,43 +1016,41 @@ void Builtins::Generate_FunctionCall(MacroAssembler* masm) { __ sub(r2, r2, Operand(kPointerSize)); __ cmp(r2, sp); __ b(ne, &loop); + // Adjust the actual number of arguments and remove the top element + // (which is a copy of the last argument). + __ sub(r0, r0, Operand(1)); + __ pop(); } - // 5. Adjust the actual number of arguments and remove the top element. - // r0: actual number of arguments (including call() receiver) - // r1: function - __ sub(r0, r0, Operand(1)); - __ add(sp, sp, Operand(kPointerSize)); - - // 6. Get the code for the function or the non-function builtin. - // If number of expected arguments matches, then call. Otherwise restart - // the arguments adaptor stub. + // 5a. Call non-function via tail call to CALL_NON_FUNCTION builtin. // r0: actual number of arguments // r1: function - { Label invoke; + { Label function; __ tst(r1, r1); - __ b(ne, &invoke); + __ b(ne, &function); __ mov(r2, Operand(0)); // expected arguments is 0 for CALL_NON_FUNCTION __ GetBuiltinEntry(r3, Builtins::CALL_NON_FUNCTION); __ Jump(Handle<Code>(builtin(ArgumentsAdaptorTrampoline)), RelocInfo::CODE_TARGET); + __ bind(&function); + } - __ bind(&invoke); - __ ldr(r3, FieldMemOperand(r1, JSFunction::kSharedFunctionInfoOffset)); - __ ldr(r2, - FieldMemOperand(r3, - SharedFunctionInfo::kFormalParameterCountOffset)); - __ ldr(r3, - MemOperand(r3, SharedFunctionInfo::kCodeOffset - kHeapObjectTag)); - __ add(r3, r3, Operand(Code::kHeaderSize - kHeapObjectTag)); - __ cmp(r2, r0); // Check formal and actual parameter counts. - __ Jump(Handle<Code>(builtin(ArgumentsAdaptorTrampoline)), - RelocInfo::CODE_TARGET, ne); + // 5b. Get the code to call from the function and check that the number of + // expected arguments matches what we're providing. If so, jump + // (tail-call) to the code in register edx without checking arguments. + // r0: actual number of arguments + // r1: function + __ ldr(r3, FieldMemOperand(r1, JSFunction::kSharedFunctionInfoOffset)); + __ ldr(r2, + FieldMemOperand(r3, SharedFunctionInfo::kFormalParameterCountOffset)); + __ ldr(r3, FieldMemOperand(r3, SharedFunctionInfo::kCodeOffset)); + __ add(r3, r3, Operand(Code::kHeaderSize - kHeapObjectTag)); + __ cmp(r2, r0); // Check formal and actual parameter counts. + __ Jump(Handle<Code>(builtin(ArgumentsAdaptorTrampoline)), + RelocInfo::CODE_TARGET, ne); - // 7. Jump to the code in r3 without checking arguments. - ParameterCount expected(0); - __ InvokeCode(r3, expected, expected, JUMP_FUNCTION); - } + ParameterCount expected(0); + __ InvokeCode(r3, expected, expected, JUMP_FUNCTION); } diff --git a/src/arm/codegen-arm.cc b/src/arm/codegen-arm.cc index 9afefac0..e47d3921 100644 --- a/src/arm/codegen-arm.cc +++ b/src/arm/codegen-arm.cc @@ -142,7 +142,7 @@ Scope* CodeGenerator::scope() { return info_->function()->scope(); } // r1: called JS function // cp: callee's context -void CodeGenerator::Generate(CompilationInfo* info, Mode mode) { +void CodeGenerator::Generate(CompilationInfo* info) { // Record the position for debugging purposes. CodeForFunctionPosition(info->function()); @@ -174,7 +174,7 @@ void CodeGenerator::Generate(CompilationInfo* info, Mode mode) { } #endif - if (mode == PRIMARY) { + if (info->mode() == CompilationInfo::PRIMARY) { frame_->Enter(); // tos: code slot @@ -277,6 +277,12 @@ void CodeGenerator::Generate(CompilationInfo* info, Mode mode) { frame_->Adjust(4); allocator_->Unuse(r1); allocator_->Unuse(lr); + + // Bind all the bailout labels to the beginning of the function. + List<CompilationInfo::Bailout*>* bailouts = info->bailouts(); + for (int i = 0; i < bailouts->length(); i++) { + __ bind(bailouts->at(i)->label()); + } } // Initialize the function return target after the locals are set @@ -2293,8 +2299,7 @@ void CodeGenerator::VisitDebuggerStatement(DebuggerStatement* node) { Comment cmnt(masm_, "[ DebuggerStatament"); CodeForStatementPosition(node); #ifdef ENABLE_DEBUGGER_SUPPORT - DebuggerStatementStub ces; - frame_->CallStub(&ces, 0); + frame_->DebugBreak(); #endif // Ignore the return value. ASSERT(frame_->height() == original_height); @@ -2719,9 +2724,9 @@ void CodeGenerator::VisitObjectLiteral(ObjectLiteral* node) { frame_->CallRuntime(Runtime::kCreateObjectLiteralShallow, 3); } frame_->EmitPush(r0); // save the result - // r0: created object literal - for (int i = 0; i < node->properties()->length(); i++) { + // At the start of each iteration, the top of stack contains + // the newly created object literal. ObjectLiteral::Property* property = node->properties()->at(i); Literal* key = property->key(); Expression* value = property->value(); @@ -2731,34 +2736,43 @@ void CodeGenerator::VisitObjectLiteral(ObjectLiteral* node) { case ObjectLiteral::Property::MATERIALIZED_LITERAL: if (CompileTimeValue::IsCompileTimeValue(property->value())) break; // else fall through - case ObjectLiteral::Property::COMPUTED: // fall through + case ObjectLiteral::Property::COMPUTED: + if (key->handle()->IsSymbol()) { + Handle<Code> ic(Builtins::builtin(Builtins::StoreIC_Initialize)); + LoadAndSpill(value); + frame_->EmitPop(r0); + __ mov(r2, Operand(key->handle())); + __ ldr(r1, frame_->Top()); // Load the receiver. + frame_->CallCodeObject(ic, RelocInfo::CODE_TARGET, 0); + break; + } + // else fall through case ObjectLiteral::Property::PROTOTYPE: { + __ ldr(r0, frame_->Top()); frame_->EmitPush(r0); // dup the result LoadAndSpill(key); LoadAndSpill(value); frame_->CallRuntime(Runtime::kSetProperty, 3); - // restore r0 - __ ldr(r0, frame_->Top()); break; } case ObjectLiteral::Property::SETTER: { + __ ldr(r0, frame_->Top()); frame_->EmitPush(r0); LoadAndSpill(key); __ mov(r0, Operand(Smi::FromInt(1))); frame_->EmitPush(r0); LoadAndSpill(value); frame_->CallRuntime(Runtime::kDefineAccessor, 4); - __ ldr(r0, frame_->Top()); break; } case ObjectLiteral::Property::GETTER: { + __ ldr(r0, frame_->Top()); frame_->EmitPush(r0); LoadAndSpill(key); __ mov(r0, Operand(Smi::FromInt(0))); frame_->EmitPush(r0); LoadAndSpill(value); frame_->CallRuntime(Runtime::kDefineAccessor, 4); - __ ldr(r0, frame_->Top()); break; } } @@ -2776,17 +2790,19 @@ void CodeGenerator::VisitArrayLiteral(ArrayLiteral* node) { // Load the function of this activation. __ ldr(r2, frame_->Function()); - // Literals array. + // Load the literals array of the function. __ ldr(r2, FieldMemOperand(r2, JSFunction::kLiteralsOffset)); - // Literal index. __ mov(r1, Operand(Smi::FromInt(node->literal_index()))); - // Constant elements. __ mov(r0, Operand(node->constant_elements())); frame_->EmitPushMultiple(3, r2.bit() | r1.bit() | r0.bit()); + int length = node->values()->length(); if (node->depth() > 1) { frame_->CallRuntime(Runtime::kCreateArrayLiteral, 3); - } else { + } else if (length > FastCloneShallowArrayStub::kMaximumLength) { frame_->CallRuntime(Runtime::kCreateArrayLiteralShallow, 3); + } else { + FastCloneShallowArrayStub stub(length); + frame_->CallStub(&stub, 3); } frame_->EmitPush(r0); // save the result // r0: created object literal @@ -3013,11 +3029,6 @@ void CodeGenerator::VisitCall(Call* node) { // ---------------------------------- // JavaScript example: 'foo(1, 2, 3)' // foo is global // ---------------------------------- - - // Push the name of the function and the receiver onto the stack. - __ mov(r0, Operand(var->name())); - frame_->EmitPush(r0); - // Pass the global object as the receiver and let the IC stub // patch the stack to use the global proxy as 'this' in the // invoked function. @@ -3029,15 +3040,14 @@ void CodeGenerator::VisitCall(Call* node) { LoadAndSpill(args->at(i)); } - // Setup the receiver register and call the IC initialization code. + // Setup the name register and call the IC initialization code. + __ mov(r2, Operand(var->name())); InLoopFlag in_loop = loop_nesting() > 0 ? IN_LOOP : NOT_IN_LOOP; Handle<Code> stub = ComputeCallInitialize(arg_count, in_loop); CodeForSourcePosition(node->position()); frame_->CallCodeObject(stub, RelocInfo::CODE_TARGET_CONTEXT, arg_count + 1); __ ldr(cp, frame_->Context()); - // Remove the function from the stack. - frame_->Drop(); frame_->EmitPush(r0); } else if (var != NULL && var->slot() != NULL && @@ -3070,28 +3080,21 @@ void CodeGenerator::VisitCall(Call* node) { // JavaScript example: 'object.foo(1, 2, 3)' or 'map["key"](1, 2, 3)' // ------------------------------------------------------------------ - // Push the name of the function and the receiver onto the stack. - __ mov(r0, Operand(literal->handle())); - frame_->EmitPush(r0); - LoadAndSpill(property->obj()); - + LoadAndSpill(property->obj()); // Receiver. // Load the arguments. int arg_count = args->length(); for (int i = 0; i < arg_count; i++) { LoadAndSpill(args->at(i)); } - // Set the receiver register and call the IC initialization code. + // Set the name register and call the IC initialization code. + __ mov(r2, Operand(literal->handle())); InLoopFlag in_loop = loop_nesting() > 0 ? IN_LOOP : NOT_IN_LOOP; Handle<Code> stub = ComputeCallInitialize(arg_count, in_loop); CodeForSourcePosition(node->position()); frame_->CallCodeObject(stub, RelocInfo::CODE_TARGET, arg_count + 1); __ ldr(cp, frame_->Context()); - - // Remove the function from the stack. - frame_->Drop(); - - frame_->EmitPush(r0); // push after get rid of function from the stack + frame_->EmitPush(r0); } else { // ------------------------------------------- @@ -3423,6 +3426,25 @@ void CodeGenerator::GenerateIsArray(ZoneList<Expression*>* args) { } +void CodeGenerator::GenerateIsRegExp(ZoneList<Expression*>* args) { + VirtualFrame::SpilledScope spilled_scope; + ASSERT(args->length() == 1); + LoadAndSpill(args->at(0)); + JumpTarget answer; + // We need the CC bits to come out as not_equal in the case where the + // object is a smi. This can't be done with the usual test opcode so + // we use XOR to get the right CC bits. + frame_->EmitPop(r0); + __ and_(r1, r0, Operand(kSmiTagMask)); + __ eor(r1, r1, Operand(kSmiTagMask), SetCC); + answer.Branch(ne); + // It is a heap object - get the map. Check if the object is a regexp. + __ CompareObjectType(r0, r1, r1, JS_REGEXP_TYPE); + answer.Bind(); + cc_reg_ = eq; +} + + void CodeGenerator::GenerateIsObject(ZoneList<Expression*>* args) { // This generates a fast version of: // (typeof(arg) === 'object' || %_ClassOf(arg) == 'RegExp') @@ -3595,6 +3617,35 @@ void CodeGenerator::GenerateRegExpExec(ZoneList<Expression*>* args) { } +void CodeGenerator::GenerateNumberToString(ZoneList<Expression*>* args) { + ASSERT_EQ(args->length(), 1); + + // Load the argument on the stack and jump to the runtime. + Load(args->at(0)); + + frame_->CallRuntime(Runtime::kNumberToString, 1); + frame_->EmitPush(r0); +} + + +void CodeGenerator::GenerateMathSin(ZoneList<Expression*>* args) { + ASSERT_EQ(args->length(), 1); + // Load the argument on the stack and jump to the runtime. + Load(args->at(0)); + frame_->CallRuntime(Runtime::kMath_sin, 1); + frame_->EmitPush(r0); +} + + +void CodeGenerator::GenerateMathCos(ZoneList<Expression*>* args) { + ASSERT_EQ(args->length(), 1); + // Load the argument on the stack and jump to the runtime. + Load(args->at(0)); + frame_->CallRuntime(Runtime::kMath_cos, 1); + frame_->EmitPush(r0); +} + + void CodeGenerator::GenerateObjectEquals(ZoneList<Expression*>* args) { VirtualFrame::SpilledScope spilled_scope; ASSERT(args->length() == 2); @@ -3626,8 +3677,6 @@ void CodeGenerator::VisitCallRuntime(CallRuntime* node) { if (function == NULL) { // Prepare stack for calling JS runtime function. - __ mov(r0, Operand(node->name())); - frame_->EmitPush(r0); // Push the builtins object found in the current global object. __ ldr(r1, GlobalObject()); __ ldr(r0, FieldMemOperand(r1, GlobalObject::kBuiltinsOffset)); @@ -3642,11 +3691,11 @@ void CodeGenerator::VisitCallRuntime(CallRuntime* node) { if (function == NULL) { // Call the JS runtime function. + __ mov(r2, Operand(node->name())); InLoopFlag in_loop = loop_nesting() > 0 ? IN_LOOP : NOT_IN_LOOP; Handle<Code> stub = ComputeCallInitialize(arg_count, in_loop); frame_->CallCodeObject(stub, RelocInfo::CODE_TARGET, arg_count + 1); __ ldr(cp, frame_->Context()); - frame_->Drop(); frame_->EmitPush(r0); } else { // Call the C runtime function. @@ -4389,11 +4438,11 @@ void Reference::SetValue(InitState init_state) { Handle<String> name(GetName()); frame->EmitPop(r0); - // Setup the name register. + frame->EmitPop(r1); __ mov(r2, Operand(name)); frame->CallCodeObject(ic, RelocInfo::CODE_TARGET, 0); frame->EmitPush(r0); - cgen_->UnloadReference(this); + set_unloaded(); break; } @@ -4405,7 +4454,6 @@ void Reference::SetValue(InitState init_state) { // Call IC code. Handle<Code> ic(Builtins::builtin(Builtins::KeyedStoreIC_Initialize)); - // TODO(1222589): Make the IC grab the values from the stack. frame->EmitPop(r0); // value frame->CallCodeObject(ic, RelocInfo::CODE_TARGET, 0); frame->EmitPush(r0); @@ -4480,7 +4528,7 @@ void FastNewContextStub::Generate(MacroAssembler* masm) { TAG_OBJECT); // Load the function from the stack. - __ ldr(r3, MemOperand(sp, 0 * kPointerSize)); + __ ldr(r3, MemOperand(sp, 0)); // Setup the object header. __ LoadRoot(r2, Heap::kContextMapRootIndex); @@ -4516,6 +4564,69 @@ void FastNewContextStub::Generate(MacroAssembler* masm) { } +void FastCloneShallowArrayStub::Generate(MacroAssembler* masm) { + // Stack layout on entry: + // + // [sp]: constant elements. + // [sp + kPointerSize]: literal index. + // [sp + (2 * kPointerSize)]: literals array. + + // All sizes here are multiples of kPointerSize. + int elements_size = (length_ > 0) ? FixedArray::SizeFor(length_) : 0; + int size = JSArray::kSize + elements_size; + + // Load boilerplate object into r3 and check if we need to create a + // boilerplate. + Label slow_case; + __ ldr(r3, MemOperand(sp, 2 * kPointerSize)); + __ ldr(r0, MemOperand(sp, 1 * kPointerSize)); + __ add(r3, r3, Operand(FixedArray::kHeaderSize - kHeapObjectTag)); + __ ldr(r3, MemOperand(r3, r0, LSL, kPointerSizeLog2 - kSmiTagSize)); + __ LoadRoot(ip, Heap::kUndefinedValueRootIndex); + __ cmp(r3, ip); + __ b(eq, &slow_case); + + // Allocate both the JS array and the elements array in one big + // allocation. This avoids multiple limit checks. + __ AllocateInNewSpace(size / kPointerSize, + r0, + r1, + r2, + &slow_case, + TAG_OBJECT); + + // Copy the JS array part. + for (int i = 0; i < JSArray::kSize; i += kPointerSize) { + if ((i != JSArray::kElementsOffset) || (length_ == 0)) { + __ ldr(r1, FieldMemOperand(r3, i)); + __ str(r1, FieldMemOperand(r0, i)); + } + } + + if (length_ > 0) { + // Get hold of the elements array of the boilerplate and setup the + // elements pointer in the resulting object. + __ ldr(r3, FieldMemOperand(r3, JSArray::kElementsOffset)); + __ add(r2, r0, Operand(JSArray::kSize)); + __ str(r2, FieldMemOperand(r0, JSArray::kElementsOffset)); + + // Copy the elements array. + for (int i = 0; i < elements_size; i += kPointerSize) { + __ ldr(r1, FieldMemOperand(r3, i)); + __ str(r1, FieldMemOperand(r2, i)); + } + } + + // Return and remove the on-stack parameters. + __ add(sp, sp, Operand(3 * kPointerSize)); + __ Ret(); + + __ bind(&slow_case); + ExternalReference runtime(Runtime::kCreateArrayLiteralShallow); + __ TailCallRuntime(runtime, 3, 1); +} + + // Count leading zeros in a 32 bit word. On ARM5 and later it uses the clz // instruction. On pre-ARM5 hardware this routine gives the wrong answer for 0 // (31 instead of 32). @@ -6584,7 +6695,7 @@ void InstanceofStub::Generate(MacroAssembler* masm) { __ b(gt, &slow); // Get the prototype of the function (r4 is result, r2 is scratch). - __ ldr(r1, MemOperand(sp, 0 * kPointerSize)); + __ ldr(r1, MemOperand(sp, 0)); __ TryGetFunctionPrototype(r1, r4, r2, &slow); // Check that the function prototype is a JS object. @@ -6699,20 +6810,102 @@ void ArgumentsAccessStub::GenerateReadElement(MacroAssembler* masm) { void ArgumentsAccessStub::GenerateNewObject(MacroAssembler* masm) { + // sp[0] : number of parameters + // sp[4] : receiver displacement + // sp[8] : function + // Check if the calling frame is an arguments adaptor frame. - Label runtime; + Label adaptor_frame, try_allocate, runtime; __ ldr(r2, MemOperand(fp, StandardFrameConstants::kCallerFPOffset)); __ ldr(r3, MemOperand(r2, StandardFrameConstants::kContextOffset)); __ cmp(r3, Operand(Smi::FromInt(StackFrame::ARGUMENTS_ADAPTOR))); - __ b(ne, &runtime); + __ b(eq, &adaptor_frame); + + // Get the length from the frame. + __ ldr(r1, MemOperand(sp, 0)); + __ b(&try_allocate); // Patch the arguments.length and the parameters pointer. - __ ldr(r0, MemOperand(r2, ArgumentsAdaptorFrameConstants::kLengthOffset)); - __ str(r0, MemOperand(sp, 0 * kPointerSize)); - __ add(r3, r2, Operand(r0, LSL, kPointerSizeLog2 - kSmiTagSize)); + __ bind(&adaptor_frame); + __ ldr(r1, MemOperand(r2, ArgumentsAdaptorFrameConstants::kLengthOffset)); + __ str(r1, MemOperand(sp, 0)); + __ add(r3, r2, Operand(r1, LSL, kPointerSizeLog2 - kSmiTagSize)); __ add(r3, r3, Operand(StandardFrameConstants::kCallerSPOffset)); __ str(r3, MemOperand(sp, 1 * kPointerSize)); + // Try the new space allocation. Start out with computing the size + // of the arguments object and the elements array (in words, not + // bytes because AllocateInNewSpace expects words). + Label add_arguments_object; + __ bind(&try_allocate); + __ cmp(r1, Operand(0)); + __ b(eq, &add_arguments_object); + __ mov(r1, Operand(r1, LSR, kSmiTagSize)); + __ add(r1, r1, Operand(FixedArray::kHeaderSize / kPointerSize)); + __ bind(&add_arguments_object); + __ add(r1, r1, Operand(Heap::kArgumentsObjectSize / kPointerSize)); + + // Do the allocation of both objects in one go. + __ AllocateInNewSpace(r1, r0, r2, r3, &runtime, TAG_OBJECT); + + // Get the arguments boilerplate from the current (global) context. + int offset = Context::SlotOffset(Context::ARGUMENTS_BOILERPLATE_INDEX); + __ ldr(r4, MemOperand(cp, Context::SlotOffset(Context::GLOBAL_INDEX))); + __ ldr(r4, FieldMemOperand(r4, GlobalObject::kGlobalContextOffset)); + __ ldr(r4, MemOperand(r4, offset)); + + // Copy the JS object part. + for (int i = 0; i < JSObject::kHeaderSize; i += kPointerSize) { + __ ldr(r3, FieldMemOperand(r4, i)); + __ str(r3, FieldMemOperand(r0, i)); + } + + // Setup the callee in-object property. + ASSERT(Heap::arguments_callee_index == 0); + __ ldr(r3, MemOperand(sp, 2 * kPointerSize)); + __ str(r3, FieldMemOperand(r0, JSObject::kHeaderSize)); + + // Get the length (smi tagged) and set that as an in-object property too. + ASSERT(Heap::arguments_length_index == 1); + __ ldr(r1, MemOperand(sp, 0 * kPointerSize)); + __ str(r1, FieldMemOperand(r0, JSObject::kHeaderSize + kPointerSize)); + + // If there are no actual arguments, we're done. + Label done; + __ cmp(r1, Operand(0)); + __ b(eq, &done); + + // Get the parameters pointer from the stack and untag the length. + __ ldr(r2, MemOperand(sp, 1 * kPointerSize)); + __ mov(r1, Operand(r1, LSR, kSmiTagSize)); + + // Setup the elements pointer in the allocated arguments object and + // initialize the header in the elements fixed array. + __ add(r4, r0, Operand(Heap::kArgumentsObjectSize)); + __ str(r4, FieldMemOperand(r0, JSObject::kElementsOffset)); + __ LoadRoot(r3, Heap::kFixedArrayMapRootIndex); + __ str(r3, FieldMemOperand(r4, FixedArray::kMapOffset)); + __ str(r1, FieldMemOperand(r4, FixedArray::kLengthOffset)); + + // Copy the fixed array slots. + Label loop; + // Setup r4 to point to the first array slot. + __ add(r4, r4, Operand(FixedArray::kHeaderSize - kHeapObjectTag)); + __ bind(&loop); + // Pre-decrement r2 with kPointerSize on each iteration. + // Pre-decrement in order to skip receiver. + __ ldr(r3, MemOperand(r2, kPointerSize, NegPreIndex)); + // Post-increment r4 with kPointerSize on each iteration. + __ str(r3, MemOperand(r4, kPointerSize, PostIndex)); + __ sub(r1, r1, Operand(1)); + __ cmp(r1, Operand(0)); + __ b(ne, &loop); + + // Return and remove the on-stack parameters. + __ bind(&done); + __ add(sp, sp, Operand(3 * kPointerSize)); + __ Ret(); + // Do the runtime call to allocate the arguments object. __ bind(&runtime); __ TailCallRuntime(ExternalReference(Runtime::kNewArgumentsFast), 3, 1); @@ -6766,6 +6959,9 @@ void CallFunctionStub::Generate(MacroAssembler* masm) { // Slow-case: Non-function called. __ bind(&slow); + // CALL_NON_FUNCTION expects the non-function callee as receiver (instead + // of the original receiver from the call site). + __ str(r1, MemOperand(sp, argc_ * kPointerSize)); __ mov(r0, Operand(argc_)); // Setup the number of arguments. __ mov(r2, Operand(0)); __ GetBuiltinEntry(r3, Builtins::CALL_NON_FUNCTION); diff --git a/src/arm/codegen-arm.h b/src/arm/codegen-arm.h index 2578a398..22dd854a 100644 --- a/src/arm/codegen-arm.h +++ b/src/arm/codegen-arm.h @@ -150,15 +150,6 @@ class CodeGenState BASE_EMBEDDED { class CodeGenerator: public AstVisitor { public: - // Compilation mode. Either the compiler is used as the primary - // compiler and needs to setup everything or the compiler is used as - // the secondary compiler for split compilation and has to handle - // bailouts. - enum Mode { - PRIMARY, - SECONDARY - }; - // Takes a function literal, generates code for it. This function should only // be called by compiler.cc. static Handle<Code> MakeCode(CompilationInfo* info); @@ -244,7 +235,7 @@ class CodeGenerator: public AstVisitor { inline void VisitStatementsAndSpill(ZoneList<Statement*>* statements); // Main code generation function - void Generate(CompilationInfo* info, Mode mode); + void Generate(CompilationInfo* info); // The following are used by class Reference. void LoadReference(Reference* ref); @@ -359,6 +350,7 @@ class CodeGenerator: public AstVisitor { void GenerateIsSmi(ZoneList<Expression*>* args); void GenerateIsNonNegativeSmi(ZoneList<Expression*>* args); void GenerateIsArray(ZoneList<Expression*>* args); + void GenerateIsRegExp(ZoneList<Expression*>* args); void GenerateIsObject(ZoneList<Expression*>* args); void GenerateIsFunction(ZoneList<Expression*>* args); void GenerateIsUndetectableObject(ZoneList<Expression*>* args); @@ -398,6 +390,13 @@ class CodeGenerator: public AstVisitor { // Support for direct calls from JavaScript to native RegExp code. void GenerateRegExpExec(ZoneList<Expression*>* args); + // Fast support for number to string. + void GenerateNumberToString(ZoneList<Expression*>* args); + + // Fast call to sine function. + void GenerateMathSin(ZoneList<Expression*>* args); + void GenerateMathCos(ZoneList<Expression*>* args); + // Simple condition analysis. enum ConditionAnalysis { ALWAYS_TRUE, diff --git a/src/arm/cpu-arm.cc b/src/arm/cpu-arm.cc index 4e39cdaf..55f31d46 100644 --- a/src/arm/cpu-arm.cc +++ b/src/arm/cpu-arm.cc @@ -122,7 +122,7 @@ void CPU::FlushICache(void* start, size_t size) { void CPU::DebugBreak() { -#if !defined (__arm__) +#if !defined (__arm__) || !defined(CAN_USE_ARMV5_INSTRUCTIONS) UNIMPLEMENTED(); // when building ARM emulator target #else asm volatile("bkpt 0"); diff --git a/src/arm/debug-arm.cc b/src/arm/debug-arm.cc index 6eb5239b..e6b61b4d 100644 --- a/src/arm/debug-arm.cc +++ b/src/arm/debug-arm.cc @@ -128,7 +128,7 @@ void Debug::GenerateLoadICDebugBreak(MacroAssembler* masm) { // -- lr : return address // -- [sp] : receiver // ----------------------------------- - // Registers r0 and r2 contain objects that needs to be pushed on the + // Registers r0 and r2 contain objects that need to be pushed on the // expression stack of the fake JS frame. Generate_DebugBreakCallHelper(masm, r0.bit() | r2.bit()); } @@ -137,14 +137,14 @@ void Debug::GenerateLoadICDebugBreak(MacroAssembler* masm) { void Debug::GenerateStoreICDebugBreak(MacroAssembler* masm) { // Calling convention for IC store (from ic-arm.cc). // ----------- S t a t e ------------- - // -- r0 : receiver + // -- r0 : value + // -- r1 : receiver // -- r2 : name // -- lr : return address - // -- [sp] : receiver // ----------------------------------- - // Registers r0 and r2 contain objects that needs to be pushed on the + // Registers r0, r1, and r2 contain objects that need to be pushed on the // expression stack of the fake JS frame. - Generate_DebugBreakCallHelper(masm, r0.bit() | r2.bit()); + Generate_DebugBreakCallHelper(masm, r0.bit() | r1.bit() | r2.bit()); } diff --git a/src/arm/fast-codegen-arm.cc b/src/arm/fast-codegen-arm.cc index 80da5336..aa7128fc 100644 --- a/src/arm/fast-codegen-arm.cc +++ b/src/arm/fast-codegen-arm.cc @@ -35,55 +35,86 @@ namespace internal { #define __ ACCESS_MASM(masm()) -void FastCodeGenerator::EmitLoadReceiver(Register reg) { +Register FastCodeGenerator::accumulator0() { return r0; } +Register FastCodeGenerator::accumulator1() { return r1; } +Register FastCodeGenerator::scratch0() { return r3; } +Register FastCodeGenerator::scratch1() { return r4; } +Register FastCodeGenerator::receiver_reg() { return r2; } +Register FastCodeGenerator::context_reg() { return cp; } + + +void FastCodeGenerator::EmitLoadReceiver() { // Offset 2 is due to return address and saved frame pointer. int index = 2 + scope()->num_parameters(); - __ ldr(reg, MemOperand(sp, index * kPointerSize)); + __ ldr(receiver_reg(), MemOperand(sp, index * kPointerSize)); } -void FastCodeGenerator::EmitReceiverMapCheck() { - Comment cmnt(masm(), ";; MapCheck(this)"); - if (FLAG_print_ir) { - PrintF("MapCheck(this)\n"); - } +void FastCodeGenerator::EmitGlobalVariableLoad(Handle<Object> cell) { + ASSERT(!destination().is(no_reg)); + ASSERT(cell->IsJSGlobalPropertyCell()); - ASSERT(info()->has_receiver() && info()->receiver()->IsHeapObject()); - Handle<HeapObject> object = Handle<HeapObject>::cast(info()->receiver()); - Handle<Map> map(object->map()); + __ mov(destination(), Operand(cell)); + __ ldr(destination(), + FieldMemOperand(destination(), JSGlobalPropertyCell::kValueOffset)); + if (FLAG_debug_code) { + __ mov(ip, Operand(Factory::the_hole_value())); + __ cmp(destination(), ip); + __ Check(ne, "DontDelete cells can't contain the hole"); + } - EmitLoadReceiver(r1); - __ CheckMap(r1, r3, map, bailout(), false); + // The loaded value is not known to be a smi. + clear_as_smi(destination()); } -void FastCodeGenerator::EmitGlobalMapCheck() { - Comment cmnt(masm(), ";; GlobalMapCheck"); - if (FLAG_print_ir) { - PrintF(";; GlobalMapCheck()"); - } +void FastCodeGenerator::EmitThisPropertyStore(Handle<String> name) { + LookupResult lookup; + info()->receiver()->Lookup(*name, &lookup); - ASSERT(info()->has_global_object()); - Handle<Map> map(info()->global_object()->map()); + ASSERT(lookup.holder() == *info()->receiver()); + ASSERT(lookup.type() == FIELD); + Handle<Map> map(Handle<HeapObject>::cast(info()->receiver())->map()); + int index = lookup.GetFieldIndex() - map->inobject_properties(); + int offset = index * kPointerSize; - __ ldr(r3, CodeGenerator::GlobalObject()); - __ CheckMap(r3, r3, map, bailout(), true); -} + // We will emit the write barrier unless the stored value is statically + // known to be a smi. + bool needs_write_barrier = !is_smi(accumulator0()); + // Negative offsets are inobject properties. + if (offset < 0) { + offset += map->instance_size(); + __ str(accumulator0(), FieldMemOperand(receiver_reg(), offset)); + if (needs_write_barrier) { + // Preserve receiver from write barrier. + __ mov(scratch0(), receiver_reg()); + } + } else { + offset += FixedArray::kHeaderSize; + __ ldr(scratch0(), + FieldMemOperand(receiver_reg(), JSObject::kPropertiesOffset)); + __ str(accumulator0(), FieldMemOperand(scratch0(), offset)); + } -void FastCodeGenerator::EmitGlobalVariableLoad(Handle<Object> cell) { - ASSERT(cell->IsJSGlobalPropertyCell()); - __ mov(r0, Operand(cell)); - __ ldr(r0, FieldMemOperand(r0, JSGlobalPropertyCell::kValueOffset)); - if (FLAG_debug_code) { - __ mov(ip, Operand(Factory::the_hole_value())); - __ cmp(r0, ip); - __ Check(ne, "DontDelete cells can't contain the hole"); + if (needs_write_barrier) { + __ mov(scratch1(), Operand(offset)); + __ RecordWrite(scratch0(), scratch1(), ip); + } + + if (destination().is(accumulator1())) { + __ mov(accumulator1(), accumulator0()); + if (is_smi(accumulator0())) { + set_as_smi(accumulator1()); + } else { + clear_as_smi(accumulator1()); + } } } -void FastCodeGenerator::EmitThisPropertyStore(Handle<String> name) { +void FastCodeGenerator::EmitThisPropertyLoad(Handle<String> name) { + ASSERT(!destination().is(no_reg)); LookupResult lookup; info()->receiver()->Lookup(*name, &lookup); @@ -93,18 +124,55 @@ void FastCodeGenerator::EmitThisPropertyStore(Handle<String> name) { int index = lookup.GetFieldIndex() - map->inobject_properties(); int offset = index * kPointerSize; - // Negative offsets are inobject properties. + // Perform the load. Negative offsets are inobject properties. if (offset < 0) { offset += map->instance_size(); - __ mov(r2, r1); // Copy receiver for write barrier. + __ ldr(destination(), FieldMemOperand(receiver_reg(), offset)); } else { offset += FixedArray::kHeaderSize; - __ ldr(r2, FieldMemOperand(r1, JSObject::kPropertiesOffset)); + __ ldr(scratch0(), + FieldMemOperand(receiver_reg(), JSObject::kPropertiesOffset)); + __ ldr(destination(), FieldMemOperand(scratch0(), offset)); + } + + // The loaded value is not known to be a smi. + clear_as_smi(destination()); +} + + +void FastCodeGenerator::EmitBitOr() { + if (is_smi(accumulator0()) && is_smi(accumulator1())) { + // If both operands are known to be a smi then there is no need to check + // the operands or result. There is no need to perform the operation in + // an effect context. + if (!destination().is(no_reg)) { + __ orr(destination(), accumulator1(), Operand(accumulator0())); + } + } else { + // Left is in accumulator1, right in accumulator0. + if (destination().is(accumulator0())) { + __ mov(scratch0(), accumulator0()); + __ orr(destination(), accumulator1(), Operand(accumulator1())); + Label* bailout = + info()->AddBailout(accumulator1(), scratch0()); // Left, right. + __ BranchOnNotSmi(destination(), bailout); + } else if (destination().is(accumulator1())) { + __ mov(scratch0(), accumulator1()); + __ orr(destination(), accumulator1(), Operand(accumulator0())); + Label* bailout = info()->AddBailout(scratch0(), accumulator0()); + __ BranchOnNotSmi(destination(), bailout); + } else { + ASSERT(destination().is(no_reg)); + __ orr(scratch0(), accumulator1(), Operand(accumulator0())); + Label* bailout = info()->AddBailout(accumulator1(), accumulator0()); + __ BranchOnNotSmi(scratch0(), bailout); + } } - // Perform the store. - __ str(r0, FieldMemOperand(r2, offset)); - __ mov(r3, Operand(offset)); - __ RecordWrite(r2, r3, ip); + + // If we didn't bailout, the result (in fact, both inputs too) is known to + // be a smi. + set_as_smi(accumulator0()); + set_as_smi(accumulator1()); } @@ -119,26 +187,45 @@ void FastCodeGenerator::Generate(CompilationInfo* compilation_info) { // Note that we keep a live register reference to cp (context) at // this point. - // Receiver (this) is allocated to r1 if there are this properties. - if (info()->has_this_properties()) EmitReceiverMapCheck(); + Label* bailout_to_beginning = info()->AddBailout(); + // Receiver (this) is allocated to a fixed register. + if (info()->has_this_properties()) { + Comment cmnt(masm(), ";; MapCheck(this)"); + if (FLAG_print_ir) { + PrintF("MapCheck(this)\n"); + } + ASSERT(info()->has_receiver() && info()->receiver()->IsHeapObject()); + Handle<HeapObject> object = Handle<HeapObject>::cast(info()->receiver()); + Handle<Map> map(object->map()); + EmitLoadReceiver(); + __ CheckMap(receiver_reg(), scratch0(), map, bailout_to_beginning, false); + } - // If there is a global variable access check if the global object - // is the same as at lazy-compilation time. - if (info()->has_globals()) EmitGlobalMapCheck(); + // If there is a global variable access check if the global object is the + // same as at lazy-compilation time. + if (info()->has_globals()) { + Comment cmnt(masm(), ";; MapCheck(GLOBAL)"); + if (FLAG_print_ir) { + PrintF("MapCheck(GLOBAL)\n"); + } + ASSERT(info()->has_global_object()); + Handle<Map> map(info()->global_object()->map()); + __ ldr(scratch0(), CodeGenerator::GlobalObject()); + __ CheckMap(scratch0(), scratch1(), map, bailout_to_beginning, true); + } VisitStatements(function()->body()); Comment return_cmnt(masm(), ";; Return(<undefined>)"); + if (FLAG_print_ir) { + PrintF("Return(<undefined>)\n"); + } __ LoadRoot(r0, Heap::kUndefinedValueRootIndex); - - Comment epilogue_cmnt(masm(), ";; Epilogue"); __ mov(sp, fp); __ ldm(ia_w, sp, fp.bit() | lr.bit()); int32_t sp_delta = (scope()->num_parameters() + 1) * kPointerSize; __ add(sp, sp, Operand(sp_delta)); __ Jump(lr); - - __ bind(&bailout_); } diff --git a/src/arm/full-codegen-arm.cc b/src/arm/full-codegen-arm.cc index 7e048fff..48963738 100644 --- a/src/arm/full-codegen-arm.cc +++ b/src/arm/full-codegen-arm.cc @@ -816,9 +816,9 @@ void FullCodeGenerator::VisitObjectLiteral(ObjectLiteral* expr) { if (key->handle()->IsSymbol()) { VisitForValue(value, kAccumulator); __ mov(r2, Operand(key->handle())); + __ ldr(r1, MemOperand(sp)); Handle<Code> ic(Builtins::builtin(Builtins::StoreIC_Initialize)); __ Call(ic, RelocInfo::CODE_TARGET); - // StoreIC leaves the receiver on the stack. break; } // Fall through. @@ -907,6 +907,92 @@ void FullCodeGenerator::VisitArrayLiteral(ArrayLiteral* expr) { } +void FullCodeGenerator::VisitAssignment(Assignment* expr) { + Comment cmnt(masm_, "[ Assignment"); + ASSERT(expr->op() != Token::INIT_CONST); + // Left-hand side can only be a property, a global or a (parameter or local) + // slot. Variables with rewrite to .arguments are treated as KEYED_PROPERTY. + enum LhsKind { VARIABLE, NAMED_PROPERTY, KEYED_PROPERTY }; + LhsKind assign_type = VARIABLE; + Property* prop = expr->target()->AsProperty(); + if (prop != NULL) { + assign_type = + (prop->key()->IsPropertyName()) ? NAMED_PROPERTY : KEYED_PROPERTY; + } + + // Evaluate LHS expression. + switch (assign_type) { + case VARIABLE: + // Nothing to do here. + break; + case NAMED_PROPERTY: + if (expr->is_compound()) { + // We need the receiver both on the stack and in the accumulator. + VisitForValue(prop->obj(), kAccumulator); + __ push(result_register()); + } else { + VisitForValue(prop->obj(), kStack); + } + break; + case KEYED_PROPERTY: + VisitForValue(prop->obj(), kStack); + VisitForValue(prop->key(), kStack); + break; + } + + // If we have a compound assignment: Get value of LHS expression and + // store in on top of the stack. + if (expr->is_compound()) { + Location saved_location = location_; + location_ = kStack; + switch (assign_type) { + case VARIABLE: + EmitVariableLoad(expr->target()->AsVariableProxy()->var(), + Expression::kValue); + break; + case NAMED_PROPERTY: + EmitNamedPropertyLoad(prop); + __ push(result_register()); + break; + case KEYED_PROPERTY: + EmitKeyedPropertyLoad(prop); + __ push(result_register()); + break; + } + location_ = saved_location; + } + + // Evaluate RHS expression. + Expression* rhs = expr->value(); + VisitForValue(rhs, kAccumulator); + + // If we have a compound assignment: Apply operator. + if (expr->is_compound()) { + Location saved_location = location_; + location_ = kAccumulator; + EmitBinaryOp(expr->binary_op(), Expression::kValue); + location_ = saved_location; + } + + // Record source position before possible IC call. + SetSourcePosition(expr->position()); + + // Store the value. + switch (assign_type) { + case VARIABLE: + EmitVariableAssignment(expr->target()->AsVariableProxy()->var(), + context_); + break; + case NAMED_PROPERTY: + EmitNamedPropertyAssignment(expr); + break; + case KEYED_PROPERTY: + EmitKeyedPropertyAssignment(expr); + break; + } +} + + void FullCodeGenerator::EmitNamedPropertyLoad(Property* prop) { SetSourcePosition(prop->position()); Literal* key = prop->key()->AsLiteral(); @@ -945,21 +1031,17 @@ void FullCodeGenerator::EmitVariableAssignment(Variable* var, ASSERT(!var->is_this()); // Assignment to a global variable. Use inline caching for the // assignment. Right-hand-side value is passed in r0, variable name in - // r2, and the global object on the stack. + // r2, and the global object in r1. __ mov(r2, Operand(var->name())); - __ ldr(ip, CodeGenerator::GlobalObject()); - __ push(ip); + __ ldr(r1, CodeGenerator::GlobalObject()); Handle<Code> ic(Builtins::builtin(Builtins::StoreIC_Initialize)); __ Call(ic, RelocInfo::CODE_TARGET); - // Overwrite the global object on the stack with the result if needed. - DropAndApply(1, context, r0); } else if (slot != NULL && slot->type() == Slot::LOOKUP) { __ push(result_register()); // Value. __ mov(r1, Operand(var->name())); __ stm(db_w, sp, cp.bit() | r1.bit()); // Context and name. __ CallRuntime(Runtime::kStoreContextSlot, 3); - Apply(context, r0); } else if (var->slot() != NULL) { Slot* slot = var->slot(); @@ -986,13 +1068,13 @@ void FullCodeGenerator::EmitVariableAssignment(Variable* var, UNREACHABLE(); break; } - Apply(context, result_register()); } else { // Variables rewritten as properties are not treated as variables in // assignments. UNREACHABLE(); } + Apply(context, result_register()); } @@ -1016,6 +1098,12 @@ void FullCodeGenerator::EmitNamedPropertyAssignment(Assignment* expr) { // Record source code position before IC call. SetSourcePosition(expr->position()); __ mov(r2, Operand(prop->key()->AsLiteral()->handle())); + if (expr->ends_initialization_block()) { + __ ldr(r1, MemOperand(sp)); + } else { + __ pop(r1); + } + Handle<Code> ic(Builtins::builtin(Builtins::StoreIC_Initialize)); __ Call(ic, RelocInfo::CODE_TARGET); @@ -1026,9 +1114,10 @@ void FullCodeGenerator::EmitNamedPropertyAssignment(Assignment* expr) { __ push(ip); __ CallRuntime(Runtime::kToFastProperties, 1); __ pop(r0); + DropAndApply(1, context_, r0); + } else { + Apply(context_, r0); } - - DropAndApply(1, context_, r0); } @@ -1087,7 +1176,7 @@ void FullCodeGenerator::VisitProperty(Property* expr) { } void FullCodeGenerator::EmitCallWithIC(Call* expr, - Handle<Object> ignored, + Handle<Object> name, RelocInfo::Mode mode) { // Code common for calls using the IC. ZoneList<Expression*>* args = expr->arguments(); @@ -1095,16 +1184,16 @@ void FullCodeGenerator::EmitCallWithIC(Call* expr, for (int i = 0; i < arg_count; i++) { VisitForValue(args->at(i), kStack); } + __ mov(r2, Operand(name)); // Record source position for debugger. SetSourcePosition(expr->position()); // Call the IC initialization code. - Handle<Code> ic = CodeGenerator::ComputeCallInitialize(arg_count, - NOT_IN_LOOP); + InLoopFlag in_loop = (loop_depth() > 0) ? IN_LOOP : NOT_IN_LOOP; + Handle<Code> ic = CodeGenerator::ComputeCallInitialize(arg_count, in_loop); __ Call(ic, mode); // Restore context register. __ ldr(cp, MemOperand(fp, StandardFrameConstants::kContextOffset)); - // Discard the function left on TOS. - DropAndApply(1, context_, r0); + Apply(context_, r0); } @@ -1121,7 +1210,6 @@ void FullCodeGenerator::EmitCallWithStub(Call* expr) { __ CallStub(&stub); // Restore context register. __ ldr(cp, MemOperand(fp, StandardFrameConstants::kContextOffset)); - // Discard the function left on TOS. DropAndApply(1, context_, r0); } @@ -1135,11 +1223,9 @@ void FullCodeGenerator::VisitCall(Call* expr) { // Call to the identifier 'eval'. UNREACHABLE(); } else if (var != NULL && !var->is_this() && var->is_global()) { - // Call to a global variable. - __ mov(r1, Operand(var->name())); - // Push global object as receiver for the call IC lookup. + // Push global object as receiver for the call IC. __ ldr(r0, CodeGenerator::GlobalObject()); - __ stm(db_w, sp, r1.bit() | r0.bit()); + __ push(r0); EmitCallWithIC(expr, var->name(), RelocInfo::CODE_TARGET_CONTEXT); } else if (var != NULL && var->slot() != NULL && var->slot()->type() == Slot::LOOKUP) { @@ -1151,8 +1237,6 @@ void FullCodeGenerator::VisitCall(Call* expr) { Literal* key = prop->key()->AsLiteral(); if (key != NULL && key->handle()->IsSymbol()) { // Call to a named property, use call IC. - __ mov(r0, Operand(key->handle())); - __ push(r0); VisitForValue(prop->obj(), kStack); EmitCallWithIC(expr, key->handle(), RelocInfo::CODE_TARGET); } else { @@ -1238,10 +1322,9 @@ void FullCodeGenerator::VisitCallRuntime(CallRuntime* expr) { if (expr->is_jsruntime()) { // Prepare for calling JS runtime function. - __ mov(r1, Operand(expr->name())); __ ldr(r0, CodeGenerator::GlobalObject()); __ ldr(r0, FieldMemOperand(r0, GlobalObject::kBuiltinsOffset)); - __ stm(db_w, sp, r1.bit() | r0.bit()); + __ push(r0); } // Push the arguments ("left-to-right"). @@ -1252,18 +1335,17 @@ void FullCodeGenerator::VisitCallRuntime(CallRuntime* expr) { if (expr->is_jsruntime()) { // Call the JS runtime function. + __ mov(r2, Operand(expr->name())); Handle<Code> ic = CodeGenerator::ComputeCallInitialize(arg_count, NOT_IN_LOOP); __ Call(ic, RelocInfo::CODE_TARGET); // Restore context register. __ ldr(cp, MemOperand(fp, StandardFrameConstants::kContextOffset)); - // Discard the function left on TOS. - DropAndApply(1, context_, r0); } else { // Call the C runtime function. __ CallRuntime(expr->function(), arg_count); - Apply(context_, r0); } + Apply(context_, r0); } @@ -1548,15 +1630,15 @@ void FullCodeGenerator::VisitCountOperation(CountOperation* expr) { break; case NAMED_PROPERTY: { __ mov(r2, Operand(prop->key()->AsLiteral()->handle())); + __ pop(r1); Handle<Code> ic(Builtins::builtin(Builtins::StoreIC_Initialize)); __ Call(ic, RelocInfo::CODE_TARGET); if (expr->is_postfix()) { - __ Drop(1); // Result is on the stack under the receiver. if (context_ != Expression::kEffect) { ApplyTOS(context_); } } else { - DropAndApply(1, context_, r0); + Apply(context_, r0); } break; } diff --git a/src/arm/ic-arm.cc b/src/arm/ic-arm.cc index 19583a98..2a1fef9b 100644 --- a/src/arm/ic-arm.cc +++ b/src/arm/ic-arm.cc @@ -59,7 +59,7 @@ static void GenerateDictionaryLoad(MacroAssembler* masm, // r3 - used as temporary and to hold the capacity of the property // dictionary. // - // r2 - holds the name of the property and is unchanges. + // r2 - holds the name of the property and is unchanged. Label done; @@ -171,7 +171,7 @@ void LoadIC::GenerateStringLength(MacroAssembler* masm) { __ ldr(r0, MemOperand(sp, 0)); - StubCompiler::GenerateLoadStringLength2(masm, r0, r1, r3, &miss); + StubCompiler::GenerateLoadStringLength(masm, r0, r1, r3, &miss); // Cache miss: Jump to runtime. __ bind(&miss); StubCompiler::GenerateLoadMiss(masm, Code::LOAD_IC); @@ -200,14 +200,13 @@ Object* CallIC_Miss(Arguments args); void CallIC::GenerateMegamorphic(MacroAssembler* masm, int argc) { // ----------- S t a t e ------------- - // -- lr: return address + // -- r2 : name + // -- lr : return address // ----------------------------------- Label number, non_number, non_string, boolean, probe, miss; // Get the receiver of the function from the stack into r1. __ ldr(r1, MemOperand(sp, argc * kPointerSize)); - // Get the name of the function from the stack; 1 ~ receiver. - __ ldr(r2, MemOperand(sp, (argc + 1) * kPointerSize)); // Probe the stub cache. Code::Flags flags = @@ -276,9 +275,9 @@ static void GenerateNormalHelper(MacroAssembler* masm, // Patch the receiver with the global proxy if necessary. if (is_global_object) { - __ ldr(r2, MemOperand(sp, argc * kPointerSize)); - __ ldr(r2, FieldMemOperand(r2, GlobalObject::kGlobalReceiverOffset)); - __ str(r2, MemOperand(sp, argc * kPointerSize)); + __ ldr(r0, MemOperand(sp, argc * kPointerSize)); + __ ldr(r0, FieldMemOperand(r0, GlobalObject::kGlobalReceiverOffset)); + __ str(r0, MemOperand(sp, argc * kPointerSize)); } // Invoke the function. @@ -289,14 +288,13 @@ static void GenerateNormalHelper(MacroAssembler* masm, void CallIC::GenerateNormal(MacroAssembler* masm, int argc) { // ----------- S t a t e ------------- - // -- lr: return address + // -- r2 : name + // -- lr : return address // ----------------------------------- Label miss, global_object, non_global_object; // Get the receiver of the function from the stack into r1. __ ldr(r1, MemOperand(sp, argc * kPointerSize)); - // Get the name of the function from the stack; 1 ~ receiver. - __ ldr(r2, MemOperand(sp, (argc + 1) * kPointerSize)); // Check that the receiver isn't a smi. __ tst(r1, Operand(kSmiTagMask)); @@ -349,18 +347,17 @@ void CallIC::GenerateNormal(MacroAssembler* masm, int argc) { void CallIC::GenerateMiss(MacroAssembler* masm, int argc) { // ----------- S t a t e ------------- - // -- lr: return address + // -- r2 : name + // -- lr : return address // ----------------------------------- // Get the receiver of the function from the stack. - __ ldr(r2, MemOperand(sp, argc * kPointerSize)); - // Get the name of the function to call from the stack. - __ ldr(r1, MemOperand(sp, (argc + 1) * kPointerSize)); + __ ldr(r3, MemOperand(sp, argc * kPointerSize)); __ EnterInternalFrame(); // Push the receiver and the name of the function. - __ stm(db_w, sp, r1.bit() | r2.bit()); + __ stm(db_w, sp, r2.bit() | r3.bit()); // Call the entry. __ mov(r0, Operand(2)); @@ -413,7 +410,7 @@ void LoadIC::GenerateMegamorphic(MacroAssembler* masm) { StubCache::GenerateProbe(masm, flags, r0, r2, r3, no_reg); // Cache miss: Jump to runtime. - Generate(masm, ExternalReference(IC_Utility(kLoadIC_Miss))); + GenerateMiss(masm); } @@ -456,16 +453,11 @@ void LoadIC::GenerateNormal(MacroAssembler* masm) { // Cache miss: Restore receiver from stack and jump to runtime. __ bind(&miss); - Generate(masm, ExternalReference(IC_Utility(kLoadIC_Miss))); + GenerateMiss(masm); } void LoadIC::GenerateMiss(MacroAssembler* masm) { - Generate(masm, ExternalReference(IC_Utility(kLoadIC_Miss))); -} - - -void LoadIC::Generate(MacroAssembler* masm, const ExternalReference& f) { // ----------- S t a t e ------------- // -- r2 : name // -- lr : return address @@ -476,7 +468,7 @@ void LoadIC::Generate(MacroAssembler* masm, const ExternalReference& f) { __ stm(db_w, sp, r2.bit() | r3.bit()); // Perform tail call to the entry. - __ TailCallRuntime(f, 2, 1); + __ TailCallRuntime(ExternalReference(IC_Utility(kLoadIC_Miss)), 2, 1); } @@ -504,11 +496,20 @@ Object* KeyedLoadIC_Miss(Arguments args); void KeyedLoadIC::GenerateMiss(MacroAssembler* masm) { - Generate(masm, ExternalReference(IC_Utility(kKeyedLoadIC_Miss))); + // ---------- S t a t e -------------- + // -- lr : return address + // -- sp[0] : key + // -- sp[4] : receiver + // ----------------------------------- + + __ ldm(ia, sp, r2.bit() | r3.bit()); + __ stm(db_w, sp, r2.bit() | r3.bit()); + + __ TailCallRuntime(ExternalReference(IC_Utility(kKeyedLoadIC_Miss)), 2, 1); } -void KeyedLoadIC::Generate(MacroAssembler* masm, const ExternalReference& f) { +void KeyedLoadIC::GenerateRuntimeGetProperty(MacroAssembler* masm) { // ---------- S t a t e -------------- // -- lr : return address // -- sp[0] : key @@ -518,7 +519,7 @@ void KeyedLoadIC::Generate(MacroAssembler* masm, const ExternalReference& f) { __ ldm(ia, sp, r2.bit() | r3.bit()); __ stm(db_w, sp, r2.bit() | r3.bit()); - __ TailCallRuntime(f, 2, 1); + __ TailCallRuntime(ExternalReference(Runtime::kGetProperty), 2, 1); } @@ -532,17 +533,11 @@ void KeyedLoadIC::GenerateGeneric(MacroAssembler* masm) { // Get the key and receiver object from the stack. __ ldm(ia, sp, r0.bit() | r1.bit()); - // Check that the key is a smi. - __ tst(r0, Operand(kSmiTagMask)); - __ b(ne, &slow); - __ mov(r0, Operand(r0, ASR, kSmiTagSize)); - // Check that the object isn't a smi. - __ tst(r1, Operand(kSmiTagMask)); - __ b(eq, &slow); + // Check that the object isn't a smi. + __ BranchOnSmi(r1, &slow); // Get the map of the receiver. __ ldr(r2, FieldMemOperand(r1, HeapObject::kMapOffset)); - // Check bit field. __ ldrb(r3, FieldMemOperand(r2, Map::kBitFieldOffset)); __ tst(r3, Operand(kSlowCaseBitFieldMask)); @@ -556,6 +551,10 @@ void KeyedLoadIC::GenerateGeneric(MacroAssembler* masm) { __ cmp(r2, Operand(JS_OBJECT_TYPE)); __ b(lt, &slow); + // Check that the key is a smi. + __ BranchOnNotSmi(r0, &slow); + __ mov(r0, Operand(r0, ASR, kSmiTagSize)); + // Get the elements array of the object. __ ldr(r1, FieldMemOperand(r1, JSObject::kElementsOffset)); // Check that the object is in fast mode (not dictionary). @@ -571,10 +570,7 @@ void KeyedLoadIC::GenerateGeneric(MacroAssembler* masm) { // Slow case: Push extra copies of the arguments (2). __ bind(&slow); __ IncrementCounter(&Counters::keyed_load_generic_slow, 1, r0, r1); - __ ldm(ia, sp, r0.bit() | r1.bit()); - __ stm(db_w, sp, r0.bit() | r1.bit()); - // Do tail-call to runtime routine. - __ TailCallRuntime(ExternalReference(Runtime::kGetProperty), 2, 1); + GenerateRuntimeGetProperty(masm); // Fast case: Do the load. __ bind(&fast); @@ -608,8 +604,47 @@ void KeyedLoadIC::GenerateExternalArray(MacroAssembler* masm, } -void KeyedStoreIC::Generate(MacroAssembler* masm, - const ExternalReference& f) { +void KeyedLoadIC::GenerateIndexedInterceptor(MacroAssembler* masm) { + // ---------- S t a t e -------------- + // -- lr : return address + // -- sp[0] : key + // -- sp[4] : receiver + // ----------------------------------- + Label slow; + + // Get the key and receiver object from the stack. + __ ldm(ia, sp, r0.bit() | r1.bit()); + + // Check that the receiver isn't a smi. + __ BranchOnSmi(r1, &slow); + + // Check that the key is a smi. + __ BranchOnNotSmi(r0, &slow); + + // Get the map of the receiver. + __ ldr(r2, FieldMemOperand(r1, HeapObject::kMapOffset)); + + // Check that it has indexed interceptor and access checks + // are not enabled for this object. + __ ldrb(r3, FieldMemOperand(r2, Map::kBitFieldOffset)); + __ and_(r3, r3, Operand(kSlowCaseBitFieldMask)); + __ cmp(r3, Operand(1 << Map::kHasIndexedInterceptor)); + __ b(ne, &slow); + + // Everything is fine, call runtime. + __ push(r1); // receiver + __ push(r0); // key + + // Perform tail call to the entry. + __ TailCallRuntime(ExternalReference( + IC_Utility(kKeyedLoadPropertyWithInterceptor)), 2, 1); + + __ bind(&slow); + GenerateMiss(masm); +} + + +void KeyedStoreIC::GenerateMiss(MacroAssembler* masm) { // ---------- S t a t e -------------- // -- r0 : value // -- lr : return address @@ -620,7 +655,21 @@ void KeyedStoreIC::Generate(MacroAssembler* masm, __ ldm(ia, sp, r2.bit() | r3.bit()); __ stm(db_w, sp, r0.bit() | r2.bit() | r3.bit()); - __ TailCallRuntime(f, 3, 1); + __ TailCallRuntime(ExternalReference(IC_Utility(kKeyedStoreIC_Miss)), 3, 1); +} + + +void KeyedStoreIC::GenerateRuntimeSetProperty(MacroAssembler* masm) { + // ---------- S t a t e -------------- + // -- r0 : value + // -- lr : return address + // -- sp[0] : key + // -- sp[1] : receiver + // ----------------------------------- + __ ldm(ia, sp, r1.bit() | r3.bit()); // r0 == value, r1 == key, r3 == object + __ stm(db_w, sp, r0.bit() | r1.bit() | r3.bit()); + + __ TailCallRuntime(ExternalReference(Runtime::kSetProperty), 3, 1); } @@ -675,12 +724,9 @@ void KeyedStoreIC::GenerateGeneric(MacroAssembler* masm) { __ b(lo, &fast); - // Slow case: Push extra copies of the arguments (3). + // Slow case: __ bind(&slow); - __ ldm(ia, sp, r1.bit() | r3.bit()); // r0 == value, r1 == key, r3 == object - __ stm(db_w, sp, r0.bit() | r1.bit() | r3.bit()); - // Do tail-call to runtime routine. - __ TailCallRuntime(ExternalReference(Runtime::kSetProperty), 3, 1); + GenerateRuntimeSetProperty(masm); // Extra capacity case: Check if there is extra capacity to // perform the store and update the length. Used for adding one @@ -751,33 +797,15 @@ void KeyedStoreIC::GenerateExternalArray(MacroAssembler* masm, } -void KeyedStoreIC::GenerateExtendStorage(MacroAssembler* masm) { - // ---------- S t a t e -------------- - // -- r0 : value - // -- lr : return address - // -- sp[0] : key - // -- sp[1] : receiver - // ----------- S t a t e ------------- - - __ ldm(ia, sp, r2.bit() | r3.bit()); - __ stm(db_w, sp, r0.bit() | r2.bit() | r3.bit()); - - // Perform tail call to the entry. - __ TailCallRuntime( - ExternalReference(IC_Utility(kSharedStoreIC_ExtendStorage)), 3, 1); -} - - void StoreIC::GenerateMegamorphic(MacroAssembler* masm) { // ----------- S t a t e ------------- // -- r0 : value + // -- r1 : receiver // -- r2 : name // -- lr : return address - // -- [sp] : receiver // ----------------------------------- // Get the receiver from the stack and probe the stub cache. - __ ldr(r1, MemOperand(sp)); Code::Flags flags = Code::ComputeFlags(Code::STORE_IC, NOT_IN_LOOP, MONOMORPHIC); @@ -788,33 +816,16 @@ void StoreIC::GenerateMegamorphic(MacroAssembler* masm) { } -void StoreIC::GenerateExtendStorage(MacroAssembler* masm) { - // ----------- S t a t e ------------- - // -- r0 : value - // -- r2 : name - // -- lr : return address - // -- [sp] : receiver - // ----------------------------------- - - __ ldr(r3, MemOperand(sp)); // copy receiver - __ stm(db_w, sp, r0.bit() | r2.bit() | r3.bit()); - - // Perform tail call to the entry. - __ TailCallRuntime( - ExternalReference(IC_Utility(kSharedStoreIC_ExtendStorage)), 3, 1); -} - - void StoreIC::GenerateMiss(MacroAssembler* masm) { // ----------- S t a t e ------------- // -- r0 : value + // -- r1 : receiver // -- r2 : name // -- lr : return address - // -- [sp] : receiver // ----------------------------------- - __ ldr(r3, MemOperand(sp)); // copy receiver - __ stm(db_w, sp, r0.bit() | r2.bit() | r3.bit()); + __ push(r1); + __ stm(db_w, sp, r2.bit() | r0.bit()); // Perform tail call to the entry. __ TailCallRuntime(ExternalReference(IC_Utility(kStoreIC_Miss)), 3, 1); diff --git a/src/arm/macro-assembler-arm.cc b/src/arm/macro-assembler-arm.cc index 1f08c7cf..b9335f8e 100644 --- a/src/arm/macro-assembler-arm.cc +++ b/src/arm/macro-assembler-arm.cc @@ -37,7 +37,6 @@ namespace internal { MacroAssembler::MacroAssembler(void* buffer, int size) : Assembler(buffer, size), - unresolved_(0), generating_stub_(false), allow_stub_calls_(true), code_object_(Heap::undefined_value()) { @@ -221,7 +220,7 @@ void MacroAssembler::RecordWrite(Register object, Register offset, // remembered set bits in the new space. // object: heap object pointer (with tag) // offset: offset to store location from the object - and_(scratch, object, Operand(Heap::NewSpaceMask())); + and_(scratch, object, Operand(ExternalReference::new_space_mask())); cmp(scratch, Operand(ExternalReference::new_space_start())); b(eq, &done); @@ -331,14 +330,10 @@ void MacroAssembler::EnterExitFrame(ExitFrame::Mode mode) { // Push in reverse order: caller_fp, sp_on_exit, and caller_pc. stm(db_w, sp, fp.bit() | ip.bit() | lr.bit()); - mov(fp, Operand(sp)); // setup new frame pointer + mov(fp, Operand(sp)); // Setup new frame pointer. - if (mode == ExitFrame::MODE_DEBUG) { - mov(ip, Operand(Smi::FromInt(0))); - } else { - mov(ip, Operand(CodeObject())); - } - push(ip); + mov(ip, Operand(CodeObject())); + push(ip); // Accessed from ExitFrame::code_slot. // Save the frame pointer and the context in top. mov(ip, Operand(ExternalReference(Top::k_c_entry_fp_address))); @@ -550,6 +545,21 @@ void MacroAssembler::InvokeFunction(Register fun, } +void MacroAssembler::InvokeFunction(JSFunction* function, + const ParameterCount& actual, + InvokeFlag flag) { + ASSERT(function->is_compiled()); + + // Get the function and setup the context. + mov(r1, Operand(Handle<JSFunction>(function))); + ldr(cp, FieldMemOperand(r1, JSFunction::kContextOffset)); + + // Invoke the cached code. + Handle<Code> code(function->code()); + ParameterCount expected(function->shared()->formal_parameter_count()); + InvokeCode(code, expected, actual, RelocInfo::CODE_TARGET, flag); +} + #ifdef ENABLE_DEBUGGER_SUPPORT void MacroAssembler::SaveRegistersToMemory(RegList regs) { ASSERT((regs & ~kJSCallerSaved) == 0); @@ -608,6 +618,15 @@ void MacroAssembler::CopyRegistersFromStackToMemory(Register base, } } } + + +void MacroAssembler::DebugBreak() { + ASSERT(allow_stub_calls()); + mov(r0, Operand(0)); + mov(r1, Operand(ExternalReference(Runtime::kDebugBreak))); + CEntryStub ces(1); + Call(ces.GetCode(), RelocInfo::DEBUG_BREAK); +} #endif @@ -1205,6 +1224,16 @@ void MacroAssembler::CallRuntime(Runtime::FunctionId fid, int num_arguments) { } +void MacroAssembler::CallExternalReference(const ExternalReference& ext, + int num_arguments) { + mov(r0, Operand(num_arguments)); + mov(r1, Operand(ext)); + + CEntryStub stub(1); + CallStub(&stub); +} + + void MacroAssembler::TailCallRuntime(const ExternalReference& ext, int num_arguments, int result_size) { @@ -1228,58 +1257,28 @@ void MacroAssembler::JumpToRuntime(const ExternalReference& builtin) { } -Handle<Code> MacroAssembler::ResolveBuiltin(Builtins::JavaScript id, - bool* resolved) { - // Contract with compiled functions is that the function is passed in r1. - int builtins_offset = - JSBuiltinsObject::kJSBuiltinsOffset + (id * kPointerSize); - ldr(r1, MemOperand(cp, Context::SlotOffset(Context::GLOBAL_INDEX))); - ldr(r1, FieldMemOperand(r1, GlobalObject::kBuiltinsOffset)); - ldr(r1, FieldMemOperand(r1, builtins_offset)); - - return Builtins::GetCode(id, resolved); -} - - void MacroAssembler::InvokeBuiltin(Builtins::JavaScript id, InvokeJSFlags flags) { - bool resolved; - Handle<Code> code = ResolveBuiltin(id, &resolved); - + GetBuiltinEntry(r2, id); if (flags == CALL_JS) { - Call(code, RelocInfo::CODE_TARGET); + Call(r2); } else { ASSERT(flags == JUMP_JS); - Jump(code, RelocInfo::CODE_TARGET); - } - - if (!resolved) { - const char* name = Builtins::GetName(id); - int argc = Builtins::GetArgumentsCount(id); - uint32_t flags = - Bootstrapper::FixupFlagsArgumentsCount::encode(argc) | - Bootstrapper::FixupFlagsUseCodeObject::encode(false); - Unresolved entry = { pc_offset() - kInstrSize, flags, name }; - unresolved_.Add(entry); + Jump(r2); } } void MacroAssembler::GetBuiltinEntry(Register target, Builtins::JavaScript id) { - bool resolved; - Handle<Code> code = ResolveBuiltin(id, &resolved); - - mov(target, Operand(code)); - if (!resolved) { - const char* name = Builtins::GetName(id); - int argc = Builtins::GetArgumentsCount(id); - uint32_t flags = - Bootstrapper::FixupFlagsArgumentsCount::encode(argc) | - Bootstrapper::FixupFlagsUseCodeObject::encode(true); - Unresolved entry = { pc_offset() - kInstrSize, flags, name }; - unresolved_.Add(entry); - } - + // Load the JavaScript builtin function from the builtins object. + ldr(r1, MemOperand(cp, Context::SlotOffset(Context::GLOBAL_INDEX))); + ldr(r1, FieldMemOperand(r1, GlobalObject::kBuiltinsOffset)); + int builtins_offset = + JSBuiltinsObject::kJSBuiltinsOffset + (id * kPointerSize); + ldr(r1, FieldMemOperand(r1, builtins_offset)); + // Load the code entry point from the function into the target register. + ldr(target, FieldMemOperand(r1, JSFunction::kSharedFunctionInfoOffset)); + ldr(target, FieldMemOperand(target, SharedFunctionInfo::kCodeOffset)); add(target, target, Operand(Code::kHeaderSize - kHeapObjectTag)); } diff --git a/src/arm/macro-assembler-arm.h b/src/arm/macro-assembler-arm.h index 66ef4f9a..98cea163 100644 --- a/src/arm/macro-assembler-arm.h +++ b/src/arm/macro-assembler-arm.h @@ -135,6 +135,10 @@ class MacroAssembler: public Assembler { const ParameterCount& actual, InvokeFlag flag); + void InvokeFunction(JSFunction* function, + const ParameterCount& actual, + InvokeFlag flag); + #ifdef ENABLE_DEBUGGER_SUPPORT // --------------------------------------------------------------------------- @@ -146,6 +150,7 @@ class MacroAssembler: public Assembler { void CopyRegistersFromStackToMemory(Register base, Register scratch, RegList regs); + void DebugBreak(); #endif // --------------------------------------------------------------------------- @@ -334,6 +339,10 @@ class MacroAssembler: public Assembler { // Convenience function: Same as above, but takes the fid instead. void CallRuntime(Runtime::FunctionId fid, int num_arguments); + // Convenience function: call an external reference. + void CallExternalReference(const ExternalReference& ext, + int num_arguments); + // Tail call of a runtime routine (jump). // Like JumpToRuntime, but also takes care of passing the number // of parameters. @@ -352,13 +361,6 @@ class MacroAssembler: public Assembler { // setup the function in r1. void GetBuiltinEntry(Register target, Builtins::JavaScript id); - struct Unresolved { - int pc; - uint32_t flags; // see Bootstrapper::FixupFlags decoders/encoders. - const char* name; - }; - List<Unresolved>* unresolved() { return &unresolved_; } - Handle<Object> CodeObject() { return code_object_; } @@ -431,23 +433,10 @@ class MacroAssembler: public Assembler { Label* done, InvokeFlag flag); - // Prepares for a call or jump to a builtin by doing two things: - // 1. Emits code that fetches the builtin's function object from the context - // at runtime, and puts it in the register rdi. - // 2. Fetches the builtin's code object, and returns it in a handle, at - // compile time, so that later code can emit instructions to jump or call - // the builtin directly. If the code object has not yet been created, it - // returns the builtin code object for IllegalFunction, and sets the - // output parameter "resolved" to false. Code that uses the return value - // should then add the address and the builtin name to the list of fixups - // called unresolved_, which is fixed up by the bootstrapper. - Handle<Code> ResolveBuiltin(Builtins::JavaScript id, bool* resolved); - // Activation support. void EnterFrame(StackFrame::Type type); void LeaveFrame(StackFrame::Type type); - List<Unresolved> unresolved_; bool generating_stub_; bool allow_stub_calls_; // This handle will be patched with the code object on installation. diff --git a/src/arm/stub-cache-arm.cc b/src/arm/stub-cache-arm.cc index d19a683d..da739421 100644 --- a/src/arm/stub-cache-arm.cc +++ b/src/arm/stub-cache-arm.cc @@ -189,8 +189,9 @@ void StubCompiler::GenerateLoadArrayLength(MacroAssembler* masm, } -// Generate code to check if an object is a string. If the object is -// a string, the map's instance type is left in the scratch1 register. +// Generate code to check if an object is a string. If the object is a +// heap object, its map's instance type is left in the scratch1 register. +// If this is not needed, scratch1 and scratch2 may be the same register. static void GenerateStringCheck(MacroAssembler* masm, Register receiver, Register scratch1, @@ -215,18 +216,16 @@ static void GenerateStringCheck(MacroAssembler* masm, // If the receiver object is not a string or a wrapped string object the // execution continues at the miss label. The register containing the // receiver is potentially clobbered. -void StubCompiler::GenerateLoadStringLength2(MacroAssembler* masm, - Register receiver, - Register scratch1, - Register scratch2, - Label* miss) { - Label check_string, check_wrapper; - - __ bind(&check_string); +void StubCompiler::GenerateLoadStringLength(MacroAssembler* masm, + Register receiver, + Register scratch1, + Register scratch2, + Label* miss) { + Label check_wrapper; + // Check if the object is a string leaving the instance type in the // scratch1 register. - GenerateStringCheck(masm, receiver, scratch1, scratch2, - miss, &check_wrapper); + GenerateStringCheck(masm, receiver, scratch1, scratch2, miss, &check_wrapper); // Load length directly from the string. __ ldr(r0, FieldMemOperand(receiver, String::kLengthOffset)); @@ -238,9 +237,12 @@ void StubCompiler::GenerateLoadStringLength2(MacroAssembler* masm, __ cmp(scratch1, Operand(JS_VALUE_TYPE)); __ b(ne, miss); - // Unwrap the value in place and check if the wrapped value is a string. - __ ldr(receiver, FieldMemOperand(receiver, JSValue::kValueOffset)); - __ b(&check_string); + // Unwrap the value and check if the wrapped value is a string. + __ ldr(scratch1, FieldMemOperand(receiver, JSValue::kValueOffset)); + GenerateStringCheck(masm, scratch1, scratch2, scratch2, miss, miss); + __ ldr(r0, FieldMemOperand(scratch1, String::kLengthOffset)); + __ mov(r0, Operand(r0, LSL, kSmiTagSize)); + __ Ret(); } @@ -256,10 +258,10 @@ void StubCompiler::GenerateLoadFunctionPrototype(MacroAssembler* masm, // Generate StoreField code, value is passed in r0 register. -// After executing generated code, the receiver_reg and name_reg -// may be clobbered. +// When leaving generated code after success, the receiver_reg and name_reg +// may be clobbered. Upon branch to miss_label, the receiver and name +// registers have their original values. void StubCompiler::GenerateStoreField(MacroAssembler* masm, - Builtins::Name storage_extend, JSObject* object, int index, Map* transition, @@ -292,11 +294,12 @@ void StubCompiler::GenerateStoreField(MacroAssembler* masm, if ((transition != NULL) && (object->map()->unused_property_fields() == 0)) { // The properties must be extended before we can store the value. // We jump to a runtime call that extends the properties array. + __ push(receiver_reg); __ mov(r2, Operand(Handle<Map>(transition))); - // Please note, if we implement keyed store for arm we need - // to call the Builtins::KeyedStoreIC_ExtendStorage. - Handle<Code> ic(Builtins::builtin(Builtins::StoreIC_ExtendStorage)); - __ Jump(ic, RelocInfo::CODE_TARGET); + __ stm(db_w, sp, r2.bit() | r0.bit()); + __ TailCallRuntime( + ExternalReference(IC_Utility(IC::kSharedStoreIC_ExtendStorage)), + 3, 1); return; } @@ -373,7 +376,7 @@ static void GenerateCallFunction(MacroAssembler* masm, // Check that the function really is a function. __ BranchOnSmi(r1, miss); - __ CompareObjectType(r1, r2, r2, JS_FUNCTION_TYPE); + __ CompareObjectType(r1, r3, r3, JS_FUNCTION_TYPE); __ b(ne, miss); // Patch the receiver on the stack with the global proxy if @@ -388,68 +391,6 @@ static void GenerateCallFunction(MacroAssembler* masm, } -static void GenerateCallConstFunction(MacroAssembler* masm, - JSFunction* function, - const ParameterCount& arguments) { - ASSERT(function->is_compiled()); - - // Get the function and setup the context. - __ mov(r1, Operand(Handle<JSFunction>(function))); - __ ldr(cp, FieldMemOperand(r1, JSFunction::kContextOffset)); - - // Jump to the cached code (tail call). - Handle<Code> code(function->code()); - ParameterCount expected(function->shared()->formal_parameter_count()); - __ InvokeCode(code, expected, arguments, - RelocInfo::CODE_TARGET, JUMP_FUNCTION); -} - - -template <class Compiler> -static void CompileLoadInterceptor(Compiler* compiler, - StubCompiler* stub_compiler, - MacroAssembler* masm, - JSObject* object, - JSObject* holder, - String* name, - LookupResult* lookup, - Register receiver, - Register scratch1, - Register scratch2, - Label* miss) { - ASSERT(holder->HasNamedInterceptor()); - ASSERT(!holder->GetNamedInterceptor()->getter()->IsUndefined()); - - // Check that the receiver isn't a smi. - __ BranchOnSmi(receiver, miss); - - // Check that the maps haven't changed. - Register reg = - stub_compiler->CheckPrototypes(object, receiver, holder, - scratch1, scratch2, name, miss); - - if (lookup->IsValid() && lookup->IsCacheable()) { - compiler->CompileCacheable(masm, - stub_compiler, - receiver, - reg, - scratch1, - scratch2, - holder, - lookup, - name, - miss); - } else { - compiler->CompileRegular(masm, - receiver, - reg, - scratch2, - holder, - miss); - } -} - - static void PushInterceptorArguments(MacroAssembler* masm, Register receiver, Register holder, @@ -500,7 +441,7 @@ class LoadInterceptorCompiler BASE_EMBEDDED { LookupResult* lookup, String* name, Label* miss_label) { - AccessorInfo* callback = 0; + AccessorInfo* callback = NULL; bool optimize = false; // So far the most popular follow ups for interceptor loads are FIELD // and CALLBACKS, so inline only them, other cases may be added @@ -523,9 +464,7 @@ class LoadInterceptorCompiler BASE_EMBEDDED { // Note: starting a frame here makes GC aware of pointers pushed below. __ EnterInternalFrame(); - if (lookup->type() == CALLBACKS) { - __ push(receiver); - } + __ push(receiver); __ push(holder); __ push(name_); @@ -546,10 +485,7 @@ class LoadInterceptorCompiler BASE_EMBEDDED { __ bind(&interceptor_failed); __ pop(name_); __ pop(holder); - - if (lookup->type() == CALLBACKS) { - __ pop(receiver); - } + __ pop(receiver); __ LeaveInternalFrame(); @@ -621,108 +557,48 @@ class LoadInterceptorCompiler BASE_EMBEDDED { }; -class CallInterceptorCompiler BASE_EMBEDDED { - public: - CallInterceptorCompiler(const ParameterCount& arguments, Register name) - : arguments_(arguments), argc_(arguments.immediate()), name_(name) {} - - void CompileCacheable(MacroAssembler* masm, - StubCompiler* stub_compiler, - Register receiver, - Register holder, - Register scratch1, - Register scratch2, - JSObject* holder_obj, - LookupResult* lookup, - String* name, - Label* miss_label) { - JSFunction* function = 0; - bool optimize = false; - // So far the most popular case for failed interceptor is - // CONSTANT_FUNCTION sitting below. - if (lookup->type() == CONSTANT_FUNCTION) { - function = lookup->GetConstantFunction(); - // JSArray holder is a special case for call constant function - // (see the corresponding code). - if (function->is_compiled() && !holder_obj->IsJSArray()) { - optimize = true; - } - } - - if (!optimize) { - CompileRegular(masm, receiver, holder, scratch2, holder_obj, miss_label); - return; - } - - // Constant functions cannot sit on global object. - ASSERT(!lookup->holder()->IsGlobalObject()); - - __ EnterInternalFrame(); - __ push(holder); // Save the holder. - __ push(name_); // Save the name. - - CompileCallLoadPropertyWithInterceptor(masm, - receiver, - holder, - name_, - holder_obj); - - ASSERT(!r0.is(name_)); - ASSERT(!r0.is(scratch1)); - __ pop(name_); // Restore the name. - __ pop(scratch1); // Restore the holder. - __ LeaveInternalFrame(); - - // Compare with no_interceptor_result_sentinel. - __ LoadRoot(scratch2, Heap::kNoInterceptorResultSentinelRootIndex); - __ cmp(r0, scratch2); - Label invoke; - __ b(ne, &invoke); - - stub_compiler->CheckPrototypes(holder_obj, scratch1, - lookup->holder(), scratch1, - scratch2, - name, - miss_label); - GenerateCallConstFunction(masm, function, arguments_); +static void CompileLoadInterceptor(LoadInterceptorCompiler* compiler, + StubCompiler* stub_compiler, + MacroAssembler* masm, + JSObject* object, + JSObject* holder, + String* name, + LookupResult* lookup, + Register receiver, + Register scratch1, + Register scratch2, + Label* miss) { + ASSERT(holder->HasNamedInterceptor()); + ASSERT(!holder->GetNamedInterceptor()->getter()->IsUndefined()); - __ bind(&invoke); - } + // Check that the receiver isn't a smi. + __ BranchOnSmi(receiver, miss); - void CompileRegular(MacroAssembler* masm, - Register receiver, - Register holder, - Register scratch, - JSObject* holder_obj, - Label* miss_label) { - __ EnterInternalFrame(); - // Save the name_ register across the call. - __ push(name_); + // Check that the maps haven't changed. + Register reg = + stub_compiler->CheckPrototypes(object, receiver, holder, + scratch1, scratch2, name, miss); - PushInterceptorArguments(masm, + if (lookup->IsProperty() && lookup->IsCacheable()) { + compiler->CompileCacheable(masm, + stub_compiler, + receiver, + reg, + scratch1, + scratch2, + holder, + lookup, + name, + miss); + } else { + compiler->CompileRegular(masm, receiver, + reg, + scratch2, holder, - name_, - holder_obj); - - ExternalReference ref = ExternalReference( - IC_Utility(IC::kLoadPropertyWithInterceptorForCall)); - __ mov(r0, Operand(5)); - __ mov(r1, Operand(ref)); - - CEntryStub stub(1); - __ CallStub(&stub); - - // Restore the name_ register. - __ pop(name_); - __ LeaveInternalFrame(); + miss); } - - private: - const ParameterCount& arguments_; - int argc_; - Register name_; -}; +} #undef __ @@ -735,7 +611,11 @@ Register StubCompiler::CheckPrototypes(JSObject* object, Register holder_reg, Register scratch, String* name, + int save_at_depth, Label* miss) { + // TODO(602): support object saving. + ASSERT(save_at_depth == kInvalidProtoDepth); + // Check that the maps haven't changed. Register result = masm()->CheckMaps(object, object_reg, holder, holder_reg, scratch, miss); @@ -762,7 +642,7 @@ Register StubCompiler::CheckPrototypes(JSObject* object, object = JSObject::cast(object->GetPrototype()); } - // Return the register containin the holder. + // Return the register containing the holder. return result; } @@ -901,12 +781,13 @@ Object* StubCompiler::CompileLazyCompile(Code::Flags flags) { } -Object* CallStubCompiler::CompileCallField(Object* object, +Object* CallStubCompiler::CompileCallField(JSObject* object, JSObject* holder, int index, String* name) { // ----------- S t a t e ------------- - // -- lr: return address + // -- r2 : name + // -- lr : return address // ----------------------------------- Label miss; @@ -919,8 +800,7 @@ Object* CallStubCompiler::CompileCallField(Object* object, __ b(eq, &miss); // Do the right check and compute the holder register. - Register reg = - CheckPrototypes(JSObject::cast(object), r0, holder, r3, r2, name, &miss); + Register reg = CheckPrototypes(object, r0, holder, r1, r3, name, &miss); GenerateFastPropertyLoad(masm(), r1, reg, holder, index); GenerateCallFunction(masm(), object, arguments(), &miss); @@ -941,7 +821,8 @@ Object* CallStubCompiler::CompileCallConstant(Object* object, String* name, CheckType check) { // ----------- S t a t e ------------- - // -- lr: return address + // -- r2 : name + // -- lr : return address // ----------------------------------- Label miss; @@ -962,7 +843,7 @@ Object* CallStubCompiler::CompileCallConstant(Object* object, switch (check) { case RECEIVER_MAP_CHECK: // Check that the maps haven't changed. - CheckPrototypes(JSObject::cast(object), r1, holder, r3, r2, name, &miss); + CheckPrototypes(JSObject::cast(object), r1, holder, r3, r0, name, &miss); // Patch the receiver on the stack with the global proxy if // necessary. @@ -978,13 +859,13 @@ Object* CallStubCompiler::CompileCallConstant(Object* object, __ jmp(&miss); } else { // Check that the object is a two-byte string or a symbol. - __ CompareObjectType(r1, r2, r2, FIRST_NONSTRING_TYPE); + __ CompareObjectType(r1, r3, r3, FIRST_NONSTRING_TYPE); __ b(hs, &miss); // Check that the maps starting from the prototype haven't changed. GenerateLoadGlobalFunctionPrototype(masm(), Context::STRING_FUNCTION_INDEX, - r2); - CheckPrototypes(JSObject::cast(object->GetPrototype()), r2, holder, r3, + r0); + CheckPrototypes(JSObject::cast(object->GetPrototype()), r0, holder, r3, r1, name, &miss); } break; @@ -998,14 +879,14 @@ Object* CallStubCompiler::CompileCallConstant(Object* object, // Check that the object is a smi or a heap number. __ tst(r1, Operand(kSmiTagMask)); __ b(eq, &fast); - __ CompareObjectType(r1, r2, r2, HEAP_NUMBER_TYPE); + __ CompareObjectType(r1, r0, r0, HEAP_NUMBER_TYPE); __ b(ne, &miss); __ bind(&fast); // Check that the maps starting from the prototype haven't changed. GenerateLoadGlobalFunctionPrototype(masm(), Context::NUMBER_FUNCTION_INDEX, - r2); - CheckPrototypes(JSObject::cast(object->GetPrototype()), r2, holder, r3, + r0); + CheckPrototypes(JSObject::cast(object->GetPrototype()), r0, holder, r3, r1, name, &miss); } break; @@ -1028,22 +909,22 @@ Object* CallStubCompiler::CompileCallConstant(Object* object, // Check that the maps starting from the prototype haven't changed. GenerateLoadGlobalFunctionPrototype(masm(), Context::BOOLEAN_FUNCTION_INDEX, - r2); - CheckPrototypes(JSObject::cast(object->GetPrototype()), r2, holder, r3, + r0); + CheckPrototypes(JSObject::cast(object->GetPrototype()), r0, holder, r3, r1, name, &miss); } break; } case JSARRAY_HAS_FAST_ELEMENTS_CHECK: - CheckPrototypes(JSObject::cast(object), r1, holder, r3, r2, name, &miss); + CheckPrototypes(JSObject::cast(object), r1, holder, r3, r0, name, &miss); // Make sure object->HasFastElements(). // Get the elements array of the object. __ ldr(r3, FieldMemOperand(r1, JSObject::kElementsOffset)); // Check that the object is in fast mode (not dictionary). - __ ldr(r2, FieldMemOperand(r3, HeapObject::kMapOffset)); + __ ldr(r0, FieldMemOperand(r3, HeapObject::kMapOffset)); __ LoadRoot(ip, Heap::kFixedArrayMapRootIndex); - __ cmp(r2, ip); + __ cmp(r0, ip); __ b(ne, &miss); break; @@ -1051,7 +932,7 @@ Object* CallStubCompiler::CompileCallConstant(Object* object, UNREACHABLE(); } - GenerateCallConstFunction(masm(), function, arguments()); + __ InvokeFunction(function, arguments(), JUMP_FUNCTION); // Handle call cache miss. __ bind(&miss); @@ -1067,14 +948,22 @@ Object* CallStubCompiler::CompileCallConstant(Object* object, } -Object* CallStubCompiler::CompileCallInterceptor(Object* object, +Object* CallStubCompiler::CompileCallInterceptor(JSObject* object, JSObject* holder, String* name) { // ----------- S t a t e ------------- - // -- lr: return address + // -- r2 : name + // -- lr : return address // ----------------------------------- + ASSERT(holder->HasNamedInterceptor()); + ASSERT(!holder->GetNamedInterceptor()->getter()->IsUndefined()); Label miss; + const Register receiver = r0; + const Register holder_reg = r1; + const Register name_reg = r2; + const Register scratch = r3; + // Get the number of arguments. const int argc = arguments().immediate(); @@ -1083,24 +972,79 @@ Object* CallStubCompiler::CompileCallInterceptor(Object* object, // Get the receiver from the stack into r0. __ ldr(r0, MemOperand(sp, argc * kPointerSize)); - // Load the name from the stack into r1. - __ ldr(r1, MemOperand(sp, (argc + 1) * kPointerSize)); - CallInterceptorCompiler compiler(arguments(), r1); - CompileLoadInterceptor(&compiler, - this, - masm(), - JSObject::cast(object), - holder, - name, - &lookup, - r0, - r2, - r3, - &miss); + // Check that the receiver isn't a smi. + __ BranchOnSmi(receiver, &miss); + + // Check that the maps haven't changed. + Register reg = CheckPrototypes(object, receiver, holder, holder_reg, + scratch, name, &miss); + if (!reg.is(holder_reg)) { + __ mov(holder_reg, reg); + } + + // If we call a constant function when the interceptor returns + // the no-result sentinel, generate code that optimizes this case. + if (lookup.IsProperty() && + lookup.IsCacheable() && + lookup.type() == CONSTANT_FUNCTION && + lookup.GetConstantFunction()->is_compiled() && + !holder->IsJSArray()) { + // Constant functions cannot sit on global object. + ASSERT(!lookup.holder()->IsGlobalObject()); + + // Call the interceptor. + __ EnterInternalFrame(); + __ push(holder_reg); + __ push(name_reg); + CompileCallLoadPropertyWithInterceptor(masm(), + receiver, + holder_reg, + name_reg, + holder); + __ pop(name_reg); + __ pop(holder_reg); + __ LeaveInternalFrame(); + // r0 no longer contains the receiver. + + // If interceptor returns no-result sentinal, call the constant function. + __ LoadRoot(scratch, Heap::kNoInterceptorResultSentinelRootIndex); + __ cmp(r0, scratch); + Label invoke; + __ b(ne, &invoke); + // Check the prototypes between the interceptor's holder and the + // constant function's holder. + CheckPrototypes(holder, holder_reg, + lookup.holder(), r0, + scratch, + name, + &miss); + + __ InvokeFunction(lookup.GetConstantFunction(), + arguments(), + JUMP_FUNCTION); + + __ bind(&invoke); + + } else { + // Call a runtime function to load the interceptor property. + __ EnterInternalFrame(); + __ push(name_reg); + + PushInterceptorArguments(masm(), receiver, holder_reg, name_reg, holder); + __ CallExternalReference( + ExternalReference(IC_Utility(IC::kLoadPropertyWithInterceptorForCall)), + 5); + + __ pop(name_reg); + __ LeaveInternalFrame(); + } + + // Move returned value, the function to call, to r1. + __ mov(r1, r0); // Restore receiver. - __ ldr(r0, MemOperand(sp, argc * kPointerSize)); + __ ldr(receiver, MemOperand(sp, argc * kPointerSize)); GenerateCallFunction(masm(), object, arguments(), &miss); @@ -1120,7 +1064,8 @@ Object* CallStubCompiler::CompileCallGlobal(JSObject* object, JSFunction* function, String* name) { // ----------- S t a t e ------------- - // -- lr: return address + // -- r2 : name + // -- lr : return address // ----------------------------------- Label miss; @@ -1139,7 +1084,7 @@ Object* CallStubCompiler::CompileCallGlobal(JSObject* object, } // Check that the maps haven't changed. - CheckPrototypes(object, r0, holder, r3, r2, name, &miss); + CheckPrototypes(object, r0, holder, r3, r1, name, &miss); // Get the value from the cell. __ mov(r3, Operand(Handle<JSGlobalPropertyCell>(cell))); @@ -1159,8 +1104,8 @@ Object* CallStubCompiler::CompileCallGlobal(JSObject* object, // Check the shared function info. Make sure it hasn't changed. __ mov(r3, Operand(Handle<SharedFunctionInfo>(function->shared()))); - __ ldr(r2, FieldMemOperand(r1, JSFunction::kSharedFunctionInfoOffset)); - __ cmp(r2, r3); + __ ldr(r4, FieldMemOperand(r1, JSFunction::kSharedFunctionInfoOffset)); + __ cmp(r4, r3); __ b(ne, &miss); } else { __ cmp(r1, Operand(Handle<JSFunction>(function))); @@ -1178,7 +1123,7 @@ Object* CallStubCompiler::CompileCallGlobal(JSObject* object, __ ldr(cp, FieldMemOperand(r1, JSFunction::kContextOffset)); // Jump to the cached code (tail call). - __ IncrementCounter(&Counters::call_global_inline, 1, r2, r3); + __ IncrementCounter(&Counters::call_global_inline, 1, r1, r3); ASSERT(function->is_compiled()); Handle<Code> code(function->code()); ParameterCount expected(function->shared()->formal_parameter_count()); @@ -1202,25 +1147,19 @@ Object* StoreStubCompiler::CompileStoreField(JSObject* object, String* name) { // ----------- S t a t e ------------- // -- r0 : value + // -- r1 : receiver // -- r2 : name // -- lr : return address - // -- [sp] : receiver // ----------------------------------- Label miss; - // Get the receiver from the stack. - __ ldr(r3, MemOperand(sp, 0 * kPointerSize)); - - // name register might be clobbered. GenerateStoreField(masm(), - Builtins::StoreIC_ExtendStorage, object, index, transition, - r3, r2, r1, + r1, r2, r3, &miss); __ bind(&miss); - __ mov(r2, Operand(Handle<String>(name))); // restore name Handle<Code> ic(Builtins::builtin(Builtins::StoreIC_Miss)); __ Jump(ic, RelocInfo::CODE_TARGET); @@ -1234,39 +1173,33 @@ Object* StoreStubCompiler::CompileStoreCallback(JSObject* object, String* name) { // ----------- S t a t e ------------- // -- r0 : value + // -- r1 : receiver // -- r2 : name // -- lr : return address - // -- [sp] : receiver // ----------------------------------- Label miss; - // Get the object from the stack. - __ ldr(r3, MemOperand(sp, 0 * kPointerSize)); - // Check that the object isn't a smi. - __ tst(r3, Operand(kSmiTagMask)); + __ tst(r1, Operand(kSmiTagMask)); __ b(eq, &miss); // Check that the map of the object hasn't changed. - __ ldr(r1, FieldMemOperand(r3, HeapObject::kMapOffset)); - __ cmp(r1, Operand(Handle<Map>(object->map()))); + __ ldr(r3, FieldMemOperand(r1, HeapObject::kMapOffset)); + __ cmp(r3, Operand(Handle<Map>(object->map()))); __ b(ne, &miss); // Perform global security token check if needed. if (object->IsJSGlobalProxy()) { - __ CheckAccessGlobalProxy(r3, r1, &miss); + __ CheckAccessGlobalProxy(r1, r3, &miss); } // Stub never generated for non-global objects that require access // checks. ASSERT(object->IsJSGlobalProxy() || !object->IsAccessCheckNeeded()); - __ ldr(ip, MemOperand(sp)); // receiver - __ push(ip); + __ push(r1); // receiver __ mov(ip, Operand(Handle<AccessorInfo>(callback))); // callback info - __ push(ip); - __ push(r2); // name - __ push(r0); // value + __ stm(db_w, sp, ip.bit() | r2.bit() | r0.bit()); // Do tail-call to the runtime system. ExternalReference store_callback_property = @@ -1287,37 +1220,33 @@ Object* StoreStubCompiler::CompileStoreInterceptor(JSObject* receiver, String* name) { // ----------- S t a t e ------------- // -- r0 : value + // -- r1 : receiver // -- r2 : name // -- lr : return address - // -- [sp] : receiver // ----------------------------------- Label miss; - // Get the object from the stack. - __ ldr(r3, MemOperand(sp, 0 * kPointerSize)); - // Check that the object isn't a smi. - __ tst(r3, Operand(kSmiTagMask)); + __ tst(r1, Operand(kSmiTagMask)); __ b(eq, &miss); // Check that the map of the object hasn't changed. - __ ldr(r1, FieldMemOperand(r3, HeapObject::kMapOffset)); - __ cmp(r1, Operand(Handle<Map>(receiver->map()))); + __ ldr(r3, FieldMemOperand(r1, HeapObject::kMapOffset)); + __ cmp(r3, Operand(Handle<Map>(receiver->map()))); __ b(ne, &miss); // Perform global security token check if needed. if (receiver->IsJSGlobalProxy()) { - __ CheckAccessGlobalProxy(r3, r1, &miss); + __ CheckAccessGlobalProxy(r1, r3, &miss); } - // Stub never generated for non-global objects that require access + // Stub is never generated for non-global objects that require access // checks. ASSERT(receiver->IsJSGlobalProxy() || !receiver->IsAccessCheckNeeded()); - __ ldr(ip, MemOperand(sp)); // receiver - __ push(ip); - __ push(r2); // name - __ push(r0); // value + __ push(r1); // receiver. + __ push(r2); // name. + __ push(r0); // value. // Do tail-call to the runtime system. ExternalReference store_ic_property = @@ -1339,14 +1268,13 @@ Object* StoreStubCompiler::CompileStoreGlobal(GlobalObject* object, String* name) { // ----------- S t a t e ------------- // -- r0 : value + // -- r1 : receiver // -- r2 : name // -- lr : return address - // -- [sp] : receiver // ----------------------------------- Label miss; // Check that the map of the global has not changed. - __ ldr(r1, MemOperand(sp, 0 * kPointerSize)); __ ldr(r3, FieldMemOperand(r1, HeapObject::kMapOffset)); __ cmp(r3, Operand(Handle<Map>(object->map()))); __ b(ne, &miss); @@ -1355,12 +1283,12 @@ Object* StoreStubCompiler::CompileStoreGlobal(GlobalObject* object, __ mov(r2, Operand(Handle<JSGlobalPropertyCell>(cell))); __ str(r0, FieldMemOperand(r2, JSGlobalPropertyCell::kValueOffset)); - __ IncrementCounter(&Counters::named_store_global_inline, 1, r1, r3); + __ IncrementCounter(&Counters::named_store_global_inline, 1, r4, r3); __ Ret(); // Handle store cache miss. __ bind(&miss); - __ IncrementCounter(&Counters::named_store_global_inline_miss, 1, r1, r3); + __ IncrementCounter(&Counters::named_store_global_inline_miss, 1, r4, r3); Handle<Code> ic(Builtins::builtin(Builtins::StoreIC_Miss)); __ Jump(ic, RelocInfo::CODE_TARGET); @@ -1672,7 +1600,7 @@ Object* KeyedLoadStubCompiler::CompileLoadStringLength(String* name) { __ cmp(r2, Operand(Handle<String>(name))); __ b(ne, &miss); - GenerateLoadStringLength2(masm(), r0, r1, r3, &miss); + GenerateLoadStringLength(masm(), r0, r1, r3, &miss); __ bind(&miss); __ DecrementCounter(&Counters::keyed_load_string_length, 1, r1, r3); @@ -1717,7 +1645,6 @@ Object* KeyedStoreStubCompiler::CompileStoreField(JSObject* object, __ ldr(r3, MemOperand(sp)); // r1 is used as scratch register, r3 and r2 might be clobbered. GenerateStoreField(masm(), - Builtins::StoreIC_ExtendStorage, object, index, transition, diff --git a/src/arm/stub-cache-arm.cc.rej b/src/arm/stub-cache-arm.cc.rej deleted file mode 100644 index f8baa413..00000000 --- a/src/arm/stub-cache-arm.cc.rej +++ /dev/null @@ -1,153 +0,0 @@ -*************** void StubCompiler::GenerateLoadInterceptor(JSObject* object, -*** 491,520 **** - Register scratch2, - String* name, - Label* miss) { -- // Check that the receiver isn't a smi. -- __ tst(receiver, Operand(kSmiTagMask)); -- __ b(eq, miss); -- -- // Check that the maps haven't changed. -- Register reg = -- CheckPrototypes(object, receiver, holder, scratch1, scratch2, name, miss); -- -- // Push the arguments on the JS stack of the caller. -- __ push(receiver); // receiver -- __ push(reg); // holder -- __ push(name_reg); // name -- -- InterceptorInfo* interceptor = holder->GetNamedInterceptor(); -- ASSERT(!Heap::InNewSpace(interceptor)); -- __ mov(scratch1, Operand(Handle<Object>(interceptor))); -- __ push(scratch1); -- __ ldr(scratch2, FieldMemOperand(scratch1, InterceptorInfo::kDataOffset)); -- __ push(scratch2); -- -- // Do tail-call to the runtime system. -- ExternalReference load_ic_property = -- ExternalReference(IC_Utility(IC::kLoadPropertyWithInterceptorForLoad)); -- __ TailCallRuntime(load_ic_property, 5, 1); - } - - ---- 854,871 ---- - Register scratch2, - String* name, - Label* miss) { -+ LoadInterceptorCompiler compiler(name_reg); -+ CompileLoadInterceptor(&compiler, -+ this, -+ masm(), -+ object, -+ holder, -+ name, -+ lookup, -+ receiver, -+ scratch1, -+ scratch2, -+ miss); - } - - -*************** Object* CallStubCompiler::CompileCallField(Object* object, -*** 572,593 **** - CheckPrototypes(JSObject::cast(object), r0, holder, r3, r2, name, &miss); - GenerateFastPropertyLoad(masm(), r1, reg, holder, index); - -- // Check that the function really is a function. -- __ tst(r1, Operand(kSmiTagMask)); -- __ b(eq, &miss); -- // Get the map. -- __ CompareObjectType(r1, r2, r2, JS_FUNCTION_TYPE); -- __ b(ne, &miss); -- -- // Patch the receiver on the stack with the global proxy if -- // necessary. -- if (object->IsGlobalObject()) { -- __ ldr(r3, FieldMemOperand(r0, GlobalObject::kGlobalReceiverOffset)); -- __ str(r3, MemOperand(sp, argc * kPointerSize)); -- } -- -- // Invoke the function. -- __ InvokeFunction(r1, arguments(), JUMP_FUNCTION); - - // Handle call cache miss. - __ bind(&miss); ---- 923,929 ---- - CheckPrototypes(JSObject::cast(object), r0, holder, r3, r2, name, &miss); - GenerateFastPropertyLoad(masm(), r1, reg, holder, index); - -+ GenerateCallFunction(masm(), object, arguments(), &miss); - - // Handle call cache miss. - __ bind(&miss); -*************** Object* CallStubCompiler::CompileCallConstant(Object* object, -*** 715,730 **** - UNREACHABLE(); - } - -- // Get the function and setup the context. -- __ mov(r1, Operand(Handle<JSFunction>(function))); -- __ ldr(cp, FieldMemOperand(r1, JSFunction::kContextOffset)); -- -- // Jump to the cached code (tail call). -- ASSERT(function->is_compiled()); -- Handle<Code> code(function->code()); -- ParameterCount expected(function->shared()->formal_parameter_count()); -- __ InvokeCode(code, expected, arguments(), -- RelocInfo::CODE_TARGET, JUMP_FUNCTION); - - // Handle call cache miss. - __ bind(&miss); ---- 1051,1057 ---- - UNREACHABLE(); - } - -+ GenerateCallConstFunction(masm(), function, arguments()); - - // Handle call cache miss. - __ bind(&miss); -*************** Object* CallStubCompiler::CompileCallInterceptor(Object* object, -*** 748,754 **** - // ----------------------------------- - Label miss; - -- // TODO(1224669): Implement. - - // Handle call cache miss. - __ bind(&miss); ---- 1075,1108 ---- - // ----------------------------------- - Label miss; - -+ // Get the number of arguments. -+ const int argc = arguments().immediate(); -+ -+ LookupResult lookup; -+ LookupPostInterceptor(holder, name, &lookup); -+ -+ // Get the receiver from the stack into r0. -+ __ ldr(r0, MemOperand(sp, argc * kPointerSize)); -+ // Load the name from the stack into r1. -+ __ ldr(r1, MemOperand(sp, (argc + 1) * kPointerSize)); -+ -+ CallInterceptorCompiler compiler(arguments(), r1); -+ CompileLoadInterceptor(&compiler, -+ this, -+ masm(), -+ JSObject::cast(object), -+ holder, -+ name, -+ &lookup, -+ r0, -+ r2, -+ r3, -+ &miss); -+ -+ // Restore receiver. -+ __ ldr(r0, MemOperand(sp, argc * kPointerSize)); -+ -+ GenerateCallFunction(masm(), object, arguments(), &miss); - - // Handle call cache miss. - __ bind(&miss); diff --git a/src/arm/virtual-frame-arm.cc b/src/arm/virtual-frame-arm.cc index 7a8ac726..0f7c5971 100644 --- a/src/arm/virtual-frame-arm.cc +++ b/src/arm/virtual-frame-arm.cc @@ -47,7 +47,7 @@ VirtualFrame::VirtualFrame() : elements_(parameter_count() + local_count() + kPreallocatedElements), stack_pointer_(parameter_count()) { // 0-based index of TOS. for (int i = 0; i <= stack_pointer_; i++) { - elements_.Add(FrameElement::MemoryElement()); + elements_.Add(FrameElement::MemoryElement(NumberInfo::kUnknown)); } for (int i = 0; i < RegisterAllocator::kNumRegisters; i++) { register_locations_[i] = kIllegalIndex; @@ -233,6 +233,14 @@ void VirtualFrame::CallRuntime(Runtime::FunctionId id, int arg_count) { } +#ifdef ENABLE_DEBUGGER_SUPPORT +void VirtualFrame::DebugBreak() { + ASSERT(cgen()->HasValidEntryRegisters()); + __ DebugBreak(); +} +#endif + + void VirtualFrame::InvokeBuiltin(Builtins::JavaScript id, InvokeJSFlags flags, int arg_count) { @@ -305,7 +313,7 @@ void VirtualFrame::EmitPop(Register reg) { void VirtualFrame::EmitPush(Register reg) { ASSERT(stack_pointer_ == element_count() - 1); - elements_.Add(FrameElement::MemoryElement()); + elements_.Add(FrameElement::MemoryElement(NumberInfo::kUnknown)); stack_pointer_++; __ push(reg); } diff --git a/src/arm/virtual-frame-arm.h b/src/arm/virtual-frame-arm.h index 9a2f7d36..a45cfc6e 100644 --- a/src/arm/virtual-frame-arm.h +++ b/src/arm/virtual-frame-arm.h @@ -68,7 +68,8 @@ class VirtualFrame : public ZoneObject { MacroAssembler* masm() { return cgen()->masm(); } // Create a duplicate of an existing valid frame element. - FrameElement CopyElementAt(int index); + FrameElement CopyElementAt(int index, + NumberInfo::Type info = NumberInfo::kUnknown); // The number of elements on the virtual frame. int element_count() { return elements_.length(); } @@ -297,6 +298,10 @@ class VirtualFrame : public ZoneObject { void CallRuntime(Runtime::Function* f, int arg_count); void CallRuntime(Runtime::FunctionId id, int arg_count); +#ifdef ENABLE_DEBUGGER_SUPPORT + void DebugBreak(); +#endif + // Invoke builtin given the number of arguments it expects on (and // removes from) the stack. void InvokeBuiltin(Builtins::JavaScript id, @@ -339,7 +344,7 @@ class VirtualFrame : public ZoneObject { void EmitPushMultiple(int count, int src_regs); // Push an element on the virtual frame. - void Push(Register reg); + void Push(Register reg, NumberInfo::Type info = NumberInfo::kUnknown); void Push(Handle<Object> value); void Push(Smi* value) { Push(Handle<Object>(value)); } diff --git a/src/array.js b/src/array.js index c3ab179d..c28a6629 100644 --- a/src/array.js +++ b/src/array.js @@ -566,10 +566,11 @@ function ArraySlice(start, end) { function ArraySplice(start, delete_count) { var num_arguments = %_ArgumentsLength(); - // SpiderMonkey and KJS return undefined in the case where no + // SpiderMonkey and JSC return undefined in the case where no // arguments are given instead of using the implicit undefined // arguments. This does not follow ECMA-262, but we do the same for // compatibility. + // TraceMonkey follows ECMA-262 though. if (num_arguments == 0) return; var len = TO_UINT32(this.length); @@ -582,7 +583,7 @@ function ArraySplice(start, delete_count) { if (start_i > len) start_i = len; } - // SpiderMonkey and KJS treat the case where no delete count is + // SpiderMonkey, TraceMonkey and JSC treat the case where no delete count is // given differently from when an undefined delete count is given. // This does not follow ECMA-262, but we do the same for // compatibility. diff --git a/src/assembler.cc b/src/assembler.cc index dbf2742b..aaf10efe 100644 --- a/src/assembler.cc +++ b/src/assembler.cc @@ -430,6 +430,11 @@ const char* RelocInfo::RelocModeName(RelocInfo::Mode rmode) { return "code target (js construct call)"; case RelocInfo::CODE_TARGET_CONTEXT: return "code target (context)"; + case RelocInfo::DEBUG_BREAK: +#ifndef ENABLE_DEBUGGER_SUPPORT + UNREACHABLE(); +#endif + return "debug break"; case RelocInfo::CODE_TARGET: return "code target"; case RelocInfo::RUNTIME_ENTRY: @@ -485,6 +490,11 @@ void RelocInfo::Verify() { case EMBEDDED_OBJECT: Object::VerifyPointer(target_object()); break; + case DEBUG_BREAK: +#ifndef ENABLE_DEBUGGER_SUPPORT + UNREACHABLE(); + break; +#endif case CONSTRUCT_CALL: case CODE_TARGET_CONTEXT: case CODE_TARGET: { @@ -569,6 +579,11 @@ ExternalReference ExternalReference::random_positive_smi_function() { } +ExternalReference ExternalReference::transcendental_cache_array_address() { + return ExternalReference(TranscendentalCache::cache_array_address()); +} + + ExternalReference ExternalReference::keyed_lookup_cache_keys() { return ExternalReference(KeyedLookupCache::keys_address()); } @@ -609,6 +624,11 @@ ExternalReference ExternalReference::new_space_start() { } +ExternalReference ExternalReference::new_space_mask() { + return ExternalReference(reinterpret_cast<Address>(Heap::NewSpaceMask())); +} + + ExternalReference ExternalReference::new_space_allocation_top_address() { return ExternalReference(Heap::NewSpaceAllocationTopAddress()); } diff --git a/src/assembler.h b/src/assembler.h index 942ce476..004ede35 100644 --- a/src/assembler.h +++ b/src/assembler.h @@ -119,6 +119,7 @@ class RelocInfo BASE_EMBEDDED { // Please note the order is important (see IsCodeTarget, IsGCRelocMode). CONSTRUCT_CALL, // code target that is a call to a JavaScript constructor. CODE_TARGET_CONTEXT, // code target used for contextual loads. + DEBUG_BREAK, CODE_TARGET, // code target which is not any of the above. EMBEDDED_OBJECT, EMBEDDED_STRING, @@ -399,6 +400,7 @@ class ExternalReference BASE_EMBEDDED { static ExternalReference perform_gc_function(); static ExternalReference random_positive_smi_function(); + static ExternalReference transcendental_cache_array_address(); // Static data in the keyed lookup cache. static ExternalReference keyed_lookup_cache_keys(); @@ -426,6 +428,7 @@ class ExternalReference BASE_EMBEDDED { // Static variable Heap::NewSpaceStart() static ExternalReference new_space_start(); + static ExternalReference new_space_mask(); static ExternalReference heap_always_allocate_scope_depth(); // Used for fast allocation in generated code. @@ -1,4 +1,4 @@ -// Copyright 2006-2008 the V8 project authors. All rights reserved. +// Copyright 2010 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: @@ -102,6 +102,7 @@ namespace internal { // Forward declarations class TargetCollector; class MaterializedLiteral; +class DefinitionInfo; #define DEF_FORWARD_DECLARATION(type) class type; AST_NODE_LIST(DEF_FORWARD_DECLARATION) @@ -182,7 +183,7 @@ class Expression: public AstNode { static const int kNoLabel = -1; - Expression() : num_(kNoLabel) {} + Expression() : num_(kNoLabel), def_(NULL), defined_vars_(NULL) {} virtual Expression* AsExpression() { return this; } @@ -193,6 +194,15 @@ class Expression: public AstNode { // names because [] for string objects is handled only by keyed ICs. virtual bool IsPropertyName() { return false; } + // True if the expression does not have (evaluated) subexpressions. + // Function literals are leaves because their subexpressions are not + // evaluated. + virtual bool IsLeaf() { return false; } + + // True if the expression has no side effects and is safe to + // evaluate out of order. + virtual bool IsTrivial() { return false; } + // Mark the expression as being compiled as an expression // statement. This is used to transform postfix increments to // (faster) prefix increments. @@ -206,9 +216,20 @@ class Expression: public AstNode { // AST node numbering ordered by evaluation order. void set_num(int n) { num_ = n; } + // Data flow information. + DefinitionInfo* var_def() { return def_; } + void set_var_def(DefinitionInfo* def) { def_ = def; } + + ZoneList<DefinitionInfo*>* defined_vars() { return defined_vars_; } + void set_defined_vars(ZoneList<DefinitionInfo*>* defined_vars) { + defined_vars_ = defined_vars; + } + private: StaticType type_; int num_; + DefinitionInfo* def_; + ZoneList<DefinitionInfo*>* defined_vars_; }; @@ -720,6 +741,9 @@ class Literal: public Expression { return false; } + virtual bool IsLeaf() { return true; } + virtual bool IsTrivial() { return true; } + // Identity testers. bool IsNull() const { return handle_.is_identical_to(Factory::null_value()); } bool IsTrue() const { return handle_.is_identical_to(Factory::true_value()); } @@ -802,6 +826,8 @@ class ObjectLiteral: public MaterializedLiteral { virtual ObjectLiteral* AsObjectLiteral() { return this; } virtual void Accept(AstVisitor* v); + virtual bool IsLeaf() { return properties()->is_empty(); } + Handle<FixedArray> constant_properties() const { return constant_properties_; } @@ -825,6 +851,8 @@ class RegExpLiteral: public MaterializedLiteral { virtual void Accept(AstVisitor* v); + virtual bool IsLeaf() { return true; } + Handle<String> pattern() const { return pattern_; } Handle<String> flags() const { return flags_; } @@ -849,6 +877,8 @@ class ArrayLiteral: public MaterializedLiteral { virtual void Accept(AstVisitor* v); virtual ArrayLiteral* AsArrayLiteral() { return this; } + virtual bool IsLeaf() { return values()->is_empty(); } + Handle<FixedArray> constant_elements() const { return constant_elements_; } ZoneList<Expression*>* values() const { return values_; } @@ -896,6 +926,15 @@ class VariableProxy: public Expression { return var_ == NULL ? true : var_->IsValidLeftHandSide(); } + virtual bool IsLeaf() { + ASSERT(var_ != NULL); // Variable must be resolved. + return var()->is_global() || var()->rewrite()->IsLeaf(); + } + + // Reading from a mutable variable is a side effect, but 'this' is + // immutable. + virtual bool IsTrivial() { return is_this(); } + bool IsVariable(Handle<String> n) { return !is_this() && name().is_identical_to(n); } @@ -981,6 +1020,8 @@ class Slot: public Expression { // Type testing & conversion virtual Slot* AsSlot() { return this; } + virtual bool IsLeaf() { return true; } + // Accessors Variable* var() const { return var_; } Type type() const { return type_; } @@ -1337,6 +1378,8 @@ class FunctionLiteral: public Expression { // Type testing & conversion virtual FunctionLiteral* AsFunctionLiteral() { return this; } + virtual bool IsLeaf() { return true; } + Handle<String> name() const { return name_; } Scope* scope() const { return scope_; } ZoneList<Statement*>* body() const { return body_; } @@ -1403,6 +1446,8 @@ class FunctionBoilerplateLiteral: public Expression { Handle<JSFunction> boilerplate() const { return boilerplate_; } + virtual bool IsLeaf() { return true; } + virtual void Accept(AstVisitor* v); private: @@ -1413,6 +1458,7 @@ class FunctionBoilerplateLiteral: public Expression { class ThisFunction: public Expression { public: virtual void Accept(AstVisitor* v); + virtual bool IsLeaf() { return true; } }; diff --git a/src/bootstrapper.cc b/src/bootstrapper.cc index 62edae50..6e565e08 100644 --- a/src/bootstrapper.cc +++ b/src/bootstrapper.cc @@ -177,116 +177,6 @@ void Bootstrapper::TearDown() { } -// Pending fixups are code positions that refer to builtin code -// objects that were not available at the time the code was generated. -// The pending list is processed whenever an environment has been -// created. -class PendingFixups : public AllStatic { - public: - static void Add(Code* code, MacroAssembler* masm); - static bool Process(Handle<JSBuiltinsObject> builtins); - - static void Iterate(ObjectVisitor* v); - - private: - static List<Object*> code_; - static List<const char*> name_; - static List<int> pc_; - static List<uint32_t> flags_; - - static void Clear(); -}; - - -List<Object*> PendingFixups::code_(0); -List<const char*> PendingFixups::name_(0); -List<int> PendingFixups::pc_(0); -List<uint32_t> PendingFixups::flags_(0); - - -void PendingFixups::Add(Code* code, MacroAssembler* masm) { - // Note this code is not only called during bootstrapping. - List<MacroAssembler::Unresolved>* unresolved = masm->unresolved(); - int n = unresolved->length(); - for (int i = 0; i < n; i++) { - const char* name = unresolved->at(i).name; - code_.Add(code); - name_.Add(name); - pc_.Add(unresolved->at(i).pc); - flags_.Add(unresolved->at(i).flags); - LOG(StringEvent("unresolved", name)); - } -} - - -bool PendingFixups::Process(Handle<JSBuiltinsObject> builtins) { - HandleScope scope; - // NOTE: Extra fixups may be added to the list during the iteration - // due to lazy compilation of functions during the processing. Do not - // cache the result of getting the length of the code list. - for (int i = 0; i < code_.length(); i++) { - const char* name = name_[i]; - uint32_t flags = flags_[i]; - Handle<String> symbol = Factory::LookupAsciiSymbol(name); - Object* o = builtins->GetProperty(*symbol); -#ifdef DEBUG - if (!o->IsJSFunction()) { - V8_Fatal(__FILE__, __LINE__, "Cannot resolve call to builtin %s", name); - } -#endif - Handle<SharedFunctionInfo> shared(JSFunction::cast(o)->shared()); - // Make sure the number of parameters match the formal parameter count. - int argc = Bootstrapper::FixupFlagsArgumentsCount::decode(flags); - USE(argc); - ASSERT(shared->formal_parameter_count() == argc); - // Do lazy compilation if necessary and check for stack overflows. - if (!EnsureCompiled(shared, CLEAR_EXCEPTION)) { - Clear(); - return false; - } - Code* code = Code::cast(code_[i]); - Address pc = code->instruction_start() + pc_[i]; - RelocInfo target(pc, RelocInfo::CODE_TARGET, 0); - bool use_code_object = Bootstrapper::FixupFlagsUseCodeObject::decode(flags); - if (use_code_object) { - target.set_target_object(shared->code()); - } else { - target.set_target_address(shared->code()->instruction_start()); - } - LOG(StringEvent("resolved", name)); - } - Clear(); - - // TODO(1240818): We should probably try to avoid doing this for all - // the V8 builtin JS files. It should only happen after running - // runtime.js - just like there shouldn't be any fixups left after - // that. - for (int i = 0; i < Builtins::NumberOfJavaScriptBuiltins(); i++) { - Builtins::JavaScript id = static_cast<Builtins::JavaScript>(i); - Handle<String> name = Factory::LookupAsciiSymbol(Builtins::GetName(id)); - JSFunction* function = JSFunction::cast(builtins->GetProperty(*name)); - builtins->set_javascript_builtin(id, function); - } - - return true; -} - - -void PendingFixups::Clear() { - code_.Clear(); - name_.Clear(); - pc_.Clear(); - flags_.Clear(); -} - - -void PendingFixups::Iterate(ObjectVisitor* v) { - if (!code_.is_empty()) { - v->VisitPointers(&code_[0], &code_[0] + code_.length()); - } -} - - class Genesis BASE_EMBEDDED { public: Genesis(Handle<Object> global_object, @@ -327,6 +217,10 @@ class Genesis BASE_EMBEDDED { // deserialized, leaving the GC to pick it up. void HookUpGlobalProxy(Handle<GlobalObject> inner_global, Handle<JSGlobalProxy> global_proxy); + // Similarly, we want to use the inner global that has been created by the + // templates passed through the API. The inner global from the snapshot is + // detached from the other objects in the snapshot. + void HookUpInnerGlobal(Handle<GlobalObject> inner_global); // New context initialization. Used for creating a context from scratch. void InitializeGlobal(Handle<GlobalObject> inner_global, Handle<JSFunction> empty_function); @@ -341,12 +235,10 @@ class Genesis BASE_EMBEDDED { static bool InstallExtension(const char* name); static bool InstallExtension(v8::RegisteredExtension* current); static void InstallSpecialObjects(Handle<Context> global_context); + bool InstallJSBuiltins(Handle<JSBuiltinsObject> builtins); bool ConfigureApiObject(Handle<JSObject> object, Handle<ObjectTemplateInfo> object_template); bool ConfigureGlobalObjects(v8::Handle<v8::ObjectTemplate> global_template); - void TransferMapsToDeserializedGlobals( - Handle<GlobalObject> inner_global_outside_snapshot, - Handle<GlobalObject> inner_global_from_snapshot); // Migrates all properties from the 'from' object to the 'to' // object and overrides the prototype in 'to' with the one from @@ -385,15 +277,6 @@ class Genesis BASE_EMBEDDED { void Bootstrapper::Iterate(ObjectVisitor* v) { extensions_cache.Iterate(v); v->Synchronize("Extensions"); - PendingFixups::Iterate(v); - v->Synchronize("PendingFixups"); -} - - -// While setting up the environment, we collect code positions that -// need to be patched before we can run any code in the environment. -void Bootstrapper::AddFixup(Code* code, MacroAssembler* masm) { - PendingFixups::Add(code, masm); } @@ -634,7 +517,7 @@ Handle<JSGlobalProxy> Genesis::CreateNewGlobals( Handle<Code> code = Handle<Code>(Builtins::builtin(Builtins::Illegal)); js_global_function = Factory::NewFunction(name, JS_GLOBAL_OBJECT_TYPE, - JSGlobalObject::kSize + 17 * kPointerSize, code, true); + JSGlobalObject::kSize, code, true); // Change the constructor property of the prototype of the // hidden global function to refer to the Object function. Handle<JSObject> prototype = @@ -704,6 +587,28 @@ void Genesis::HookUpGlobalProxy(Handle<GlobalObject> inner_global, } +void Genesis::HookUpInnerGlobal(Handle<GlobalObject> inner_global) { + Handle<GlobalObject> inner_global_from_snapshot( + GlobalObject::cast(global_context_->extension())); + Handle<JSBuiltinsObject> builtins_global(global_context_->builtins()); + global_context_->set_extension(*inner_global); + global_context_->set_global(*inner_global); + global_context_->set_security_token(*inner_global); + static const PropertyAttributes attributes = + static_cast<PropertyAttributes>(READ_ONLY | DONT_DELETE); + ForceSetProperty(builtins_global, + Factory::LookupAsciiSymbol("global"), + inner_global, + attributes); + // Setup the reference from the global object to the builtins object. + JSGlobalObject::cast(*inner_global)->set_builtins(*builtins_global); + TransferNamedProperties(inner_global_from_snapshot, inner_global); + TransferIndexedProperties(inner_global_from_snapshot, inner_global); +} + + +// This is only called if we are not using snapshots. The equivalent +// work in the snapshot case is done in HookUpInnerGlobal. void Genesis::InitializeGlobal(Handle<GlobalObject> inner_global, Handle<JSFunction> empty_function) { // --- G l o b a l C o n t e x t --- @@ -865,11 +770,11 @@ void Genesis::InitializeGlobal(Handle<GlobalObject> inner_global, #ifdef DEBUG LookupResult lookup; result->LocalLookup(Heap::callee_symbol(), &lookup); - ASSERT(lookup.IsValid() && (lookup.type() == FIELD)); + ASSERT(lookup.IsProperty() && (lookup.type() == FIELD)); ASSERT(lookup.GetFieldIndex() == Heap::arguments_callee_index); result->LocalLookup(Heap::length_symbol(), &lookup); - ASSERT(lookup.IsValid() && (lookup.type() == FIELD)); + ASSERT(lookup.IsProperty() && (lookup.type() == FIELD)); ASSERT(lookup.GetFieldIndex() == Heap::arguments_length_index); ASSERT(result->map()->inobject_properties() > Heap::arguments_callee_index); @@ -977,6 +882,7 @@ bool Genesis::CompileScriptCached(Vector<const char> name, 0, extension, NULL, + Handle<String>::null(), use_runtime_context ? NATIVES_CODE : NOT_NATIVES_CODE); if (boilerplate.is_null()) return false; if (cache != NULL) cache->Add(name, boilerplate); @@ -1003,8 +909,7 @@ bool Genesis::CompileScriptCached(Vector<const char> name, Handle<Object> result = Execution::Call(fun, receiver, 0, NULL, &has_pending_exception); if (has_pending_exception) return false; - return PendingFixups::Process( - Handle<JSBuiltinsObject>(top_context->builtins())); + return true; } @@ -1024,7 +929,6 @@ void Genesis::InstallNativeFunctions() { INSTALL_NATIVE(JSFunction, "ToInteger", to_integer_fun); INSTALL_NATIVE(JSFunction, "ToUint32", to_uint32_fun); INSTALL_NATIVE(JSFunction, "ToInt32", to_int32_fun); - INSTALL_NATIVE(JSFunction, "ToBoolean", to_boolean_fun); INSTALL_NATIVE(JSFunction, "GlobalEval", global_eval_fun); INSTALL_NATIVE(JSFunction, "Instantiate", instantiate_fun); INSTALL_NATIVE(JSFunction, "ConfigureTemplateInstance", @@ -1211,6 +1115,10 @@ bool Genesis::InstallNatives() { i++) { Vector<const char> name = Natives::GetScriptName(i); if (!CompileBuiltin(i)) return false; + // TODO(ager): We really only need to install the JS builtin + // functions on the builtins object after compiling and running + // runtime.js. + if (!InstallJSBuiltins(builtins)) return false; } InstallNativeFunctions(); @@ -1397,17 +1305,19 @@ bool Genesis::InstallExtension(v8::RegisteredExtension* current) { } -void Genesis::TransferMapsToDeserializedGlobals( - Handle<GlobalObject> inner_global_outside_snapshot, - Handle<GlobalObject> inner_global_from_snapshot) { - Handle<Map> from_map(inner_global_outside_snapshot->map()); -#ifdef DEBUG - Handle<Map> to_map(inner_global_from_snapshot->map()); - ASSERT_EQ(to_map->instance_size(), from_map->instance_size()); - ASSERT_EQ(0, to_map->inobject_properties()); - ASSERT_EQ(0, from_map->inobject_properties()); -#endif - inner_global_from_snapshot->set_map(*from_map); +bool Genesis::InstallJSBuiltins(Handle<JSBuiltinsObject> builtins) { + HandleScope scope; + for (int i = 0; i < Builtins::NumberOfJavaScriptBuiltins(); i++) { + Builtins::JavaScript id = static_cast<Builtins::JavaScript>(i); + Handle<String> name = Factory::LookupAsciiSymbol(Builtins::GetName(id)); + Handle<JSFunction> function + = Handle<JSFunction>(JSFunction::cast(builtins->GetProperty(*name))); + builtins->set_javascript_builtin(id, *function); + Handle<SharedFunctionInfo> shared + = Handle<SharedFunctionInfo>(function->shared()); + if (!EnsureCompiled(shared, CLEAR_EXCEPTION)) return false; + } + return true; } @@ -1415,7 +1325,7 @@ bool Genesis::ConfigureGlobalObjects( v8::Handle<v8::ObjectTemplate> global_proxy_template) { Handle<JSObject> global_proxy( JSObject::cast(global_context()->global_proxy())); - Handle<JSObject> js_global(JSObject::cast(global_context()->global())); + Handle<JSObject> inner_global(JSObject::cast(global_context()->global())); if (!global_proxy_template.IsEmpty()) { // Configure the global proxy object. @@ -1429,11 +1339,11 @@ bool Genesis::ConfigureGlobalObjects( if (!proxy_constructor->prototype_template()->IsUndefined()) { Handle<ObjectTemplateInfo> inner_data( ObjectTemplateInfo::cast(proxy_constructor->prototype_template())); - if (!ConfigureApiObject(js_global, inner_data)) return false; + if (!ConfigureApiObject(inner_global, inner_data)) return false; } } - SetObjectPrototype(global_proxy, js_global); + SetObjectPrototype(global_proxy, inner_global); return true; } @@ -1485,7 +1395,7 @@ void Genesis::TransferNamedProperties(Handle<JSObject> from, LookupResult result; to->LocalLookup(descs->GetKey(i), &result); // If the property is already there we skip it - if (result.IsValid()) continue; + if (result.IsProperty()) continue; HandleScope inner; ASSERT(!to->HasFastProperties()); // Add to dictionary. @@ -1520,7 +1430,7 @@ void Genesis::TransferNamedProperties(Handle<JSObject> from, // If the property is already there we skip it. LookupResult result; to->LocalLookup(String::cast(raw_key), &result); - if (result.IsValid()) continue; + if (result.IsProperty()) continue; // Set the property. Handle<String> key = Handle<String>(String::cast(raw_key)); Handle<Object> value = Handle<Object>(properties->ValueAt(i)); @@ -1604,25 +1514,33 @@ void Genesis::AddSpecialFunction(Handle<JSObject> prototype, void Genesis::BuildSpecialFunctionTable() { HandleScope scope; Handle<JSObject> global = Handle<JSObject>(global_context()->global()); - // Add special versions for Array.prototype.pop and push. + // Add special versions for some Array.prototype functions. Handle<JSFunction> function = Handle<JSFunction>( JSFunction::cast(global->GetProperty(Heap::Array_symbol()))); Handle<JSObject> visible_prototype = Handle<JSObject>(JSObject::cast(function->prototype())); - // Remember to put push and pop on the hidden prototype if it's there. - Handle<JSObject> push_and_pop_prototype; + // Remember to put those specializations on the hidden prototype if present. + Handle<JSObject> special_prototype; Handle<Object> superproto(visible_prototype->GetPrototype()); if (superproto->IsJSObject() && JSObject::cast(*superproto)->map()->is_hidden_prototype()) { - push_and_pop_prototype = Handle<JSObject>::cast(superproto); + special_prototype = Handle<JSObject>::cast(superproto); } else { - push_and_pop_prototype = visible_prototype; + special_prototype = visible_prototype; } - AddSpecialFunction(push_and_pop_prototype, "pop", + AddSpecialFunction(special_prototype, "pop", Handle<Code>(Builtins::builtin(Builtins::ArrayPop))); - AddSpecialFunction(push_and_pop_prototype, "push", + AddSpecialFunction(special_prototype, "push", Handle<Code>(Builtins::builtin(Builtins::ArrayPush))); + AddSpecialFunction(special_prototype, "shift", + Handle<Code>(Builtins::builtin(Builtins::ArrayShift))); + AddSpecialFunction(special_prototype, "unshift", + Handle<Code>(Builtins::builtin(Builtins::ArrayUnshift))); + AddSpecialFunction(special_prototype, "slice", + Handle<Code>(Builtins::builtin(Builtins::ArraySlice))); + AddSpecialFunction(special_prototype, "splice", + Handle<Code>(Builtins::builtin(Builtins::ArraySplice))); } @@ -1648,19 +1566,15 @@ Genesis::Genesis(Handle<Object> global_object, JSFunction* empty_function = JSFunction::cast(result_->function_map()->prototype()); empty_function_ = Handle<JSFunction>(empty_function); - Handle<GlobalObject> inner_global_outside_snapshot; + Handle<GlobalObject> inner_global; Handle<JSGlobalProxy> global_proxy = CreateNewGlobals(global_template, global_object, - &inner_global_outside_snapshot); - // CreateNewGlobals returns an inner global that it just made, but - // we won't give that to HookUpGlobalProxy because we want to hook - // up the global proxy to the one from the snapshot. - Handle<GlobalObject> inner_global( - GlobalObject::cast(global_context_->extension())); + &inner_global); + HookUpGlobalProxy(inner_global, global_proxy); - TransferMapsToDeserializedGlobals(inner_global_outside_snapshot, - inner_global); + HookUpInnerGlobal(inner_global); + if (!ConfigureGlobalObjects(global_template)) return; } else { // We get here if there was no context snapshot. diff --git a/src/bootstrapper.h b/src/bootstrapper.h index 19fc39aa..f905a28b 100644 --- a/src/bootstrapper.h +++ b/src/bootstrapper.h @@ -74,9 +74,6 @@ class Bootstrapper : public AllStatic { // Accessors for the native scripts cache. Used in lazy loading. static Handle<String> NativesSourceLookup(int index); - // Append code that needs fixup at the end of boot strapping. - static void AddFixup(Code* code, MacroAssembler* masm); - // Tells whether bootstrapping is active. static bool IsActive() { return BootstrapperActive::IsActive(); } diff --git a/src/builtins.cc b/src/builtins.cc index db0770f3..ee98769b 100644 --- a/src/builtins.cc +++ b/src/builtins.cc @@ -168,28 +168,6 @@ static inline bool CalledAsConstructor() { // ---------------------------------------------------------------------------- -Handle<Code> Builtins::GetCode(JavaScript id, bool* resolved) { - Code* code = Builtins::builtin(Builtins::Illegal); - *resolved = false; - - if (Top::context() != NULL) { - Object* object = Top::builtins()->javascript_builtin(id); - if (object->IsJSFunction()) { - Handle<SharedFunctionInfo> shared(JSFunction::cast(object)->shared()); - // Make sure the number of parameters match the formal parameter count. - ASSERT(shared->formal_parameter_count() == - Builtins::GetArgumentsCount(id)); - if (EnsureCompiled(shared, CLEAR_EXCEPTION)) { - code = shared->code(); - *resolved = true; - } - } - } - - return Handle<Code>(code); -} - - BUILTIN(Illegal) { UNREACHABLE(); return Heap::undefined_value(); // Make compiler happy. @@ -268,19 +246,19 @@ BUILTIN(ArrayPush) { JSArray* array = JSArray::cast(*args.receiver()); ASSERT(array->HasFastElements()); - // Make sure we have space for the elements. int len = Smi::cast(array->length())->value(); + int to_add = args.length() - 1; + if (to_add == 0) { + return Smi::FromInt(len); + } + // Currently fixed arrays cannot grow too big, so + // we should never hit this case. + ASSERT(to_add <= (Smi::kMaxValue - len)); - // Set new length. - int new_length = len + args.length() - 1; + int new_length = len + to_add; FixedArray* elms = FixedArray::cast(array->elements()); - if (new_length <= elms->length()) { - // Backing storage has extra space for the provided values. - for (int index = 0; index < args.length() - 1; index++) { - elms->set(index + len, args[index+1]); - } - } else { + if (new_length > elms->length()) { // New backing storage is needed. int capacity = new_length + (new_length >> 1) + 16; Object* obj = Heap::AllocateFixedArrayWithHoles(capacity); @@ -291,16 +269,21 @@ BUILTIN(ArrayPush) { WriteBarrierMode mode = new_elms->GetWriteBarrierMode(no_gc); // Fill out the new array with old elements. for (int i = 0; i < len; i++) new_elms->set(i, elms->get(i), mode); - // Add the provided values. - for (int index = 0; index < args.length() - 1; index++) { - new_elms->set(index + len, args[index+1], mode); - } - // Set the new backing storage. - array->set_elements(new_elms); + elms = new_elms; + array->set_elements(elms); + } + + AssertNoAllocation no_gc; + WriteBarrierMode mode = elms->GetWriteBarrierMode(no_gc); + + // Add the provided values. + for (int index = 0; index < to_add; index++) { + elms->set(index + len, args[index + 1], mode); } + // Set the length. array->set_length(Smi::FromInt(new_length)); - return array->length(); + return Smi::FromInt(new_length); } @@ -335,6 +318,355 @@ BUILTIN(ArrayPop) { } +static Object* GetElementToMove(uint32_t index, + FixedArray* elms, + JSObject* prototype) { + Object* e = elms->get(index); + if (e->IsTheHole() && prototype->HasElement(index)) { + e = prototype->GetElement(index); + } + return e; +} + + +BUILTIN(ArrayShift) { + JSArray* array = JSArray::cast(*args.receiver()); + ASSERT(array->HasFastElements()); + + int len = Smi::cast(array->length())->value(); + if (len == 0) return Heap::undefined_value(); + + // Fetch the prototype. + JSFunction* array_function = + Top::context()->global_context()->array_function(); + JSObject* prototype = JSObject::cast(array_function->prototype()); + + FixedArray* elms = FixedArray::cast(array->elements()); + + // Get first element + Object* first = elms->get(0); + if (first->IsTheHole()) { + first = prototype->GetElement(0); + } + + // Shift the elements. + for (int i = 0; i < len - 1; i++) { + elms->set(i, GetElementToMove(i + 1, elms, prototype)); + } + elms->set(len - 1, Heap::the_hole_value()); + + // Set the length. + array->set_length(Smi::FromInt(len - 1)); + + return first; +} + + +BUILTIN(ArrayUnshift) { + JSArray* array = JSArray::cast(*args.receiver()); + ASSERT(array->HasFastElements()); + + int len = Smi::cast(array->length())->value(); + int to_add = args.length() - 1; + // Note that we cannot quit early if to_add == 0 as + // values should be lifted from prototype into + // the array. + + int new_length = len + to_add; + // Currently fixed arrays cannot grow too big, so + // we should never hit this case. + ASSERT(to_add <= (Smi::kMaxValue - len)); + + FixedArray* elms = FixedArray::cast(array->elements()); + + // Fetch the prototype. + JSFunction* array_function = + Top::context()->global_context()->array_function(); + JSObject* prototype = JSObject::cast(array_function->prototype()); + + if (new_length > elms->length()) { + // New backing storage is needed. + int capacity = new_length + (new_length >> 1) + 16; + Object* obj = Heap::AllocateFixedArrayWithHoles(capacity); + if (obj->IsFailure()) return obj; + + AssertNoAllocation no_gc; + FixedArray* new_elms = FixedArray::cast(obj); + WriteBarrierMode mode = new_elms->GetWriteBarrierMode(no_gc); + // Fill out the new array with old elements. + for (int i = 0; i < len; i++) + new_elms->set(to_add + i, + GetElementToMove(i, elms, prototype), + mode); + + elms = new_elms; + array->set_elements(elms); + } else { + AssertNoAllocation no_gc; + WriteBarrierMode mode = elms->GetWriteBarrierMode(no_gc); + + // Move elements to the right + for (int i = 0; i < len; i++) { + elms->set(new_length - i - 1, + GetElementToMove(len - i - 1, elms, prototype), + mode); + } + } + + // Add the provided values. + AssertNoAllocation no_gc; + WriteBarrierMode mode = elms->GetWriteBarrierMode(no_gc); + for (int i = 0; i < to_add; i++) { + elms->set(i, args[i + 1], mode); + } + + // Set the length. + array->set_length(Smi::FromInt(new_length)); + return Smi::FromInt(new_length); +} + + +static Object* CallJsBuiltin(const char* name, + BuiltinArguments<NO_EXTRA_ARGUMENTS> args) { + HandleScope handleScope; + + Handle<Object> js_builtin = + GetProperty(Handle<JSObject>(Top::global_context()->builtins()), + name); + ASSERT(js_builtin->IsJSFunction()); + Handle<JSFunction> function(Handle<JSFunction>::cast(js_builtin)); + Vector<Object**> argv(Vector<Object**>::New(args.length() - 1)); + int n_args = args.length() - 1; + for (int i = 0; i < n_args; i++) { + argv[i] = &args[i + 1]; + } + bool pending_exception = false; + Handle<Object> result = Execution::Call(function, + args.receiver(), + n_args, + argv.start(), + &pending_exception); + if (pending_exception) return Failure::Exception(); + return *result; +} + + +BUILTIN(ArraySlice) { + JSArray* array = JSArray::cast(*args.receiver()); + ASSERT(array->HasFastElements()); + + int len = Smi::cast(array->length())->value(); + + int n_arguments = args.length() - 1; + + // Note carefully choosen defaults---if argument is missing, + // it's undefined which gets converted to 0 for relativeStart + // and to len for relativeEnd. + int relativeStart = 0; + int relativeEnd = len; + if (n_arguments > 0) { + Object* arg1 = args[1]; + if (arg1->IsSmi()) { + relativeStart = Smi::cast(arg1)->value(); + } else if (!arg1->IsUndefined()) { + return CallJsBuiltin("ArraySlice", args); + } + if (n_arguments > 1) { + Object* arg2 = args[2]; + if (arg2->IsSmi()) { + relativeEnd = Smi::cast(arg2)->value(); + } else if (!arg2->IsUndefined()) { + return CallJsBuiltin("ArraySlice", args); + } + } + } + + // ECMAScript 232, 3rd Edition, Section 15.4.4.10, step 6. + int k = (relativeStart < 0) ? Max(len + relativeStart, 0) + : Min(relativeStart, len); + + // ECMAScript 232, 3rd Edition, Section 15.4.4.10, step 8. + int final = (relativeEnd < 0) ? Max(len + relativeEnd, 0) + : Min(relativeEnd, len); + + // Calculate the length of result array. + int result_len = final - k; + if (result_len < 0) { + result_len = 0; + } + + JSFunction* array_function = + Top::context()->global_context()->array_function(); + Object* result = Heap::AllocateJSObject(array_function); + if (result->IsFailure()) return result; + JSArray* result_array = JSArray::cast(result); + + result = Heap::AllocateFixedArrayWithHoles(result_len); + if (result->IsFailure()) return result; + FixedArray* result_elms = FixedArray::cast(result); + + FixedArray* elms = FixedArray::cast(array->elements()); + + // Fetch the prototype. + JSObject* prototype = JSObject::cast(array_function->prototype()); + + AssertNoAllocation no_gc; + WriteBarrierMode mode = result_elms->GetWriteBarrierMode(no_gc); + + // Fill newly created array. + for (int i = 0; i < result_len; i++) { + result_elms->set(i, + GetElementToMove(k + i, elms, prototype), + mode); + } + + // Set elements. + result_array->set_elements(result_elms); + + // Set the length. + result_array->set_length(Smi::FromInt(result_len)); + return result_array; +} + + +BUILTIN(ArraySplice) { + JSArray* array = JSArray::cast(*args.receiver()); + ASSERT(array->HasFastElements()); + + int len = Smi::cast(array->length())->value(); + + int n_arguments = args.length() - 1; + + // SpiderMonkey and JSC return undefined in the case where no + // arguments are given instead of using the implicit undefined + // arguments. This does not follow ECMA-262, but we do the same for + // compatibility. + // TraceMonkey follows ECMA-262 though. + if (n_arguments == 0) { + return Heap::undefined_value(); + } + + int relativeStart = 0; + Object* arg1 = args[1]; + if (arg1->IsSmi()) { + relativeStart = Smi::cast(arg1)->value(); + } else if (!arg1->IsUndefined()) { + return CallJsBuiltin("ArraySplice", args); + } + int actualStart = (relativeStart < 0) ? Max(len + relativeStart, 0) + : Min(relativeStart, len); + + // SpiderMonkey, TraceMonkey and JSC treat the case where no delete count is + // given differently from when an undefined delete count is given. + // This does not follow ECMA-262, but we do the same for + // compatibility. + int deleteCount = len; + if (n_arguments > 1) { + Object* arg2 = args[2]; + if (arg2->IsSmi()) { + deleteCount = Smi::cast(arg2)->value(); + } else { + return CallJsBuiltin("ArraySplice", args); + } + } + int actualDeleteCount = Min(Max(deleteCount, 0), len - actualStart); + + JSFunction* array_function = + Top::context()->global_context()->array_function(); + + // Allocate result array. + Object* result = Heap::AllocateJSObject(array_function); + if (result->IsFailure()) return result; + JSArray* result_array = JSArray::cast(result); + + result = Heap::AllocateFixedArrayWithHoles(actualDeleteCount); + if (result->IsFailure()) return result; + FixedArray* result_elms = FixedArray::cast(result); + + FixedArray* elms = FixedArray::cast(array->elements()); + + // Fetch the prototype. + JSObject* prototype = JSObject::cast(array_function->prototype()); + + AssertNoAllocation no_gc; + WriteBarrierMode mode = result_elms->GetWriteBarrierMode(no_gc); + + // Fill newly created array. + for (int k = 0; k < actualDeleteCount; k++) { + result_elms->set(k, + GetElementToMove(actualStart + k, elms, prototype), + mode); + } + + // Set elements. + result_array->set_elements(result_elms); + + // Set the length. + result_array->set_length(Smi::FromInt(actualDeleteCount)); + + int itemCount = (n_arguments > 1) ? (n_arguments - 2) : 0; + + int new_length = len - actualDeleteCount + itemCount; + + mode = elms->GetWriteBarrierMode(no_gc); + if (itemCount < actualDeleteCount) { + // Shrink the array. + for (int k = actualStart; k < (len - actualDeleteCount); k++) { + elms->set(k + itemCount, + GetElementToMove(k + actualDeleteCount, elms, prototype), + mode); + } + + for (int k = len; k > new_length; k--) { + elms->set(k - 1, Heap::the_hole_value()); + } + } else if (itemCount > actualDeleteCount) { + // Currently fixed arrays cannot grow too big, so + // we should never hit this case. + ASSERT((itemCount - actualDeleteCount) <= (Smi::kMaxValue - len)); + + FixedArray* source_elms = elms; + + // Check if array need to grow. + if (new_length > elms->length()) { + // New backing storage is needed. + int capacity = new_length + (new_length >> 1) + 16; + Object* obj = Heap::AllocateFixedArrayWithHoles(capacity); + if (obj->IsFailure()) return obj; + + FixedArray* new_elms = FixedArray::cast(obj); + mode = new_elms->GetWriteBarrierMode(no_gc); + + // Copy the part before actualStart as is. + for (int k = 0; k < actualStart; k++) { + new_elms->set(k, elms->get(k), mode); + } + + source_elms = elms; + elms = new_elms; + array->set_elements(elms); + } + + for (int k = len - actualDeleteCount; k > actualStart; k--) { + elms->set(k + itemCount - 1, + GetElementToMove(k + actualDeleteCount - 1, + source_elms, + prototype), + mode); + } + } + + for (int k = actualStart; k < actualStart + itemCount; k++) { + elms->set(k, args[3 + k - actualStart], mode); + } + + // Set the length. + array->set_length(Smi::FromInt(new_length)); + + return result_array; +} + + // ----------------------------------------------------------------------------- // @@ -474,6 +806,76 @@ BUILTIN(HandleApiCallConstruct) { } +#ifdef DEBUG + +static void VerifyTypeCheck(Handle<JSObject> object, + Handle<JSFunction> function) { + FunctionTemplateInfo* info = + FunctionTemplateInfo::cast(function->shared()->function_data()); + if (info->signature()->IsUndefined()) return; + SignatureInfo* signature = SignatureInfo::cast(info->signature()); + Object* receiver_type = signature->receiver(); + if (receiver_type->IsUndefined()) return; + FunctionTemplateInfo* type = FunctionTemplateInfo::cast(receiver_type); + ASSERT(object->IsInstanceOf(type)); +} + +#endif + + +BUILTIN(FastHandleApiCall) { + ASSERT(!CalledAsConstructor()); + const bool is_construct = false; + + // We expect four more arguments: function, callback, call data, and holder. + const int args_length = args.length() - 4; + ASSERT(args_length >= 0); + + Handle<JSFunction> function = args.at<JSFunction>(args_length); + Object* callback_obj = args[args_length + 1]; + Handle<Object> data_handle = args.at<Object>(args_length + 2); + Handle<JSObject> checked_holder = args.at<JSObject>(args_length + 3); + +#ifdef DEBUG + VerifyTypeCheck(checked_holder, function); +#endif + + v8::Local<v8::Object> holder = v8::Utils::ToLocal(checked_holder); + v8::Local<v8::Function> callee = v8::Utils::ToLocal(function); + v8::InvocationCallback callback = + v8::ToCData<v8::InvocationCallback>(callback_obj); + v8::Local<v8::Value> data = v8::Utils::ToLocal(data_handle); + + v8::Arguments new_args = v8::ImplementationUtilities::NewArguments( + data, + holder, + callee, + is_construct, + reinterpret_cast<void**>(&args[0] - 1), + args_length - 1); + + HandleScope scope; + Object* result; + v8::Handle<v8::Value> value; + { + // Leaving JavaScript. + VMState state(EXTERNAL); +#ifdef ENABLE_LOGGING_AND_PROFILING + state.set_external_callback(v8::ToCData<Address>(callback_obj)); +#endif + value = callback(new_args); + } + if (value.IsEmpty()) { + result = Heap::undefined_value(); + } else { + result = *reinterpret_cast<Object**>(*value); + } + + RETURN_IF_SCHEDULED_EXCEPTION(); + return result; +} + + // Helper function to handle calls to non-function objects created through the // API. The object can be called as either a constructor (using new) or just as // a function (without new). @@ -657,6 +1059,10 @@ static void Generate_KeyedLoadIC_PreMonomorphic(MacroAssembler* masm) { KeyedLoadIC::GeneratePreMonomorphic(masm); } +static void Generate_KeyedLoadIC_IndexedInterceptor(MacroAssembler* masm) { + KeyedLoadIC::GenerateIndexedInterceptor(masm); +} + static void Generate_StoreIC_Initialize(MacroAssembler* masm) { StoreIC::GenerateInitialize(masm); @@ -668,10 +1074,6 @@ static void Generate_StoreIC_Miss(MacroAssembler* masm) { } -static void Generate_StoreIC_ExtendStorage(MacroAssembler* masm) { - StoreIC::GenerateExtendStorage(masm); -} - static void Generate_StoreIC_Megamorphic(MacroAssembler* masm) { StoreIC::GenerateMegamorphic(masm); } @@ -720,11 +1122,6 @@ static void Generate_KeyedStoreIC_ExternalFloatArray(MacroAssembler* masm) { } -static void Generate_KeyedStoreIC_ExtendStorage(MacroAssembler* masm) { - KeyedStoreIC::GenerateExtendStorage(masm); -} - - static void Generate_KeyedStoreIC_Miss(MacroAssembler* masm) { KeyedStoreIC::GenerateMiss(masm); } @@ -869,9 +1266,6 @@ void Builtins::Setup(bool create_heap_objects) { v8::internal::V8::FatalProcessOutOfMemory("CreateCode"); } } - // Add any unresolved jumps or calls to the fixup list in the - // bootstrapper. - Bootstrapper::AddFixup(Code::cast(code), &masm); // Log the event and add the code to the builtins array. LOG(CodeCreateEvent(Logger::BUILTIN_TAG, Code::cast(code), functions[i].s_name)); diff --git a/src/builtins.h b/src/builtins.h index 418948f7..13322c23 100644 --- a/src/builtins.h +++ b/src/builtins.h @@ -48,8 +48,13 @@ enum BuiltinExtraArguments { \ V(ArrayPush, NO_EXTRA_ARGUMENTS) \ V(ArrayPop, NO_EXTRA_ARGUMENTS) \ + V(ArrayShift, NO_EXTRA_ARGUMENTS) \ + V(ArrayUnshift, NO_EXTRA_ARGUMENTS) \ + V(ArraySlice, NO_EXTRA_ARGUMENTS) \ + V(ArraySplice, NO_EXTRA_ARGUMENTS) \ \ V(HandleApiCall, NEEDS_CALLED_FUNCTION) \ + V(FastHandleApiCall, NO_EXTRA_ARGUMENTS) \ V(HandleApiCallConstruct, NEEDS_CALLED_FUNCTION) \ V(HandleApiCallAsFunction, NO_EXTRA_ARGUMENTS) \ V(HandleApiCallAsConstructor, NO_EXTRA_ARGUMENTS) @@ -69,9 +74,6 @@ enum BuiltinExtraArguments { V(StoreIC_Miss, BUILTIN, UNINITIALIZED) \ V(KeyedStoreIC_Miss, BUILTIN, UNINITIALIZED) \ \ - V(StoreIC_ExtendStorage, BUILTIN, UNINITIALIZED) \ - V(KeyedStoreIC_ExtendStorage, BUILTIN, UNINITIALIZED) \ - \ V(LoadIC_Initialize, LOAD_IC, UNINITIALIZED) \ V(LoadIC_PreMonomorphic, LOAD_IC, PREMONOMORPHIC) \ V(LoadIC_Normal, LOAD_IC, MONOMORPHIC) \ @@ -91,6 +93,7 @@ enum BuiltinExtraArguments { V(KeyedLoadIC_ExternalIntArray, KEYED_LOAD_IC, MEGAMORPHIC) \ V(KeyedLoadIC_ExternalUnsignedIntArray, KEYED_LOAD_IC, MEGAMORPHIC) \ V(KeyedLoadIC_ExternalFloatArray, KEYED_LOAD_IC, MEGAMORPHIC) \ + V(KeyedLoadIC_IndexedInterceptor, KEYED_LOAD_IC, MEGAMORPHIC) \ \ V(StoreIC_Initialize, STORE_IC, UNINITIALIZED) \ V(StoreIC_Megamorphic, STORE_IC, MEGAMORPHIC) \ diff --git a/src/checks.h b/src/checks.h index 3b0c8513..eeb748b4 100644 --- a/src/checks.h +++ b/src/checks.h @@ -125,7 +125,9 @@ static inline void CheckEqualsHelper(const char* file, const char* expected, const char* value_source, const char* value) { - if (strcmp(expected, value) != 0) { + if ((expected == NULL && value != NULL) || + (expected != NULL && value == NULL) || + (expected != NULL && value != NULL && strcmp(expected, value) != 0)) { V8_Fatal(file, line, "CHECK_EQ(%s, %s) failed\n# Expected: %s\n# Found: %s", expected_source, value_source, expected, value); diff --git a/src/code-stubs.cc b/src/code-stubs.cc index 95f0760a..4d0fd299 100644 --- a/src/code-stubs.cc +++ b/src/code-stubs.cc @@ -31,6 +31,7 @@ #include "code-stubs.h" #include "factory.h" #include "macro-assembler.h" +#include "oprofile-agent.h" namespace v8 { namespace internal { @@ -60,8 +61,12 @@ void CodeStub::GenerateCode(MacroAssembler* masm) { void CodeStub::RecordCodeGeneration(Code* code, MacroAssembler* masm) { code->set_major_key(MajorKey()); - // Add unresolved entries in the code to the fixup list. - Bootstrapper::AddFixup(code, masm); +#ifdef ENABLE_OPROFILE_AGENT + // Register the generated stub with the OPROFILE agent. + OProfileAgent::CreateNativeCodeRegion(GetName(), + code->instruction_start(), + code->instruction_size()); +#endif LOG(CodeCreateEvent(Logger::STUB_TAG, code, GetName())); Counters::total_stubs_code_size.Increment(code->instruction_size()); diff --git a/src/code-stubs.h b/src/code-stubs.h index d502f14c..d5189c27 100644 --- a/src/code-stubs.h +++ b/src/code-stubs.h @@ -48,6 +48,7 @@ namespace internal { V(FastNewClosure) \ V(FastNewContext) \ V(FastCloneShallowArray) \ + V(TranscendentalCache) \ V(GenericUnaryOp) \ V(RevertToNumber) \ V(ToBoolean) \ @@ -55,6 +56,7 @@ namespace internal { V(CounterOp) \ V(ArgumentsAccess) \ V(RegExpExec) \ + V(NumberToString) \ V(CEntry) \ V(JSEntry) \ V(DebuggerStatement) diff --git a/src/codegen.cc b/src/codegen.cc index 24eb4762..bc722bbe 100644 --- a/src/codegen.cc +++ b/src/codegen.cc @@ -31,6 +31,7 @@ #include "codegen-inl.h" #include "compiler.h" #include "debug.h" +#include "liveedit.h" #include "oprofile-agent.h" #include "prettyprinter.h" #include "register-allocator-inl.h" @@ -197,9 +198,6 @@ Handle<Code> CodeGenerator::MakeCodeEpilogue(MacroAssembler* masm, Handle<Code> code = Factory::NewCode(desc, &sinfo, flags, masm->CodeObject()); - // Add unresolved entries in the code to the fixup list. - Bootstrapper::AddFixup(*code, masm); - #ifdef ENABLE_DISASSEMBLER bool print_code = Bootstrapper::IsActive() ? FLAG_print_builtin_code @@ -237,6 +235,7 @@ Handle<Code> CodeGenerator::MakeCodeEpilogue(MacroAssembler* masm, // all the pieces into a Code object. This function is only to be called by // the compiler.cc code. Handle<Code> CodeGenerator::MakeCode(CompilationInfo* info) { + LiveEditFunctionTracker live_edit_tracker(info->function()); Handle<Script> script = info->script(); if (!script->IsUndefined() && !script->source()->IsUndefined()) { int len = String::cast(script->source())->length(); @@ -248,7 +247,8 @@ Handle<Code> CodeGenerator::MakeCode(CompilationInfo* info) { MacroAssembler masm(NULL, kInitialBufferSize); CodeGenerator cgen(&masm); CodeGeneratorScope scope(&cgen); - cgen.Generate(info, PRIMARY); + live_edit_tracker.RecordFunctionScope(info->function()->scope()); + cgen.Generate(info); if (cgen.HasStackOverflow()) { ASSERT(!Top::has_pending_exception()); return Handle<Code>::null(); @@ -256,7 +256,9 @@ Handle<Code> CodeGenerator::MakeCode(CompilationInfo* info) { InLoopFlag in_loop = (cgen.loop_nesting() != 0) ? IN_LOOP : NOT_IN_LOOP; Code::Flags flags = Code::ComputeFlags(Code::FUNCTION, in_loop); - return MakeCodeEpilogue(cgen.masm(), flags, info); + Handle<Code> result = MakeCodeEpilogue(cgen.masm(), flags, info); + live_edit_tracker.RecordFunctionCode(result); + return result; } @@ -358,6 +360,7 @@ CodeGenerator::InlineRuntimeLUT CodeGenerator::kInlineRuntimeLUT[] = { {&CodeGenerator::GenerateIsSmi, "_IsSmi"}, {&CodeGenerator::GenerateIsNonNegativeSmi, "_IsNonNegativeSmi"}, {&CodeGenerator::GenerateIsArray, "_IsArray"}, + {&CodeGenerator::GenerateIsRegExp, "_IsRegExp"}, {&CodeGenerator::GenerateIsConstructCall, "_IsConstructCall"}, {&CodeGenerator::GenerateArgumentsLength, "_ArgumentsLength"}, {&CodeGenerator::GenerateArgumentsAccess, "_Arguments"}, @@ -375,6 +378,9 @@ CodeGenerator::InlineRuntimeLUT CodeGenerator::kInlineRuntimeLUT[] = { {&CodeGenerator::GenerateSubString, "_SubString"}, {&CodeGenerator::GenerateStringCompare, "_StringCompare"}, {&CodeGenerator::GenerateRegExpExec, "_RegExpExec"}, + {&CodeGenerator::GenerateNumberToString, "_NumberToString"}, + {&CodeGenerator::GenerateMathSin, "_Math_sin"}, + {&CodeGenerator::GenerateMathCos, "_Math_cos"}, }; @@ -525,12 +531,5 @@ void ApiGetterEntryStub::SetCustomCache(Code* value) { info()->set_load_stub_cache(value); } -#ifdef ENABLE_DEBUGGER_SUPPORT -void DebuggerStatementStub::Generate(MacroAssembler* masm) { - Runtime::Function* f = Runtime::FunctionForId(Runtime::kDebugBreak); - masm->TailCallRuntime(ExternalReference(f), 0, f->result_size); -} -#endif - } } // namespace v8::internal diff --git a/src/codegen.h b/src/codegen.h index 3afa0414..8dcde84b 100644 --- a/src/codegen.h +++ b/src/codegen.h @@ -31,6 +31,7 @@ #include "ast.h" #include "code-stubs.h" #include "runtime.h" +#include "number-info.h" // Include the declaration of the architecture defined class CodeGenerator. // The contract to the shared code is that the the CodeGenerator is a subclass @@ -415,21 +416,6 @@ class ApiGetterEntryStub : public CodeStub { }; -// Mark the debugger statement to be recognized by debugger (by the MajorKey) -class DebuggerStatementStub : public CodeStub { - public: - DebuggerStatementStub() { } - - void Generate(MacroAssembler* masm); - - private: - Major MajorKey() { return DebuggerStatement; } - int MinorKey() { return 0; } - - const char* GetName() { return "DebuggerStatementStub"; } -}; - - class JSEntryStub : public CodeStub { public: JSEntryStub() { } @@ -532,14 +518,14 @@ class CallFunctionStub: public CodeStub { } #endif - // Minor key encoding in 31 bits AAAAAAAAAAAAAAAAAAAAAFI A(rgs)F(lag)I(nloop). + // Minor key encoding in 32 bits with Bitfield <Type, shift, size>. class InLoopBits: public BitField<InLoopFlag, 0, 1> {}; class FlagBits: public BitField<CallFunctionFlags, 1, 1> {}; - class ArgcBits: public BitField<int, 2, 29> {}; + class ArgcBits: public BitField<int, 2, 32 - 2> {}; Major MajorKey() { return CallFunction; } int MinorKey() { - // Encode the parameters in a unique 31 bit value. + // Encode the parameters in a unique 32 bit value. return InLoopBits::encode(in_loop_) | FlagBits::encode(flags_) | ArgcBits::encode(argc_); diff --git a/src/compiler.cc b/src/compiler.cc index 6556d374..7b6734a3 100755 --- a/src/compiler.cc +++ b/src/compiler.cc @@ -38,6 +38,7 @@ #include "rewriter.h" #include "scopes.h" #include "usage-analyzer.h" +#include "liveedit.h" namespace v8 { namespace internal { @@ -238,6 +239,7 @@ Handle<JSFunction> Compiler::Compile(Handle<String> source, int line_offset, int column_offset, v8::Extension* extension, ScriptDataImpl* input_pre_data, + Handle<Object> script_data, NativesFlag natives) { int source_length = source->length(); Counters::total_load_size.Increment(source_length); @@ -275,6 +277,9 @@ Handle<JSFunction> Compiler::Compile(Handle<String> source, script->set_column_offset(Smi::FromInt(column_offset)); } + script->set_data(script_data.is_null() ? Heap::undefined_value() + : *script_data); + // Compile the function and add it to the cache. result = MakeFunction(true, false, @@ -429,7 +434,8 @@ Handle<JSFunction> Compiler::BuildBoilerplate(FunctionLiteral* literal, // compiled. These builtins cannot be handled lazily by the parser, // since we have to know if a function uses the special natives // syntax, which is something the parser records. - bool allow_lazy = literal->AllowsLazyCompilation(); + bool allow_lazy = literal->AllowsLazyCompilation() && + !LiveEditFunctionTracker::IsActive(); // Generate code Handle<Code> code; diff --git a/src/compiler.h b/src/compiler.h index 88f4479a..f972ac9b 100644 --- a/src/compiler.h +++ b/src/compiler.h @@ -28,8 +28,10 @@ #ifndef V8_COMPILER_H_ #define V8_COMPILER_H_ +#include "ast.h" #include "frame-element.h" #include "parser.h" +#include "register-allocator.h" #include "zone.h" namespace v8 { @@ -39,6 +41,37 @@ namespace internal { // is constructed based on the resources available at compile-time. class CompilationInfo BASE_EMBEDDED { public: + // Compilation mode. Either the compiler is used as the primary + // compiler and needs to setup everything or the compiler is used as + // the secondary compiler for split compilation and has to handle + // bailouts. + enum Mode { + PRIMARY, + SECONDARY + }; + + // A description of the compilation state at a bailout to the secondary + // code generator. + // + // The state is currently simple: there are no parameters or local + // variables to worry about ('this' can be found in the stack frame). + // There are at most two live values. + // + // There is a label that should be bound to the beginning of the bailout + // stub code. + class Bailout : public ZoneObject { + public: + Bailout(Register left, Register right) : left_(left), right_(right) {} + + Label* label() { return &label_; } + + private: + Register left_; + Register right_; + Label label_; + }; + + // Lazy compilation of a JSFunction. CompilationInfo(Handle<JSFunction> closure, int loop_nesting, @@ -115,9 +148,13 @@ class CompilationInfo BASE_EMBEDDED { int loop_nesting() { return loop_nesting_; } bool has_receiver() { return !receiver_.is_null(); } Handle<Object> receiver() { return receiver_; } + List<Bailout*>* bailouts() { return &bailouts_; } - // Accessors for mutable fields, possibly set by analysis passes with + // Accessors for mutable fields (possibly set by analysis passes) with // default values given by Initialize. + Mode mode() { return mode_; } + void set_mode(Mode mode) { mode_ = mode; } + bool has_this_properties() { return has_this_properties_; } void set_has_this_properties(bool flag) { has_this_properties_ = flag; } @@ -135,8 +172,19 @@ class CompilationInfo BASE_EMBEDDED { // Derived accessors. Scope* scope() { return function()->scope(); } + // Add a bailout with two live values. + Label* AddBailout(Register left, Register right) { + Bailout* bailout = new Bailout(left, right); + bailouts_.Add(bailout); + return bailout->label(); + } + + // Add a bailout with no live values. + Label* AddBailout() { return AddBailout(no_reg, no_reg); } + private: void Initialize() { + mode_ = PRIMARY; has_this_properties_ = false; has_globals_ = false; } @@ -146,6 +194,7 @@ class CompilationInfo BASE_EMBEDDED { Handle<Script> script_; FunctionLiteral* function_; + Mode mode_; bool is_eval_; int loop_nesting_; @@ -155,6 +204,10 @@ class CompilationInfo BASE_EMBEDDED { bool has_this_properties_; bool has_globals_; + // An ordered list of bailout points encountered during fast-path + // compilation. + List<Bailout*> bailouts_; + DISALLOW_COPY_AND_ASSIGN(CompilationInfo); }; @@ -183,7 +236,8 @@ class Compiler : public AllStatic { Handle<Object> script_name, int line_offset, int column_offset, v8::Extension* extension, - ScriptDataImpl* script_Data, + ScriptDataImpl* pre_data, + Handle<Object> script_data, NativesFlag is_natives_code); // Compile a String source within a context for Eval. diff --git a/src/contexts.h b/src/contexts.h index 3feb6ce4..2453db7f 100644 --- a/src/contexts.h +++ b/src/contexts.h @@ -76,7 +76,6 @@ enum ContextLookupFlags { V(TO_INTEGER_FUN_INDEX, JSFunction, to_integer_fun) \ V(TO_UINT32_FUN_INDEX, JSFunction, to_uint32_fun) \ V(TO_INT32_FUN_INDEX, JSFunction, to_int32_fun) \ - V(TO_BOOLEAN_FUN_INDEX, JSFunction, to_boolean_fun) \ V(GLOBAL_EVAL_FUN_INDEX, JSFunction, global_eval_fun) \ V(INSTANTIATE_FUN_INDEX, JSFunction, instantiate_fun) \ V(CONFIGURE_INSTANCE_FUN_INDEX, JSFunction, configure_instance_fun) \ diff --git a/src/d8-readline.cc b/src/d8-readline.cc index 34b7b60d..67fc9eff 100644 --- a/src/d8-readline.cc +++ b/src/d8-readline.cc @@ -27,8 +27,8 @@ #include <cstdio> // NOLINT -#include <readline/readline.h> -#include <readline/history.h> +#include <readline/readline.h> // NOLINT +#include <readline/history.h> // NOLINT #include "d8.h" diff --git a/src/data-flow.cc b/src/data-flow.cc index 22ec66fd..5e9d217d 100644 --- a/src/data-flow.cc +++ b/src/data-flow.cc @@ -199,15 +199,11 @@ void AstLabeler::VisitCatchExtensionObject( void AstLabeler::VisitAssignment(Assignment* expr) { Property* prop = expr->target()->AsProperty(); ASSERT(prop != NULL); - if (prop != NULL) { - ASSERT(prop->key()->IsPropertyName()); - VariableProxy* proxy = prop->obj()->AsVariableProxy(); - if (proxy != NULL && proxy->var()->is_this()) { - info()->set_has_this_properties(true); - } else { - Visit(prop->obj()); - } - } + ASSERT(prop->key()->IsPropertyName()); + VariableProxy* proxy = prop->obj()->AsVariableProxy(); + USE(proxy); + ASSERT(proxy != NULL && proxy->var()->is_this()); + info()->set_has_this_properties(true); Visit(expr->value()); expr->set_num(next_number_++); } @@ -219,7 +215,12 @@ void AstLabeler::VisitThrow(Throw* expr) { void AstLabeler::VisitProperty(Property* expr) { - UNREACHABLE(); + ASSERT(expr->key()->IsPropertyName()); + VariableProxy* proxy = expr->obj()->AsVariableProxy(); + USE(proxy); + ASSERT(proxy != NULL && proxy->var()->is_this()); + info()->set_has_this_properties(true); + expr->set_num(next_number_++); } @@ -269,4 +270,292 @@ void AstLabeler::VisitDeclaration(Declaration* decl) { UNREACHABLE(); } + +ZoneList<Expression*>* VarUseMap::Lookup(Variable* var) { + HashMap::Entry* entry = HashMap::Lookup(var, var->name()->Hash(), true); + if (entry->value == NULL) { + entry->value = new ZoneList<Expression*>(1); + } + return reinterpret_cast<ZoneList<Expression*>*>(entry->value); +} + + +void LivenessAnalyzer::Analyze(FunctionLiteral* fun) { + // Process the function body. + VisitStatements(fun->body()); + + // All variables are implicitly defined at the function start. + // Record a definition of all variables live at function entry. + for (HashMap::Entry* p = live_vars_.Start(); + p != NULL; + p = live_vars_.Next(p)) { + Variable* var = reinterpret_cast<Variable*>(p->key); + RecordDef(var, fun); + } +} + + +void LivenessAnalyzer::VisitStatements(ZoneList<Statement*>* stmts) { + // Visit statements right-to-left. + for (int i = stmts->length() - 1; i >= 0; i--) { + Visit(stmts->at(i)); + } +} + + +void LivenessAnalyzer::RecordUse(Variable* var, Expression* expr) { + ASSERT(var->is_global() || var->is_this()); + ZoneList<Expression*>* uses = live_vars_.Lookup(var); + uses->Add(expr); +} + + +void LivenessAnalyzer::RecordDef(Variable* var, Expression* expr) { + ASSERT(var->is_global() || var->is_this()); + + // We do not support other expressions that can define variables. + ASSERT(expr->AsFunctionLiteral() != NULL); + + // Add the variable to the list of defined variables. + if (expr->defined_vars() == NULL) { + expr->set_defined_vars(new ZoneList<DefinitionInfo*>(1)); + } + DefinitionInfo* def = new DefinitionInfo(); + expr->AsFunctionLiteral()->defined_vars()->Add(def); + + // Compute the last use of the definition. The variable uses are + // inserted in reversed evaluation order. The first element + // in the list of live uses is the last use. + ZoneList<Expression*>* uses = live_vars_.Lookup(var); + while (uses->length() > 0) { + Expression* use_site = uses->RemoveLast(); + use_site->set_var_def(def); + if (uses->length() == 0) { + def->set_last_use(use_site); + } + } +} + + +// Visitor functions for live variable analysis. +void LivenessAnalyzer::VisitDeclaration(Declaration* decl) { + UNREACHABLE(); +} + + +void LivenessAnalyzer::VisitBlock(Block* stmt) { + VisitStatements(stmt->statements()); +} + + +void LivenessAnalyzer::VisitExpressionStatement( + ExpressionStatement* stmt) { + Visit(stmt->expression()); +} + + +void LivenessAnalyzer::VisitEmptyStatement(EmptyStatement* stmt) { + // Do nothing. +} + + +void LivenessAnalyzer::VisitIfStatement(IfStatement* stmt) { + UNREACHABLE(); +} + + +void LivenessAnalyzer::VisitContinueStatement(ContinueStatement* stmt) { + UNREACHABLE(); +} + + +void LivenessAnalyzer::VisitBreakStatement(BreakStatement* stmt) { + UNREACHABLE(); +} + + +void LivenessAnalyzer::VisitReturnStatement(ReturnStatement* stmt) { + UNREACHABLE(); +} + + +void LivenessAnalyzer::VisitWithEnterStatement( + WithEnterStatement* stmt) { + UNREACHABLE(); +} + + +void LivenessAnalyzer::VisitWithExitStatement(WithExitStatement* stmt) { + UNREACHABLE(); +} + + +void LivenessAnalyzer::VisitSwitchStatement(SwitchStatement* stmt) { + UNREACHABLE(); +} + + +void LivenessAnalyzer::VisitDoWhileStatement(DoWhileStatement* stmt) { + UNREACHABLE(); +} + + +void LivenessAnalyzer::VisitWhileStatement(WhileStatement* stmt) { + UNREACHABLE(); +} + + +void LivenessAnalyzer::VisitForStatement(ForStatement* stmt) { + UNREACHABLE(); +} + + +void LivenessAnalyzer::VisitForInStatement(ForInStatement* stmt) { + UNREACHABLE(); +} + + +void LivenessAnalyzer::VisitTryCatchStatement(TryCatchStatement* stmt) { + UNREACHABLE(); +} + + +void LivenessAnalyzer::VisitTryFinallyStatement( + TryFinallyStatement* stmt) { + UNREACHABLE(); +} + + +void LivenessAnalyzer::VisitDebuggerStatement( + DebuggerStatement* stmt) { + UNREACHABLE(); +} + + +void LivenessAnalyzer::VisitFunctionLiteral(FunctionLiteral* expr) { + UNREACHABLE(); +} + + +void LivenessAnalyzer::VisitFunctionBoilerplateLiteral( + FunctionBoilerplateLiteral* expr) { + UNREACHABLE(); +} + + +void LivenessAnalyzer::VisitConditional(Conditional* expr) { + UNREACHABLE(); +} + + +void LivenessAnalyzer::VisitSlot(Slot* expr) { + UNREACHABLE(); +} + + +void LivenessAnalyzer::VisitVariableProxy(VariableProxy* expr) { + Variable* var = expr->var(); + ASSERT(var->is_global()); + ASSERT(!var->is_this()); + RecordUse(var, expr); +} + + +void LivenessAnalyzer::VisitLiteral(Literal* expr) { + UNREACHABLE(); +} + + +void LivenessAnalyzer::VisitRegExpLiteral(RegExpLiteral* expr) { + UNREACHABLE(); +} + + +void LivenessAnalyzer::VisitObjectLiteral(ObjectLiteral* expr) { + UNREACHABLE(); +} + + +void LivenessAnalyzer::VisitArrayLiteral(ArrayLiteral* expr) { + UNREACHABLE(); +} + + +void LivenessAnalyzer::VisitCatchExtensionObject( + CatchExtensionObject* expr) { + UNREACHABLE(); +} + + +void LivenessAnalyzer::VisitAssignment(Assignment* expr) { + Property* prop = expr->target()->AsProperty(); + ASSERT(prop != NULL); + ASSERT(prop->key()->IsPropertyName()); + VariableProxy* proxy = prop->obj()->AsVariableProxy(); + ASSERT(proxy != NULL && proxy->var()->is_this()); + + // Record use of this at the assignment node. Assignments to + // this-properties are treated like unary operations. + RecordUse(proxy->var(), expr); + + // Visit right-hand side. + Visit(expr->value()); +} + + +void LivenessAnalyzer::VisitThrow(Throw* expr) { + UNREACHABLE(); +} + + +void LivenessAnalyzer::VisitProperty(Property* expr) { + ASSERT(expr->key()->IsPropertyName()); + VariableProxy* proxy = expr->obj()->AsVariableProxy(); + ASSERT(proxy != NULL && proxy->var()->is_this()); + RecordUse(proxy->var(), expr); +} + + +void LivenessAnalyzer::VisitCall(Call* expr) { + UNREACHABLE(); +} + + +void LivenessAnalyzer::VisitCallNew(CallNew* expr) { + UNREACHABLE(); +} + + +void LivenessAnalyzer::VisitCallRuntime(CallRuntime* expr) { + UNREACHABLE(); +} + + +void LivenessAnalyzer::VisitUnaryOperation(UnaryOperation* expr) { + UNREACHABLE(); +} + + +void LivenessAnalyzer::VisitCountOperation(CountOperation* expr) { + UNREACHABLE(); +} + + +void LivenessAnalyzer::VisitBinaryOperation(BinaryOperation* expr) { + // Visit child nodes in reverse evaluation order. + Visit(expr->right()); + Visit(expr->left()); +} + + +void LivenessAnalyzer::VisitCompareOperation(CompareOperation* expr) { + UNREACHABLE(); +} + + +void LivenessAnalyzer::VisitThisFunction(ThisFunction* expr) { + UNREACHABLE(); +} + + } } // namespace v8::internal diff --git a/src/data-flow.h b/src/data-flow.h index 7c16d5dc..23319444 100644 --- a/src/data-flow.h +++ b/src/data-flow.h @@ -62,6 +62,56 @@ class AstLabeler: public AstVisitor { }; +class VarUseMap : public HashMap { + public: + VarUseMap() : HashMap(VarMatch) {} + + ZoneList<Expression*>* Lookup(Variable* var); + + private: + static bool VarMatch(void* key1, void* key2) { return key1 == key2; } +}; + + +class DefinitionInfo : public ZoneObject { + public: + explicit DefinitionInfo() : last_use_(NULL) {} + + Expression* last_use() { return last_use_; } + void set_last_use(Expression* expr) { last_use_ = expr; } + + private: + Expression* last_use_; + Register location_; +}; + + +class LivenessAnalyzer : public AstVisitor { + public: + LivenessAnalyzer() {} + + void Analyze(FunctionLiteral* fun); + + private: + void VisitStatements(ZoneList<Statement*>* stmts); + + void RecordUse(Variable* var, Expression* expr); + void RecordDef(Variable* var, Expression* expr); + + + // AST node visit functions. +#define DECLARE_VISIT(type) virtual void Visit##type(type* node); + AST_NODE_LIST(DECLARE_VISIT) +#undef DECLARE_VISIT + + // Map for tracking the live variables. + VarUseMap live_vars_; + + DISALLOW_COPY_AND_ASSIGN(LivenessAnalyzer); +}; + + } } // namespace v8::internal + #endif // V8_DATAFLOW_H_ diff --git a/src/date-delay.js b/src/date-delay.js deleted file mode 100644 index 7d8f4588..00000000 --- a/src/date-delay.js +++ /dev/null @@ -1,1138 +0,0 @@ -// 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. - - -// This file relies on the fact that the following declarations have been made -// in v8natives.js: -// const $isFinite = GlobalIsFinite; - -// ------------------------------------------------------------------- - -// This file contains date support implemented in JavaScript. - - -// Keep reference to original values of some global properties. This -// has the added benefit that the code in this file is isolated from -// changes to these properties. -const $Date = global.Date; - -// Helper function to throw error. -function ThrowDateTypeError() { - throw new $TypeError('this is not a Date object.'); -} - -// ECMA 262 - 5.2 -function Modulo(value, remainder) { - var mod = value % remainder; - // Guard against returning -0. - if (mod == 0) return 0; - return mod >= 0 ? mod : mod + remainder; -} - - -function TimeWithinDay(time) { - return Modulo(time, msPerDay); -} - - -// ECMA 262 - 15.9.1.3 -function DaysInYear(year) { - if (year % 4 != 0) return 365; - if ((year % 100 == 0) && (year % 400 != 0)) return 365; - return 366; -} - - -function DayFromYear(year) { - return 365 * (year-1970) - + FLOOR((year-1969)/4) - - FLOOR((year-1901)/100) - + FLOOR((year-1601)/400); -} - - -function TimeFromYear(year) { - return msPerDay * DayFromYear(year); -} - - -function InLeapYear(time) { - return DaysInYear(YEAR_FROM_TIME(time)) == 366 ? 1 : 0; -} - - -function DayWithinYear(time) { - return DAY(time) - DayFromYear(YEAR_FROM_TIME(time)); -} - - -// ECMA 262 - 15.9.1.9 -function EquivalentYear(year) { - // Returns an equivalent year in the range [2008-2035] matching - // - leap year. - // - week day of first day. - var time = TimeFromYear(year); - var recent_year = (InLeapYear(time) == 0 ? 1967 : 1956) + - (WeekDay(time) * 12) % 28; - // Find the year in the range 2008..2037 that is equivalent mod 28. - // Add 3*28 to give a positive argument to the modulus operator. - return 2008 + (recent_year + 3*28 - 2008) % 28; -} - - -function EquivalentTime(t) { - // The issue here is that some library calls don't work right for dates - // that cannot be represented using a non-negative signed 32 bit integer - // (measured in whole seconds based on the 1970 epoch). - // We solve this by mapping the time to a year with same leap-year-ness - // and same starting day for the year. The ECMAscript specification says - // we must do this, but for compatibility with other browsers, we use - // the actual year if it is in the range 1970..2037 - if (t >= 0 && t <= 2.1e12) return t; - var day = MakeDay(EquivalentYear(YEAR_FROM_TIME(t)), MONTH_FROM_TIME(t), DATE_FROM_TIME(t)); - return TimeClip(MakeDate(day, TimeWithinDay(t))); -} - - -// Because computing the DST offset is a pretty expensive operation -// we keep a cache of last computed offset along with a time interval -// where we know the cache is valid. -var DST_offset_cache = { - // Cached DST offset. - offset: 0, - // Time interval where the cached offset is valid. - start: 0, end: -1, - // Size of next interval expansion. - increment: 0 -}; - - -// 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. -function DaylightSavingsOffset(t) { - // Load the cache object from the builtins object. - var cache = DST_offset_cache; - - // Cache the start and the end in local variables for fast access. - var start = cache.start; - var end = cache.end; - - if (start <= t) { - // If the time fits in the cached interval, return the cached offset. - if (t <= end) return cache.offset; - - // Compute a possible new interval end. - var new_end = end + cache.increment; - - if (t <= new_end) { - var end_offset = %DateDaylightSavingsOffset(EquivalentTime(new_end)); - if (cache.offset == end_offset) { - // 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 = new_end; - cache.increment = msPerMonth; - return end_offset; - } else { - var offset = %DateDaylightSavingsOffset(EquivalentTime(t)); - if (offset == end_offset) { - // 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 = t; - cache.end = new_end; - 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 = t; - } - // Update the offset in the cache and return it. - cache.offset = offset; - return offset; - } - } - } - - // 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. - var offset = %DateDaylightSavingsOffset(EquivalentTime(t)); - cache.offset = offset; - cache.start = cache.end = t; - cache.increment = msPerMonth; - return offset; -} - - -var timezone_cache_time = $NaN; -var timezone_cache_timezone; - -function LocalTimezone(t) { - if (NUMBER_IS_NAN(t)) return ""; - if (t == timezone_cache_time) { - return timezone_cache_timezone; - } - var timezone = %DateLocalTimezone(EquivalentTime(t)); - timezone_cache_time = t; - timezone_cache_timezone = timezone; - return timezone; -} - - -function WeekDay(time) { - return Modulo(DAY(time) + 4, 7); -} - -var local_time_offset = %DateLocalTimeOffset(); - -function LocalTime(time) { - if (NUMBER_IS_NAN(time)) return time; - return time + local_time_offset + DaylightSavingsOffset(time); -} - -function LocalTimeNoCheck(time) { - // Inline the DST offset cache checks for speed. - var cache = DST_offset_cache; - if (cache.start <= time && time <= cache.end) { - var dst_offset = cache.offset; - } else { - var dst_offset = DaylightSavingsOffset(time); - } - return time + local_time_offset + dst_offset; -} - - -function UTC(time) { - if (NUMBER_IS_NAN(time)) return time; - var tmp = time - local_time_offset; - return tmp - DaylightSavingsOffset(tmp); -} - - -// ECMA 262 - 15.9.1.11 -function MakeTime(hour, min, sec, ms) { - if (!$isFinite(hour)) return $NaN; - if (!$isFinite(min)) return $NaN; - if (!$isFinite(sec)) return $NaN; - if (!$isFinite(ms)) return $NaN; - return TO_INTEGER(hour) * msPerHour - + TO_INTEGER(min) * msPerMinute - + TO_INTEGER(sec) * msPerSecond - + TO_INTEGER(ms); -} - - -// ECMA 262 - 15.9.1.12 -function TimeInYear(year) { - return DaysInYear(year) * msPerDay; -} - - -// Compute modified Julian day from year, month, date. -function ToJulianDay(year, month, date) { - var jy = (month > 1) ? year : year - 1; - var jm = (month > 1) ? month + 2 : month + 14; - var ja = FLOOR(jy / 100); - return FLOOR(FLOOR(365.25*jy) + FLOOR(30.6001*jm) + date + 1720995) + 2 - ja + FLOOR(0.25*ja); -} - -var four_year_cycle_table = CalculateDateTable(); - - -function CalculateDateTable() { - var month_lengths = [31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31]; - var four_year_cycle_table = new $Array(1461); - - var cumulative = 0; - var position = 0; - var leap_position = 0; - for (var month = 0; month < 12; month++) { - var month_bits = month << kMonthShift; - var length = month_lengths[month]; - for (var day = 1; day <= length; day++) { - four_year_cycle_table[leap_position] = - month_bits + day; - four_year_cycle_table[366 + position] = - (1 << kYearShift) + month_bits + day; - four_year_cycle_table[731 + position] = - (2 << kYearShift) + month_bits + day; - four_year_cycle_table[1096 + position] = - (3 << kYearShift) + month_bits + day; - leap_position++; - position++; - } - if (month == 1) { - four_year_cycle_table[leap_position++] = month_bits + 29; - } - } - return four_year_cycle_table; -} - - -// Constructor for creating objects holding year, month, and date. -// Introduced to ensure the two return points in FromJulianDay match same map. -function DayTriplet(year, month, date) { - this.year = year; - this.month = month; - this.date = date; -} - -var julian_day_cache_triplet; -var julian_day_cache_day = $NaN; - -// Compute year, month, and day from modified Julian day. -// The missing days in 1582 are ignored for JavaScript compatibility. -function FromJulianDay(julian) { - if (julian_day_cache_day == julian) { - return julian_day_cache_triplet; - } - var result; - // Avoid floating point and non-Smi maths in common case. This is also a period of - // time where leap years are very regular. The range is not too large to avoid overflow - // when doing the multiply-to-divide trick. - if (julian > kDayZeroInJulianDay && - (julian - kDayZeroInJulianDay) < 40177) { // 1970 - 2080 - var jsimple = (julian - kDayZeroInJulianDay) + 731; // Day 0 is 1st January 1968 - var y = 1968; - // Divide by 1461 by multiplying with 22967 and shifting down by 25! - var after_1968 = (jsimple * 22967) >> 25; - y += after_1968 << 2; - jsimple -= 1461 * after_1968; - var four_year_cycle = four_year_cycle_table[jsimple]; - result = new DayTriplet(y + (four_year_cycle >> kYearShift), - (four_year_cycle & kMonthMask) >> kMonthShift, - four_year_cycle & kDayMask); - } else { - var jalpha = FLOOR((julian - 1867216.25) / 36524.25); - var jb = julian + 1 + jalpha - FLOOR(0.25 * jalpha) + 1524; - var jc = FLOOR(6680.0 + ((jb-2439870) - 122.1)/365.25); - var jd = FLOOR(365 * jc + (0.25 * jc)); - var je = FLOOR((jb - jd)/30.6001); - var m = je - 1; - if (m > 12) m -= 13; - var y = jc - 4715; - if (m > 2) { --y; --m; } - var d = jb - jd - FLOOR(30.6001 * je); - result = new DayTriplet(y, m, d); - } - julian_day_cache_day = julian; - julian_day_cache_triplet = result; - return result; -} - - -// Compute number of days given a year, month, date. -// Note that month and date can lie outside the normal range. -// For example: -// MakeDay(2007, -4, 20) --> MakeDay(2006, 8, 20) -// MakeDay(2007, -33, 1) --> MakeDay(2004, 3, 1) -// MakeDay(2007, 14, -50) --> MakeDay(2007, 8, 11) -function MakeDay(year, month, date) { - if (!$isFinite(year) || !$isFinite(month) || !$isFinite(date)) return $NaN; - - // Conversion to integers. - year = TO_INTEGER(year); - month = TO_INTEGER(month); - date = TO_INTEGER(date); - - // Overflow months into year. - year = year + FLOOR(month/12); - month = month % 12; - if (month < 0) { - month += 12; - } - - // Return days relative to Jan 1 1970. - return ToJulianDay(year, month, date) - kDayZeroInJulianDay; -} - - -// ECMA 262 - 15.9.1.13 -function MakeDate(day, time) { - if (!$isFinite(day)) return $NaN; - if (!$isFinite(time)) return $NaN; - return day * msPerDay + time; -} - - -// ECMA 262 - 15.9.1.14 -function TimeClip(time) { - if (!$isFinite(time)) return $NaN; - if ($abs(time) > 8.64E15) return $NaN; - return TO_INTEGER(time); -} - - -// The Date cache is used to limit the cost of parsing the same Date -// strings over and over again. -var Date_cache = { - // Cached time value. - time: $NaN, - // Cached year when interpreting the time as a local time. Only - // valid when the time matches cached time. - year: $NaN, - // String input for which the cached time is valid. - string: null -}; - - -%SetCode($Date, function(year, month, date, hours, minutes, seconds, ms) { - if (!%_IsConstructCall()) { - // ECMA 262 - 15.9.2 - return (new $Date()).toString(); - } - - // ECMA 262 - 15.9.3 - var argc = %_ArgumentsLength(); - var value; - if (argc == 0) { - value = %DateCurrentTime(); - - } else if (argc == 1) { - if (IS_NUMBER(year)) { - value = TimeClip(year); - - } else if (IS_STRING(year)) { - // Probe the Date cache. If we already have a time value for the - // given time, we re-use that instead of parsing the string again. - var cache = Date_cache; - if (cache.string === year) { - value = cache.time; - } else { - value = DateParse(year); - if (!NUMBER_IS_NAN(value)) { - cache.time = value; - cache.year = YEAR_FROM_TIME(LocalTimeNoCheck(value)); - cache.string = year; - } - } - - } else { - // According to ECMA 262, no hint should be given for this - // conversion. However, ToPrimitive defaults to STRING_HINT for - // Date objects which will lose precision when the Date - // constructor is called with another Date object as its - // argument. We therefore use NUMBER_HINT for the conversion, - // which is the default for everything else than Date objects. - // This makes us behave like KJS and SpiderMonkey. - var time = ToPrimitive(year, NUMBER_HINT); - value = IS_STRING(time) ? DateParse(time) : TimeClip(ToNumber(time)); - } - - } else { - year = ToNumber(year); - month = ToNumber(month); - date = argc > 2 ? ToNumber(date) : 1; - hours = argc > 3 ? ToNumber(hours) : 0; - minutes = argc > 4 ? ToNumber(minutes) : 0; - seconds = argc > 5 ? ToNumber(seconds) : 0; - ms = argc > 6 ? ToNumber(ms) : 0; - year = (!NUMBER_IS_NAN(year) && 0 <= TO_INTEGER(year) && TO_INTEGER(year) <= 99) - ? 1900 + TO_INTEGER(year) : year; - var day = MakeDay(year, month, date); - var time = MakeTime(hours, minutes, seconds, ms); - value = TimeClip(UTC(MakeDate(day, time))); - } - %_SetValueOf(this, value); -}); - - -// Helper functions. -function GetTimeFrom(aDate) { - return DATE_VALUE(aDate); -} - -function GetMillisecondsFrom(aDate) { - var t = DATE_VALUE(aDate); - if (NUMBER_IS_NAN(t)) return t; - return MS_FROM_TIME(LocalTimeNoCheck(t)); -} - - -function GetUTCMillisecondsFrom(aDate) { - var t = DATE_VALUE(aDate); - if (NUMBER_IS_NAN(t)) return t; - return MS_FROM_TIME(t); -} - - -function GetSecondsFrom(aDate) { - var t = DATE_VALUE(aDate); - if (NUMBER_IS_NAN(t)) return t; - return SEC_FROM_TIME(LocalTimeNoCheck(t)); -} - - -function GetUTCSecondsFrom(aDate) { - var t = DATE_VALUE(aDate); - if (NUMBER_IS_NAN(t)) return t; - return SEC_FROM_TIME(t); -} - - -function GetMinutesFrom(aDate) { - var t = DATE_VALUE(aDate); - if (NUMBER_IS_NAN(t)) return t; - return MIN_FROM_TIME(LocalTimeNoCheck(t)); -} - - -function GetUTCMinutesFrom(aDate) { - var t = DATE_VALUE(aDate); - if (NUMBER_IS_NAN(t)) return t; - return MIN_FROM_TIME(t); -} - - -function GetHoursFrom(aDate) { - var t = DATE_VALUE(aDate); - if (NUMBER_IS_NAN(t)) return t; - return HOUR_FROM_TIME(LocalTimeNoCheck(t)); -} - - -function GetUTCHoursFrom(aDate) { - var t = DATE_VALUE(aDate); - if (NUMBER_IS_NAN(t)) return t; - return HOUR_FROM_TIME(t); -} - - -function GetFullYearFrom(aDate) { - var t = DATE_VALUE(aDate); - if (NUMBER_IS_NAN(t)) return t; - var cache = Date_cache; - if (cache.time === t) return cache.year; - return YEAR_FROM_TIME(LocalTimeNoCheck(t)); -} - - -function GetUTCFullYearFrom(aDate) { - var t = DATE_VALUE(aDate); - if (NUMBER_IS_NAN(t)) return t; - return YEAR_FROM_TIME(t); -} - - -function GetMonthFrom(aDate) { - var t = DATE_VALUE(aDate); - if (NUMBER_IS_NAN(t)) return t; - return MONTH_FROM_TIME(LocalTimeNoCheck(t)); -} - - -function GetUTCMonthFrom(aDate) { - var t = DATE_VALUE(aDate); - if (NUMBER_IS_NAN(t)) return t; - return MONTH_FROM_TIME(t); -} - - -function GetDateFrom(aDate) { - var t = DATE_VALUE(aDate); - if (NUMBER_IS_NAN(t)) return t; - return DATE_FROM_TIME(LocalTimeNoCheck(t)); -} - - -function GetUTCDateFrom(aDate) { - var t = DATE_VALUE(aDate); - if (NUMBER_IS_NAN(t)) return t; - return DATE_FROM_TIME(t); -} - - -%FunctionSetPrototype($Date, new $Date($NaN)); - - -var WeekDays = ['Sun', 'Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat']; -var Months = ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec']; - - -function TwoDigitString(value) { - return value < 10 ? "0" + value : "" + value; -} - - -function DateString(time) { - var YMD = FromJulianDay(DAY(time) + kDayZeroInJulianDay); - return WeekDays[WeekDay(time)] + ' ' - + Months[YMD.month] + ' ' - + TwoDigitString(YMD.date) + ' ' - + YMD.year; -} - - -var LongWeekDays = ['Sunday', 'Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday', 'Saturday']; -var LongMonths = ['January', 'February', 'March', 'April', 'May', 'June', 'July', 'August', 'September', 'October', 'November', 'December']; - - -function LongDateString(time) { - var YMD = FromJulianDay(DAY(time) + kDayZeroInJulianDay); - return LongWeekDays[WeekDay(time)] + ', ' - + LongMonths[YMD.month] + ' ' - + TwoDigitString(YMD.date) + ', ' - + YMD.year; -} - - -function TimeString(time) { - return TwoDigitString(HOUR_FROM_TIME(time)) + ':' - + TwoDigitString(MIN_FROM_TIME(time)) + ':' - + TwoDigitString(SEC_FROM_TIME(time)); -} - - -function LocalTimezoneString(time) { - var timezoneOffset = - (local_time_offset + DaylightSavingsOffset(time)) / msPerMinute; - var sign = (timezoneOffset >= 0) ? 1 : -1; - var hours = FLOOR((sign * timezoneOffset)/60); - var min = FLOOR((sign * timezoneOffset)%60); - var gmt = ' GMT' + ((sign == 1) ? '+' : '-') + - TwoDigitString(hours) + TwoDigitString(min); - return gmt + ' (' + LocalTimezone(time) + ')'; -} - - -function DatePrintString(time) { - return DateString(time) + ' ' + TimeString(time); -} - -// ------------------------------------------------------------------- - -// Reused output buffer. Used when parsing date strings. -var parse_buffer = $Array(7); - -// ECMA 262 - 15.9.4.2 -function DateParse(string) { - var arr = %DateParseString(ToString(string), parse_buffer); - if (IS_NULL(arr)) return $NaN; - - var day = MakeDay(arr[0], arr[1], arr[2]); - var time = MakeTime(arr[3], arr[4], arr[5], 0); - var date = MakeDate(day, time); - - if (IS_NULL(arr[6])) { - return TimeClip(UTC(date)); - } else { - return TimeClip(date - arr[6] * 1000); - } -} - - -// ECMA 262 - 15.9.4.3 -function DateUTC(year, month, date, hours, minutes, seconds, ms) { - year = ToNumber(year); - month = ToNumber(month); - var argc = %_ArgumentsLength(); - date = argc > 2 ? ToNumber(date) : 1; - hours = argc > 3 ? ToNumber(hours) : 0; - minutes = argc > 4 ? ToNumber(minutes) : 0; - seconds = argc > 5 ? ToNumber(seconds) : 0; - ms = argc > 6 ? ToNumber(ms) : 0; - year = (!NUMBER_IS_NAN(year) && 0 <= TO_INTEGER(year) && TO_INTEGER(year) <= 99) - ? 1900 + TO_INTEGER(year) : year; - var day = MakeDay(year, month, date); - var time = MakeTime(hours, minutes, seconds, ms); - return %_SetValueOf(this, TimeClip(MakeDate(day, time))); -} - - -// Mozilla-specific extension. Returns the number of milliseconds -// elapsed since 1 January 1970 00:00:00 UTC. -function DateNow() { - return %DateCurrentTime(); -} - - -// ECMA 262 - 15.9.5.2 -function DateToString() { - var t = DATE_VALUE(this); - if (NUMBER_IS_NAN(t)) return kInvalidDate; - return DatePrintString(LocalTimeNoCheck(t)) + LocalTimezoneString(t); -} - - -// ECMA 262 - 15.9.5.3 -function DateToDateString() { - var t = DATE_VALUE(this); - if (NUMBER_IS_NAN(t)) return kInvalidDate; - return DateString(LocalTimeNoCheck(t)); -} - - -// ECMA 262 - 15.9.5.4 -function DateToTimeString() { - var t = DATE_VALUE(this); - if (NUMBER_IS_NAN(t)) return kInvalidDate; - var lt = LocalTimeNoCheck(t); - return TimeString(lt) + LocalTimezoneString(lt); -} - - -// ECMA 262 - 15.9.5.5 -function DateToLocaleString() { - return DateToString.call(this); -} - - -// ECMA 262 - 15.9.5.6 -function DateToLocaleDateString() { - var t = DATE_VALUE(this); - if (NUMBER_IS_NAN(t)) return kInvalidDate; - return LongDateString(LocalTimeNoCheck(t)); -} - - -// ECMA 262 - 15.9.5.7 -function DateToLocaleTimeString() { - var t = DATE_VALUE(this); - if (NUMBER_IS_NAN(t)) return kInvalidDate; - var lt = LocalTimeNoCheck(t); - return TimeString(lt); -} - - -// ECMA 262 - 15.9.5.8 -function DateValueOf() { - return DATE_VALUE(this); -} - - -// ECMA 262 - 15.9.5.9 -function DateGetTime() { - return DATE_VALUE(this); -} - - -// ECMA 262 - 15.9.5.10 -function DateGetFullYear() { - return GetFullYearFrom(this) -} - - -// ECMA 262 - 15.9.5.11 -function DateGetUTCFullYear() { - return GetUTCFullYearFrom(this) -} - - -// ECMA 262 - 15.9.5.12 -function DateGetMonth() { - return GetMonthFrom(this); -} - - -// ECMA 262 - 15.9.5.13 -function DateGetUTCMonth() { - return GetUTCMonthFrom(this); -} - - -// ECMA 262 - 15.9.5.14 -function DateGetDate() { - return GetDateFrom(this); -} - - -// ECMA 262 - 15.9.5.15 -function DateGetUTCDate() { - return GetUTCDateFrom(this); -} - - -// ECMA 262 - 15.9.5.16 -function DateGetDay() { - var t = %_ValueOf(this); - if (NUMBER_IS_NAN(t)) return t; - return WeekDay(LocalTimeNoCheck(t)); -} - - -// ECMA 262 - 15.9.5.17 -function DateGetUTCDay() { - var t = %_ValueOf(this); - if (NUMBER_IS_NAN(t)) return t; - return WeekDay(t); -} - - -// ECMA 262 - 15.9.5.18 -function DateGetHours() { - return GetHoursFrom(this); -} - - -// ECMA 262 - 15.9.5.19 -function DateGetUTCHours() { - return GetUTCHoursFrom(this); -} - - -// ECMA 262 - 15.9.5.20 -function DateGetMinutes() { - return GetMinutesFrom(this); -} - - -// ECMA 262 - 15.9.5.21 -function DateGetUTCMinutes() { - return GetUTCMinutesFrom(this); -} - - -// ECMA 262 - 15.9.5.22 -function DateGetSeconds() { - return GetSecondsFrom(this); -} - - -// ECMA 262 - 15.9.5.23 -function DateGetUTCSeconds() { - return GetUTCSecondsFrom(this); -} - - -// ECMA 262 - 15.9.5.24 -function DateGetMilliseconds() { - return GetMillisecondsFrom(this); -} - - -// ECMA 262 - 15.9.5.25 -function DateGetUTCMilliseconds() { - return GetUTCMillisecondsFrom(this); -} - - -// ECMA 262 - 15.9.5.26 -function DateGetTimezoneOffset() { - var t = DATE_VALUE(this); - if (NUMBER_IS_NAN(t)) return t; - return (t - LocalTimeNoCheck(t)) / msPerMinute; -} - - -// ECMA 262 - 15.9.5.27 -function DateSetTime(ms) { - if (!IS_DATE(this)) ThrowDateTypeError(); - return %_SetValueOf(this, TimeClip(ToNumber(ms))); -} - - -// ECMA 262 - 15.9.5.28 -function DateSetMilliseconds(ms) { - var t = LocalTime(DATE_VALUE(this)); - ms = ToNumber(ms); - var time = MakeTime(HOUR_FROM_TIME(t), MIN_FROM_TIME(t), SEC_FROM_TIME(t), ms); - return %_SetValueOf(this, TimeClip(UTC(MakeDate(DAY(t), time)))); -} - - -// ECMA 262 - 15.9.5.29 -function DateSetUTCMilliseconds(ms) { - var t = DATE_VALUE(this); - ms = ToNumber(ms); - var time = MakeTime(HOUR_FROM_TIME(t), MIN_FROM_TIME(t), SEC_FROM_TIME(t), ms); - return %_SetValueOf(this, TimeClip(MakeDate(DAY(t), time))); -} - - -// ECMA 262 - 15.9.5.30 -function DateSetSeconds(sec, ms) { - var t = LocalTime(DATE_VALUE(this)); - sec = ToNumber(sec); - ms = %_ArgumentsLength() < 2 ? GetMillisecondsFrom(this) : ToNumber(ms); - var time = MakeTime(HOUR_FROM_TIME(t), MIN_FROM_TIME(t), sec, ms); - return %_SetValueOf(this, TimeClip(UTC(MakeDate(DAY(t), time)))); -} - - -// ECMA 262 - 15.9.5.31 -function DateSetUTCSeconds(sec, ms) { - var t = DATE_VALUE(this); - sec = ToNumber(sec); - ms = %_ArgumentsLength() < 2 ? GetUTCMillisecondsFrom(this) : ToNumber(ms); - var time = MakeTime(HOUR_FROM_TIME(t), MIN_FROM_TIME(t), sec, ms); - return %_SetValueOf(this, TimeClip(MakeDate(DAY(t), time))); -} - - -// ECMA 262 - 15.9.5.33 -function DateSetMinutes(min, sec, ms) { - var t = LocalTime(DATE_VALUE(this)); - min = ToNumber(min); - var argc = %_ArgumentsLength(); - sec = argc < 2 ? GetSecondsFrom(this) : ToNumber(sec); - ms = argc < 3 ? GetMillisecondsFrom(this) : ToNumber(ms); - var time = MakeTime(HOUR_FROM_TIME(t), min, sec, ms); - return %_SetValueOf(this, TimeClip(UTC(MakeDate(DAY(t), time)))); -} - - -// ECMA 262 - 15.9.5.34 -function DateSetUTCMinutes(min, sec, ms) { - var t = DATE_VALUE(this); - min = ToNumber(min); - var argc = %_ArgumentsLength(); - sec = argc < 2 ? GetUTCSecondsFrom(this) : ToNumber(sec); - ms = argc < 3 ? GetUTCMillisecondsFrom(this) : ToNumber(ms); - var time = MakeTime(HOUR_FROM_TIME(t), min, sec, ms); - return %_SetValueOf(this, TimeClip(MakeDate(DAY(t), time))); -} - - -// ECMA 262 - 15.9.5.35 -function DateSetHours(hour, min, sec, ms) { - var t = LocalTime(DATE_VALUE(this)); - hour = ToNumber(hour); - var argc = %_ArgumentsLength(); - min = argc < 2 ? GetMinutesFrom(this) : ToNumber(min); - sec = argc < 3 ? GetSecondsFrom(this) : ToNumber(sec); - ms = argc < 4 ? GetMillisecondsFrom(this) : ToNumber(ms); - var time = MakeTime(hour, min, sec, ms); - return %_SetValueOf(this, TimeClip(UTC(MakeDate(DAY(t), time)))); -} - - -// ECMA 262 - 15.9.5.34 -function DateSetUTCHours(hour, min, sec, ms) { - var t = DATE_VALUE(this); - hour = ToNumber(hour); - var argc = %_ArgumentsLength(); - min = argc < 2 ? GetUTCMinutesFrom(this) : ToNumber(min); - sec = argc < 3 ? GetUTCSecondsFrom(this) : ToNumber(sec); - ms = argc < 4 ? GetUTCMillisecondsFrom(this) : ToNumber(ms); - var time = MakeTime(hour, min, sec, ms); - return %_SetValueOf(this, TimeClip(MakeDate(DAY(t), time))); -} - - -// ECMA 262 - 15.9.5.36 -function DateSetDate(date) { - var t = LocalTime(DATE_VALUE(this)); - date = ToNumber(date); - var day = MakeDay(YEAR_FROM_TIME(t), MONTH_FROM_TIME(t), date); - return %_SetValueOf(this, TimeClip(UTC(MakeDate(day, TimeWithinDay(t))))); -} - - -// ECMA 262 - 15.9.5.37 -function DateSetUTCDate(date) { - var t = DATE_VALUE(this); - date = ToNumber(date); - var day = MakeDay(YEAR_FROM_TIME(t), MONTH_FROM_TIME(t), date); - return %_SetValueOf(this, TimeClip(MakeDate(day, TimeWithinDay(t)))); -} - - -// ECMA 262 - 15.9.5.38 -function DateSetMonth(month, date) { - var t = LocalTime(DATE_VALUE(this)); - month = ToNumber(month); - date = %_ArgumentsLength() < 2 ? GetDateFrom(this) : ToNumber(date); - var day = MakeDay(YEAR_FROM_TIME(t), month, date); - return %_SetValueOf(this, TimeClip(UTC(MakeDate(day, TimeWithinDay(t))))); -} - - -// ECMA 262 - 15.9.5.39 -function DateSetUTCMonth(month, date) { - var t = DATE_VALUE(this); - month = ToNumber(month); - date = %_ArgumentsLength() < 2 ? GetUTCDateFrom(this) : ToNumber(date); - var day = MakeDay(YEAR_FROM_TIME(t), month, date); - return %_SetValueOf(this, TimeClip(MakeDate(day, TimeWithinDay(t)))); -} - - -// ECMA 262 - 15.9.5.40 -function DateSetFullYear(year, month, date) { - var t = DATE_VALUE(this); - t = NUMBER_IS_NAN(t) ? 0 : LocalTimeNoCheck(t); - year = ToNumber(year); - var argc = %_ArgumentsLength(); - month = argc < 2 ? MONTH_FROM_TIME(t) : ToNumber(month); - date = argc < 3 ? DATE_FROM_TIME(t) : ToNumber(date); - var day = MakeDay(year, month, date); - return %_SetValueOf(this, TimeClip(UTC(MakeDate(day, TimeWithinDay(t))))); -} - - -// ECMA 262 - 15.9.5.41 -function DateSetUTCFullYear(year, month, date) { - var t = DATE_VALUE(this); - if (NUMBER_IS_NAN(t)) t = 0; - var argc = %_ArgumentsLength(); - year = ToNumber(year); - month = argc < 2 ? MONTH_FROM_TIME(t) : ToNumber(month); - date = argc < 3 ? DATE_FROM_TIME(t) : ToNumber(date); - var day = MakeDay(year, month, date); - return %_SetValueOf(this, TimeClip(MakeDate(day, TimeWithinDay(t)))); -} - - -// ECMA 262 - 15.9.5.42 -function DateToUTCString() { - var t = DATE_VALUE(this); - if (NUMBER_IS_NAN(t)) return kInvalidDate; - // Return UTC string of the form: Sat, 31 Jan 1970 23:00:00 GMT - return WeekDays[WeekDay(t)] + ', ' - + TwoDigitString(DATE_FROM_TIME(t)) + ' ' - + Months[MONTH_FROM_TIME(t)] + ' ' - + YEAR_FROM_TIME(t) + ' ' - + TimeString(t) + ' GMT'; -} - - -// ECMA 262 - B.2.4 -function DateGetYear() { - var t = DATE_VALUE(this); - if (NUMBER_IS_NAN(t)) return $NaN; - return YEAR_FROM_TIME(LocalTimeNoCheck(t)) - 1900; -} - - -// ECMA 262 - B.2.5 -function DateSetYear(year) { - var t = LocalTime(DATE_VALUE(this)); - if (NUMBER_IS_NAN(t)) t = 0; - year = ToNumber(year); - if (NUMBER_IS_NAN(year)) return %_SetValueOf(this, $NaN); - year = (0 <= TO_INTEGER(year) && TO_INTEGER(year) <= 99) - ? 1900 + TO_INTEGER(year) : year; - var day = MakeDay(year, MONTH_FROM_TIME(t), DATE_FROM_TIME(t)); - return %_SetValueOf(this, TimeClip(UTC(MakeDate(day, TimeWithinDay(t))))); -} - - -// ECMA 262 - B.2.6 -// -// Notice that this does not follow ECMA 262 completely. ECMA 262 -// says that toGMTString should be the same Function object as -// toUTCString. JSC does not do this, so for compatibility we do not -// do that either. Instead, we create a new function whose name -// property will return toGMTString. -function DateToGMTString() { - return DateToUTCString.call(this); -} - - -function PadInt(n, digits) { - if (digits == 1) return n; - return n < MathPow(10, digits - 1) ? '0' + PadInt(n, digits - 1) : n; -} - - -function DateToISOString() { - var t = DATE_VALUE(this); - if (NUMBER_IS_NAN(t)) return kInvalidDate; - return this.getUTCFullYear() + '-' + PadInt(this.getUTCMonth() + 1, 2) + - '-' + PadInt(this.getUTCDate(), 2) + 'T' + PadInt(this.getUTCHours(), 2) + - ':' + PadInt(this.getUTCMinutes(), 2) + ':' + PadInt(this.getUTCSeconds(), 2) + - '.' + PadInt(this.getUTCMilliseconds(), 3) + - 'Z'; -} - - -function DateToJSON(key) { - return CheckJSONPrimitive(this.toISOString()); -} - - -// ------------------------------------------------------------------- - -function SetupDate() { - // Setup non-enumerable properties of the Date object itself. - InstallFunctions($Date, DONT_ENUM, $Array( - "UTC", DateUTC, - "parse", DateParse, - "now", DateNow - )); - - // Setup non-enumerable constructor property of the Date prototype object. - %SetProperty($Date.prototype, "constructor", $Date, DONT_ENUM); - - // Setup non-enumerable functions of the Date prototype object and - // set their names. - InstallFunctionsOnHiddenPrototype($Date.prototype, DONT_ENUM, $Array( - "toString", DateToString, - "toDateString", DateToDateString, - "toTimeString", DateToTimeString, - "toLocaleString", DateToLocaleString, - "toLocaleDateString", DateToLocaleDateString, - "toLocaleTimeString", DateToLocaleTimeString, - "valueOf", DateValueOf, - "getTime", DateGetTime, - "getFullYear", DateGetFullYear, - "getUTCFullYear", DateGetUTCFullYear, - "getMonth", DateGetMonth, - "getUTCMonth", DateGetUTCMonth, - "getDate", DateGetDate, - "getUTCDate", DateGetUTCDate, - "getDay", DateGetDay, - "getUTCDay", DateGetUTCDay, - "getHours", DateGetHours, - "getUTCHours", DateGetUTCHours, - "getMinutes", DateGetMinutes, - "getUTCMinutes", DateGetUTCMinutes, - "getSeconds", DateGetSeconds, - "getUTCSeconds", DateGetUTCSeconds, - "getMilliseconds", DateGetMilliseconds, - "getUTCMilliseconds", DateGetUTCMilliseconds, - "getTimezoneOffset", DateGetTimezoneOffset, - "setTime", DateSetTime, - "setMilliseconds", DateSetMilliseconds, - "setUTCMilliseconds", DateSetUTCMilliseconds, - "setSeconds", DateSetSeconds, - "setUTCSeconds", DateSetUTCSeconds, - "setMinutes", DateSetMinutes, - "setUTCMinutes", DateSetUTCMinutes, - "setHours", DateSetHours, - "setUTCHours", DateSetUTCHours, - "setDate", DateSetDate, - "setUTCDate", DateSetUTCDate, - "setMonth", DateSetMonth, - "setUTCMonth", DateSetUTCMonth, - "setFullYear", DateSetFullYear, - "setUTCFullYear", DateSetUTCFullYear, - "toGMTString", DateToGMTString, - "toUTCString", DateToUTCString, - "getYear", DateGetYear, - "setYear", DateSetYear, - "toISOString", DateToISOString, - "toJSON", DateToJSON - )); -} - -SetupDate(); diff --git a/src/date.js b/src/date.js index 2ffd0060..7d8f4588 100644 --- a/src/date.js +++ b/src/date.js @@ -730,8 +730,7 @@ function DateValueOf() { // ECMA 262 - 15.9.5.9 -function DateGetTime(logMarker) { - if (logMarker) %ProfileLogMarker(logMarker); +function DateGetTime() { return DATE_VALUE(this); } diff --git a/src/debug-debugger.js b/src/debug-debugger.js index 14d8c883..55c25a92 100644 --- a/src/debug-debugger.js +++ b/src/debug-debugger.js @@ -1202,11 +1202,16 @@ DebugCommandProcessor.prototype.processDebugJSONRequest = function(json_request) throw new Error('Command not specified'); } - // TODO(yurys): remove request.arguments.compactFormat check once - // ChromeDevTools are switched to 'inlineRefs' - if (request.arguments && (request.arguments.inlineRefs || - request.arguments.compactFormat)) { - response.setOption('inlineRefs', true); + if (request.arguments) { + var args = request.arguments; + // TODO(yurys): remove request.arguments.compactFormat check once + // ChromeDevTools are switched to 'inlineRefs' + if (args.inlineRefs || args.compactFormat) { + response.setOption('inlineRefs', true); + } + if (!IS_UNDEFINED(args.maxStringLength)) { + response.setOption('maxStringLength', args.maxStringLength); + } } if (request.command == 'continue') { @@ -1934,10 +1939,14 @@ DebugCommandProcessor.prototype.profileRequest_ = function(request, response) { if (isNaN(modules)) { return response.failed('Modules is not an integer'); } + var tag = parseInt(request.arguments.tag); + if (isNaN(tag)) { + tag = 0; + } if (request.arguments.command == 'resume') { - %ProfilerResume(modules); + %ProfilerResume(modules, tag); } else if (request.arguments.command == 'pause') { - %ProfilerPause(modules); + %ProfilerPause(modules, tag); } else { return response.failed('Unknown command'); } diff --git a/src/debug-delay.js b/src/debug-delay.js deleted file mode 100644 index 14d8c883..00000000 --- a/src/debug-delay.js +++ /dev/null @@ -1,2073 +0,0 @@ -// 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. - -// Default number of frames to include in the response to backtrace request. -const kDefaultBacktraceLength = 10; - -const Debug = {}; - -// Regular expression to skip "crud" at the beginning of a source line which is -// not really code. Currently the regular expression matches whitespace and -// comments. -const sourceLineBeginningSkip = /^(?:\s*(?:\/\*.*?\*\/)*)*/; - -// Debug events which can occour in the V8 JavaScript engine. These originate -// from the API include file debug.h. -Debug.DebugEvent = { Break: 1, - Exception: 2, - NewFunction: 3, - BeforeCompile: 4, - AfterCompile: 5, - ScriptCollected: 6 }; - -// Types of exceptions that can be broken upon. -Debug.ExceptionBreak = { All : 0, - Uncaught: 1 }; - -// The different types of steps. -Debug.StepAction = { StepOut: 0, - StepNext: 1, - StepIn: 2, - StepMin: 3, - StepInMin: 4 }; - -// The different types of scripts matching enum ScriptType in objects.h. -Debug.ScriptType = { Native: 0, - Extension: 1, - Normal: 2 }; - -// The different types of script compilations matching enum -// Script::CompilationType in objects.h. -Debug.ScriptCompilationType = { Host: 0, - Eval: 1, - JSON: 2 }; - -// The different script break point types. -Debug.ScriptBreakPointType = { ScriptId: 0, - ScriptName: 1 }; - -function ScriptTypeFlag(type) { - return (1 << type); -} - -// Globals. -var next_response_seq = 0; -var next_break_point_number = 1; -var break_points = []; -var script_break_points = []; - - -// Create a new break point object and add it to the list of break points. -function MakeBreakPoint(source_position, opt_line, opt_column, opt_script_break_point) { - var break_point = new BreakPoint(source_position, opt_line, opt_column, opt_script_break_point); - break_points.push(break_point); - return break_point; -} - - -// Object representing a break point. -// NOTE: This object does not have a reference to the function having break -// point as this would cause function not to be garbage collected when it is -// not used any more. We do not want break points to keep functions alive. -function BreakPoint(source_position, opt_line, opt_column, opt_script_break_point) { - this.source_position_ = source_position; - this.source_line_ = opt_line; - this.source_column_ = opt_column; - if (opt_script_break_point) { - this.script_break_point_ = opt_script_break_point; - } else { - this.number_ = next_break_point_number++; - } - this.hit_count_ = 0; - this.active_ = true; - this.condition_ = null; - this.ignoreCount_ = 0; -} - - -BreakPoint.prototype.number = function() { - return this.number_; -}; - - -BreakPoint.prototype.func = function() { - return this.func_; -}; - - -BreakPoint.prototype.source_position = function() { - return this.source_position_; -}; - - -BreakPoint.prototype.hit_count = function() { - return this.hit_count_; -}; - - -BreakPoint.prototype.active = function() { - if (this.script_break_point()) { - return this.script_break_point().active(); - } - return this.active_; -}; - - -BreakPoint.prototype.condition = function() { - if (this.script_break_point() && this.script_break_point().condition()) { - return this.script_break_point().condition(); - } - return this.condition_; -}; - - -BreakPoint.prototype.ignoreCount = function() { - return this.ignoreCount_; -}; - - -BreakPoint.prototype.script_break_point = function() { - return this.script_break_point_; -}; - - -BreakPoint.prototype.enable = function() { - this.active_ = true; -}; - - -BreakPoint.prototype.disable = function() { - this.active_ = false; -}; - - -BreakPoint.prototype.setCondition = function(condition) { - this.condition_ = condition; -}; - - -BreakPoint.prototype.setIgnoreCount = function(ignoreCount) { - this.ignoreCount_ = ignoreCount; -}; - - -BreakPoint.prototype.isTriggered = function(exec_state) { - // Break point not active - not triggered. - if (!this.active()) return false; - - // Check for conditional break point. - if (this.condition()) { - // If break point has condition try to evaluate it in the top frame. - try { - var mirror = exec_state.frame(0).evaluate(this.condition()); - // If no sensible mirror or non true value break point not triggered. - if (!(mirror instanceof ValueMirror) || !%ToBoolean(mirror.value_)) { - return false; - } - } catch (e) { - // Exception evaluating condition counts as not triggered. - return false; - } - } - - // Update the hit count. - this.hit_count_++; - if (this.script_break_point_) { - this.script_break_point_.hit_count_++; - } - - // If the break point has an ignore count it is not triggered. - if (this.ignoreCount_ > 0) { - this.ignoreCount_--; - return false; - } - - // Break point triggered. - return true; -}; - - -// Function called from the runtime when a break point is hit. Returns true if -// the break point is triggered and supposed to break execution. -function IsBreakPointTriggered(break_id, break_point) { - return break_point.isTriggered(MakeExecutionState(break_id)); -} - - -// Object representing a script break point. The script is referenced by its -// script name or script id and the break point is represented as line and -// column. -function ScriptBreakPoint(type, script_id_or_name, opt_line, opt_column, - opt_groupId) { - this.type_ = type; - if (type == Debug.ScriptBreakPointType.ScriptId) { - this.script_id_ = script_id_or_name; - } else { // type == Debug.ScriptBreakPointType.ScriptName - this.script_name_ = script_id_or_name; - } - this.line_ = opt_line || 0; - this.column_ = opt_column; - this.groupId_ = opt_groupId; - this.hit_count_ = 0; - this.active_ = true; - this.condition_ = null; - this.ignoreCount_ = 0; -} - - -ScriptBreakPoint.prototype.number = function() { - return this.number_; -}; - - -ScriptBreakPoint.prototype.groupId = function() { - return this.groupId_; -}; - - -ScriptBreakPoint.prototype.type = function() { - return this.type_; -}; - - -ScriptBreakPoint.prototype.script_id = function() { - return this.script_id_; -}; - - -ScriptBreakPoint.prototype.script_name = function() { - return this.script_name_; -}; - - -ScriptBreakPoint.prototype.line = function() { - return this.line_; -}; - - -ScriptBreakPoint.prototype.column = function() { - return this.column_; -}; - - -ScriptBreakPoint.prototype.hit_count = function() { - return this.hit_count_; -}; - - -ScriptBreakPoint.prototype.active = function() { - return this.active_; -}; - - -ScriptBreakPoint.prototype.condition = function() { - return this.condition_; -}; - - -ScriptBreakPoint.prototype.ignoreCount = function() { - return this.ignoreCount_; -}; - - -ScriptBreakPoint.prototype.enable = function() { - this.active_ = true; -}; - - -ScriptBreakPoint.prototype.disable = function() { - this.active_ = false; -}; - - -ScriptBreakPoint.prototype.setCondition = function(condition) { - this.condition_ = condition; -}; - - -ScriptBreakPoint.prototype.setIgnoreCount = function(ignoreCount) { - this.ignoreCount_ = ignoreCount; - - // Set ignore count on all break points created from this script break point. - for (var i = 0; i < break_points.length; i++) { - if (break_points[i].script_break_point() === this) { - break_points[i].setIgnoreCount(ignoreCount); - } - } -}; - - -// Check whether a script matches this script break point. Currently this is -// only based on script name. -ScriptBreakPoint.prototype.matchesScript = function(script) { - if (this.type_ == Debug.ScriptBreakPointType.ScriptId) { - return this.script_id_ == script.id; - } else { // this.type_ == Debug.ScriptBreakPointType.ScriptName - return this.script_name_ == script.name && - script.line_offset <= this.line_ && - this.line_ < script.line_offset + script.lineCount(); - } -}; - - -// Set the script break point in a script. -ScriptBreakPoint.prototype.set = function (script) { - var column = this.column(); - var line = this.line(); - // If the column is undefined the break is on the line. To help locate the - // first piece of breakable code on the line try to find the column on the - // line which contains some source. - if (IS_UNDEFINED(column)) { - var source_line = script.sourceLine(this.line()); - - // Allocate array for caching the columns where the actual source starts. - if (!script.sourceColumnStart_) { - script.sourceColumnStart_ = new Array(script.lineCount()); - } - - // Fill cache if needed and get column where the actual source starts. - if (IS_UNDEFINED(script.sourceColumnStart_[line])) { - script.sourceColumnStart_[line] = - source_line.match(sourceLineBeginningSkip)[0].length; - } - column = script.sourceColumnStart_[line]; - } - - // Convert the line and column into an absolute position within the script. - var pos = Debug.findScriptSourcePosition(script, this.line(), column); - - // If the position is not found in the script (the script might be shorter - // than it used to be) just ignore it. - if (pos === null) return; - - // Create a break point object and set the break point. - break_point = MakeBreakPoint(pos, this.line(), this.column(), this); - break_point.setIgnoreCount(this.ignoreCount()); - %SetScriptBreakPoint(script, pos, break_point); - - return break_point; -}; - - -// Clear all the break points created from this script break point -ScriptBreakPoint.prototype.clear = function () { - var remaining_break_points = []; - for (var i = 0; i < break_points.length; i++) { - if (break_points[i].script_break_point() && - break_points[i].script_break_point() === this) { - %ClearBreakPoint(break_points[i]); - } else { - remaining_break_points.push(break_points[i]); - } - } - break_points = remaining_break_points; -}; - - -// Function called from runtime when a new script is compiled to set any script -// break points set in this script. -function UpdateScriptBreakPoints(script) { - for (var i = 0; i < script_break_points.length; i++) { - if (script_break_points[i].type() == Debug.ScriptBreakPointType.ScriptName && - script_break_points[i].matchesScript(script)) { - script_break_points[i].set(script); - } - } -} - - -Debug.setListener = function(listener, opt_data) { - if (!IS_FUNCTION(listener) && !IS_UNDEFINED(listener) && !IS_NULL(listener)) { - throw new Error('Parameters have wrong types.'); - } - %SetDebugEventListener(listener, opt_data); -}; - - -Debug.breakExecution = function(f) { - %Break(); -}; - -Debug.breakLocations = function(f) { - if (!IS_FUNCTION(f)) throw new Error('Parameters have wrong types.'); - return %GetBreakLocations(f); -}; - -// Returns a Script object. If the parameter is a function the return value -// is the script in which the function is defined. If the parameter is a string -// the return value is the script for which the script name has that string -// value. If it is a regexp and there is a unique script whose name matches -// we return that, otherwise undefined. -Debug.findScript = function(func_or_script_name) { - if (IS_FUNCTION(func_or_script_name)) { - return %FunctionGetScript(func_or_script_name); - } else if (IS_REGEXP(func_or_script_name)) { - var scripts = Debug.scripts(); - var last_result = null; - var result_count = 0; - for (var i in scripts) { - var script = scripts[i]; - if (func_or_script_name.test(script.name)) { - last_result = script; - result_count++; - } - } - // Return the unique script matching the regexp. If there are more - // than one we don't return a value since there is no good way to - // decide which one to return. Returning a "random" one, say the - // first, would introduce nondeterminism (or something close to it) - // because the order is the heap iteration order. - if (result_count == 1) { - return last_result; - } else { - return undefined; - } - } else { - return %GetScript(func_or_script_name); - } -}; - -// Returns the script source. If the parameter is a function the return value -// is the script source for the script in which the function is defined. If the -// parameter is a string the return value is the script for which the script -// name has that string value. -Debug.scriptSource = function(func_or_script_name) { - return this.findScript(func_or_script_name).source; -}; - -Debug.source = function(f) { - if (!IS_FUNCTION(f)) throw new Error('Parameters have wrong types.'); - return %FunctionGetSourceCode(f); -}; - -Debug.disassemble = function(f) { - if (!IS_FUNCTION(f)) throw new Error('Parameters have wrong types.'); - return %DebugDisassembleFunction(f); -}; - -Debug.disassembleConstructor = function(f) { - if (!IS_FUNCTION(f)) throw new Error('Parameters have wrong types.'); - return %DebugDisassembleConstructor(f); -}; - -Debug.sourcePosition = function(f) { - if (!IS_FUNCTION(f)) throw new Error('Parameters have wrong types.'); - return %FunctionGetScriptSourcePosition(f); -}; - - -Debug.findFunctionSourceLocation = function(func, opt_line, opt_column) { - var script = %FunctionGetScript(func); - var script_offset = %FunctionGetScriptSourcePosition(func); - return script.locationFromLine(opt_line, opt_column, script_offset); -} - - -// Returns the character position in a script based on a line number and an -// optional position within that line. -Debug.findScriptSourcePosition = function(script, opt_line, opt_column) { - var location = script.locationFromLine(opt_line, opt_column); - return location ? location.position : null; -} - - -Debug.findBreakPoint = function(break_point_number, remove) { - var break_point; - for (var i = 0; i < break_points.length; i++) { - if (break_points[i].number() == break_point_number) { - break_point = break_points[i]; - // Remove the break point from the list if requested. - if (remove) { - break_points.splice(i, 1); - } - break; - } - } - if (break_point) { - return break_point; - } else { - return this.findScriptBreakPoint(break_point_number, remove); - } -}; - - -Debug.setBreakPoint = function(func, opt_line, opt_column, opt_condition) { - if (!IS_FUNCTION(func)) throw new Error('Parameters have wrong types.'); - // Break points in API functions are not supported. - if (%FunctionIsAPIFunction(func)) { - throw new Error('Cannot set break point in native code.'); - } - // Find source position relative to start of the function - var break_position = - this.findFunctionSourceLocation(func, opt_line, opt_column).position; - var source_position = break_position - this.sourcePosition(func); - // Find the script for the function. - var script = %FunctionGetScript(func); - // Break in builtin JavaScript code is not supported. - if (script.type == Debug.ScriptType.Native) { - throw new Error('Cannot set break point in native code.'); - } - // If the script for the function has a name convert this to a script break - // point. - if (script && script.id) { - // Adjust the source position to be script relative. - source_position += %FunctionGetScriptSourcePosition(func); - // Find line and column for the position in the script and set a script - // break point from that. - var location = script.locationFromPosition(source_position, false); - return this.setScriptBreakPointById(script.id, - location.line, location.column, - opt_condition); - } else { - // Set a break point directly on the function. - var break_point = MakeBreakPoint(source_position, opt_line, opt_column); - %SetFunctionBreakPoint(func, source_position, break_point); - break_point.setCondition(opt_condition); - return break_point.number(); - } -}; - - -Debug.enableBreakPoint = function(break_point_number) { - var break_point = this.findBreakPoint(break_point_number, false); - break_point.enable(); -}; - - -Debug.disableBreakPoint = function(break_point_number) { - var break_point = this.findBreakPoint(break_point_number, false); - break_point.disable(); -}; - - -Debug.changeBreakPointCondition = function(break_point_number, condition) { - var break_point = this.findBreakPoint(break_point_number, false); - break_point.setCondition(condition); -}; - - -Debug.changeBreakPointIgnoreCount = function(break_point_number, ignoreCount) { - if (ignoreCount < 0) { - throw new Error('Invalid argument'); - } - var break_point = this.findBreakPoint(break_point_number, false); - break_point.setIgnoreCount(ignoreCount); -}; - - -Debug.clearBreakPoint = function(break_point_number) { - var break_point = this.findBreakPoint(break_point_number, true); - if (break_point) { - return %ClearBreakPoint(break_point); - } else { - break_point = this.findScriptBreakPoint(break_point_number, true); - if (!break_point) { - throw new Error('Invalid breakpoint'); - } - } -}; - - -Debug.clearAllBreakPoints = function() { - for (var i = 0; i < break_points.length; i++) { - break_point = break_points[i]; - %ClearBreakPoint(break_point); - } - break_points = []; -}; - - -Debug.findScriptBreakPoint = function(break_point_number, remove) { - var script_break_point; - for (var i = 0; i < script_break_points.length; i++) { - if (script_break_points[i].number() == break_point_number) { - script_break_point = script_break_points[i]; - // Remove the break point from the list if requested. - if (remove) { - script_break_point.clear(); - script_break_points.splice(i,1); - } - break; - } - } - return script_break_point; -} - - -// Sets a breakpoint in a script identified through id or name at the -// specified source line and column within that line. -Debug.setScriptBreakPoint = function(type, script_id_or_name, - opt_line, opt_column, opt_condition, - opt_groupId) { - // Create script break point object. - var script_break_point = - new ScriptBreakPoint(type, script_id_or_name, opt_line, opt_column, - opt_groupId); - - // Assign number to the new script break point and add it. - script_break_point.number_ = next_break_point_number++; - script_break_point.setCondition(opt_condition); - script_break_points.push(script_break_point); - - // Run through all scripts to see if this script break point matches any - // loaded scripts. - var scripts = this.scripts(); - for (var i = 0; i < scripts.length; i++) { - if (script_break_point.matchesScript(scripts[i])) { - script_break_point.set(scripts[i]); - } - } - - return script_break_point.number(); -} - - -Debug.setScriptBreakPointById = function(script_id, - opt_line, opt_column, - opt_condition, opt_groupId) { - return this.setScriptBreakPoint(Debug.ScriptBreakPointType.ScriptId, - script_id, opt_line, opt_column, - opt_condition, opt_groupId); -} - - -Debug.setScriptBreakPointByName = function(script_name, - opt_line, opt_column, - opt_condition, opt_groupId) { - return this.setScriptBreakPoint(Debug.ScriptBreakPointType.ScriptName, - script_name, opt_line, opt_column, - opt_condition, opt_groupId); -} - - -Debug.enableScriptBreakPoint = function(break_point_number) { - var script_break_point = this.findScriptBreakPoint(break_point_number, false); - script_break_point.enable(); -}; - - -Debug.disableScriptBreakPoint = function(break_point_number) { - var script_break_point = this.findScriptBreakPoint(break_point_number, false); - script_break_point.disable(); -}; - - -Debug.changeScriptBreakPointCondition = function(break_point_number, condition) { - var script_break_point = this.findScriptBreakPoint(break_point_number, false); - script_break_point.setCondition(condition); -}; - - -Debug.changeScriptBreakPointIgnoreCount = function(break_point_number, ignoreCount) { - if (ignoreCount < 0) { - throw new Error('Invalid argument'); - } - var script_break_point = this.findScriptBreakPoint(break_point_number, false); - script_break_point.setIgnoreCount(ignoreCount); -}; - - -Debug.scriptBreakPoints = function() { - return script_break_points; -} - - -Debug.clearStepping = function() { - %ClearStepping(); -} - -Debug.setBreakOnException = function() { - return %ChangeBreakOnException(Debug.ExceptionBreak.All, true); -}; - -Debug.clearBreakOnException = function() { - return %ChangeBreakOnException(Debug.ExceptionBreak.All, false); -}; - -Debug.setBreakOnUncaughtException = function() { - return %ChangeBreakOnException(Debug.ExceptionBreak.Uncaught, true); -}; - -Debug.clearBreakOnUncaughtException = function() { - return %ChangeBreakOnException(Debug.ExceptionBreak.Uncaught, false); -}; - -Debug.showBreakPoints = function(f, full) { - if (!IS_FUNCTION(f)) throw new Error('Parameters have wrong types.'); - var source = full ? this.scriptSource(f) : this.source(f); - var offset = full ? this.sourcePosition(f) : 0; - var locations = this.breakLocations(f); - if (!locations) return source; - locations.sort(function(x, y) { return x - y; }); - var result = ""; - var prev_pos = 0; - var pos; - for (var i = 0; i < locations.length; i++) { - pos = locations[i] - offset; - result += source.slice(prev_pos, pos); - result += "[B" + i + "]"; - prev_pos = pos; - } - pos = source.length; - result += source.substring(prev_pos, pos); - return result; -}; - - -// Get all the scripts currently loaded. Locating all the scripts is based on -// scanning the heap. -Debug.scripts = function() { - // Collect all scripts in the heap. - return %DebugGetLoadedScripts(); -} - -function MakeExecutionState(break_id) { - return new ExecutionState(break_id); -} - -function ExecutionState(break_id) { - this.break_id = break_id; - this.selected_frame = 0; -} - -ExecutionState.prototype.prepareStep = function(opt_action, opt_count) { - var action = Debug.StepAction.StepIn; - if (!IS_UNDEFINED(opt_action)) action = %ToNumber(opt_action); - var count = opt_count ? %ToNumber(opt_count) : 1; - - return %PrepareStep(this.break_id, action, count); -} - -ExecutionState.prototype.evaluateGlobal = function(source, disable_break) { - return MakeMirror( - %DebugEvaluateGlobal(this.break_id, source, Boolean(disable_break))); -}; - -ExecutionState.prototype.frameCount = function() { - return %GetFrameCount(this.break_id); -}; - -ExecutionState.prototype.threadCount = function() { - return %GetThreadCount(this.break_id); -}; - -ExecutionState.prototype.frame = function(opt_index) { - // If no index supplied return the selected frame. - if (opt_index == null) opt_index = this.selected_frame; - return new FrameMirror(this.break_id, opt_index); -}; - -ExecutionState.prototype.cframesValue = function(opt_from_index, opt_to_index) { - return %GetCFrames(this.break_id); -}; - -ExecutionState.prototype.setSelectedFrame = function(index) { - var i = %ToNumber(index); - if (i < 0 || i >= this.frameCount()) throw new Error('Illegal frame index.'); - this.selected_frame = i; -}; - -ExecutionState.prototype.selectedFrame = function() { - return this.selected_frame; -}; - -ExecutionState.prototype.debugCommandProcessor = function(opt_is_running) { - return new DebugCommandProcessor(this, opt_is_running); -}; - - -function MakeBreakEvent(exec_state, break_points_hit) { - return new BreakEvent(exec_state, break_points_hit); -} - - -function BreakEvent(exec_state, break_points_hit) { - this.exec_state_ = exec_state; - this.break_points_hit_ = break_points_hit; -} - - -BreakEvent.prototype.executionState = function() { - return this.exec_state_; -}; - - -BreakEvent.prototype.eventType = function() { - return Debug.DebugEvent.Break; -}; - - -BreakEvent.prototype.func = function() { - return this.exec_state_.frame(0).func(); -}; - - -BreakEvent.prototype.sourceLine = function() { - return this.exec_state_.frame(0).sourceLine(); -}; - - -BreakEvent.prototype.sourceColumn = function() { - return this.exec_state_.frame(0).sourceColumn(); -}; - - -BreakEvent.prototype.sourceLineText = function() { - return this.exec_state_.frame(0).sourceLineText(); -}; - - -BreakEvent.prototype.breakPointsHit = function() { - return this.break_points_hit_; -}; - - -BreakEvent.prototype.toJSONProtocol = function() { - var o = { seq: next_response_seq++, - type: "event", - event: "break", - body: { invocationText: this.exec_state_.frame(0).invocationText(), - } - }; - - // Add script related information to the event if available. - var script = this.func().script(); - if (script) { - o.body.sourceLine = this.sourceLine(), - o.body.sourceColumn = this.sourceColumn(), - o.body.sourceLineText = this.sourceLineText(), - o.body.script = MakeScriptObject_(script, false); - } - - // Add an Array of break points hit if any. - if (this.breakPointsHit()) { - o.body.breakpoints = []; - for (var i = 0; i < this.breakPointsHit().length; i++) { - // Find the break point number. For break points originating from a - // script break point supply the script break point number. - var breakpoint = this.breakPointsHit()[i]; - var script_break_point = breakpoint.script_break_point(); - var number; - if (script_break_point) { - number = script_break_point.number(); - } else { - number = breakpoint.number(); - } - o.body.breakpoints.push(number); - } - } - return JSON.stringify(ObjectToProtocolObject_(o)); -}; - - -function MakeExceptionEvent(exec_state, exception, uncaught) { - return new ExceptionEvent(exec_state, exception, uncaught); -} - - -function ExceptionEvent(exec_state, exception, uncaught) { - this.exec_state_ = exec_state; - this.exception_ = exception; - this.uncaught_ = uncaught; -} - - -ExceptionEvent.prototype.executionState = function() { - return this.exec_state_; -}; - - -ExceptionEvent.prototype.eventType = function() { - return Debug.DebugEvent.Exception; -}; - - -ExceptionEvent.prototype.exception = function() { - return this.exception_; -} - - -ExceptionEvent.prototype.uncaught = function() { - return this.uncaught_; -} - - -ExceptionEvent.prototype.func = function() { - return this.exec_state_.frame(0).func(); -}; - - -ExceptionEvent.prototype.sourceLine = function() { - return this.exec_state_.frame(0).sourceLine(); -}; - - -ExceptionEvent.prototype.sourceColumn = function() { - return this.exec_state_.frame(0).sourceColumn(); -}; - - -ExceptionEvent.prototype.sourceLineText = function() { - return this.exec_state_.frame(0).sourceLineText(); -}; - - -ExceptionEvent.prototype.toJSONProtocol = function() { - var o = new ProtocolMessage(); - o.event = "exception"; - o.body = { uncaught: this.uncaught_, - exception: MakeMirror(this.exception_) - }; - - // Exceptions might happen whithout any JavaScript frames. - if (this.exec_state_.frameCount() > 0) { - o.body.sourceLine = this.sourceLine(); - o.body.sourceColumn = this.sourceColumn(); - o.body.sourceLineText = this.sourceLineText(); - - // Add script information to the event if available. - var script = this.func().script(); - if (script) { - o.body.script = MakeScriptObject_(script, false); - } - } else { - o.body.sourceLine = -1; - } - - return o.toJSONProtocol(); -}; - - -function MakeCompileEvent(exec_state, script, before) { - return new CompileEvent(exec_state, script, before); -} - - -function CompileEvent(exec_state, script, before) { - this.exec_state_ = exec_state; - this.script_ = MakeMirror(script); - this.before_ = before; -} - - -CompileEvent.prototype.executionState = function() { - return this.exec_state_; -}; - - -CompileEvent.prototype.eventType = function() { - if (this.before_) { - return Debug.DebugEvent.BeforeCompile; - } else { - return Debug.DebugEvent.AfterCompile; - } -}; - - -CompileEvent.prototype.script = function() { - return this.script_; -}; - - -CompileEvent.prototype.toJSONProtocol = function() { - var o = new ProtocolMessage(); - o.running = true; - if (this.before_) { - o.event = "beforeCompile"; - } else { - o.event = "afterCompile"; - } - o.body = {}; - o.body.script = this.script_; - - return o.toJSONProtocol(); -} - - -function MakeNewFunctionEvent(func) { - return new NewFunctionEvent(func); -} - - -function NewFunctionEvent(func) { - this.func = func; -} - - -NewFunctionEvent.prototype.eventType = function() { - return Debug.DebugEvent.NewFunction; -}; - - -NewFunctionEvent.prototype.name = function() { - return this.func.name; -}; - - -NewFunctionEvent.prototype.setBreakPoint = function(p) { - Debug.setBreakPoint(this.func, p || 0); -}; - - -function MakeScriptCollectedEvent(exec_state, id) { - return new ScriptCollectedEvent(exec_state, id); -} - - -function ScriptCollectedEvent(exec_state, id) { - this.exec_state_ = exec_state; - this.id_ = id; -} - - -ScriptCollectedEvent.prototype.id = function() { - return this.id_; -}; - - -ScriptCollectedEvent.prototype.executionState = function() { - return this.exec_state_; -}; - - -ScriptCollectedEvent.prototype.toJSONProtocol = function() { - var o = new ProtocolMessage(); - o.running = true; - o.event = "scriptCollected"; - o.body = {}; - o.body.script = { id: this.id() }; - return o.toJSONProtocol(); -} - - -function MakeScriptObject_(script, include_source) { - var o = { id: script.id(), - name: script.name(), - lineOffset: script.lineOffset(), - columnOffset: script.columnOffset(), - lineCount: script.lineCount(), - }; - if (!IS_UNDEFINED(script.data())) { - o.data = script.data(); - } - if (include_source) { - o.source = script.source(); - } - return o; -}; - - -function DebugCommandProcessor(exec_state, opt_is_running) { - this.exec_state_ = exec_state; - this.running_ = opt_is_running || false; -}; - - -DebugCommandProcessor.prototype.processDebugRequest = function (request) { - return this.processDebugJSONRequest(request); -} - - -function ProtocolMessage(request) { - // Update sequence number. - this.seq = next_response_seq++; - - if (request) { - // If message is based on a request this is a response. Fill the initial - // response from the request. - this.type = 'response'; - this.request_seq = request.seq; - this.command = request.command; - } else { - // If message is not based on a request it is a dabugger generated event. - this.type = 'event'; - } - this.success = true; - // Handler may set this field to control debugger state. - this.running = undefined; -} - - -ProtocolMessage.prototype.setOption = function(name, value) { - if (!this.options_) { - this.options_ = {}; - } - this.options_[name] = value; -} - - -ProtocolMessage.prototype.failed = function(message) { - this.success = false; - this.message = message; -} - - -ProtocolMessage.prototype.toJSONProtocol = function() { - // Encode the protocol header. - var json = {}; - json.seq= this.seq; - if (this.request_seq) { - json.request_seq = this.request_seq; - } - json.type = this.type; - if (this.event) { - json.event = this.event; - } - if (this.command) { - json.command = this.command; - } - if (this.success) { - json.success = this.success; - } else { - json.success = false; - } - if (this.body) { - // Encode the body part. - var bodyJson; - var serializer = MakeMirrorSerializer(true, this.options_); - if (this.body instanceof Mirror) { - bodyJson = serializer.serializeValue(this.body); - } else if (this.body instanceof Array) { - bodyJson = []; - for (var i = 0; i < this.body.length; i++) { - if (this.body[i] instanceof Mirror) { - bodyJson.push(serializer.serializeValue(this.body[i])); - } else { - bodyJson.push(ObjectToProtocolObject_(this.body[i], serializer)); - } - } - } else { - bodyJson = ObjectToProtocolObject_(this.body, serializer); - } - json.body = bodyJson; - json.refs = serializer.serializeReferencedObjects(); - } - if (this.message) { - json.message = this.message; - } - json.running = this.running; - return JSON.stringify(json); -} - - -DebugCommandProcessor.prototype.createResponse = function(request) { - return new ProtocolMessage(request); -}; - - -DebugCommandProcessor.prototype.processDebugJSONRequest = function(json_request) { - var request; // Current request. - var response; // Generated response. - try { - try { - // Convert the JSON string to an object. - request = %CompileString('(' + json_request + ')', false)(); - - // Create an initial response. - response = this.createResponse(request); - - if (!request.type) { - throw new Error('Type not specified'); - } - - if (request.type != 'request') { - throw new Error("Illegal type '" + request.type + "' in request"); - } - - if (!request.command) { - throw new Error('Command not specified'); - } - - // TODO(yurys): remove request.arguments.compactFormat check once - // ChromeDevTools are switched to 'inlineRefs' - if (request.arguments && (request.arguments.inlineRefs || - request.arguments.compactFormat)) { - response.setOption('inlineRefs', true); - } - - if (request.command == 'continue') { - this.continueRequest_(request, response); - } else if (request.command == 'break') { - this.breakRequest_(request, response); - } else if (request.command == 'setbreakpoint') { - this.setBreakPointRequest_(request, response); - } else if (request.command == 'changebreakpoint') { - this.changeBreakPointRequest_(request, response); - } else if (request.command == 'clearbreakpoint') { - this.clearBreakPointRequest_(request, response); - } else if (request.command == 'clearbreakpointgroup') { - this.clearBreakPointGroupRequest_(request, response); - } else if (request.command == 'backtrace') { - this.backtraceRequest_(request, response); - } else if (request.command == 'frame') { - this.frameRequest_(request, response); - } else if (request.command == 'scopes') { - this.scopesRequest_(request, response); - } else if (request.command == 'scope') { - this.scopeRequest_(request, response); - } else if (request.command == 'evaluate') { - this.evaluateRequest_(request, response); - } else if (request.command == 'lookup') { - this.lookupRequest_(request, response); - } else if (request.command == 'references') { - this.referencesRequest_(request, response); - } else if (request.command == 'source') { - this.sourceRequest_(request, response); - } else if (request.command == 'scripts') { - this.scriptsRequest_(request, response); - } else if (request.command == 'threads') { - this.threadsRequest_(request, response); - } else if (request.command == 'suspend') { - this.suspendRequest_(request, response); - } else if (request.command == 'version') { - this.versionRequest_(request, response); - } else if (request.command == 'profile') { - this.profileRequest_(request, response); - } else { - throw new Error('Unknown command "' + request.command + '" in request'); - } - } catch (e) { - // If there is no response object created one (without command). - if (!response) { - response = this.createResponse(); - } - response.success = false; - response.message = %ToString(e); - } - - // Return the response as a JSON encoded string. - try { - if (!IS_UNDEFINED(response.running)) { - // Response controls running state. - this.running_ = response.running; - } - response.running = this.running_; - return response.toJSONProtocol(); - } catch (e) { - // Failed to generate response - return generic error. - return '{"seq":' + response.seq + ',' + - '"request_seq":' + request.seq + ',' + - '"type":"response",' + - '"success":false,' + - '"message":"Internal error: ' + %ToString(e) + '"}'; - } - } catch (e) { - // Failed in one of the catch blocks above - most generic error. - return '{"seq":0,"type":"response","success":false,"message":"Internal error"}'; - } -}; - - -DebugCommandProcessor.prototype.continueRequest_ = function(request, response) { - // Check for arguments for continue. - if (request.arguments) { - var count = 1; - var action = Debug.StepAction.StepIn; - - // Pull out arguments. - var stepaction = request.arguments.stepaction; - var stepcount = request.arguments.stepcount; - - // Get the stepcount argument if any. - if (stepcount) { - count = %ToNumber(stepcount); - if (count < 0) { - throw new Error('Invalid stepcount argument "' + stepcount + '".'); - } - } - - // Get the stepaction argument. - if (stepaction) { - if (stepaction == 'in') { - action = Debug.StepAction.StepIn; - } else if (stepaction == 'min') { - action = Debug.StepAction.StepMin; - } else if (stepaction == 'next') { - action = Debug.StepAction.StepNext; - } else if (stepaction == 'out') { - action = Debug.StepAction.StepOut; - } else { - throw new Error('Invalid stepaction argument "' + stepaction + '".'); - } - } - - // Setup the VM for stepping. - this.exec_state_.prepareStep(action, count); - } - - // VM should be running after executing this request. - response.running = true; -}; - - -DebugCommandProcessor.prototype.breakRequest_ = function(request, response) { - // Ignore as break command does not do anything when broken. -}; - - -DebugCommandProcessor.prototype.setBreakPointRequest_ = - function(request, response) { - // Check for legal request. - if (!request.arguments) { - response.failed('Missing arguments'); - return; - } - - // Pull out arguments. - var type = request.arguments.type; - var target = request.arguments.target; - var line = request.arguments.line; - var column = request.arguments.column; - var enabled = IS_UNDEFINED(request.arguments.enabled) ? - true : request.arguments.enabled; - var condition = request.arguments.condition; - var ignoreCount = request.arguments.ignoreCount; - var groupId = request.arguments.groupId; - - // Check for legal arguments. - if (!type || IS_UNDEFINED(target)) { - response.failed('Missing argument "type" or "target"'); - return; - } - if (type != 'function' && type != 'handle' && - type != 'script' && type != 'scriptId') { - response.failed('Illegal type "' + type + '"'); - return; - } - - // Either function or script break point. - var break_point_number; - if (type == 'function') { - // Handle function break point. - if (!IS_STRING(target)) { - response.failed('Argument "target" is not a string value'); - return; - } - var f; - try { - // Find the function through a global evaluate. - f = this.exec_state_.evaluateGlobal(target).value(); - } catch (e) { - response.failed('Error: "' + %ToString(e) + - '" evaluating "' + target + '"'); - return; - } - if (!IS_FUNCTION(f)) { - response.failed('"' + target + '" does not evaluate to a function'); - return; - } - - // Set function break point. - break_point_number = Debug.setBreakPoint(f, line, column, condition); - } else if (type == 'handle') { - // Find the object pointed by the specified handle. - var handle = parseInt(target, 10); - var mirror = LookupMirror(handle); - if (!mirror) { - return response.failed('Object #' + handle + '# not found'); - } - if (!mirror.isFunction()) { - return response.failed('Object #' + handle + '# is not a function'); - } - - // Set function break point. - break_point_number = Debug.setBreakPoint(mirror.value(), - line, column, condition); - } else if (type == 'script') { - // set script break point. - break_point_number = - Debug.setScriptBreakPointByName(target, line, column, condition, - groupId); - } else { // type == 'scriptId. - break_point_number = - Debug.setScriptBreakPointById(target, line, column, condition, groupId); - } - - // Set additional break point properties. - var break_point = Debug.findBreakPoint(break_point_number); - if (ignoreCount) { - Debug.changeBreakPointIgnoreCount(break_point_number, ignoreCount); - } - if (!enabled) { - Debug.disableBreakPoint(break_point_number); - } - - // Add the break point number to the response. - response.body = { type: type, - breakpoint: break_point_number } - - // Add break point information to the response. - if (break_point instanceof ScriptBreakPoint) { - if (break_point.type() == Debug.ScriptBreakPointType.ScriptId) { - response.body.type = 'scriptId'; - response.body.script_id = break_point.script_id(); - } else { - response.body.type = 'scriptName'; - response.body.script_name = break_point.script_name(); - } - response.body.line = break_point.line(); - response.body.column = break_point.column(); - } else { - response.body.type = 'function'; - } -}; - - -DebugCommandProcessor.prototype.changeBreakPointRequest_ = function(request, response) { - // Check for legal request. - if (!request.arguments) { - response.failed('Missing arguments'); - return; - } - - // Pull out arguments. - var break_point = %ToNumber(request.arguments.breakpoint); - var enabled = request.arguments.enabled; - var condition = request.arguments.condition; - var ignoreCount = request.arguments.ignoreCount; - - // Check for legal arguments. - if (!break_point) { - response.failed('Missing argument "breakpoint"'); - return; - } - - // Change enabled state if supplied. - if (!IS_UNDEFINED(enabled)) { - if (enabled) { - Debug.enableBreakPoint(break_point); - } else { - Debug.disableBreakPoint(break_point); - } - } - - // Change condition if supplied - if (!IS_UNDEFINED(condition)) { - Debug.changeBreakPointCondition(break_point, condition); - } - - // Change ignore count if supplied - if (!IS_UNDEFINED(ignoreCount)) { - Debug.changeBreakPointIgnoreCount(break_point, ignoreCount); - } -} - - -DebugCommandProcessor.prototype.clearBreakPointGroupRequest_ = function(request, response) { - // Check for legal request. - if (!request.arguments) { - response.failed('Missing arguments'); - return; - } - - // Pull out arguments. - var group_id = request.arguments.groupId; - - // Check for legal arguments. - if (!group_id) { - response.failed('Missing argument "groupId"'); - return; - } - - var cleared_break_points = []; - var new_script_break_points = []; - for (var i = 0; i < script_break_points.length; i++) { - var next_break_point = script_break_points[i]; - if (next_break_point.groupId() == group_id) { - cleared_break_points.push(next_break_point.number()); - next_break_point.clear(); - } else { - new_script_break_points.push(next_break_point); - } - } - script_break_points = new_script_break_points; - - // Add the cleared break point numbers to the response. - response.body = { breakpoints: cleared_break_points }; -} - - -DebugCommandProcessor.prototype.clearBreakPointRequest_ = function(request, response) { - // Check for legal request. - if (!request.arguments) { - response.failed('Missing arguments'); - return; - } - - // Pull out arguments. - var break_point = %ToNumber(request.arguments.breakpoint); - - // Check for legal arguments. - if (!break_point) { - response.failed('Missing argument "breakpoint"'); - return; - } - - // Clear break point. - Debug.clearBreakPoint(break_point); - - // Add the cleared break point number to the response. - response.body = { breakpoint: break_point } -} - - -DebugCommandProcessor.prototype.backtraceRequest_ = function(request, response) { - // Get the number of frames. - var total_frames = this.exec_state_.frameCount(); - - // Create simple response if there are no frames. - if (total_frames == 0) { - response.body = { - totalFrames: total_frames - } - return; - } - - // Default frame range to include in backtrace. - var from_index = 0 - var to_index = kDefaultBacktraceLength; - - // Get the range from the arguments. - if (request.arguments) { - if (request.arguments.fromFrame) { - from_index = request.arguments.fromFrame; - } - if (request.arguments.toFrame) { - to_index = request.arguments.toFrame; - } - if (request.arguments.bottom) { - var tmp_index = total_frames - from_index; - from_index = total_frames - to_index - to_index = tmp_index; - } - if (from_index < 0 || to_index < 0) { - return response.failed('Invalid frame number'); - } - } - - // Adjust the index. - to_index = Math.min(total_frames, to_index); - - if (to_index <= from_index) { - var error = 'Invalid frame range'; - return response.failed(error); - } - - // Create the response body. - var frames = []; - for (var i = from_index; i < to_index; i++) { - frames.push(this.exec_state_.frame(i)); - } - response.body = { - fromFrame: from_index, - toFrame: to_index, - totalFrames: total_frames, - frames: frames - } -}; - - -DebugCommandProcessor.prototype.backtracec = function(cmd, args) { - return this.exec_state_.cframesValue(); -}; - - -DebugCommandProcessor.prototype.frameRequest_ = function(request, response) { - // No frames no source. - if (this.exec_state_.frameCount() == 0) { - return response.failed('No frames'); - } - - // With no arguments just keep the selected frame. - if (request.arguments) { - var index = request.arguments.number; - if (index < 0 || this.exec_state_.frameCount() <= index) { - return response.failed('Invalid frame number'); - } - - this.exec_state_.setSelectedFrame(request.arguments.number); - } - response.body = this.exec_state_.frame(); -}; - - -DebugCommandProcessor.prototype.frameForScopeRequest_ = function(request) { - // Get the frame for which the scope or scopes are requested. With no frameNumber - // argument use the currently selected frame. - if (request.arguments && !IS_UNDEFINED(request.arguments.frameNumber)) { - frame_index = request.arguments.frameNumber; - if (frame_index < 0 || this.exec_state_.frameCount() <= frame_index) { - return response.failed('Invalid frame number'); - } - return this.exec_state_.frame(frame_index); - } else { - return this.exec_state_.frame(); - } -} - - -DebugCommandProcessor.prototype.scopesRequest_ = function(request, response) { - // No frames no scopes. - if (this.exec_state_.frameCount() == 0) { - return response.failed('No scopes'); - } - - // Get the frame for which the scopes are requested. - var frame = this.frameForScopeRequest_(request); - - // Fill all scopes for this frame. - var total_scopes = frame.scopeCount(); - var scopes = []; - for (var i = 0; i < total_scopes; i++) { - scopes.push(frame.scope(i)); - } - response.body = { - fromScope: 0, - toScope: total_scopes, - totalScopes: total_scopes, - scopes: scopes - } -}; - - -DebugCommandProcessor.prototype.scopeRequest_ = function(request, response) { - // No frames no scopes. - if (this.exec_state_.frameCount() == 0) { - return response.failed('No scopes'); - } - - // Get the frame for which the scope is requested. - var frame = this.frameForScopeRequest_(request); - - // With no scope argument just return top scope. - var scope_index = 0; - if (request.arguments && !IS_UNDEFINED(request.arguments.number)) { - scope_index = %ToNumber(request.arguments.number); - if (scope_index < 0 || frame.scopeCount() <= scope_index) { - return response.failed('Invalid scope number'); - } - } - - response.body = frame.scope(scope_index); -}; - - -DebugCommandProcessor.prototype.evaluateRequest_ = function(request, response) { - if (!request.arguments) { - return response.failed('Missing arguments'); - } - - // Pull out arguments. - var expression = request.arguments.expression; - var frame = request.arguments.frame; - var global = request.arguments.global; - var disable_break = request.arguments.disable_break; - - // The expression argument could be an integer so we convert it to a - // string. - try { - expression = String(expression); - } catch(e) { - return response.failed('Failed to convert expression argument to string'); - } - - // Check for legal arguments. - if (!IS_UNDEFINED(frame) && global) { - return response.failed('Arguments "frame" and "global" are exclusive'); - } - - // Global evaluate. - if (global) { - // Evaluate in the global context. - response.body = - this.exec_state_.evaluateGlobal(expression, Boolean(disable_break)); - return; - } - - // Default value for disable_break is true. - if (IS_UNDEFINED(disable_break)) { - disable_break = true; - } - - // No frames no evaluate in frame. - if (this.exec_state_.frameCount() == 0) { - return response.failed('No frames'); - } - - // Check whether a frame was specified. - if (!IS_UNDEFINED(frame)) { - var frame_number = %ToNumber(frame); - if (frame_number < 0 || frame_number >= this.exec_state_.frameCount()) { - return response.failed('Invalid frame "' + frame + '"'); - } - // Evaluate in the specified frame. - response.body = this.exec_state_.frame(frame_number).evaluate( - expression, Boolean(disable_break)); - return; - } else { - // Evaluate in the selected frame. - response.body = this.exec_state_.frame().evaluate( - expression, Boolean(disable_break)); - return; - } -}; - - -DebugCommandProcessor.prototype.lookupRequest_ = function(request, response) { - if (!request.arguments) { - return response.failed('Missing arguments'); - } - - // Pull out arguments. - var handles = request.arguments.handles; - - // Check for legal arguments. - if (IS_UNDEFINED(handles)) { - return response.failed('Argument "handles" missing'); - } - - // Set 'includeSource' option for script lookup. - if (!IS_UNDEFINED(request.arguments.includeSource)) { - includeSource = %ToBoolean(request.arguments.includeSource); - response.setOption('includeSource', includeSource); - } - - // Lookup handles. - var mirrors = {}; - for (var i = 0; i < handles.length; i++) { - var handle = handles[i]; - var mirror = LookupMirror(handle); - if (!mirror) { - return response.failed('Object #' + handle + '# not found'); - } - mirrors[handle] = mirror; - } - response.body = mirrors; -}; - - -DebugCommandProcessor.prototype.referencesRequest_ = - function(request, response) { - if (!request.arguments) { - return response.failed('Missing arguments'); - } - - // Pull out arguments. - var type = request.arguments.type; - var handle = request.arguments.handle; - - // Check for legal arguments. - if (IS_UNDEFINED(type)) { - return response.failed('Argument "type" missing'); - } - if (IS_UNDEFINED(handle)) { - return response.failed('Argument "handle" missing'); - } - if (type != 'referencedBy' && type != 'constructedBy') { - return response.failed('Invalid type "' + type + '"'); - } - - // Lookup handle and return objects with references the object. - var mirror = LookupMirror(handle); - if (mirror) { - if (type == 'referencedBy') { - response.body = mirror.referencedBy(); - } else { - response.body = mirror.constructedBy(); - } - } else { - return response.failed('Object #' + handle + '# not found'); - } -}; - - -DebugCommandProcessor.prototype.sourceRequest_ = function(request, response) { - // No frames no source. - if (this.exec_state_.frameCount() == 0) { - return response.failed('No source'); - } - - var from_line; - var to_line; - var frame = this.exec_state_.frame(); - if (request.arguments) { - // Pull out arguments. - from_line = request.arguments.fromLine; - to_line = request.arguments.toLine; - - if (!IS_UNDEFINED(request.arguments.frame)) { - var frame_number = %ToNumber(request.arguments.frame); - if (frame_number < 0 || frame_number >= this.exec_state_.frameCount()) { - return response.failed('Invalid frame "' + frame + '"'); - } - frame = this.exec_state_.frame(frame_number); - } - } - - // Get the script selected. - var script = frame.func().script(); - if (!script) { - return response.failed('No source'); - } - - // Get the source slice and fill it into the response. - var slice = script.sourceSlice(from_line, to_line); - if (!slice) { - return response.failed('Invalid line interval'); - } - response.body = {}; - response.body.source = slice.sourceText(); - response.body.fromLine = slice.from_line; - response.body.toLine = slice.to_line; - response.body.fromPosition = slice.from_position; - response.body.toPosition = slice.to_position; - response.body.totalLines = script.lineCount(); -}; - - -DebugCommandProcessor.prototype.scriptsRequest_ = function(request, response) { - var types = ScriptTypeFlag(Debug.ScriptType.Normal); - var includeSource = false; - var idsToInclude = null; - if (request.arguments) { - // Pull out arguments. - if (!IS_UNDEFINED(request.arguments.types)) { - types = %ToNumber(request.arguments.types); - if (isNaN(types) || types < 0) { - return response.failed('Invalid types "' + request.arguments.types + '"'); - } - } - - if (!IS_UNDEFINED(request.arguments.includeSource)) { - includeSource = %ToBoolean(request.arguments.includeSource); - response.setOption('includeSource', includeSource); - } - - if (IS_ARRAY(request.arguments.ids)) { - idsToInclude = {}; - var ids = request.arguments.ids; - for (var i = 0; i < ids.length; i++) { - idsToInclude[ids[i]] = true; - } - } - } - - // Collect all scripts in the heap. - var scripts = %DebugGetLoadedScripts(); - - response.body = []; - - for (var i = 0; i < scripts.length; i++) { - if (idsToInclude && !idsToInclude[scripts[i].id]) { - continue; - } - if (types & ScriptTypeFlag(scripts[i].type)) { - response.body.push(MakeMirror(scripts[i])); - } - } -}; - - -DebugCommandProcessor.prototype.threadsRequest_ = function(request, response) { - // Get the number of threads. - var total_threads = this.exec_state_.threadCount(); - - // Get information for all threads. - var threads = []; - for (var i = 0; i < total_threads; i++) { - var details = %GetThreadDetails(this.exec_state_.break_id, i); - var thread_info = { current: details[0], - id: details[1] - } - threads.push(thread_info); - } - - // Create the response body. - response.body = { - totalThreads: total_threads, - threads: threads - } -}; - - -DebugCommandProcessor.prototype.suspendRequest_ = function(request, response) { - response.running = false; -}; - - -DebugCommandProcessor.prototype.versionRequest_ = function(request, response) { - response.body = { - V8Version: %GetV8Version() - } -}; - - -DebugCommandProcessor.prototype.profileRequest_ = function(request, response) { - if (!request.arguments) { - return response.failed('Missing arguments'); - } - var modules = parseInt(request.arguments.modules); - if (isNaN(modules)) { - return response.failed('Modules is not an integer'); - } - if (request.arguments.command == 'resume') { - %ProfilerResume(modules); - } else if (request.arguments.command == 'pause') { - %ProfilerPause(modules); - } else { - return response.failed('Unknown command'); - } - response.body = {}; -}; - - -// Check whether the previously processed command caused the VM to become -// running. -DebugCommandProcessor.prototype.isRunning = function() { - return this.running_; -} - - -DebugCommandProcessor.prototype.systemBreak = function(cmd, args) { - return %SystemBreak(); -}; - - -function NumberToHex8Str(n) { - var r = ""; - for (var i = 0; i < 8; ++i) { - var c = hexCharArray[n & 0x0F]; // hexCharArray is defined in uri.js - r = c + r; - n = n >>> 4; - } - return r; -}; - -DebugCommandProcessor.prototype.formatCFrames = function(cframes_value) { - var result = ""; - if (cframes_value == null || cframes_value.length == 0) { - result += "(stack empty)"; - } else { - for (var i = 0; i < cframes_value.length; ++i) { - if (i != 0) result += "\n"; - result += this.formatCFrame(cframes_value[i]); - } - } - return result; -}; - - -DebugCommandProcessor.prototype.formatCFrame = function(cframe_value) { - var result = ""; - result += "0x" + NumberToHex8Str(cframe_value.address); - if (!IS_UNDEFINED(cframe_value.text)) { - result += " " + cframe_value.text; - } - return result; -} - - -/** - * Convert an Object to its debugger protocol representation. The representation - * may be serilized to a JSON object using JSON.stringify(). - * This implementation simply runs through all string property names, converts - * each property value to a protocol value and adds the property to the result - * object. For type "object" the function will be called recursively. Note that - * circular structures will cause infinite recursion. - * @param {Object} object The object to format as protocol object. - * @param {MirrorSerializer} mirror_serializer The serializer to use if any - * mirror objects are encountered. - * @return {Object} Protocol object value. - */ -function ObjectToProtocolObject_(object, mirror_serializer) { - var content = {}; - for (var key in object) { - // Only consider string keys. - if (typeof key == 'string') { - // Format the value based on its type. - var property_value_json = ValueToProtocolValue_(object[key], - mirror_serializer); - // Add the property if relevant. - if (!IS_UNDEFINED(property_value_json)) { - content[key] = property_value_json; - } - } - } - - return content; -} - - -/** - * Convert an array to its debugger protocol representation. It will convert - * each array element to a protocol value. - * @param {Array} array The array to format as protocol array. - * @param {MirrorSerializer} mirror_serializer The serializer to use if any - * mirror objects are encountered. - * @return {Array} Protocol array value. - */ -function ArrayToProtocolArray_(array, mirror_serializer) { - var json = []; - for (var i = 0; i < array.length; i++) { - json.push(ValueToProtocolValue_(array[i], mirror_serializer)); - } - return json; -} - - -/** - * Convert a value to its debugger protocol representation. - * @param {*} value The value to format as protocol value. - * @param {MirrorSerializer} mirror_serializer The serializer to use if any - * mirror objects are encountered. - * @return {*} Protocol value. - */ -function ValueToProtocolValue_(value, mirror_serializer) { - // Format the value based on its type. - var json; - switch (typeof value) { - case 'object': - if (value instanceof Mirror) { - json = mirror_serializer.serializeValue(value); - } else if (IS_ARRAY(value)){ - json = ArrayToProtocolArray_(value, mirror_serializer); - } else { - json = ObjectToProtocolObject_(value, mirror_serializer); - } - break; - - case 'boolean': - case 'string': - case 'number': - json = value; - break - - default: - json = null; - } - return json; -} diff --git a/src/debug.cc b/src/debug.cc index 68f8d1e2..c71a98fc 100644 --- a/src/debug.cc +++ b/src/debug.cc @@ -31,6 +31,7 @@ #include "arguments.h" #include "bootstrapper.h" #include "code-stubs.h" +#include "codegen.h" #include "compilation-cache.h" #include "compiler.h" #include "debug.h" @@ -453,15 +454,7 @@ void BreakLocationIterator::ClearDebugBreakAtIC() { bool BreakLocationIterator::IsDebuggerStatement() { - if (RelocInfo::IsCodeTarget(rmode())) { - Address target = original_rinfo()->target_address(); - Code* code = Code::GetCodeFromTargetAddress(target); - if (code->kind() == Code::STUB) { - CodeStub::Major major_key = code->major_key(); - return (major_key == CodeStub::DebuggerStatement); - } - } - return false; + return RelocInfo::DEBUG_BREAK == rmode(); } @@ -696,6 +689,7 @@ bool Debug::CompileDebuggerScript(int index) { 0, NULL, NULL, + Handle<String>::null(), NATIVES_CODE); FLAG_allow_natives_syntax = allow_natives_syntax; diff --git a/src/execution.cc b/src/execution.cc index a79af237..20684136 100644 --- a/src/execution.cc +++ b/src/execution.cc @@ -91,7 +91,7 @@ static Handle<Object> Invoke(bool construct, JSEntryFunction entry = FUNCTION_CAST<JSEntryFunction>(code->entry()); // Call the function through the right JS entry stub. - byte* entry_address= func->code()->entry(); + byte* entry_address = func->code()->entry(); JSFunction* function = *func; Object* receiver_pointer = *receiver; value = CALL_GENERATED_CODE(entry, entry_address, function, diff --git a/src/fast-codegen.cc b/src/fast-codegen.cc index 36d7297f..602d6b88 100644 --- a/src/fast-codegen.cc +++ b/src/fast-codegen.cc @@ -89,10 +89,10 @@ void FastCodeGenSyntaxChecker::VisitDeclarations( void FastCodeGenSyntaxChecker::VisitStatements(ZoneList<Statement*>* stmts) { - for (int i = 0, len = stmts->length(); i < len; i++) { - Visit(stmts->at(i)); - CHECK_BAILOUT; + if (stmts->length() != 1) { + BAILOUT("Function body is not a singleton statement."); } + Visit(stmts->at(0)); } @@ -220,8 +220,16 @@ void FastCodeGenSyntaxChecker::VisitVariableProxy(VariableProxy* expr) { if (info()->has_global_object()) { LookupResult lookup; info()->global_object()->Lookup(*expr->name(), &lookup); - if (!lookup.IsValid() || !lookup.IsDontDelete()) { - BAILOUT("Non-existing or deletable global variable"); + if (!lookup.IsProperty()) { + BAILOUT("Non-existing global variable"); + } + // We do not handle global variables with accessors or interceptors. + if (lookup.type() != NORMAL) { + BAILOUT("Global variable with accessors or interceptors."); + } + // We do not handle deletable global variables. + if (!lookup.IsDontDelete()) { + BAILOUT("Deletable global variable"); } } } @@ -276,6 +284,9 @@ void FastCodeGenSyntaxChecker::VisitAssignment(Assignment* expr) { Handle<String> name = Handle<String>::cast(key->handle()); LookupResult lookup; receiver->Lookup(*name, &lookup); + if (!lookup.IsProperty()) { + BAILOUT("Assigned property not found at compile time"); + } if (lookup.holder() != *receiver) BAILOUT("Non-own property assignment"); if (!lookup.type() == FIELD) BAILOUT("Non-field property assignment"); } else { @@ -293,7 +304,33 @@ void FastCodeGenSyntaxChecker::VisitThrow(Throw* expr) { void FastCodeGenSyntaxChecker::VisitProperty(Property* expr) { - BAILOUT("Property"); + // We support named this property references. + VariableProxy* proxy = expr->obj()->AsVariableProxy(); + if (proxy == NULL || !proxy->var()->is_this()) { + BAILOUT("Non-this-property reference"); + } + if (!expr->key()->IsPropertyName()) { + BAILOUT("Non-named-property reference"); + } + + // We will only specialize for fields on the object itself. + // Expression::IsPropertyName implies that the name is a literal + // symbol but we do not assume that. + Literal* key = expr->key()->AsLiteral(); + if (key != NULL && key->handle()->IsString()) { + Handle<Object> receiver = info()->receiver(); + Handle<String> name = Handle<String>::cast(key->handle()); + LookupResult lookup; + receiver->Lookup(*name, &lookup); + if (!lookup.IsProperty()) { + BAILOUT("Referenced property not found at compile time"); + } + if (lookup.holder() != *receiver) BAILOUT("Non-own property reference"); + if (!lookup.type() == FIELD) BAILOUT("Non-field property reference"); + } else { + UNREACHABLE(); + BAILOUT("Unexpected non-string-literal property key"); + } } @@ -323,7 +360,58 @@ void FastCodeGenSyntaxChecker::VisitCountOperation(CountOperation* expr) { void FastCodeGenSyntaxChecker::VisitBinaryOperation(BinaryOperation* expr) { - BAILOUT("BinaryOperation"); + // We support bitwise OR. + switch (expr->op()) { + case Token::COMMA: + BAILOUT("BinaryOperation COMMA"); + case Token::OR: + BAILOUT("BinaryOperation OR"); + case Token::AND: + BAILOUT("BinaryOperation AND"); + + case Token::BIT_OR: + // We support expressions nested on the left because they only require + // a pair of registers to keep all intermediate values in registers + // (i.e., the expression stack has height no more than two). + if (!expr->right()->IsLeaf()) BAILOUT("expression nested on right"); + + // We do not allow subexpressions with side effects because we + // (currently) bail out to the beginning of the full function. The + // only expressions with side effects that we would otherwise handle + // are assignments. + if (expr->left()->AsAssignment() != NULL || + expr->right()->AsAssignment() != NULL) { + BAILOUT("subexpression of binary operation has side effects"); + } + + Visit(expr->left()); + CHECK_BAILOUT; + Visit(expr->right()); + break; + + case Token::BIT_XOR: + BAILOUT("BinaryOperation BIT_XOR"); + case Token::BIT_AND: + BAILOUT("BinaryOperation BIT_AND"); + case Token::SHL: + BAILOUT("BinaryOperation SHL"); + case Token::SAR: + BAILOUT("BinaryOperation SAR"); + case Token::SHR: + BAILOUT("BinaryOperation SHR"); + case Token::ADD: + BAILOUT("BinaryOperation ADD"); + case Token::SUB: + BAILOUT("BinaryOperation SUB"); + case Token::MUL: + BAILOUT("BinaryOperation MUL"); + case Token::DIV: + BAILOUT("BinaryOperation DIV"); + case Token::MOD: + BAILOUT("BinaryOperation MOD"); + default: + UNREACHABLE(); + } } @@ -348,6 +436,9 @@ Handle<Code> FastCodeGenerator::MakeCode(CompilationInfo* info) { AstLabeler labeler; labeler.Label(info); + LivenessAnalyzer analyzer; + analyzer.Analyze(info->function()); + CodeGenerator::MakeCodePrologue(info); const int kInitialBufferSize = 4 * KB; @@ -365,7 +456,8 @@ Handle<Code> FastCodeGenerator::MakeCode(CompilationInfo* info) { // macro assembler. CodeGenerator cgen(&masm); CodeGeneratorScope scope(&cgen); - cgen.Generate(info, CodeGenerator::SECONDARY); + info->set_mode(CompilationInfo::SECONDARY); + cgen.Generate(info); if (cgen.HasStackOverflow()) { ASSERT(!Top::has_pending_exception()); return Handle<Code>::null(); @@ -489,21 +581,28 @@ void FastCodeGenerator::VisitSlot(Slot* expr) { void FastCodeGenerator::VisitVariableProxy(VariableProxy* expr) { ASSERT(expr->var()->is_global() && !expr->var()->is_this()); - Comment cmnt(masm(), ";; Global"); - if (FLAG_print_ir) { - SmartPointer<char> name = expr->name()->ToCString(); - PrintF("%d: t%d = Global(%s)\n", expr->num(), expr->num(), *name); - } - // Check if we can compile a global variable load directly from the cell. ASSERT(info()->has_global_object()); LookupResult lookup; info()->global_object()->Lookup(*expr->name(), &lookup); - // We only support DontDelete properties for now. - ASSERT(lookup.IsValid()); + // We only support normal (non-accessor/interceptor) DontDelete properties + // for now. + ASSERT(lookup.IsProperty()); + ASSERT_EQ(NORMAL, lookup.type()); ASSERT(lookup.IsDontDelete()); Handle<Object> cell(info()->global_object()->GetPropertyCell(&lookup)); - EmitGlobalVariableLoad(cell); + + // Global variable lookups do not have side effects, so we do not need to + // emit code if we are in an effect context. + if (!destination().is(no_reg)) { + Comment cmnt(masm(), ";; Global"); + if (FLAG_print_ir) { + SmartPointer<char> name = expr->name()->ToCString(); + PrintF("%d: t%d = Global(%s) // last_use = %d\n", expr->num(), + expr->num(), *name, expr->var_def()->last_use()->num()); + } + EmitGlobalVariableLoad(cell); + } } @@ -533,8 +632,13 @@ void FastCodeGenerator::VisitCatchExtensionObject(CatchExtensionObject* expr) { void FastCodeGenerator::VisitAssignment(Assignment* expr) { - // Known to be a simple this property assignment. - Visit(expr->value()); + // Known to be a simple this property assignment. Effectively a unary + // operation. + { Register my_destination = destination(); + set_destination(accumulator0()); + Visit(expr->value()); + set_destination(my_destination); + } Property* prop = expr->target()->AsProperty(); ASSERT_NOT_NULL(prop); @@ -544,11 +648,14 @@ void FastCodeGenerator::VisitAssignment(Assignment* expr) { Handle<String> name = Handle<String>::cast(prop->key()->AsLiteral()->handle()); - Comment cmnt(masm(), ";; Store(this)"); + Comment cmnt(masm(), ";; Store to this"); if (FLAG_print_ir) { SmartPointer<char> name_string = name->ToCString(); - PrintF("%d: t%d = Store(this, \"%s\", t%d)\n", - expr->num(), expr->num(), *name_string, expr->value()->num()); + PrintF("%d: ", expr->num()); + if (!destination().is(no_reg)) PrintF("t%d = ", expr->num()); + PrintF("Store(this, \"%s\", t%d) // last_use(this) = %d\n", *name_string, + expr->value()->num(), + expr->var_def()->last_use()->num()); } EmitThisPropertyStore(name); @@ -561,7 +668,22 @@ void FastCodeGenerator::VisitThrow(Throw* expr) { void FastCodeGenerator::VisitProperty(Property* expr) { - UNREACHABLE(); + ASSERT_NOT_NULL(expr->obj()->AsVariableProxy()); + ASSERT(expr->obj()->AsVariableProxy()->var()->is_this()); + ASSERT(expr->key()->IsPropertyName()); + if (!destination().is(no_reg)) { + Handle<String> name = + Handle<String>::cast(expr->key()->AsLiteral()->handle()); + + Comment cmnt(masm(), ";; Load from this"); + if (FLAG_print_ir) { + SmartPointer<char> name_string = name->ToCString(); + PrintF("%d: t%d = Load(this, \"%s\") // last_use(this) = %d\n", + expr->num(), expr->num(), *name_string, + expr->var_def()->last_use()->num()); + } + EmitThisPropertyLoad(name); + } } @@ -591,7 +713,26 @@ void FastCodeGenerator::VisitCountOperation(CountOperation* expr) { void FastCodeGenerator::VisitBinaryOperation(BinaryOperation* expr) { - UNREACHABLE(); + // We support limited binary operations: bitwise OR only allowed to be + // nested on the left. + ASSERT(expr->op() == Token::BIT_OR); + ASSERT(expr->right()->IsLeaf()); + + { Register my_destination = destination(); + set_destination(accumulator1()); + Visit(expr->left()); + set_destination(accumulator0()); + Visit(expr->right()); + set_destination(my_destination); + } + + Comment cmnt(masm(), ";; BIT_OR"); + if (FLAG_print_ir) { + PrintF("%d: ", expr->num()); + if (!destination().is(no_reg)) PrintF("t%d = ", expr->num()); + PrintF("BIT_OR(t%d, t%d)\n", expr->left()->num(), expr->right()->num()); + } + EmitBitOr(); } diff --git a/src/fast-codegen.h b/src/fast-codegen.h index cbcb5bfa..e96daf65 100644 --- a/src/fast-codegen.h +++ b/src/fast-codegen.h @@ -28,10 +28,15 @@ #ifndef V8_FAST_CODEGEN_H_ #define V8_FAST_CODEGEN_H_ +#if V8_TARGET_ARCH_IA32 +#include "ia32/fast-codegen-ia32.h" +#else + #include "v8.h" #include "ast.h" #include "compiler.h" +#include "list.h" namespace v8 { namespace internal { @@ -65,7 +70,9 @@ class FastCodeGenSyntaxChecker: public AstVisitor { class FastCodeGenerator: public AstVisitor { public: - explicit FastCodeGenerator(MacroAssembler* masm) : masm_(masm), info_(NULL) {} + explicit FastCodeGenerator(MacroAssembler* masm) + : masm_(masm), info_(NULL), destination_(no_reg), smi_bits_(0) { + } static Handle<Code> MakeCode(CompilationInfo* info); @@ -74,42 +81,73 @@ class FastCodeGenerator: public AstVisitor { private: MacroAssembler* masm() { return masm_; } CompilationInfo* info() { return info_; } - Label* bailout() { return &bailout_; } + + Register destination() { return destination_; } + void set_destination(Register reg) { destination_ = reg; } FunctionLiteral* function() { return info_->function(); } Scope* scope() { return info_->scope(); } + // Platform-specific fixed registers, all guaranteed distinct. + Register accumulator0(); + Register accumulator1(); + Register scratch0(); + Register scratch1(); + Register receiver_reg(); + Register context_reg(); + + Register other_accumulator(Register reg) { + ASSERT(reg.is(accumulator0()) || reg.is(accumulator1())); + return (reg.is(accumulator0())) ? accumulator1() : accumulator0(); + } + + // Flags are true if the respective register is statically known to hold a + // smi. We do not track every register, only the accumulator registers. + bool is_smi(Register reg) { + ASSERT(!reg.is(no_reg)); + return (smi_bits_ & reg.bit()) != 0; + } + void set_as_smi(Register reg) { + ASSERT(!reg.is(no_reg)); + smi_bits_ = smi_bits_ | reg.bit(); + } + void clear_as_smi(Register reg) { + ASSERT(!reg.is(no_reg)); + smi_bits_ = smi_bits_ & ~reg.bit(); + } + // AST node visit functions. #define DECLARE_VISIT(type) virtual void Visit##type(type* node); AST_NODE_LIST(DECLARE_VISIT) #undef DECLARE_VISIT - // Emit code to load the receiver from the stack into a given register. - void EmitLoadReceiver(Register reg); - - // Emit code to check that the receiver has the same map as the - // compile-time receiver. Receiver is expected in {ia32-edx, x64-rdx, - // arm-r1}. Emit a branch to the (single) bailout label if check fails. - void EmitReceiverMapCheck(); + // Emit code to load the receiver from the stack into receiver_reg. + void EmitLoadReceiver(); - // Emit code to check that the global object has the same map as the - // global object seen at compile time. - void EmitGlobalMapCheck(); - - // Emit code to load a global variable directly from a global - // property cell into {ia32-eax, x64-rax, arm-r0}. + // Emit code to load a global variable directly from a global property + // cell into the destination register. void EmitGlobalVariableLoad(Handle<Object> cell); // Emit a store to an own property of this. The stored value is expected - // in {ia32-eax, x64-rax, arm-r0} and the receiver in {is32-edx, x64-rdx, - // arm-r1}. Both are preserve. + // in accumulator0 and the receiver in receiver_reg. The receiver + // register is preserved and the result (the stored value) is left in the + // destination register. void EmitThisPropertyStore(Handle<String> name); - MacroAssembler* masm_; + // Emit a load from an own property of this. The receiver is expected in + // receiver_reg. The receiver register is preserved and the result is + // left in the destination register. + void EmitThisPropertyLoad(Handle<String> name); - CompilationInfo* info_; + // Emit a bitwise or operation. The left operand is in accumulator1 and + // the right is in accumulator0. The result should be left in the + // destination register. + void EmitBitOr(); - Label bailout_; + MacroAssembler* masm_; + CompilationInfo* info_; + Register destination_; + uint32_t smi_bits_; DISALLOW_COPY_AND_ASSIGN(FastCodeGenerator); }; @@ -117,4 +155,6 @@ class FastCodeGenerator: public AstVisitor { } } // namespace v8::internal +#endif // V8_TARGET_ARCH_IA32 + #endif // V8_FAST_CODEGEN_H_ diff --git a/src/flag-definitions.h b/src/flag-definitions.h index 9afdea4c..dbb9ce72 100644 --- a/src/flag-definitions.h +++ b/src/flag-definitions.h @@ -384,7 +384,7 @@ DEFINE_bool(prof_lazy, false, DEFINE_bool(log_regexp, false, "Log regular expression execution.") DEFINE_bool(sliding_state_window, false, "Update sliding state window counters.") -DEFINE_string(logfile, "/sdcard/v8.log", "Specify the name of the log file.") +DEFINE_string(logfile, "v8.log", "Specify the name of the log file.") DEFINE_bool(oprofile, false, "Enable JIT agent for OProfile.") // diff --git a/src/frame-element.cc b/src/frame-element.cc index e6bc2eaf..14555596 100644 --- a/src/frame-element.cc +++ b/src/frame-element.cc @@ -32,10 +32,6 @@ namespace v8 { namespace internal { -// ------------------------------------------------------------------------- -// FrameElement implementation. - - FrameElement::ZoneObjectList* FrameElement::ConstantList() { static ZoneObjectList list(10); return &list; diff --git a/src/frame-element.h b/src/frame-element.h index ccdecf1d..5762814f 100644 --- a/src/frame-element.h +++ b/src/frame-element.h @@ -28,7 +28,8 @@ #ifndef V8_FRAME_ELEMENT_H_ #define V8_FRAME_ELEMENT_H_ -#include "register-allocator-inl.h" +#include "number-info.h" +#include "macro-assembler.h" namespace v8 { namespace internal { @@ -52,11 +53,31 @@ class FrameElement BASE_EMBEDDED { SYNCED }; + inline NumberInfo::Type number_info() { + // Copied elements do not have number info. Instead + // we have to inspect their backing element in the frame. + ASSERT(!is_copy()); + if (!is_constant()) return NumberInfoField::decode(value_); + Handle<Object> value = handle(); + if (value->IsSmi()) return NumberInfo::kSmi; + if (value->IsHeapNumber()) return NumberInfo::kHeapNumber; + return NumberInfo::kUnknown; + } + + inline void set_number_info(NumberInfo::Type info) { + // Copied elements do not have number info. Instead + // we have to inspect their backing element in the frame. + ASSERT(!is_copy()); + value_ = value_ & ~NumberInfoField::mask(); + value_ = value_ | NumberInfoField::encode(info); + } + // The default constructor creates an invalid frame element. FrameElement() { value_ = TypeField::encode(INVALID) | CopiedField::encode(false) | SyncedField::encode(false) + | NumberInfoField::encode(NumberInfo::kUninitialized) | DataField::encode(0); } @@ -67,15 +88,16 @@ class FrameElement BASE_EMBEDDED { } // Factory function to construct an in-memory frame element. - static FrameElement MemoryElement() { - FrameElement result(MEMORY, no_reg, SYNCED); + static FrameElement MemoryElement(NumberInfo::Type info) { + FrameElement result(MEMORY, no_reg, SYNCED, info); return result; } // Factory function to construct an in-register frame element. static FrameElement RegisterElement(Register reg, - SyncFlag is_synced) { - return FrameElement(REGISTER, reg, is_synced); + SyncFlag is_synced, + NumberInfo::Type info) { + return FrameElement(REGISTER, reg, is_synced, info); } // Factory function to construct a frame element whose value is known at @@ -185,10 +207,14 @@ class FrameElement BASE_EMBEDDED { }; // Used to construct memory and register elements. - FrameElement(Type type, Register reg, SyncFlag is_synced) { + FrameElement(Type type, + Register reg, + SyncFlag is_synced, + NumberInfo::Type info) { value_ = TypeField::encode(type) | CopiedField::encode(false) | SyncedField::encode(is_synced != NOT_SYNCED) + | NumberInfoField::encode(info) | DataField::encode(reg.code_ > 0 ? reg.code_ : 0); } @@ -197,6 +223,7 @@ class FrameElement BASE_EMBEDDED { value_ = TypeField::encode(CONSTANT) | CopiedField::encode(false) | SyncedField::encode(is_synced != NOT_SYNCED) + | NumberInfoField::encode(NumberInfo::kUninitialized) | DataField::encode(ConstantList()->length()); ConstantList()->Add(value); } @@ -223,9 +250,10 @@ class FrameElement BASE_EMBEDDED { uint32_t value_; class TypeField: public BitField<Type, 0, 3> {}; - class CopiedField: public BitField<uint32_t, 3, 1> {}; - class SyncedField: public BitField<uint32_t, 4, 1> {}; - class DataField: public BitField<uint32_t, 5, 32 - 6> {}; + class CopiedField: public BitField<bool, 3, 1> {}; + class SyncedField: public BitField<bool, 4, 1> {}; + class NumberInfoField: public BitField<NumberInfo::Type, 5, 3> {}; + class DataField: public BitField<uint32_t, 8, 32 - 8> {}; friend class VirtualFrame; }; diff --git a/src/frames.cc b/src/frames.cc index 05507406..06896ea7 100644 --- a/src/frames.cc +++ b/src/frames.cc @@ -410,12 +410,7 @@ Object*& ExitFrame::code_slot() const { Code* ExitFrame::code() const { - Object* code = code_slot(); - if (code->IsSmi()) { - return Heap::debugger_statement_code(); - } else { - return Code::cast(code); - } + return Code::cast(code_slot()); } diff --git a/src/full-codegen.cc b/src/full-codegen.cc index 22510e9d..63714392 100644 --- a/src/full-codegen.cc +++ b/src/full-codegen.cc @@ -32,6 +32,7 @@ #include "full-codegen.h" #include "stub-cache.h" #include "debug.h" +#include "liveedit.h" namespace v8 { namespace internal { @@ -448,6 +449,8 @@ Handle<Code> FullCodeGenerator::MakeCode(CompilationInfo* info) { CodeGenerator::MakeCodePrologue(info); const int kInitialBufferSize = 4 * KB; MacroAssembler masm(NULL, kInitialBufferSize); + LiveEditFunctionTracker live_edit_tracker(info->function()); + FullCodeGenerator cgen(&masm); cgen.Generate(info, PRIMARY); if (cgen.HasStackOverflow()) { @@ -455,7 +458,9 @@ Handle<Code> FullCodeGenerator::MakeCode(CompilationInfo* info) { return Handle<Code>::null(); } Code::Flags flags = Code::ComputeFlags(Code::FUNCTION, NOT_IN_LOOP); - return CodeGenerator::MakeCodeEpilogue(&masm, flags, info); + Handle<Code> result = CodeGenerator::MakeCodeEpilogue(&masm, flags, info); + live_edit_tracker.RecordFunctionCode(result); + return result; } @@ -986,8 +991,7 @@ void FullCodeGenerator::VisitDebuggerStatement(DebuggerStatement* stmt) { Comment cmnt(masm_, "[ DebuggerStatement"); SetStatementPosition(stmt); - DebuggerStatementStub ces; - __ CallStub(&ces); + __ DebugBreak(); // Ignore the return value. #endif } @@ -1032,86 +1036,6 @@ void FullCodeGenerator::VisitLiteral(Literal* expr) { } -void FullCodeGenerator::VisitAssignment(Assignment* expr) { - Comment cmnt(masm_, "[ Assignment"); - ASSERT(expr->op() != Token::INIT_CONST); - // Left-hand side can only be a property, a global or a (parameter or local) - // slot. Variables with rewrite to .arguments are treated as KEYED_PROPERTY. - enum LhsKind { VARIABLE, NAMED_PROPERTY, KEYED_PROPERTY }; - LhsKind assign_type = VARIABLE; - Property* prop = expr->target()->AsProperty(); - if (prop != NULL) { - assign_type = - (prop->key()->IsPropertyName()) ? NAMED_PROPERTY : KEYED_PROPERTY; - } - - // Evaluate LHS expression. - switch (assign_type) { - case VARIABLE: - // Nothing to do here. - break; - case NAMED_PROPERTY: - VisitForValue(prop->obj(), kStack); - break; - case KEYED_PROPERTY: - VisitForValue(prop->obj(), kStack); - VisitForValue(prop->key(), kStack); - break; - } - - // If we have a compound assignment: Get value of LHS expression and - // store in on top of the stack. - if (expr->is_compound()) { - Location saved_location = location_; - location_ = kStack; - switch (assign_type) { - case VARIABLE: - EmitVariableLoad(expr->target()->AsVariableProxy()->var(), - Expression::kValue); - break; - case NAMED_PROPERTY: - EmitNamedPropertyLoad(prop); - __ push(result_register()); - break; - case KEYED_PROPERTY: - EmitKeyedPropertyLoad(prop); - __ push(result_register()); - break; - } - location_ = saved_location; - } - - // Evaluate RHS expression. - Expression* rhs = expr->value(); - VisitForValue(rhs, kAccumulator); - - // If we have a compound assignment: Apply operator. - if (expr->is_compound()) { - Location saved_location = location_; - location_ = kAccumulator; - EmitBinaryOp(expr->binary_op(), Expression::kValue); - location_ = saved_location; - } - - // Record source position before possible IC call. - SetSourcePosition(expr->position()); - - // Store the value. - switch (assign_type) { - case VARIABLE: - EmitVariableAssignment(expr->target()->AsVariableProxy()->var(), - context_); - break; - case NAMED_PROPERTY: - EmitNamedPropertyAssignment(expr); - break; - case KEYED_PROPERTY: - EmitKeyedPropertyAssignment(expr); - break; - } -} - - void FullCodeGenerator::VisitCatchExtensionObject(CatchExtensionObject* expr) { // Call runtime routine to allocate the catch extension object and // assign the exception value to the catch variable. diff --git a/src/handles.cc b/src/handles.cc index 05f561ef..c9a28774 100644 --- a/src/handles.cc +++ b/src/handles.cc @@ -309,6 +309,12 @@ Handle<Object> GetPrototype(Handle<Object> obj) { } +Handle<Object> SetPrototype(Handle<JSObject> obj, Handle<Object> value) { + const bool skip_hidden_prototypes = false; + CALL_HEAP_FUNCTION(obj->SetPrototype(*value, skip_hidden_prototypes), Object); +} + + Handle<Object> GetHiddenProperties(Handle<JSObject> obj, bool create_if_needed) { Object* holder = obj->BypassGlobalProxy(); @@ -486,25 +492,25 @@ void InitScriptLineEnds(Handle<Script> script) { int GetScriptLineNumber(Handle<Script> script, int code_pos) { InitScriptLineEnds(script); AssertNoAllocation no_allocation; - FixedArray* line_ends_array = - FixedArray::cast(script->line_ends()); + FixedArray* line_ends_array = FixedArray::cast(script->line_ends()); const int line_ends_len = line_ends_array->length(); - int line = -1; - if (line_ends_len > 0 && - code_pos <= (Smi::cast(line_ends_array->get(0)))->value()) { - line = 0; - } else { - for (int i = 1; i < line_ends_len; ++i) { - if ((Smi::cast(line_ends_array->get(i - 1)))->value() < code_pos && - code_pos <= (Smi::cast(line_ends_array->get(i)))->value()) { - line = i; - break; - } + if (!line_ends_len) + return -1; + + if ((Smi::cast(line_ends_array->get(0)))->value() >= code_pos) + return script->line_offset()->value(); + + int left = 0; + int right = line_ends_len; + while (int half = (right - left) / 2) { + if ((Smi::cast(line_ends_array->get(left + half)))->value() > code_pos) { + right -= half; + } else { + left += half; } } - - return line != -1 ? line + script->line_offset()->value() : line; + return right + script->line_offset()->value(); } diff --git a/src/handles.h b/src/handles.h index caa99664..79029095 100644 --- a/src/handles.h +++ b/src/handles.h @@ -245,6 +245,8 @@ Handle<Object> GetPropertyWithInterceptor(Handle<JSObject> receiver, Handle<Object> GetPrototype(Handle<Object> obj); +Handle<Object> SetPrototype(Handle<JSObject> obj, Handle<Object> value); + // Return the object's hidden properties object. If the object has no hidden // properties and create_if_needed is true, then a new hidden property object // will be allocated. Otherwise the Heap::undefined_value is returned. diff --git a/src/heap.cc b/src/heap.cc index 7263e230..cfb786ac 100644 --- a/src/heap.cc +++ b/src/heap.cc @@ -1498,14 +1498,6 @@ void Heap::CreateRegExpCEntryStub() { #endif -#ifdef ENABLE_DEBUGGER_SUPPORT -void Heap::CreateCEntryDebugBreakStub() { - DebuggerStatementStub stub; - set_debugger_statement_code(*stub.GetCode()); -} -#endif - - void Heap::CreateJSEntryStub() { JSEntryStub stub; set_js_entry_code(*stub.GetCode()); @@ -1533,9 +1525,6 @@ void Heap::CreateFixedStubs() { // } // To workaround the problem, make separate functions without inlining. Heap::CreateCEntryStub(); -#ifdef ENABLE_DEBUGGER_SUPPORT - Heap::CreateCEntryDebugBreakStub(); -#endif Heap::CreateJSEntryStub(); Heap::CreateJSConstructEntryStub(); #if V8_TARGET_ARCH_ARM && V8_NATIVE_REGEXP @@ -1778,6 +1767,7 @@ Object* Heap::SmiOrNumberFromDouble(double value, Object* Heap::NumberToString(Object* number) { + Counters::number_to_string_runtime.Increment(); Object* cached = GetNumberStringCache(number); if (cached != undefined_value()) { return cached; @@ -2393,12 +2383,13 @@ Object* Heap::AllocateInitialMap(JSFunction* fun) { map->set_unused_property_fields(in_object_properties); map->set_prototype(prototype); - // If the function has only simple this property assignments add field - // descriptors for these to the initial map as the object cannot be - // constructed without having these properties. + // If the function has only simple this property assignments add + // field descriptors for these to the initial map as the object + // cannot be constructed without having these properties. Guard by + // the inline_new flag so we only change the map if we generate a + // specialized construct stub. ASSERT(in_object_properties <= Map::kMaxPreAllocatedPropertyFields); - if (fun->shared()->has_only_simple_this_property_assignments() && - fun->shared()->this_property_assignments_count() > 0) { + if (fun->shared()->CanGenerateInlineConstructor(prototype)) { int count = fun->shared()->this_property_assignments_count(); if (count > in_object_properties) { count = in_object_properties; @@ -4120,7 +4111,7 @@ int KeyedLookupCache::Hash(Map* map, String* name) { // Uses only lower 32 bits if pointers are larger. uintptr_t addr_hash = static_cast<uint32_t>(reinterpret_cast<uintptr_t>(map)) >> kMapHashShift; - return (addr_hash ^ name->Hash()) & kCapacityMask; + return static_cast<uint32_t>((addr_hash ^ name->Hash()) & kCapacityMask); } @@ -101,7 +101,6 @@ namespace internal { V(Code, js_entry_code, JsEntryCode) \ V(Code, js_construct_entry_code, JsConstructEntryCode) \ V(Code, c_entry_code, CEntryCode) \ - V(Code, debugger_statement_code, DebuggerStatementCode) \ V(FixedArray, number_string_cache, NumberStringCache) \ V(FixedArray, single_character_string_cache, SingleCharacterStringCache) \ V(FixedArray, natives_source_cache, NativesSourceCache) \ @@ -1051,7 +1050,6 @@ class Heap : public AllStatic { // These four Create*EntryStub functions are here because of a gcc-4.4 bug // that assigns wrong vtable entries. static void CreateCEntryStub(); - static void CreateCEntryDebugBreakStub(); static void CreateJSEntryStub(); static void CreateJSConstructEntryStub(); static void CreateRegExpCEntryStub(); @@ -1390,9 +1388,9 @@ class DescriptorLookupCache { private: static int Hash(DescriptorArray* array, String* name) { // Uses only lower 32 bits if pointers are larger. - uintptr_t array_hash = + uint32_t array_hash = static_cast<uint32_t>(reinterpret_cast<uintptr_t>(array)) >> 2; - uintptr_t name_hash = + uint32_t name_hash = static_cast<uint32_t>(reinterpret_cast<uintptr_t>(name)) >> 2; return (array_hash ^ name_hash) % kLength; } @@ -1617,6 +1615,7 @@ class TranscendentalCache { if (e.in[0] == c.integers[0] && e.in[1] == c.integers[1]) { ASSERT(e.output != NULL); + Counters::transcendental_cache_hit.Increment(); return e.output; } double answer = Calculate(input); @@ -1626,6 +1625,7 @@ class TranscendentalCache { elements_[hash].in[1] = c.integers[1]; elements_[hash].output = heap_number; } + Counters::transcendental_cache_miss.Increment(); return heap_number; } @@ -1666,6 +1666,17 @@ class TranscendentalCache { hash ^= hash >> 8; return (hash & (kCacheSize - 1)); } + + static Address cache_array_address() { + // Used to create an external reference. + return reinterpret_cast<Address>(caches_); + } + + // Allow access to the caches_ array as an ExternalReference. + friend class ExternalReference; + // Inline implementation of the caching. + friend class TranscendentalCacheStub; + static TranscendentalCache* caches_[kNumberOfCaches]; Element elements_[kCacheSize]; Type type_; diff --git a/src/ia32/assembler-ia32.cc b/src/ia32/assembler-ia32.cc index ffcefe0b..89708aae 100644 --- a/src/ia32/assembler-ia32.cc +++ b/src/ia32/assembler-ia32.cc @@ -1637,6 +1637,13 @@ void Assembler::fld(int i) { } +void Assembler::fstp(int i) { + EnsureSpace ensure_space(this); + last_pc_ = pc_; + emit_farith(0xDD, 0xD8, i); +} + + void Assembler::fld1() { EnsureSpace ensure_space(this); last_pc_ = pc_; @@ -1645,6 +1652,14 @@ void Assembler::fld1() { } +void Assembler::fldpi() { + EnsureSpace ensure_space(this); + last_pc_ = pc_; + EMIT(0xD9); + EMIT(0xEB); +} + + void Assembler::fldz() { EnsureSpace ensure_space(this); last_pc_ = pc_; @@ -1685,6 +1700,14 @@ void Assembler::fstp_d(const Operand& adr) { } +void Assembler::fst_d(const Operand& adr) { + EnsureSpace ensure_space(this); + last_pc_ = pc_; + EMIT(0xDD); + emit_operand(edx, adr); +} + + void Assembler::fild_s(const Operand& adr) { EnsureSpace ensure_space(this); last_pc_ = pc_; diff --git a/src/ia32/assembler-ia32.h b/src/ia32/assembler-ia32.h index f0cf4f11..36aad5e5 100644 --- a/src/ia32/assembler-ia32.h +++ b/src/ia32/assembler-ia32.h @@ -231,7 +231,8 @@ enum ScaleFactor { times_8 = 3, times_int_size = times_4, times_half_pointer_size = times_2, - times_pointer_size = times_4 + times_pointer_size = times_4, + times_twice_pointer_size = times_8 }; @@ -667,6 +668,7 @@ class Assembler : public Malloced { void call(Label* L); void call(byte* entry, RelocInfo::Mode rmode); void call(const Operand& adr); + void call(const ExternalReference& target); void call(Handle<Code> code, RelocInfo::Mode rmode); // Jumps @@ -682,15 +684,18 @@ class Assembler : public Malloced { // Floating-point operations void fld(int i); + void fstp(int i); void fld1(); void fldz(); + void fldpi(); void fld_s(const Operand& adr); void fld_d(const Operand& adr); void fstp_s(const Operand& adr); void fstp_d(const Operand& adr); + void fst_d(const Operand& adr); void fild_s(const Operand& adr); void fild_d(const Operand& adr); diff --git a/src/ia32/builtins-ia32.cc b/src/ia32/builtins-ia32.cc index 2c5b1d1f..54ef382a 100644 --- a/src/ia32/builtins-ia32.cc +++ b/src/ia32/builtins-ia32.cc @@ -93,7 +93,10 @@ void Builtins::Generate_JSConstructCall(MacroAssembler* masm) { // edi: called object // eax: number of arguments __ bind(&non_function_call); - + // CALL_NON_FUNCTION expects the non-function constructor as receiver + // (instead of the original receiver from the call site). The receiver is + // stack element argc+1. + __ mov(Operand(esp, eax, times_4, kPointerSize), edi); // Set expected number of arguments to zero (not changing eax). __ Set(ebx, Immediate(0)); __ GetBuiltinEntry(edx, Builtins::CALL_NON_FUNCTION_AS_CONSTRUCTOR); @@ -437,33 +440,26 @@ void Builtins::Generate_FunctionCall(MacroAssembler* masm) { __ bind(&done); } - // 2. Get the function to call from the stack. - { Label done, non_function, function; - // +1 ~ return address. - __ mov(edi, Operand(esp, eax, times_4, +1 * kPointerSize)); - __ test(edi, Immediate(kSmiTagMask)); - __ j(zero, &non_function, not_taken); - __ CmpObjectType(edi, JS_FUNCTION_TYPE, ecx); - __ j(equal, &function, taken); - - // Non-function called: Clear the function to force exception. - __ bind(&non_function); - __ xor_(edi, Operand(edi)); - __ jmp(&done); - - // Function called: Change context eagerly to get the right global object. - __ bind(&function); - __ mov(esi, FieldOperand(edi, JSFunction::kContextOffset)); + // 2. Get the function to call (passed as receiver) from the stack, check + // if it is a function. + Label non_function; + // 1 ~ return address. + __ mov(edi, Operand(esp, eax, times_4, 1 * kPointerSize)); + __ test(edi, Immediate(kSmiTagMask)); + __ j(zero, &non_function, not_taken); + __ CmpObjectType(edi, JS_FUNCTION_TYPE, ecx); + __ j(not_equal, &non_function, not_taken); - __ bind(&done); - } - // 3. Make sure first argument is an object; convert if necessary. - { Label call_to_object, use_global_receiver, patch_receiver, done; - __ mov(ebx, Operand(esp, eax, times_4, 0)); + // 3a. Patch the first argument if necessary when calling a function. + Label shift_arguments; + { Label convert_to_object, use_global_receiver, patch_receiver; + // Change context eagerly in case we need the global receiver. + __ mov(esi, FieldOperand(edi, JSFunction::kContextOffset)); + __ mov(ebx, Operand(esp, eax, times_4, 0)); // First argument. __ test(ebx, Immediate(kSmiTagMask)); - __ j(zero, &call_to_object); + __ j(zero, &convert_to_object); __ cmp(ebx, Factory::null_value()); __ j(equal, &use_global_receiver); @@ -473,31 +469,28 @@ void Builtins::Generate_FunctionCall(MacroAssembler* masm) { __ mov(ecx, FieldOperand(ebx, HeapObject::kMapOffset)); __ movzx_b(ecx, FieldOperand(ecx, Map::kInstanceTypeOffset)); __ cmp(ecx, FIRST_JS_OBJECT_TYPE); - __ j(less, &call_to_object); + __ j(below, &convert_to_object); __ cmp(ecx, LAST_JS_OBJECT_TYPE); - __ j(less_equal, &done); + __ j(below_equal, &shift_arguments); - __ bind(&call_to_object); - __ EnterInternalFrame(); // preserves eax, ebx, edi - - // Store the arguments count on the stack (smi tagged). + __ bind(&convert_to_object); + __ EnterInternalFrame(); // In order to preserve argument count. __ SmiTag(eax); __ push(eax); - __ push(edi); // save edi across the call __ push(ebx); __ InvokeBuiltin(Builtins::TO_OBJECT, CALL_FUNCTION); __ mov(ebx, eax); - __ pop(edi); // restore edi after the call - // Get the arguments count and untag it. __ pop(eax); __ SmiUntag(eax); - __ LeaveInternalFrame(); + // Restore the function to edi. + __ mov(edi, Operand(esp, eax, times_4, 1 * kPointerSize)); __ jmp(&patch_receiver); - // Use the global receiver object from the called function as the receiver. + // Use the global receiver object from the called function as the + // receiver. __ bind(&use_global_receiver); const int kGlobalIndex = Context::kHeaderSize + Context::GLOBAL_INDEX * kPointerSize; @@ -509,50 +502,55 @@ void Builtins::Generate_FunctionCall(MacroAssembler* masm) { __ bind(&patch_receiver); __ mov(Operand(esp, eax, times_4, 0), ebx); - __ bind(&done); + __ jmp(&shift_arguments); } - // 4. Check that the function really is a function. - { Label done; - __ test(edi, Operand(edi)); - __ j(not_zero, &done, taken); - __ xor_(ebx, Operand(ebx)); - // CALL_NON_FUNCTION will expect to find the non-function callee on the - // expression stack of the caller. Transfer it from receiver to the - // caller's expression stack (and make the first argument the receiver - // for CALL_NON_FUNCTION) by decrementing the argument count. - __ dec(eax); - __ GetBuiltinEntry(edx, Builtins::CALL_NON_FUNCTION); - __ jmp(Handle<Code>(builtin(ArgumentsAdaptorTrampoline)), - RelocInfo::CODE_TARGET); - __ bind(&done); - } - - // 5. Shift arguments and return address one slot down on the stack - // (overwriting the receiver). + // 3b. Patch the first argument when calling a non-function. The + // CALL_NON_FUNCTION builtin expects the non-function callee as + // receiver, so overwrite the first argument which will ultimately + // become the receiver. + __ bind(&non_function); + __ mov(Operand(esp, eax, times_4, 0), edi); + // Clear edi to indicate a non-function being called. + __ xor_(edi, Operand(edi)); + + // 4. Shift arguments and return address one slot down on the stack + // (overwriting the original receiver). Adjust argument count to make + // the original first argument the new receiver. + __ bind(&shift_arguments); { Label loop; __ mov(ecx, eax); __ bind(&loop); __ mov(ebx, Operand(esp, ecx, times_4, 0)); __ mov(Operand(esp, ecx, times_4, kPointerSize), ebx); __ dec(ecx); - __ j(not_sign, &loop); + __ j(not_sign, &loop); // While non-negative (to copy return address). __ pop(ebx); // Discard copy of return address. __ dec(eax); // One fewer argument (first argument is new receiver). } - // 6. Get the code to call from the function and check that the number of - // expected arguments matches what we're providing. - { __ mov(edx, FieldOperand(edi, JSFunction::kSharedFunctionInfoOffset)); - __ mov(ebx, - FieldOperand(edx, SharedFunctionInfo::kFormalParameterCountOffset)); - __ mov(edx, FieldOperand(edx, SharedFunctionInfo::kCodeOffset)); - __ lea(edx, FieldOperand(edx, Code::kHeaderSize)); - __ cmp(eax, Operand(ebx)); - __ j(not_equal, Handle<Code>(builtin(ArgumentsAdaptorTrampoline))); + // 5a. Call non-function via tail call to CALL_NON_FUNCTION builtin. + { Label function; + __ test(edi, Operand(edi)); + __ j(not_zero, &function, taken); + __ xor_(ebx, Operand(ebx)); + __ GetBuiltinEntry(edx, Builtins::CALL_NON_FUNCTION); + __ jmp(Handle<Code>(builtin(ArgumentsAdaptorTrampoline)), + RelocInfo::CODE_TARGET); + __ bind(&function); } - // 7. Jump (tail-call) to the code in register edx without checking arguments. + // 5b. Get the code to call from the function and check that the number of + // expected arguments matches what we're providing. If so, jump + // (tail-call) to the code in register edx without checking arguments. + __ mov(edx, FieldOperand(edi, JSFunction::kSharedFunctionInfoOffset)); + __ mov(ebx, + FieldOperand(edx, SharedFunctionInfo::kFormalParameterCountOffset)); + __ mov(edx, FieldOperand(edx, SharedFunctionInfo::kCodeOffset)); + __ lea(edx, FieldOperand(edx, Code::kHeaderSize)); + __ cmp(eax, Operand(ebx)); + __ j(not_equal, Handle<Code>(builtin(ArgumentsAdaptorTrampoline))); + ParameterCount expected(0); __ InvokeCode(Operand(edx), expected, expected, JUMP_FUNCTION); } @@ -647,9 +645,7 @@ void Builtins::Generate_FunctionApply(MacroAssembler* masm) { __ mov(eax, Operand(ebp, kIndexOffset)); __ jmp(&entry); __ bind(&loop); - __ mov(ecx, Operand(ebp, 2 * kPointerSize)); // load arguments - __ push(ecx); - __ push(eax); + __ mov(edx, Operand(ebp, 2 * kPointerSize)); // load arguments // Use inline caching to speed up access to arguments. Handle<Code> ic(Builtins::builtin(Builtins::KeyedLoadIC_Initialize)); @@ -659,8 +655,7 @@ void Builtins::Generate_FunctionApply(MacroAssembler* masm) { // we have generated an inline version of the keyed load. In this // case, we know that we are not generating a test instruction next. - // Remove IC arguments from the stack and push the nth argument. - __ add(Operand(esp), Immediate(2 * kPointerSize)); + // Push the nth argument. __ push(eax); // Update the index on the stack and in register eax. diff --git a/src/ia32/codegen-ia32.cc b/src/ia32/codegen-ia32.cc index d0fbabba..ecb4c497 100644 --- a/src/ia32/codegen-ia32.cc +++ b/src/ia32/codegen-ia32.cc @@ -125,7 +125,7 @@ Scope* CodeGenerator::scope() { return info_->function()->scope(); } // edi: called JS function // esi: callee's context -void CodeGenerator::Generate(CompilationInfo* info, Mode mode) { +void CodeGenerator::Generate(CompilationInfo* info) { // Record the position for debugging purposes. CodeForFunctionPosition(info->function()); @@ -164,7 +164,7 @@ void CodeGenerator::Generate(CompilationInfo* info, Mode mode) { // esi: callee's context allocator_->Initialize(); - if (mode == PRIMARY) { + if (info->mode() == CompilationInfo::PRIMARY) { frame_->Enter(); // Allocate space for locals and initialize them. @@ -255,6 +255,12 @@ void CodeGenerator::Generate(CompilationInfo* info, Mode mode) { // frame to match this state. frame_->Adjust(3); allocator_->Unuse(edi); + + // Bind all the bailout labels to the beginning of the function. + List<CompilationInfo::Bailout*>* bailouts = info->bailouts(); + for (int i = 0; i < bailouts->length(); i++) { + __ bind(bailouts->at(i)->label()); + } } // Initialize the function return target after the locals are set @@ -574,7 +580,9 @@ void CodeGenerator::LoadTypeofExpression(Expression* expr) { } else if (variable != NULL && variable->slot() != NULL) { // For a variable that rewrites to a slot, we signal it is the immediate // subexpression of a typeof. - LoadFromSlotCheckForArguments(variable->slot(), INSIDE_TYPEOF); + Result result = + LoadFromSlotCheckForArguments(variable->slot(), INSIDE_TYPEOF); + frame()->Push(&result); } else { // Anything else can be handled normally. Load(expr); @@ -623,8 +631,7 @@ Result CodeGenerator::StoreArgumentsObject(bool initial) { // We have to skip storing into the arguments slot if it has already // been written to. This can happen if the a function has a local // variable named 'arguments'. - LoadFromSlot(arguments->slot(), NOT_INSIDE_TYPEOF); - Result probe = frame_->Pop(); + Result probe = LoadFromSlot(arguments->slot(), NOT_INSIDE_TYPEOF); if (probe.is_constant()) { // We have to skip updating the arguments object if it has // been assigned a proper value. @@ -688,6 +695,11 @@ void CodeGenerator::LoadReference(Reference* ref) { // The expression is a variable proxy that does not rewrite to a // property. Global variables are treated as named property references. if (var->is_global()) { + // If eax is free, the register allocator prefers it. Thus the code + // generator will load the global object into eax, which is where + // LoadIC wants it. Most uses of Reference call LoadIC directly + // after the reference is created. + frame_->Spill(eax); LoadGlobal(); ref->set_type(Reference::NAMED); } else { @@ -721,35 +733,54 @@ void CodeGenerator::ToBoolean(ControlDestination* dest) { // The value to convert should be popped from the frame. Result value = frame_->Pop(); value.ToRegister(); - // Fast case checks. - // 'false' => false. - __ cmp(value.reg(), Factory::false_value()); - dest->false_target()->Branch(equal); + if (value.is_number()) { + Comment cmnt(masm_, "ONLY_NUMBER"); + // Fast case if NumberInfo indicates only numbers. + if (FLAG_debug_code) { + __ AbortIfNotNumber(value.reg(), "ToBoolean operand is not a number."); + } + // Smi => false iff zero. + ASSERT(kSmiTag == 0); + __ test(value.reg(), Operand(value.reg())); + dest->false_target()->Branch(zero); + __ test(value.reg(), Immediate(kSmiTagMask)); + dest->true_target()->Branch(zero); + __ fldz(); + __ fld_d(FieldOperand(value.reg(), HeapNumber::kValueOffset)); + __ FCmp(); + value.Unuse(); + dest->Split(not_zero); + } else { + // Fast case checks. + // 'false' => false. + __ cmp(value.reg(), Factory::false_value()); + dest->false_target()->Branch(equal); - // 'true' => true. - __ cmp(value.reg(), Factory::true_value()); - dest->true_target()->Branch(equal); + // 'true' => true. + __ cmp(value.reg(), Factory::true_value()); + dest->true_target()->Branch(equal); - // 'undefined' => false. - __ cmp(value.reg(), Factory::undefined_value()); - dest->false_target()->Branch(equal); + // 'undefined' => false. + __ cmp(value.reg(), Factory::undefined_value()); + dest->false_target()->Branch(equal); - // Smi => false iff zero. - ASSERT(kSmiTag == 0); - __ test(value.reg(), Operand(value.reg())); - dest->false_target()->Branch(zero); - __ test(value.reg(), Immediate(kSmiTagMask)); - dest->true_target()->Branch(zero); - - // Call the stub for all other cases. - frame_->Push(&value); // Undo the Pop() from above. - ToBooleanStub stub; - Result temp = frame_->CallStub(&stub, 1); - // Convert the result to a condition code. - __ test(temp.reg(), Operand(temp.reg())); - temp.Unuse(); - dest->Split(not_equal); + // Smi => false iff zero. + ASSERT(kSmiTag == 0); + __ test(value.reg(), Operand(value.reg())); + dest->false_target()->Branch(zero); + __ test(value.reg(), Immediate(kSmiTagMask)); + dest->true_target()->Branch(zero); + + // Call the stub for all other cases. + frame_->Push(&value); // Undo the Pop() from above. + ToBooleanStub stub; + Result temp = frame_->CallStub(&stub, 1); + // Convert the result to a condition code. + __ test(temp.reg(), Operand(temp.reg())); + temp.Unuse(); + dest->Split(not_equal); + } } @@ -789,6 +820,10 @@ class FloatingPointHelper : public AllStatic { static void LoadAsIntegers(MacroAssembler* masm, bool use_sse3, Label* operand_conversion_failure); + // Test if operands are smis or heap numbers and load them + // into xmm0 and xmm1 if they are. Operands are in edx and eax. + // Leaves operands unchanged. + static void LoadSSE2Operands(MacroAssembler* masm); // Test if operands are numbers (smi or HeapNumber objects), and load // them into xmm0 and xmm1 if they are. Jump to label not_numbers if // either operand is not a number. Operands are in edx and eax. @@ -816,12 +851,13 @@ const char* GenericBinaryOpStub::GetName() { } OS::SNPrintF(Vector<char>(name_, kMaxNameLength), - "GenericBinaryOpStub_%s_%s%s_%s%s", + "GenericBinaryOpStub_%s_%s%s_%s%s_%s", op_name, overwrite_name, (flags_ & NO_SMI_CODE_IN_STUB) ? "_NoSmiInStub" : "", args_in_registers_ ? "RegArgs" : "StackArgs", - args_reversed_ ? "_R" : ""); + args_reversed_ ? "_R" : "", + NumberInfo::ToString(operands_type_)); return name_; } @@ -971,27 +1007,35 @@ void CodeGenerator::GenericBinaryOperation(Token::Value op, // Neither operand is known to be a string. } - bool left_is_smi = left.is_constant() && left.handle()->IsSmi(); - bool left_is_non_smi = left.is_constant() && !left.handle()->IsSmi(); - bool right_is_smi = right.is_constant() && right.handle()->IsSmi(); - bool right_is_non_smi = right.is_constant() && !right.handle()->IsSmi(); + bool left_is_smi_constant = left.is_constant() && left.handle()->IsSmi(); + bool left_is_non_smi_constant = left.is_constant() && !left.handle()->IsSmi(); + bool right_is_smi_constant = right.is_constant() && right.handle()->IsSmi(); + bool right_is_non_smi_constant = + right.is_constant() && !right.handle()->IsSmi(); - if (left_is_smi && right_is_smi) { + if (left_is_smi_constant && right_is_smi_constant) { // Compute the constant result at compile time, and leave it on the frame. int left_int = Smi::cast(*left.handle())->value(); int right_int = Smi::cast(*right.handle())->value(); if (FoldConstantSmis(op, left_int, right_int)) return; } + // Get number type of left and right sub-expressions. + NumberInfo::Type operands_type = + NumberInfo::Combine(left.number_info(), right.number_info()); + Result answer; - if (left_is_non_smi || right_is_non_smi) { + if (left_is_non_smi_constant || right_is_non_smi_constant) { // Go straight to the slow case, with no smi code. - GenericBinaryOpStub stub(op, overwrite_mode, NO_SMI_CODE_IN_STUB); + GenericBinaryOpStub stub(op, + overwrite_mode, + NO_SMI_CODE_IN_STUB, + operands_type); answer = stub.GenerateCall(masm_, frame_, &left, &right); - } else if (right_is_smi) { + } else if (right_is_smi_constant) { answer = ConstantSmiBinaryOperation(op, &left, right.handle(), type, false, overwrite_mode); - } else if (left_is_smi) { + } else if (left_is_smi_constant) { answer = ConstantSmiBinaryOperation(op, &right, left.handle(), type, true, overwrite_mode); } else { @@ -1003,10 +1047,67 @@ void CodeGenerator::GenericBinaryOperation(Token::Value op, if (loop_nesting() > 0 && (Token::IsBitOp(op) || type->IsLikelySmi())) { answer = LikelySmiBinaryOperation(op, &left, &right, overwrite_mode); } else { - GenericBinaryOpStub stub(op, overwrite_mode, NO_GENERIC_BINARY_FLAGS); + GenericBinaryOpStub stub(op, + overwrite_mode, + NO_GENERIC_BINARY_FLAGS, + operands_type); answer = stub.GenerateCall(masm_, frame_, &left, &right); } } + + // Set NumberInfo of result according to the operation performed. + // Rely on the fact that smis have a 31 bit payload on ia32. + ASSERT(kSmiValueSize == 31); + NumberInfo::Type result_type = NumberInfo::kUnknown; + switch (op) { + case Token::COMMA: + result_type = right.number_info(); + break; + case Token::OR: + case Token::AND: + // Result type can be either of the two input types. + result_type = operands_type; + break; + case Token::BIT_OR: + case Token::BIT_XOR: + case Token::BIT_AND: + // Result is always a number. Smi property of inputs is preserved. + result_type = (operands_type == NumberInfo::kSmi) + ? NumberInfo::kSmi + : NumberInfo::kNumber; + break; + case Token::SAR: + // Result is a smi if we shift by a constant >= 1, otherwise a number. + result_type = (right.is_constant() && right.handle()->IsSmi() + && Smi::cast(*right.handle())->value() >= 1) + ? NumberInfo::kSmi + : NumberInfo::kNumber; + break; + case Token::SHR: + // Result is a smi if we shift by a constant >= 2, otherwise a number. + result_type = (right.is_constant() && right.handle()->IsSmi() + && Smi::cast(*right.handle())->value() >= 2) + ? NumberInfo::kSmi + : NumberInfo::kNumber; + break; + case Token::ADD: + // Result could be a string or a number. Check types of inputs. + result_type = NumberInfo::IsNumber(operands_type) + ? NumberInfo::kNumber + : NumberInfo::kUnknown; + break; + case Token::SHL: + case Token::SUB: + case Token::MUL: + case Token::DIV: + case Token::MOD: + // Result is always a number. + result_type = NumberInfo::kNumber; + break; + default: + UNREACHABLE(); + } + answer.set_number_info(result_type); frame_->Push(&answer); } @@ -1848,6 +1949,39 @@ Result CodeGenerator::ConstantSmiBinaryOperation(Token::Value op, break; } + case Token::DIV: + if (!reversed && int_value == 2) { + operand->ToRegister(); + frame_->Spill(operand->reg()); + + DeferredInlineSmiOperation* deferred = + new DeferredInlineSmiOperation(op, + operand->reg(), + operand->reg(), + smi_value, + overwrite_mode); + // Check that lowest log2(value) bits of operand are zero, and test + // smi tag at the same time. + ASSERT_EQ(0, kSmiTag); + ASSERT_EQ(1, kSmiTagSize); + __ test(operand->reg(), Immediate(3)); + deferred->Branch(not_zero); // Branch if non-smi or odd smi. + __ sar(operand->reg(), 1); + deferred->BindExit(); + answer = *operand; + } else { + // Cannot fall through MOD to default case, so we duplicate the + // default case here. + Result constant_operand(value); + if (reversed) { + answer = LikelySmiBinaryOperation(op, &constant_operand, operand, + overwrite_mode); + } else { + answer = LikelySmiBinaryOperation(op, operand, &constant_operand, + overwrite_mode); + } + } + break; // Generate inline code for mod of powers of 2 and negative powers of 2. case Token::MOD: if (!reversed && @@ -2327,6 +2461,7 @@ void CodeGenerator::CallApplyLazy(Expression* applicand, // Load applicand.apply onto the stack. This will usually // give us a megamorphic load site. Not super, but it works. Load(applicand); + frame()->Dup(); Handle<String> name = Factory::LookupAsciiSymbol("apply"); frame()->Push(name); Result answer = frame()->CallLoadIC(RelocInfo::CODE_TARGET); @@ -2336,7 +2471,9 @@ void CodeGenerator::CallApplyLazy(Expression* applicand, // Load the receiver and the existing arguments object onto the // expression stack. Avoid allocating the arguments object here. Load(receiver); - LoadFromSlot(scope()->arguments()->var()->slot(), NOT_INSIDE_TYPEOF); + Result existing_args = + LoadFromSlot(scope()->arguments()->var()->slot(), NOT_INSIDE_TYPEOF); + frame()->Push(&existing_args); // Emit the source position information after having loaded the // receiver and the arguments. @@ -3906,35 +4043,32 @@ void CodeGenerator::VisitDebuggerStatement(DebuggerStatement* node) { // Spill everything, even constants, to the frame. frame_->SpillAll(); - DebuggerStatementStub ces; - frame_->CallStub(&ces, 0); + frame_->DebugBreak(); // Ignore the return value. #endif } -void CodeGenerator::InstantiateBoilerplate(Handle<JSFunction> boilerplate) { +Result CodeGenerator::InstantiateBoilerplate(Handle<JSFunction> boilerplate) { ASSERT(boilerplate->IsBoilerplate()); // The inevitable call will sync frame elements to memory anyway, so // we do it eagerly to allow us to push the arguments directly into // place. - frame_->SyncRange(0, frame_->element_count() - 1); + frame()->SyncRange(0, frame()->element_count() - 1); // Use the fast case closure allocation code that allocates in new // space for nested functions that don't need literals cloning. if (scope()->is_function_scope() && boilerplate->NumberOfLiterals() == 0) { FastNewClosureStub stub; - frame_->EmitPush(Immediate(boilerplate)); - Result answer = frame_->CallStub(&stub, 1); - frame_->Push(&answer); + frame()->EmitPush(Immediate(boilerplate)); + return frame()->CallStub(&stub, 1); } else { // Call the runtime to instantiate the function boilerplate // object. - frame_->EmitPush(esi); - frame_->EmitPush(Immediate(boilerplate)); - Result result = frame_->CallRuntime(Runtime::kNewClosure, 2); - frame_->Push(&result); + frame()->EmitPush(esi); + frame()->EmitPush(Immediate(boilerplate)); + return frame()->CallRuntime(Runtime::kNewClosure, 2); } } @@ -3947,14 +4081,16 @@ void CodeGenerator::VisitFunctionLiteral(FunctionLiteral* node) { Compiler::BuildBoilerplate(node, script(), this); // Check for stack-overflow exception. if (HasStackOverflow()) return; - InstantiateBoilerplate(boilerplate); + Result result = InstantiateBoilerplate(boilerplate); + frame()->Push(&result); } void CodeGenerator::VisitFunctionBoilerplateLiteral( FunctionBoilerplateLiteral* node) { Comment cmnt(masm_, "[ FunctionBoilerplateLiteral"); - InstantiateBoilerplate(node->boilerplate()); + Result result = InstantiateBoilerplate(node->boilerplate()); + frame()->Push(&result); } @@ -3990,13 +4126,12 @@ void CodeGenerator::VisitConditional(Conditional* node) { } -void CodeGenerator::LoadFromSlot(Slot* slot, TypeofState typeof_state) { +Result CodeGenerator::LoadFromSlot(Slot* slot, TypeofState typeof_state) { + Result result; if (slot->type() == Slot::LOOKUP) { ASSERT(slot->var()->is_dynamic()); - JumpTarget slow; JumpTarget done; - Result value; // Generate fast-case code for variables that might be shadowed by // eval-introduced variables. Eval is used a lot without @@ -4004,14 +4139,10 @@ void CodeGenerator::LoadFromSlot(Slot* slot, TypeofState typeof_state) { // perform a runtime call for all variables in the scope // containing the eval. if (slot->var()->mode() == Variable::DYNAMIC_GLOBAL) { - value = LoadFromGlobalSlotCheckExtensions(slot, typeof_state, &slow); + result = LoadFromGlobalSlotCheckExtensions(slot, typeof_state, &slow); // If there was no control flow to slow, we can exit early. - if (!slow.is_linked()) { - frame_->Push(&value); - return; - } - - done.Jump(&value); + if (!slow.is_linked()) return result; + done.Jump(&result); } else if (slot->var()->mode() == Variable::DYNAMIC_LOCAL) { Slot* potential_slot = slot->var()->local_if_not_shadowed()->slot(); @@ -4021,21 +4152,21 @@ void CodeGenerator::LoadFromSlot(Slot* slot, TypeofState typeof_state) { // Allocate a fresh register to use as a temp in // ContextSlotOperandCheckExtensions and to hold the result // value. - value = allocator_->Allocate(); - ASSERT(value.is_valid()); - __ mov(value.reg(), + result = allocator()->Allocate(); + ASSERT(result.is_valid()); + __ mov(result.reg(), ContextSlotOperandCheckExtensions(potential_slot, - value, + result, &slow)); if (potential_slot->var()->mode() == Variable::CONST) { - __ cmp(value.reg(), Factory::the_hole_value()); - done.Branch(not_equal, &value); - __ mov(value.reg(), Factory::undefined_value()); + __ cmp(result.reg(), Factory::the_hole_value()); + done.Branch(not_equal, &result); + __ mov(result.reg(), Factory::undefined_value()); } // There is always control flow to slow from // ContextSlotOperandCheckExtensions so we have to jump around // it. - done.Jump(&value); + done.Jump(&result); } } @@ -4043,18 +4174,18 @@ void CodeGenerator::LoadFromSlot(Slot* slot, TypeofState typeof_state) { // A runtime call is inevitable. We eagerly sync frame elements // to memory so that we can push the arguments directly into place // on top of the frame. - frame_->SyncRange(0, frame_->element_count() - 1); - frame_->EmitPush(esi); - frame_->EmitPush(Immediate(slot->var()->name())); + frame()->SyncRange(0, frame()->element_count() - 1); + frame()->EmitPush(esi); + frame()->EmitPush(Immediate(slot->var()->name())); if (typeof_state == INSIDE_TYPEOF) { - value = - frame_->CallRuntime(Runtime::kLoadContextSlotNoReferenceError, 2); + result = + frame()->CallRuntime(Runtime::kLoadContextSlotNoReferenceError, 2); } else { - value = frame_->CallRuntime(Runtime::kLoadContextSlot, 2); + result = frame()->CallRuntime(Runtime::kLoadContextSlot, 2); } - done.Bind(&value); - frame_->Push(&value); + done.Bind(&result); + return result; } else if (slot->var()->mode() == Variable::CONST) { // Const slots may contain 'the hole' value (the constant hasn't been @@ -4065,19 +4196,21 @@ void CodeGenerator::LoadFromSlot(Slot* slot, TypeofState typeof_state) { // potentially unsafe direct-frame access of SlotOperand. VirtualFrame::SpilledScope spilled_scope; Comment cmnt(masm_, "[ Load const"); - JumpTarget exit; + Label exit; __ mov(ecx, SlotOperand(slot, ecx)); __ cmp(ecx, Factory::the_hole_value()); - exit.Branch(not_equal); + __ j(not_equal, &exit); __ mov(ecx, Factory::undefined_value()); - exit.Bind(); - frame_->EmitPush(ecx); + __ bind(&exit); + return Result(ecx); } else if (slot->type() == Slot::PARAMETER) { - frame_->PushParameterAt(slot->index()); + frame()->PushParameterAt(slot->index()); + return frame()->Pop(); } else if (slot->type() == Slot::LOCAL) { - frame_->PushLocalAt(slot->index()); + frame()->PushLocalAt(slot->index()); + return frame()->Pop(); } else { // The other remaining slot types (LOOKUP and GLOBAL) cannot reach @@ -4086,49 +4219,46 @@ void CodeGenerator::LoadFromSlot(Slot* slot, TypeofState typeof_state) { // The use of SlotOperand below is safe for an unspilled frame // because it will always be a context slot. ASSERT(slot->type() == Slot::CONTEXT); - Result temp = allocator_->Allocate(); - ASSERT(temp.is_valid()); - __ mov(temp.reg(), SlotOperand(slot, temp.reg())); - frame_->Push(&temp); + result = allocator()->Allocate(); + ASSERT(result.is_valid()); + __ mov(result.reg(), SlotOperand(slot, result.reg())); + return result; } } -void CodeGenerator::LoadFromSlotCheckForArguments(Slot* slot, - TypeofState state) { - LoadFromSlot(slot, state); +Result CodeGenerator::LoadFromSlotCheckForArguments(Slot* slot, + TypeofState state) { + Result result = LoadFromSlot(slot, state); // Bail out quickly if we're not using lazy arguments allocation. - if (ArgumentsMode() != LAZY_ARGUMENTS_ALLOCATION) return; + if (ArgumentsMode() != LAZY_ARGUMENTS_ALLOCATION) return result; // ... or if the slot isn't a non-parameter arguments slot. - if (slot->type() == Slot::PARAMETER || !slot->is_arguments()) return; - - // Pop the loaded value from the stack. - Result value = frame_->Pop(); + if (slot->type() == Slot::PARAMETER || !slot->is_arguments()) return result; // If the loaded value is a constant, we know if the arguments // object has been lazily loaded yet. - if (value.is_constant()) { - if (value.handle()->IsTheHole()) { - Result arguments = StoreArgumentsObject(false); - frame_->Push(&arguments); + if (result.is_constant()) { + if (result.handle()->IsTheHole()) { + result.Unuse(); + return StoreArgumentsObject(false); } else { - frame_->Push(&value); + return result; } - return; } // The loaded value is in a register. If it is the sentinel that // indicates that we haven't loaded the arguments object yet, we // need to do it now. JumpTarget exit; - __ cmp(Operand(value.reg()), Immediate(Factory::the_hole_value())); - frame_->Push(&value); - exit.Branch(not_equal); - Result arguments = StoreArgumentsObject(false); - frame_->SetElementAt(0, &arguments); - exit.Bind(); + __ cmp(Operand(result.reg()), Immediate(Factory::the_hole_value())); + exit.Branch(not_equal, &result); + + result.Unuse(); + result = StoreArgumentsObject(false); + exit.Bind(&result); + return result; } @@ -4188,6 +4318,10 @@ Result CodeGenerator::LoadFromGlobalSlotCheckExtensions( // All extension objects were empty and it is safe to use a global // load IC call. + // The register allocator prefers eax if it is free, so the code generator + // will load the global object directly into eax, which is where the LoadIC + // expects it. + frame_->Spill(eax); LoadGlobal(); frame_->Push(slot->var()->name()); RelocInfo::Mode mode = (typeof_state == INSIDE_TYPEOF) @@ -4198,8 +4332,6 @@ Result CodeGenerator::LoadFromGlobalSlotCheckExtensions( // property case was inlined. Ensure that there is not a test eax // instruction here. __ nop(); - // Discard the global object. The result is in answer. - frame_->Drop(); return answer; } @@ -4304,7 +4436,8 @@ void CodeGenerator::StoreToSlot(Slot* slot, InitState init_state) { void CodeGenerator::VisitSlot(Slot* node) { Comment cmnt(masm_, "[ Slot"); - LoadFromSlotCheckForArguments(node, NOT_INSIDE_TYPEOF); + Result result = LoadFromSlotCheckForArguments(node, NOT_INSIDE_TYPEOF); + frame()->Push(&result); } @@ -4474,8 +4607,8 @@ void CodeGenerator::VisitObjectLiteral(ObjectLiteral* node) { // Duplicate the object as the IC receiver. frame_->Dup(); Load(property->value()); - frame_->Push(key); - Result ignored = frame_->CallStoreIC(); + Result dummy = frame_->CallStoreIC(Handle<String>::cast(key), false); + dummy.Unuse(); break; } // Fall through @@ -4599,106 +4732,241 @@ void CodeGenerator::VisitCatchExtensionObject(CatchExtensionObject* node) { } -void CodeGenerator::VisitAssignment(Assignment* node) { +void CodeGenerator::EmitSlotAssignment(Assignment* node) { #ifdef DEBUG - int original_height = frame_->height(); + int original_height = frame()->height(); #endif - Comment cmnt(masm_, "[ Assignment"); + Comment cmnt(masm(), "[ Variable Assignment"); + Variable* var = node->target()->AsVariableProxy()->AsVariable(); + ASSERT(var != NULL); + Slot* slot = var->slot(); + ASSERT(slot != NULL); - { Reference target(this, node->target(), node->is_compound()); - if (target.is_illegal()) { - // Fool the virtual frame into thinking that we left the assignment's - // value on the frame. - frame_->Push(Smi::FromInt(0)); - return; - } - Variable* var = node->target()->AsVariableProxy()->AsVariable(); + // Evaluate the right-hand side. + if (node->is_compound()) { + Result result = LoadFromSlotCheckForArguments(slot, NOT_INSIDE_TYPEOF); + frame()->Push(&result); + Load(node->value()); + + bool overwrite_value = + (node->value()->AsBinaryOperation() != NULL && + node->value()->AsBinaryOperation()->ResultOverwriteAllowed()); + GenericBinaryOperation(node->binary_op(), + node->type(), + overwrite_value ? OVERWRITE_RIGHT : NO_OVERWRITE); + } else { + Load(node->value()); + } - if (node->starts_initialization_block()) { - ASSERT(target.type() == Reference::NAMED || - target.type() == Reference::KEYED); - // Change to slow case in the beginning of an initialization - // block to avoid the quadratic behavior of repeatedly adding - // fast properties. + // Perform the assignment. + if (var->mode() != Variable::CONST || node->op() == Token::INIT_CONST) { + CodeForSourcePosition(node->position()); + StoreToSlot(slot, + node->op() == Token::INIT_CONST ? CONST_INIT : NOT_CONST_INIT); + } + ASSERT(frame()->height() == original_height + 1); +} - // The receiver is the argument to the runtime call. It is the - // first value pushed when the reference was loaded to the - // frame. - frame_->PushElementAt(target.size() - 1); - Result ignored = frame_->CallRuntime(Runtime::kToSlowProperties, 1); - } - if (node->ends_initialization_block()) { - // Add an extra copy of the receiver to the frame, so that it can be - // converted back to fast case after the assignment. - ASSERT(target.type() == Reference::NAMED || - target.type() == Reference::KEYED); - if (target.type() == Reference::NAMED) { - frame_->Dup(); - // Dup target receiver on stack. - } else { - ASSERT(target.type() == Reference::KEYED); - Result temp = frame_->Pop(); - frame_->Dup(); - frame_->Push(&temp); - } - } - if (node->op() == Token::ASSIGN || - node->op() == Token::INIT_VAR || - node->op() == Token::INIT_CONST) { - Load(node->value()); - - } else { // Assignment is a compound assignment. - Literal* literal = node->value()->AsLiteral(); - bool overwrite_value = - (node->value()->AsBinaryOperation() != NULL && - node->value()->AsBinaryOperation()->ResultOverwriteAllowed()); - Variable* right_var = node->value()->AsVariableProxy()->AsVariable(); - // There are two cases where the target is not read in the right hand - // side, that are easy to test for: the right hand side is a literal, - // or the right hand side is a different variable. TakeValue invalidates - // the target, with an implicit promise that it will be written to again - // before it is read. - if (literal != NULL || (right_var != NULL && right_var != var)) { - target.TakeValue(); - } else { - target.GetValue(); - } - Load(node->value()); - GenericBinaryOperation(node->binary_op(), - node->type(), - overwrite_value ? OVERWRITE_RIGHT : NO_OVERWRITE); + +void CodeGenerator::EmitNamedPropertyAssignment(Assignment* node) { +#ifdef DEBUG + int original_height = frame()->height(); +#endif + Comment cmnt(masm(), "[ Named Property Assignment"); + Variable* var = node->target()->AsVariableProxy()->AsVariable(); + Property* prop = node->target()->AsProperty(); + ASSERT(var == NULL || (prop == NULL && var->is_global())); + + // Initialize name and evaluate the receiver subexpression if necessary. + Handle<String> name; + bool is_trivial_receiver = false; + if (var != NULL) { + name = var->name(); + } else { + Literal* lit = prop->key()->AsLiteral(); + ASSERT_NOT_NULL(lit); + name = Handle<String>::cast(lit->handle()); + // Do not materialize the receiver on the frame if it is trivial. + is_trivial_receiver = prop->obj()->IsTrivial(); + if (!is_trivial_receiver) Load(prop->obj()); + } + + if (node->starts_initialization_block()) { + ASSERT_EQ(NULL, var); + // Change to slow case in the beginning of an initialization block to + // avoid the quadratic behavior of repeatedly adding fast properties. + if (is_trivial_receiver) { + frame()->Push(prop->obj()); + } else { + frame()->Dup(); } + Result ignored = frame()->CallRuntime(Runtime::kToSlowProperties, 1); + } + + if (node->ends_initialization_block() && !is_trivial_receiver) { + // Add an extra copy of the receiver to the frame, so that it can be + // converted back to fast case after the assignment. + frame()->Dup(); + } - if (var != NULL && - var->mode() == Variable::CONST && - node->op() != Token::INIT_VAR && node->op() != Token::INIT_CONST) { - // Assignment ignored - leave the value on the stack. - UnloadReference(&target); + // Evaluate the right-hand side. + if (node->is_compound()) { + if (is_trivial_receiver) { + frame()->Push(prop->obj()); + } else if (var != NULL) { + // The LoadIC stub expects the object in eax. + // Freeing eax causes the code generator to load the global into it. + frame_->Spill(eax); + LoadGlobal(); } else { - CodeForSourcePosition(node->position()); - if (node->op() == Token::INIT_CONST) { - // Dynamic constant initializations must use the function context - // and initialize the actual constant declared. Dynamic variable - // initializations are simply assignments and use SetValue. - target.SetValue(CONST_INIT); - } else { - target.SetValue(NOT_CONST_INIT); - } - if (node->ends_initialization_block()) { - ASSERT(target.type() == Reference::UNLOADED); - // End of initialization block. Revert to fast case. The - // argument to the runtime call is the extra copy of the receiver, - // which is below the value of the assignment. - // Swap the receiver and the value of the assignment expression. - Result lhs = frame_->Pop(); - Result receiver = frame_->Pop(); - frame_->Push(&lhs); - frame_->Push(&receiver); - Result ignored = frame_->CallRuntime(Runtime::kToFastProperties, 1); - } + frame()->Dup(); + } + Result value = EmitNamedLoad(name, var != NULL); + frame()->Push(&value); + Load(node->value()); + + bool overwrite_value = + (node->value()->AsBinaryOperation() != NULL && + node->value()->AsBinaryOperation()->ResultOverwriteAllowed()); + GenericBinaryOperation(node->binary_op(), + node->type(), + overwrite_value ? OVERWRITE_RIGHT : NO_OVERWRITE); + } else { + Load(node->value()); + } + + // Perform the assignment. It is safe to ignore constants here. + ASSERT(var == NULL || var->mode() != Variable::CONST); + ASSERT_NE(Token::INIT_CONST, node->op()); + if (is_trivial_receiver) { + Result value = frame()->Pop(); + frame()->Push(prop->obj()); + frame()->Push(&value); + } + CodeForSourcePosition(node->position()); + bool is_contextual = (var != NULL); + Result answer = EmitNamedStore(name, is_contextual); + frame()->Push(&answer); + + if (node->ends_initialization_block()) { + ASSERT_EQ(NULL, var); + // The argument to the runtime call is the receiver. + if (is_trivial_receiver) { + frame()->Push(prop->obj()); + } else { + // A copy of the receiver is below the value of the assignment. Swap + // the receiver and the value of the assignment expression. + Result result = frame()->Pop(); + Result receiver = frame()->Pop(); + frame()->Push(&result); + frame()->Push(&receiver); } + Result ignored = frame_->CallRuntime(Runtime::kToFastProperties, 1); } - ASSERT(frame_->height() == original_height + 1); + + ASSERT_EQ(frame()->height(), original_height + 1); +} + + +void CodeGenerator::EmitKeyedPropertyAssignment(Assignment* node) { +#ifdef DEBUG + int original_height = frame()->height(); +#endif + Comment cmnt(masm_, "[ Named Property Assignment"); + Property* prop = node->target()->AsProperty(); + ASSERT_NOT_NULL(prop); + + // Evaluate the receiver subexpression. + Load(prop->obj()); + + if (node->starts_initialization_block()) { + // Change to slow case in the beginning of an initialization block to + // avoid the quadratic behavior of repeatedly adding fast properties. + frame_->Dup(); + Result ignored = frame_->CallRuntime(Runtime::kToSlowProperties, 1); + } + + if (node->ends_initialization_block()) { + // Add an extra copy of the receiver to the frame, so that it can be + // converted back to fast case after the assignment. + frame_->Dup(); + } + + // Evaluate the key subexpression. + Load(prop->key()); + + // Evaluate the right-hand side. + if (node->is_compound()) { + // Duplicate receiver and key. + frame()->PushElementAt(1); + frame()->PushElementAt(1); + Result value = EmitKeyedLoad(); + frame()->Push(&value); + Load(node->value()); + + bool overwrite_value = + (node->value()->AsBinaryOperation() != NULL && + node->value()->AsBinaryOperation()->ResultOverwriteAllowed()); + GenericBinaryOperation(node->binary_op(), + node->type(), + overwrite_value ? OVERWRITE_RIGHT : NO_OVERWRITE); + } else { + Load(node->value()); + } + + // Perform the assignment. It is safe to ignore constants here. + ASSERT(node->op() != Token::INIT_CONST); + CodeForSourcePosition(node->position()); + Result answer = EmitKeyedStore(prop->key()->type()); + frame()->Push(&answer); + + if (node->ends_initialization_block()) { + // The argument to the runtime call is the extra copy of the receiver, + // which is below the value of the assignment. Swap the receiver and + // the value of the assignment expression. + Result result = frame()->Pop(); + Result receiver = frame()->Pop(); + frame()->Push(&result); + frame()->Push(&receiver); + Result ignored = frame_->CallRuntime(Runtime::kToFastProperties, 1); + } + + ASSERT(frame()->height() == original_height + 1); +} + + +void CodeGenerator::VisitAssignment(Assignment* node) { +#ifdef DEBUG + int original_height = frame()->height(); +#endif + Variable* var = node->target()->AsVariableProxy()->AsVariable(); + Property* prop = node->target()->AsProperty(); + + if (var != NULL && !var->is_global()) { + EmitSlotAssignment(node); + + } else if ((prop != NULL && prop->key()->IsPropertyName()) || + (var != NULL && var->is_global())) { + // Properties whose keys are property names and global variables are + // treated as named property references. We do not need to consider + // global 'this' because it is not a valid left-hand side. + EmitNamedPropertyAssignment(node); + + } else if (prop != NULL) { + // Other properties (including rewritten parameters for a function that + // uses arguments) are keyed property assignments. + EmitKeyedPropertyAssignment(node); + + } else { + // Invalid left-hand side. + Load(node->target()); + Result result = frame()->CallRuntime(Runtime::kThrowReferenceError, 1); + // The runtime call doesn't actually return but the code generator will + // still generate code and expects a certain frame height. + frame()->Push(&result); + } + + ASSERT(frame()->height() == original_height + 1); } @@ -4903,9 +5171,9 @@ void CodeGenerator::VisitCall(Call* node) { LoadGlobalReceiver(); } else { Load(property->obj()); + frame()->Dup(); Load(property->key()); - Result function = EmitKeyedLoad(false); - frame_->Drop(); // Key. + Result function = EmitKeyedLoad(); Result receiver = frame_->Pop(); frame_->Push(&function); frame_->Push(&receiver); @@ -5173,6 +5441,25 @@ void CodeGenerator::GenerateIsArray(ZoneList<Expression*>* args) { } +void CodeGenerator::GenerateIsRegExp(ZoneList<Expression*>* args) { + ASSERT(args->length() == 1); + Load(args->at(0)); + Result value = frame_->Pop(); + value.ToRegister(); + ASSERT(value.is_valid()); + __ test(value.reg(), Immediate(kSmiTagMask)); + destination()->false_target()->Branch(equal); + // It is a heap object - get map. + Result temp = allocator()->Allocate(); + ASSERT(temp.is_valid()); + // Check if the object is a regexp. + __ CmpObjectType(value.reg(), JS_REGEXP_TYPE, temp.reg()); + value.Unuse(); + temp.Unuse(); + destination()->Split(equal); +} + + void CodeGenerator::GenerateIsObject(ZoneList<Expression*>* args) { // This generates a fast version of: // (typeof(arg) === 'object' || %_ClassOf(arg) == 'RegExp') @@ -5527,6 +5814,35 @@ void CodeGenerator::GenerateRegExpExec(ZoneList<Expression*>* args) { } +void CodeGenerator::GenerateNumberToString(ZoneList<Expression*>* args) { + ASSERT_EQ(args->length(), 1); + + // Load the argument on the stack and call the stub. + Load(args->at(0)); + NumberToStringStub stub; + Result result = frame_->CallStub(&stub, 1); + frame_->Push(&result); +} + + +void CodeGenerator::GenerateMathSin(ZoneList<Expression*>* args) { + ASSERT_EQ(args->length(), 1); + Load(args->at(0)); + TranscendentalCacheStub stub(TranscendentalCache::SIN); + Result result = frame_->CallStub(&stub, 1); + frame_->Push(&result); +} + + +void CodeGenerator::GenerateMathCos(ZoneList<Expression*>* args) { + ASSERT_EQ(args->length(), 1); + Load(args->at(0)); + TranscendentalCacheStub stub(TranscendentalCache::COS); + Result result = frame_->CallStub(&stub, 1); + frame_->Push(&result); +} + + void CodeGenerator::VisitCallRuntime(CallRuntime* node) { if (CheckForInlineRuntimeCall(node)) { return; @@ -5661,7 +5977,6 @@ void CodeGenerator::VisitUnaryOperation(UnaryOperation* node) { switch (op) { case Token::SUB: { GenericUnaryOpStub stub(Token::SUB, overwrite); - // TODO(1222589): remove dependency of TOS being cached inside stub Result operand = frame_->Pop(); Result answer = frame_->CallStub(&stub, &operand); frame_->Push(&answer); @@ -6111,13 +6426,10 @@ void CodeGenerator::VisitCompareOperation(CompareOperation* node) { __ movzx_b(temp.reg(), FieldOperand(temp.reg(), Map::kBitFieldOffset)); __ test(temp.reg(), Immediate(1 << Map::kIsUndetectable)); destination()->false_target()->Branch(not_zero); - __ mov(temp.reg(), FieldOperand(answer.reg(), HeapObject::kMapOffset)); - __ movzx_b(temp.reg(), - FieldOperand(temp.reg(), Map::kInstanceTypeOffset)); - __ cmp(temp.reg(), FIRST_NONSTRING_TYPE); + __ CmpObjectType(answer.reg(), FIRST_NONSTRING_TYPE, temp.reg()); temp.Unuse(); answer.Unuse(); - destination()->Split(less); + destination()->Split(below); } else if (check->Equals(Heap::boolean_symbol())) { __ cmp(answer.reg(), Factory::true_value()); @@ -6277,7 +6589,7 @@ bool CodeGenerator::HasValidEntryRegisters() { // Emit a LoadIC call to get the value from receiver and leave it in -// dst. The receiver register is restored after the call. +// dst. class DeferredReferenceGetNamedValue: public DeferredCode { public: DeferredReferenceGetNamedValue(Register dst, @@ -6300,7 +6612,9 @@ class DeferredReferenceGetNamedValue: public DeferredCode { void DeferredReferenceGetNamedValue::Generate() { - __ push(receiver_); + if (!receiver_.is(eax)) { + __ mov(eax, receiver_); + } __ Set(ecx, Immediate(name_)); Handle<Code> ic(Builtins::builtin(Builtins::LoadIC_Initialize)); __ call(ic, RelocInfo::CODE_TARGET); @@ -6317,7 +6631,6 @@ void DeferredReferenceGetNamedValue::Generate() { __ IncrementCounter(&Counters::named_load_inline_miss, 1); if (!dst_.is(eax)) __ mov(dst_, eax); - __ pop(receiver_); } @@ -6325,9 +6638,8 @@ class DeferredReferenceGetKeyedValue: public DeferredCode { public: explicit DeferredReferenceGetKeyedValue(Register dst, Register receiver, - Register key, - bool is_global) - : dst_(dst), receiver_(receiver), key_(key), is_global_(is_global) { + Register key) + : dst_(dst), receiver_(receiver), key_(key) { set_comment("[ DeferredReferenceGetKeyedValue"); } @@ -6340,14 +6652,29 @@ class DeferredReferenceGetKeyedValue: public DeferredCode { Register dst_; Register receiver_; Register key_; - bool is_global_; }; void DeferredReferenceGetKeyedValue::Generate() { - __ push(receiver_); // First IC argument. - __ push(key_); // Second IC argument. - + if (!receiver_.is(eax)) { + // Register eax is available for key. + if (!key_.is(eax)) { + __ mov(eax, key_); + } + if (!receiver_.is(edx)) { + __ mov(edx, receiver_); + } + } else if (!key_.is(edx)) { + // Register edx is available for receiver. + if (!receiver_.is(edx)) { + __ mov(edx, receiver_); + } + if (!key_.is(eax)) { + __ mov(eax, key_); + } + } else { + __ xchg(edx, eax); + } // Calculate the delta from the IC call instruction to the map check // cmp instruction in the inlined version. This delta is stored in // a test(eax, delta) instruction after the call so that we can find @@ -6355,10 +6682,7 @@ void DeferredReferenceGetKeyedValue::Generate() { // This means that we cannot allow test instructions after calls to // KeyedLoadIC stubs in other places. Handle<Code> ic(Builtins::builtin(Builtins::KeyedLoadIC_Initialize)); - RelocInfo::Mode mode = is_global_ - ? RelocInfo::CODE_TARGET_CONTEXT - : RelocInfo::CODE_TARGET; - __ call(ic, mode); + __ call(ic, RelocInfo::CODE_TARGET); // The delta from the start of the map-compare instruction to the // test instruction. We use masm_-> directly here instead of the __ // macro because the macro sometimes uses macro expansion to turn @@ -6371,8 +6695,6 @@ void DeferredReferenceGetKeyedValue::Generate() { __ IncrementCounter(&Counters::keyed_load_inline_miss, 1); if (!dst_.is(eax)) __ mov(dst_, eax); - __ pop(key_); - __ pop(receiver_); } @@ -6424,12 +6746,90 @@ void DeferredReferenceSetKeyedValue::Generate() { } -Result CodeGenerator::EmitKeyedLoad(bool is_global) { - Comment cmnt(masm_, "[ Load from keyed Property"); - // Inline array load code if inside of a loop. We do not know - // the receiver map yet, so we initially generate the code with - // a check against an invalid map. In the inline cache code, we - // patch the map check if appropriate. +Result CodeGenerator::EmitNamedLoad(Handle<String> name, bool is_contextual) { +#ifdef DEBUG + int original_height = frame()->height(); +#endif + Result result; + // Do not inline the inobject property case for loads from the global + // object. Also do not inline for unoptimized code. This saves time in + // the code generator. Unoptimized code is toplevel code or code that is + // not in a loop. + if (is_contextual || scope()->is_global_scope() || loop_nesting() == 0) { + Comment cmnt(masm(), "[ Load from named Property"); + frame()->Push(name); + + RelocInfo::Mode mode = is_contextual + ? RelocInfo::CODE_TARGET_CONTEXT + : RelocInfo::CODE_TARGET; + result = frame()->CallLoadIC(mode); + // A test eax instruction following the call signals that the inobject + // property case was inlined. Ensure that there is not a test eax + // instruction here. + __ nop(); + } else { + // Inline the inobject property case. + Comment cmnt(masm(), "[ Inlined named property load"); + Result receiver = frame()->Pop(); + receiver.ToRegister(); + + result = allocator()->Allocate(); + ASSERT(result.is_valid()); + DeferredReferenceGetNamedValue* deferred = + new DeferredReferenceGetNamedValue(result.reg(), receiver.reg(), name); + + // Check that the receiver is a heap object. + __ test(receiver.reg(), Immediate(kSmiTagMask)); + deferred->Branch(zero); + + __ bind(deferred->patch_site()); + // This is the map check instruction that will be patched (so we can't + // use the double underscore macro that may insert instructions). + // Initially use an invalid map to force a failure. + masm()->cmp(FieldOperand(receiver.reg(), HeapObject::kMapOffset), + Immediate(Factory::null_value())); + // This branch is always a forwards branch so it's always a fixed size + // which allows the assert below to succeed and patching to work. + deferred->Branch(not_equal); + + // The delta from the patch label to the load offset must be statically + // known. + ASSERT(masm()->SizeOfCodeGeneratedSince(deferred->patch_site()) == + LoadIC::kOffsetToLoadInstruction); + // The initial (invalid) offset has to be large enough to force a 32-bit + // instruction encoding to allow patching with an arbitrary offset. Use + // kMaxInt (minus kHeapObjectTag). + int offset = kMaxInt; + masm()->mov(result.reg(), FieldOperand(receiver.reg(), offset)); + + __ IncrementCounter(&Counters::named_load_inline, 1); + deferred->BindExit(); + } + ASSERT(frame()->height() == original_height - 1); + return result; +} + + +Result CodeGenerator::EmitNamedStore(Handle<String> name, bool is_contextual) { +#ifdef DEBUG + int expected_height = frame()->height() - (is_contextual ? 1 : 2); +#endif + Result result = frame()->CallStoreIC(name, is_contextual); + + ASSERT_EQ(expected_height, frame()->height()); + return result; +} + + +Result CodeGenerator::EmitKeyedLoad() { +#ifdef DEBUG + int original_height = frame()->height(); +#endif + Result result; + // Inline array load code if inside of a loop. We do not know the + // receiver map yet, so we initially generate the code with a check + // against an invalid map. In the inline cache code, we patch the map + // check if appropriate. if (loop_nesting() > 0) { Comment cmnt(masm_, "[ Inlined load from keyed Property"); @@ -6445,22 +6845,16 @@ Result CodeGenerator::EmitKeyedLoad(bool is_global) { // Use a fresh temporary for the index and later the loaded // value. - Result index = allocator()->Allocate(); - ASSERT(index.is_valid()); + result = allocator()->Allocate(); + ASSERT(result.is_valid()); DeferredReferenceGetKeyedValue* deferred = - new DeferredReferenceGetKeyedValue(index.reg(), + new DeferredReferenceGetKeyedValue(result.reg(), receiver.reg(), - key.reg(), - is_global); + key.reg()); - // Check that the receiver is not a smi (only needed if this - // is not a load from the global context) and that it has the - // expected map. - if (!is_global) { - __ test(receiver.reg(), Immediate(kSmiTagMask)); - deferred->Branch(zero); - } + __ test(receiver.reg(), Immediate(kSmiTagMask)); + deferred->Branch(zero); // Initially, use an invalid map. The map is patched in the IC // initialization code. @@ -6485,50 +6879,132 @@ Result CodeGenerator::EmitKeyedLoad(bool is_global) { // Shift the key to get the actual index value and check that // it is within bounds. - __ mov(index.reg(), key.reg()); - __ SmiUntag(index.reg()); - __ cmp(index.reg(), + __ mov(result.reg(), key.reg()); + __ SmiUntag(result.reg()); + __ cmp(result.reg(), FieldOperand(elements.reg(), FixedArray::kLengthOffset)); deferred->Branch(above_equal); - // Load and check that the result is not the hole. We could - // reuse the index or elements register for the value. - // - // TODO(206): Consider whether it makes sense to try some - // heuristic about which register to reuse. For example, if - // one is eax, the we can reuse that one because the value - // coming from the deferred code will be in eax. - Result value = index; - __ mov(value.reg(), Operand(elements.reg(), - index.reg(), - times_4, - FixedArray::kHeaderSize - kHeapObjectTag)); + // Load and check that the result is not the hole. + __ mov(result.reg(), Operand(elements.reg(), + result.reg(), + times_4, + FixedArray::kHeaderSize - kHeapObjectTag)); elements.Unuse(); - index.Unuse(); - __ cmp(Operand(value.reg()), Immediate(Factory::the_hole_value())); + __ cmp(Operand(result.reg()), Immediate(Factory::the_hole_value())); deferred->Branch(equal); __ IncrementCounter(&Counters::keyed_load_inline, 1); deferred->BindExit(); - // Restore the receiver and key to the frame and push the - // result on top of it. - frame_->Push(&receiver); - frame_->Push(&key); - return value; } else { Comment cmnt(masm_, "[ Load from keyed Property"); - RelocInfo::Mode mode = is_global - ? RelocInfo::CODE_TARGET_CONTEXT - : RelocInfo::CODE_TARGET; - Result answer = frame_->CallKeyedLoadIC(mode); + result = frame_->CallKeyedLoadIC(RelocInfo::CODE_TARGET); // Make sure that we do not have a test instruction after the // call. A test instruction after the call is used to // indicate that we have generated an inline version of the // keyed load. The explicit nop instruction is here because // the push that follows might be peep-hole optimized away. __ nop(); - return answer; } + ASSERT(frame()->height() == original_height - 2); + return result; +} + + +Result CodeGenerator::EmitKeyedStore(StaticType* key_type) { +#ifdef DEBUG + int original_height = frame()->height(); +#endif + Result result; + // Generate inlined version of the keyed store if the code is in a loop + // and the key is likely to be a smi. + if (loop_nesting() > 0 && key_type->IsLikelySmi()) { + Comment cmnt(masm(), "[ Inlined store to keyed Property"); + + // Get the receiver, key and value into registers. + result = frame()->Pop(); + Result key = frame()->Pop(); + Result receiver = frame()->Pop(); + + Result tmp = allocator_->Allocate(); + ASSERT(tmp.is_valid()); + + // Determine whether the value is a constant before putting it in a + // register. + bool value_is_constant = result.is_constant(); + + // Make sure that value, key and receiver are in registers. + result.ToRegister(); + key.ToRegister(); + receiver.ToRegister(); + + DeferredReferenceSetKeyedValue* deferred = + new DeferredReferenceSetKeyedValue(result.reg(), + key.reg(), + receiver.reg()); + + // Check that the value is a smi if it is not a constant. We can skip + // the write barrier for smis and constants. + if (!value_is_constant) { + __ test(result.reg(), Immediate(kSmiTagMask)); + deferred->Branch(not_zero); + } + + // Check that the key is a non-negative smi. + __ test(key.reg(), Immediate(kSmiTagMask | 0x80000000)); + deferred->Branch(not_zero); + + // Check that the receiver is not a smi. + __ test(receiver.reg(), Immediate(kSmiTagMask)); + deferred->Branch(zero); + + // Check that the receiver is a JSArray. + __ mov(tmp.reg(), + FieldOperand(receiver.reg(), HeapObject::kMapOffset)); + __ movzx_b(tmp.reg(), + FieldOperand(tmp.reg(), Map::kInstanceTypeOffset)); + __ cmp(tmp.reg(), JS_ARRAY_TYPE); + deferred->Branch(not_equal); + + // Check that the key is within bounds. Both the key and the length of + // the JSArray are smis. + __ cmp(key.reg(), + FieldOperand(receiver.reg(), JSArray::kLengthOffset)); + deferred->Branch(greater_equal); + + // Get the elements array from the receiver and check that it is not a + // dictionary. + __ mov(tmp.reg(), + FieldOperand(receiver.reg(), JSObject::kElementsOffset)); + // Bind the deferred code patch site to be able to locate the fixed + // array map comparison. When debugging, we patch this comparison to + // always fail so that we will hit the IC call in the deferred code + // which will allow the debugger to break for fast case stores. + __ bind(deferred->patch_site()); + __ cmp(FieldOperand(tmp.reg(), HeapObject::kMapOffset), + Immediate(Factory::fixed_array_map())); + deferred->Branch(not_equal); + + // Store the value. + __ mov(Operand(tmp.reg(), + key.reg(), + times_2, + FixedArray::kHeaderSize - kHeapObjectTag), + result.reg()); + __ IncrementCounter(&Counters::keyed_store_inline, 1); + + deferred->BindExit(); + } else { + result = frame()->CallKeyedStoreIC(); + // Make sure that we do not have a test instruction after the + // call. A test instruction after the call is used to + // indicate that we have generated an inline version of the + // keyed store. + __ nop(); + frame()->Drop(2); + } + ASSERT(frame()->height() == original_height - 3); + return result; } @@ -6548,7 +7024,7 @@ Handle<String> Reference::GetName() { } else { Literal* raw_name = property->key()->AsLiteral(); ASSERT(raw_name != NULL); - return Handle<String>(String::cast(*raw_name->handle())); + return Handle<String>::cast(raw_name->handle()); } } @@ -6570,7 +7046,10 @@ void Reference::GetValue() { Comment cmnt(masm, "[ Load from Slot"); Slot* slot = expression_->AsVariableProxy()->AsVariable()->slot(); ASSERT(slot != NULL); - cgen_->LoadFromSlotCheckForArguments(slot, NOT_INSIDE_TYPEOF); + Result result = + cgen_->LoadFromSlotCheckForArguments(slot, NOT_INSIDE_TYPEOF); + if (!persist_after_get_) set_unloaded(); + cgen_->frame()->Push(&result); break; } @@ -6578,87 +7057,27 @@ void Reference::GetValue() { Variable* var = expression_->AsVariableProxy()->AsVariable(); bool is_global = var != NULL; ASSERT(!is_global || var->is_global()); - - // Do not inline the inobject property case for loads from the global - // object. Also do not inline for unoptimized code. This saves time - // in the code generator. Unoptimized code is toplevel code or code - // that is not in a loop. - if (is_global || - cgen_->scope()->is_global_scope() || - cgen_->loop_nesting() == 0) { - Comment cmnt(masm, "[ Load from named Property"); - cgen_->frame()->Push(GetName()); - - RelocInfo::Mode mode = is_global - ? RelocInfo::CODE_TARGET_CONTEXT - : RelocInfo::CODE_TARGET; - Result answer = cgen_->frame()->CallLoadIC(mode); - // A test eax instruction following the call signals that the - // inobject property case was inlined. Ensure that there is not - // a test eax instruction here. - __ nop(); - cgen_->frame()->Push(&answer); - } else { - // Inline the inobject property case. - Comment cmnt(masm, "[ Inlined named property load"); - Result receiver = cgen_->frame()->Pop(); - receiver.ToRegister(); - - Result value = cgen_->allocator()->Allocate(); - ASSERT(value.is_valid()); - DeferredReferenceGetNamedValue* deferred = - new DeferredReferenceGetNamedValue(value.reg(), - receiver.reg(), - GetName()); - - // Check that the receiver is a heap object. - __ test(receiver.reg(), Immediate(kSmiTagMask)); - deferred->Branch(zero); - - __ bind(deferred->patch_site()); - // This is the map check instruction that will be patched (so we can't - // use the double underscore macro that may insert instructions). - // Initially use an invalid map to force a failure. - masm->cmp(FieldOperand(receiver.reg(), HeapObject::kMapOffset), - Immediate(Factory::null_value())); - // This branch is always a forwards branch so it's always a fixed - // size which allows the assert below to succeed and patching to work. - deferred->Branch(not_equal); - - // The delta from the patch label to the load offset must be - // statically known. - ASSERT(masm->SizeOfCodeGeneratedSince(deferred->patch_site()) == - LoadIC::kOffsetToLoadInstruction); - // The initial (invalid) offset has to be large enough to force - // a 32-bit instruction encoding to allow patching with an - // arbitrary offset. Use kMaxInt (minus kHeapObjectTag). - int offset = kMaxInt; - masm->mov(value.reg(), FieldOperand(receiver.reg(), offset)); - - __ IncrementCounter(&Counters::named_load_inline, 1); - deferred->BindExit(); - cgen_->frame()->Push(&receiver); - cgen_->frame()->Push(&value); - } + if (persist_after_get_) cgen_->frame()->Dup(); + Result result = cgen_->EmitNamedLoad(GetName(), is_global); + if (!persist_after_get_) set_unloaded(); + cgen_->frame()->Push(&result); break; } case KEYED: { - Variable* var = expression_->AsVariableProxy()->AsVariable(); - bool is_global = var != NULL; - ASSERT(!is_global || var->is_global()); - Result value = cgen_->EmitKeyedLoad(is_global); + if (persist_after_get_) { + cgen_->frame()->PushElementAt(1); + cgen_->frame()->PushElementAt(1); + } + Result value = cgen_->EmitKeyedLoad(); cgen_->frame()->Push(&value); + if (!persist_after_get_) set_unloaded(); break; } default: UNREACHABLE(); } - - if (!persist_after_get_) { - cgen_->UnloadReference(this); - } } @@ -6708,14 +7127,13 @@ void Reference::SetValue(InitState init_state) { Slot* slot = expression_->AsVariableProxy()->AsVariable()->slot(); ASSERT(slot != NULL); cgen_->StoreToSlot(slot, init_state); - cgen_->UnloadReference(this); + set_unloaded(); break; } case NAMED: { Comment cmnt(masm, "[ Store to named Property"); - cgen_->frame()->Push(GetName()); - Result answer = cgen_->frame()->CallStoreIC(); + Result answer = cgen_->EmitNamedStore(GetName(), false); cgen_->frame()->Push(&answer); set_unloaded(); break; @@ -6723,108 +7141,16 @@ void Reference::SetValue(InitState init_state) { case KEYED: { Comment cmnt(masm, "[ Store to keyed Property"); - - // Generate inlined version of the keyed store if the code is in - // a loop and the key is likely to be a smi. Property* property = expression()->AsProperty(); ASSERT(property != NULL); - StaticType* key_smi_analysis = property->key()->type(); - - if (cgen_->loop_nesting() > 0 && key_smi_analysis->IsLikelySmi()) { - Comment cmnt(masm, "[ Inlined store to keyed Property"); - - // Get the receiver, key and value into registers. - Result value = cgen_->frame()->Pop(); - Result key = cgen_->frame()->Pop(); - Result receiver = cgen_->frame()->Pop(); - - Result tmp = cgen_->allocator_->Allocate(); - ASSERT(tmp.is_valid()); - - // Determine whether the value is a constant before putting it - // in a register. - bool value_is_constant = value.is_constant(); - - // Make sure that value, key and receiver are in registers. - value.ToRegister(); - key.ToRegister(); - receiver.ToRegister(); - - DeferredReferenceSetKeyedValue* deferred = - new DeferredReferenceSetKeyedValue(value.reg(), - key.reg(), - receiver.reg()); - - // Check that the value is a smi if it is not a constant. We - // can skip the write barrier for smis and constants. - if (!value_is_constant) { - __ test(value.reg(), Immediate(kSmiTagMask)); - deferred->Branch(not_zero); - } - - // Check that the key is a non-negative smi. - __ test(key.reg(), Immediate(kSmiTagMask | 0x80000000)); - deferred->Branch(not_zero); - - // Check that the receiver is not a smi. - __ test(receiver.reg(), Immediate(kSmiTagMask)); - deferred->Branch(zero); - - // Check that the receiver is a JSArray. - __ mov(tmp.reg(), - FieldOperand(receiver.reg(), HeapObject::kMapOffset)); - __ movzx_b(tmp.reg(), - FieldOperand(tmp.reg(), Map::kInstanceTypeOffset)); - __ cmp(tmp.reg(), JS_ARRAY_TYPE); - deferred->Branch(not_equal); - - // Check that the key is within bounds. Both the key and the - // length of the JSArray are smis. - __ cmp(key.reg(), - FieldOperand(receiver.reg(), JSArray::kLengthOffset)); - deferred->Branch(greater_equal); - - // Get the elements array from the receiver and check that it - // is not a dictionary. - __ mov(tmp.reg(), - FieldOperand(receiver.reg(), JSObject::kElementsOffset)); - // Bind the deferred code patch site to be able to locate the - // fixed array map comparison. When debugging, we patch this - // comparison to always fail so that we will hit the IC call - // in the deferred code which will allow the debugger to - // break for fast case stores. - __ bind(deferred->patch_site()); - __ cmp(FieldOperand(tmp.reg(), HeapObject::kMapOffset), - Immediate(Factory::fixed_array_map())); - deferred->Branch(not_equal); - - // Store the value. - __ mov(Operand(tmp.reg(), - key.reg(), - times_2, - FixedArray::kHeaderSize - kHeapObjectTag), - value.reg()); - __ IncrementCounter(&Counters::keyed_store_inline, 1); - - deferred->BindExit(); - - cgen_->frame()->Push(&receiver); - cgen_->frame()->Push(&key); - cgen_->frame()->Push(&value); - } else { - Result answer = cgen_->frame()->CallKeyedStoreIC(); - // Make sure that we do not have a test instruction after the - // call. A test instruction after the call is used to - // indicate that we have generated an inline version of the - // keyed store. - __ nop(); - cgen_->frame()->Push(&answer); - } - cgen_->UnloadReference(this); + Result answer = cgen_->EmitKeyedStore(property->key()->type()); + cgen_->frame()->Push(&answer); + set_unloaded(); break; } - default: + case UNLOADED: + case ILLEGAL: UNREACHABLE(); } } @@ -6918,6 +7244,13 @@ void FastNewContextStub::Generate(MacroAssembler* masm) { void FastCloneShallowArrayStub::Generate(MacroAssembler* masm) { + // Stack layout on entry: + // + // [esp + kPointerSize]: constant elements. + // [esp + (2 * kPointerSize)]: literal index. + // [esp + (3 * kPointerSize)]: literals array. + + // All sizes here are multiples of kPointerSize. int elements_size = (length_ > 0) ? FixedArray::SizeFor(length_) : 0; int size = JSArray::kSize + elements_size; @@ -7037,6 +7370,8 @@ void GenericBinaryOpStub::GenerateCall( } } else if (left.is(left_arg)) { __ mov(right_arg, right); + } else if (right.is(right_arg)) { + __ mov(left_arg, left); } else if (left.is(right_arg)) { if (IsOperationCommutative()) { __ mov(left_arg, right); @@ -7055,8 +7390,6 @@ void GenericBinaryOpStub::GenerateCall( __ mov(right_arg, right); __ mov(left_arg, left); } - } else if (right.is(right_arg)) { - __ mov(left_arg, left); } else { // Order of moves is not important. __ mov(left_arg, left); @@ -7092,6 +7425,10 @@ void GenericBinaryOpStub::GenerateCall( __ mov(left_arg, Immediate(right)); SetArgsReversed(); } else { + // For non-commutative operations, left and right_arg might be + // the same register. Therefore, the order of the moves is + // important here in order to not overwrite left before moving + // it to left_arg. __ mov(left_arg, left); __ mov(right_arg, Immediate(right)); } @@ -7124,8 +7461,12 @@ void GenericBinaryOpStub::GenerateCall( __ mov(right_arg, Immediate(left)); SetArgsReversed(); } else { - __ mov(left_arg, Immediate(left)); + // For non-commutative operations, right and left_arg might be + // the same register. Therefore, the order of the moves is + // important here in order to not overwrite right before moving + // it to right_arg. __ mov(right_arg, right); + __ mov(left_arg, Immediate(left)); } // Update flags to indicate that arguments are in registers. SetArgsInRegisters(); @@ -7493,7 +7834,18 @@ void GenericBinaryOpStub::Generate(MacroAssembler* masm) { case Token::DIV: { if (CpuFeatures::IsSupported(SSE2)) { CpuFeatures::Scope use_sse2(SSE2); - FloatingPointHelper::LoadSSE2Operands(masm, &call_runtime); + if (NumberInfo::IsNumber(operands_type_)) { + if (FLAG_debug_code) { + // Assert at runtime that inputs are only numbers. + __ AbortIfNotNumber(edx, + "GenericBinaryOpStub operand not a number."); + __ AbortIfNotNumber(eax, + "GenericBinaryOpStub operand not a number."); + } + FloatingPointHelper::LoadSSE2Operands(masm); + } else { + FloatingPointHelper::LoadSSE2Operands(masm, &call_runtime); + } switch (op_) { case Token::ADD: __ addsd(xmm0, xmm1); break; @@ -7506,7 +7858,17 @@ void GenericBinaryOpStub::Generate(MacroAssembler* masm) { __ movdbl(FieldOperand(eax, HeapNumber::kValueOffset), xmm0); GenerateReturn(masm); } else { // SSE2 not available, use FPU. - FloatingPointHelper::CheckFloatOperands(masm, &call_runtime, ebx); + if (NumberInfo::IsNumber(operands_type_)) { + if (FLAG_debug_code) { + // Assert at runtime that inputs are only numbers. + __ AbortIfNotNumber(edx, + "GenericBinaryOpStub operand not a number."); + __ AbortIfNotNumber(eax, + "GenericBinaryOpStub operand not a number."); + } + } else { + FloatingPointHelper::CheckFloatOperands(masm, &call_runtime, ebx); + } FloatingPointHelper::LoadFloatOperands( masm, ecx, @@ -7618,7 +7980,7 @@ void GenericBinaryOpStub::Generate(MacroAssembler* masm) { switch (op_) { case Token::ADD: { // Test for string arguments before calling runtime. - Label not_strings, not_string1, string1; + Label not_strings, not_string1, string1, string1_smi2; Result answer; __ test(edx, Immediate(kSmiTagMask)); __ j(zero, ¬_string1); @@ -7627,15 +7989,28 @@ void GenericBinaryOpStub::Generate(MacroAssembler* masm) { // First argument is a string, test second. __ test(eax, Immediate(kSmiTagMask)); - __ j(zero, &string1); + __ j(zero, &string1_smi2); __ CmpObjectType(eax, FIRST_NONSTRING_TYPE, ecx); __ j(above_equal, &string1); // First and second argument are strings. Jump to the string add stub. - StringAddStub stub(NO_STRING_CHECK_IN_STUB); - __ TailCallStub(&stub); + StringAddStub string_add_stub(NO_STRING_CHECK_IN_STUB); + __ TailCallStub(&string_add_stub); + + __ bind(&string1_smi2); + // First argument is a string, second is a smi. Try to lookup the number + // string for the smi in the number string cache. + NumberToStringStub::GenerateLookupNumberStringCache( + masm, eax, edi, ebx, ecx, true, &string1); + + // Call the string add stub to make the result. + __ EnterInternalFrame(); + __ push(edx); // Original first argument. + __ push(edi); // Number to string result for second argument. + __ CallStub(&string_add_stub); + __ LeaveInternalFrame(); + __ ret(2 * kPointerSize); - // Only first argument is a string. __ bind(&string1); __ InvokeBuiltin( HasArgsReversed() ? @@ -7766,6 +8141,212 @@ void GenericBinaryOpStub::GenerateReturn(MacroAssembler* masm) { } +void TranscendentalCacheStub::Generate(MacroAssembler* masm) { + // Input on stack: + // esp[4]: argument (should be number). + // esp[0]: return address. + // Test that eax is a number. + Label runtime_call; + Label runtime_call_clear_stack; + Label input_not_smi; + Label loaded; + __ mov(eax, Operand(esp, kPointerSize)); + __ test(eax, Immediate(kSmiTagMask)); + __ j(not_zero, &input_not_smi); + // Input is a smi. Untag and load it onto the FPU stack. + // Then load the low and high words of the double into ebx, edx. + ASSERT_EQ(1, kSmiTagSize); + __ sar(eax, 1); + __ sub(Operand(esp), Immediate(2 * kPointerSize)); + __ mov(Operand(esp, 0), eax); + __ fild_s(Operand(esp, 0)); + __ fst_d(Operand(esp, 0)); + __ pop(edx); + __ pop(ebx); + __ jmp(&loaded); + __ bind(&input_not_smi); + // Check if input is a HeapNumber. + __ mov(ebx, FieldOperand(eax, HeapObject::kMapOffset)); + __ cmp(Operand(ebx), Immediate(Factory::heap_number_map())); + __ j(not_equal, &runtime_call); + // Input is a HeapNumber. Push it on the FPU stack and load its + // low and high words into ebx, edx. + __ fld_d(FieldOperand(eax, HeapNumber::kValueOffset)); + __ mov(edx, FieldOperand(eax, HeapNumber::kExponentOffset)); + __ mov(ebx, FieldOperand(eax, HeapNumber::kMantissaOffset)); + + __ bind(&loaded); + // ST[0] == double value + // ebx = low 32 bits of double value + // edx = high 32 bits of double value + // Compute hash: + // h = (low ^ high); h ^= h >> 16; h ^= h >> 8; h = h & (cacheSize - 1); + __ mov(ecx, ebx); + __ xor_(ecx, Operand(edx)); + __ mov(eax, ecx); + __ sar(eax, 16); + __ xor_(ecx, Operand(eax)); + __ mov(eax, ecx); + __ sar(eax, 8); + __ xor_(ecx, Operand(eax)); + ASSERT(IsPowerOf2(TranscendentalCache::kCacheSize)); + __ and_(Operand(ecx), Immediate(TranscendentalCache::kCacheSize - 1)); + // ST[0] == double value. + // ebx = low 32 bits of double value. + // edx = high 32 bits of double value. + // ecx = TranscendentalCache::hash(double value). + __ mov(eax, + Immediate(ExternalReference::transcendental_cache_array_address())); + // Eax points to cache array. + __ mov(eax, Operand(eax, type_ * sizeof(TranscendentalCache::caches_[0]))); + // Eax points to the cache for the type type_. + // If NULL, the cache hasn't been initialized yet, so go through runtime. + __ test(eax, Operand(eax)); + __ j(zero, &runtime_call_clear_stack); +#ifdef DEBUG + // Check that the layout of cache elements match expectations. + { // NOLINT - doesn't like a single brace on a line. + TranscendentalCache::Element test_elem[2]; + char* elem_start = reinterpret_cast<char*>(&test_elem[0]); + char* elem2_start = reinterpret_cast<char*>(&test_elem[1]); + char* elem_in0 = reinterpret_cast<char*>(&(test_elem[0].in[0])); + char* elem_in1 = reinterpret_cast<char*>(&(test_elem[0].in[1])); + char* elem_out = reinterpret_cast<char*>(&(test_elem[0].output)); + CHECK_EQ(12, elem2_start - elem_start); // Two uint_32's and a pointer. + CHECK_EQ(0, elem_in0 - elem_start); + CHECK_EQ(kIntSize, elem_in1 - elem_start); + CHECK_EQ(2 * kIntSize, elem_out - elem_start); + } +#endif + // Find the address of the ecx'th entry in the cache, i.e., &eax[ecx*12]. + __ lea(ecx, Operand(ecx, ecx, times_2, 0)); + __ lea(ecx, Operand(eax, ecx, times_4, 0)); + // Check if cache matches: Double value is stored in uint32_t[2] array. + Label cache_miss; + __ cmp(ebx, Operand(ecx, 0)); + __ j(not_equal, &cache_miss); + __ cmp(edx, Operand(ecx, kIntSize)); + __ j(not_equal, &cache_miss); + // Cache hit! + __ mov(eax, Operand(ecx, 2 * kIntSize)); + __ fstp(0); + __ ret(kPointerSize); + + __ bind(&cache_miss); + // Update cache with new value. + // We are short on registers, so use no_reg as scratch. + // This gives slightly larger code. + __ AllocateHeapNumber(eax, edi, no_reg, &runtime_call_clear_stack); + GenerateOperation(masm); + __ mov(Operand(ecx, 0), ebx); + __ mov(Operand(ecx, kIntSize), edx); + __ mov(Operand(ecx, 2 * kIntSize), eax); + __ fstp_d(FieldOperand(eax, HeapNumber::kValueOffset)); + __ ret(kPointerSize); + + __ bind(&runtime_call_clear_stack); + __ fstp(0); + __ bind(&runtime_call); + __ TailCallRuntime(ExternalReference(RuntimeFunction()), 1, 1); +} + + +Runtime::FunctionId TranscendentalCacheStub::RuntimeFunction() { + switch (type_) { + // Add more cases when necessary. + case TranscendentalCache::SIN: return Runtime::kMath_sin; + case TranscendentalCache::COS: return Runtime::kMath_cos; + default: + UNIMPLEMENTED(); + return Runtime::kAbort; + } +} + + +void TranscendentalCacheStub::GenerateOperation(MacroAssembler* masm) { + // Only free register is edi. + Label done; + ASSERT(type_ == TranscendentalCache::SIN || + type_ == TranscendentalCache::COS); + // More transcendental types can be added later. + + // Both fsin and fcos require arguments in the range +/-2^63 and + // return NaN for infinities and NaN. They can share all code except + // the actual fsin/fcos operation. + Label in_range; + // If argument is outside the range -2^63..2^63, fsin/cos doesn't + // work. We must reduce it to the appropriate range. + __ mov(edi, edx); + __ and_(Operand(edi), Immediate(0x7ff00000)); // Exponent only. + int supported_exponent_limit = + (63 + HeapNumber::kExponentBias) << HeapNumber::kExponentShift; + __ cmp(Operand(edi), Immediate(supported_exponent_limit)); + __ j(below, &in_range, taken); + // Check for infinity and NaN. Both return NaN for sin. + __ cmp(Operand(edi), Immediate(0x7ff00000)); + Label non_nan_result; + __ j(not_equal, &non_nan_result, taken); + // Input is +/-Infinity or NaN. Result is NaN. + __ fstp(0); + // NaN is represented by 0x7ff8000000000000. + __ push(Immediate(0x7ff80000)); + __ push(Immediate(0)); + __ fld_d(Operand(esp, 0)); + __ add(Operand(esp), Immediate(2 * kPointerSize)); + __ jmp(&done); + + __ bind(&non_nan_result); + + // Use fpmod to restrict argument to the range +/-2*PI. + __ mov(edi, eax); // Save eax before using fnstsw_ax. + __ fldpi(); + __ fadd(0); + __ fld(1); + // FPU Stack: input, 2*pi, input. + { + Label no_exceptions; + __ fwait(); + __ fnstsw_ax(); + // Clear if Illegal Operand or Zero Division exceptions are set. + __ test(Operand(eax), Immediate(5)); + __ j(zero, &no_exceptions); + __ fnclex(); + __ bind(&no_exceptions); + } + + // Compute st(0) % st(1) + { + Label partial_remainder_loop; + __ bind(&partial_remainder_loop); + __ fprem1(); + __ fwait(); + __ fnstsw_ax(); + __ test(Operand(eax), Immediate(0x400 /* C2 */)); + // If C2 is set, computation only has partial result. Loop to + // continue computation. + __ j(not_zero, &partial_remainder_loop); + } + // FPU Stack: input, 2*pi, input % 2*pi + __ fstp(2); + __ fstp(0); + __ mov(eax, edi); // Restore eax (allocated HeapNumber pointer). + + // FPU Stack: input % 2*pi + __ bind(&in_range); + switch (type_) { + case TranscendentalCache::SIN: + __ fsin(); + break; + case TranscendentalCache::COS: + __ fcos(); + break; + default: + UNREACHABLE(); + } + __ bind(&done); +} + + // Get the integer part of a heap number. Surprisingly, all this bit twiddling // is faster than using the built-in instructions on floating point registers. // Trashes edi and ebx. Dest is ecx. Source cannot be ecx or one of the @@ -7977,6 +8558,35 @@ void FloatingPointHelper::LoadFloatOperand(MacroAssembler* masm, } +void FloatingPointHelper::LoadSSE2Operands(MacroAssembler* masm) { + Label load_smi_edx, load_eax, load_smi_eax, done; + // Load operand in edx into xmm0. + __ test(edx, Immediate(kSmiTagMask)); + __ j(zero, &load_smi_edx, not_taken); // Argument in edx is a smi. + __ movdbl(xmm0, FieldOperand(edx, HeapNumber::kValueOffset)); + + __ bind(&load_eax); + // Load operand in eax into xmm1. + __ test(eax, Immediate(kSmiTagMask)); + __ j(zero, &load_smi_eax, not_taken); // Argument in eax is a smi. + __ movdbl(xmm1, FieldOperand(eax, HeapNumber::kValueOffset)); + __ jmp(&done); + + __ bind(&load_smi_edx); + __ SmiUntag(edx); // Untag smi before converting to float. + __ cvtsi2sd(xmm0, Operand(edx)); + __ SmiTag(edx); // Retag smi for heap number overwriting test. + __ jmp(&load_eax); + + __ bind(&load_smi_eax); + __ SmiUntag(eax); // Untag smi before converting to float. + __ cvtsi2sd(xmm1, Operand(eax)); + __ SmiTag(eax); // Retag smi for heap number overwriting test. + + __ bind(&done); +} + + void FloatingPointHelper::LoadSSE2Operands(MacroAssembler* masm, Label* not_numbers) { Label load_smi_edx, load_eax, load_smi_eax, load_float_eax, done; @@ -8306,6 +8916,11 @@ void ArgumentsAccessStub::GenerateReadElement(MacroAssembler* masm) { void ArgumentsAccessStub::GenerateNewObject(MacroAssembler* masm) { + // esp[0] : return address + // esp[4] : number of parameters + // esp[8] : receiver displacement + // esp[16] : function + // The displacement is used for skipping the return address and the // frame pointer on the stack. It is the offset of the last // parameter (if any) relative to the frame pointer. @@ -8389,7 +9004,6 @@ void ArgumentsAccessStub::GenerateNewObject(MacroAssembler* masm) { __ add(Operand(edi), Immediate(kPointerSize)); __ sub(Operand(edx), Immediate(kPointerSize)); __ dec(ecx); - __ test(ecx, Operand(ecx)); __ j(not_zero, &loop); // Return and remove the on-stack parameters. @@ -8737,6 +9351,74 @@ void RegExpExecStub::Generate(MacroAssembler* masm) { } +void NumberToStringStub::GenerateLookupNumberStringCache(MacroAssembler* masm, + Register object, + Register result, + Register scratch1, + Register scratch2, + bool object_is_smi, + Label* not_found) { + // Currently only lookup for smis. Check for smi if object is not known to be + // a smi. + if (!object_is_smi) { + ASSERT(kSmiTag == 0); + __ test(object, Immediate(kSmiTagMask)); + __ j(not_zero, not_found); + } + + // Use of registers. Register result is used as a temporary. + Register number_string_cache = result; + Register mask = scratch1; + Register scratch = scratch2; + + // Load the number string cache. + ExternalReference roots_address = ExternalReference::roots_address(); + __ mov(scratch, Immediate(Heap::kNumberStringCacheRootIndex)); + __ mov(number_string_cache, + Operand::StaticArray(scratch, times_pointer_size, roots_address)); + // Make the hash mask from the length of the number string cache. It + // contains two elements (number and string) for each cache entry. + __ mov(mask, FieldOperand(number_string_cache, FixedArray::kLengthOffset)); + __ shr(mask, 1); // Divide length by two (length is not a smi). + __ sub(Operand(mask), Immediate(1)); // Make mask. + // Calculate the entry in the number string cache. The hash value in the + // number string cache for smis is just the smi value. + __ mov(scratch, object); + __ SmiUntag(scratch); + __ and_(scratch, Operand(mask)); + // Check if the entry is the smi we are looking for. + __ cmp(object, + FieldOperand(number_string_cache, + scratch, + times_twice_pointer_size, + FixedArray::kHeaderSize)); + __ j(not_equal, not_found); + + // Get the result from the cache. + __ mov(result, + FieldOperand(number_string_cache, + scratch, + times_twice_pointer_size, + FixedArray::kHeaderSize + kPointerSize)); + __ IncrementCounter(&Counters::number_to_string_native, 1); +} + + +void NumberToStringStub::Generate(MacroAssembler* masm) { + Label runtime; + + __ mov(ebx, Operand(esp, kPointerSize)); + + // Generate code to lookup number in the number string cache. + GenerateLookupNumberStringCache(masm, ebx, eax, ecx, edx, false, &runtime); + __ ret(1 * kPointerSize); + + __ bind(&runtime); + // Handle number to string in the runtime system if not found in the cache. + __ TailCallRuntime(ExternalReference(Runtime::kNumberToString), 1, 1); +} + + void CompareStub::Generate(MacroAssembler* masm) { Label call_builtin, done; @@ -9069,6 +9751,9 @@ void CallFunctionStub::Generate(MacroAssembler* masm) { // Slow-case: Non-function called. __ bind(&slow); + // CALL_NON_FUNCTION expects the non-function callee as receiver (instead + // of the original receiver from the call site). + __ mov(Operand(esp, (argc_ + 1) * kPointerSize), edi); __ Set(eax, Immediate(argc_)); __ Set(ebx, Immediate(0)); __ GetBuiltinEntry(edx, Builtins::CALL_NON_FUNCTION); @@ -9642,13 +10327,34 @@ void StringAddStub::Generate(MacroAssembler* masm) { // ecx: length of second string // edx: second string // Look at the length of the result of adding the two strings. - Label string_add_flat_result; + Label string_add_flat_result, longer_than_two; __ bind(&both_not_zero_length); __ add(ebx, Operand(ecx)); // Use the runtime system when adding two one character strings, as it // contains optimizations for this specific case using the symbol table. __ cmp(ebx, 2); - __ j(equal, &string_add_runtime); + __ j(not_equal, &longer_than_two); + + // Check that both strings are non-external ascii strings. + __ JumpIfNotBothSequentialAsciiStrings(eax, edx, ebx, ecx, + &string_add_runtime); + + // Get the two characters forming the sub string. + __ movzx_b(ebx, FieldOperand(eax, SeqAsciiString::kHeaderSize)); + __ movzx_b(ecx, FieldOperand(edx, SeqAsciiString::kHeaderSize)); + + // Try to lookup two character string in symbol table. If it is not found + // just allocate a new one. + Label make_two_character_string, make_flat_ascii_string; + GenerateTwoCharacterSymbolTableProbe(masm, ebx, ecx, eax, edx, edi, + &make_two_character_string); + __ ret(2 * kPointerSize); + + __ bind(&make_two_character_string); + __ Set(ebx, Immediate(2)); + __ jmp(&make_flat_ascii_string); + + __ bind(&longer_than_two); // Check if resulting string will be flat. __ cmp(ebx, String::kMinNonFlatLength); __ j(below, &string_add_flat_result); @@ -9715,7 +10421,10 @@ void StringAddStub::Generate(MacroAssembler* masm) { __ movzx_b(ecx, FieldOperand(ecx, Map::kInstanceTypeOffset)); __ test(ecx, Immediate(kAsciiStringTag)); __ j(zero, &string_add_runtime); + + __ bind(&make_flat_ascii_string); // Both strings are ascii strings. As they are short they are both flat. + // ebx: length of resulting flat string __ AllocateAsciiString(eax, ebx, ecx, edx, edi, &string_add_runtime); // eax: result string __ mov(ecx, eax); @@ -9872,6 +10581,190 @@ void StringStubBase::GenerateCopyCharactersREP(MacroAssembler* masm, } +void StringStubBase::GenerateTwoCharacterSymbolTableProbe(MacroAssembler* masm, + Register c1, + Register c2, + Register scratch1, + Register scratch2, + Register scratch3, + Label* not_found) { + // Register scratch3 is the general scratch register in this function. + Register scratch = scratch3; + + // Make sure that both characters are not digits as such strings has a + // different hash algorithm. Don't try to look for these in the symbol table. + Label not_array_index; + __ mov(scratch, c1); + __ sub(Operand(scratch), Immediate(static_cast<int>('0'))); + __ cmp(Operand(scratch), Immediate(static_cast<int>('9' - '0'))); + __ j(above, ¬_array_index); + __ mov(scratch, c2); + __ sub(Operand(scratch), Immediate(static_cast<int>('0'))); + __ cmp(Operand(scratch), Immediate(static_cast<int>('9' - '0'))); + __ j(below_equal, not_found); + + __ bind(¬_array_index); + // Calculate the two character string hash. + Register hash = scratch1; + GenerateHashInit(masm, hash, c1, scratch); + GenerateHashAddCharacter(masm, hash, c2, scratch); + GenerateHashGetHash(masm, hash, scratch); + + // Collect the two characters in a register. + Register chars = c1; + __ shl(c2, kBitsPerByte); + __ or_(chars, Operand(c2)); + + // chars: two character string, char 1 in byte 0 and char 2 in byte 1. + // hash: hash of two character string. + + // Load the symbol table. + Register symbol_table = c2; + ExternalReference roots_address = ExternalReference::roots_address(); + __ mov(scratch, Immediate(Heap::kSymbolTableRootIndex)); + __ mov(symbol_table, + Operand::StaticArray(scratch, times_pointer_size, roots_address)); + + // Calculate capacity mask from the symbol table capacity. + Register mask = scratch2; + static const int kCapacityOffset = + FixedArray::kHeaderSize + + SymbolTable::kCapacityIndex * kPointerSize; + __ mov(mask, FieldOperand(symbol_table, kCapacityOffset)); + __ SmiUntag(mask); + __ sub(Operand(mask), Immediate(1)); + + // Registers + // chars: two character string, char 1 in byte 0 and char 2 in byte 1. + // hash: hash of two character string + // symbol_table: symbol table + // mask: capacity mask + // scratch: - + + // Perform a number of probes in the symbol table. + static const int kProbes = 4; + Label found_in_symbol_table; + Label next_probe[kProbes], next_probe_pop_mask[kProbes]; + for (int i = 0; i < kProbes; i++) { + // Calculate entry in symbol table. + __ mov(scratch, hash); + if (i > 0) { + __ add(Operand(scratch), Immediate(SymbolTable::GetProbeOffset(i))); + } + __ and_(scratch, Operand(mask)); + + // Load the entry from the symble table. + Register candidate = scratch; // Scratch register contains candidate. + ASSERT_EQ(1, SymbolTableShape::kEntrySize); + static const int kFirstElementOffset = + FixedArray::kHeaderSize + + SymbolTable::kPrefixStartIndex * kPointerSize + + SymbolTableShape::kPrefixSize * kPointerSize; + __ mov(candidate, + FieldOperand(symbol_table, + scratch, + times_pointer_size, + kFirstElementOffset)); + + // If entry is undefined no string with this hash can be found. + __ cmp(candidate, Factory::undefined_value()); + __ j(equal, not_found); + + // If length is not 2 the string is not a candidate. + __ cmp(FieldOperand(candidate, String::kLengthOffset), Immediate(2)); + __ j(not_equal, &next_probe[i]); + + // As we are out of registers save the mask on the stack and use that + // register as a temporary. + __ push(mask); + Register temp = mask; + + // Check that the candidate is a non-external ascii string. + __ mov(temp, FieldOperand(candidate, HeapObject::kMapOffset)); + __ movzx_b(temp, FieldOperand(temp, Map::kInstanceTypeOffset)); + __ JumpIfInstanceTypeIsNotSequentialAscii( + temp, temp, &next_probe_pop_mask[i]); + + // Check if the two characters match. + __ mov(temp, FieldOperand(candidate, SeqAsciiString::kHeaderSize)); + __ and_(temp, 0x0000ffff); + __ cmp(chars, Operand(temp)); + __ j(equal, &found_in_symbol_table); + __ bind(&next_probe_pop_mask[i]); + __ pop(mask); + __ bind(&next_probe[i]); + } + + // No matching 2 character string found by probing. + __ jmp(not_found); + + // Scratch register contains result when we fall through to here. + Register result = scratch; + __ bind(&found_in_symbol_table); + __ pop(mask); // Pop temporally saved mask from the stack. + if (!result.is(eax)) { + __ mov(eax, result); + } +} + + +void StringStubBase::GenerateHashInit(MacroAssembler* masm, + Register hash, + Register character, + Register scratch) { + // hash = character + (character << 10); + __ mov(hash, character); + __ shl(hash, 10); + __ add(hash, Operand(character)); + // hash ^= hash >> 6; + __ mov(scratch, hash); + __ sar(scratch, 6); + __ xor_(hash, Operand(scratch)); +} + + +void StringStubBase::GenerateHashAddCharacter(MacroAssembler* masm, + Register hash, + Register character, + Register scratch) { + // hash += character; + __ add(hash, Operand(character)); + // hash += hash << 10; + __ mov(scratch, hash); + __ shl(scratch, 10); + __ add(hash, Operand(scratch)); + // hash ^= hash >> 6; + __ mov(scratch, hash); + __ sar(scratch, 6); + __ xor_(hash, Operand(scratch)); +} + + +void StringStubBase::GenerateHashGetHash(MacroAssembler* masm, + Register hash, + Register scratch) { + // hash += hash << 3; + __ mov(scratch, hash); + __ shl(scratch, 3); + __ add(hash, Operand(scratch)); + // hash ^= hash >> 11; + __ mov(scratch, hash); + __ sar(scratch, 11); + __ xor_(hash, Operand(scratch)); + // hash += hash << 15; + __ mov(scratch, hash); + __ shl(scratch, 15); + __ add(hash, Operand(scratch)); + + // if (hash == 0) hash = 27; + Label hash_not_zero; + __ test(hash, Operand(hash)); + __ j(not_zero, &hash_not_zero); + __ mov(hash, Immediate(27)); + __ bind(&hash_not_zero); +} + + void SubStringStub::Generate(MacroAssembler* masm) { Label runtime; @@ -9892,26 +10785,55 @@ void SubStringStub::Generate(MacroAssembler* masm) { // eax: string // ebx: instance type // Calculate length of sub string using the smi values. - __ mov(ecx, Operand(esp, 1 * kPointerSize)); // to + Label result_longer_than_two; + __ mov(ecx, Operand(esp, 1 * kPointerSize)); // To index. __ test(ecx, Immediate(kSmiTagMask)); __ j(not_zero, &runtime); - __ mov(edx, Operand(esp, 2 * kPointerSize)); // from + __ mov(edx, Operand(esp, 2 * kPointerSize)); // From index. __ test(edx, Immediate(kSmiTagMask)); __ j(not_zero, &runtime); __ sub(ecx, Operand(edx)); - // Handle sub-strings of length 2 and less in the runtime system. + // Special handling of sub-strings of length 1 and 2. One character strings + // are handled in the runtime system (looked up in the single character + // cache). Two character strings are looked for in the symbol cache. __ SmiUntag(ecx); // Result length is no longer smi. __ cmp(ecx, 2); - __ j(below_equal, &runtime); + __ j(greater, &result_longer_than_two); + __ j(less, &runtime); + + // Sub string of length 2 requested. + // eax: string + // ebx: instance type + // ecx: sub string length (value is 2) + // edx: from index (smi) + __ JumpIfInstanceTypeIsNotSequentialAscii(ebx, ebx, &runtime); + + // Get the two characters forming the sub string. + __ SmiUntag(edx); // From index is no longer smi. + __ movzx_b(ebx, FieldOperand(eax, edx, times_1, SeqAsciiString::kHeaderSize)); + __ movzx_b(ecx, + FieldOperand(eax, edx, times_1, SeqAsciiString::kHeaderSize + 1)); + + // Try to lookup two character string in symbol table. + Label make_two_character_string; + GenerateTwoCharacterSymbolTableProbe(masm, ebx, ecx, eax, edx, edi, + &make_two_character_string); + __ ret(2 * kPointerSize); + + __ bind(&make_two_character_string); + // Setup registers for allocating the two character string. + __ mov(eax, Operand(esp, 3 * kPointerSize)); + __ mov(ebx, FieldOperand(eax, HeapObject::kMapOffset)); + __ movzx_b(ebx, FieldOperand(ebx, Map::kInstanceTypeOffset)); + __ Set(ecx, Immediate(2)); + __ bind(&result_longer_than_two); // eax: string // ebx: instance type // ecx: result string length // Check for flat ascii string Label non_ascii_flat; - __ and_(ebx, kStringRepresentationMask | kStringEncodingMask); - __ cmp(ebx, kSeqStringTag | kAsciiStringTag); - __ j(not_equal, &non_ascii_flat); + __ JumpIfInstanceTypeIsNotSequentialAscii(ebx, ebx, &non_ascii_flat); // Allocate the result. __ AllocateAsciiString(eax, ecx, ebx, edx, edi, &runtime); diff --git a/src/ia32/codegen-ia32.h b/src/ia32/codegen-ia32.h index 843bbf76..b84a6bba 100644 --- a/src/ia32/codegen-ia32.h +++ b/src/ia32/codegen-ia32.h @@ -294,15 +294,6 @@ enum ArgumentsAllocationMode { class CodeGenerator: public AstVisitor { public: - // Compilation mode. Either the compiler is used as the primary - // compiler and needs to setup everything or the compiler is used as - // the secondary compiler for split compilation and has to handle - // bailouts. - enum Mode { - PRIMARY, - SECONDARY - }; - // Takes a function literal, generates code for it. This function should only // be called by compiler.cc. static Handle<Code> MakeCode(CompilationInfo* info); @@ -384,7 +375,7 @@ class CodeGenerator: public AstVisitor { void VisitStatementsAndSpill(ZoneList<Statement*>* statements); // Main code generation function - void Generate(CompilationInfo* info, Mode mode); + void Generate(CompilationInfo* info); // Generate the return sequence code. Should be called no more than // once per compiled function, immediately after binding the return @@ -429,8 +420,8 @@ class CodeGenerator: public AstVisitor { void LoadAndSpill(Expression* expression); // Read a value from a slot and leave it on top of the expression stack. - void LoadFromSlot(Slot* slot, TypeofState typeof_state); - void LoadFromSlotCheckForArguments(Slot* slot, TypeofState typeof_state); + Result LoadFromSlot(Slot* slot, TypeofState typeof_state); + Result LoadFromSlotCheckForArguments(Slot* slot, TypeofState typeof_state); Result LoadFromGlobalSlotCheckExtensions(Slot* slot, TypeofState typeof_state, JumpTarget* slow); @@ -439,10 +430,23 @@ class CodeGenerator: public AstVisitor { // value in place. void StoreToSlot(Slot* slot, InitState init_state); - // Load a property of an object, returning it in a Result. - // The object and the property name are passed on the stack, and - // not changed. - Result EmitKeyedLoad(bool is_global); + // Support for compiling assignment expressions. + void EmitSlotAssignment(Assignment* node); + void EmitNamedPropertyAssignment(Assignment* node); + void EmitKeyedPropertyAssignment(Assignment* node); + + // Receiver is passed on the frame and consumed. + Result EmitNamedLoad(Handle<String> name, bool is_contextual); + + // If the store is contextual, value is passed on the frame and consumed. + // Otherwise, receiver and value are passed on the frame and consumed. + Result EmitNamedStore(Handle<String> name, bool is_contextual); + + // Receiver and key are passed on the frame and consumed. + Result EmitKeyedLoad(); + + // Receiver, key, and value are passed on the frame and consumed. + Result EmitKeyedStore(StaticType* key_type); // Special code for typeof expressions: Unfortunately, we must // be careful when loading the expression in 'typeof' @@ -533,12 +537,13 @@ class CodeGenerator: public AstVisitor { void DeclareGlobals(Handle<FixedArray> pairs); // Instantiate the function boilerplate. - void InstantiateBoilerplate(Handle<JSFunction> boilerplate); + Result InstantiateBoilerplate(Handle<JSFunction> boilerplate); // Support for type checks. void GenerateIsSmi(ZoneList<Expression*>* args); void GenerateIsNonNegativeSmi(ZoneList<Expression*>* args); void GenerateIsArray(ZoneList<Expression*>* args); + void GenerateIsRegExp(ZoneList<Expression*>* args); void GenerateIsObject(ZoneList<Expression*>* args); void GenerateIsFunction(ZoneList<Expression*>* args); void GenerateIsUndetectableObject(ZoneList<Expression*>* args); @@ -580,6 +585,13 @@ class CodeGenerator: public AstVisitor { // Support for direct calls from JavaScript to native RegExp code. void GenerateRegExpExec(ZoneList<Expression*>* args); + // Fast support for number to string. + void GenerateNumberToString(ZoneList<Expression*>* args); + + // Fast call to transcendental functions. + void GenerateMathSin(ZoneList<Expression*>* args); + void GenerateMathCos(ZoneList<Expression*>* args); + // Simple condition analysis. enum ConditionAnalysis { ALWAYS_TRUE, @@ -647,6 +659,22 @@ class CodeGenerator: public AstVisitor { }; +// Compute a transcendental math function natively, or call the +// TranscendentalCache runtime function. +class TranscendentalCacheStub: public CodeStub { + public: + explicit TranscendentalCacheStub(TranscendentalCache::Type type) + : type_(type) {} + void Generate(MacroAssembler* masm); + private: + TranscendentalCache::Type type_; + Major MajorKey() { return TranscendentalCache; } + int MinorKey() { return type_; } + Runtime::FunctionId RuntimeFunction(); + void GenerateOperation(MacroAssembler* masm); +}; + + // Flag that indicates how to generate code for the stub GenericBinaryOpStub. enum GenericBinaryFlags { NO_GENERIC_BINARY_FLAGS = 0, @@ -658,13 +686,15 @@ class GenericBinaryOpStub: public CodeStub { public: GenericBinaryOpStub(Token::Value op, OverwriteMode mode, - GenericBinaryFlags flags) + GenericBinaryFlags flags, + NumberInfo::Type operands_type = NumberInfo::kUnknown) : op_(op), mode_(mode), flags_(flags), args_in_registers_(false), args_reversed_(false), - name_(NULL) { + name_(NULL), + operands_type_(operands_type) { use_sse3_ = CpuFeatures::IsSupported(SSE3); ASSERT(OpBits::is_valid(Token::NUM_TOKENS)); } @@ -689,28 +719,32 @@ class GenericBinaryOpStub: public CodeStub { bool args_reversed_; // Left and right argument are swapped. bool use_sse3_; char* name_; + NumberInfo::Type operands_type_; // Number type information of operands. const char* GetName(); #ifdef DEBUG void Print() { - PrintF("GenericBinaryOpStub (op %s), " - "(mode %d, flags %d, registers %d, reversed %d)\n", + PrintF("GenericBinaryOpStub %d (op %s), " + "(mode %d, flags %d, registers %d, reversed %d, number_info %s)\n", + MinorKey(), Token::String(op_), static_cast<int>(mode_), static_cast<int>(flags_), static_cast<int>(args_in_registers_), - static_cast<int>(args_reversed_)); + static_cast<int>(args_reversed_), + NumberInfo::ToString(operands_type_)); } #endif - // Minor key encoding in 16 bits FRASOOOOOOOOOOMM. + // Minor key encoding in 16 bits NNNFRASOOOOOOOMM. class ModeBits: public BitField<OverwriteMode, 0, 2> {}; - class OpBits: public BitField<Token::Value, 2, 10> {}; - class SSE3Bits: public BitField<bool, 12, 1> {}; - class ArgsInRegistersBits: public BitField<bool, 13, 1> {}; - class ArgsReversedBits: public BitField<bool, 14, 1> {}; - class FlagBits: public BitField<GenericBinaryFlags, 15, 1> {}; + class OpBits: public BitField<Token::Value, 2, 7> {}; + class SSE3Bits: public BitField<bool, 9, 1> {}; + class ArgsInRegistersBits: public BitField<bool, 10, 1> {}; + class ArgsReversedBits: public BitField<bool, 11, 1> {}; + class FlagBits: public BitField<GenericBinaryFlags, 12, 1> {}; + class NumberInfoBits: public BitField<NumberInfo::Type, 13, 3> {}; Major MajorKey() { return GenericBinaryOp; } int MinorKey() { @@ -720,7 +754,8 @@ class GenericBinaryOpStub: public CodeStub { | FlagBits::encode(flags_) | SSE3Bits::encode(use_sse3_) | ArgsInRegistersBits::encode(args_in_registers_) - | ArgsReversedBits::encode(args_reversed_); + | ArgsReversedBits::encode(args_reversed_) + | NumberInfoBits::encode(operands_type_); } void Generate(MacroAssembler* masm); @@ -767,6 +802,31 @@ class StringStubBase: public CodeStub { Register count, // Must be ecx. Register scratch, // Neither of the above. bool ascii); + + // Probe the symbol table for a two character string. If the string is + // not found by probing a jump to the label not_found is performed. This jump + // does not guarantee that the string is not in the symbol table. If the + // string is found the code falls through with the string in register eax. + void GenerateTwoCharacterSymbolTableProbe(MacroAssembler* masm, + Register c1, + Register c2, + Register scratch1, + Register scratch2, + Register scratch3, + Label* not_found); + + // Generate string hash. + void GenerateHashInit(MacroAssembler* masm, + Register hash, + Register character, + Register scratch); + void GenerateHashAddCharacter(MacroAssembler* masm, + Register hash, + Register character, + Register scratch); + void GenerateHashGetHash(MacroAssembler* masm, + Register hash, + Register scratch); }; @@ -828,6 +888,39 @@ class StringCompareStub: public StringStubBase { }; +class NumberToStringStub: public CodeStub { + public: + NumberToStringStub() { } + + // Generate code to do a lookup in the number string cache. If the number in + // the register object is found in the cache the generated code falls through + // with the result in the result register. The object and the result register + // can be the same. If the number is not found in the cache the code jumps to + // the label not_found with only the content of register object unchanged. + static void GenerateLookupNumberStringCache(MacroAssembler* masm, + Register object, + Register result, + Register scratch1, + Register scratch2, + bool object_is_smi, + Label* not_found); + + private: + Major MajorKey() { return NumberToString; } + int MinorKey() { return 0; } + + void Generate(MacroAssembler* masm); + + const char* GetName() { return "NumberToStringStub"; } + +#ifdef DEBUG + void Print() { + PrintF("NumberToStringStub\n"); + } +#endif +}; + + } } // namespace v8::internal #endif // V8_IA32_CODEGEN_IA32_H_ diff --git a/src/ia32/debug-ia32.cc b/src/ia32/debug-ia32.cc index 1f34b302..a9e26263 100644 --- a/src/ia32/debug-ia32.cc +++ b/src/ia32/debug-ia32.cc @@ -125,9 +125,10 @@ static void Generate_DebugBreakCallHelper(MacroAssembler* masm, void Debug::GenerateLoadICDebugBreak(MacroAssembler* masm) { // Register state for IC load call (from ic-ia32.cc). // ----------- S t a t e ------------- + // -- eax : receiver // -- ecx : name // ----------------------------------- - Generate_DebugBreakCallHelper(masm, ecx.bit(), false); + Generate_DebugBreakCallHelper(masm, eax.bit() | ecx.bit(), false); } diff --git a/src/ia32/disasm-ia32.cc b/src/ia32/disasm-ia32.cc index cb500d56..a085900a 100644 --- a/src/ia32/disasm-ia32.cc +++ b/src/ia32/disasm-ia32.cc @@ -679,6 +679,7 @@ int DisassemblerIA32::MemoryFPUInstruction(int escape_opcode, case 0xDD: switch (regop) { case 0: mnem = "fld_d"; break; + case 2: mnem = "fstp"; break; case 3: mnem = "fstp_d"; break; default: UnimplementedInstruction(); } @@ -720,6 +721,7 @@ int DisassemblerIA32::RegisterFPUInstruction(int escape_opcode, case 0xE1: mnem = "fabs"; break; case 0xE4: mnem = "ftst"; break; case 0xE8: mnem = "fld1"; break; + case 0xEB: mnem = "fldpi"; break; case 0xEE: mnem = "fldz"; break; case 0xF5: mnem = "fprem1"; break; case 0xF7: mnem = "fincstp"; break; @@ -1014,7 +1016,6 @@ int DisassemblerIA32::InstructionDecode(v8::internal::Vector<char> out_buffer, int mod, regop, rm; get_modrm(*data, &mod, ®op, &rm); const char* mnem = NULL; - printf("%d\n", regop); switch (regop) { case 5: mnem = "subb"; break; case 7: mnem = "cmpb"; break; diff --git a/src/ia32/fast-codegen-ia32.cc b/src/ia32/fast-codegen-ia32.cc index 126f96b3..f1c25077 100644 --- a/src/ia32/fast-codegen-ia32.cc +++ b/src/ia32/fast-codegen-ia32.cc @@ -29,56 +29,474 @@ #include "codegen-inl.h" #include "fast-codegen.h" +#include "data-flow.h" +#include "scopes.h" namespace v8 { namespace internal { -#define __ ACCESS_MASM(masm()) +#define BAILOUT(reason) \ + do { \ + if (FLAG_trace_bailout) { \ + PrintF("%s\n", reason); \ + } \ + has_supported_syntax_ = false; \ + return; \ + } while (false) + + +#define CHECK_BAILOUT \ + do { \ + if (!has_supported_syntax_) return; \ + } while (false) + + +void FastCodeGenSyntaxChecker::Check(CompilationInfo* info) { + info_ = info; + + // We do not specialize if we do not have a receiver or if it is not a + // JS object with fast mode properties. + if (!info->has_receiver()) BAILOUT("No receiver"); + if (!info->receiver()->IsJSObject()) BAILOUT("Receiver is not an object"); + Handle<JSObject> object = Handle<JSObject>::cast(info->receiver()); + if (!object->HasFastProperties()) BAILOUT("Receiver is in dictionary mode"); + + // We do not support stack or heap slots (both of which require + // allocation). + Scope* scope = info->scope(); + if (scope->num_stack_slots() > 0) { + BAILOUT("Function has stack-allocated locals"); + } + if (scope->num_heap_slots() > 0) { + BAILOUT("Function has context-allocated locals"); + } -void FastCodeGenerator::EmitLoadReceiver(Register reg) { - // Offset 2 is due to return address and saved frame pointer. - int index = 2 + function()->scope()->num_parameters(); - __ mov(reg, Operand(ebp, index * kPointerSize)); + VisitDeclarations(scope->declarations()); + CHECK_BAILOUT; + + // We do not support empty function bodies. + if (info->function()->body()->is_empty()) { + BAILOUT("Function has an empty body"); + } + VisitStatements(info->function()->body()); } -void FastCodeGenerator::EmitReceiverMapCheck() { - Comment cmnt(masm(), ";; MapCheck(this)"); - if (FLAG_print_ir) { - PrintF("MapCheck(this)\n"); +void FastCodeGenSyntaxChecker::VisitDeclarations( + ZoneList<Declaration*>* decls) { + if (!decls->is_empty()) BAILOUT("Function has declarations"); +} + + +void FastCodeGenSyntaxChecker::VisitStatements(ZoneList<Statement*>* stmts) { + if (stmts->length() != 1) { + BAILOUT("Function body is not a singleton statement."); } + Visit(stmts->at(0)); +} - ASSERT(info()->has_receiver() && info()->receiver()->IsHeapObject()); - Handle<HeapObject> object = Handle<HeapObject>::cast(info()->receiver()); - Handle<Map> map(object->map()); - EmitLoadReceiver(edx); - __ CheckMap(edx, map, bailout(), false); +void FastCodeGenSyntaxChecker::VisitDeclaration(Declaration* decl) { + UNREACHABLE(); } -void FastCodeGenerator::EmitGlobalMapCheck() { - Comment cmnt(masm(), ";; GlobalMapCheck"); - if (FLAG_print_ir) { - PrintF(";; GlobalMapCheck()"); +void FastCodeGenSyntaxChecker::VisitBlock(Block* stmt) { + VisitStatements(stmt->statements()); +} + + +void FastCodeGenSyntaxChecker::VisitExpressionStatement( + ExpressionStatement* stmt) { + Visit(stmt->expression()); +} + + +void FastCodeGenSyntaxChecker::VisitEmptyStatement(EmptyStatement* stmt) { + // Supported. +} + + +void FastCodeGenSyntaxChecker::VisitIfStatement(IfStatement* stmt) { + BAILOUT("IfStatement"); +} + + +void FastCodeGenSyntaxChecker::VisitContinueStatement(ContinueStatement* stmt) { + BAILOUT("Continuestatement"); +} + + +void FastCodeGenSyntaxChecker::VisitBreakStatement(BreakStatement* stmt) { + BAILOUT("BreakStatement"); +} + + +void FastCodeGenSyntaxChecker::VisitReturnStatement(ReturnStatement* stmt) { + BAILOUT("ReturnStatement"); +} + + +void FastCodeGenSyntaxChecker::VisitWithEnterStatement( + WithEnterStatement* stmt) { + BAILOUT("WithEnterStatement"); +} + + +void FastCodeGenSyntaxChecker::VisitWithExitStatement(WithExitStatement* stmt) { + BAILOUT("WithExitStatement"); +} + + +void FastCodeGenSyntaxChecker::VisitSwitchStatement(SwitchStatement* stmt) { + BAILOUT("SwitchStatement"); +} + + +void FastCodeGenSyntaxChecker::VisitDoWhileStatement(DoWhileStatement* stmt) { + BAILOUT("DoWhileStatement"); +} + + +void FastCodeGenSyntaxChecker::VisitWhileStatement(WhileStatement* stmt) { + BAILOUT("WhileStatement"); +} + + +void FastCodeGenSyntaxChecker::VisitForStatement(ForStatement* stmt) { + BAILOUT("ForStatement"); +} + + +void FastCodeGenSyntaxChecker::VisitForInStatement(ForInStatement* stmt) { + BAILOUT("ForInStatement"); +} + + +void FastCodeGenSyntaxChecker::VisitTryCatchStatement(TryCatchStatement* stmt) { + BAILOUT("TryCatchStatement"); +} + + +void FastCodeGenSyntaxChecker::VisitTryFinallyStatement( + TryFinallyStatement* stmt) { + BAILOUT("TryFinallyStatement"); +} + + +void FastCodeGenSyntaxChecker::VisitDebuggerStatement( + DebuggerStatement* stmt) { + BAILOUT("DebuggerStatement"); +} + + +void FastCodeGenSyntaxChecker::VisitFunctionLiteral(FunctionLiteral* expr) { + BAILOUT("FunctionLiteral"); +} + + +void FastCodeGenSyntaxChecker::VisitFunctionBoilerplateLiteral( + FunctionBoilerplateLiteral* expr) { + BAILOUT("FunctionBoilerplateLiteral"); +} + + +void FastCodeGenSyntaxChecker::VisitConditional(Conditional* expr) { + BAILOUT("Conditional"); +} + + +void FastCodeGenSyntaxChecker::VisitSlot(Slot* expr) { + UNREACHABLE(); +} + + +void FastCodeGenSyntaxChecker::VisitVariableProxy(VariableProxy* expr) { + // Only global variable references are supported. + Variable* var = expr->var(); + if (!var->is_global() || var->is_this()) BAILOUT("Non-global variable"); + + // Check if the global variable is existing and non-deletable. + if (info()->has_global_object()) { + LookupResult lookup; + info()->global_object()->Lookup(*expr->name(), &lookup); + if (!lookup.IsProperty()) { + BAILOUT("Non-existing global variable"); + } + // We do not handle global variables with accessors or interceptors. + if (lookup.type() != NORMAL) { + BAILOUT("Global variable with accessors or interceptors."); + } + // We do not handle deletable global variables. + if (!lookup.IsDontDelete()) { + BAILOUT("Deletable global variable"); + } } +} + + +void FastCodeGenSyntaxChecker::VisitLiteral(Literal* expr) { + BAILOUT("Literal"); +} - ASSERT(info()->has_global_object()); - Handle<Map> map(info()->global_object()->map()); - __ mov(ebx, CodeGenerator::GlobalObject()); - __ CheckMap(ebx, map, bailout(), true); +void FastCodeGenSyntaxChecker::VisitRegExpLiteral(RegExpLiteral* expr) { + BAILOUT("RegExpLiteral"); +} + + +void FastCodeGenSyntaxChecker::VisitObjectLiteral(ObjectLiteral* expr) { + BAILOUT("ObjectLiteral"); +} + + +void FastCodeGenSyntaxChecker::VisitArrayLiteral(ArrayLiteral* expr) { + BAILOUT("ArrayLiteral"); +} + + +void FastCodeGenSyntaxChecker::VisitCatchExtensionObject( + CatchExtensionObject* expr) { + BAILOUT("CatchExtensionObject"); +} + + +void FastCodeGenSyntaxChecker::VisitAssignment(Assignment* expr) { + // Simple assignments to (named) this properties are supported. + if (expr->op() != Token::ASSIGN) BAILOUT("Non-simple assignment"); + + Property* prop = expr->target()->AsProperty(); + if (prop == NULL) BAILOUT("Non-property assignment"); + VariableProxy* proxy = prop->obj()->AsVariableProxy(); + if (proxy == NULL || !proxy->var()->is_this()) { + BAILOUT("Non-this-property assignment"); + } + if (!prop->key()->IsPropertyName()) { + BAILOUT("Non-named-property assignment"); + } + + // We will only specialize for fields on the object itself. + // Expression::IsPropertyName implies that the name is a literal + // symbol but we do not assume that. + Literal* key = prop->key()->AsLiteral(); + if (key != NULL && key->handle()->IsString()) { + Handle<Object> receiver = info()->receiver(); + Handle<String> name = Handle<String>::cast(key->handle()); + LookupResult lookup; + receiver->Lookup(*name, &lookup); + if (!lookup.IsProperty()) { + BAILOUT("Assigned property not found at compile time"); + } + if (lookup.holder() != *receiver) BAILOUT("Non-own property assignment"); + if (!lookup.type() == FIELD) BAILOUT("Non-field property assignment"); + } else { + UNREACHABLE(); + BAILOUT("Unexpected non-string-literal property key"); + } + + Visit(expr->value()); +} + + +void FastCodeGenSyntaxChecker::VisitThrow(Throw* expr) { + BAILOUT("Throw"); +} + + +void FastCodeGenSyntaxChecker::VisitProperty(Property* expr) { + // We support named this property references. + VariableProxy* proxy = expr->obj()->AsVariableProxy(); + if (proxy == NULL || !proxy->var()->is_this()) { + BAILOUT("Non-this-property reference"); + } + if (!expr->key()->IsPropertyName()) { + BAILOUT("Non-named-property reference"); + } + + // We will only specialize for fields on the object itself. + // Expression::IsPropertyName implies that the name is a literal + // symbol but we do not assume that. + Literal* key = expr->key()->AsLiteral(); + if (key != NULL && key->handle()->IsString()) { + Handle<Object> receiver = info()->receiver(); + Handle<String> name = Handle<String>::cast(key->handle()); + LookupResult lookup; + receiver->Lookup(*name, &lookup); + if (!lookup.IsProperty()) { + BAILOUT("Referenced property not found at compile time"); + } + if (lookup.holder() != *receiver) BAILOUT("Non-own property reference"); + if (!lookup.type() == FIELD) BAILOUT("Non-field property reference"); + } else { + UNREACHABLE(); + BAILOUT("Unexpected non-string-literal property key"); + } +} + + +void FastCodeGenSyntaxChecker::VisitCall(Call* expr) { + BAILOUT("Call"); +} + + +void FastCodeGenSyntaxChecker::VisitCallNew(CallNew* expr) { + BAILOUT("CallNew"); +} + + +void FastCodeGenSyntaxChecker::VisitCallRuntime(CallRuntime* expr) { + BAILOUT("CallRuntime"); +} + + +void FastCodeGenSyntaxChecker::VisitUnaryOperation(UnaryOperation* expr) { + BAILOUT("UnaryOperation"); +} + + +void FastCodeGenSyntaxChecker::VisitCountOperation(CountOperation* expr) { + BAILOUT("CountOperation"); +} + + +void FastCodeGenSyntaxChecker::VisitBinaryOperation(BinaryOperation* expr) { + // We support bitwise OR. + switch (expr->op()) { + case Token::COMMA: + BAILOUT("BinaryOperation COMMA"); + case Token::OR: + BAILOUT("BinaryOperation OR"); + case Token::AND: + BAILOUT("BinaryOperation AND"); + + case Token::BIT_OR: + // We support expressions nested on the left because they only require + // a pair of registers to keep all intermediate values in registers + // (i.e., the expression stack has height no more than two). + if (!expr->right()->IsLeaf()) BAILOUT("expression nested on right"); + + // We do not allow subexpressions with side effects because we + // (currently) bail out to the beginning of the full function. The + // only expressions with side effects that we would otherwise handle + // are assignments. + if (expr->left()->AsAssignment() != NULL || + expr->right()->AsAssignment() != NULL) { + BAILOUT("subexpression of binary operation has side effects"); + } + + Visit(expr->left()); + CHECK_BAILOUT; + Visit(expr->right()); + break; + + case Token::BIT_XOR: + BAILOUT("BinaryOperation BIT_XOR"); + case Token::BIT_AND: + BAILOUT("BinaryOperation BIT_AND"); + case Token::SHL: + BAILOUT("BinaryOperation SHL"); + case Token::SAR: + BAILOUT("BinaryOperation SAR"); + case Token::SHR: + BAILOUT("BinaryOperation SHR"); + case Token::ADD: + BAILOUT("BinaryOperation ADD"); + case Token::SUB: + BAILOUT("BinaryOperation SUB"); + case Token::MUL: + BAILOUT("BinaryOperation MUL"); + case Token::DIV: + BAILOUT("BinaryOperation DIV"); + case Token::MOD: + BAILOUT("BinaryOperation MOD"); + default: + UNREACHABLE(); + } +} + + +void FastCodeGenSyntaxChecker::VisitCompareOperation(CompareOperation* expr) { + BAILOUT("CompareOperation"); +} + + +void FastCodeGenSyntaxChecker::VisitThisFunction(ThisFunction* expr) { + BAILOUT("ThisFunction"); +} + +#undef BAILOUT +#undef CHECK_BAILOUT + + +#define __ ACCESS_MASM(masm()) + +Handle<Code> FastCodeGenerator::MakeCode(CompilationInfo* info) { + // Label the AST before calling MakeCodePrologue, so AST node numbers are + // printed with the AST. + AstLabeler labeler; + labeler.Label(info); + + LivenessAnalyzer analyzer; + analyzer.Analyze(info->function()); + + CodeGenerator::MakeCodePrologue(info); + + const int kInitialBufferSize = 4 * KB; + MacroAssembler masm(NULL, kInitialBufferSize); + + // Generate the fast-path code. + FastCodeGenerator fast_cgen(&masm); + fast_cgen.Generate(info); + if (fast_cgen.HasStackOverflow()) { + ASSERT(!Top::has_pending_exception()); + return Handle<Code>::null(); + } + + // Generate the full code for the function in bailout mode, using the same + // macro assembler. + CodeGenerator cgen(&masm); + CodeGeneratorScope scope(&cgen); + info->set_mode(CompilationInfo::SECONDARY); + cgen.Generate(info); + if (cgen.HasStackOverflow()) { + ASSERT(!Top::has_pending_exception()); + return Handle<Code>::null(); + } + + Code::Flags flags = Code::ComputeFlags(Code::FUNCTION, NOT_IN_LOOP); + return CodeGenerator::MakeCodeEpilogue(&masm, flags, info); +} + + +Register FastCodeGenerator::accumulator0() { return eax; } +Register FastCodeGenerator::accumulator1() { return edx; } +Register FastCodeGenerator::scratch0() { return ecx; } +Register FastCodeGenerator::scratch1() { return edi; } +Register FastCodeGenerator::receiver_reg() { return ebx; } +Register FastCodeGenerator::context_reg() { return esi; } + + +void FastCodeGenerator::EmitLoadReceiver() { + // Offset 2 is due to return address and saved frame pointer. + int index = 2 + function()->scope()->num_parameters(); + __ mov(receiver_reg(), Operand(ebp, index * kPointerSize)); } void FastCodeGenerator::EmitGlobalVariableLoad(Handle<Object> cell) { + ASSERT(!destination().is(no_reg)); ASSERT(cell->IsJSGlobalPropertyCell()); - __ mov(eax, Immediate(cell)); - __ mov(eax, FieldOperand(eax, JSGlobalPropertyCell::kValueOffset)); + + __ mov(destination(), Immediate(cell)); + __ mov(destination(), + FieldOperand(destination(), JSGlobalPropertyCell::kValueOffset)); if (FLAG_debug_code) { - __ cmp(eax, Factory::the_hole_value()); + __ cmp(destination(), Factory::the_hole_value()); __ Check(not_equal, "DontDelete cells can't contain the hole"); } + + // The loaded value is not known to be a smi. + clear_as_smi(destination()); } @@ -92,19 +510,111 @@ void FastCodeGenerator::EmitThisPropertyStore(Handle<String> name) { int index = lookup.GetFieldIndex() - map->inobject_properties(); int offset = index * kPointerSize; - // Negative offsets are inobject properties. + // We will emit the write barrier unless the stored value is statically + // known to be a smi. + bool needs_write_barrier = !is_smi(accumulator0()); + + // Perform the store. Negative offsets are inobject properties. if (offset < 0) { offset += map->instance_size(); - __ mov(ecx, edx); // Copy receiver for write barrier. + __ mov(FieldOperand(receiver_reg(), offset), accumulator0()); + if (needs_write_barrier) { + // Preserve receiver from write barrier. + __ mov(scratch0(), receiver_reg()); + } } else { offset += FixedArray::kHeaderSize; - __ mov(ecx, FieldOperand(edx, JSObject::kPropertiesOffset)); + __ mov(scratch0(), + FieldOperand(receiver_reg(), JSObject::kPropertiesOffset)); + __ mov(FieldOperand(scratch0(), offset), accumulator0()); } - // Perform the store. - __ mov(FieldOperand(ecx, offset), eax); - // Preserve value from write barrier in case it's needed. - __ mov(ebx, eax); - __ RecordWrite(ecx, offset, ebx, edi); + + if (needs_write_barrier) { + if (destination().is(no_reg)) { + // After RecordWrite accumulator0 is only accidently a smi, but it is + // already marked as not known to be one. + __ RecordWrite(scratch0(), offset, accumulator0(), scratch1()); + } else { + // Copy the value to the other accumulator to preserve a copy from the + // write barrier. One of the accumulators is available as a scratch + // register. Neither is a smi. + __ mov(accumulator1(), accumulator0()); + clear_as_smi(accumulator1()); + Register value_scratch = other_accumulator(destination()); + __ RecordWrite(scratch0(), offset, value_scratch, scratch1()); + } + } else if (destination().is(accumulator1())) { + __ mov(accumulator1(), accumulator0()); + // Is a smi because we do not need the write barrier. + set_as_smi(accumulator1()); + } +} + + +void FastCodeGenerator::EmitThisPropertyLoad(Handle<String> name) { + ASSERT(!destination().is(no_reg)); + LookupResult lookup; + info()->receiver()->Lookup(*name, &lookup); + + ASSERT(lookup.holder() == *info()->receiver()); + ASSERT(lookup.type() == FIELD); + Handle<Map> map(Handle<HeapObject>::cast(info()->receiver())->map()); + int index = lookup.GetFieldIndex() - map->inobject_properties(); + int offset = index * kPointerSize; + + // Perform the load. Negative offsets are inobject properties. + if (offset < 0) { + offset += map->instance_size(); + __ mov(destination(), FieldOperand(receiver_reg(), offset)); + } else { + offset += FixedArray::kHeaderSize; + __ mov(scratch0(), + FieldOperand(receiver_reg(), JSObject::kPropertiesOffset)); + __ mov(destination(), FieldOperand(scratch0(), offset)); + } + + // The loaded value is not known to be a smi. + clear_as_smi(destination()); +} + + +void FastCodeGenerator::EmitBitOr() { + if (is_smi(accumulator0()) && is_smi(accumulator1())) { + // If both operands are known to be a smi then there is no need to check + // the operands or result. There is no need to perform the operation in + // an effect context. + if (!destination().is(no_reg)) { + // Leave the result in the destination register. Bitwise or is + // commutative. + __ or_(destination(), Operand(other_accumulator(destination()))); + } + } else { + // Left is in accumulator1, right in accumulator0. + Label* bailout = NULL; + if (destination().is(accumulator0())) { + __ mov(scratch0(), accumulator0()); + __ or_(destination(), Operand(accumulator1())); // Or is commutative. + __ test(destination(), Immediate(kSmiTagMask)); + bailout = info()->AddBailout(accumulator1(), scratch0()); // Left, right. + } else if (destination().is(accumulator1())) { + __ mov(scratch0(), accumulator1()); + __ or_(destination(), Operand(accumulator0())); + __ test(destination(), Immediate(kSmiTagMask)); + bailout = info()->AddBailout(scratch0(), accumulator0()); + } else { + ASSERT(destination().is(no_reg)); + __ mov(scratch0(), accumulator1()); + __ or_(scratch0(), Operand(accumulator0())); + __ test(scratch0(), Immediate(kSmiTagMask)); + bailout = info()->AddBailout(accumulator1(), accumulator0()); + } + __ j(not_zero, bailout, not_taken); + } + + // If we didn't bailout, the result (in fact, both inputs too) is known to + // be a smi. + set_as_smi(accumulator0()); + set_as_smi(accumulator1()); } @@ -121,27 +631,323 @@ void FastCodeGenerator::Generate(CompilationInfo* compilation_info) { // Note that we keep a live register reference to esi (context) at this // point. - // Receiver (this) is allocated to edx if there are this properties. - if (info()->has_this_properties()) EmitReceiverMapCheck(); + Label* bailout_to_beginning = info()->AddBailout(); + // Receiver (this) is allocated to a fixed register. + if (info()->has_this_properties()) { + Comment cmnt(masm(), ";; MapCheck(this)"); + if (FLAG_print_ir) { + PrintF("#: MapCheck(this)\n"); + } + ASSERT(info()->has_receiver() && info()->receiver()->IsHeapObject()); + Handle<HeapObject> object = Handle<HeapObject>::cast(info()->receiver()); + Handle<Map> map(object->map()); + EmitLoadReceiver(); + __ CheckMap(receiver_reg(), map, bailout_to_beginning, false); + } - // If there is a global variable access check if the global object - // is the same as at lazy-compilation time. - if (info()->has_globals()) EmitGlobalMapCheck(); + // If there is a global variable access check if the global object is the + // same as at lazy-compilation time. + if (info()->has_globals()) { + Comment cmnt(masm(), ";; MapCheck(GLOBAL)"); + if (FLAG_print_ir) { + PrintF("#: MapCheck(GLOBAL)\n"); + } + ASSERT(info()->has_global_object()); + Handle<Map> map(info()->global_object()->map()); + __ mov(scratch0(), CodeGenerator::GlobalObject()); + __ CheckMap(scratch0(), map, bailout_to_beginning, true); + } VisitStatements(function()->body()); Comment return_cmnt(masm(), ";; Return(<undefined>)"); + if (FLAG_print_ir) { + PrintF("#: Return(<undefined>)\n"); + } __ mov(eax, Factory::undefined_value()); - - Comment epilogue_cmnt(masm(), ";; Epilogue"); __ mov(esp, ebp); __ pop(ebp); __ ret((scope()->num_parameters() + 1) * kPointerSize); +} + + +void FastCodeGenerator::VisitDeclaration(Declaration* decl) { + UNREACHABLE(); +} + + +void FastCodeGenerator::VisitBlock(Block* stmt) { + VisitStatements(stmt->statements()); +} + + +void FastCodeGenerator::VisitExpressionStatement(ExpressionStatement* stmt) { + Visit(stmt->expression()); +} + + +void FastCodeGenerator::VisitEmptyStatement(EmptyStatement* stmt) { + // Nothing to do. +} + + +void FastCodeGenerator::VisitIfStatement(IfStatement* stmt) { + UNREACHABLE(); +} + + +void FastCodeGenerator::VisitContinueStatement(ContinueStatement* stmt) { + UNREACHABLE(); +} + + +void FastCodeGenerator::VisitBreakStatement(BreakStatement* stmt) { + UNREACHABLE(); +} + + +void FastCodeGenerator::VisitReturnStatement(ReturnStatement* stmt) { + UNREACHABLE(); +} + + +void FastCodeGenerator::VisitWithEnterStatement(WithEnterStatement* stmt) { + UNREACHABLE(); +} + + +void FastCodeGenerator::VisitWithExitStatement(WithExitStatement* stmt) { + UNREACHABLE(); +} + + +void FastCodeGenerator::VisitSwitchStatement(SwitchStatement* stmt) { + UNREACHABLE(); +} + + +void FastCodeGenerator::VisitDoWhileStatement(DoWhileStatement* stmt) { + UNREACHABLE(); +} + + +void FastCodeGenerator::VisitWhileStatement(WhileStatement* stmt) { + UNREACHABLE(); +} + + +void FastCodeGenerator::VisitForStatement(ForStatement* stmt) { + UNREACHABLE(); +} + + +void FastCodeGenerator::VisitForInStatement(ForInStatement* stmt) { + UNREACHABLE(); +} + + +void FastCodeGenerator::VisitTryCatchStatement(TryCatchStatement* stmt) { + UNREACHABLE(); +} + - __ bind(&bailout_); +void FastCodeGenerator::VisitTryFinallyStatement(TryFinallyStatement* stmt) { + UNREACHABLE(); } +void FastCodeGenerator::VisitDebuggerStatement(DebuggerStatement* stmt) { + UNREACHABLE(); +} + + +void FastCodeGenerator::VisitFunctionLiteral(FunctionLiteral* expr) { + UNREACHABLE(); +} + + +void FastCodeGenerator::VisitFunctionBoilerplateLiteral( + FunctionBoilerplateLiteral* expr) { + UNREACHABLE(); +} + + +void FastCodeGenerator::VisitConditional(Conditional* expr) { + UNREACHABLE(); +} + + +void FastCodeGenerator::VisitSlot(Slot* expr) { + UNREACHABLE(); +} + + +void FastCodeGenerator::VisitVariableProxy(VariableProxy* expr) { + ASSERT(expr->var()->is_global() && !expr->var()->is_this()); + // Check if we can compile a global variable load directly from the cell. + ASSERT(info()->has_global_object()); + LookupResult lookup; + info()->global_object()->Lookup(*expr->name(), &lookup); + // We only support normal (non-accessor/interceptor) DontDelete properties + // for now. + ASSERT(lookup.IsProperty()); + ASSERT_EQ(NORMAL, lookup.type()); + ASSERT(lookup.IsDontDelete()); + Handle<Object> cell(info()->global_object()->GetPropertyCell(&lookup)); + + // Global variable lookups do not have side effects, so we do not need to + // emit code if we are in an effect context. + if (!destination().is(no_reg)) { + Comment cmnt(masm(), ";; Global"); + if (FLAG_print_ir) { + SmartPointer<char> name = expr->name()->ToCString(); + PrintF("%d: t%d = Global(%s) // last_use = %d\n", expr->num(), + expr->num(), *name, expr->var_def()->last_use()->num()); + } + EmitGlobalVariableLoad(cell); + } +} + + +void FastCodeGenerator::VisitLiteral(Literal* expr) { + UNREACHABLE(); +} + + +void FastCodeGenerator::VisitRegExpLiteral(RegExpLiteral* expr) { + UNREACHABLE(); +} + + +void FastCodeGenerator::VisitObjectLiteral(ObjectLiteral* expr) { + UNREACHABLE(); +} + + +void FastCodeGenerator::VisitArrayLiteral(ArrayLiteral* expr) { + UNREACHABLE(); +} + + +void FastCodeGenerator::VisitCatchExtensionObject(CatchExtensionObject* expr) { + UNREACHABLE(); +} + + +void FastCodeGenerator::VisitAssignment(Assignment* expr) { + // Known to be a simple this property assignment. Effectively a unary + // operation. + { Register my_destination = destination(); + set_destination(accumulator0()); + Visit(expr->value()); + set_destination(my_destination); + } + + Property* prop = expr->target()->AsProperty(); + ASSERT_NOT_NULL(prop); + ASSERT_NOT_NULL(prop->obj()->AsVariableProxy()); + ASSERT(prop->obj()->AsVariableProxy()->var()->is_this()); + ASSERT(prop->key()->IsPropertyName()); + Handle<String> name = + Handle<String>::cast(prop->key()->AsLiteral()->handle()); + + Comment cmnt(masm(), ";; Store to this"); + if (FLAG_print_ir) { + SmartPointer<char> name_string = name->ToCString(); + PrintF("%d: ", expr->num()); + if (!destination().is(no_reg)) PrintF("t%d = ", expr->num()); + PrintF("Store(this, \"%s\", t%d) // last_use(this) = %d\n", *name_string, + expr->value()->num(), + expr->var_def()->last_use()->num()); + } + + EmitThisPropertyStore(name); +} + + +void FastCodeGenerator::VisitThrow(Throw* expr) { + UNREACHABLE(); +} + + +void FastCodeGenerator::VisitProperty(Property* expr) { + ASSERT_NOT_NULL(expr->obj()->AsVariableProxy()); + ASSERT(expr->obj()->AsVariableProxy()->var()->is_this()); + ASSERT(expr->key()->IsPropertyName()); + if (!destination().is(no_reg)) { + Handle<String> name = + Handle<String>::cast(expr->key()->AsLiteral()->handle()); + + Comment cmnt(masm(), ";; Load from this"); + if (FLAG_print_ir) { + SmartPointer<char> name_string = name->ToCString(); + PrintF("%d: t%d = Load(this, \"%s\") // last_use(this) = %d\n", + expr->num(), expr->num(), *name_string, + expr->var_def()->last_use()->num()); + } + EmitThisPropertyLoad(name); + } +} + + +void FastCodeGenerator::VisitCall(Call* expr) { + UNREACHABLE(); +} + + +void FastCodeGenerator::VisitCallNew(CallNew* expr) { + UNREACHABLE(); +} + + +void FastCodeGenerator::VisitCallRuntime(CallRuntime* expr) { + UNREACHABLE(); +} + + +void FastCodeGenerator::VisitUnaryOperation(UnaryOperation* expr) { + UNREACHABLE(); +} + + +void FastCodeGenerator::VisitCountOperation(CountOperation* expr) { + UNREACHABLE(); +} + + +void FastCodeGenerator::VisitBinaryOperation(BinaryOperation* expr) { + // We support limited binary operations: bitwise OR only allowed to be + // nested on the left. + ASSERT(expr->op() == Token::BIT_OR); + ASSERT(expr->right()->IsLeaf()); + + { Register my_destination = destination(); + set_destination(accumulator1()); + Visit(expr->left()); + set_destination(accumulator0()); + Visit(expr->right()); + set_destination(my_destination); + } + + Comment cmnt(masm(), ";; BIT_OR"); + if (FLAG_print_ir) { + PrintF("%d: ", expr->num()); + if (!destination().is(no_reg)) PrintF("t%d = ", expr->num()); + PrintF("BIT_OR(t%d, t%d)\n", expr->left()->num(), expr->right()->num()); + } + EmitBitOr(); +} + + +void FastCodeGenerator::VisitCompareOperation(CompareOperation* expr) { + UNREACHABLE(); +} + + +void FastCodeGenerator::VisitThisFunction(ThisFunction* expr) { + UNREACHABLE(); +} + #undef __ diff --git a/src/ia32/fast-codegen-ia32.h b/src/ia32/fast-codegen-ia32.h new file mode 100644 index 00000000..e0851afe --- /dev/null +++ b/src/ia32/fast-codegen-ia32.h @@ -0,0 +1,155 @@ +// Copyright 2010 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. + +#ifndef V8_FAST_CODEGEN_IA32_H_ +#define V8_FAST_CODEGEN_IA32_H_ + +#include "v8.h" + +#include "ast.h" +#include "compiler.h" +#include "list.h" + +namespace v8 { +namespace internal { + +class FastCodeGenSyntaxChecker: public AstVisitor { + public: + explicit FastCodeGenSyntaxChecker() + : info_(NULL), has_supported_syntax_(true) { + } + + void Check(CompilationInfo* info); + + CompilationInfo* info() { return info_; } + bool has_supported_syntax() { return has_supported_syntax_; } + + private: + void VisitDeclarations(ZoneList<Declaration*>* decls); + void VisitStatements(ZoneList<Statement*>* stmts); + + // AST node visit functions. +#define DECLARE_VISIT(type) virtual void Visit##type(type* node); + AST_NODE_LIST(DECLARE_VISIT) +#undef DECLARE_VISIT + + CompilationInfo* info_; + bool has_supported_syntax_; + + DISALLOW_COPY_AND_ASSIGN(FastCodeGenSyntaxChecker); +}; + + +class FastCodeGenerator: public AstVisitor { + public: + explicit FastCodeGenerator(MacroAssembler* masm) + : masm_(masm), info_(NULL), destination_(no_reg), smi_bits_(0) { + } + + static Handle<Code> MakeCode(CompilationInfo* info); + + void Generate(CompilationInfo* compilation_info); + + private: + MacroAssembler* masm() { return masm_; } + CompilationInfo* info() { return info_; } + + Register destination() { return destination_; } + void set_destination(Register reg) { destination_ = reg; } + + FunctionLiteral* function() { return info_->function(); } + Scope* scope() { return info_->scope(); } + + // Platform-specific fixed registers, all guaranteed distinct. + Register accumulator0(); + Register accumulator1(); + Register scratch0(); + Register scratch1(); + Register receiver_reg(); + Register context_reg(); + + Register other_accumulator(Register reg) { + ASSERT(reg.is(accumulator0()) || reg.is(accumulator1())); + return (reg.is(accumulator0())) ? accumulator1() : accumulator0(); + } + + // Flags are true if the respective register is statically known to hold a + // smi. We do not track every register, only the accumulator registers. + bool is_smi(Register reg) { + ASSERT(!reg.is(no_reg)); + return (smi_bits_ & reg.bit()) != 0; + } + void set_as_smi(Register reg) { + ASSERT(!reg.is(no_reg)); + smi_bits_ = smi_bits_ | reg.bit(); + } + void clear_as_smi(Register reg) { + ASSERT(!reg.is(no_reg)); + smi_bits_ = smi_bits_ & ~reg.bit(); + } + + // AST node visit functions. +#define DECLARE_VISIT(type) virtual void Visit##type(type* node); + AST_NODE_LIST(DECLARE_VISIT) +#undef DECLARE_VISIT + + // Emit code to load the receiver from the stack into receiver_reg. + void EmitLoadReceiver(); + + // Emit code to load a global variable directly from a global property + // cell into the destination register. + void EmitGlobalVariableLoad(Handle<Object> cell); + + // Emit a store to an own property of this. The stored value is expected + // in accumulator0 and the receiver in receiver_reg. The receiver + // register is preserved and the result (the stored value) is left in the + // destination register. + void EmitThisPropertyStore(Handle<String> name); + + // Emit a load from an own property of this. The receiver is expected in + // receiver_reg. The receiver register is preserved and the result is + // left in the destination register. + void EmitThisPropertyLoad(Handle<String> name); + + // Emit a bitwise or operation. The left operand is in accumulator1 and + // the right is in accumulator0. The result should be left in the + // destination register. + void EmitBitOr(); + + MacroAssembler* masm_; + CompilationInfo* info_; + + Register destination_; + uint32_t smi_bits_; + + DISALLOW_COPY_AND_ASSIGN(FastCodeGenerator); +}; + + +} } // namespace v8::internal + +#endif // V8_FAST_CODEGEN_IA32_H_ diff --git a/src/ia32/full-codegen-ia32.cc b/src/ia32/full-codegen-ia32.cc index 3163b190..2394bed6 100644 --- a/src/ia32/full-codegen-ia32.cc +++ b/src/ia32/full-codegen-ia32.cc @@ -808,7 +808,7 @@ void FullCodeGenerator::EmitVariableLoad(Variable* var, Comment cmnt(masm_, "Global variable"); // Use inline caching. Variable name is passed in ecx and the global // object on the stack. - __ push(CodeGenerator::GlobalObject()); + __ mov(eax, CodeGenerator::GlobalObject()); __ mov(ecx, var->name()); Handle<Code> ic(Builtins::builtin(Builtins::LoadIC_Initialize)); __ call(ic, RelocInfo::CODE_TARGET_CONTEXT); @@ -817,7 +817,7 @@ void FullCodeGenerator::EmitVariableLoad(Variable* var, // Remember that the assembler may choose to do peephole optimization // (eg, push/pop elimination). __ nop(); - DropAndApply(1, context, eax); + Apply(context, eax); } else if (slot != NULL && slot->type() == Slot::LOOKUP) { Comment cmnt(masm_, "Lookup slot"); @@ -845,7 +845,7 @@ void FullCodeGenerator::EmitVariableLoad(Variable* var, // Load the object. MemOperand object_loc = EmitSlotSearch(object_slot, eax); - __ push(object_loc); + __ mov(edx, object_loc); // Assert that the key is a smi. Literal* key_literal = property->key()->AsLiteral(); @@ -853,7 +853,7 @@ void FullCodeGenerator::EmitVariableLoad(Variable* var, ASSERT(key_literal->handle()->IsSmi()); // Load the key. - __ push(Immediate(key_literal->handle())); + __ mov(eax, Immediate(key_literal->handle())); // Do a keyed property load. Handle<Code> ic(Builtins::builtin(Builtins::KeyedLoadIC_Initialize)); @@ -862,7 +862,7 @@ void FullCodeGenerator::EmitVariableLoad(Variable* var, // call. It is treated specially by the LoadIC code. __ nop(); // Drop key and object left on the stack by IC. - DropAndApply(2, context, eax); + Apply(context, eax); } } @@ -1013,6 +1013,99 @@ void FullCodeGenerator::VisitArrayLiteral(ArrayLiteral* expr) { } +void FullCodeGenerator::VisitAssignment(Assignment* expr) { + Comment cmnt(masm_, "[ Assignment"); + ASSERT(expr->op() != Token::INIT_CONST); + // Left-hand side can only be a property, a global or a (parameter or local) + // slot. Variables with rewrite to .arguments are treated as KEYED_PROPERTY. + enum LhsKind { VARIABLE, NAMED_PROPERTY, KEYED_PROPERTY }; + LhsKind assign_type = VARIABLE; + Property* prop = expr->target()->AsProperty(); + if (prop != NULL) { + assign_type = + (prop->key()->IsPropertyName()) ? NAMED_PROPERTY : KEYED_PROPERTY; + } + + // Evaluate LHS expression. + switch (assign_type) { + case VARIABLE: + // Nothing to do here. + break; + case NAMED_PROPERTY: + if (expr->is_compound()) { + // We need the receiver both on the stack and in the accumulator. + VisitForValue(prop->obj(), kAccumulator); + __ push(result_register()); + } else { + VisitForValue(prop->obj(), kStack); + } + break; + case KEYED_PROPERTY: + if (expr->is_compound()) { + VisitForValue(prop->obj(), kStack); + VisitForValue(prop->key(), kAccumulator); + __ mov(edx, Operand(esp, 0)); + __ push(eax); + } else { + VisitForValue(prop->obj(), kStack); + VisitForValue(prop->key(), kStack); + } + break; + } + + // If we have a compound assignment: Get value of LHS expression and + // store in on top of the stack. + if (expr->is_compound()) { + Location saved_location = location_; + location_ = kStack; + switch (assign_type) { + case VARIABLE: + EmitVariableLoad(expr->target()->AsVariableProxy()->var(), + Expression::kValue); + break; + case NAMED_PROPERTY: + EmitNamedPropertyLoad(prop); + __ push(result_register()); + break; + case KEYED_PROPERTY: + EmitKeyedPropertyLoad(prop); + __ push(result_register()); + break; + } + location_ = saved_location; + } + + // Evaluate RHS expression. + Expression* rhs = expr->value(); + VisitForValue(rhs, kAccumulator); + + // If we have a compound assignment: Apply operator. + if (expr->is_compound()) { + Location saved_location = location_; + location_ = kAccumulator; + EmitBinaryOp(expr->binary_op(), Expression::kValue); + location_ = saved_location; + } + + // Record source position before possible IC call. + SetSourcePosition(expr->position()); + + // Store the value. + switch (assign_type) { + case VARIABLE: + EmitVariableAssignment(expr->target()->AsVariableProxy()->var(), + context_); + break; + case NAMED_PROPERTY: + EmitNamedPropertyAssignment(expr); + break; + case KEYED_PROPERTY: + EmitKeyedPropertyAssignment(expr); + break; + } +} + + void FullCodeGenerator::EmitNamedPropertyLoad(Property* prop) { SetSourcePosition(prop->position()); Literal* key = prop->key()->AsLiteral(); @@ -1183,18 +1276,16 @@ void FullCodeGenerator::VisitProperty(Property* expr) { Comment cmnt(masm_, "[ Property"); Expression* key = expr->key(); - // Evaluate the receiver. - VisitForValue(expr->obj(), kStack); - if (key->IsPropertyName()) { + VisitForValue(expr->obj(), kAccumulator); EmitNamedPropertyLoad(expr); - // Drop receiver left on the stack by IC. - DropAndApply(1, context_, eax); + Apply(context_, eax); } else { - VisitForValue(expr->key(), kStack); + VisitForValue(expr->obj(), kStack); + VisitForValue(expr->key(), kAccumulator); + __ pop(edx); EmitKeyedPropertyLoad(expr); - // Drop key and receiver left on the stack by IC. - DropAndApply(2, context_, eax); + Apply(context_, eax); } } @@ -1265,25 +1356,31 @@ void FullCodeGenerator::VisitCall(Call* expr) { // Call to a keyed property, use keyed load IC followed by function // call. VisitForValue(prop->obj(), kStack); - VisitForValue(prop->key(), kStack); + VisitForValue(prop->key(), kAccumulator); // Record source code position for IC call. SetSourcePosition(prop->position()); + if (prop->is_synthetic()) { + __ pop(edx); // We do not need to keep the receiver. + } else { + __ mov(edx, Operand(esp, 0)); // Keep receiver, to call function on. + } + Handle<Code> ic(Builtins::builtin(Builtins::KeyedLoadIC_Initialize)); __ call(ic, RelocInfo::CODE_TARGET); // By emitting a nop we make sure that we do not have a "test eax,..." // instruction after the call it is treated specially by the LoadIC code. __ nop(); - // Drop key left on the stack by IC. - __ Drop(1); - // Pop receiver. - __ pop(ebx); - // Push result (function). - __ push(eax); - // Push receiver object on stack. if (prop->is_synthetic()) { + // Push result (function). + __ push(eax); + // Push Global receiver. __ mov(ecx, CodeGenerator::GlobalObject()); __ push(FieldOperand(ecx, GlobalObject::kGlobalReceiverOffset)); } else { + // Pop receiver. + __ pop(ebx); + // Push result (function). + __ push(eax); __ push(ebx); } EmitCallWithStub(expr); @@ -1455,13 +1552,13 @@ void FullCodeGenerator::VisitUnaryOperation(UnaryOperation* expr) { !proxy->var()->is_this() && proxy->var()->is_global()) { Comment cmnt(masm_, "Global variable"); - __ push(CodeGenerator::GlobalObject()); + __ mov(eax, CodeGenerator::GlobalObject()); __ mov(ecx, Immediate(proxy->name())); Handle<Code> ic(Builtins::builtin(Builtins::LoadIC_Initialize)); // Use a regular load, not a contextual load, to avoid a reference // error. __ call(ic, RelocInfo::CODE_TARGET); - __ mov(Operand(esp, 0), eax); + __ push(eax); } else if (proxy != NULL && proxy->var()->slot() != NULL && proxy->var()->slot()->type() == Slot::LOOKUP) { @@ -1565,11 +1662,16 @@ void FullCodeGenerator::VisitCountOperation(CountOperation* expr) { if (expr->is_postfix() && context_ != Expression::kEffect) { __ push(Immediate(Smi::FromInt(0))); } - VisitForValue(prop->obj(), kStack); if (assign_type == NAMED_PROPERTY) { + // Put the object both on the stack and in the accumulator. + VisitForValue(prop->obj(), kAccumulator); + __ push(eax); EmitNamedPropertyLoad(prop); } else { - VisitForValue(prop->key(), kStack); + VisitForValue(prop->obj(), kStack); + VisitForValue(prop->key(), kAccumulator); + __ mov(edx, Operand(esp, 0)); + __ push(eax); EmitKeyedPropertyLoad(prop); } } diff --git a/src/ia32/ic-ia32.cc b/src/ia32/ic-ia32.cc index d1ae28b1..0d79c54d 100644 --- a/src/ia32/ic-ia32.cc +++ b/src/ia32/ic-ia32.cc @@ -50,28 +50,29 @@ namespace internal { // or if name is not a symbol, and will jump to the miss_label in that case. static void GenerateDictionaryLoad(MacroAssembler* masm, Label* miss_label, + Register receiver, + Register name, Register r0, Register r1, Register r2, - Register name, DictionaryCheck check_dictionary) { // Register use: // + // name - holds the name of the property and is unchanged. + // receiver - holds the receiver and is unchanged. + // Scratch registers: // r0 - used to hold the property dictionary. // - // r1 - initially the receiver - // - used for the index into the property dictionary + // r1 - used for the index into the property dictionary // - holds the result on exit. // // r2 - used to hold the capacity of the property dictionary. - // - // name - holds the name of the property and is unchanged. Label done; // Check for the absence of an interceptor. // Load the map into r0. - __ mov(r0, FieldOperand(r1, JSObject::kMapOffset)); + __ mov(r0, FieldOperand(receiver, JSObject::kMapOffset)); // Test the has_named_interceptor bit in the map. __ test(FieldOperand(r0, Map::kInstanceAttributesOffset), Immediate(1 << (Map::kHasNamedInterceptor + (3 * 8)))); @@ -91,7 +92,7 @@ static void GenerateDictionaryLoad(MacroAssembler* masm, __ j(equal, miss_label, not_taken); // Load properties array. - __ mov(r0, FieldOperand(r1, JSObject::kPropertiesOffset)); + __ mov(r0, FieldOperand(receiver, JSObject::kPropertiesOffset)); // Check that the properties array is a dictionary. if (check_dictionary == CHECK_DICTIONARY) { @@ -159,14 +160,12 @@ const int LoadIC::kOffsetToLoadInstruction = 13; void LoadIC::GenerateArrayLength(MacroAssembler* masm) { // ----------- S t a t e ------------- + // -- eax : receiver // -- ecx : name // -- esp[0] : return address - // -- esp[4] : receiver // ----------------------------------- Label miss; - __ mov(eax, Operand(esp, kPointerSize)); - StubCompiler::GenerateLoadArrayLength(masm, eax, edx, &miss); __ bind(&miss); StubCompiler::GenerateLoadMiss(masm, Code::LOAD_IC); @@ -175,15 +174,13 @@ void LoadIC::GenerateArrayLength(MacroAssembler* masm) { void LoadIC::GenerateStringLength(MacroAssembler* masm) { // ----------- S t a t e ------------- + // -- eax : receiver // -- ecx : name // -- esp[0] : return address - // -- esp[4] : receiver // ----------------------------------- Label miss; - __ mov(eax, Operand(esp, kPointerSize)); - - StubCompiler::GenerateLoadStringLength(masm, eax, edx, &miss); + StubCompiler::GenerateLoadStringLength(masm, eax, edx, ebx, &miss); __ bind(&miss); StubCompiler::GenerateLoadMiss(masm, Code::LOAD_IC); } @@ -191,14 +188,12 @@ void LoadIC::GenerateStringLength(MacroAssembler* masm) { void LoadIC::GenerateFunctionPrototype(MacroAssembler* masm) { // ----------- S t a t e ------------- + // -- eax : receiver // -- ecx : name // -- esp[0] : return address - // -- esp[4] : receiver // ----------------------------------- Label miss; - __ mov(eax, Operand(esp, kPointerSize)); - StubCompiler::GenerateLoadFunctionPrototype(masm, eax, edx, ebx, &miss); __ bind(&miss); StubCompiler::GenerateLoadMiss(masm, Code::LOAD_IC); @@ -207,26 +202,22 @@ void LoadIC::GenerateFunctionPrototype(MacroAssembler* masm) { void KeyedLoadIC::GenerateGeneric(MacroAssembler* masm) { // ----------- S t a t e ------------- + // -- eax : key + // -- edx : receiver // -- esp[0] : return address - // -- esp[4] : name - // -- esp[8] : receiver // ----------------------------------- Label slow, check_string, index_int, index_string; Label check_pixel_array, probe_dictionary; - // Load name and receiver. - __ mov(eax, Operand(esp, kPointerSize)); - __ mov(ecx, Operand(esp, 2 * kPointerSize)); - // Check that the object isn't a smi. - __ test(ecx, Immediate(kSmiTagMask)); + __ test(edx, Immediate(kSmiTagMask)); __ j(zero, &slow, not_taken); // Get the map of the receiver. - __ mov(edx, FieldOperand(ecx, HeapObject::kMapOffset)); + __ mov(ecx, FieldOperand(edx, HeapObject::kMapOffset)); // Check bit field. - __ movzx_b(ebx, FieldOperand(edx, Map::kBitFieldOffset)); + __ movzx_b(ebx, FieldOperand(ecx, Map::kBitFieldOffset)); __ test(ebx, Immediate(kSlowCaseBitFieldMask)); __ j(not_zero, &slow, not_taken); // Check that the object is some kind of JS object EXCEPT JS Value type. @@ -234,56 +225,58 @@ void KeyedLoadIC::GenerateGeneric(MacroAssembler* masm) { // we enter the runtime system to make sure that indexing // into string objects work as intended. ASSERT(JS_OBJECT_TYPE > JS_VALUE_TYPE); - __ movzx_b(edx, FieldOperand(edx, Map::kInstanceTypeOffset)); - __ cmp(edx, JS_OBJECT_TYPE); - __ j(less, &slow, not_taken); + __ CmpInstanceType(ecx, JS_OBJECT_TYPE); + __ j(below, &slow, not_taken); // Check that the key is a smi. __ test(eax, Immediate(kSmiTagMask)); __ j(not_zero, &check_string, not_taken); - __ sar(eax, kSmiTagSize); + __ mov(ebx, eax); + __ SmiUntag(ebx); // Get the elements array of the object. __ bind(&index_int); - __ mov(ecx, FieldOperand(ecx, JSObject::kElementsOffset)); + __ mov(ecx, FieldOperand(edx, JSObject::kElementsOffset)); // Check that the object is in fast mode (not dictionary). - __ cmp(FieldOperand(ecx, HeapObject::kMapOffset), - Immediate(Factory::fixed_array_map())); - __ j(not_equal, &check_pixel_array); + __ CheckMap(ecx, Factory::fixed_array_map(), &check_pixel_array, true); // Check that the key (index) is within bounds. - __ cmp(eax, FieldOperand(ecx, FixedArray::kLengthOffset)); + __ cmp(ebx, FieldOperand(ecx, FixedArray::kLengthOffset)); __ j(above_equal, &slow); // Fast case: Do the load. - __ mov(eax, - Operand(ecx, eax, times_4, FixedArray::kHeaderSize - kHeapObjectTag)); - __ cmp(Operand(eax), Immediate(Factory::the_hole_value())); + __ mov(ecx, FieldOperand(ecx, ebx, times_4, FixedArray::kHeaderSize)); + __ cmp(Operand(ecx), Immediate(Factory::the_hole_value())); // In case the loaded value is the_hole we have to consult GetProperty // to ensure the prototype chain is searched. __ j(equal, &slow); + __ mov(eax, ecx); __ IncrementCounter(&Counters::keyed_load_generic_smi, 1); __ ret(0); - // Check whether the elements is a pixel array. - // eax: untagged index - // ecx: elements array __ bind(&check_pixel_array); - __ cmp(FieldOperand(ecx, HeapObject::kMapOffset), - Immediate(Factory::pixel_array_map())); - __ j(not_equal, &slow); - __ cmp(eax, FieldOperand(ecx, PixelArray::kLengthOffset)); + // Check whether the elements is a pixel array. + // edx: receiver + // ebx: untagged index + // eax: key + // ecx: elements + __ CheckMap(ecx, Factory::pixel_array_map(), &slow, true); + __ cmp(ebx, FieldOperand(ecx, PixelArray::kLengthOffset)); __ j(above_equal, &slow); - __ mov(ecx, FieldOperand(ecx, PixelArray::kExternalPointerOffset)); - __ movzx_b(eax, Operand(ecx, eax, times_1, 0)); - __ shl(eax, kSmiTagSize); + __ mov(eax, FieldOperand(ecx, PixelArray::kExternalPointerOffset)); + __ movzx_b(eax, Operand(eax, ebx, times_1, 0)); + __ SmiTag(eax); __ ret(0); - // Slow case: Load name and receiver from stack and jump to runtime. __ bind(&slow); + // Slow case: jump to runtime. + // edx: receiver + // eax: key __ IncrementCounter(&Counters::keyed_load_generic_slow, 1); - Generate(masm, ExternalReference(Runtime::kKeyedGetProperty)); + GenerateRuntimeGetProperty(masm); __ bind(&check_string); // The key is not a smi. // Is it a string? - __ CmpObjectType(eax, FIRST_NONSTRING_TYPE, edx); + // edx: receiver + // eax: key + __ CmpObjectType(eax, FIRST_NONSTRING_TYPE, ecx); __ j(above_equal, &slow); // Is the string an array index, with cached numeric value? __ mov(ebx, FieldOperand(eax, String::kHashFieldOffset)); @@ -291,55 +284,58 @@ void KeyedLoadIC::GenerateGeneric(MacroAssembler* masm) { __ j(not_zero, &index_string, not_taken); // Is the string a symbol? - __ movzx_b(ebx, FieldOperand(edx, Map::kInstanceTypeOffset)); + __ movzx_b(ebx, FieldOperand(ecx, Map::kInstanceTypeOffset)); ASSERT(kSymbolTag != 0); __ test(ebx, Immediate(kIsSymbolMask)); __ j(zero, &slow, not_taken); // If the receiver is a fast-case object, check the keyed lookup - // cache. Otherwise probe the dictionary leaving result in ecx. - __ mov(ebx, FieldOperand(ecx, JSObject::kPropertiesOffset)); + // cache. Otherwise probe the dictionary. + __ mov(ebx, FieldOperand(edx, JSObject::kPropertiesOffset)); __ cmp(FieldOperand(ebx, HeapObject::kMapOffset), Immediate(Factory::hash_table_map())); __ j(equal, &probe_dictionary); // Load the map of the receiver, compute the keyed lookup cache hash // based on 32 bits of the map pointer and the string hash. - __ mov(ebx, FieldOperand(ecx, HeapObject::kMapOffset)); - __ mov(edx, ebx); - __ shr(edx, KeyedLookupCache::kMapHashShift); - __ mov(eax, FieldOperand(eax, String::kHashFieldOffset)); - __ shr(eax, String::kHashShift); - __ xor_(edx, Operand(eax)); - __ and_(edx, KeyedLookupCache::kCapacityMask); + __ mov(ebx, FieldOperand(edx, HeapObject::kMapOffset)); + __ mov(ecx, ebx); + __ shr(ecx, KeyedLookupCache::kMapHashShift); + __ mov(edi, FieldOperand(eax, String::kHashFieldOffset)); + __ shr(edi, String::kHashShift); + __ xor_(ecx, Operand(edi)); + __ and_(ecx, KeyedLookupCache::kCapacityMask); // Load the key (consisting of map and symbol) from the cache and // check for match. ExternalReference cache_keys = ExternalReference::keyed_lookup_cache_keys(); - __ mov(edi, edx); + __ mov(edi, ecx); __ shl(edi, kPointerSizeLog2 + 1); __ cmp(ebx, Operand::StaticArray(edi, times_1, cache_keys)); __ j(not_equal, &slow); __ add(Operand(edi), Immediate(kPointerSize)); - __ mov(edi, Operand::StaticArray(edi, times_1, cache_keys)); - __ cmp(edi, Operand(esp, kPointerSize)); + __ cmp(eax, Operand::StaticArray(edi, times_1, cache_keys)); __ j(not_equal, &slow); // Get field offset and check that it is an in-object property. + // edx : receiver + // ebx : receiver's map + // eax : key + // ecx : lookup cache index ExternalReference cache_field_offsets = ExternalReference::keyed_lookup_cache_field_offsets(); - __ mov(eax, - Operand::StaticArray(edx, times_pointer_size, cache_field_offsets)); - __ movzx_b(edx, FieldOperand(ebx, Map::kInObjectPropertiesOffset)); - __ cmp(eax, Operand(edx)); + __ mov(edi, + Operand::StaticArray(ecx, times_pointer_size, cache_field_offsets)); + __ movzx_b(ecx, FieldOperand(ebx, Map::kInObjectPropertiesOffset)); + __ cmp(edi, Operand(ecx)); __ j(above_equal, &slow); // Load in-object property. - __ sub(eax, Operand(edx)); - __ movzx_b(edx, FieldOperand(ebx, Map::kInstanceSizeOffset)); - __ add(eax, Operand(edx)); - __ mov(eax, FieldOperand(ecx, eax, times_pointer_size, 0)); + __ sub(edi, Operand(ecx)); + __ movzx_b(ecx, FieldOperand(ebx, Map::kInstanceSizeOffset)); + __ add(ecx, Operand(edi)); + __ mov(eax, FieldOperand(edx, ecx, times_pointer_size, 0)); __ ret(0); // Do a quick inline probe of the receiver's dictionary, if it @@ -347,10 +343,11 @@ void KeyedLoadIC::GenerateGeneric(MacroAssembler* masm) { __ bind(&probe_dictionary); GenerateDictionaryLoad(masm, &slow, - ebx, - ecx, edx, eax, + ebx, + ecx, + edi, DICTIONARY_CHECK_DONE); __ mov(eax, Operand(ecx)); __ IncrementCounter(&Counters::keyed_load_generic_symbol, 1); @@ -363,51 +360,47 @@ void KeyedLoadIC::GenerateGeneric(MacroAssembler* masm) { ASSERT(TenToThe(String::kMaxCachedArrayIndexLength) < (1 << String::kArrayIndexValueBits)); __ bind(&index_string); - __ mov(eax, Operand(ebx)); - __ and_(eax, String::kArrayIndexHashMask); - __ shr(eax, String::kHashShift); + __ and_(ebx, String::kArrayIndexHashMask); + __ shr(ebx, String::kHashShift); __ jmp(&index_int); } void KeyedLoadIC::GenerateString(MacroAssembler* masm) { // ----------- S t a t e ------------- + // -- eax : key + // -- edx : receiver // -- esp[0] : return address - // -- esp[4] : key - // -- esp[8] : receiver // ----------------------------------- Label miss, index_ok; // Pop return address. // Performing the load early is better in the common case. - __ pop(eax); + __ pop(ebx); - __ mov(ebx, Operand(esp, 1 * kPointerSize)); - __ test(ebx, Immediate(kSmiTagMask)); + __ test(edx, Immediate(kSmiTagMask)); __ j(zero, &miss); - __ mov(ecx, FieldOperand(ebx, HeapObject::kMapOffset)); + __ mov(ecx, FieldOperand(edx, HeapObject::kMapOffset)); __ movzx_b(ecx, FieldOperand(ecx, Map::kInstanceTypeOffset)); __ test(ecx, Immediate(kIsNotStringMask)); __ j(not_zero, &miss); // Check if key is a smi or a heap number. - __ mov(edx, Operand(esp, 0)); - __ test(edx, Immediate(kSmiTagMask)); + __ test(eax, Immediate(kSmiTagMask)); __ j(zero, &index_ok); - __ mov(ecx, FieldOperand(ebx, HeapObject::kMapOffset)); + __ mov(ecx, FieldOperand(eax, HeapObject::kMapOffset)); __ cmp(ecx, Factory::heap_number_map()); __ j(not_equal, &miss); __ bind(&index_ok); - // Duplicate receiver and key since they are expected on the stack after - // the KeyedLoadIC call. - __ push(ebx); // receiver - __ push(edx); // key - __ push(eax); // return address + // Push receiver and key on the stack, and make a tail call. + __ push(edx); // receiver + __ push(eax); // key + __ push(ebx); // return address __ InvokeBuiltin(Builtins::STRING_CHAR_AT, JUMP_FUNCTION); __ bind(&miss); - __ push(eax); + __ push(ebx); GenerateMiss(masm); } @@ -415,18 +408,14 @@ void KeyedLoadIC::GenerateString(MacroAssembler* masm) { void KeyedLoadIC::GenerateExternalArray(MacroAssembler* masm, ExternalArrayType array_type) { // ----------- S t a t e ------------- + // -- eax : key + // -- edx : receiver // -- esp[0] : return address - // -- esp[4] : key - // -- esp[8] : receiver // ----------------------------------- Label slow, failed_allocation; - // Load name and receiver. - __ mov(eax, Operand(esp, kPointerSize)); - __ mov(ecx, Operand(esp, 2 * kPointerSize)); - // Check that the object isn't a smi. - __ test(ecx, Immediate(kSmiTagMask)); + __ test(edx, Immediate(kSmiTagMask)); __ j(zero, &slow, not_taken); // Check that the key is a smi. @@ -434,59 +423,56 @@ void KeyedLoadIC::GenerateExternalArray(MacroAssembler* masm, __ j(not_zero, &slow, not_taken); // Get the map of the receiver. - __ mov(edx, FieldOperand(ecx, HeapObject::kMapOffset)); + __ mov(ecx, FieldOperand(edx, HeapObject::kMapOffset)); // Check that the receiver does not require access checks. We need // to check this explicitly since this generic stub does not perform // map checks. - __ movzx_b(ebx, FieldOperand(edx, Map::kBitFieldOffset)); + __ movzx_b(ebx, FieldOperand(ecx, Map::kBitFieldOffset)); __ test(ebx, Immediate(1 << Map::kIsAccessCheckNeeded)); __ j(not_zero, &slow, not_taken); - // Get the instance type from the map of the receiver. - __ movzx_b(edx, FieldOperand(edx, Map::kInstanceTypeOffset)); - // Check that the object is a JS object. - __ cmp(edx, JS_OBJECT_TYPE); + __ CmpInstanceType(ecx, JS_OBJECT_TYPE); __ j(not_equal, &slow, not_taken); // Check that the elements array is the appropriate type of // ExternalArray. - // eax: index (as a smi) - // ecx: JSObject - __ mov(ecx, FieldOperand(ecx, JSObject::kElementsOffset)); + __ mov(ebx, FieldOperand(edx, JSObject::kElementsOffset)); Handle<Map> map(Heap::MapForExternalArrayType(array_type)); - __ cmp(FieldOperand(ecx, HeapObject::kMapOffset), + __ cmp(FieldOperand(ebx, HeapObject::kMapOffset), Immediate(map)); __ j(not_equal, &slow, not_taken); + // eax: key, known to be a smi. + // edx: receiver, known to be a JSObject. + // ebx: elements object, known to be an external array. // Check that the index is in range. - __ sar(eax, kSmiTagSize); // Untag the index. - __ cmp(eax, FieldOperand(ecx, ExternalArray::kLengthOffset)); + __ mov(ecx, eax); + __ SmiUntag(ecx); // Untag the index. + __ cmp(ecx, FieldOperand(ebx, ExternalArray::kLengthOffset)); // Unsigned comparison catches both negative and too-large values. __ j(above_equal, &slow); - // eax: untagged index - // ecx: elements array - __ mov(ecx, FieldOperand(ecx, ExternalArray::kExternalPointerOffset)); - // ecx: base pointer of external storage + __ mov(ebx, FieldOperand(ebx, ExternalArray::kExternalPointerOffset)); + // ebx: base pointer of external storage switch (array_type) { case kExternalByteArray: - __ movsx_b(eax, Operand(ecx, eax, times_1, 0)); + __ movsx_b(ecx, Operand(ebx, ecx, times_1, 0)); break; case kExternalUnsignedByteArray: - __ movzx_b(eax, Operand(ecx, eax, times_1, 0)); + __ movzx_b(ecx, Operand(ebx, ecx, times_1, 0)); break; case kExternalShortArray: - __ movsx_w(eax, Operand(ecx, eax, times_2, 0)); + __ movsx_w(ecx, Operand(ebx, ecx, times_2, 0)); break; case kExternalUnsignedShortArray: - __ movzx_w(eax, Operand(ecx, eax, times_2, 0)); + __ movzx_w(ecx, Operand(ebx, ecx, times_2, 0)); break; case kExternalIntArray: case kExternalUnsignedIntArray: - __ mov(eax, Operand(ecx, eax, times_4, 0)); + __ mov(ecx, Operand(ebx, ecx, times_4, 0)); break; case kExternalFloatArray: - __ fld_s(Operand(ecx, eax, times_4, 0)); + __ fld_s(Operand(ebx, ecx, times_4, 0)); break; default: UNREACHABLE(); @@ -494,7 +480,7 @@ void KeyedLoadIC::GenerateExternalArray(MacroAssembler* masm, } // For integer array types: - // eax: value + // ecx: value // For floating-point array type: // FP(0): value @@ -505,21 +491,19 @@ void KeyedLoadIC::GenerateExternalArray(MacroAssembler* masm, // it to a HeapNumber. Label box_int; if (array_type == kExternalIntArray) { - // See Smi::IsValid for why this works. - __ mov(ebx, eax); - __ add(Operand(ebx), Immediate(0x40000000)); - __ cmp(ebx, 0x80000000); - __ j(above_equal, &box_int); + __ cmp(ecx, 0xC0000000); + __ j(sign, &box_int); } else { ASSERT_EQ(array_type, kExternalUnsignedIntArray); // The test is different for unsigned int values. Since we need - // the Smi-encoded result to be treated as unsigned, we can't + // the value to be in the range of a positive smi, we can't // handle either of the top two bits being set in the value. - __ test(eax, Immediate(0xC0000000)); + __ test(ecx, Immediate(0xC0000000)); __ j(not_zero, &box_int); } - __ shl(eax, kSmiTagSize); + __ mov(eax, ecx); + __ SmiTag(eax); __ ret(0); __ bind(&box_int); @@ -527,34 +511,37 @@ void KeyedLoadIC::GenerateExternalArray(MacroAssembler* masm, // Allocate a HeapNumber for the int and perform int-to-double // conversion. if (array_type == kExternalIntArray) { - __ push(eax); + __ push(ecx); __ fild_s(Operand(esp, 0)); - __ pop(eax); + __ pop(ecx); } else { ASSERT(array_type == kExternalUnsignedIntArray); // Need to zero-extend the value. // There's no fild variant for unsigned values, so zero-extend // to a 64-bit int manually. __ push(Immediate(0)); - __ push(eax); + __ push(ecx); __ fild_d(Operand(esp, 0)); - __ pop(eax); - __ pop(eax); + __ pop(ecx); + __ pop(ecx); } // FP(0): value - __ AllocateHeapNumber(eax, ebx, ecx, &failed_allocation); + __ AllocateHeapNumber(ecx, ebx, edi, &failed_allocation); // Set the value. + __ mov(eax, ecx); __ fstp_d(FieldOperand(eax, HeapNumber::kValueOffset)); __ ret(0); } else if (array_type == kExternalFloatArray) { // For the floating-point array type, we need to always allocate a // HeapNumber. - __ AllocateHeapNumber(eax, ebx, ecx, &failed_allocation); + __ AllocateHeapNumber(ecx, ebx, edi, &failed_allocation); // Set the value. + __ mov(eax, ecx); __ fstp_d(FieldOperand(eax, HeapNumber::kValueOffset)); __ ret(0); } else { - __ shl(eax, kSmiTagSize); + __ mov(eax, ecx); + __ SmiTag(eax); __ ret(0); } @@ -565,10 +552,51 @@ void KeyedLoadIC::GenerateExternalArray(MacroAssembler* masm, __ fincstp(); // Fall through to slow case. - // Slow case: Load name and receiver from stack and jump to runtime. + // Slow case: Load key and receiver from stack and jump to runtime. __ bind(&slow); __ IncrementCounter(&Counters::keyed_load_external_array_slow, 1); - Generate(masm, ExternalReference(Runtime::kKeyedGetProperty)); + GenerateRuntimeGetProperty(masm); +} + + +void KeyedLoadIC::GenerateIndexedInterceptor(MacroAssembler* masm) { + // ----------- S t a t e ------------- + // -- eax : key + // -- edx : receiver + // -- esp[0] : return address + // ----------------------------------- + Label slow; + + // Check that the receiver isn't a smi. + __ test(edx, Immediate(kSmiTagMask)); + __ j(zero, &slow, not_taken); + + // Check that the key is a smi. + __ test(eax, Immediate(kSmiTagMask)); + __ j(not_zero, &slow, not_taken); + + // Get the map of the receiver. + __ mov(ecx, FieldOperand(edx, HeapObject::kMapOffset)); + + // Check that it has indexed interceptor and access checks + // are not enabled for this object. + __ movzx_b(ecx, FieldOperand(ecx, Map::kBitFieldOffset)); + __ and_(Operand(ecx), Immediate(kSlowCaseBitFieldMask)); + __ cmp(Operand(ecx), Immediate(1 << Map::kHasIndexedInterceptor)); + __ j(not_zero, &slow, not_taken); + + // Everything is fine, call runtime. + __ pop(ecx); + __ push(edx); // receiver + __ push(eax); // key + __ push(ecx); // return address + + // Perform tail call to the entry. + __ TailCallRuntime(ExternalReference( + IC_Utility(kKeyedLoadPropertyWithInterceptor)), 2, 1); + + __ bind(&slow); + GenerateMiss(masm); } @@ -627,7 +655,7 @@ void KeyedStoreIC::GenerateGeneric(MacroAssembler* masm) { // Slow case: call runtime. __ bind(&slow); - Generate(masm, ExternalReference(Runtime::kSetProperty)); + GenerateRuntimeSetProperty(masm); // Check whether the elements is a pixel array. // eax: value @@ -900,7 +928,7 @@ void KeyedStoreIC::GenerateExternalArray(MacroAssembler* masm, // Slow case: call runtime. __ bind(&slow); - Generate(masm, ExternalReference(Runtime::kSetProperty)); + GenerateRuntimeSetProperty(masm); } @@ -983,7 +1011,7 @@ static void GenerateNormalHelper(MacroAssembler* masm, // Search dictionary - put result in register edi. __ mov(edi, edx); - GenerateDictionaryLoad(masm, miss, eax, edi, ebx, ecx, CHECK_DICTIONARY); + GenerateDictionaryLoad(masm, miss, edx, ecx, eax, edi, ebx, CHECK_DICTIONARY); // Check that the result is not a smi. __ test(edi, Immediate(kSmiTagMask)); @@ -1127,13 +1155,11 @@ Object* LoadIC_Miss(Arguments args); void LoadIC::GenerateMegamorphic(MacroAssembler* masm) { // ----------- S t a t e ------------- + // -- eax : receiver // -- ecx : name // -- esp[0] : return address - // -- esp[4] : receiver // ----------------------------------- - __ mov(eax, Operand(esp, kPointerSize)); - // Probe the stub cache. Code::Flags flags = Code::ComputeFlags(Code::LOAD_IC, NOT_IN_LOOP, @@ -1141,20 +1167,18 @@ void LoadIC::GenerateMegamorphic(MacroAssembler* masm) { StubCache::GenerateProbe(masm, flags, eax, ecx, ebx, edx); // Cache miss: Jump to runtime. - Generate(masm, ExternalReference(IC_Utility(kLoadIC_Miss))); + GenerateMiss(masm); } void LoadIC::GenerateNormal(MacroAssembler* masm) { // ----------- S t a t e ------------- + // -- eax : receiver // -- ecx : name // -- esp[0] : return address - // -- esp[4] : receiver // ----------------------------------- Label miss, probe, global; - __ mov(eax, Operand(esp, kPointerSize)); - // Check that the receiver isn't a smi. __ test(eax, Immediate(kSmiTagMask)); __ j(zero, &miss, not_taken); @@ -1179,7 +1203,15 @@ void LoadIC::GenerateNormal(MacroAssembler* masm) { // Search the dictionary placing the result in eax. __ bind(&probe); - GenerateDictionaryLoad(masm, &miss, edx, eax, ebx, ecx, CHECK_DICTIONARY); + GenerateDictionaryLoad(masm, + &miss, + eax, + ecx, + edx, + edi, + ebx, + CHECK_DICTIONARY); + __ mov(eax, edi); __ ret(0); // Global object access: Check access rights. @@ -1189,37 +1221,24 @@ void LoadIC::GenerateNormal(MacroAssembler* masm) { // Cache miss: Restore receiver from stack and jump to runtime. __ bind(&miss); - __ mov(eax, Operand(esp, 1 * kPointerSize)); - Generate(masm, ExternalReference(IC_Utility(kLoadIC_Miss))); + GenerateMiss(masm); } void LoadIC::GenerateMiss(MacroAssembler* masm) { // ----------- S t a t e ------------- + // -- eax : receiver // -- ecx : name // -- esp[0] : return address - // -- esp[4] : receiver // ----------------------------------- - Generate(masm, ExternalReference(IC_Utility(kLoadIC_Miss))); -} - - -void LoadIC::Generate(MacroAssembler* masm, const ExternalReference& f) { - // ----------- S t a t e ------------- - // -- ecx : name - // -- esp[0] : return address - // -- esp[4] : receiver - // ----------------------------------- - - __ mov(eax, Operand(esp, kPointerSize)); __ pop(ebx); __ push(eax); // receiver __ push(ecx); // name __ push(ebx); // return address // Perform tail call to the entry. - __ TailCallRuntime(f, 2, 1); + __ TailCallRuntime(ExternalReference(IC_Utility(kLoadIC_Miss)), 2, 1); } @@ -1323,31 +1342,35 @@ Object* KeyedLoadIC_Miss(Arguments args); void KeyedLoadIC::GenerateMiss(MacroAssembler* masm) { // ----------- S t a t e ------------- + // -- eax : key + // -- edx : receiver // -- esp[0] : return address - // -- esp[4] : name - // -- esp[8] : receiver // ----------------------------------- - Generate(masm, ExternalReference(IC_Utility(kKeyedLoadIC_Miss))); + __ pop(ebx); + __ push(edx); // receiver + __ push(eax); // name + __ push(ebx); // return address + + // Perform tail call to the entry. + __ TailCallRuntime(ExternalReference(IC_Utility(kKeyedLoadIC_Miss)), 2, 1); } -void KeyedLoadIC::Generate(MacroAssembler* masm, const ExternalReference& f) { +void KeyedLoadIC::GenerateRuntimeGetProperty(MacroAssembler* masm) { // ----------- S t a t e ------------- + // -- eax : key + // -- edx : receiver // -- esp[0] : return address - // -- esp[4] : name - // -- esp[8] : receiver // ----------------------------------- - __ mov(eax, Operand(esp, kPointerSize)); - __ mov(ecx, Operand(esp, 2 * kPointerSize)); __ pop(ebx); - __ push(ecx); // receiver + __ push(edx); // receiver __ push(eax); // name __ push(ebx); // return address // Perform tail call to the entry. - __ TailCallRuntime(f, 2, 1); + __ TailCallRuntime(ExternalReference(Runtime::kKeyedGetProperty), 2, 1); } @@ -1369,26 +1392,6 @@ void StoreIC::GenerateMegamorphic(MacroAssembler* masm) { } -void StoreIC::GenerateExtendStorage(MacroAssembler* masm) { - // ----------- S t a t e ------------- - // -- eax : value - // -- ecx : transition map - // -- edx : receiver - // -- esp[0] : return address - // ----------------------------------- - - __ pop(ebx); - __ push(edx); // receiver - __ push(ecx); // transition map - __ push(eax); // value - __ push(ebx); // return address - - // Perform tail call to the entry. - __ TailCallRuntime( - ExternalReference(IC_Utility(kSharedStoreIC_ExtendStorage)), 3, 1); -} - - void StoreIC::GenerateMiss(MacroAssembler* masm) { // ----------- S t a t e ------------- // -- eax : value @@ -1411,7 +1414,7 @@ void StoreIC::GenerateMiss(MacroAssembler* masm) { // Defined in ic.cc. Object* KeyedStoreIC_Miss(Arguments args); -void KeyedStoreIC::Generate(MacroAssembler* masm, const ExternalReference& f) { +void KeyedStoreIC::GenerateRuntimeSetProperty(MacroAssembler* masm) { // ----------- S t a t e ------------- // -- eax : value // -- esp[0] : return address @@ -1426,28 +1429,26 @@ void KeyedStoreIC::Generate(MacroAssembler* masm, const ExternalReference& f) { __ push(ecx); // Do tail-call to runtime routine. - __ TailCallRuntime(f, 3, 1); + __ TailCallRuntime(ExternalReference(Runtime::kSetProperty), 3, 1); } -void KeyedStoreIC::GenerateExtendStorage(MacroAssembler* masm) { +void KeyedStoreIC::GenerateMiss(MacroAssembler* masm) { // ----------- S t a t e ------------- // -- eax : value - // -- ecx : transition map // -- esp[0] : return address // -- esp[4] : key // -- esp[8] : receiver // ----------------------------------- - __ pop(ebx); + __ pop(ecx); + __ push(Operand(esp, 1 * kPointerSize)); __ push(Operand(esp, 1 * kPointerSize)); - __ push(ecx); __ push(eax); - __ push(ebx); + __ push(ecx); // Do tail-call to runtime routine. - __ TailCallRuntime( - ExternalReference(IC_Utility(kSharedStoreIC_ExtendStorage)), 3, 1); + __ TailCallRuntime(ExternalReference(IC_Utility(kKeyedStoreIC_Miss)), 3, 1); } #undef __ diff --git a/src/ia32/macro-assembler-ia32.cc b/src/ia32/macro-assembler-ia32.cc index 4dd6a9bc..1f08e872 100644 --- a/src/ia32/macro-assembler-ia32.cc +++ b/src/ia32/macro-assembler-ia32.cc @@ -41,7 +41,6 @@ namespace internal { MacroAssembler::MacroAssembler(void* buffer, int size) : Assembler(buffer, size), - unresolved_(0), generating_stub_(false), allow_stub_calls_(true), code_object_(Heap::undefined_value()) { @@ -165,7 +164,10 @@ void MacroAssembler::RecordWrite(Register object, int offset, if (Serializer::enabled()) { // Can't do arithmetic on external references if it might get serialized. mov(value, Operand(object)); - and_(value, Heap::NewSpaceMask()); + // The mask isn't really an address. We load it as an external reference in + // case the size of the new space is different between the snapshot maker + // and the running system. + and_(Operand(value), Immediate(ExternalReference::new_space_mask())); cmp(Operand(value), Immediate(ExternalReference::new_space_start())); j(equal, &done); } else { @@ -308,6 +310,13 @@ void MacroAssembler::CopyRegistersFromStackToMemory(Register base, } } } + +void MacroAssembler::DebugBreak() { + Set(eax, Immediate(0)); + mov(ebx, Immediate(ExternalReference(Runtime::kDebugBreak))); + CEntryStub ces(1); + call(ces.GetCode(), RelocInfo::DEBUG_BREAK); +} #endif void MacroAssembler::Set(Register dst, const Immediate& x) { @@ -377,6 +386,17 @@ void MacroAssembler::FCmp() { } +void MacroAssembler::AbortIfNotNumber(Register object, const char* msg) { + Label ok; + test(object, Immediate(kSmiTagMask)); + j(zero, &ok); + cmp(FieldOperand(object, HeapObject::kMapOffset), + Factory::heap_number_map()); + Assert(equal, msg); + bind(&ok); +} + + void MacroAssembler::EnterFrame(StackFrame::Type type) { push(ebp); mov(ebp, Operand(esp)); @@ -409,12 +429,8 @@ void MacroAssembler::EnterExitFramePrologue(ExitFrame::Mode mode) { // Reserve room for entry stack pointer and push the debug marker. ASSERT(ExitFrameConstants::kSPOffset == -1 * kPointerSize); - push(Immediate(0)); // saved entry sp, patched before call - if (mode == ExitFrame::MODE_DEBUG) { - push(Immediate(0)); - } else { - push(Immediate(CodeObject())); - } + push(Immediate(0)); // Saved entry sp, patched before call. + push(Immediate(CodeObject())); // Accessed from ExitFrame::code_slot. // Save the frame pointer and the context in top. ExternalReference c_entry_fp_address(Top::k_c_entry_fp_address); @@ -551,6 +567,7 @@ void MacroAssembler::PopTryHandler() { Register MacroAssembler::CheckMaps(JSObject* object, Register object_reg, JSObject* holder, Register holder_reg, Register scratch, + int save_at_depth, Label* miss) { // Make sure there's no overlap between scratch and the other // registers. @@ -558,7 +575,11 @@ Register MacroAssembler::CheckMaps(JSObject* object, Register object_reg, // Keep track of the current object in register reg. Register reg = object_reg; - int depth = 1; + int depth = 0; + + if (save_at_depth == depth) { + mov(Operand(esp, kPointerSize), object_reg); + } // Check the maps in the prototype chain. // Traverse the prototype chain from the object and do map checks. @@ -590,7 +611,6 @@ Register MacroAssembler::CheckMaps(JSObject* object, Register object_reg, // to it in the code. Load it from the map. reg = holder_reg; // from now the object is in holder_reg mov(reg, FieldOperand(scratch, Map::kPrototypeOffset)); - } else { // Check the map of the current object. cmp(FieldOperand(reg, HeapObject::kMapOffset), @@ -608,6 +628,10 @@ Register MacroAssembler::CheckMaps(JSObject* object, Register object_reg, mov(reg, Handle<JSObject>(prototype)); } + if (save_at_depth == depth) { + mov(Operand(esp, kPointerSize), reg); + } + // Go to the next object in the prototype chain. object = prototype; } @@ -618,7 +642,7 @@ Register MacroAssembler::CheckMaps(JSObject* object, Register object_reg, j(not_equal, miss, not_taken); // Log the check depth. - LOG(IntEvent("check-maps-depth", depth)); + LOG(IntEvent("check-maps-depth", depth + 1)); // Perform security check for access to the global object and return // the holder register. @@ -1135,6 +1159,16 @@ void MacroAssembler::CallRuntime(Runtime::Function* f, int num_arguments) { } +void MacroAssembler::CallExternalReference(ExternalReference ref, + int num_arguments) { + mov(eax, Immediate(num_arguments)); + mov(ebx, Immediate(ref)); + + CEntryStub stub(1); + CallStub(&stub); +} + + Object* MacroAssembler::TryCallRuntime(Runtime::Function* f, int num_arguments) { if (f->nargs >= 0 && f->nargs != num_arguments) { @@ -1355,10 +1389,22 @@ void MacroAssembler::InvokeFunction(Register fun, } -void MacroAssembler::InvokeBuiltin(Builtins::JavaScript id, InvokeFlag flag) { - bool resolved; - Handle<Code> code = ResolveBuiltin(id, &resolved); +void MacroAssembler::InvokeFunction(JSFunction* function, + const ParameterCount& actual, + InvokeFlag flag) { + ASSERT(function->is_compiled()); + // Get the function and setup the context. + mov(edi, Immediate(Handle<JSFunction>(function))); + mov(esi, FieldOperand(edi, JSFunction::kContextOffset)); + + // Invoke the cached code. + Handle<Code> code(function->code()); + ParameterCount expected(function->shared()->formal_parameter_count()); + InvokeCode(code, expected, actual, RelocInfo::CODE_TARGET, flag); +} + +void MacroAssembler::InvokeBuiltin(Builtins::JavaScript id, InvokeFlag flag) { // Calls are not allowed in some stubs. ASSERT(flag == JUMP_FUNCTION || allow_stub_calls()); @@ -1366,55 +1412,22 @@ void MacroAssembler::InvokeBuiltin(Builtins::JavaScript id, InvokeFlag flag) { // arguments match the expected number of arguments. Fake a // parameter count to avoid emitting code to do the check. ParameterCount expected(0); - InvokeCode(Handle<Code>(code), expected, expected, - RelocInfo::CODE_TARGET, flag); - - const char* name = Builtins::GetName(id); - int argc = Builtins::GetArgumentsCount(id); - - if (!resolved) { - uint32_t flags = - Bootstrapper::FixupFlagsArgumentsCount::encode(argc) | - Bootstrapper::FixupFlagsUseCodeObject::encode(false); - Unresolved entry = { pc_offset() - sizeof(int32_t), flags, name }; - unresolved_.Add(entry); - } + GetBuiltinEntry(edx, id); + InvokeCode(Operand(edx), expected, expected, flag); } void MacroAssembler::GetBuiltinEntry(Register target, Builtins::JavaScript id) { - bool resolved; - Handle<Code> code = ResolveBuiltin(id, &resolved); - - const char* name = Builtins::GetName(id); - int argc = Builtins::GetArgumentsCount(id); - - mov(Operand(target), Immediate(code)); - if (!resolved) { - uint32_t flags = - Bootstrapper::FixupFlagsArgumentsCount::encode(argc) | - Bootstrapper::FixupFlagsUseCodeObject::encode(true); - Unresolved entry = { pc_offset() - sizeof(int32_t), flags, name }; - unresolved_.Add(entry); - } - add(Operand(target), Immediate(Code::kHeaderSize - kHeapObjectTag)); -} - - -Handle<Code> MacroAssembler::ResolveBuiltin(Builtins::JavaScript id, - bool* resolved) { - // Move the builtin function into the temporary function slot by - // reading it from the builtins object. NOTE: We should be able to - // reduce this to two instructions by putting the function table in - // the global object instead of the "builtins" object and by using a - // real register for the function. - mov(edx, Operand(esi, Context::SlotOffset(Context::GLOBAL_INDEX))); - mov(edx, FieldOperand(edx, GlobalObject::kBuiltinsOffset)); + // Load the JavaScript builtin function from the builtins object. + mov(edi, Operand(esi, Context::SlotOffset(Context::GLOBAL_INDEX))); + mov(edi, FieldOperand(edi, GlobalObject::kBuiltinsOffset)); int builtins_offset = JSBuiltinsObject::kJSBuiltinsOffset + (id * kPointerSize); - mov(edi, FieldOperand(edx, builtins_offset)); - - return Builtins::GetCode(id, resolved); + mov(edi, FieldOperand(edi, builtins_offset)); + // Load the code entry point from the function into the target register. + mov(target, FieldOperand(edi, JSFunction::kSharedFunctionInfoOffset)); + mov(target, FieldOperand(target, SharedFunctionInfo::kCodeOffset)); + add(Operand(target), Immediate(Code::kHeaderSize - kHeapObjectTag)); } @@ -1559,6 +1572,20 @@ void MacroAssembler::Abort(const char* msg) { } +void MacroAssembler::JumpIfInstanceTypeIsNotSequentialAscii( + Register instance_type, + Register scratch, + Label *failure) { + if (!scratch.is(instance_type)) { + mov(scratch, instance_type); + } + and_(scratch, + kIsNotStringMask | kStringRepresentationMask | kStringEncodingMask); + cmp(scratch, kStringTag | kSeqStringTag | kAsciiStringTag); + j(not_equal, failure); +} + + void MacroAssembler::JumpIfNotBothSequentialAsciiStrings(Register object1, Register object2, Register scratch1, diff --git a/src/ia32/macro-assembler-ia32.h b/src/ia32/macro-assembler-ia32.h index 0ddbd5d3..69dc54ca 100644 --- a/src/ia32/macro-assembler-ia32.h +++ b/src/ia32/macro-assembler-ia32.h @@ -69,6 +69,7 @@ class MacroAssembler: public Assembler { void CopyRegistersFromStackToMemory(Register base, Register scratch, RegList regs); + void DebugBreak(); #endif // --------------------------------------------------------------------------- @@ -123,6 +124,10 @@ class MacroAssembler: public Assembler { const ParameterCount& actual, InvokeFlag flag); + void InvokeFunction(JSFunction* function, + const ParameterCount& actual, + InvokeFlag flag); + // Invoke specified builtin JavaScript function. Adds an entry to // the unresolved list if the name does not resolve. void InvokeBuiltin(Builtins::JavaScript id, InvokeFlag flag); @@ -171,6 +176,9 @@ class MacroAssembler: public Assembler { sar(reg, kSmiTagSize); } + // Abort execution if argument is not a number. Used in debug code. + void AbortIfNotNumber(Register object, const char* msg); + // --------------------------------------------------------------------------- // Exception handling @@ -193,9 +201,14 @@ class MacroAssembler: public Assembler { // clobbered if it the same as the holder register. The function // returns a register containing the holder - either object_reg or // holder_reg. + // The function can optionally (when save_at_depth != + // kInvalidProtoDepth) save the object at the given depth by moving + // it to [esp + kPointerSize]. Register CheckMaps(JSObject* object, Register object_reg, JSObject* holder, Register holder_reg, - Register scratch, Label* miss); + Register scratch, + int save_at_depth, + Label* miss); // Generate code for checking access rights - used for security checks // on access to global objects across environments. The holder register @@ -347,6 +360,9 @@ class MacroAssembler: public Assembler { // Convenience function: Same as above, but takes the fid instead. void CallRuntime(Runtime::FunctionId id, int num_arguments); + // Convenience function: call an external reference. + void CallExternalReference(ExternalReference ref, int num_arguments); + // Convenience function: Same as above, but takes the fid instead. Object* TryCallRuntime(Runtime::FunctionId id, int num_arguments); @@ -384,13 +400,6 @@ class MacroAssembler: public Assembler { void Move(Register target, Handle<Object> value); - struct Unresolved { - int pc; - uint32_t flags; // see Bootstrapper::FixupFlags decoders/encoders. - const char* name; - }; - List<Unresolved>* unresolved() { return &unresolved_; } - Handle<Object> CodeObject() { return code_object_; } @@ -426,6 +435,13 @@ class MacroAssembler: public Assembler { // --------------------------------------------------------------------------- // String utilities. + // Check whether the instance type represents a flat ascii string. Jump to the + // label if not. If the instance type can be scratched specify same register + // for both instance type and scratch. + void JumpIfInstanceTypeIsNotSequentialAscii(Register instance_type, + Register scratch, + Label *on_not_flat_ascii_string); + // Checks if both objects are sequential ASCII strings, and jumps to label // if either is not. void JumpIfNotBothSequentialAsciiStrings(Register object1, @@ -435,7 +451,6 @@ class MacroAssembler: public Assembler { Label *on_not_flat_ascii_strings); private: - List<Unresolved> unresolved_; bool generating_stub_; bool allow_stub_calls_; // This handle will be patched with the code object on installation. @@ -449,18 +464,6 @@ class MacroAssembler: public Assembler { Label* done, InvokeFlag flag); - // Prepares for a call or jump to a builtin by doing two things: - // 1. Emits code that fetches the builtin's function object from the context - // at runtime, and puts it in the register rdi. - // 2. Fetches the builtin's code object, and returns it in a handle, at - // compile time, so that later code can emit instructions to jump or call - // the builtin directly. If the code object has not yet been created, it - // returns the builtin code object for IllegalFunction, and sets the - // output parameter "resolved" to false. Code that uses the return value - // should then add the address and the builtin name to the list of fixups - // called unresolved_, which is fixed up by the bootstrapper. - Handle<Code> ResolveBuiltin(Builtins::JavaScript id, bool* resolved); - // Activation support. void EnterFrame(StackFrame::Type type); void LeaveFrame(StackFrame::Type type); diff --git a/src/ia32/stub-cache-ia32.cc b/src/ia32/stub-cache-ia32.cc index 7acf81c9..5729d9d7 100644 --- a/src/ia32/stub-cache-ia32.cc +++ b/src/ia32/stub-cache-ia32.cc @@ -152,22 +152,6 @@ void StubCache::GenerateProbe(MacroAssembler* masm, } -static void PushInterceptorArguments(MacroAssembler* masm, - Register receiver, - Register holder, - Register name, - JSObject* holder_obj) { - __ push(receiver); - __ push(holder); - __ push(name); - InterceptorInfo* interceptor = holder_obj->GetNamedInterceptor(); - ASSERT(!Heap::InNewSpace(interceptor)); - __ mov(receiver, Immediate(Handle<Object>(interceptor))); - __ push(receiver); - __ push(FieldOperand(receiver, InterceptorInfo::kDataOffset)); -} - - void StubCompiler::GenerateLoadGlobalFunctionPrototype(MacroAssembler* masm, int index, Register prototype) { @@ -226,30 +210,32 @@ static void GenerateStringCheck(MacroAssembler* masm, void StubCompiler::GenerateLoadStringLength(MacroAssembler* masm, Register receiver, - Register scratch, + Register scratch1, + Register scratch2, Label* miss) { - Label load_length, check_wrapper; + Label check_wrapper; // Check if the object is a string leaving the instance type in the // scratch register. - GenerateStringCheck(masm, receiver, scratch, miss, &check_wrapper); + GenerateStringCheck(masm, receiver, scratch1, miss, &check_wrapper); // Load length from the string and convert to a smi. - __ bind(&load_length); __ mov(eax, FieldOperand(receiver, String::kLengthOffset)); __ SmiTag(eax); __ ret(0); // Check if the object is a JSValue wrapper. __ bind(&check_wrapper); - __ cmp(scratch, JS_VALUE_TYPE); + __ cmp(scratch1, JS_VALUE_TYPE); __ j(not_equal, miss, not_taken); // Check if the wrapped value is a string and load the length // directly if it is. - __ mov(receiver, FieldOperand(receiver, JSValue::kValueOffset)); - GenerateStringCheck(masm, receiver, scratch, miss, miss); - __ jmp(&load_length); + __ mov(scratch2, FieldOperand(receiver, JSValue::kValueOffset)); + GenerateStringCheck(masm, scratch2, scratch1, miss, miss); + __ mov(eax, FieldOperand(scratch2, String::kLengthOffset)); + __ SmiTag(eax); + __ ret(0); } @@ -285,20 +271,31 @@ void StubCompiler::GenerateFastPropertyLoad(MacroAssembler* masm, } +static void PushInterceptorArguments(MacroAssembler* masm, + Register receiver, + Register holder, + Register name, + JSObject* holder_obj) { + __ push(receiver); + __ push(holder); + __ push(name); + InterceptorInfo* interceptor = holder_obj->GetNamedInterceptor(); + ASSERT(!Heap::InNewSpace(interceptor)); + __ mov(receiver, Immediate(Handle<Object>(interceptor))); + __ push(receiver); + __ push(FieldOperand(receiver, InterceptorInfo::kDataOffset)); +} + + static void CompileCallLoadPropertyWithInterceptor(MacroAssembler* masm, Register receiver, Register holder, Register name, JSObject* holder_obj) { PushInterceptorArguments(masm, receiver, holder, name, holder_obj); - - ExternalReference ref = - ExternalReference(IC_Utility(IC::kLoadPropertyWithInterceptorOnly)); - __ mov(eax, Immediate(5)); - __ mov(ebx, Immediate(ref)); - - CEntryStub stub(1); - __ CallStub(&stub); + __ CallExternalReference( + ExternalReference(IC_Utility(IC::kLoadPropertyWithInterceptorOnly)), + 5); } @@ -326,7 +323,7 @@ static void CompileLoadInterceptor(Compiler* compiler, stub_compiler->CheckPrototypes(object, receiver, holder, scratch1, scratch2, name, miss); - if (lookup->IsValid() && lookup->IsCacheable()) { + if (lookup->IsProperty() && lookup->IsCacheable()) { compiler->CompileCacheable(masm, stub_compiler, receiver, @@ -362,7 +359,7 @@ class LoadInterceptorCompiler BASE_EMBEDDED { LookupResult* lookup, String* name, Label* miss_label) { - AccessorInfo* callback = 0; + AccessorInfo* callback = NULL; bool optimize = false; // So far the most popular follow ups for interceptor loads are FIELD // and CALLBACKS, so inline only them, other cases may be added @@ -479,88 +476,337 @@ class LoadInterceptorCompiler BASE_EMBEDDED { }; +// Holds information about possible function call optimizations. +class CallOptimization BASE_EMBEDDED { + public: + explicit CallOptimization(LookupResult* lookup) + : constant_function_(NULL), + is_simple_api_call_(false), + expected_receiver_type_(NULL), + api_call_info_(NULL) { + if (!lookup->IsProperty() || !lookup->IsCacheable()) return; + + // We only optimize constant function calls. + if (lookup->type() != CONSTANT_FUNCTION) return; + + Initialize(lookup->GetConstantFunction()); + } + + explicit CallOptimization(JSFunction* function) { + Initialize(function); + } + + bool is_constant_call() const { + return constant_function_ != NULL; + } + + JSFunction* constant_function() const { + ASSERT(constant_function_ != NULL); + return constant_function_; + } + + bool is_simple_api_call() const { + return is_simple_api_call_; + } + + FunctionTemplateInfo* expected_receiver_type() const { + ASSERT(is_simple_api_call_); + return expected_receiver_type_; + } + + CallHandlerInfo* api_call_info() const { + ASSERT(is_simple_api_call_); + return api_call_info_; + } + + // Returns the depth of the object having the expected type in the + // prototype chain between the two arguments. + int GetPrototypeDepthOfExpectedType(JSObject* object, + JSObject* holder) const { + ASSERT(is_simple_api_call_); + if (expected_receiver_type_ == NULL) return 0; + int depth = 0; + while (object != holder) { + if (object->IsInstanceOf(expected_receiver_type_)) return depth; + object = JSObject::cast(object->GetPrototype()); + ++depth; + } + if (holder->IsInstanceOf(expected_receiver_type_)) return depth; + return kInvalidProtoDepth; + } + + private: + void Initialize(JSFunction* function) { + if (!function->is_compiled()) return; + + constant_function_ = function; + is_simple_api_call_ = false; + + AnalyzePossibleApiFunction(function); + } + + // Determines whether the given function can be called using the + // fast api call builtin. + void AnalyzePossibleApiFunction(JSFunction* function) { + SharedFunctionInfo* sfi = function->shared(); + if (sfi->function_data()->IsUndefined()) return; + FunctionTemplateInfo* info = + FunctionTemplateInfo::cast(sfi->function_data()); + + // Require a C++ callback. + if (info->call_code()->IsUndefined()) return; + api_call_info_ = CallHandlerInfo::cast(info->call_code()); + + // Accept signatures that either have no restrictions at all or + // only have restrictions on the receiver. + if (!info->signature()->IsUndefined()) { + SignatureInfo* signature = SignatureInfo::cast(info->signature()); + if (!signature->args()->IsUndefined()) return; + if (!signature->receiver()->IsUndefined()) { + expected_receiver_type_ = + FunctionTemplateInfo::cast(signature->receiver()); + } + } + + is_simple_api_call_ = true; + } + + JSFunction* constant_function_; + bool is_simple_api_call_; + FunctionTemplateInfo* expected_receiver_type_; + CallHandlerInfo* api_call_info_; +}; + + +// Reserves space for the extra arguments to FastHandleApiCall in the +// caller's frame. +// +// These arguments are set by CheckPrototypes and GenerateFastApiCall. +static void ReserveSpaceForFastApiCall(MacroAssembler* masm, Register scratch) { + // ----------- S t a t e ------------- + // -- esp[0] : return address + // -- esp[4] : last argument in the internal frame of the caller + // ----------------------------------- + __ pop(scratch); + __ push(Immediate(Smi::FromInt(0))); + __ push(Immediate(Smi::FromInt(0))); + __ push(Immediate(Smi::FromInt(0))); + __ push(Immediate(Smi::FromInt(0))); + __ push(scratch); +} + + +// Undoes the effects of ReserveSpaceForFastApiCall. +static void FreeSpaceForFastApiCall(MacroAssembler* masm, Register scratch) { + // ----------- S t a t e ------------- + // -- esp[0] : return address + // -- esp[4] : last fast api call extra argument + // -- ... + // -- esp[16] : first fast api call extra argument + // -- esp[20] : last argument in the internal frame + // ----------------------------------- + __ pop(scratch); + __ add(Operand(esp), Immediate(kPointerSize * 4)); + __ push(scratch); +} + + +// Generates call to FastHandleApiCall builtin. +static void GenerateFastApiCall(MacroAssembler* masm, + const CallOptimization& optimization, + int argc) { + // ----------- S t a t e ------------- + // -- esp[0] : return address + // -- esp[4] : object passing the type check + // (last fast api call extra argument, + // set by CheckPrototypes) + // -- esp[8] : api call data + // -- esp[12] : api callback + // -- esp[16] : api function + // (first fast api call extra argument) + // -- esp[20] : last argument + // -- ... + // -- esp[(argc + 5) * 4] : first argument + // -- esp[(argc + 6) * 4] : receiver + // ----------------------------------- + + // Get the function and setup the context. + JSFunction* function = optimization.constant_function(); + __ mov(edi, Immediate(Handle<JSFunction>(function))); + __ mov(esi, FieldOperand(edi, JSFunction::kContextOffset)); + + // Pass the additional arguments FastHandleApiCall expects. + __ mov(Operand(esp, 4 * kPointerSize), edi); + bool info_loaded = false; + Object* callback = optimization.api_call_info()->callback(); + if (Heap::InNewSpace(callback)) { + info_loaded = true; + __ mov(ecx, Handle<CallHandlerInfo>(optimization.api_call_info())); + __ mov(ebx, FieldOperand(ecx, CallHandlerInfo::kCallbackOffset)); + __ mov(Operand(esp, 3 * kPointerSize), ebx); + } else { + __ mov(Operand(esp, 3 * kPointerSize), Immediate(Handle<Object>(callback))); + } + Object* call_data = optimization.api_call_info()->data(); + if (Heap::InNewSpace(call_data)) { + if (!info_loaded) { + __ mov(ecx, Handle<CallHandlerInfo>(optimization.api_call_info())); + } + __ mov(ebx, FieldOperand(ecx, CallHandlerInfo::kDataOffset)); + __ mov(Operand(esp, 2 * kPointerSize), ebx); + } else { + __ mov(Operand(esp, 2 * kPointerSize), + Immediate(Handle<Object>(call_data))); + } + + // Set the number of arguments. + __ mov(eax, Immediate(argc + 4)); + + // Jump to the fast api call builtin (tail call). + Handle<Code> code = Handle<Code>( + Builtins::builtin(Builtins::FastHandleApiCall)); + ParameterCount expected(0); + __ InvokeCode(code, expected, expected, + RelocInfo::CODE_TARGET, JUMP_FUNCTION); +} + + class CallInterceptorCompiler BASE_EMBEDDED { public: - CallInterceptorCompiler(const ParameterCount& arguments, Register name) - : arguments_(arguments), argc_(arguments.immediate()), name_(name) {} + CallInterceptorCompiler(StubCompiler* stub_compiler, + const ParameterCount& arguments, + Register name) + : stub_compiler_(stub_compiler), + arguments_(arguments), + name_(name) {} + + void Compile(MacroAssembler* masm, + JSObject* object, + JSObject* holder, + String* name, + LookupResult* lookup, + Register receiver, + Register scratch1, + Register scratch2, + Label* miss) { + ASSERT(holder->HasNamedInterceptor()); + ASSERT(!holder->GetNamedInterceptor()->getter()->IsUndefined()); + + // Check that the receiver isn't a smi. + __ test(receiver, Immediate(kSmiTagMask)); + __ j(zero, miss, not_taken); + + CallOptimization optimization(lookup); + + if (optimization.is_constant_call() && + !Top::CanHaveSpecialFunctions(holder)) { + CompileCacheable(masm, + object, + receiver, + scratch1, + scratch2, + holder, + lookup, + name, + optimization, + miss); + } else { + CompileRegular(masm, + object, + receiver, + scratch1, + scratch2, + name, + holder, + miss); + } + } + private: void CompileCacheable(MacroAssembler* masm, - StubCompiler* stub_compiler, + JSObject* object, Register receiver, - Register holder, Register scratch1, Register scratch2, JSObject* holder_obj, LookupResult* lookup, String* name, + const CallOptimization& optimization, Label* miss_label) { - JSFunction* function = 0; - bool optimize = false; - // So far the most popular case for failed interceptor is - // CONSTANT_FUNCTION sitting below. - if (lookup->type() == CONSTANT_FUNCTION) { - function = lookup->GetConstantFunction(); - // JSArray holder is a special case for call constant function - // (see the corresponding code). - if (function->is_compiled() && !holder_obj->IsJSArray()) { - optimize = true; + ASSERT(optimization.is_constant_call()); + ASSERT(!lookup->holder()->IsGlobalObject()); + + int depth1 = kInvalidProtoDepth; + int depth2 = kInvalidProtoDepth; + bool can_do_fast_api_call = false; + if (optimization.is_simple_api_call() && + !lookup->holder()->IsGlobalObject()) { + depth1 = optimization.GetPrototypeDepthOfExpectedType(object, holder_obj); + if (depth1 == kInvalidProtoDepth) { + depth2 = optimization.GetPrototypeDepthOfExpectedType(holder_obj, + lookup->holder()); } + can_do_fast_api_call = (depth1 != kInvalidProtoDepth) || + (depth2 != kInvalidProtoDepth); } - if (!optimize) { - CompileRegular(masm, receiver, holder, scratch2, holder_obj, miss_label); - return; + __ IncrementCounter(&Counters::call_const_interceptor, 1); + + if (can_do_fast_api_call) { + __ IncrementCounter(&Counters::call_const_interceptor_fast_api, 1); + ReserveSpaceForFastApiCall(masm, scratch1); } - __ EnterInternalFrame(); - __ push(holder); // Save the holder. - __ push(name_); // Save the name. + Label miss_cleanup; + Label* miss = can_do_fast_api_call ? &miss_cleanup : miss_label; + Register holder = + stub_compiler_->CheckPrototypes(object, receiver, holder_obj, + scratch1, scratch2, name, + depth1, miss); - CompileCallLoadPropertyWithInterceptor(masm, - receiver, - holder, - name_, - holder_obj); + Label regular_invoke; + LoadWithInterceptor(masm, receiver, holder, holder_obj, ®ular_invoke); - __ pop(name_); // Restore the name. - __ pop(receiver); // Restore the holder. - __ LeaveInternalFrame(); + // Generate code for the failed interceptor case. - __ cmp(eax, Factory::no_interceptor_result_sentinel()); - Label invoke; - __ j(not_equal, &invoke); - - stub_compiler->CheckPrototypes(holder_obj, receiver, - lookup->holder(), scratch1, - scratch2, - name, - miss_label); - if (lookup->holder()->IsGlobalObject()) { - __ mov(edx, Operand(esp, (argc_ + 1) * kPointerSize)); - __ mov(edx, FieldOperand(edx, GlobalObject::kGlobalReceiverOffset)); - __ mov(Operand(esp, (argc_ + 1) * kPointerSize), edx); - } + // Check the lookup is still valid. + stub_compiler_->CheckPrototypes(holder_obj, receiver, + lookup->holder(), + scratch1, scratch2, name, + depth2, miss); - ASSERT(function->is_compiled()); - // Get the function and setup the context. - __ mov(edi, Immediate(Handle<JSFunction>(function))); - __ mov(esi, FieldOperand(edi, JSFunction::kContextOffset)); + if (can_do_fast_api_call) { + GenerateFastApiCall(masm, optimization, arguments_.immediate()); + } else { + __ InvokeFunction(optimization.constant_function(), arguments_, + JUMP_FUNCTION); + } - // Jump to the cached code (tail call). - Handle<Code> code(function->code()); - ParameterCount expected(function->shared()->formal_parameter_count()); - __ InvokeCode(code, expected, arguments_, - RelocInfo::CODE_TARGET, JUMP_FUNCTION); + if (can_do_fast_api_call) { + __ bind(&miss_cleanup); + FreeSpaceForFastApiCall(masm, scratch1); + __ jmp(miss_label); + } - __ bind(&invoke); + __ bind(®ular_invoke); + if (can_do_fast_api_call) { + FreeSpaceForFastApiCall(masm, scratch1); + } } void CompileRegular(MacroAssembler* masm, + JSObject* object, Register receiver, - Register holder, - Register scratch, + Register scratch1, + Register scratch2, + String* name, JSObject* holder_obj, Label* miss_label) { + Register holder = + stub_compiler_->CheckPrototypes(object, receiver, holder_obj, + scratch1, scratch2, name, + miss_label); + __ EnterInternalFrame(); // Save the name_ register across the call. __ push(name_); @@ -571,22 +817,41 @@ class CallInterceptorCompiler BASE_EMBEDDED { name_, holder_obj); - ExternalReference ref = ExternalReference( - IC_Utility(IC::kLoadPropertyWithInterceptorForCall)); - __ mov(eax, Immediate(5)); - __ mov(ebx, Immediate(ref)); - - CEntryStub stub(1); - __ CallStub(&stub); + __ CallExternalReference( + ExternalReference( + IC_Utility(IC::kLoadPropertyWithInterceptorForCall)), + 5); // Restore the name_ register. __ pop(name_); __ LeaveInternalFrame(); } - private: + void LoadWithInterceptor(MacroAssembler* masm, + Register receiver, + Register holder, + JSObject* holder_obj, + Label* interceptor_succeeded) { + __ EnterInternalFrame(); + __ push(holder); // Save the holder. + __ push(name_); // Save the name. + + CompileCallLoadPropertyWithInterceptor(masm, + receiver, + holder, + name_, + holder_obj); + + __ pop(name_); // Restore the name. + __ pop(receiver); // Restore the holder. + __ LeaveInternalFrame(); + + __ cmp(eax, Factory::no_interceptor_result_sentinel()); + __ j(not_equal, interceptor_succeeded); + } + + StubCompiler* stub_compiler_; const ParameterCount& arguments_; - int argc_; Register name_; }; @@ -605,8 +870,9 @@ void StubCompiler::GenerateLoadMiss(MacroAssembler* masm, Code::Kind kind) { } +// Both name_reg and receiver_reg are preserved on jumps to miss_label, +// but may be destroyed if store is successful. void StubCompiler::GenerateStoreField(MacroAssembler* masm, - Builtins::Name storage_extend, JSObject* object, int index, Map* transition, @@ -636,9 +902,13 @@ void StubCompiler::GenerateStoreField(MacroAssembler* masm, if ((transition != NULL) && (object->map()->unused_property_fields() == 0)) { // The properties must be extended before we can store the value. // We jump to a runtime call that extends the properties array. - __ mov(ecx, Immediate(Handle<Map>(transition))); - Handle<Code> ic(Builtins::builtin(storage_extend)); - __ jmp(ic, RelocInfo::CODE_TARGET); + __ pop(scratch); // Return address. + __ push(receiver_reg); + __ push(Immediate(Handle<Map>(transition))); + __ push(eax); + __ push(scratch); + __ TailCallRuntime( + ExternalReference(IC_Utility(IC::kSharedStoreIC_ExtendStorage)), 3, 1); return; } @@ -691,10 +961,12 @@ Register StubCompiler::CheckPrototypes(JSObject* object, Register holder_reg, Register scratch, String* name, + int push_at_depth, Label* miss) { // Check that the maps haven't changed. Register result = - masm()->CheckMaps(object, object_reg, holder, holder_reg, scratch, miss); + masm()->CheckMaps(object, object_reg, holder, holder_reg, scratch, + push_at_depth, miss); // If we've skipped any global objects, it's not enough to verify // that their maps haven't changed. @@ -716,7 +988,7 @@ Register StubCompiler::CheckPrototypes(JSObject* object, object = JSObject::cast(object->GetPrototype()); } - // Return the register containin the holder. + // Return the register containing the holder. return result; } @@ -887,7 +1159,7 @@ Object* StubCompiler::CompileLazyCompile(Code::Flags flags) { } -Object* CallStubCompiler::CompileCallField(Object* object, +Object* CallStubCompiler::CompileCallField(JSObject* object, JSObject* holder, int index, String* name) { @@ -909,9 +1181,7 @@ Object* CallStubCompiler::CompileCallField(Object* object, __ j(zero, &miss, not_taken); // Do the right check and compute the holder register. - Register reg = - CheckPrototypes(JSObject::cast(object), edx, holder, - ebx, eax, name, &miss); + Register reg = CheckPrototypes(object, edx, holder, ebx, eax, name, &miss); GenerateFastPropertyLoad(masm(), edi, reg, holder, index); @@ -969,15 +1239,31 @@ Object* CallStubCompiler::CompileCallConstant(Object* object, // unless we're doing a receiver map check. ASSERT(!object->IsGlobalObject() || check == RECEIVER_MAP_CHECK); + CallOptimization optimization(function); + int depth = kInvalidProtoDepth; + switch (check) { case RECEIVER_MAP_CHECK: + __ IncrementCounter(&Counters::call_const, 1); + + if (optimization.is_simple_api_call() && !object->IsGlobalObject()) { + depth = optimization.GetPrototypeDepthOfExpectedType( + JSObject::cast(object), holder); + } + + if (depth != kInvalidProtoDepth) { + __ IncrementCounter(&Counters::call_const_fast_api, 1); + ReserveSpaceForFastApiCall(masm(), eax); + } + // Check that the maps haven't changed. CheckPrototypes(JSObject::cast(object), edx, holder, - ebx, eax, name, &miss); + ebx, eax, name, depth, &miss); // Patch the receiver on the stack with the global proxy if // necessary. if (object->IsGlobalObject()) { + ASSERT(depth == kInvalidProtoDepth); __ mov(edx, FieldOperand(edx, GlobalObject::kGlobalReceiverOffset)); __ mov(Operand(esp, (argc + 1) * kPointerSize), edx); } @@ -1062,19 +1348,17 @@ Object* CallStubCompiler::CompileCallConstant(Object* object, UNREACHABLE(); } - // Get the function and setup the context. - __ mov(edi, Immediate(Handle<JSFunction>(function))); - __ mov(esi, FieldOperand(edi, JSFunction::kContextOffset)); - - // Jump to the cached code (tail call). - ASSERT(function->is_compiled()); - Handle<Code> code(function->code()); - ParameterCount expected(function->shared()->formal_parameter_count()); - __ InvokeCode(code, expected, arguments(), - RelocInfo::CODE_TARGET, JUMP_FUNCTION); + if (depth != kInvalidProtoDepth) { + GenerateFastApiCall(masm(), optimization, argc); + } else { + __ InvokeFunction(function, arguments(), JUMP_FUNCTION); + } // Handle call cache miss. __ bind(&miss); + if (depth != kInvalidProtoDepth) { + FreeSpaceForFastApiCall(masm(), eax); + } Handle<Code> ic = ComputeCallMiss(arguments().immediate()); __ jmp(ic, RelocInfo::CODE_TARGET); @@ -1087,7 +1371,7 @@ Object* CallStubCompiler::CompileCallConstant(Object* object, } -Object* CallStubCompiler::CompileCallInterceptor(Object* object, +Object* CallStubCompiler::CompileCallInterceptor(JSObject* object, JSObject* holder, String* name) { // ----------- S t a t e ------------- @@ -1108,18 +1392,16 @@ Object* CallStubCompiler::CompileCallInterceptor(Object* object, // Get the receiver from the stack. __ mov(edx, Operand(esp, (argc + 1) * kPointerSize)); - CallInterceptorCompiler compiler(arguments(), ecx); - CompileLoadInterceptor(&compiler, - this, - masm(), - JSObject::cast(object), - holder, - name, - &lookup, - edx, - ebx, - edi, - &miss); + CallInterceptorCompiler compiler(this, arguments(), ecx); + compiler.Compile(masm(), + object, + holder, + name, + &lookup, + edx, + ebx, + edi, + &miss); // Restore receiver. __ mov(edx, Operand(esp, (argc + 1) * kPointerSize)); @@ -1249,7 +1531,6 @@ Object* StoreStubCompiler::CompileStoreField(JSObject* object, // Generate store field code. Trashes the name register. GenerateStoreField(masm(), - Builtins::StoreIC_ExtendStorage, object, index, transition, @@ -1423,15 +1704,14 @@ Object* KeyedStoreStubCompiler::CompileStoreField(JSObject* object, __ j(not_equal, &miss, not_taken); // Get the object from the stack. - __ mov(ebx, Operand(esp, 2 * kPointerSize)); + __ mov(edx, Operand(esp, 2 * kPointerSize)); // Generate store field code. Trashes the name register. GenerateStoreField(masm(), - Builtins::KeyedStoreIC_ExtendStorage, object, index, transition, - ebx, ecx, edx, + edx, ecx, ebx, &miss); // Handle store cache miss. @@ -1451,13 +1731,12 @@ Object* LoadStubCompiler::CompileLoadField(JSObject* object, int index, String* name) { // ----------- S t a t e ------------- + // -- eax : receiver // -- ecx : name // -- esp[0] : return address - // -- esp[4] : receiver // ----------------------------------- Label miss; - __ mov(eax, Operand(esp, kPointerSize)); GenerateLoadField(object, holder, eax, ebx, edx, index, name, &miss); __ bind(&miss); GenerateLoadMiss(masm(), Code::LOAD_IC); @@ -1472,13 +1751,12 @@ Object* LoadStubCompiler::CompileLoadCallback(String* name, JSObject* holder, AccessorInfo* callback) { // ----------- S t a t e ------------- + // -- eax : receiver // -- ecx : name // -- esp[0] : return address - // -- esp[4] : receiver // ----------------------------------- Label miss; - __ mov(eax, Operand(esp, kPointerSize)); Failure* failure = Failure::InternalError(); bool success = GenerateLoadCallback(object, holder, eax, ecx, ebx, edx, callback, name, &miss, &failure); @@ -1497,13 +1775,12 @@ Object* LoadStubCompiler::CompileLoadConstant(JSObject* object, Object* value, String* name) { // ----------- S t a t e ------------- + // -- eax : receiver // -- ecx : name // -- esp[0] : return address - // -- esp[4] : receiver // ----------------------------------- Label miss; - __ mov(eax, Operand(esp, kPointerSize)); GenerateLoadConstant(object, holder, eax, ebx, edx, value, name, &miss); __ bind(&miss); GenerateLoadMiss(masm(), Code::LOAD_IC); @@ -1517,16 +1794,15 @@ Object* LoadStubCompiler::CompileLoadInterceptor(JSObject* receiver, JSObject* holder, String* name) { // ----------- S t a t e ------------- + // -- eax : receiver // -- ecx : name // -- esp[0] : return address - // -- esp[4] : receiver // ----------------------------------- Label miss; LookupResult lookup; LookupPostInterceptor(holder, name, &lookup); - __ mov(eax, Operand(esp, kPointerSize)); // TODO(368): Compile in the whole chain: all the interceptors in // prototypes and ultimate answer. GenerateLoadInterceptor(receiver, @@ -1553,15 +1829,12 @@ Object* LoadStubCompiler::CompileLoadGlobal(JSObject* object, String* name, bool is_dont_delete) { // ----------- S t a t e ------------- + // -- eax : receiver // -- ecx : name // -- esp[0] : return address - // -- esp[4] : receiver // ----------------------------------- Label miss; - // Get the receiver from the stack. - __ mov(eax, Operand(esp, kPointerSize)); - // If the object is the holder then we know that it's a global // object which can only happen for contextual loads. In this case, // the receiver cannot be a smi. @@ -1574,19 +1847,20 @@ Object* LoadStubCompiler::CompileLoadGlobal(JSObject* object, CheckPrototypes(object, eax, holder, ebx, edx, name, &miss); // Get the value from the cell. - __ mov(eax, Immediate(Handle<JSGlobalPropertyCell>(cell))); - __ mov(eax, FieldOperand(eax, JSGlobalPropertyCell::kValueOffset)); + __ mov(ebx, Immediate(Handle<JSGlobalPropertyCell>(cell))); + __ mov(ebx, FieldOperand(ebx, JSGlobalPropertyCell::kValueOffset)); // Check for deleted property if property can actually be deleted. if (!is_dont_delete) { - __ cmp(eax, Factory::the_hole_value()); + __ cmp(ebx, Factory::the_hole_value()); __ j(equal, &miss, not_taken); } else if (FLAG_debug_code) { - __ cmp(eax, Factory::the_hole_value()); + __ cmp(ebx, Factory::the_hole_value()); __ Check(not_equal, "DontDelete cells can't contain the hole"); } __ IncrementCounter(&Counters::named_load_global_inline, 1); + __ mov(eax, ebx); __ ret(0); __ bind(&miss); @@ -1603,21 +1877,19 @@ Object* KeyedLoadStubCompiler::CompileLoadField(String* name, JSObject* holder, int index) { // ----------- S t a t e ------------- + // -- eax : key + // -- edx : receiver // -- esp[0] : return address - // -- esp[4] : name - // -- esp[8] : receiver // ----------------------------------- Label miss; - __ mov(eax, Operand(esp, kPointerSize)); - __ mov(ecx, Operand(esp, 2 * kPointerSize)); __ IncrementCounter(&Counters::keyed_load_field, 1); // Check that the name has not changed. __ cmp(Operand(eax), Immediate(Handle<String>(name))); __ j(not_equal, &miss, not_taken); - GenerateLoadField(receiver, holder, ecx, ebx, edx, index, name, &miss); + GenerateLoadField(receiver, holder, edx, ebx, ecx, index, name, &miss); __ bind(&miss); __ DecrementCounter(&Counters::keyed_load_field, 1); @@ -1633,14 +1905,12 @@ Object* KeyedLoadStubCompiler::CompileLoadCallback(String* name, JSObject* holder, AccessorInfo* callback) { // ----------- S t a t e ------------- + // -- eax : key + // -- edx : receiver // -- esp[0] : return address - // -- esp[4] : name - // -- esp[8] : receiver // ----------------------------------- Label miss; - __ mov(eax, Operand(esp, kPointerSize)); - __ mov(ecx, Operand(esp, 2 * kPointerSize)); __ IncrementCounter(&Counters::keyed_load_callback, 1); // Check that the name has not changed. @@ -1648,7 +1918,7 @@ Object* KeyedLoadStubCompiler::CompileLoadCallback(String* name, __ j(not_equal, &miss, not_taken); Failure* failure = Failure::InternalError(); - bool success = GenerateLoadCallback(receiver, holder, ecx, eax, ebx, edx, + bool success = GenerateLoadCallback(receiver, holder, edx, eax, ebx, ecx, callback, name, &miss, &failure); if (!success) return failure; @@ -1666,21 +1936,19 @@ Object* KeyedLoadStubCompiler::CompileLoadConstant(String* name, JSObject* holder, Object* value) { // ----------- S t a t e ------------- + // -- eax : key + // -- edx : receiver // -- esp[0] : return address - // -- esp[4] : name - // -- esp[8] : receiver // ----------------------------------- Label miss; - __ mov(eax, Operand(esp, kPointerSize)); - __ mov(ecx, Operand(esp, 2 * kPointerSize)); __ IncrementCounter(&Counters::keyed_load_constant_function, 1); // Check that the name has not changed. __ cmp(Operand(eax), Immediate(Handle<String>(name))); __ j(not_equal, &miss, not_taken); - GenerateLoadConstant(receiver, holder, ecx, ebx, edx, + GenerateLoadConstant(receiver, holder, edx, ebx, ecx, value, name, &miss); __ bind(&miss); __ DecrementCounter(&Counters::keyed_load_constant_function, 1); @@ -1695,14 +1963,12 @@ Object* KeyedLoadStubCompiler::CompileLoadInterceptor(JSObject* receiver, JSObject* holder, String* name) { // ----------- S t a t e ------------- + // -- eax : key + // -- edx : receiver // -- esp[0] : return address - // -- esp[4] : name - // -- esp[8] : receiver // ----------------------------------- Label miss; - __ mov(eax, Operand(esp, kPointerSize)); - __ mov(ecx, Operand(esp, 2 * kPointerSize)); __ IncrementCounter(&Counters::keyed_load_interceptor, 1); // Check that the name has not changed. @@ -1714,9 +1980,9 @@ Object* KeyedLoadStubCompiler::CompileLoadInterceptor(JSObject* receiver, GenerateLoadInterceptor(receiver, holder, &lookup, - ecx, - eax, edx, + eax, + ecx, ebx, name, &miss); @@ -1733,21 +1999,19 @@ Object* KeyedLoadStubCompiler::CompileLoadInterceptor(JSObject* receiver, Object* KeyedLoadStubCompiler::CompileLoadArrayLength(String* name) { // ----------- S t a t e ------------- + // -- eax : key + // -- edx : receiver // -- esp[0] : return address - // -- esp[4] : name - // -- esp[8] : receiver // ----------------------------------- Label miss; - __ mov(eax, Operand(esp, kPointerSize)); - __ mov(ecx, Operand(esp, 2 * kPointerSize)); __ IncrementCounter(&Counters::keyed_load_array_length, 1); // Check that the name has not changed. __ cmp(Operand(eax), Immediate(Handle<String>(name))); __ j(not_equal, &miss, not_taken); - GenerateLoadArrayLength(masm(), ecx, edx, &miss); + GenerateLoadArrayLength(masm(), edx, ecx, &miss); __ bind(&miss); __ DecrementCounter(&Counters::keyed_load_array_length, 1); GenerateLoadMiss(masm(), Code::KEYED_LOAD_IC); @@ -1759,21 +2023,19 @@ Object* KeyedLoadStubCompiler::CompileLoadArrayLength(String* name) { Object* KeyedLoadStubCompiler::CompileLoadStringLength(String* name) { // ----------- S t a t e ------------- + // -- eax : key + // -- edx : receiver // -- esp[0] : return address - // -- esp[4] : name - // -- esp[8] : receiver // ----------------------------------- Label miss; - __ mov(eax, Operand(esp, kPointerSize)); - __ mov(ecx, Operand(esp, 2 * kPointerSize)); __ IncrementCounter(&Counters::keyed_load_string_length, 1); // Check that the name has not changed. __ cmp(Operand(eax), Immediate(Handle<String>(name))); __ j(not_equal, &miss, not_taken); - GenerateLoadStringLength(masm(), ecx, edx, &miss); + GenerateLoadStringLength(masm(), edx, ecx, ebx, &miss); __ bind(&miss); __ DecrementCounter(&Counters::keyed_load_string_length, 1); GenerateLoadMiss(masm(), Code::KEYED_LOAD_IC); @@ -1785,21 +2047,19 @@ Object* KeyedLoadStubCompiler::CompileLoadStringLength(String* name) { Object* KeyedLoadStubCompiler::CompileLoadFunctionPrototype(String* name) { // ----------- S t a t e ------------- + // -- eax : key + // -- edx : receiver // -- esp[0] : return address - // -- esp[4] : name - // -- esp[8] : receiver // ----------------------------------- Label miss; - __ mov(eax, Operand(esp, kPointerSize)); - __ mov(ecx, Operand(esp, 2 * kPointerSize)); __ IncrementCounter(&Counters::keyed_load_function_prototype, 1); // Check that the name has not changed. __ cmp(Operand(eax), Immediate(Handle<String>(name))); __ j(not_equal, &miss, not_taken); - GenerateLoadFunctionPrototype(masm(), ecx, edx, ebx, &miss); + GenerateLoadFunctionPrototype(masm(), edx, ecx, ebx, &miss); __ bind(&miss); __ DecrementCounter(&Counters::keyed_load_function_prototype, 1); GenerateLoadMiss(masm(), Code::KEYED_LOAD_IC); diff --git a/src/ia32/virtual-frame-ia32.cc b/src/ia32/virtual-frame-ia32.cc index 9267507c..7df028e9 100644 --- a/src/ia32/virtual-frame-ia32.cc +++ b/src/ia32/virtual-frame-ia32.cc @@ -45,7 +45,7 @@ VirtualFrame::VirtualFrame() : elements_(parameter_count() + local_count() + kPreallocatedElements), stack_pointer_(parameter_count() + 1) { // 0-based index of TOS. for (int i = 0; i <= stack_pointer_; i++) { - elements_.Add(FrameElement::MemoryElement()); + elements_.Add(FrameElement::MemoryElement(NumberInfo::kUnknown)); } for (int i = 0; i < RegisterAllocator::kNumRegisters; i++) { register_locations_[i] = kIllegalIndex; @@ -173,10 +173,12 @@ void VirtualFrame::MakeMergable() { for (int i = 0; i < element_count(); i++) { FrameElement element = elements_[i]; + // All number type information is reset to unknown for a mergable frame + // because of incoming back edges. if (element.is_constant() || element.is_copy()) { if (element.is_synced()) { // Just spill. - elements_[i] = FrameElement::MemoryElement(); + elements_[i] = FrameElement::MemoryElement(NumberInfo::kUnknown); } else { // Allocate to a register. FrameElement backing_element; // Invalid if not a copy. @@ -187,7 +189,8 @@ void VirtualFrame::MakeMergable() { ASSERT(fresh.is_valid()); // A register was spilled if all were in use. elements_[i] = FrameElement::RegisterElement(fresh.reg(), - FrameElement::NOT_SYNCED); + FrameElement::NOT_SYNCED, + NumberInfo::kUnknown); Use(fresh.reg(), i); // Emit a move. @@ -220,6 +223,7 @@ void VirtualFrame::MakeMergable() { // The copy flag is not relied on before the end of this loop, // including when registers are spilled. elements_[i].clear_copied(); + elements_[i].set_number_info(NumberInfo::kUnknown); } } } @@ -607,10 +611,14 @@ int VirtualFrame::InvalidateFrameSlotAt(int index) { // Set the new backing element. if (elements_[new_backing_index].is_synced()) { elements_[new_backing_index] = - FrameElement::RegisterElement(backing_reg, FrameElement::SYNCED); + FrameElement::RegisterElement(backing_reg, + FrameElement::SYNCED, + original.number_info()); } else { elements_[new_backing_index] = - FrameElement::RegisterElement(backing_reg, FrameElement::NOT_SYNCED); + FrameElement::RegisterElement(backing_reg, + FrameElement::NOT_SYNCED, + original.number_info()); } // Update the other copies. for (int i = new_backing_index + 1; i < element_count(); i++) { @@ -641,7 +649,8 @@ void VirtualFrame::TakeFrameSlotAt(int index) { ASSERT(fresh.is_valid()); FrameElement new_element = FrameElement::RegisterElement(fresh.reg(), - FrameElement::NOT_SYNCED); + FrameElement::NOT_SYNCED, + original.number_info()); Use(fresh.reg(), element_count()); elements_.Add(new_element); __ mov(fresh.reg(), Operand(ebp, fp_relative(index))); @@ -853,6 +862,17 @@ Result VirtualFrame::CallRuntime(Runtime::FunctionId id, int arg_count) { } +#ifdef ENABLE_DEBUGGER_SUPPORT +void VirtualFrame::DebugBreak() { + PrepareForCall(0, 0); + ASSERT(cgen()->HasValidEntryRegisters()); + __ DebugBreak(); + Result result = cgen()->allocator()->Allocate(eax); + ASSERT(result.is_valid()); +} +#endif + + Result VirtualFrame::InvokeBuiltin(Builtins::JavaScript id, InvokeFlag flag, int arg_count) { @@ -877,67 +897,89 @@ Result VirtualFrame::RawCallCodeObject(Handle<Code> code, Result VirtualFrame::CallLoadIC(RelocInfo::Mode mode) { // Name and receiver are on the top of the frame. The IC expects - // name in ecx and receiver on the stack. It does not drop the - // receiver. + // name in ecx and receiver in eax. Handle<Code> ic(Builtins::builtin(Builtins::LoadIC_Initialize)); Result name = Pop(); - PrepareForCall(1, 0); // One stack arg, not callee-dropped. - name.ToRegister(ecx); + Result receiver = Pop(); + PrepareForCall(0, 0); // No stack arguments. + // Move results to the right registers: + if (name.is_register() && name.reg().is(eax)) { + if (receiver.is_register() && receiver.reg().is(ecx)) { + // Wrong registers. + __ xchg(eax, ecx); + } else { + // Register ecx is free for name, which frees eax for receiver. + name.ToRegister(ecx); + receiver.ToRegister(eax); + } + } else { + // Register eax is free for receiver, which frees ecx for name. + receiver.ToRegister(eax); + name.ToRegister(ecx); + } name.Unuse(); + receiver.Unuse(); return RawCallCodeObject(ic, mode); } Result VirtualFrame::CallKeyedLoadIC(RelocInfo::Mode mode) { - // Key and receiver are on top of the frame. The IC expects them on - // the stack. It does not drop them. + // Key and receiver are on top of the frame. Put them in eax and edx. + Result key = Pop(); + Result receiver = Pop(); + PrepareForCall(0, 0); + + if (!key.is_register() || !key.reg().is(edx)) { + // Register edx is available for receiver. + receiver.ToRegister(edx); + key.ToRegister(eax); + } else if (!receiver.is_register() || !receiver.reg().is(eax)) { + // Register eax is available for key. + key.ToRegister(eax); + receiver.ToRegister(edx); + } else { + __ xchg(edx, eax); + } + key.Unuse(); + receiver.Unuse(); + Handle<Code> ic(Builtins::builtin(Builtins::KeyedLoadIC_Initialize)); - PrepareForCall(2, 0); // Two stack args, neither callee-dropped. return RawCallCodeObject(ic, mode); } -Result VirtualFrame::CallStoreIC() { - // Name, value, and receiver are on top of the frame. The IC - // expects name in ecx, value in eax, and receiver in edx. +Result VirtualFrame::CallStoreIC(Handle<String> name, bool is_contextual) { + // Value and (if not contextual) receiver are on top of the frame. + // The IC expects name in ecx, value in eax, and receiver in edx. Handle<Code> ic(Builtins::builtin(Builtins::StoreIC_Initialize)); - Result name = Pop(); Result value = Pop(); - Result receiver = Pop(); - PrepareForCall(0, 0); + if (is_contextual) { + PrepareForCall(0, 0); + value.ToRegister(eax); + __ mov(edx, Operand(esi, Context::SlotOffset(Context::GLOBAL_INDEX))); + __ mov(ecx, name); + } else { + Result receiver = Pop(); + PrepareForCall(0, 0); - // Optimized for case in which name is a constant value. - if (name.is_register() && (name.reg().is(edx) || name.reg().is(eax))) { - if (!is_used(ecx)) { - name.ToRegister(ecx); - } else if (!is_used(ebx)) { - name.ToRegister(ebx); - } else { - ASSERT(!is_used(edi)); // Only three results are live, so edi is free. - name.ToRegister(edi); - } - } - // Now name is not in edx or eax, so we can fix them, then move name to ecx. - if (value.is_register() && value.reg().is(edx)) { - if (receiver.is_register() && receiver.reg().is(eax)) { - // Wrong registers. - __ xchg(eax, edx); + if (value.is_register() && value.reg().is(edx)) { + if (receiver.is_register() && receiver.reg().is(eax)) { + // Wrong registers. + __ xchg(eax, edx); + } else { + // Register eax is free for value, which frees edx for receiver. + value.ToRegister(eax); + receiver.ToRegister(edx); + } } else { - // Register eax is free for value, which frees edx for receiver. - value.ToRegister(eax); + // Register edx is free for receiver, which guarantees eax is free for + // value. receiver.ToRegister(edx); + value.ToRegister(eax); } - } else { - // Register edx is free for receiver, which guarantees eax is free for - // value. - receiver.ToRegister(edx); - value.ToRegister(eax); } - // Receiver and value are in the right place, so ecx is free for name. - name.ToRegister(ecx); - name.Unuse(); + __ mov(ecx, name); value.Unuse(); - receiver.Unuse(); return RawCallCodeObject(ic, RelocInfo::CODE_TARGET); } @@ -947,7 +989,6 @@ Result VirtualFrame::CallKeyedStoreIC() { // expects value in eax and key and receiver on the stack. It does // not drop the key and receiver. Handle<Code> ic(Builtins::builtin(Builtins::KeyedStoreIC_Initialize)); - // TODO(1222589): Make the IC grab the values from the stack. Result value = Pop(); PrepareForCall(2, 0); // Two stack args, neither callee-dropped. value.ToRegister(eax); @@ -1025,6 +1066,14 @@ Result VirtualFrame::Pop() { int index = element_count(); ASSERT(element.is_valid()); + // Get number type information of the result. + NumberInfo::Type info; + if (!element.is_copy()) { + info = element.number_info(); + } else { + info = elements_[element.index()].number_info(); + } + bool pop_needed = (stack_pointer_ == index); if (pop_needed) { stack_pointer_--; @@ -1032,6 +1081,7 @@ Result VirtualFrame::Pop() { Result temp = cgen()->allocator()->Allocate(); ASSERT(temp.is_valid()); __ pop(temp.reg()); + temp.set_number_info(info); return temp; } @@ -1059,14 +1109,16 @@ Result VirtualFrame::Pop() { ASSERT(temp.is_valid()); Use(temp.reg(), index); FrameElement new_element = - FrameElement::RegisterElement(temp.reg(), FrameElement::SYNCED); + FrameElement::RegisterElement(temp.reg(), + FrameElement::SYNCED, + element.number_info()); // Preserve the copy flag on the element. if (element.is_copied()) new_element.set_copied(); elements_[index] = new_element; __ mov(temp.reg(), Operand(ebp, fp_relative(index))); - return Result(temp.reg()); + return Result(temp.reg(), info); } else if (element.is_register()) { - return Result(element.reg()); + return Result(element.reg(), info); } else { ASSERT(element.is_constant()); return Result(element.handle()); @@ -1090,30 +1142,49 @@ void VirtualFrame::EmitPop(Operand operand) { } -void VirtualFrame::EmitPush(Register reg) { +void VirtualFrame::EmitPush(Register reg, NumberInfo::Type info) { ASSERT(stack_pointer_ == element_count() - 1); - elements_.Add(FrameElement::MemoryElement()); + elements_.Add(FrameElement::MemoryElement(info)); stack_pointer_++; __ push(reg); } -void VirtualFrame::EmitPush(Operand operand) { +void VirtualFrame::EmitPush(Operand operand, NumberInfo::Type info) { ASSERT(stack_pointer_ == element_count() - 1); - elements_.Add(FrameElement::MemoryElement()); + elements_.Add(FrameElement::MemoryElement(info)); stack_pointer_++; __ push(operand); } -void VirtualFrame::EmitPush(Immediate immediate) { +void VirtualFrame::EmitPush(Immediate immediate, NumberInfo::Type info) { ASSERT(stack_pointer_ == element_count() - 1); - elements_.Add(FrameElement::MemoryElement()); + elements_.Add(FrameElement::MemoryElement(info)); stack_pointer_++; __ push(immediate); } +void VirtualFrame::Push(Expression* expr) { + ASSERT(expr->IsTrivial()); + + Literal* lit = expr->AsLiteral(); + if (lit != NULL) { + Push(lit->handle()); + return; + } + + VariableProxy* proxy = expr->AsVariableProxy(); + if (proxy != NULL && proxy->is_this()) { + PushParameterAt(-1); + return; + } + + UNREACHABLE(); +} + + #undef __ } } // namespace v8::internal diff --git a/src/ia32/virtual-frame-ia32.h b/src/ia32/virtual-frame-ia32.h index d6d55d12..7be593cc 100644 --- a/src/ia32/virtual-frame-ia32.h +++ b/src/ia32/virtual-frame-ia32.h @@ -28,6 +28,7 @@ #ifndef V8_IA32_VIRTUAL_FRAME_IA32_H_ #define V8_IA32_VIRTUAL_FRAME_IA32_H_ +#include "number-info.h" #include "register-allocator.h" #include "scopes.h" @@ -82,7 +83,8 @@ class VirtualFrame: public ZoneObject { MacroAssembler* masm() { return cgen()->masm(); } // Create a duplicate of an existing valid frame element. - FrameElement CopyElementAt(int index); + FrameElement CopyElementAt(int index, + NumberInfo::Type info = NumberInfo::kUninitialized); // The number of elements on the virtual frame. int element_count() { return elements_.length(); } @@ -324,21 +326,25 @@ class VirtualFrame: public ZoneObject { Result CallRuntime(Runtime::Function* f, int arg_count); Result CallRuntime(Runtime::FunctionId id, int arg_count); +#ifdef ENABLE_DEBUGGER_SUPPORT + void DebugBreak(); +#endif + // Invoke builtin given the number of arguments it expects on (and // removes from) the stack. Result InvokeBuiltin(Builtins::JavaScript id, InvokeFlag flag, int arg_count); // Call load IC. Name and receiver are found on top of the frame. - // Receiver is not dropped. + // Both are dropped. Result CallLoadIC(RelocInfo::Mode mode); // Call keyed load IC. Key and receiver are found on top of the - // frame. They are not dropped. + // frame. Both are dropped. Result CallKeyedLoadIC(RelocInfo::Mode mode); - // Call store IC. Name, value, and receiver are found on top of the - // frame. Receiver is not dropped. - Result CallStoreIC(); + // Call store IC. If the load is contextual, value is found on top of the + // frame. If not, value and receiver are on the frame. Both are dropped. + Result CallStoreIC(Handle<String> name, bool is_contextual); // Call keyed store IC. Value, key, and receiver are found on top // of the frame. Key and receiver are not dropped. @@ -381,12 +387,15 @@ class VirtualFrame: public ZoneObject { // Push an element on top of the expression stack and emit a // corresponding push instruction. - void EmitPush(Register reg); - void EmitPush(Operand operand); - void EmitPush(Immediate immediate); + void EmitPush(Register reg, + NumberInfo::Type info = NumberInfo::kUnknown); + void EmitPush(Operand operand, + NumberInfo::Type info = NumberInfo::kUnknown); + void EmitPush(Immediate immediate, + NumberInfo::Type info = NumberInfo::kUnknown); // Push an element on the virtual frame. - void Push(Register reg); + void Push(Register reg, NumberInfo::Type info = NumberInfo::kUnknown); void Push(Handle<Object> value); void Push(Smi* value) { Push(Handle<Object> (value)); @@ -398,7 +407,7 @@ class VirtualFrame: public ZoneObject { // This assert will trigger if you try to push the same value twice. ASSERT(result->is_valid()); if (result->is_register()) { - Push(result->reg()); + Push(result->reg(), result->number_info()); } else { ASSERT(result->is_constant()); Push(result->handle()); @@ -406,6 +415,10 @@ class VirtualFrame: public ZoneObject { result->Unuse(); } + // Pushing an expression expects that the expression is trivial (according + // to Expression::IsTrivial). + void Push(Expression* expr); + // Nip removes zero or more elements from immediately below the top // of the frame, leaving the previous top-of-frame value on top of // the frame. Nip(k) is equivalent to x = Pop(), Drop(k), Push(x). @@ -330,10 +330,11 @@ static void LookupForRead(Object* object, while (true) { object->Lookup(name, lookup); // Besides normal conditions (property not found or it's not - // an interceptor), bail out of lookup is not cacheable: we won't + // an interceptor), bail out if lookup is not cacheable: we won't // be able to IC it anyway and regular lookup should work fine. - if (lookup->IsNotFound() || lookup->type() != INTERCEPTOR || - !lookup->IsCacheable()) { + if (!lookup->IsFound() + || (lookup->type() != INTERCEPTOR) + || !lookup->IsCacheable()) { return; } @@ -343,7 +344,7 @@ static void LookupForRead(Object* object, } holder->LocalLookupRealNamedProperty(name, lookup); - if (lookup->IsValid()) { + if (lookup->IsProperty()) { ASSERT(lookup->type() != INTERCEPTOR); return; } @@ -422,7 +423,7 @@ Object* CallIC::LoadFunction(State state, LookupResult lookup; LookupForRead(*object, *name, &lookup); - if (!lookup.IsValid()) { + if (!lookup.IsProperty()) { // If the object does not have the requested property, check which // exception we need to throw. if (IsContextual(object)) { @@ -455,7 +456,7 @@ Object* CallIC::LoadFunction(State state, if (result->IsJSFunction()) { // Check if there is an optimized (builtin) version of the function. - // Ignored this will degrade performance for Array.prototype.{push,pop}. + // Ignored this will degrade performance for some Array functions. // Please note we only return the optimized function iff // the JSObject has FastElements. if (object->IsJSObject() && JSObject::cast(*object)->HasFastElements()) { @@ -492,7 +493,7 @@ void CallIC::UpdateCaches(LookupResult* lookup, Handle<Object> object, Handle<String> name) { // Bail out if we didn't find a result. - if (!lookup->IsValid() || !lookup->IsCacheable()) return; + if (!lookup->IsProperty() || !lookup->IsCacheable()) return; // Compute the number of arguments. int argc = target()->arguments_count(); @@ -641,8 +642,8 @@ Object* LoadIC::Load(State state, Handle<Object> object, Handle<String> name) { LookupResult lookup; LookupForRead(*object, *name, &lookup); - // If lookup is invalid, check if we need to throw an exception. - if (!lookup.IsValid()) { + // If we did not find a property, check if we need to throw an exception. + if (!lookup.IsProperty()) { if (FLAG_strict || IsContextual(object)) { return ReferenceError("not_defined", name); } @@ -652,7 +653,7 @@ Object* LoadIC::Load(State state, Handle<Object> object, Handle<String> name) { bool can_be_inlined = FLAG_use_ic && state == PREMONOMORPHIC && - lookup.IsValid() && + lookup.IsProperty() && lookup.IsCacheable() && lookup.holder() == *object && lookup.type() == FIELD && @@ -679,7 +680,7 @@ Object* LoadIC::Load(State state, Handle<Object> object, Handle<String> name) { } PropertyAttributes attr; - if (lookup.IsValid() && lookup.type() == INTERCEPTOR) { + if (lookup.IsProperty() && lookup.type() == INTERCEPTOR) { // Get the property. Object* result = object->GetProperty(*object, &lookup, *name, &attr); if (result->IsFailure()) return result; @@ -701,7 +702,7 @@ void LoadIC::UpdateCaches(LookupResult* lookup, Handle<Object> object, Handle<String> name) { // Bail out if we didn't find a result. - if (!lookup->IsValid() || !lookup->IsCacheable()) return; + if (!lookup->IsProperty() || !lookup->IsCacheable()) return; // Loading properties from values is not common, so don't try to // deal with non-JS objects here. @@ -854,8 +855,8 @@ Object* KeyedLoadIC::Load(State state, LookupResult lookup; LookupForRead(*object, *name, &lookup); - // If lookup is invalid, check if we need to throw an exception. - if (!lookup.IsValid()) { + // If we did not find a property, check if we need to throw an exception. + if (!lookup.IsProperty()) { if (FLAG_strict || IsContextual(object)) { return ReferenceError("not_defined", name); } @@ -866,7 +867,7 @@ Object* KeyedLoadIC::Load(State state, } PropertyAttributes attr; - if (lookup.IsValid() && lookup.type() == INTERCEPTOR) { + if (lookup.IsProperty() && lookup.type() == INTERCEPTOR) { // Get the property. Object* result = object->GetProperty(*object, &lookup, *name, &attr); if (result->IsFailure()) return result; @@ -893,6 +894,8 @@ Object* KeyedLoadIC::Load(State state, Handle<JSObject> receiver = Handle<JSObject>::cast(object); if (receiver->HasExternalArrayElements()) { stub = external_array_stub(receiver->GetElementsKind()); + } else if (receiver->HasIndexedInterceptor()) { + stub = indexed_interceptor_stub(); } } set_target(stub); @@ -915,7 +918,7 @@ Object* KeyedLoadIC::Load(State state, void KeyedLoadIC::UpdateCaches(LookupResult* lookup, State state, Handle<Object> object, Handle<String> name) { // Bail out if we didn't find a result. - if (!lookup->IsValid() || !lookup->IsCacheable()) return; + if (!lookup->IsProperty() || !lookup->IsCacheable()) return; if (!object->IsJSObject()) return; Handle<JSObject> receiver = Handle<JSObject>::cast(object); @@ -988,7 +991,7 @@ void KeyedLoadIC::UpdateCaches(LookupResult* lookup, State state, static bool StoreICableLookup(LookupResult* lookup) { // Bail out if we didn't find a result. - if (!lookup->IsValid() || !lookup->IsCacheable()) return false; + if (!lookup->IsPropertyOrTransition() || !lookup->IsCacheable()) return false; // If the property is read-only, we leave the IC in its current // state. @@ -1203,7 +1206,7 @@ void KeyedStoreIC::UpdateCaches(LookupResult* lookup, if (receiver->IsJSGlobalProxy()) return; // Bail out if we didn't find a result. - if (!lookup->IsValid() || !lookup->IsCacheable()) return; + if (!lookup->IsPropertyOrTransition() || !lookup->IsCacheable()) return; // If the property is read-only, we leave the IC in its current // state. @@ -1311,16 +1314,6 @@ Object* LoadIC_Miss(Arguments args) { } -void LoadIC::GenerateInitialize(MacroAssembler* masm) { - Generate(masm, ExternalReference(IC_Utility(kLoadIC_Miss))); -} - - -void LoadIC::GeneratePreMonomorphic(MacroAssembler* masm) { - Generate(masm, ExternalReference(IC_Utility(kLoadIC_Miss))); -} - - // Used from ic_<arch>.cc Object* KeyedLoadIC_Miss(Arguments args) { NoHandleAllocation na; @@ -1331,16 +1324,6 @@ Object* KeyedLoadIC_Miss(Arguments args) { } -void KeyedLoadIC::GenerateInitialize(MacroAssembler* masm) { - Generate(masm, ExternalReference(IC_Utility(kKeyedLoadIC_Miss))); -} - - -void KeyedLoadIC::GeneratePreMonomorphic(MacroAssembler* masm) { - Generate(masm, ExternalReference(IC_Utility(kKeyedLoadIC_Miss))); -} - - // Used from ic_<arch>.cc. Object* StoreIC_Miss(Arguments args) { NoHandleAllocation na; @@ -1397,16 +1380,6 @@ Object* KeyedStoreIC_Miss(Arguments args) { } -void KeyedStoreIC::GenerateInitialize(MacroAssembler* masm) { - Generate(masm, ExternalReference(IC_Utility(kKeyedStoreIC_Miss))); -} - - -void KeyedStoreIC::GenerateMiss(MacroAssembler* masm) { - Generate(masm, ExternalReference(IC_Utility(kKeyedStoreIC_Miss))); -} - - static Address IC_utilities[] = { #define ADDR(name) FUNCTION_ADDR(name), IC_UTIL_LIST(ADDR) @@ -53,6 +53,7 @@ enum DictionaryCheck { CHECK_DICTIONARY, DICTIONARY_CHECK_DONE }; ICU(LoadPropertyWithInterceptorOnly) \ ICU(LoadPropertyWithInterceptorForLoad) \ ICU(LoadPropertyWithInterceptorForCall) \ + ICU(KeyedLoadPropertyWithInterceptor) \ ICU(StoreInterceptorProperty) // @@ -223,8 +224,10 @@ class LoadIC: public IC { Object* Load(State state, Handle<Object> object, Handle<String> name); // Code generator routines. - static void GenerateInitialize(MacroAssembler* masm); - static void GeneratePreMonomorphic(MacroAssembler* masm); + static void GenerateInitialize(MacroAssembler* masm) { GenerateMiss(masm); } + static void GeneratePreMonomorphic(MacroAssembler* masm) { + GenerateMiss(masm); + } static void GenerateMiss(MacroAssembler* masm); static void GenerateMegamorphic(MacroAssembler* masm); static void GenerateNormal(MacroAssembler* masm); @@ -240,8 +243,6 @@ class LoadIC: public IC { static const int kOffsetToLoadInstruction; private: - static void Generate(MacroAssembler* masm, const ExternalReference& f); - // Update the inline cache and the global stub cache based on the // lookup result. void UpdateCaches(LookupResult* lookup, @@ -279,8 +280,11 @@ class KeyedLoadIC: public IC { // Code generator routines. static void GenerateMiss(MacroAssembler* masm); - static void GenerateInitialize(MacroAssembler* masm); - static void GeneratePreMonomorphic(MacroAssembler* masm); + static void GenerateRuntimeGetProperty(MacroAssembler* masm); + static void GenerateInitialize(MacroAssembler* masm) { GenerateMiss(masm); } + static void GeneratePreMonomorphic(MacroAssembler* masm) { + GenerateMiss(masm); + } static void GenerateGeneric(MacroAssembler* masm); static void GenerateString(MacroAssembler* masm); @@ -290,6 +294,7 @@ class KeyedLoadIC: public IC { // for all other types. static void GenerateExternalArray(MacroAssembler* masm, ExternalArrayType array_type); + static void GenerateIndexedInterceptor(MacroAssembler* masm); // Clear the use of the inlined version. static void ClearInlinedVersion(Address address); @@ -302,8 +307,6 @@ class KeyedLoadIC: public IC { static const int kSlowCaseBitFieldMask = (1 << Map::kIsAccessCheckNeeded) | (1 << Map::kHasIndexedInterceptor); - static void Generate(MacroAssembler* masm, const ExternalReference& f); - // Update the inline cache. void UpdateCaches(LookupResult* lookup, State state, @@ -328,6 +331,10 @@ class KeyedLoadIC: public IC { } static Code* external_array_stub(JSObject::ElementsKind elements_kind); + static Code* indexed_interceptor_stub() { + return Builtins::builtin(Builtins::KeyedLoadIC_IndexedInterceptor); + } + static void Clear(Address address, Code* target); // Support for patching the map that is checked in an inlined @@ -351,7 +358,6 @@ class StoreIC: public IC { static void GenerateInitialize(MacroAssembler* masm) { GenerateMiss(masm); } static void GenerateMiss(MacroAssembler* masm); static void GenerateMegamorphic(MacroAssembler* masm); - static void GenerateExtendStorage(MacroAssembler* masm); private: // Update the inline cache and the global stub cache based on the @@ -384,10 +390,10 @@ class KeyedStoreIC: public IC { Handle<Object> value); // Code generators for stub routines. Only called once at startup. - static void GenerateInitialize(MacroAssembler* masm); + static void GenerateInitialize(MacroAssembler* masm) { GenerateMiss(masm); } static void GenerateMiss(MacroAssembler* masm); + static void GenerateRuntimeSetProperty(MacroAssembler* masm); static void GenerateGeneric(MacroAssembler* masm); - static void GenerateExtendStorage(MacroAssembler* masm); // Generators for external array types. See objects.h. // These are similar to the generic IC; they optimize the case of @@ -403,8 +409,6 @@ class KeyedStoreIC: public IC { static void RestoreInlinedVersion(Address address); private: - static void Generate(MacroAssembler* masm, const ExternalReference& f); - // Update the inline cache. void UpdateCaches(LookupResult* lookup, State state, diff --git a/src/json-delay.js b/src/json-delay.js deleted file mode 100644 index 7788f516..00000000 --- a/src/json-delay.js +++ /dev/null @@ -1,254 +0,0 @@ -// Copyright 2009 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. - -var $JSON = global.JSON; - -function ParseJSONUnfiltered(text) { - var s = $String(text); - var f = %CompileString(text, true); - return f(); -} - -function Revive(holder, name, reviver) { - var val = holder[name]; - if (IS_OBJECT(val)) { - if (IS_ARRAY(val)) { - var length = val.length; - for (var i = 0; i < length; i++) { - var newElement = Revive(val, $String(i), reviver); - val[i] = newElement; - } - } else { - for (var p in val) { - if (ObjectHasOwnProperty.call(val, p)) { - var newElement = Revive(val, p, reviver); - if (IS_UNDEFINED(newElement)) { - delete val[p]; - } else { - val[p] = newElement; - } - } - } - } - } - return reviver.call(holder, name, val); -} - -function JSONParse(text, reviver) { - var unfiltered = ParseJSONUnfiltered(text); - if (IS_FUNCTION(reviver)) { - return Revive({'': unfiltered}, '', reviver); - } else { - return unfiltered; - } -} - -var characterQuoteCache = { - '\"': '\\"', - '\\': '\\\\', - '/': '\\/', - '\b': '\\b', - '\f': '\\f', - '\n': '\\n', - '\r': '\\r', - '\t': '\\t', - '\x0B': '\\u000b' -}; - -function QuoteSingleJSONCharacter(c) { - if (c in characterQuoteCache) - return characterQuoteCache[c]; - var charCode = c.charCodeAt(0); - var result; - if (charCode < 16) result = '\\u000'; - else if (charCode < 256) result = '\\u00'; - else if (charCode < 4096) result = '\\u0'; - else result = '\\u'; - result += charCode.toString(16); - characterQuoteCache[c] = result; - return result; -} - -function QuoteJSONString(str) { - var quotable = /[\\\"\x00-\x1f\x80-\uffff]/g; - return '"' + str.replace(quotable, QuoteSingleJSONCharacter) + '"'; -} - -function StackContains(stack, val) { - var length = stack.length; - for (var i = 0; i < length; i++) { - if (stack[i] === val) - return true; - } - return false; -} - -function SerializeArray(value, replacer, stack, indent, gap) { - if (StackContains(stack, value)) - throw MakeTypeError('circular_structure', []); - stack.push(value); - var stepback = indent; - indent += gap; - var partial = []; - var len = value.length; - for (var i = 0; i < len; i++) { - var strP = JSONSerialize($String(i), value, replacer, stack, - indent, gap); - if (IS_UNDEFINED(strP)) - strP = "null"; - partial.push(strP); - } - var final; - if (gap == "") { - final = "[" + partial.join(",") + "]"; - } else if (partial.length > 0) { - var separator = ",\n" + indent; - final = "[\n" + indent + partial.join(separator) + "\n" + - stepback + "]"; - } else { - final = "[]"; - } - stack.pop(); - return final; -} - -function SerializeObject(value, replacer, stack, indent, gap) { - if (StackContains(stack, value)) - throw MakeTypeError('circular_structure', []); - stack.push(value); - var stepback = indent; - indent += gap; - var partial = []; - if (IS_ARRAY(replacer)) { - var length = replacer.length; - for (var i = 0; i < length; i++) { - if (ObjectHasOwnProperty.call(replacer, i)) { - var p = replacer[i]; - var strP = JSONSerialize(p, value, replacer, stack, indent, gap); - if (!IS_UNDEFINED(strP)) { - var member = QuoteJSONString(p) + ":"; - if (gap != "") member += " "; - member += strP; - partial.push(member); - } - } - } - } else { - for (var p in value) { - if (ObjectHasOwnProperty.call(value, p)) { - var strP = JSONSerialize(p, value, replacer, stack, indent, gap); - if (!IS_UNDEFINED(strP)) { - var member = QuoteJSONString(p) + ":"; - if (gap != "") member += " "; - member += strP; - partial.push(member); - } - } - } - } - var final; - if (gap == "") { - final = "{" + partial.join(",") + "}"; - } else if (partial.length > 0) { - var separator = ",\n" + indent; - final = "{\n" + indent + partial.join(separator) + "\n" + - stepback + "}"; - } else { - final = "{}"; - } - stack.pop(); - return final; -} - -function JSONSerialize(key, holder, replacer, stack, indent, gap) { - var value = holder[key]; - if (IS_OBJECT(value) && value) { - var toJSON = value.toJSON; - if (IS_FUNCTION(toJSON)) - value = toJSON.call(value, key); - } - if (IS_FUNCTION(replacer)) - value = replacer.call(holder, key, value); - // Unwrap value if necessary - if (IS_OBJECT(value)) { - if (IS_NUMBER_WRAPPER(value)) { - value = $Number(value); - } else if (IS_STRING_WRAPPER(value)) { - value = $String(value); - } - } - switch (typeof value) { - case "string": - return QuoteJSONString(value); - case "object": - if (!value) { - return "null"; - } else if (IS_ARRAY(value)) { - return SerializeArray(value, replacer, stack, indent, gap); - } else { - return SerializeObject(value, replacer, stack, indent, gap); - } - case "number": - return $isFinite(value) ? $String(value) : "null"; - case "boolean": - return value ? "true" : "false"; - } -} - -function JSONStringify(value, replacer, space) { - var stack = []; - var indent = ""; - if (IS_OBJECT(space)) { - // Unwrap 'space' if it is wrapped - if (IS_NUMBER_WRAPPER(space)) { - space = $Number(space); - } else if (IS_STRING_WRAPPER(space)) { - space = $String(space); - } - } - var gap; - if (IS_NUMBER(space)) { - space = $Math.min(space, 100); - gap = ""; - for (var i = 0; i < space; i++) - gap += " "; - } else if (IS_STRING(space)) { - gap = space; - } else { - gap = ""; - } - return JSONSerialize('', {'': value}, replacer, stack, indent, gap); -} - -function SetupJSON() { - InstallFunctions($JSON, DONT_ENUM, $Array( - "parse", JSONParse, - "stringify", JSONStringify - )); -} - -SetupJSON(); diff --git a/src/jump-target-inl.h b/src/jump-target-inl.h index 1f0676df..3cd9a8bd 100644 --- a/src/jump-target-inl.h +++ b/src/jump-target-inl.h @@ -42,6 +42,9 @@ void JumpTarget::InitializeEntryElement(int index, FrameElement* target) { } else if (target->is_copy()) { entry_frame_->elements_[target->index()].set_copied(); } + if (direction_ == BIDIRECTIONAL && !target->is_copy()) { + entry_frame_->elements_[index].set_number_info(NumberInfo::kUnknown); + } } } } // namespace v8::internal diff --git a/src/jump-target.cc b/src/jump-target.cc index 3782f92a..bce379a9 100644 --- a/src/jump-target.cc +++ b/src/jump-target.cc @@ -101,6 +101,16 @@ void JumpTarget::ComputeEntryFrame() { if (element == NULL || !element->is_valid()) break; element = element->Combine(&reaching_frames_[j]->elements_[i]); + + FrameElement* other = &reaching_frames_[j]->elements_[i]; + if (element != NULL && !element->is_copy()) { + ASSERT(other != NULL); + // We overwrite the number information of one of the incoming frames. + // This is safe because we only use the frame for emitting merge code. + // The number information of incoming frames is not used anymore. + element->set_number_info(NumberInfo::Combine(element->number_info(), + other->number_info())); + } } elements[i] = element; } @@ -125,7 +135,8 @@ void JumpTarget::ComputeEntryFrame() { for (; index < length; index++) { FrameElement* target = elements[index]; if (target == NULL) { - entry_frame_->elements_.Add(FrameElement::MemoryElement()); + entry_frame_->elements_.Add( + FrameElement::MemoryElement(NumberInfo::kUninitialized)); } else { entry_frame_->elements_.Add(*target); InitializeEntryElement(index, target); @@ -142,9 +153,20 @@ void JumpTarget::ComputeEntryFrame() { RegisterFile candidate_registers; int best_count = kMinInt; int best_reg_num = RegisterAllocator::kInvalidRegister; + NumberInfo::Type info = NumberInfo::kUninitialized; for (int j = 0; j < reaching_frames_.length(); j++) { FrameElement element = reaching_frames_[j]->elements_[i]; + if (direction_ == BIDIRECTIONAL) { + info = NumberInfo::kUnknown; + } else if (!element.is_copy()) { + info = NumberInfo::Combine(info, element.number_info()); + } else { + // New elements will not be copies, so get number information from + // backing element in the reaching frame. + info = NumberInfo::Combine(info, + reaching_frames_[j]->elements_[element.index()].number_info()); + } is_synced = is_synced && element.is_synced(); if (element.is_register() && !entry_frame_->is_used(element.reg())) { // Count the register occurrence and remember it if better @@ -158,11 +180,17 @@ void JumpTarget::ComputeEntryFrame() { } } + // We must have a number type information now (not for copied elements). + ASSERT(entry_frame_->elements_[i].is_copy() + || info != NumberInfo::kUninitialized); + // If the value is synced on all frames, put it in memory. This // costs nothing at the merge code but will incur a // memory-to-register move when the value is needed later. if (is_synced) { // Already recorded as a memory element. + // Set combined number info. + entry_frame_->elements_[i].set_number_info(info); continue; } @@ -183,13 +211,27 @@ void JumpTarget::ComputeEntryFrame() { bool is_copied = entry_frame_->elements_[i].is_copied(); Register reg = RegisterAllocator::ToRegister(best_reg_num); entry_frame_->elements_[i] = - FrameElement::RegisterElement(reg, - FrameElement::NOT_SYNCED); + FrameElement::RegisterElement(reg, FrameElement::NOT_SYNCED, + NumberInfo::kUninitialized); if (is_copied) entry_frame_->elements_[i].set_copied(); entry_frame_->set_register_location(reg, i); } + // Set combined number info. + entry_frame_->elements_[i].set_number_info(info); + } + } + + // If we have incoming backward edges assert we forget all number information. +#ifdef DEBUG + if (direction_ == BIDIRECTIONAL) { + for (int i = 0; i < length; ++i) { + if (!entry_frame_->elements_[i].is_copy()) { + ASSERT(entry_frame_->elements_[i].number_info() == + NumberInfo::kUnknown); + } } } +#endif // The stack pointer is at the highest synced element or the base of // the expression stack. diff --git a/src/liveedit.cc b/src/liveedit.cc new file mode 100644 index 00000000..c50e007f --- /dev/null +++ b/src/liveedit.cc @@ -0,0 +1,87 @@ +// Copyright 2010 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 "v8.h" + +#include "liveedit.h" +#include "compiler.h" +#include "oprofile-agent.h" +#include "scopes.h" +#include "global-handles.h" +#include "debug.h" + +namespace v8 { +namespace internal { + + +class FunctionInfoListener { + public: + void FunctionStarted(FunctionLiteral* fun) { + // Implementation follows. + } + + void FunctionDone() { + // Implementation follows. + } + + void FunctionScope(Scope* scope) { + // Implementation follows. + } + + void FunctionCode(Handle<Code> function_code) { + // Implementation follows. + } +}; + +static FunctionInfoListener* active_function_info_listener = NULL; + +LiveEditFunctionTracker::LiveEditFunctionTracker(FunctionLiteral* fun) { + if (active_function_info_listener != NULL) { + active_function_info_listener->FunctionStarted(fun); + } +} +LiveEditFunctionTracker::~LiveEditFunctionTracker() { + if (active_function_info_listener != NULL) { + active_function_info_listener->FunctionDone(); + } +} +void LiveEditFunctionTracker::RecordFunctionCode(Handle<Code> code) { + if (active_function_info_listener != NULL) { + active_function_info_listener->FunctionCode(code); + } +} +void LiveEditFunctionTracker::RecordFunctionScope(Scope* scope) { + if (active_function_info_listener != NULL) { + active_function_info_listener->FunctionScope(scope); + } +} +bool LiveEditFunctionTracker::IsActive() { + return active_function_info_listener != NULL; +} + +} } // namespace v8::internal diff --git a/src/liveedit.h b/src/liveedit.h new file mode 100644 index 00000000..73aa7d3d --- /dev/null +++ b/src/liveedit.h @@ -0,0 +1,78 @@ +// Copyright 2010 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. + +#ifndef V8_LIVEEDIT_H_ +#define V8_LIVEEDIT_H_ + + + +// Live Edit feature implementation. +// User should be able to change script on already running VM. This feature +// matches hot swap features in other frameworks. +// +// The basic use-case is when user spots some mistake in function body +// from debugger and wishes to change the algorithm without restart. +// +// A single change always has a form of a simple replacement (in pseudo-code): +// script.source[positions, positions+length] = new_string; +// Implementation first determines, which function's body includes this +// change area. Then both old and new versions of script are fully compiled +// in order to analyze, whether the function changed its outer scope +// expectations (or number of parameters). If it didn't, function's code is +// patched with a newly compiled code. If it did change, enclosing function +// gets patched. All inner functions are left untouched, whatever happened +// to them in a new script version. However, new version of code will +// instantiate newly compiled functions. + + +#include "compiler.h" + +namespace v8 { +namespace internal { + +// This class collects some specific information on structure of functions +// in a particular script. It gets called from compiler all the time, but +// actually records any data only when liveedit operation is in process; +// in any other time this class is very cheap. +// +// The primary interest of the Tracker is to record function scope structures +// in order to analyze whether function code maybe safely patched (with new +// code successfully reading existing data from function scopes). The Tracker +// also collects compiled function codes. +class LiveEditFunctionTracker { + public: + explicit LiveEditFunctionTracker(FunctionLiteral* fun); + ~LiveEditFunctionTracker(); + void RecordFunctionCode(Handle<Code> code); + void RecordFunctionScope(Scope* scope); + + static bool IsActive(); +}; + +} } // namespace v8::internal + +#endif /* V*_LIVEEDIT_H_ */ diff --git a/src/log-utils.cc b/src/log-utils.cc index fd956041..722e0fc0 100644 --- a/src/log-utils.cc +++ b/src/log-utils.cc @@ -351,15 +351,6 @@ void LogMessageBuilder::WriteToLogFile() { } -void LogMessageBuilder::WriteCStringToLogFile(const char* str) { - const int len = StrLength(str); - const int written = Log::Write(str, len); - if (written != len && write_failure_handler != NULL) { - write_failure_handler(); - } -} - - // Formatting string for back references to the whole line. E.g. "#2" means // "the second line above". const char* LogRecordCompressor::kLineBackwardReferenceFormat = "#%d"; diff --git a/src/log-utils.h b/src/log-utils.h index 3e25b0e7..b769e904 100644 --- a/src/log-utils.h +++ b/src/log-utils.h @@ -268,9 +268,6 @@ class LogMessageBuilder BASE_EMBEDDED { // Write the log message to the log file currently opened. void WriteToLogFile(); - // Write a null-terminated string to to the log file currently opened. - void WriteCStringToLogFile(const char* str); - // A handler that is called when Log::Write fails. typedef void (*WriteFailureHandler)(); @@ -330,6 +330,8 @@ SlidingStateWindow* Logger::sliding_state_window_ = NULL; const char** Logger::log_events_ = NULL; CompressionHelper* Logger::compression_helper_ = NULL; bool Logger::is_logging_ = false; +int Logger::cpu_profiler_nesting_ = 0; +int Logger::heap_profiler_nesting_ = 0; #define DECLARE_LONG_EVENT(ignore1, long_name, ignore2) long_name, const char* kLongLogEventsNames[Logger::NUMBER_OF_LOG_EVENTS] = { @@ -368,15 +370,6 @@ void Logger::LogAliases() { #endif // ENABLE_LOGGING_AND_PROFILING -void Logger::Preamble(const char* content) { -#ifdef ENABLE_LOGGING_AND_PROFILING - if (!Log::IsEnabled() || !FLAG_log_code) return; - LogMessageBuilder msg; - msg.WriteCStringToLogFile(content); -#endif -} - - void Logger::StringEvent(const char* name, const char* value) { #ifdef ENABLE_LOGGING_AND_PROFILING if (FLAG_log) UncheckedStringEvent(name, value); @@ -573,20 +566,6 @@ void Logger::LogRuntime(Vector<const char> format, JSArray* args) { } -void Logger::LogProfileMarker(Vector<const char> marker) { -#ifdef ENABLE_LOGGING_AND_PROFILING - if (!Log::IsEnabled() || !FLAG_prof) return; - LogMessageBuilder msg; - for (int i = 0; i < marker.length(); i++) { - char c = marker[i]; - msg.Append(c); - } - msg.Append('\n'); - msg.WriteToLogFile(); -#endif -} - - void Logger::ApiIndexedSecurityCheck(uint32_t index) { #ifdef ENABLE_LOGGING_AND_PROFILING if (!Log::IsEnabled() || !FLAG_log_api) return; @@ -1178,53 +1157,61 @@ int Logger::GetActiveProfilerModules() { } -void Logger::PauseProfiler(int flags) { +void Logger::PauseProfiler(int flags, int tag) { if (!Log::IsEnabled()) return; - const int active_modules = GetActiveProfilerModules(); - const int modules_to_disable = active_modules & flags; - if (modules_to_disable == PROFILER_MODULE_NONE) return; - - if (modules_to_disable & PROFILER_MODULE_CPU) { - profiler_->pause(); - if (FLAG_prof_lazy) { - if (!FLAG_sliding_state_window) ticker_->Stop(); - FLAG_log_code = false; - // Must be the same message as Log::kDynamicBufferSeal. - LOG(UncheckedStringEvent("profiler", "pause")); + if (flags & PROFILER_MODULE_CPU) { + // It is OK to have negative nesting. + if (--cpu_profiler_nesting_ == 0) { + profiler_->pause(); + if (FLAG_prof_lazy) { + if (!FLAG_sliding_state_window) ticker_->Stop(); + FLAG_log_code = false; + // Must be the same message as Log::kDynamicBufferSeal. + LOG(UncheckedStringEvent("profiler", "pause")); + } } } - if (modules_to_disable & + if (flags & (PROFILER_MODULE_HEAP_STATS | PROFILER_MODULE_JS_CONSTRUCTORS)) { - FLAG_log_gc = false; + if (--heap_profiler_nesting_ == 0) { + FLAG_log_gc = false; + } + } + if (tag != 0) { + IntEvent("close-tag", tag); } - // Turn off logging if no active modules remain. - if ((active_modules & ~flags) == PROFILER_MODULE_NONE) { + if (GetActiveProfilerModules() == PROFILER_MODULE_NONE) { is_logging_ = false; } } -void Logger::ResumeProfiler(int flags) { +void Logger::ResumeProfiler(int flags, int tag) { if (!Log::IsEnabled()) return; - const int modules_to_enable = ~GetActiveProfilerModules() & flags; - if (modules_to_enable != PROFILER_MODULE_NONE) { - is_logging_ = true; + if (tag != 0) { + IntEvent("open-tag", tag); } - if (modules_to_enable & PROFILER_MODULE_CPU) { - if (FLAG_prof_lazy) { - profiler_->Engage(); - LOG(UncheckedStringEvent("profiler", "resume")); - FLAG_log_code = true; - LogCompiledFunctions(); - LogFunctionObjects(); - LogAccessorCallbacks(); - if (!FLAG_sliding_state_window) ticker_->Start(); + if (flags & PROFILER_MODULE_CPU) { + if (cpu_profiler_nesting_++ == 0) { + is_logging_ = true; + if (FLAG_prof_lazy) { + profiler_->Engage(); + LOG(UncheckedStringEvent("profiler", "resume")); + FLAG_log_code = true; + LogCompiledFunctions(); + LogFunctionObjects(); + LogAccessorCallbacks(); + if (!FLAG_sliding_state_window) ticker_->Start(); + } + profiler_->resume(); } - profiler_->resume(); } - if (modules_to_enable & + if (flags & (PROFILER_MODULE_HEAP_STATS | PROFILER_MODULE_JS_CONSTRUCTORS)) { - FLAG_log_gc = true; + if (heap_profiler_nesting_++ == 0) { + is_logging_ = true; + FLAG_log_gc = true; + } } } @@ -1233,7 +1220,7 @@ void Logger::ResumeProfiler(int flags) { // either from main or Profiler's thread. void Logger::StopLoggingAndProfiling() { Log::stop(); - PauseProfiler(PROFILER_MODULE_CPU); + PauseProfiler(PROFILER_MODULE_CPU, 0); } @@ -161,12 +161,6 @@ class Logger { // Enable the computation of a sliding window of states. static void EnableSlidingStateWindow(); - // Write a raw string to the log to be used as a preamble. - // No check is made that the 'preamble' is actually at the beginning - // of the log. The preample is used to write code events saved in the - // snapshot. - static void Preamble(const char* content); - // Emits an event with a string value -> (name, value). static void StringEvent(const char* name, const char* value); @@ -265,9 +259,6 @@ class Logger { // Log an event reported from generated code static void LogRuntime(Vector<const char> format, JSArray* args); - // Log a profiling marker. - static void LogProfileMarker(Vector<const char> marker); - #ifdef ENABLE_LOGGING_AND_PROFILING static StateTag state() { return current_state_ ? current_state_->state() : OTHER; @@ -280,8 +271,8 @@ class Logger { // Pause/Resume collection of profiling data. // When data collection is paused, CPU Tick events are discarded until // data collection is Resumed. - static void PauseProfiler(int flags); - static void ResumeProfiler(int flags); + static void PauseProfiler(int flags, int tag); + static void ResumeProfiler(int flags, int tag); static int GetActiveProfilerModules(); // If logging is performed into a memory buffer, allows to @@ -382,6 +373,8 @@ class Logger { friend class LoggerTestHelper; static bool is_logging_; + static int cpu_profiler_nesting_; + static int heap_profiler_nesting_; #else static bool is_logging() { return false; } #endif diff --git a/src/macro-assembler.h b/src/macro-assembler.h index e33148ce..81e5bf7a 100644 --- a/src/macro-assembler.h +++ b/src/macro-assembler.h @@ -61,6 +61,8 @@ enum AllocationFlags { RESULT_CONTAINS_TOP = 1 << 1 }; +// Invalid depth in prototype chain. +const int kInvalidProtoDepth = -1; #if V8_TARGET_ARCH_IA32 #include "assembler.h" diff --git a/src/macros.py b/src/macros.py index 537113ce..47519338 100644 --- a/src/macros.py +++ b/src/macros.py @@ -74,6 +74,10 @@ const kYearShift = 9; const kMonthShift = 5; # Type query macros. +# +# Note: We have special support for typeof(foo) === 'bar' in the compiler. +# It will *not* generate a runtime typeof call for the most important +# values of 'bar'. macro IS_NULL(arg) = (arg === null); macro IS_NULL_OR_UNDEFINED(arg) = (arg == null); macro IS_UNDEFINED(arg) = (typeof(arg) === 'undefined'); @@ -83,7 +87,7 @@ macro IS_BOOLEAN(arg) = (typeof(arg) === 'boolean'); macro IS_OBJECT(arg) = (%_IsObject(arg)); macro IS_ARRAY(arg) = (%_IsArray(arg)); macro IS_FUNCTION(arg) = (%_IsFunction(arg)); -macro IS_REGEXP(arg) = (%_ClassOf(arg) === 'RegExp'); +macro IS_REGEXP(arg) = (%_IsRegExp(arg)); macro IS_DATE(arg) = (%_ClassOf(arg) === 'Date'); macro IS_NUMBER_WRAPPER(arg) = (%_ClassOf(arg) === 'Number'); macro IS_STRING_WRAPPER(arg) = (%_ClassOf(arg) === 'String'); @@ -97,9 +101,11 @@ macro FLOOR(arg) = $floor(arg); # Inline macros. Use %IS_VAR to make sure arg is evaluated only once. macro NUMBER_IS_NAN(arg) = (!%_IsSmi(%IS_VAR(arg)) && !(arg == arg)); -macro TO_INTEGER(arg) = (%_IsSmi(%IS_VAR(arg)) ? arg : ToInteger(arg)); -macro TO_INT32(arg) = (%_IsSmi(%IS_VAR(arg)) ? arg : (arg >> 0)); -macro TO_UINT32(arg) = (arg >>> 0); +macro TO_INTEGER(arg) = (%_IsSmi(%IS_VAR(arg)) ? arg : ToInteger(arg)); +macro TO_INT32(arg) = (%_IsSmi(%IS_VAR(arg)) ? arg : (arg >> 0)); +macro TO_UINT32(arg) = (arg >>> 0); +macro TO_STRING_INLINE(arg) = (IS_STRING(%IS_VAR(arg)) ? arg : NonStringToString(arg)); + # Macros implemented in Python. python macro CHAR_CODE(str) = ord(str[1]); diff --git a/src/math.js b/src/math.js index d804648f..4c9de674 100644 --- a/src/math.js +++ b/src/math.js @@ -84,7 +84,7 @@ function MathCeil(x) { // ECMA 262 - 15.8.2.7 function MathCos(x) { if (!IS_NUMBER(x)) x = ToNumber(x); - return %Math_cos(x); + return %_Math_cos(x); } // ECMA 262 - 15.8.2.8 @@ -176,7 +176,7 @@ function MathRound(x) { // ECMA 262 - 15.8.2.16 function MathSin(x) { if (!IS_NUMBER(x)) x = ToNumber(x); - return %Math_sin(x); + return %_Math_sin(x); } // ECMA 262 - 15.8.2.17 @@ -233,7 +233,7 @@ function SetupMath() { "SQRT2", 1.4142135623730951, DONT_ENUM | DONT_DELETE | READ_ONLY); - %TransformToFastProperties($Math); + %ToFastProperties($Math); // Setup non-enumerable functions of the Math object and // set their names. diff --git a/src/mips/codegen-mips.cc b/src/mips/codegen-mips.cc index 5a27c286..2de45f67 100644 --- a/src/mips/codegen-mips.cc +++ b/src/mips/codegen-mips.cc @@ -305,6 +305,11 @@ void CodeGenerator::GenerateIsArray(ZoneList<Expression*>* args) { } +void CodeGenerator::GenerateIsRegExp(ZoneList<Expression*>* args) { + UNIMPLEMENTED_MIPS(); +} + + void CodeGenerator::GenerateIsConstructCall(ZoneList<Expression*>* args) { UNIMPLEMENTED_MIPS(); } @@ -365,6 +370,11 @@ void CodeGenerator::GenerateRegExpExec(ZoneList<Expression*>* args) { } +void CodeGenerator::GenerateNumberToString(ZoneList<Expression*>* args) { + UNIMPLEMENTED_MIPS(); +} + + void CodeGenerator::VisitCallRuntime(CallRuntime* node) { UNIMPLEMENTED_MIPS(); } @@ -498,4 +508,3 @@ int CompareStub::MinorKey() { #undef __ } } // namespace v8::internal - diff --git a/src/mips/codegen-mips.h b/src/mips/codegen-mips.h index 05138bc6..147b8724 100644 --- a/src/mips/codegen-mips.h +++ b/src/mips/codegen-mips.h @@ -210,6 +210,7 @@ class CodeGenerator: public AstVisitor { void GenerateIsSmi(ZoneList<Expression*>* args); void GenerateIsNonNegativeSmi(ZoneList<Expression*>* args); void GenerateIsArray(ZoneList<Expression*>* args); + void GenerateIsRegExp(ZoneList<Expression*>* args); // Support for construct call checks. void GenerateIsConstructCall(ZoneList<Expression*>* args); @@ -241,6 +242,8 @@ class CodeGenerator: public AstVisitor { void GenerateSubString(ZoneList<Expression*>* args); void GenerateStringCompare(ZoneList<Expression*>* args); void GenerateRegExpExec(ZoneList<Expression*>* args); + void GenerateNumberToString(ZoneList<Expression*>* args); + // Fast support for Math.sin and Math.cos. inline void GenerateMathSin(ZoneList<Expression*>* args); @@ -308,4 +311,3 @@ class CodeGenerator: public AstVisitor { } } // namespace v8::internal #endif // V8_MIPS_CODEGEN_MIPS_H_ - diff --git a/src/mirror-debugger.js b/src/mirror-debugger.js index 3b34797e..dfe297b1 100644 --- a/src/mirror-debugger.js +++ b/src/mirror-debugger.js @@ -546,14 +546,16 @@ StringMirror.prototype.length = function() { return this.value_.length; }; - -StringMirror.prototype.toText = function() { - if (this.length() > kMaxProtocolStringLength) { - return this.value_.substring(0, kMaxProtocolStringLength) + +StringMirror.prototype.getTruncatedValue = function(maxLength) { + if (maxLength != -1 && this.length() > maxLength) { + return this.value_.substring(0, maxLength) + '... (length: ' + this.length() + ')'; - } else { - return this.value_; } + return this.value_; +} + +StringMirror.prototype.toText = function() { + return this.getTruncatedValue(kMaxProtocolStringLength); } @@ -1726,7 +1728,8 @@ ScriptMirror.prototype.value = function() { ScriptMirror.prototype.name = function() { - return this.script_.name; + // If we have name, we trust it more than sourceURL from comments + return this.script_.name || this.sourceUrlFromComment_(); }; @@ -1822,6 +1825,29 @@ ScriptMirror.prototype.toText = function() { /** + * Returns a suggested script URL from comments in script code (if found), + * undefined otherwise. Used primarily by debuggers for identifying eval()'ed + * scripts. See + * http://fbug.googlecode.com/svn/branches/firebug1.1/docs/ReleaseNotes_1.1.txt + * for details. + * + * @return {?string} value for //@ sourceURL comment + */ +ScriptMirror.prototype.sourceUrlFromComment_ = function() { + if (!('sourceUrl_' in this) && this.source()) { + // TODO(608): the spaces in a regexp below had to be escaped as \040 + // because this file is being processed by js2c whose handling of spaces + // in regexps is broken. + // We're not using \s here to prevent \n from matching. + var sourceUrlPattern = /\/\/@[\040\t]sourceURL=[\040\t]*(\S+)[\040\t]*$/m; + var match = sourceUrlPattern.exec(this.source()); + this.sourceUrl_ = match ? match[1] : undefined; + } + return this.sourceUrl_; +}; + + +/** * Mirror object for context. * @param {Object} data The context data * @constructor @@ -1924,6 +1950,15 @@ JSONProtocolSerializer.prototype.inlineRefs_ = function() { } +JSONProtocolSerializer.prototype.maxStringLength_ = function() { + if (IS_UNDEFINED(this.options_) || + IS_UNDEFINED(this.options_.maxStringLength)) { + return kMaxProtocolStringLength; + } + return this.options_.maxStringLength; +} + + JSONProtocolSerializer.prototype.add_ = function(mirror) { // If this mirror is already in the list just return. for (var i = 0; i < this.mirrors_.length; i++) { @@ -1956,8 +1991,7 @@ JSONProtocolSerializer.prototype.serializeReferenceWithDisplayData_ = o.value = mirror.value(); break; case STRING_TYPE: - // Limit string length. - o.value = mirror.toText(); + o.value = mirror.getTruncatedValue(this.maxStringLength_()); break; case FUNCTION_TYPE: o.name = mirror.name(); @@ -2021,11 +2055,12 @@ JSONProtocolSerializer.prototype.serialize_ = function(mirror, reference, case STRING_TYPE: // String values might have their value cropped to keep down size. - if (mirror.length() > kMaxProtocolStringLength) { - var str = mirror.value().substring(0, kMaxProtocolStringLength); + if (this.maxStringLength_() != -1 && + mirror.length() > this.maxStringLength_()) { + var str = mirror.getTruncatedValue(this.maxStringLength_()); content.value = str; content.fromIndex = 0; - content.toIndex = kMaxProtocolStringLength; + content.toIndex = this.maxStringLength_(); } else { content.value = mirror.value(); } diff --git a/src/mirror-delay.js b/src/mirror-delay.js deleted file mode 100644 index 1487ce57..00000000 --- a/src/mirror-delay.js +++ /dev/null @@ -1,2334 +0,0 @@ -// 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. - -// Touch the RegExp and Date functions to make sure that date-delay.js and -// regexp-delay.js has been loaded. This is required as the mirrors use -// functions within these files through the builtins object. -RegExp; -Date; - - -// Handle id counters. -var next_handle_ = 0; -var next_transient_handle_ = -1; - -// Mirror cache. -var mirror_cache_ = []; - - -/** - * Clear the mirror handle cache. - */ -function ClearMirrorCache() { - next_handle_ = 0; - mirror_cache_ = []; -} - - -/** - * Returns the mirror for a specified value or object. - * - * @param {value or Object} value the value or object to retreive the mirror for - * @param {boolean} transient indicate whether this object is transient and - * should not be added to the mirror cache. The default is not transient. - * @returns {Mirror} the mirror reflects the passed value or object - */ -function MakeMirror(value, opt_transient) { - var mirror; - - // Look for non transient mirrors in the mirror cache. - if (!opt_transient) { - for (id in mirror_cache_) { - mirror = mirror_cache_[id]; - if (mirror.value() === value) { - return mirror; - } - // Special check for NaN as NaN == NaN is false. - if (mirror.isNumber() && isNaN(mirror.value()) && - typeof value == 'number' && isNaN(value)) { - return mirror; - } - } - } - - if (IS_UNDEFINED(value)) { - mirror = new UndefinedMirror(); - } else if (IS_NULL(value)) { - mirror = new NullMirror(); - } else if (IS_BOOLEAN(value)) { - mirror = new BooleanMirror(value); - } else if (IS_NUMBER(value)) { - mirror = new NumberMirror(value); - } else if (IS_STRING(value)) { - mirror = new StringMirror(value); - } else if (IS_ARRAY(value)) { - mirror = new ArrayMirror(value); - } else if (IS_DATE(value)) { - mirror = new DateMirror(value); - } else if (IS_FUNCTION(value)) { - mirror = new FunctionMirror(value); - } else if (IS_REGEXP(value)) { - mirror = new RegExpMirror(value); - } else if (IS_ERROR(value)) { - mirror = new ErrorMirror(value); - } else if (IS_SCRIPT(value)) { - mirror = new ScriptMirror(value); - } else { - mirror = new ObjectMirror(value, OBJECT_TYPE, opt_transient); - } - - mirror_cache_[mirror.handle()] = mirror; - return mirror; -} - - -/** - * Returns the mirror for a specified mirror handle. - * - * @param {number} handle the handle to find the mirror for - * @returns {Mirror or undefiend} the mirror with the requested handle or - * undefined if no mirror with the requested handle was found - */ -function LookupMirror(handle) { - return mirror_cache_[handle]; -} - - -/** - * Returns the mirror for the undefined value. - * - * @returns {Mirror} the mirror reflects the undefined value - */ -function GetUndefinedMirror() { - return MakeMirror(void 0); -} - - -/** - * Inherit the prototype methods from one constructor into another. - * - * The Function.prototype.inherits from lang.js rewritten as a standalone - * function (not on Function.prototype). NOTE: If this file is to be loaded - * during bootstrapping this function needs to be revritten using some native - * functions as prototype setup using normal JavaScript does not work as - * expected during bootstrapping (see mirror.js in r114903). - * - * @param {function} ctor Constructor function which needs to inherit the - * prototype - * @param {function} superCtor Constructor function to inherit prototype from - */ -function inherits(ctor, superCtor) { - var tempCtor = function(){}; - tempCtor.prototype = superCtor.prototype; - ctor.super_ = superCtor.prototype; - ctor.prototype = new tempCtor(); - ctor.prototype.constructor = ctor; -} - - -// Type names of the different mirrors. -const UNDEFINED_TYPE = 'undefined'; -const NULL_TYPE = 'null'; -const BOOLEAN_TYPE = 'boolean'; -const NUMBER_TYPE = 'number'; -const STRING_TYPE = 'string'; -const OBJECT_TYPE = 'object'; -const FUNCTION_TYPE = 'function'; -const REGEXP_TYPE = 'regexp'; -const ERROR_TYPE = 'error'; -const PROPERTY_TYPE = 'property'; -const FRAME_TYPE = 'frame'; -const SCRIPT_TYPE = 'script'; -const CONTEXT_TYPE = 'context'; -const SCOPE_TYPE = 'scope'; - -// Maximum length when sending strings through the JSON protocol. -const kMaxProtocolStringLength = 80; - -// Different kind of properties. -PropertyKind = {}; -PropertyKind.Named = 1; -PropertyKind.Indexed = 2; - - -// A copy of the PropertyType enum from global.h -PropertyType = {}; -PropertyType.Normal = 0; -PropertyType.Field = 1; -PropertyType.ConstantFunction = 2; -PropertyType.Callbacks = 3; -PropertyType.Interceptor = 4; -PropertyType.MapTransition = 5; -PropertyType.ConstantTransition = 6; -PropertyType.NullDescriptor = 7; - - -// Different attributes for a property. -PropertyAttribute = {}; -PropertyAttribute.None = NONE; -PropertyAttribute.ReadOnly = READ_ONLY; -PropertyAttribute.DontEnum = DONT_ENUM; -PropertyAttribute.DontDelete = DONT_DELETE; - - -// A copy of the scope types from runtime.cc. -ScopeType = { Global: 0, - Local: 1, - With: 2, - Closure: 3, - Catch: 4 }; - - -// Mirror hierarchy: -// - Mirror -// - ValueMirror -// - UndefinedMirror -// - NullMirror -// - NumberMirror -// - StringMirror -// - ObjectMirror -// - FunctionMirror -// - UnresolvedFunctionMirror -// - ArrayMirror -// - DateMirror -// - RegExpMirror -// - ErrorMirror -// - PropertyMirror -// - FrameMirror -// - ScriptMirror - - -/** - * Base class for all mirror objects. - * @param {string} type The type of the mirror - * @constructor - */ -function Mirror(type) { - this.type_ = type; -}; - - -Mirror.prototype.type = function() { - return this.type_; -}; - - -/** - * Check whether the mirror reflects a value. - * @returns {boolean} True if the mirror reflects a value. - */ -Mirror.prototype.isValue = function() { - return this instanceof ValueMirror; -} - - -/** - * Check whether the mirror reflects the undefined value. - * @returns {boolean} True if the mirror reflects the undefined value. - */ -Mirror.prototype.isUndefined = function() { - return this instanceof UndefinedMirror; -} - - -/** - * Check whether the mirror reflects the null value. - * @returns {boolean} True if the mirror reflects the null value - */ -Mirror.prototype.isNull = function() { - return this instanceof NullMirror; -} - - -/** - * Check whether the mirror reflects a boolean value. - * @returns {boolean} True if the mirror reflects a boolean value - */ -Mirror.prototype.isBoolean = function() { - return this instanceof BooleanMirror; -} - - -/** - * Check whether the mirror reflects a number value. - * @returns {boolean} True if the mirror reflects a number value - */ -Mirror.prototype.isNumber = function() { - return this instanceof NumberMirror; -} - - -/** - * Check whether the mirror reflects a string value. - * @returns {boolean} True if the mirror reflects a string value - */ -Mirror.prototype.isString = function() { - return this instanceof StringMirror; -} - - -/** - * Check whether the mirror reflects an object. - * @returns {boolean} True if the mirror reflects an object - */ -Mirror.prototype.isObject = function() { - return this instanceof ObjectMirror; -} - - -/** - * Check whether the mirror reflects a function. - * @returns {boolean} True if the mirror reflects a function - */ -Mirror.prototype.isFunction = function() { - return this instanceof FunctionMirror; -} - - -/** - * Check whether the mirror reflects an unresolved function. - * @returns {boolean} True if the mirror reflects an unresolved function - */ -Mirror.prototype.isUnresolvedFunction = function() { - return this instanceof UnresolvedFunctionMirror; -} - - -/** - * Check whether the mirror reflects an array. - * @returns {boolean} True if the mirror reflects an array - */ -Mirror.prototype.isArray = function() { - return this instanceof ArrayMirror; -} - - -/** - * Check whether the mirror reflects a date. - * @returns {boolean} True if the mirror reflects a date - */ -Mirror.prototype.isDate = function() { - return this instanceof DateMirror; -} - - -/** - * Check whether the mirror reflects a regular expression. - * @returns {boolean} True if the mirror reflects a regular expression - */ -Mirror.prototype.isRegExp = function() { - return this instanceof RegExpMirror; -} - - -/** - * Check whether the mirror reflects an error. - * @returns {boolean} True if the mirror reflects an error - */ -Mirror.prototype.isError = function() { - return this instanceof ErrorMirror; -} - - -/** - * Check whether the mirror reflects a property. - * @returns {boolean} True if the mirror reflects a property - */ -Mirror.prototype.isProperty = function() { - return this instanceof PropertyMirror; -} - - -/** - * Check whether the mirror reflects a stack frame. - * @returns {boolean} True if the mirror reflects a stack frame - */ -Mirror.prototype.isFrame = function() { - return this instanceof FrameMirror; -} - - -/** - * Check whether the mirror reflects a script. - * @returns {boolean} True if the mirror reflects a script - */ -Mirror.prototype.isScript = function() { - return this instanceof ScriptMirror; -} - - -/** - * Check whether the mirror reflects a context. - * @returns {boolean} True if the mirror reflects a context - */ -Mirror.prototype.isContext = function() { - return this instanceof ContextMirror; -} - - -/** - * Check whether the mirror reflects a scope. - * @returns {boolean} True if the mirror reflects a scope - */ -Mirror.prototype.isScope = function() { - return this instanceof ScopeMirror; -} - - -/** - * Allocate a handle id for this object. - */ -Mirror.prototype.allocateHandle_ = function() { - this.handle_ = next_handle_++; -} - - -/** - * Allocate a transient handle id for this object. Transient handles are - * negative. - */ -Mirror.prototype.allocateTransientHandle_ = function() { - this.handle_ = next_transient_handle_--; -} - - -Mirror.prototype.toText = function() { - // Simpel to text which is used when on specialization in subclass. - return "#<" + builtins.GetInstanceName(this.constructor.name) + ">"; -} - - -/** - * Base class for all value mirror objects. - * @param {string} type The type of the mirror - * @param {value} value The value reflected by this mirror - * @param {boolean} transient indicate whether this object is transient with a - * transient handle - * @constructor - * @extends Mirror - */ -function ValueMirror(type, value, transient) { - Mirror.call(this, type); - this.value_ = value; - if (!transient) { - this.allocateHandle_(); - } else { - this.allocateTransientHandle_(); - } -} -inherits(ValueMirror, Mirror); - - -Mirror.prototype.handle = function() { - return this.handle_; -}; - - -/** - * Check whether this is a primitive value. - * @return {boolean} True if the mirror reflects a primitive value - */ -ValueMirror.prototype.isPrimitive = function() { - var type = this.type(); - return type === 'undefined' || - type === 'null' || - type === 'boolean' || - type === 'number' || - type === 'string'; -}; - - -/** - * Get the actual value reflected by this mirror. - * @return {value} The value reflected by this mirror - */ -ValueMirror.prototype.value = function() { - return this.value_; -}; - - -/** - * Mirror object for Undefined. - * @constructor - * @extends ValueMirror - */ -function UndefinedMirror() { - ValueMirror.call(this, UNDEFINED_TYPE, void 0); -} -inherits(UndefinedMirror, ValueMirror); - - -UndefinedMirror.prototype.toText = function() { - return 'undefined'; -} - - -/** - * Mirror object for null. - * @constructor - * @extends ValueMirror - */ -function NullMirror() { - ValueMirror.call(this, NULL_TYPE, null); -} -inherits(NullMirror, ValueMirror); - - -NullMirror.prototype.toText = function() { - return 'null'; -} - - -/** - * Mirror object for boolean values. - * @param {boolean} value The boolean value reflected by this mirror - * @constructor - * @extends ValueMirror - */ -function BooleanMirror(value) { - ValueMirror.call(this, BOOLEAN_TYPE, value); -} -inherits(BooleanMirror, ValueMirror); - - -BooleanMirror.prototype.toText = function() { - return this.value_ ? 'true' : 'false'; -} - - -/** - * Mirror object for number values. - * @param {number} value The number value reflected by this mirror - * @constructor - * @extends ValueMirror - */ -function NumberMirror(value) { - ValueMirror.call(this, NUMBER_TYPE, value); -} -inherits(NumberMirror, ValueMirror); - - -NumberMirror.prototype.toText = function() { - return %NumberToString(this.value_); -} - - -/** - * Mirror object for string values. - * @param {string} value The string value reflected by this mirror - * @constructor - * @extends ValueMirror - */ -function StringMirror(value) { - ValueMirror.call(this, STRING_TYPE, value); -} -inherits(StringMirror, ValueMirror); - - -StringMirror.prototype.length = function() { - return this.value_.length; -}; - - -StringMirror.prototype.toText = function() { - if (this.length() > kMaxProtocolStringLength) { - return this.value_.substring(0, kMaxProtocolStringLength) + - '... (length: ' + this.length() + ')'; - } else { - return this.value_; - } -} - - -/** - * Mirror object for objects. - * @param {object} value The object reflected by this mirror - * @param {boolean} transient indicate whether this object is transient with a - * transient handle - * @constructor - * @extends ValueMirror - */ -function ObjectMirror(value, type, transient) { - ValueMirror.call(this, type || OBJECT_TYPE, value, transient); -} -inherits(ObjectMirror, ValueMirror); - - -ObjectMirror.prototype.className = function() { - return %_ClassOf(this.value_); -}; - - -ObjectMirror.prototype.constructorFunction = function() { - return MakeMirror(%DebugGetProperty(this.value_, 'constructor')); -}; - - -ObjectMirror.prototype.prototypeObject = function() { - return MakeMirror(%DebugGetProperty(this.value_, 'prototype')); -}; - - -ObjectMirror.prototype.protoObject = function() { - return MakeMirror(%DebugGetPrototype(this.value_)); -}; - - -ObjectMirror.prototype.hasNamedInterceptor = function() { - // Get information on interceptors for this object. - var x = %GetInterceptorInfo(this.value_); - return (x & 2) != 0; -}; - - -ObjectMirror.prototype.hasIndexedInterceptor = function() { - // Get information on interceptors for this object. - var x = %GetInterceptorInfo(this.value_); - return (x & 1) != 0; -}; - - -/** - * Return the property names for this object. - * @param {number} kind Indicate whether named, indexed or both kinds of - * properties are requested - * @param {number} limit Limit the number of names returend to the specified - value - * @return {Array} Property names for this object - */ -ObjectMirror.prototype.propertyNames = function(kind, limit) { - // Find kind and limit and allocate array for the result - kind = kind || PropertyKind.Named | PropertyKind.Indexed; - - var propertyNames; - var elementNames; - var total = 0; - - // Find all the named properties. - if (kind & PropertyKind.Named) { - // Get the local property names. - propertyNames = %GetLocalPropertyNames(this.value_); - total += propertyNames.length; - - // Get names for named interceptor properties if any. - if (this.hasNamedInterceptor() && (kind & PropertyKind.Named)) { - var namedInterceptorNames = - %GetNamedInterceptorPropertyNames(this.value_); - if (namedInterceptorNames) { - propertyNames = propertyNames.concat(namedInterceptorNames); - total += namedInterceptorNames.length; - } - } - } - - // Find all the indexed properties. - if (kind & PropertyKind.Indexed) { - // Get the local element names. - elementNames = %GetLocalElementNames(this.value_); - total += elementNames.length; - - // Get names for indexed interceptor properties. - if (this.hasIndexedInterceptor() && (kind & PropertyKind.Indexed)) { - var indexedInterceptorNames = - %GetIndexedInterceptorElementNames(this.value_); - if (indexedInterceptorNames) { - elementNames = elementNames.concat(indexedInterceptorNames); - total += indexedInterceptorNames.length; - } - } - } - limit = Math.min(limit || total, total); - - var names = new Array(limit); - var index = 0; - - // Copy names for named properties. - if (kind & PropertyKind.Named) { - for (var i = 0; index < limit && i < propertyNames.length; i++) { - names[index++] = propertyNames[i]; - } - } - - // Copy names for indexed properties. - if (kind & PropertyKind.Indexed) { - for (var i = 0; index < limit && i < elementNames.length; i++) { - names[index++] = elementNames[i]; - } - } - - return names; -}; - - -/** - * Return the properties for this object as an array of PropertyMirror objects. - * @param {number} kind Indicate whether named, indexed or both kinds of - * properties are requested - * @param {number} limit Limit the number of properties returend to the - specified value - * @return {Array} Property mirrors for this object - */ -ObjectMirror.prototype.properties = function(kind, limit) { - var names = this.propertyNames(kind, limit); - var properties = new Array(names.length); - for (var i = 0; i < names.length; i++) { - properties[i] = this.property(names[i]); - } - - return properties; -}; - - -ObjectMirror.prototype.property = function(name) { - var details = %DebugGetPropertyDetails(this.value_, %ToString(name)); - if (details) { - return new PropertyMirror(this, name, details); - } - - // Nothing found. - return GetUndefinedMirror(); -}; - - - -/** - * Try to find a property from its value. - * @param {Mirror} value The property value to look for - * @return {PropertyMirror} The property with the specified value. If no - * property was found with the specified value UndefinedMirror is returned - */ -ObjectMirror.prototype.lookupProperty = function(value) { - var properties = this.properties(); - - // Look for property value in properties. - for (var i = 0; i < properties.length; i++) { - - // Skip properties which are defined through assessors. - var property = properties[i]; - if (property.propertyType() != PropertyType.Callbacks) { - if (%_ObjectEquals(property.value_, value.value_)) { - return property; - } - } - } - - // Nothing found. - return GetUndefinedMirror(); -}; - - -/** - * Returns objects which has direct references to this object - * @param {number} opt_max_objects Optional parameter specifying the maximum - * number of referencing objects to return. - * @return {Array} The objects which has direct references to this object. - */ -ObjectMirror.prototype.referencedBy = function(opt_max_objects) { - // Find all objects with direct references to this object. - var result = %DebugReferencedBy(this.value_, - Mirror.prototype, opt_max_objects || 0); - - // Make mirrors for all the references found. - for (var i = 0; i < result.length; i++) { - result[i] = MakeMirror(result[i]); - } - - return result; -}; - - -ObjectMirror.prototype.toText = function() { - var name; - var ctor = this.constructorFunction(); - if (!ctor.isFunction()) { - name = this.className(); - } else { - name = ctor.name(); - if (!name) { - name = this.className(); - } - } - return '#<' + builtins.GetInstanceName(name) + '>'; -}; - - -/** - * Mirror object for functions. - * @param {function} value The function object reflected by this mirror. - * @constructor - * @extends ObjectMirror - */ -function FunctionMirror(value) { - ObjectMirror.call(this, value, FUNCTION_TYPE); - this.resolved_ = true; -} -inherits(FunctionMirror, ObjectMirror); - - -/** - * Returns whether the function is resolved. - * @return {boolean} True if the function is resolved. Unresolved functions can - * only originate as functions from stack frames - */ -FunctionMirror.prototype.resolved = function() { - return this.resolved_; -}; - - -/** - * Returns the name of the function. - * @return {string} Name of the function - */ -FunctionMirror.prototype.name = function() { - return %FunctionGetName(this.value_); -}; - - -/** - * Returns the inferred name of the function. - * @return {string} Name of the function - */ -FunctionMirror.prototype.inferredName = function() { - return %FunctionGetInferredName(this.value_); -}; - - -/** - * Returns the source code for the function. - * @return {string or undefined} The source code for the function. If the - * function is not resolved undefined will be returned. - */ -FunctionMirror.prototype.source = function() { - // Return source if function is resolved. Otherwise just fall through to - // return undefined. - if (this.resolved()) { - return builtins.FunctionSourceString(this.value_); - } -}; - - -/** - * Returns the script object for the function. - * @return {ScriptMirror or undefined} Script object for the function or - * undefined if the function has no script - */ -FunctionMirror.prototype.script = function() { - // Return script if function is resolved. Otherwise just fall through - // to return undefined. - if (this.resolved()) { - var script = %FunctionGetScript(this.value_); - if (script) { - return MakeMirror(script); - } - } -}; - - -/** - * Returns the script source position for the function. Only makes sense - * for functions which has a script defined. - * @return {Number or undefined} in-script position for the function - */ -FunctionMirror.prototype.sourcePosition_ = function() { - // Return script if function is resolved. Otherwise just fall through - // to return undefined. - if (this.resolved()) { - return %FunctionGetScriptSourcePosition(this.value_); - } -}; - - -/** - * Returns the script source location object for the function. Only makes sense - * for functions which has a script defined. - * @return {Location or undefined} in-script location for the function begin - */ -FunctionMirror.prototype.sourceLocation = function() { - if (this.resolved() && this.script()) { - return this.script().locationFromPosition(this.sourcePosition_(), - true); - } -}; - - -/** - * Returns objects constructed by this function. - * @param {number} opt_max_instances Optional parameter specifying the maximum - * number of instances to return. - * @return {Array or undefined} The objects constructed by this function. - */ -FunctionMirror.prototype.constructedBy = function(opt_max_instances) { - if (this.resolved()) { - // Find all objects constructed from this function. - var result = %DebugConstructedBy(this.value_, opt_max_instances || 0); - - // Make mirrors for all the instances found. - for (var i = 0; i < result.length; i++) { - result[i] = MakeMirror(result[i]); - } - - return result; - } else { - return []; - } -}; - - -FunctionMirror.prototype.toText = function() { - return this.source(); -} - - -/** - * Mirror object for unresolved functions. - * @param {string} value The name for the unresolved function reflected by this - * mirror. - * @constructor - * @extends ObjectMirror - */ -function UnresolvedFunctionMirror(value) { - // Construct this using the ValueMirror as an unresolved function is not a - // real object but just a string. - ValueMirror.call(this, FUNCTION_TYPE, value); - this.propertyCount_ = 0; - this.elementCount_ = 0; - this.resolved_ = false; -} -inherits(UnresolvedFunctionMirror, FunctionMirror); - - -UnresolvedFunctionMirror.prototype.className = function() { - return 'Function'; -}; - - -UnresolvedFunctionMirror.prototype.constructorFunction = function() { - return GetUndefinedMirror(); -}; - - -UnresolvedFunctionMirror.prototype.prototypeObject = function() { - return GetUndefinedMirror(); -}; - - -UnresolvedFunctionMirror.prototype.protoObject = function() { - return GetUndefinedMirror(); -}; - - -UnresolvedFunctionMirror.prototype.name = function() { - return this.value_; -}; - - -UnresolvedFunctionMirror.prototype.inferredName = function() { - return undefined; -}; - - -UnresolvedFunctionMirror.prototype.propertyNames = function(kind, limit) { - return []; -} - - -/** - * Mirror object for arrays. - * @param {Array} value The Array object reflected by this mirror - * @constructor - * @extends ObjectMirror - */ -function ArrayMirror(value) { - ObjectMirror.call(this, value); -} -inherits(ArrayMirror, ObjectMirror); - - -ArrayMirror.prototype.length = function() { - return this.value_.length; -}; - - -ArrayMirror.prototype.indexedPropertiesFromRange = function(opt_from_index, opt_to_index) { - var from_index = opt_from_index || 0; - var to_index = opt_to_index || this.length() - 1; - if (from_index > to_index) return new Array(); - var values = new Array(to_index - from_index + 1); - for (var i = from_index; i <= to_index; i++) { - var details = %DebugGetPropertyDetails(this.value_, %ToString(i)); - var value; - if (details) { - value = new PropertyMirror(this, i, details); - } else { - value = GetUndefinedMirror(); - } - values[i - from_index] = value; - } - return values; -} - - -/** - * Mirror object for dates. - * @param {Date} value The Date object reflected by this mirror - * @constructor - * @extends ObjectMirror - */ -function DateMirror(value) { - ObjectMirror.call(this, value); -} -inherits(DateMirror, ObjectMirror); - - -DateMirror.prototype.toText = function() { - var s = JSON.stringify(this.value_); - return s.substring(1, s.length - 1); // cut quotes -} - - -/** - * Mirror object for regular expressions. - * @param {RegExp} value The RegExp object reflected by this mirror - * @constructor - * @extends ObjectMirror - */ -function RegExpMirror(value) { - ObjectMirror.call(this, value, REGEXP_TYPE); -} -inherits(RegExpMirror, ObjectMirror); - - -/** - * Returns the source to the regular expression. - * @return {string or undefined} The source to the regular expression - */ -RegExpMirror.prototype.source = function() { - return this.value_.source; -}; - - -/** - * Returns whether this regular expression has the global (g) flag set. - * @return {boolean} Value of the global flag - */ -RegExpMirror.prototype.global = function() { - return this.value_.global; -}; - - -/** - * Returns whether this regular expression has the ignore case (i) flag set. - * @return {boolean} Value of the ignore case flag - */ -RegExpMirror.prototype.ignoreCase = function() { - return this.value_.ignoreCase; -}; - - -/** - * Returns whether this regular expression has the multiline (m) flag set. - * @return {boolean} Value of the multiline flag - */ -RegExpMirror.prototype.multiline = function() { - return this.value_.multiline; -}; - - -RegExpMirror.prototype.toText = function() { - // Simpel to text which is used when on specialization in subclass. - return "/" + this.source() + "/"; -} - - -/** - * Mirror object for error objects. - * @param {Error} value The error object reflected by this mirror - * @constructor - * @extends ObjectMirror - */ -function ErrorMirror(value) { - ObjectMirror.call(this, value, ERROR_TYPE); -} -inherits(ErrorMirror, ObjectMirror); - - -/** - * Returns the message for this eror object. - * @return {string or undefined} The message for this eror object - */ -ErrorMirror.prototype.message = function() { - return this.value_.message; -}; - - -ErrorMirror.prototype.toText = function() { - // Use the same text representation as in messages.js. - var text; - try { - str = builtins.ToDetailString(this.value_); - } catch (e) { - str = '#<an Error>'; - } - return str; -} - - -/** - * Base mirror object for properties. - * @param {ObjectMirror} mirror The mirror object having this property - * @param {string} name The name of the property - * @param {Array} details Details about the property - * @constructor - * @extends Mirror - */ -function PropertyMirror(mirror, name, details) { - Mirror.call(this, PROPERTY_TYPE); - this.mirror_ = mirror; - this.name_ = name; - this.value_ = details[0]; - this.details_ = details[1]; - if (details.length > 2) { - this.exception_ = details[2] - this.getter_ = details[3]; - this.setter_ = details[4]; - } -} -inherits(PropertyMirror, Mirror); - - -PropertyMirror.prototype.isReadOnly = function() { - return (this.attributes() & PropertyAttribute.ReadOnly) != 0; -} - - -PropertyMirror.prototype.isEnum = function() { - return (this.attributes() & PropertyAttribute.DontEnum) == 0; -} - - -PropertyMirror.prototype.canDelete = function() { - return (this.attributes() & PropertyAttribute.DontDelete) == 0; -} - - -PropertyMirror.prototype.name = function() { - return this.name_; -} - - -PropertyMirror.prototype.isIndexed = function() { - for (var i = 0; i < this.name_.length; i++) { - if (this.name_[i] < '0' || '9' < this.name_[i]) { - return false; - } - } - return true; -} - - -PropertyMirror.prototype.value = function() { - return MakeMirror(this.value_, false); -} - - -/** - * Returns whether this property value is an exception. - * @return {booolean} True if this property value is an exception - */ -PropertyMirror.prototype.isException = function() { - return this.exception_ ? true : false; -} - - -PropertyMirror.prototype.attributes = function() { - return %DebugPropertyAttributesFromDetails(this.details_); -} - - -PropertyMirror.prototype.propertyType = function() { - return %DebugPropertyTypeFromDetails(this.details_); -} - - -PropertyMirror.prototype.insertionIndex = function() { - return %DebugPropertyIndexFromDetails(this.details_); -} - - -/** - * Returns whether this property has a getter defined through __defineGetter__. - * @return {booolean} True if this property has a getter - */ -PropertyMirror.prototype.hasGetter = function() { - return this.getter_ ? true : false; -} - - -/** - * Returns whether this property has a setter defined through __defineSetter__. - * @return {booolean} True if this property has a setter - */ -PropertyMirror.prototype.hasSetter = function() { - return this.setter_ ? true : false; -} - - -/** - * Returns the getter for this property defined through __defineGetter__. - * @return {Mirror} FunctionMirror reflecting the getter function or - * UndefinedMirror if there is no getter for this property - */ -PropertyMirror.prototype.getter = function() { - if (this.hasGetter()) { - return MakeMirror(this.getter_); - } else { - return GetUndefinedMirror(); - } -} - - -/** - * Returns the setter for this property defined through __defineSetter__. - * @return {Mirror} FunctionMirror reflecting the setter function or - * UndefinedMirror if there is no setter for this property - */ -PropertyMirror.prototype.setter = function() { - if (this.hasSetter()) { - return MakeMirror(this.setter_); - } else { - return GetUndefinedMirror(); - } -} - - -/** - * Returns whether this property is natively implemented by the host or a set - * through JavaScript code. - * @return {boolean} True if the property is - * UndefinedMirror if there is no setter for this property - */ -PropertyMirror.prototype.isNative = function() { - return (this.propertyType() == PropertyType.Interceptor) || - ((this.propertyType() == PropertyType.Callbacks) && - !this.hasGetter() && !this.hasSetter()); -} - - -const kFrameDetailsFrameIdIndex = 0; -const kFrameDetailsReceiverIndex = 1; -const kFrameDetailsFunctionIndex = 2; -const kFrameDetailsArgumentCountIndex = 3; -const kFrameDetailsLocalCountIndex = 4; -const kFrameDetailsSourcePositionIndex = 5; -const kFrameDetailsConstructCallIndex = 6; -const kFrameDetailsDebuggerFrameIndex = 7; -const kFrameDetailsFirstDynamicIndex = 8; - -const kFrameDetailsNameIndex = 0; -const kFrameDetailsValueIndex = 1; -const kFrameDetailsNameValueSize = 2; - -/** - * Wrapper for the frame details information retreived from the VM. The frame - * details from the VM is an array with the following content. See runtime.cc - * Runtime_GetFrameDetails. - * 0: Id - * 1: Receiver - * 2: Function - * 3: Argument count - * 4: Local count - * 5: Source position - * 6: Construct call - * Arguments name, value - * Locals name, value - * @param {number} break_id Current break id - * @param {number} index Frame number - * @constructor - */ -function FrameDetails(break_id, index) { - this.break_id_ = break_id; - this.details_ = %GetFrameDetails(break_id, index); -} - - -FrameDetails.prototype.frameId = function() { - %CheckExecutionState(this.break_id_); - return this.details_[kFrameDetailsFrameIdIndex]; -} - - -FrameDetails.prototype.receiver = function() { - %CheckExecutionState(this.break_id_); - return this.details_[kFrameDetailsReceiverIndex]; -} - - -FrameDetails.prototype.func = function() { - %CheckExecutionState(this.break_id_); - return this.details_[kFrameDetailsFunctionIndex]; -} - - -FrameDetails.prototype.isConstructCall = function() { - %CheckExecutionState(this.break_id_); - return this.details_[kFrameDetailsConstructCallIndex]; -} - - -FrameDetails.prototype.isDebuggerFrame = function() { - %CheckExecutionState(this.break_id_); - return this.details_[kFrameDetailsDebuggerFrameIndex]; -} - - -FrameDetails.prototype.argumentCount = function() { - %CheckExecutionState(this.break_id_); - return this.details_[kFrameDetailsArgumentCountIndex]; -} - - -FrameDetails.prototype.argumentName = function(index) { - %CheckExecutionState(this.break_id_); - if (index >= 0 && index < this.argumentCount()) { - return this.details_[kFrameDetailsFirstDynamicIndex + - index * kFrameDetailsNameValueSize + - kFrameDetailsNameIndex] - } -} - - -FrameDetails.prototype.argumentValue = function(index) { - %CheckExecutionState(this.break_id_); - if (index >= 0 && index < this.argumentCount()) { - return this.details_[kFrameDetailsFirstDynamicIndex + - index * kFrameDetailsNameValueSize + - kFrameDetailsValueIndex] - } -} - - -FrameDetails.prototype.localCount = function() { - %CheckExecutionState(this.break_id_); - return this.details_[kFrameDetailsLocalCountIndex]; -} - - -FrameDetails.prototype.sourcePosition = function() { - %CheckExecutionState(this.break_id_); - return this.details_[kFrameDetailsSourcePositionIndex]; -} - - -FrameDetails.prototype.localName = function(index) { - %CheckExecutionState(this.break_id_); - if (index >= 0 && index < this.localCount()) { - var locals_offset = kFrameDetailsFirstDynamicIndex + this.argumentCount() * kFrameDetailsNameValueSize - return this.details_[locals_offset + - index * kFrameDetailsNameValueSize + - kFrameDetailsNameIndex] - } -} - - -FrameDetails.prototype.localValue = function(index) { - %CheckExecutionState(this.break_id_); - if (index >= 0 && index < this.localCount()) { - var locals_offset = kFrameDetailsFirstDynamicIndex + this.argumentCount() * kFrameDetailsNameValueSize - return this.details_[locals_offset + - index * kFrameDetailsNameValueSize + - kFrameDetailsValueIndex] - } -} - - -FrameDetails.prototype.scopeCount = function() { - return %GetScopeCount(this.break_id_, this.frameId()); -} - - -/** - * Mirror object for stack frames. - * @param {number} break_id The break id in the VM for which this frame is - valid - * @param {number} index The frame index (top frame is index 0) - * @constructor - * @extends Mirror - */ -function FrameMirror(break_id, index) { - Mirror.call(this, FRAME_TYPE); - this.break_id_ = break_id; - this.index_ = index; - this.details_ = new FrameDetails(break_id, index); -} -inherits(FrameMirror, Mirror); - - -FrameMirror.prototype.index = function() { - return this.index_; -}; - - -FrameMirror.prototype.func = function() { - // Get the function for this frame from the VM. - var f = this.details_.func(); - - // Create a function mirror. NOTE: MakeMirror cannot be used here as the - // value returned from the VM might be a string if the function for the - // frame is unresolved. - if (IS_FUNCTION(f)) { - return MakeMirror(f); - } else { - return new UnresolvedFunctionMirror(f); - } -}; - - -FrameMirror.prototype.receiver = function() { - return MakeMirror(this.details_.receiver()); -}; - - -FrameMirror.prototype.isConstructCall = function() { - return this.details_.isConstructCall(); -}; - - -FrameMirror.prototype.isDebuggerFrame = function() { - return this.details_.isDebuggerFrame(); -}; - - -FrameMirror.prototype.argumentCount = function() { - return this.details_.argumentCount(); -}; - - -FrameMirror.prototype.argumentName = function(index) { - return this.details_.argumentName(index); -}; - - -FrameMirror.prototype.argumentValue = function(index) { - return MakeMirror(this.details_.argumentValue(index)); -}; - - -FrameMirror.prototype.localCount = function() { - return this.details_.localCount(); -}; - - -FrameMirror.prototype.localName = function(index) { - return this.details_.localName(index); -}; - - -FrameMirror.prototype.localValue = function(index) { - return MakeMirror(this.details_.localValue(index)); -}; - - -FrameMirror.prototype.sourcePosition = function() { - return this.details_.sourcePosition(); -}; - - -FrameMirror.prototype.sourceLocation = function() { - if (this.func().resolved() && this.func().script()) { - return this.func().script().locationFromPosition(this.sourcePosition(), - true); - } -}; - - -FrameMirror.prototype.sourceLine = function() { - if (this.func().resolved()) { - var location = this.sourceLocation(); - if (location) { - return location.line; - } - } -}; - - -FrameMirror.prototype.sourceColumn = function() { - if (this.func().resolved()) { - var location = this.sourceLocation(); - if (location) { - return location.column; - } - } -}; - - -FrameMirror.prototype.sourceLineText = function() { - if (this.func().resolved()) { - var location = this.sourceLocation(); - if (location) { - return location.sourceText(); - } - } -}; - - -FrameMirror.prototype.scopeCount = function() { - return this.details_.scopeCount(); -}; - - -FrameMirror.prototype.scope = function(index) { - return new ScopeMirror(this, index); -}; - - -FrameMirror.prototype.evaluate = function(source, disable_break) { - var result = %DebugEvaluate(this.break_id_, this.details_.frameId(), - source, Boolean(disable_break)); - return MakeMirror(result); -}; - - -FrameMirror.prototype.invocationText = function() { - // Format frame invoaction (receiver, function and arguments). - var result = ''; - var func = this.func(); - var receiver = this.receiver(); - if (this.isConstructCall()) { - // For constructor frames display new followed by the function name. - result += 'new '; - result += func.name() ? func.name() : '[anonymous]'; - } else if (this.isDebuggerFrame()) { - result += '[debugger]'; - } else { - // If the receiver has a className which is 'global' don't display it. - var display_receiver = !receiver.className || receiver.className() != 'global'; - if (display_receiver) { - result += receiver.toText(); - } - // Try to find the function as a property in the receiver. Include the - // prototype chain in the lookup. - var property = GetUndefinedMirror(); - if (!receiver.isUndefined()) { - for (var r = receiver; !r.isNull() && property.isUndefined(); r = r.protoObject()) { - property = r.lookupProperty(func); - } - } - if (!property.isUndefined()) { - // The function invoked was found on the receiver. Use the property name - // for the backtrace. - if (!property.isIndexed()) { - if (display_receiver) { - result += '.'; - } - result += property.name(); - } else { - result += '['; - result += property.name(); - result += ']'; - } - // Also known as - if the name in the function doesn't match the name - // under which it was looked up. - if (func.name() && func.name() != property.name()) { - result += '(aka ' + func.name() + ')'; - } - } else { - // The function invoked was not found on the receiver. Use the function - // name if available for the backtrace. - if (display_receiver) { - result += '.'; - } - result += func.name() ? func.name() : '[anonymous]'; - } - } - - // Render arguments for normal frames. - if (!this.isDebuggerFrame()) { - result += '('; - for (var i = 0; i < this.argumentCount(); i++) { - if (i != 0) result += ', '; - if (this.argumentName(i)) { - result += this.argumentName(i); - result += '='; - } - result += this.argumentValue(i).toText(); - } - result += ')'; - } - - return result; -} - - -FrameMirror.prototype.sourceAndPositionText = function() { - // Format source and position. - var result = ''; - var func = this.func(); - if (func.resolved()) { - if (func.script()) { - if (func.script().name()) { - result += func.script().name(); - } else { - result += '[unnamed]'; - } - if (!this.isDebuggerFrame()) { - var location = this.sourceLocation(); - result += ' line '; - result += !IS_UNDEFINED(location) ? (location.line + 1) : '?'; - result += ' column '; - result += !IS_UNDEFINED(location) ? (location.column + 1) : '?'; - if (!IS_UNDEFINED(this.sourcePosition())) { - result += ' (position ' + (this.sourcePosition() + 1) + ')'; - } - } - } else { - result += '[no source]'; - } - } else { - result += '[unresolved]'; - } - - return result; -} - - -FrameMirror.prototype.localsText = function() { - // Format local variables. - var result = ''; - var locals_count = this.localCount() - if (locals_count > 0) { - for (var i = 0; i < locals_count; ++i) { - result += ' var '; - result += this.localName(i); - result += ' = '; - result += this.localValue(i).toText(); - if (i < locals_count - 1) result += '\n'; - } - } - - return result; -} - - -FrameMirror.prototype.toText = function(opt_locals) { - var result = ''; - result += '#' + (this.index() <= 9 ? '0' : '') + this.index(); - result += ' '; - result += this.invocationText(); - result += ' '; - result += this.sourceAndPositionText(); - if (opt_locals) { - result += '\n'; - result += this.localsText(); - } - return result; -} - - -const kScopeDetailsTypeIndex = 0; -const kScopeDetailsObjectIndex = 1; - -function ScopeDetails(frame, index) { - this.break_id_ = frame.break_id_; - this.details_ = %GetScopeDetails(frame.break_id_, - frame.details_.frameId(), - index); -} - - -ScopeDetails.prototype.type = function() { - %CheckExecutionState(this.break_id_); - return this.details_[kScopeDetailsTypeIndex]; -} - - -ScopeDetails.prototype.object = function() { - %CheckExecutionState(this.break_id_); - return this.details_[kScopeDetailsObjectIndex]; -} - - -/** - * Mirror object for scope. - * @param {FrameMirror} frame The frame this scope is a part of - * @param {number} index The scope index in the frame - * @constructor - * @extends Mirror - */ -function ScopeMirror(frame, index) { - Mirror.call(this, SCOPE_TYPE); - this.frame_index_ = frame.index_; - this.scope_index_ = index; - this.details_ = new ScopeDetails(frame, index); -} -inherits(ScopeMirror, Mirror); - - -ScopeMirror.prototype.frameIndex = function() { - return this.frame_index_; -}; - - -ScopeMirror.prototype.scopeIndex = function() { - return this.scope_index_; -}; - - -ScopeMirror.prototype.scopeType = function() { - return this.details_.type(); -}; - - -ScopeMirror.prototype.scopeObject = function() { - // For local and closure scopes create a transient mirror as these objects are - // created on the fly materializing the local or closure scopes and - // therefore will not preserve identity. - var transient = this.scopeType() == ScopeType.Local || - this.scopeType() == ScopeType.Closure; - return MakeMirror(this.details_.object(), transient); -}; - - -/** - * Mirror object for script source. - * @param {Script} script The script object - * @constructor - * @extends Mirror - */ -function ScriptMirror(script) { - Mirror.call(this, SCRIPT_TYPE); - this.script_ = script; - this.context_ = new ContextMirror(script.context_data); - this.allocateHandle_(); -} -inherits(ScriptMirror, Mirror); - - -ScriptMirror.prototype.value = function() { - return this.script_; -}; - - -ScriptMirror.prototype.name = function() { - return this.script_.name; -}; - - -ScriptMirror.prototype.id = function() { - return this.script_.id; -}; - - -ScriptMirror.prototype.source = function() { - return this.script_.source; -}; - - -ScriptMirror.prototype.lineOffset = function() { - return this.script_.line_offset; -}; - - -ScriptMirror.prototype.columnOffset = function() { - return this.script_.column_offset; -}; - - -ScriptMirror.prototype.data = function() { - return this.script_.data; -}; - - -ScriptMirror.prototype.scriptType = function() { - return this.script_.type; -}; - - -ScriptMirror.prototype.compilationType = function() { - return this.script_.compilation_type; -}; - - -ScriptMirror.prototype.lineCount = function() { - return this.script_.lineCount(); -}; - - -ScriptMirror.prototype.locationFromPosition = function( - position, include_resource_offset) { - return this.script_.locationFromPosition(position, include_resource_offset); -} - - -ScriptMirror.prototype.sourceSlice = function (opt_from_line, opt_to_line) { - return this.script_.sourceSlice(opt_from_line, opt_to_line); -} - - -ScriptMirror.prototype.context = function() { - return this.context_; -}; - - -ScriptMirror.prototype.evalFromScript = function() { - return MakeMirror(this.script_.eval_from_script); -}; - - -ScriptMirror.prototype.evalFromFunctionName = function() { - return MakeMirror(this.script_.eval_from_function_name); -}; - - -ScriptMirror.prototype.evalFromLocation = function() { - var eval_from_script = this.evalFromScript(); - if (!eval_from_script.isUndefined()) { - var position = this.script_.eval_from_script_position; - return eval_from_script.locationFromPosition(position, true); - } -}; - - -ScriptMirror.prototype.toText = function() { - var result = ''; - result += this.name(); - result += ' (lines: '; - if (this.lineOffset() > 0) { - result += this.lineOffset(); - result += '-'; - result += this.lineOffset() + this.lineCount() - 1; - } else { - result += this.lineCount(); - } - result += ')'; - return result; -} - - -/** - * Mirror object for context. - * @param {Object} data The context data - * @constructor - * @extends Mirror - */ -function ContextMirror(data) { - Mirror.call(this, CONTEXT_TYPE); - this.data_ = data; - this.allocateHandle_(); -} -inherits(ContextMirror, Mirror); - - -ContextMirror.prototype.data = function() { - return this.data_; -}; - - -/** - * Returns a mirror serializer - * - * @param {boolean} details Set to true to include details - * @param {Object} options Options comtrolling the serialization - * The following options can be set: - * includeSource: include ths full source of scripts - * @returns {MirrorSerializer} mirror serializer - */ -function MakeMirrorSerializer(details, options) { - return new JSONProtocolSerializer(details, options); -} - - -/** - * Object for serializing a mirror objects and its direct references. - * @param {boolean} details Indicates whether to include details for the mirror - * serialized - * @constructor - */ -function JSONProtocolSerializer(details, options) { - this.details_ = details; - this.options_ = options; - this.mirrors_ = [ ]; -} - - -/** - * Returns a serialization of an object reference. The referenced object are - * added to the serialization state. - * - * @param {Mirror} mirror The mirror to serialize - * @returns {String} JSON serialization - */ -JSONProtocolSerializer.prototype.serializeReference = function(mirror) { - return this.serialize_(mirror, true, true); -} - - -/** - * Returns a serialization of an object value. The referenced objects are - * added to the serialization state. - * - * @param {Mirror} mirror The mirror to serialize - * @returns {String} JSON serialization - */ -JSONProtocolSerializer.prototype.serializeValue = function(mirror) { - var json = this.serialize_(mirror, false, true); - return json; -} - - -/** - * Returns a serialization of all the objects referenced. - * - * @param {Mirror} mirror The mirror to serialize. - * @returns {Array.<Object>} Array of the referenced objects converted to - * protcol objects. - */ -JSONProtocolSerializer.prototype.serializeReferencedObjects = function() { - // Collect the protocol representation of the referenced objects in an array. - var content = []; - - // Get the number of referenced objects. - var count = this.mirrors_.length; - - for (var i = 0; i < count; i++) { - content.push(this.serialize_(this.mirrors_[i], false, false)); - } - - return content; -} - - -JSONProtocolSerializer.prototype.includeSource_ = function() { - return this.options_ && this.options_.includeSource; -} - - -JSONProtocolSerializer.prototype.inlineRefs_ = function() { - return this.options_ && this.options_.inlineRefs; -} - - -JSONProtocolSerializer.prototype.add_ = function(mirror) { - // If this mirror is already in the list just return. - for (var i = 0; i < this.mirrors_.length; i++) { - if (this.mirrors_[i] === mirror) { - return; - } - } - - // Add the mirror to the list of mirrors to be serialized. - this.mirrors_.push(mirror); -} - - -/** - * Formats mirror object to protocol reference object with some data that can - * be used to display the value in debugger. - * @param {Mirror} mirror Mirror to serialize. - * @return {Object} Protocol reference object. - */ -JSONProtocolSerializer.prototype.serializeReferenceWithDisplayData_ = - function(mirror) { - var o = {}; - o.ref = mirror.handle(); - o.type = mirror.type(); - switch (mirror.type()) { - case UNDEFINED_TYPE: - case NULL_TYPE: - case BOOLEAN_TYPE: - case NUMBER_TYPE: - o.value = mirror.value(); - break; - case STRING_TYPE: - // Limit string length. - o.value = mirror.toText(); - break; - case FUNCTION_TYPE: - o.name = mirror.name(); - o.inferredName = mirror.inferredName(); - if (mirror.script()) { - o.scriptId = mirror.script().id(); - } - break; - case ERROR_TYPE: - case REGEXP_TYPE: - o.value = mirror.toText(); - break; - case OBJECT_TYPE: - o.className = mirror.className(); - break; - } - return o; -}; - - -JSONProtocolSerializer.prototype.serialize_ = function(mirror, reference, - details) { - // If serializing a reference to a mirror just return the reference and add - // the mirror to the referenced mirrors. - if (reference && - (mirror.isValue() || mirror.isScript() || mirror.isContext())) { - if (this.inlineRefs_() && mirror.isValue()) { - return this.serializeReferenceWithDisplayData_(mirror); - } else { - this.add_(mirror); - return {'ref' : mirror.handle()}; - } - } - - // Collect the JSON property/value pairs. - var content = {}; - - // Add the mirror handle. - if (mirror.isValue() || mirror.isScript() || mirror.isContext()) { - content.handle = mirror.handle(); - } - - // Always add the type. - content.type = mirror.type(); - - switch (mirror.type()) { - case UNDEFINED_TYPE: - case NULL_TYPE: - // Undefined and null are represented just by their type. - break; - - case BOOLEAN_TYPE: - // Boolean values are simply represented by their value. - content.value = mirror.value(); - break; - - case NUMBER_TYPE: - // Number values are simply represented by their value. - content.value = NumberToJSON_(mirror.value()); - break; - - case STRING_TYPE: - // String values might have their value cropped to keep down size. - if (mirror.length() > kMaxProtocolStringLength) { - var str = mirror.value().substring(0, kMaxProtocolStringLength); - content.value = str; - content.fromIndex = 0; - content.toIndex = kMaxProtocolStringLength; - } else { - content.value = mirror.value(); - } - content.length = mirror.length(); - break; - - case OBJECT_TYPE: - case FUNCTION_TYPE: - case ERROR_TYPE: - case REGEXP_TYPE: - // Add object representation. - this.serializeObject_(mirror, content, details); - break; - - case PROPERTY_TYPE: - throw new Error('PropertyMirror cannot be serialized independeltly') - break; - - case FRAME_TYPE: - // Add object representation. - this.serializeFrame_(mirror, content); - break; - - case SCOPE_TYPE: - // Add object representation. - this.serializeScope_(mirror, content); - break; - - case SCRIPT_TYPE: - // Script is represented by id, name and source attributes. - if (mirror.name()) { - content.name = mirror.name(); - } - content.id = mirror.id(); - content.lineOffset = mirror.lineOffset(); - content.columnOffset = mirror.columnOffset(); - content.lineCount = mirror.lineCount(); - if (mirror.data()) { - content.data = mirror.data(); - } - if (this.includeSource_()) { - content.source = mirror.source(); - } else { - var sourceStart = mirror.source().substring(0, 80); - content.sourceStart = sourceStart; - } - content.sourceLength = mirror.source().length; - content.scriptType = mirror.scriptType(); - content.compilationType = mirror.compilationType(); - // For compilation type eval emit information on the script from which - // eval was called if a script is present. - if (mirror.compilationType() == 1 && - mirror.evalFromScript()) { - content.evalFromScript = - this.serializeReference(mirror.evalFromScript()); - var evalFromLocation = mirror.evalFromLocation() - if (evalFromLocation) { - content.evalFromLocation = { line: evalFromLocation.line, - column: evalFromLocation.column }; - } - if (mirror.evalFromFunctionName()) { - content.evalFromFunctionName = mirror.evalFromFunctionName(); - } - } - if (mirror.context()) { - content.context = this.serializeReference(mirror.context()); - } - break; - - case CONTEXT_TYPE: - content.data = mirror.data(); - break; - } - - // Always add the text representation. - content.text = mirror.toText(); - - // Create and return the JSON string. - return content; -} - - -/** - * Serialize object information to the following JSON format. - * - * {"className":"<class name>", - * "constructorFunction":{"ref":<number>}, - * "protoObject":{"ref":<number>}, - * "prototypeObject":{"ref":<number>}, - * "namedInterceptor":<boolean>, - * "indexedInterceptor":<boolean>, - * "properties":[<properties>]} - */ -JSONProtocolSerializer.prototype.serializeObject_ = function(mirror, content, - details) { - // Add general object properties. - content.className = mirror.className(); - content.constructorFunction = - this.serializeReference(mirror.constructorFunction()); - content.protoObject = this.serializeReference(mirror.protoObject()); - content.prototypeObject = this.serializeReference(mirror.prototypeObject()); - - // Add flags to indicate whether there are interceptors. - if (mirror.hasNamedInterceptor()) { - content.namedInterceptor = true; - } - if (mirror.hasIndexedInterceptor()) { - content.indexedInterceptor = true; - } - - // Add function specific properties. - if (mirror.isFunction()) { - // Add function specific properties. - content.name = mirror.name(); - if (!IS_UNDEFINED(mirror.inferredName())) { - content.inferredName = mirror.inferredName(); - } - content.resolved = mirror.resolved(); - if (mirror.resolved()) { - content.source = mirror.source(); - } - if (mirror.script()) { - content.script = this.serializeReference(mirror.script()); - content.scriptId = mirror.script().id(); - - serializeLocationFields(mirror.sourceLocation(), content); - } - } - - // Add date specific properties. - if (mirror.isDate()) { - // Add date specific properties. - content.value = mirror.value(); - } - - // Add actual properties - named properties followed by indexed properties. - var propertyNames = mirror.propertyNames(PropertyKind.Named); - var propertyIndexes = mirror.propertyNames(PropertyKind.Indexed); - var p = new Array(propertyNames.length + propertyIndexes.length); - for (var i = 0; i < propertyNames.length; i++) { - var propertyMirror = mirror.property(propertyNames[i]); - p[i] = this.serializeProperty_(propertyMirror); - if (details) { - this.add_(propertyMirror.value()); - } - } - for (var i = 0; i < propertyIndexes.length; i++) { - var propertyMirror = mirror.property(propertyIndexes[i]); - p[propertyNames.length + i] = this.serializeProperty_(propertyMirror); - if (details) { - this.add_(propertyMirror.value()); - } - } - content.properties = p; -} - - -/** - * Serialize location information to the following JSON format: - * - * "position":"<position>", - * "line":"<line>", - * "column":"<column>", - * - * @param {SourceLocation} location The location to serialize, may be undefined. - */ -function serializeLocationFields (location, content) { - if (!location) { - return; - } - content.position = location.position; - var line = location.line; - if (!IS_UNDEFINED(line)) { - content.line = line; - } - var column = location.column; - if (!IS_UNDEFINED(column)) { - content.column = column; - } -} - - -/** - * Serialize property information to the following JSON format for building the - * array of properties. - * - * {"name":"<property name>", - * "attributes":<number>, - * "propertyType":<number>, - * "ref":<number>} - * - * If the attribute for the property is PropertyAttribute.None it is not added. - * If the propertyType for the property is PropertyType.Normal it is not added. - * Here are a couple of examples. - * - * {"name":"hello","ref":1} - * {"name":"length","attributes":7,"propertyType":3,"ref":2} - * - * @param {PropertyMirror} propertyMirror The property to serialize. - * @returns {Object} Protocol object representing the property. - */ -JSONProtocolSerializer.prototype.serializeProperty_ = function(propertyMirror) { - var result = {}; - - result.name = propertyMirror.name(); - var propertyValue = propertyMirror.value(); - if (this.inlineRefs_() && propertyValue.isValue()) { - result.value = this.serializeReferenceWithDisplayData_(propertyValue); - } else { - if (propertyMirror.attributes() != PropertyAttribute.None) { - result.attributes = propertyMirror.attributes(); - } - if (propertyMirror.propertyType() != PropertyType.Normal) { - result.propertyType = propertyMirror.propertyType(); - } - result.ref = propertyValue.handle(); - } - return result; -} - - -JSONProtocolSerializer.prototype.serializeFrame_ = function(mirror, content) { - content.index = mirror.index(); - content.receiver = this.serializeReference(mirror.receiver()); - var func = mirror.func(); - content.func = this.serializeReference(func); - if (func.script()) { - content.script = this.serializeReference(func.script()); - } - content.constructCall = mirror.isConstructCall(); - content.debuggerFrame = mirror.isDebuggerFrame(); - var x = new Array(mirror.argumentCount()); - for (var i = 0; i < mirror.argumentCount(); i++) { - var arg = {}; - var argument_name = mirror.argumentName(i) - if (argument_name) { - arg.name = argument_name; - } - arg.value = this.serializeReference(mirror.argumentValue(i)); - x[i] = arg; - } - content.arguments = x; - var x = new Array(mirror.localCount()); - for (var i = 0; i < mirror.localCount(); i++) { - var local = {}; - local.name = mirror.localName(i); - local.value = this.serializeReference(mirror.localValue(i)); - x[i] = local; - } - content.locals = x; - serializeLocationFields(mirror.sourceLocation(), content); - var source_line_text = mirror.sourceLineText(); - if (!IS_UNDEFINED(source_line_text)) { - content.sourceLineText = source_line_text; - } - - content.scopes = []; - for (var i = 0; i < mirror.scopeCount(); i++) { - var scope = mirror.scope(i); - content.scopes.push({ - type: scope.scopeType(), - index: i - }); - } -} - - -JSONProtocolSerializer.prototype.serializeScope_ = function(mirror, content) { - content.index = mirror.scopeIndex(); - content.frameIndex = mirror.frameIndex(); - content.type = mirror.scopeType(); - content.object = this.inlineRefs_() ? - this.serializeValue(mirror.scopeObject()) : - this.serializeReference(mirror.scopeObject()); -} - - -/** - * Convert a number to a protocol value. For all finite numbers the number - * itself is returned. For non finite numbers NaN, Infinite and - * -Infinite the string representation "NaN", "Infinite" or "-Infinite" - * (not including the quotes) is returned. - * - * @param {number} value The number value to convert to a protocol value. - * @returns {number|string} Protocol value. - */ -function NumberToJSON_(value) { - if (isNaN(value)) { - return 'NaN'; - } - if (!isFinite(value)) { - if (value > 0) { - return 'Infinity'; - } else { - return '-Infinity'; - } - } - return value; -} diff --git a/src/mksnapshot.cc b/src/mksnapshot.cc index 37cf263d..a30b4507 100644 --- a/src/mksnapshot.cc +++ b/src/mksnapshot.cc @@ -205,7 +205,6 @@ class CppByteSink : public i::SnapshotByteSink { int main(int argc, char** argv) { - #ifdef ENABLE_LOGGING_AND_PROFILING // By default, log code create information in the snapshot. i::FLAG_log_code = true; diff --git a/src/number-info.h b/src/number-info.h new file mode 100644 index 00000000..c6f32e47 --- /dev/null +++ b/src/number-info.h @@ -0,0 +1,72 @@ +// Copyright 2010 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. + +#ifndef V8_NUMBER_INFO_H_ +#define V8_NUMBER_INFO_H_ + +namespace v8 { +namespace internal { + +class NumberInfo : public AllStatic { + public: + enum Type { + kUnknown = 0, + kNumber = 1, + kSmi = 3, + kHeapNumber = 5, + kUninitialized = 7 + }; + + // Return the weakest (least precise) common type. + static Type Combine(Type a, Type b) { + // Make use of the order of enum values. + return static_cast<Type>(a & b); + } + + static bool IsNumber(Type a) { + ASSERT(a != kUninitialized); + return ((a & kNumber) != 0); + } + + static const char* ToString(Type a) { + switch (a) { + case kUnknown: return "UnknownType"; + case kNumber: return "NumberType"; + case kSmi: return "SmiType"; + case kHeapNumber: return "HeapNumberType"; + case kUninitialized: + UNREACHABLE(); + return "UninitializedType"; + } + UNREACHABLE(); + return "Unreachable code"; + } +}; + +} } // namespace v8::internal + +#endif // V8_NUMBER_INFO_H_ diff --git a/src/objects-debug.cc b/src/objects-debug.cc index ded213b2..8f26f742 100644 --- a/src/objects-debug.cc +++ b/src/objects-debug.cc @@ -1033,6 +1033,8 @@ void FunctionTemplateInfo::FunctionTemplateInfoVerify() { void FunctionTemplateInfo::FunctionTemplateInfoPrint() { HeapObject::PrintHeader("FunctionTemplateInfo"); + PrintF("\n - class name: "); + class_name()->ShortPrint(); PrintF("\n - tag: "); tag()->ShortPrint(); PrintF("\n - property_list: "); diff --git a/src/objects.cc b/src/objects.cc index a8328ac0..99532aca 100644 --- a/src/objects.cc +++ b/src/objects.cc @@ -219,7 +219,7 @@ Object* JSObject::GetPropertyWithFailedAccessCheck( LookupResult* result, String* name, PropertyAttributes* attributes) { - if (result->IsValid()) { + if (result->IsProperty()) { switch (result->type()) { case CALLBACKS: { // Only allow API accessors. @@ -242,7 +242,7 @@ Object* JSObject::GetPropertyWithFailedAccessCheck( // Search ALL_CAN_READ accessors in prototype chain. LookupResult r; result->holder()->LookupRealNamedPropertyInPrototypes(name, &r); - if (r.IsValid()) { + if (r.IsProperty()) { return GetPropertyWithFailedAccessCheck(receiver, &r, name, @@ -255,16 +255,16 @@ Object* JSObject::GetPropertyWithFailedAccessCheck( // No access check in GetPropertyAttributeWithInterceptor. LookupResult r; result->holder()->LookupRealNamedProperty(name, &r); - if (r.IsValid()) { + if (r.IsProperty()) { return GetPropertyWithFailedAccessCheck(receiver, &r, name, attributes); } - } - default: { break; } + default: + UNREACHABLE(); } } @@ -280,7 +280,7 @@ PropertyAttributes JSObject::GetPropertyAttributeWithFailedAccessCheck( LookupResult* result, String* name, bool continue_search) { - if (result->IsValid()) { + if (result->IsProperty()) { switch (result->type()) { case CALLBACKS: { // Only allow API accessors. @@ -301,7 +301,7 @@ PropertyAttributes JSObject::GetPropertyAttributeWithFailedAccessCheck( // Search ALL_CAN_READ accessors in prototype chain. LookupResult r; result->holder()->LookupRealNamedPropertyInPrototypes(name, &r); - if (r.IsValid()) { + if (r.IsProperty()) { return GetPropertyAttributeWithFailedAccessCheck(receiver, &r, name, @@ -319,7 +319,7 @@ PropertyAttributes JSObject::GetPropertyAttributeWithFailedAccessCheck( } else { result->holder()->LocalLookupRealNamedProperty(name, &r); } - if (r.IsValid()) { + if (r.IsProperty()) { return GetPropertyAttributeWithFailedAccessCheck(receiver, &r, name, @@ -328,9 +328,8 @@ PropertyAttributes JSObject::GetPropertyAttributeWithFailedAccessCheck( break; } - default: { - break; - } + default: + UNREACHABLE(); } } @@ -456,7 +455,7 @@ Object* Object::GetProperty(Object* receiver, // holder will always be the interceptor holder and the search may // only continue with a current object just after the interceptor // holder in the prototype chain. - Object* last = result->IsValid() ? result->holder() : Heap::null_value(); + Object* last = result->IsProperty() ? result->holder() : Heap::null_value(); for (Object* current = this; true; current = current->GetPrototype()) { if (current->IsAccessCheckNeeded()) { // Check if we're allowed to read from the current object. Note @@ -1408,8 +1407,12 @@ Object* JSObject::SetPropertyPostInterceptor(String* name, // Check local property, ignore interceptor. LookupResult result; LocalLookupRealNamedProperty(name, &result); - if (result.IsValid()) return SetProperty(&result, name, value, attributes); - // Add real property. + if (result.IsFound()) { + // An existing property, a map transition or a null descriptor was + // found. Use set property to handle all these cases. + return SetProperty(&result, name, value, attributes); + } + // Add a new real property. return AddProperty(name, value, attributes); } @@ -1641,8 +1644,8 @@ void JSObject::LookupCallbackSetterInPrototypes(String* name, pt != Heap::null_value(); pt = pt->GetPrototype()) { JSObject::cast(pt)->LocalLookupRealNamedProperty(name, result); - if (result->IsValid()) { - if (!result->IsTransitionType() && result->IsReadOnly()) { + if (result->IsProperty()) { + if (result->IsReadOnly()) { result->NotFound(); return; } @@ -1703,7 +1706,11 @@ void JSObject::LocalLookupRealNamedProperty(String* name, if (HasFastProperties()) { LookupInDescriptor(name, result); - if (result->IsValid()) { + if (result->IsFound()) { + // A property, a map transition or a null descriptor was found. + // We return all of these result types because + // LocalLookupRealNamedProperty is used when setting properties + // where map transitions and null descriptors are handled. ASSERT(result->holder() == this && result->type() != NORMAL); // Disallow caching for uninitialized constants. These can only // occur as fields. @@ -1752,16 +1759,7 @@ void JSObject::LookupRealNamedPropertyInPrototypes(String* name, pt != Heap::null_value(); pt = JSObject::cast(pt)->GetPrototype()) { JSObject::cast(pt)->LocalLookupRealNamedProperty(name, result); - if (result->IsValid()) { - switch (result->type()) { - case NORMAL: - case FIELD: - case CONSTANT_FUNCTION: - case CALLBACKS: - return; - default: break; - } - } + if (result->IsProperty() && (result->type() != INTERCEPTOR)) return; } result->NotFound(); } @@ -1847,14 +1845,15 @@ Object* JSObject::SetProperty(LookupResult* result, // accessor that wants to handle the property. LookupResult accessor_result; LookupCallbackSetterInPrototypes(name, &accessor_result); - if (accessor_result.IsValid()) { + if (accessor_result.IsProperty()) { return SetPropertyWithCallback(accessor_result.GetCallbackObject(), name, value, accessor_result.holder()); } } - if (result->IsNotFound()) { + if (!result->IsFound()) { + // Neither properties nor transitions found. return AddProperty(name, value, attributes); } if (result->IsReadOnly() && result->IsProperty()) return value; @@ -1913,15 +1912,12 @@ Object* JSObject::IgnoreAttributesAndSetLocalProperty( // Make sure that the top context does not change when doing callbacks or // interceptor calls. AssertNoContextChange ncc; - // ADDED TO CLONE - LookupResult result_struct; - LocalLookup(name, &result_struct); - LookupResult* result = &result_struct; - // END ADDED TO CLONE + LookupResult result; + LocalLookup(name, &result); // Check access rights if needed. if (IsAccessCheckNeeded() - && !Top::MayNamedAccess(this, name, v8::ACCESS_SET)) { - return SetPropertyWithFailedAccessCheck(result, name, value); + && !Top::MayNamedAccess(this, name, v8::ACCESS_SET)) { + return SetPropertyWithFailedAccessCheck(&result, name, value); } if (IsJSGlobalProxy()) { @@ -1935,28 +1931,31 @@ Object* JSObject::IgnoreAttributesAndSetLocalProperty( } // Check for accessor in prototype chain removed here in clone. - if (result->IsNotFound()) { + if (!result.IsFound()) { + // Neither properties nor transitions found. return AddProperty(name, value, attributes); } + PropertyDetails details = PropertyDetails(attributes, NORMAL); + // Check of IsReadOnly removed from here in clone. - switch (result->type()) { + switch (result.type()) { case NORMAL: - return SetNormalizedProperty(result, value); + return SetNormalizedProperty(name, value, details); case FIELD: - return FastPropertyAtPut(result->GetFieldIndex(), value); + return FastPropertyAtPut(result.GetFieldIndex(), value); case MAP_TRANSITION: - if (attributes == result->GetAttributes()) { + if (attributes == result.GetAttributes()) { // Only use map transition if the attributes match. - return AddFastPropertyUsingMap(result->GetTransitionMap(), + return AddFastPropertyUsingMap(result.GetTransitionMap(), name, value); } return ConvertDescriptorToField(name, value, attributes); case CONSTANT_FUNCTION: // Only replace the function if necessary. - if (value == result->GetConstantFunction()) return value; + if (value == result.GetConstantFunction()) return value; // Preserve the attributes of this existing property. - attributes = result->GetAttributes(); + attributes = result.GetAttributes(); return ConvertDescriptorToField(name, value, attributes); case CALLBACKS: case INTERCEPTOR: @@ -2072,7 +2071,7 @@ PropertyAttributes JSObject::GetPropertyAttribute(JSObject* receiver, name, continue_search); } - if (result->IsValid()) { + if (result->IsProperty()) { switch (result->type()) { case NORMAL: // fall through case FIELD: @@ -2082,13 +2081,8 @@ PropertyAttributes JSObject::GetPropertyAttribute(JSObject* receiver, case INTERCEPTOR: return result->holder()-> GetPropertyAttributeWithInterceptor(receiver, name, continue_search); - case MAP_TRANSITION: - case CONSTANT_TRANSITION: - case NULL_DESCRIPTOR: - return ABSENT; default: UNREACHABLE(); - break; } } return ABSENT; @@ -2261,7 +2255,7 @@ Object* JSObject::DeletePropertyPostInterceptor(String* name, DeleteMode mode) { // Check local property, ignore interceptor. LookupResult result; LocalLookupRealNamedProperty(name, &result); - if (!result.IsValid()) return Heap::true_value(); + if (!result.IsProperty()) return Heap::true_value(); // Normalize object if needed. Object* obj = NormalizeProperties(CLEAR_INOBJECT_PROPERTIES, 0); @@ -2445,7 +2439,7 @@ Object* JSObject::DeleteProperty(String* name, DeleteMode mode) { } else { LookupResult result; LocalLookup(name, &result); - if (!result.IsValid()) return Heap::true_value(); + if (!result.IsProperty()) return Heap::true_value(); // Ignore attributes if forcing a deletion. if (result.IsDontDelete() && mode != FORCE_DELETION) { return Heap::false_value(); @@ -2675,7 +2669,7 @@ void JSObject::Lookup(String* name, LookupResult* result) { current != Heap::null_value(); current = JSObject::cast(current)->GetPrototype()) { JSObject::cast(current)->LocalLookup(name, result); - if (result->IsValid() && !result->IsTransitionType()) return; + if (result->IsProperty()) return; } result->NotFound(); } @@ -2687,7 +2681,7 @@ void JSObject::LookupCallback(String* name, LookupResult* result) { current != Heap::null_value(); current = JSObject::cast(current)->GetPrototype()) { JSObject::cast(current)->LocalLookupRealNamedProperty(name, result); - if (result->IsValid() && result->type() == CALLBACKS) return; + if (result->IsProperty() && result->type() == CALLBACKS) return; } result->NotFound(); } @@ -2717,7 +2711,7 @@ Object* JSObject::DefineGetterSetter(String* name, // cause security problems. LookupResult callback_result; LookupCallback(name, &callback_result); - if (callback_result.IsValid()) { + if (callback_result.IsFound()) { Object* obj = callback_result.GetCallbackObject(); if (obj->IsAccessorInfo() && AccessorInfo::cast(obj)->prohibits_overwriting()) { @@ -2768,11 +2762,16 @@ Object* JSObject::DefineGetterSetter(String* name, // Lookup the name. LookupResult result; LocalLookup(name, &result); - if (result.IsValid()) { + if (result.IsProperty()) { if (result.IsReadOnly()) return Heap::undefined_value(); if (result.type() == CALLBACKS) { Object* obj = result.GetCallbackObject(); if (obj->IsFixedArray()) { + // The object might be in fast mode even though it has + // a getter/setter. + Object* ok = NormalizeProperties(CLEAR_INOBJECT_PROPERTIES, 0); + if (ok->IsFailure()) return ok; + PropertyDetails details = PropertyDetails(attributes, CALLBACKS); SetNormalizedProperty(name, obj, details); return obj; @@ -2885,7 +2884,7 @@ Object* JSObject::LookupAccessor(String* name, bool is_getter) { obj = JSObject::cast(obj)->GetPrototype()) { LookupResult result; JSObject::cast(obj)->LocalLookup(name, &result); - if (result.IsValid()) { + if (result.IsProperty()) { if (result.IsReadOnly()) return Heap::undefined_value(); if (result.type() == CALLBACKS) { Object* obj = result.GetCallbackObject(); @@ -4753,6 +4752,40 @@ int SharedFunctionInfo::CalculateInObjectProperties() { } +bool SharedFunctionInfo::CanGenerateInlineConstructor(Object* prototype) { + // Check the basic conditions for generating inline constructor code. + if (!FLAG_inline_new + || !has_only_simple_this_property_assignments() + || this_property_assignments_count() == 0) { + return false; + } + + // If the prototype is null inline constructors cause no problems. + if (!prototype->IsJSObject()) { + ASSERT(prototype->IsNull()); + return true; + } + + // Traverse the proposed prototype chain looking for setters for properties of + // the same names as are set by the inline constructor. + for (Object* obj = prototype; + obj != Heap::null_value(); + obj = obj->GetPrototype()) { + JSObject* js_object = JSObject::cast(obj); + for (int i = 0; i < this_property_assignments_count(); i++) { + LookupResult result; + String* name = GetThisPropertyAssignmentName(i); + js_object->LocalLookupRealNamedProperty(name, &result); + if (result.IsProperty() && result.type() == CALLBACKS) { + return false; + } + } + } + + return true; +} + + void SharedFunctionInfo::SetThisPropertyAssignmentsInfo( bool only_simple_this_property_assignments, FixedArray* assignments) { @@ -4808,7 +4841,6 @@ Object* SharedFunctionInfo::GetThisPropertyAssignmentConstant(int index) { } - // Support function for printing the source code to a StringStream // without any allocation in the heap. void SharedFunctionInfo::SourceCodePrint(StringStream* accumulator, @@ -5286,6 +5318,48 @@ Object* JSObject::SetElementsLength(Object* len) { } +Object* JSObject::SetPrototype(Object* value, + bool skip_hidden_prototypes) { + // Silently ignore the change if value is not a JSObject or null. + // SpiderMonkey behaves this way. + if (!value->IsJSObject() && !value->IsNull()) return value; + + // Before we can set the prototype we need to be sure + // prototype cycles are prevented. + // It is sufficient to validate that the receiver is not in the new prototype + // chain. + for (Object* pt = value; pt != Heap::null_value(); pt = pt->GetPrototype()) { + if (JSObject::cast(pt) == this) { + // Cycle detected. + HandleScope scope; + return Top::Throw(*Factory::NewError("cyclic_proto", + HandleVector<Object>(NULL, 0))); + } + } + + JSObject* real_receiver = this; + + if (skip_hidden_prototypes) { + // Find the first object in the chain whose prototype object is not + // hidden and set the new prototype on that object. + Object* current_proto = real_receiver->GetPrototype(); + while (current_proto->IsJSObject() && + JSObject::cast(current_proto)->map()->is_hidden_prototype()) { + real_receiver = JSObject::cast(current_proto); + current_proto = current_proto->GetPrototype(); + } + } + + // Set the new prototype of the object. + Object* new_map = real_receiver->map()->CopyDropTransitions(); + if (new_map->IsFailure()) return new_map; + Map::cast(new_map)->set_prototype(value); + real_receiver->set_map(Map::cast(new_map)); + + return value; +} + + bool JSObject::HasElementPostInterceptor(JSObject* receiver, uint32_t index) { switch (GetElementsKind()) { case FAST_ELEMENTS: { @@ -6103,7 +6177,9 @@ Object* JSObject::GetPropertyPostInterceptor(JSObject* receiver, // Check local property in holder, ignore interceptor. LookupResult result; LocalLookupRealNamedProperty(name, &result); - if (result.IsValid()) return GetProperty(receiver, &result, name, attributes); + if (result.IsProperty()) { + return GetProperty(receiver, &result, name, attributes); + } // Continue searching via the prototype chain. Object* pt = GetPrototype(); *attributes = ABSENT; @@ -6119,8 +6195,10 @@ Object* JSObject::GetLocalPropertyPostInterceptor( // Check local property in holder, ignore interceptor. LookupResult result; LocalLookupRealNamedProperty(name, &result); - if (!result.IsValid()) return Heap::undefined_value(); - return GetProperty(receiver, &result, name, attributes); + if (result.IsProperty()) { + return GetProperty(receiver, &result, name, attributes); + } + return Heap::undefined_value(); } @@ -6172,24 +6250,7 @@ bool JSObject::HasRealNamedProperty(String* key) { LookupResult result; LocalLookupRealNamedProperty(key, &result); - if (result.IsValid()) { - switch (result.type()) { - case NORMAL: // fall through. - case FIELD: // fall through. - case CALLBACKS: // fall through. - case CONSTANT_FUNCTION: - return true; - case INTERCEPTOR: - case MAP_TRANSITION: - case CONSTANT_TRANSITION: - case NULL_DESCRIPTOR: - return false; - default: - UNREACHABLE(); - } - } - - return false; + return result.IsProperty() && (result.type() != INTERCEPTOR); } @@ -6251,7 +6312,7 @@ bool JSObject::HasRealNamedCallbackProperty(String* key) { LookupResult result; LocalLookupRealNamedProperty(key, &result); - return result.IsValid() && (result.type() == CALLBACKS); + return result.IsProperty() && (result.type() == CALLBACKS); } diff --git a/src/objects.h b/src/objects.h index 48936661..00304968 100644 --- a/src/objects.h +++ b/src/objects.h @@ -179,7 +179,7 @@ class PropertyDetails BASE_EMBEDDED { class TypeField: public BitField<PropertyType, 0, 3> {}; class AttributesField: public BitField<PropertyAttributes, 3, 3> {}; class DeletedField: public BitField<uint32_t, 6, 1> {}; - class IndexField: public BitField<uint32_t, 7, 31-7> {}; + class IndexField: public BitField<uint32_t, 7, 32-7> {}; static const int kInitialIndex = 1; private: @@ -1301,6 +1301,9 @@ class JSObject: public HeapObject { // Return the object's prototype (might be Heap::null_value()). inline Object* GetPrototype(); + // Set the object's prototype (only JSObject and null are allowed). + Object* SetPrototype(Object* value, bool skip_hidden_prototypes); + // Tells whether the index'th element is present. inline bool HasElement(uint32_t index); bool HasElementWithReceiver(JSObject* receiver, uint32_t index); @@ -3200,6 +3203,10 @@ class SharedFunctionInfo: public HeapObject { inline bool try_full_codegen(); inline void set_try_full_codegen(bool flag); + // Check whether a inlined constructor can be generated with the given + // prototype. + bool CanGenerateInlineConstructor(Object* prototype); + // For functions which only contains this property assignments this provides // access to the names for the properties assigned. DECL_ACCESSORS(this_property_assignments, Object) diff --git a/src/parser.cc b/src/parser.cc index b06d86f5..5058296d 100644 --- a/src/parser.cc +++ b/src/parser.cc @@ -1690,7 +1690,8 @@ void* Parser::ParseSourceElements(ZoneListWrapper<Statement>* processor, // Propagate the collected information on this property assignments. if (top_scope_->is_function_scope()) { bool only_simple_this_property_assignments = - this_property_assignment_finder.only_simple_this_property_assignments(); + this_property_assignment_finder.only_simple_this_property_assignments() + && top_scope_->declarations()->length() == 0; if (only_simple_this_property_assignments) { temp_scope_->SetThisPropertyAssignmentInfo( only_simple_this_property_assignments, diff --git a/src/platform-freebsd.cc b/src/platform-freebsd.cc index ff757768..a9787776 100644 --- a/src/platform-freebsd.cc +++ b/src/platform-freebsd.cc @@ -192,7 +192,8 @@ void OS::Abort() { void OS::DebugBreak() { -#if defined(__arm__) || defined(__thumb__) +#if (defined(__arm__) || defined(__thumb__)) && \ + defined(CAN_USE_ARMV5_INSTRUCTIONS) asm("bkpt 0"); #else asm("int $3"); diff --git a/src/platform-linux.cc b/src/platform-linux.cc index ef4ae17b..8cc513d8 100644 --- a/src/platform-linux.cc +++ b/src/platform-linux.cc @@ -266,7 +266,8 @@ void OS::Abort() { void OS::DebugBreak() { // TODO(lrn): Introduce processor define for runtime system (!= V8_ARCH_x, // which is the architecture of generated code). -#if defined(__arm__) || defined(__thumb__) +#if (defined(__arm__) || defined(__thumb__)) && \ + defined(CAN_USE_ARMV5_INSTRUCTIONS) asm("bkpt 0"); #elif defined(__mips__) asm("break"); @@ -721,11 +722,9 @@ static inline bool IsVmThread() { static void ProfilerSignalHandler(int signal, siginfo_t* info, void* context) { - return; -/*#ifndef V8_HOST_ARCH_MIPS +#ifndef V8_HOST_ARCH_MIPS USE(info); if (signal != SIGPROF) return; - if (!IsVmThread()) return; if (active_sampler_ == NULL) return; TickSample sample; @@ -766,7 +765,7 @@ static void ProfilerSignalHandler(int signal, siginfo_t* info, void* context) { sample.state = Logger::state(); active_sampler_->Tick(&sample); -#endif*/ +#endif } @@ -805,7 +804,7 @@ void Sampler::Start() { sa.sa_sigaction = ProfilerSignalHandler; sigemptyset(&sa.sa_mask); sa.sa_flags = SA_SIGINFO; - if (sigaction(SIGALRM, &sa, &data_->old_signal_handler_) != 0) return; + if (sigaction(SIGPROF, &sa, &data_->old_signal_handler_) != 0) return; data_->signal_handler_installed_ = true; // Set the itimer to generate a tick for each interval. @@ -814,7 +813,7 @@ void Sampler::Start() { itimer.it_interval.tv_usec = (interval_ % 1000) * 1000; itimer.it_value.tv_sec = itimer.it_interval.tv_sec; itimer.it_value.tv_usec = itimer.it_interval.tv_usec; - setitimer(ITIMER_REAL, &itimer, &data_->old_timer_value_); + setitimer(ITIMER_PROF, &itimer, &data_->old_timer_value_); // Set this sampler as the active sampler. active_sampler_ = this; diff --git a/src/platform-openbsd.cc b/src/platform-openbsd.cc index 62e60044..f96e769c 100644 --- a/src/platform-openbsd.cc +++ b/src/platform-openbsd.cc @@ -190,7 +190,8 @@ void OS::Abort() { void OS::DebugBreak() { -#if defined(__arm__) || defined(__thumb__) +#if (defined(__arm__) || defined(__thumb__)) && \ + defined(CAN_USE_ARMV5_INSTRUCTIONS) asm("bkpt 0"); #else asm("int $3"); diff --git a/src/property.cc b/src/property.cc index caa73975..b579b687 100644 --- a/src/property.cc +++ b/src/property.cc @@ -33,7 +33,7 @@ namespace internal { #ifdef DEBUG void LookupResult::Print() { - if (!IsValid()) { + if (!IsFound()) { PrintF("Not Found\n"); return; } diff --git a/src/property.h b/src/property.h index b993af11..15a56528 100644 --- a/src/property.h +++ b/src/property.h @@ -201,23 +201,17 @@ class LookupResult BASE_EMBEDDED { } JSObject* holder() { - ASSERT(IsValid()); + ASSERT(IsFound()); return holder_; } PropertyType type() { - ASSERT(IsValid()); + ASSERT(IsFound()); return details_.type(); } - bool IsTransitionType() { - PropertyType t = type(); - if (t == MAP_TRANSITION || t == CONSTANT_TRANSITION) return true; - return false; - } - PropertyAttributes GetAttributes() { - ASSERT(IsValid()); + ASSERT(IsFound()); return details_.attributes(); } @@ -229,14 +223,17 @@ class LookupResult BASE_EMBEDDED { bool IsDontDelete() { return details_.IsDontDelete(); } bool IsDontEnum() { return details_.IsDontEnum(); } bool IsDeleted() { return details_.IsDeleted(); } + bool IsFound() { return lookup_type_ != NOT_FOUND; } - bool IsValid() { return lookup_type_ != NOT_FOUND; } - bool IsNotFound() { return lookup_type_ == NOT_FOUND; } - - // Tells whether the result is a property. - // Excluding transitions and the null descriptor. + // Is the result is a property excluding transitions and the null + // descriptor? bool IsProperty() { - return IsValid() && type() < FIRST_PHANTOM_PROPERTY_TYPE; + return IsFound() && (type() < FIRST_PHANTOM_PROPERTY_TYPE); + } + + // Is the result a property or a transition? + bool IsPropertyOrTransition() { + return IsFound() && (type() != NULL_DESCRIPTOR); } bool IsCacheable() { return cacheable_; } diff --git a/src/regexp-delay.js b/src/regexp-delay.js deleted file mode 100644 index 7bec455d..00000000 --- a/src/regexp-delay.js +++ /dev/null @@ -1,406 +0,0 @@ -// Copyright 2006-2009 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. - -// Expect $Object = global.Object; -// Expect $Array = global.Array; - -const $RegExp = global.RegExp; - -// A recursive descent parser for Patterns according to the grammar of -// ECMA-262 15.10.1, with deviations noted below. -function DoConstructRegExp(object, pattern, flags, isConstructorCall) { - // RegExp : Called as constructor; see ECMA-262, section 15.10.4. - if (IS_REGEXP(pattern)) { - if (!IS_UNDEFINED(flags)) { - throw MakeTypeError('regexp_flags', []); - } - flags = (pattern.global ? 'g' : '') - + (pattern.ignoreCase ? 'i' : '') - + (pattern.multiline ? 'm' : ''); - pattern = pattern.source; - } - - pattern = IS_UNDEFINED(pattern) ? '' : ToString(pattern); - flags = IS_UNDEFINED(flags) ? '' : ToString(flags); - - var global = false; - var ignoreCase = false; - var multiline = false; - - for (var i = 0; i < flags.length; i++) { - var c = StringCharAt.call(flags, i); - switch (c) { - case 'g': - // Allow duplicate flags to be consistent with JSC and others. - global = true; - break; - case 'i': - ignoreCase = true; - break; - case 'm': - multiline = true; - break; - default: - // Ignore flags that have no meaning to be consistent with - // JSC. - break; - } - } - - if (isConstructorCall) { - // ECMA-262, section 15.10.7.1. - %SetProperty(object, 'source', pattern, - DONT_DELETE | READ_ONLY | DONT_ENUM); - - // ECMA-262, section 15.10.7.2. - %SetProperty(object, 'global', global, DONT_DELETE | READ_ONLY | DONT_ENUM); - - // ECMA-262, section 15.10.7.3. - %SetProperty(object, 'ignoreCase', ignoreCase, - DONT_DELETE | READ_ONLY | DONT_ENUM); - - // ECMA-262, section 15.10.7.4. - %SetProperty(object, 'multiline', multiline, - DONT_DELETE | READ_ONLY | DONT_ENUM); - - // ECMA-262, section 15.10.7.5. - %SetProperty(object, 'lastIndex', 0, DONT_DELETE | DONT_ENUM); - } else { // RegExp is being recompiled via RegExp.prototype.compile. - %IgnoreAttributesAndSetProperty(object, 'source', pattern); - %IgnoreAttributesAndSetProperty(object, 'global', global); - %IgnoreAttributesAndSetProperty(object, 'ignoreCase', ignoreCase); - %IgnoreAttributesAndSetProperty(object, 'multiline', multiline); - %IgnoreAttributesAndSetProperty(object, 'lastIndex', 0); - } - - // Call internal function to compile the pattern. - %RegExpCompile(object, pattern, flags); -} - - -function RegExpConstructor(pattern, flags) { - if (%_IsConstructCall()) { - DoConstructRegExp(this, pattern, flags, true); - } else { - // RegExp : Called as function; see ECMA-262, section 15.10.3.1. - if (IS_REGEXP(pattern) && IS_UNDEFINED(flags)) { - return pattern; - } - return new $RegExp(pattern, flags); - } -} - - -// Deprecated RegExp.prototype.compile method. We behave like the constructor -// were called again. In SpiderMonkey, this method returns the regexp object. -// In JSC, it returns undefined. For compatibility with JSC, we match their -// behavior. -function CompileRegExp(pattern, flags) { - // Both JSC and SpiderMonkey treat a missing pattern argument as the - // empty subject string, and an actual undefined value passed as the - // pattern as the string 'undefined'. Note that JSC is inconsistent - // here, treating undefined values differently in - // RegExp.prototype.compile and in the constructor, where they are - // the empty string. For compatibility with JSC, we match their - // behavior. - if (IS_UNDEFINED(pattern) && %_ArgumentsLength() != 0) { - DoConstructRegExp(this, 'undefined', flags, false); - } else { - DoConstructRegExp(this, pattern, flags, false); - } -} - - -function DoRegExpExec(regexp, string, index) { - return %_RegExpExec(regexp, string, index, lastMatchInfo); -} - - -function RegExpExec(string) { - if (!IS_REGEXP(this)) { - throw MakeTypeError('method_called_on_incompatible', - ['RegExp.prototype.exec', this]); - } - if (%_ArgumentsLength() == 0) { - var regExpInput = LAST_INPUT(lastMatchInfo); - if (IS_UNDEFINED(regExpInput)) { - throw MakeError('no_input_to_regexp', [this]); - } - string = regExpInput; - } - var s = ToString(string); - var length = s.length; - var lastIndex = this.lastIndex; - var i = this.global ? TO_INTEGER(lastIndex) : 0; - - if (i < 0 || i > s.length) { - this.lastIndex = 0; - return null; - } - - %_Log('regexp', 'regexp-exec,%0r,%1S,%2i', [this, s, lastIndex]); - // matchIndices is either null or the lastMatchInfo array. - var matchIndices = %_RegExpExec(this, s, i, lastMatchInfo); - - if (matchIndices == null) { - if (this.global) this.lastIndex = 0; - return matchIndices; // no match - } - - var numResults = NUMBER_OF_CAPTURES(lastMatchInfo) >> 1; - var result = new $Array(numResults); - for (var i = 0; i < numResults; i++) { - var matchStart = lastMatchInfo[CAPTURE(i << 1)]; - var matchEnd = lastMatchInfo[CAPTURE((i << 1) + 1)]; - if (matchStart != -1 && matchEnd != -1) { - result[i] = SubString(s, matchStart, matchEnd); - } else { - // Make sure the element is present. Avoid reading the undefined - // property from the global object since this may change. - result[i] = void 0; - } - } - - if (this.global) - this.lastIndex = lastMatchInfo[CAPTURE1]; - result.index = lastMatchInfo[CAPTURE0]; - result.input = s; - return result; -} - - -// Section 15.10.6.3 doesn't actually make sense, but the intention seems to be -// that test is defined in terms of String.prototype.exec. However, it probably -// means the original value of String.prototype.exec, which is what everybody -// else implements. -function RegExpTest(string) { - if (!IS_REGEXP(this)) { - throw MakeTypeError('method_called_on_incompatible', - ['RegExp.prototype.test', this]); - } - if (%_ArgumentsLength() == 0) { - var regExpInput = LAST_INPUT(lastMatchInfo); - if (IS_UNDEFINED(regExpInput)) { - throw MakeError('no_input_to_regexp', [this]); - } - string = regExpInput; - } - var s = ToString(string); - var length = s.length; - var lastIndex = this.lastIndex; - var i = this.global ? TO_INTEGER(lastIndex) : 0; - - if (i < 0 || i > s.length) { - this.lastIndex = 0; - return false; - } - - %_Log('regexp', 'regexp-exec,%0r,%1S,%2i', [this, s, lastIndex]); - // matchIndices is either null or the lastMatchInfo array. - var matchIndices = %_RegExpExec(this, s, i, lastMatchInfo); - - if (matchIndices == null) { - if (this.global) this.lastIndex = 0; - return false; - } - - if (this.global) this.lastIndex = lastMatchInfo[CAPTURE1]; - return true; -} - - -function RegExpToString() { - // If this.source is an empty string, output /(?:)/. - // http://bugzilla.mozilla.org/show_bug.cgi?id=225550 - // ecma_2/RegExp/properties-001.js. - var src = this.source ? this.source : '(?:)'; - var result = '/' + src + '/'; - if (this.global) - result += 'g'; - if (this.ignoreCase) - result += 'i'; - if (this.multiline) - result += 'm'; - return result; -} - - -// Getters for the static properties lastMatch, lastParen, leftContext, and -// rightContext of the RegExp constructor. The properties are computed based -// on the captures array of the last successful match and the subject string -// of the last successful match. -function RegExpGetLastMatch() { - var regExpSubject = LAST_SUBJECT(lastMatchInfo); - return SubString(regExpSubject, - lastMatchInfo[CAPTURE0], - lastMatchInfo[CAPTURE1]); -} - - -function RegExpGetLastParen() { - var length = NUMBER_OF_CAPTURES(lastMatchInfo); - if (length <= 2) return ''; // There were no captures. - // We match the SpiderMonkey behavior: return the substring defined by the - // last pair (after the first pair) of elements of the capture array even if - // it is empty. - var regExpSubject = LAST_SUBJECT(lastMatchInfo); - var start = lastMatchInfo[CAPTURE(length - 2)]; - var end = lastMatchInfo[CAPTURE(length - 1)]; - if (start != -1 && end != -1) { - return SubString(regExpSubject, start, end); - } - return ""; -} - - -function RegExpGetLeftContext() { - return SubString(LAST_SUBJECT(lastMatchInfo), - 0, - lastMatchInfo[CAPTURE0]); -} - - -function RegExpGetRightContext() { - var subject = LAST_SUBJECT(lastMatchInfo); - return SubString(subject, - lastMatchInfo[CAPTURE1], - subject.length); -} - - -// The properties $1..$9 are the first nine capturing substrings of the last -// successful match, or ''. The function RegExpMakeCaptureGetter will be -// called with indices from 1 to 9. -function RegExpMakeCaptureGetter(n) { - return function() { - var index = n * 2; - if (index >= NUMBER_OF_CAPTURES(lastMatchInfo)) return ''; - var matchStart = lastMatchInfo[CAPTURE(index)]; - var matchEnd = lastMatchInfo[CAPTURE(index + 1)]; - if (matchStart == -1 || matchEnd == -1) return ''; - return SubString(LAST_SUBJECT(lastMatchInfo), matchStart, matchEnd); - }; -} - - -// Property of the builtins object for recording the result of the last -// regexp match. The property lastMatchInfo includes the matchIndices -// array of the last successful regexp match (an array of start/end index -// pairs for the match and all the captured substrings), the invariant is -// that there are at least two capture indeces. The array also contains -// the subject string for the last successful match. -var lastMatchInfo = [ - 2, // REGEXP_NUMBER_OF_CAPTURES - "", // Last subject. - void 0, // Last input - settable with RegExpSetInput. - 0, // REGEXP_FIRST_CAPTURE + 0 - 0, // REGEXP_FIRST_CAPTURE + 1 -]; - -// ------------------------------------------------------------------- - -function SetupRegExp() { - %FunctionSetInstanceClassName($RegExp, 'RegExp'); - %FunctionSetPrototype($RegExp, new $Object()); - %SetProperty($RegExp.prototype, 'constructor', $RegExp, DONT_ENUM); - %SetCode($RegExp, RegExpConstructor); - - InstallFunctions($RegExp.prototype, DONT_ENUM, $Array( - "exec", RegExpExec, - "test", RegExpTest, - "toString", RegExpToString, - "compile", CompileRegExp - )); - - // The length of compile is 1 in SpiderMonkey. - %FunctionSetLength($RegExp.prototype.compile, 1); - - // The properties input, $input, and $_ are aliases for each other. When this - // value is set the value it is set to is coerced to a string. - // Getter and setter for the input. - function RegExpGetInput() { - var regExpInput = LAST_INPUT(lastMatchInfo); - return IS_UNDEFINED(regExpInput) ? "" : regExpInput; - } - function RegExpSetInput(string) { - LAST_INPUT(lastMatchInfo) = ToString(string); - }; - - %DefineAccessor($RegExp, 'input', GETTER, RegExpGetInput, DONT_DELETE); - %DefineAccessor($RegExp, 'input', SETTER, RegExpSetInput, DONT_DELETE); - %DefineAccessor($RegExp, '$_', GETTER, RegExpGetInput, DONT_ENUM | DONT_DELETE); - %DefineAccessor($RegExp, '$_', SETTER, RegExpSetInput, DONT_ENUM | DONT_DELETE); - %DefineAccessor($RegExp, '$input', GETTER, RegExpGetInput, DONT_ENUM | DONT_DELETE); - %DefineAccessor($RegExp, '$input', SETTER, RegExpSetInput, DONT_ENUM | DONT_DELETE); - - // The properties multiline and $* are aliases for each other. When this - // value is set in SpiderMonkey, the value it is set to is coerced to a - // boolean. We mimic that behavior with a slight difference: in SpiderMonkey - // the value of the expression 'RegExp.multiline = null' (for instance) is the - // boolean false (ie, the value after coercion), while in V8 it is the value - // null (ie, the value before coercion). - - // Getter and setter for multiline. - var multiline = false; - function RegExpGetMultiline() { return multiline; }; - function RegExpSetMultiline(flag) { multiline = flag ? true : false; }; - - %DefineAccessor($RegExp, 'multiline', GETTER, RegExpGetMultiline, DONT_DELETE); - %DefineAccessor($RegExp, 'multiline', SETTER, RegExpSetMultiline, DONT_DELETE); - %DefineAccessor($RegExp, '$*', GETTER, RegExpGetMultiline, DONT_ENUM | DONT_DELETE); - %DefineAccessor($RegExp, '$*', SETTER, RegExpSetMultiline, DONT_ENUM | DONT_DELETE); - - - function NoOpSetter(ignored) {} - - - // Static properties set by a successful match. - %DefineAccessor($RegExp, 'lastMatch', GETTER, RegExpGetLastMatch, DONT_DELETE); - %DefineAccessor($RegExp, 'lastMatch', SETTER, NoOpSetter, DONT_DELETE); - %DefineAccessor($RegExp, '$&', GETTER, RegExpGetLastMatch, DONT_ENUM | DONT_DELETE); - %DefineAccessor($RegExp, '$&', SETTER, NoOpSetter, DONT_ENUM | DONT_DELETE); - %DefineAccessor($RegExp, 'lastParen', GETTER, RegExpGetLastParen, DONT_DELETE); - %DefineAccessor($RegExp, 'lastParen', SETTER, NoOpSetter, DONT_DELETE); - %DefineAccessor($RegExp, '$+', GETTER, RegExpGetLastParen, DONT_ENUM | DONT_DELETE); - %DefineAccessor($RegExp, '$+', SETTER, NoOpSetter, DONT_ENUM | DONT_DELETE); - %DefineAccessor($RegExp, 'leftContext', GETTER, RegExpGetLeftContext, DONT_DELETE); - %DefineAccessor($RegExp, 'leftContext', SETTER, NoOpSetter, DONT_DELETE); - %DefineAccessor($RegExp, '$`', GETTER, RegExpGetLeftContext, DONT_ENUM | DONT_DELETE); - %DefineAccessor($RegExp, '$`', SETTER, NoOpSetter, DONT_ENUM | DONT_DELETE); - %DefineAccessor($RegExp, 'rightContext', GETTER, RegExpGetRightContext, DONT_DELETE); - %DefineAccessor($RegExp, 'rightContext', SETTER, NoOpSetter, DONT_DELETE); - %DefineAccessor($RegExp, "$'", GETTER, RegExpGetRightContext, DONT_ENUM | DONT_DELETE); - %DefineAccessor($RegExp, "$'", SETTER, NoOpSetter, DONT_ENUM | DONT_DELETE); - - for (var i = 1; i < 10; ++i) { - %DefineAccessor($RegExp, '$' + i, GETTER, RegExpMakeCaptureGetter(i), DONT_DELETE); - %DefineAccessor($RegExp, '$' + i, SETTER, NoOpSetter, DONT_DELETE); - } -} - - -SetupRegExp(); diff --git a/src/register-allocator-inl.h b/src/register-allocator-inl.h index b3fa4747..a99f4550 100644 --- a/src/register-allocator-inl.h +++ b/src/register-allocator-inl.h @@ -48,6 +48,20 @@ namespace v8 { namespace internal { +Result::Result(const Result& other) { + other.CopyTo(this); +} + + +Result& Result::operator=(const Result& other) { + if (this != &other) { + Unuse(); + other.CopyTo(this); + } + return *this; +} + + Result::~Result() { if (is_register()) { CodeGeneratorScope::Current()->allocator()->Unuse(reg()); @@ -71,6 +85,25 @@ void Result::CopyTo(Result* destination) const { } +bool RegisterAllocator::is_used(Register reg) { + return registers_.is_used(ToNumber(reg)); +} + + +int RegisterAllocator::count(Register reg) { + return registers_.count(ToNumber(reg)); +} + + +void RegisterAllocator::Use(Register reg) { + registers_.Use(ToNumber(reg)); +} + + +void RegisterAllocator::Unuse(Register reg) { + registers_.Unuse(ToNumber(reg)); +} + } } // namespace v8::internal #endif // V8_REGISTER_ALLOCATOR_INL_H_ diff --git a/src/register-allocator.cc b/src/register-allocator.cc index d55f949d..349cc246 100644 --- a/src/register-allocator.cc +++ b/src/register-allocator.cc @@ -37,10 +37,12 @@ namespace internal { // Result implementation. -Result::Result(Register reg) { +Result::Result(Register reg, NumberInfo::Type info) { ASSERT(reg.is_valid() && !RegisterAllocator::IsReserved(reg)); CodeGeneratorScope::Current()->allocator()->Use(reg); - value_ = TypeField::encode(REGISTER) | DataField::encode(reg.code_); + value_ = TypeField::encode(REGISTER) + | NumberInfoField::encode(info) + | DataField::encode(reg.code_); } @@ -50,6 +52,23 @@ Result::ZoneObjectList* Result::ConstantList() { } +NumberInfo::Type Result::number_info() { + ASSERT(is_valid()); + if (!is_constant()) return NumberInfoField::decode(value_); + Handle<Object> value = handle(); + if (value->IsSmi()) return NumberInfo::kSmi; + if (value->IsHeapNumber()) return NumberInfo::kHeapNumber; + return NumberInfo::kUnknown; +} + + +void Result::set_number_info(NumberInfo::Type info) { + ASSERT(is_valid()); + value_ = value_ & ~NumberInfoField::mask(); + value_ = value_ | NumberInfoField::encode(info); +} + + // ------------------------------------------------------------------------- // RegisterAllocator implementation. diff --git a/src/register-allocator.h b/src/register-allocator.h index 0f46996c..747200a0 100644 --- a/src/register-allocator.h +++ b/src/register-allocator.h @@ -29,6 +29,7 @@ #define V8_REGISTER_ALLOCATOR_H_ #include "macro-assembler.h" +#include "number-info.h" #if V8_TARGET_ARCH_IA32 #include "ia32/register-allocator-ia32.h" @@ -64,28 +65,21 @@ class Result BASE_EMBEDDED { Result() { invalidate(); } // Construct a register Result. - explicit Result(Register reg); + explicit Result(Register reg, NumberInfo::Type info = NumberInfo::kUnknown); // Construct a Result whose value is a compile-time constant. explicit Result(Handle<Object> value) { value_ = TypeField::encode(CONSTANT) + | NumberInfoField::encode(NumberInfo::kUninitialized) | DataField::encode(ConstantList()->length()); ConstantList()->Add(value); } // The copy constructor and assignment operators could each create a new // register reference. - Result(const Result& other) { - other.CopyTo(this); - } + inline Result(const Result& other); - Result& operator=(const Result& other) { - if (this != &other) { - Unuse(); - other.CopyTo(this); - } - return *this; - } + inline Result& operator=(const Result& other); inline ~Result(); @@ -107,6 +101,14 @@ class Result BASE_EMBEDDED { void invalidate() { value_ = TypeField::encode(INVALID); } + NumberInfo::Type number_info(); + void set_number_info(NumberInfo::Type info); + bool is_number() { + return (number_info() & NumberInfo::kNumber) != 0; + } + bool is_smi() { return number_info() == NumberInfo::kSmi; } + bool is_heap_number() { return number_info() == NumberInfo::kHeapNumber; } + bool is_valid() const { return type() != INVALID; } bool is_register() const { return type() == REGISTER; } bool is_constant() const { return type() == CONSTANT; } @@ -138,7 +140,8 @@ class Result BASE_EMBEDDED { uint32_t value_; class TypeField: public BitField<Type, 0, 2> {}; - class DataField: public BitField<uint32_t, 2, 32 - 3> {}; + class NumberInfoField : public BitField<NumberInfo::Type, 2, 3> {}; + class DataField: public BitField<uint32_t, 5, 32 - 5> {}; inline void CopyTo(Result* destination) const; @@ -237,18 +240,18 @@ class RegisterAllocator BASE_EMBEDDED { // Predicates and accessors for the registers' reference counts. bool is_used(int num) { return registers_.is_used(num); } - bool is_used(Register reg) { return registers_.is_used(ToNumber(reg)); } + inline bool is_used(Register reg); int count(int num) { return registers_.count(num); } - int count(Register reg) { return registers_.count(ToNumber(reg)); } + inline int count(Register reg); // Explicitly record a reference to a register. void Use(int num) { registers_.Use(num); } - void Use(Register reg) { registers_.Use(ToNumber(reg)); } + inline void Use(Register reg); // Explicitly record that a register will no longer be used. void Unuse(int num) { registers_.Unuse(num); } - void Unuse(Register reg) { registers_.Unuse(ToNumber(reg)); } + inline void Unuse(Register reg); // Reset the register reference counts to free all non-reserved registers. void Reset() { registers_.Reset(); } diff --git a/src/runtime.cc b/src/runtime.cc index aa94ce5c..70755423 100644 --- a/src/runtime.cc +++ b/src/runtime.cc @@ -1208,17 +1208,6 @@ static Object* Runtime_OptimizeObjectForAddingMultipleProperties( } -static Object* Runtime_TransformToFastProperties(Arguments args) { - HandleScope scope; - ASSERT(args.length() == 1); - CONVERT_ARG_CHECKED(JSObject, object, 0); - if (!object->HasFastProperties() && !object->IsGlobalObject()) { - TransformToFastProperties(object, 0); - } - return *object; -} - - static Object* Runtime_RegExpExec(Arguments args) { HandleScope scope; ASSERT(args.length() == 4); @@ -2287,6 +2276,20 @@ static int SingleCharIndexOf(Vector<const schar> string, return -1; } + +template <typename schar> +static int SingleCharLastIndexOf(Vector<const schar> string, + schar pattern_char, + int start_index) { + for (int i = start_index; i >= 0; i--) { + if (pattern_char == string[i]) { + return i; + } + } + return -1; +} + + // Trivial string search for shorter strings. // On return, if "complete" is set to true, the return value is the // final result of searching for the patter in the subject. @@ -2363,7 +2366,7 @@ static int StringMatchStrategy(Vector<const schar> sub, // We have an ASCII haystack and a non-ASCII needle. Check if there // really is a non-ASCII character in the needle and bail out if there // is. - if (sizeof(pchar) > 1 && sizeof(schar) == 1) { + if (sizeof(schar) == 1 && sizeof(pchar) > 1) { for (int i = 0; i < pat.length(); i++) { uc16 c = pat[i]; if (c > String::kMaxAsciiCharCode) { @@ -2466,39 +2469,115 @@ static Object* Runtime_StringIndexOf(Arguments args) { } +template <typename schar, typename pchar> +static int StringMatchBackwards(Vector<const schar> sub, + Vector<const pchar> pat, + int idx) { + ASSERT(pat.length() >= 1); + ASSERT(idx + pat.length() <= sub.length()); + + if (sizeof(schar) == 1 && sizeof(pchar) > 1) { + for (int i = 0; i < pat.length(); i++) { + uc16 c = pat[i]; + if (c > String::kMaxAsciiCharCode) { + return -1; + } + } + } + + pchar pattern_first_char = pat[0]; + for (int i = idx; i >= 0; i--) { + if (sub[i] != pattern_first_char) continue; + int j = 1; + while (j < pat.length()) { + if (pat[j] != sub[i+j]) { + break; + } + j++; + } + if (j == pat.length()) { + return i; + } + } + return -1; +} + static Object* Runtime_StringLastIndexOf(Arguments args) { - NoHandleAllocation ha; + HandleScope scope; // create a new handle scope ASSERT(args.length() == 3); - CONVERT_CHECKED(String, sub, args[0]); - CONVERT_CHECKED(String, pat, args[1]); - Object* index = args[2]; - - sub->TryFlattenIfNotFlat(); - pat->TryFlattenIfNotFlat(); + CONVERT_ARG_CHECKED(String, sub, 0); + CONVERT_ARG_CHECKED(String, pat, 1); + Object* index = args[2]; uint32_t start_index; if (!Array::IndexFromObject(index, &start_index)) return Smi::FromInt(-1); - uint32_t pattern_length = pat->length(); + uint32_t pat_length = pat->length(); uint32_t sub_length = sub->length(); - if (start_index + pattern_length > sub_length) { - start_index = sub_length - pattern_length; + if (start_index + pat_length > sub_length) { + start_index = sub_length - pat_length; } - for (int i = start_index; i >= 0; i--) { - bool found = true; - for (uint32_t j = 0; j < pattern_length; j++) { - if (sub->Get(i + j) != pat->Get(j)) { - found = false; - break; + if (pat_length == 0) { + return Smi::FromInt(start_index); + } + + if (!sub->IsFlat()) { + FlattenString(sub); + } + + if (pat_length == 1) { + AssertNoAllocation no_heap_allocation; // ensure vectors stay valid + if (sub->IsAsciiRepresentation()) { + uc16 pchar = pat->Get(0); + if (pchar > String::kMaxAsciiCharCode) { + return Smi::FromInt(-1); } + return Smi::FromInt(SingleCharLastIndexOf(sub->ToAsciiVector(), + static_cast<char>(pat->Get(0)), + start_index)); + } else { + return Smi::FromInt(SingleCharLastIndexOf(sub->ToUC16Vector(), + pat->Get(0), + start_index)); + } + } + + if (!pat->IsFlat()) { + FlattenString(pat); + } + + AssertNoAllocation no_heap_allocation; // ensure vectors stay valid + + int position = -1; + + if (pat->IsAsciiRepresentation()) { + Vector<const char> pat_vector = pat->ToAsciiVector(); + if (sub->IsAsciiRepresentation()) { + position = StringMatchBackwards(sub->ToAsciiVector(), + pat_vector, + start_index); + } else { + position = StringMatchBackwards(sub->ToUC16Vector(), + pat_vector, + start_index); + } + } else { + Vector<const uc16> pat_vector = pat->ToUC16Vector(); + if (sub->IsAsciiRepresentation()) { + position = StringMatchBackwards(sub->ToAsciiVector(), + pat_vector, + start_index); + } else { + position = StringMatchBackwards(sub->ToUC16Vector(), + pat_vector, + start_index); } - if (found) return Smi::FromInt(i); } - return Smi::FromInt(-1); + return Smi::FromInt(position); } @@ -2906,7 +2985,7 @@ static Object* Runtime_DefineOrRedefineAccessorProperty(Arguments args) { // If an existing property is either FIELD, NORMAL or CONSTANT_FUNCTION // delete it to avoid running into trouble in DefineAccessor, which // handles this incorrectly if the property is readonly (does nothing) - if (result.IsValid() && + if (result.IsProperty() && (result.type() == FIELD || result.type() == NORMAL || result.type() == CONSTANT_FUNCTION)) { obj->DeleteProperty(name, JSObject::NORMAL_DELETION); @@ -2937,12 +3016,14 @@ static Object* Runtime_DefineOrRedefineDataProperty(Arguments args) { // correctly in the case where a property is a field and is reset with // new attributes. if (result.IsProperty() && attr != result.GetAttributes()) { - PropertyDetails details = PropertyDetails(attr, NORMAL); // New attributes - normalize to avoid writing to instance descriptor - js_object->NormalizeProperties(KEEP_INOBJECT_PROPERTIES, 0); - return js_object->SetNormalizedProperty(*name, *obj_value, details); + js_object->NormalizeProperties(CLEAR_INOBJECT_PROPERTIES, 0); + // Use IgnoreAttributes version since a readonly property may be + // overridden and SetProperty does not allow this. + return js_object->IgnoreAttributesAndSetLocalProperty(*name, + *obj_value, + attr); } - return Runtime::SetObjectProperty(js_object, name, obj_value, attr); } @@ -2969,8 +3050,6 @@ Object* Runtime::SetObjectProperty(Handle<Object> object, // Check if the given key is an array index. uint32_t index; if (Array::IndexFromObject(*key, &index)) { - ASSERT(attr == NONE); - // In Firefox/SpiderMonkey, Safari and Opera you can access the characters // of a string using [] notation. We need to support this too in // JavaScript. @@ -2990,7 +3069,6 @@ Object* Runtime::SetObjectProperty(Handle<Object> object, if (key->IsString()) { Handle<Object> result; if (Handle<String>::cast(key)->AsArrayIndex(&index)) { - ASSERT(attr == NONE); result = SetElement(js_object, index, value); } else { Handle<String> key_string = Handle<String>::cast(key); @@ -3008,7 +3086,6 @@ Object* Runtime::SetObjectProperty(Handle<Object> object, Handle<String> name = Handle<String>::cast(converted); if (name->AsArrayIndex(&index)) { - ASSERT(attr == NONE); return js_object->SetElement(index, *value); } else { return js_object->SetProperty(*name, *value, attr); @@ -3025,8 +3102,6 @@ Object* Runtime::ForceSetObjectProperty(Handle<JSObject> js_object, // Check if the given key is an array index. uint32_t index; if (Array::IndexFromObject(*key, &index)) { - ASSERT(attr == NONE); - // In Firefox/SpiderMonkey, Safari and Opera you can access the characters // of a string using [] notation. We need to support this too in // JavaScript. @@ -3043,7 +3118,6 @@ Object* Runtime::ForceSetObjectProperty(Handle<JSObject> js_object, if (key->IsString()) { if (Handle<String>::cast(key)->AsArrayIndex(&index)) { - ASSERT(attr == NONE); return js_object->SetElement(index, *value); } else { Handle<String> key_string = Handle<String>::cast(key); @@ -3061,7 +3135,6 @@ Object* Runtime::ForceSetObjectProperty(Handle<JSObject> js_object, Handle<String> name = Handle<String>::cast(converted); if (name->AsArrayIndex(&index)) { - ASSERT(attr == NONE); return js_object->SetElement(index, *value); } else { return js_object->IgnoreAttributesAndSetLocalProperty(*name, *value, attr); @@ -3520,17 +3593,23 @@ static Object* Runtime_GetArgumentsProperty(Arguments args) { static Object* Runtime_ToFastProperties(Arguments args) { + HandleScope scope; + ASSERT(args.length() == 1); Handle<Object> object = args.at<Object>(0); if (object->IsJSObject()) { Handle<JSObject> js_object = Handle<JSObject>::cast(object); - js_object->TransformToFastProperties(0); + if (!js_object->HasFastProperties() && !js_object->IsGlobalObject()) { + js_object->TransformToFastProperties(0); + } } return *object; } static Object* Runtime_ToSlowProperties(Arguments args) { + HandleScope scope; + ASSERT(args.length() == 1); Handle<Object> object = args.at<Object>(0); if (object->IsJSObject()) { @@ -4559,6 +4638,7 @@ static Object* Runtime_StringCompare(Arguments args) { static Object* Runtime_Math_abs(Arguments args) { NoHandleAllocation ha; ASSERT(args.length() == 1); + Counters::math_abs.Increment(); CONVERT_DOUBLE_CHECKED(x, args[0]); return Heap::AllocateHeapNumber(fabs(x)); @@ -4568,6 +4648,7 @@ static Object* Runtime_Math_abs(Arguments args) { static Object* Runtime_Math_acos(Arguments args) { NoHandleAllocation ha; ASSERT(args.length() == 1); + Counters::math_acos.Increment(); CONVERT_DOUBLE_CHECKED(x, args[0]); return TranscendentalCache::Get(TranscendentalCache::ACOS, x); @@ -4577,6 +4658,7 @@ static Object* Runtime_Math_acos(Arguments args) { static Object* Runtime_Math_asin(Arguments args) { NoHandleAllocation ha; ASSERT(args.length() == 1); + Counters::math_asin.Increment(); CONVERT_DOUBLE_CHECKED(x, args[0]); return TranscendentalCache::Get(TranscendentalCache::ASIN, x); @@ -4586,6 +4668,7 @@ static Object* Runtime_Math_asin(Arguments args) { static Object* Runtime_Math_atan(Arguments args) { NoHandleAllocation ha; ASSERT(args.length() == 1); + Counters::math_atan.Increment(); CONVERT_DOUBLE_CHECKED(x, args[0]); return TranscendentalCache::Get(TranscendentalCache::ATAN, x); @@ -4595,6 +4678,7 @@ static Object* Runtime_Math_atan(Arguments args) { static Object* Runtime_Math_atan2(Arguments args) { NoHandleAllocation ha; ASSERT(args.length() == 2); + Counters::math_atan2.Increment(); CONVERT_DOUBLE_CHECKED(x, args[0]); CONVERT_DOUBLE_CHECKED(y, args[1]); @@ -4618,6 +4702,7 @@ static Object* Runtime_Math_atan2(Arguments args) { static Object* Runtime_Math_ceil(Arguments args) { NoHandleAllocation ha; ASSERT(args.length() == 1); + Counters::math_ceil.Increment(); CONVERT_DOUBLE_CHECKED(x, args[0]); return Heap::NumberFromDouble(ceiling(x)); @@ -4627,6 +4712,7 @@ static Object* Runtime_Math_ceil(Arguments args) { static Object* Runtime_Math_cos(Arguments args) { NoHandleAllocation ha; ASSERT(args.length() == 1); + Counters::math_cos.Increment(); CONVERT_DOUBLE_CHECKED(x, args[0]); return TranscendentalCache::Get(TranscendentalCache::COS, x); @@ -4636,6 +4722,7 @@ static Object* Runtime_Math_cos(Arguments args) { static Object* Runtime_Math_exp(Arguments args) { NoHandleAllocation ha; ASSERT(args.length() == 1); + Counters::math_exp.Increment(); CONVERT_DOUBLE_CHECKED(x, args[0]); return TranscendentalCache::Get(TranscendentalCache::EXP, x); @@ -4645,6 +4732,7 @@ static Object* Runtime_Math_exp(Arguments args) { static Object* Runtime_Math_floor(Arguments args) { NoHandleAllocation ha; ASSERT(args.length() == 1); + Counters::math_floor.Increment(); CONVERT_DOUBLE_CHECKED(x, args[0]); return Heap::NumberFromDouble(floor(x)); @@ -4654,6 +4742,7 @@ static Object* Runtime_Math_floor(Arguments args) { static Object* Runtime_Math_log(Arguments args) { NoHandleAllocation ha; ASSERT(args.length() == 1); + Counters::math_log.Increment(); CONVERT_DOUBLE_CHECKED(x, args[0]); return TranscendentalCache::Get(TranscendentalCache::LOG, x); @@ -4694,6 +4783,7 @@ static double powi(double x, int y) { static Object* Runtime_Math_pow(Arguments args) { NoHandleAllocation ha; ASSERT(args.length() == 2); + Counters::math_pow.Increment(); CONVERT_DOUBLE_CHECKED(x, args[0]); @@ -4732,6 +4822,7 @@ static Object* Runtime_Math_pow(Arguments args) { static Object* Runtime_Math_round(Arguments args) { NoHandleAllocation ha; ASSERT(args.length() == 1); + Counters::math_round.Increment(); CONVERT_DOUBLE_CHECKED(x, args[0]); if (signbit(x) && x >= -0.5) return Heap::minus_zero_value(); @@ -4744,6 +4835,7 @@ static Object* Runtime_Math_round(Arguments args) { static Object* Runtime_Math_sin(Arguments args) { NoHandleAllocation ha; ASSERT(args.length() == 1); + Counters::math_sin.Increment(); CONVERT_DOUBLE_CHECKED(x, args[0]); return TranscendentalCache::Get(TranscendentalCache::SIN, x); @@ -4753,6 +4845,7 @@ static Object* Runtime_Math_sin(Arguments args) { static Object* Runtime_Math_sqrt(Arguments args) { NoHandleAllocation ha; ASSERT(args.length() == 1); + Counters::math_sqrt.Increment(); CONVERT_DOUBLE_CHECKED(x, args[0]); return Heap::AllocateHeapNumber(sqrt(x)); @@ -4762,47 +4855,13 @@ static Object* Runtime_Math_sqrt(Arguments args) { static Object* Runtime_Math_tan(Arguments args) { NoHandleAllocation ha; ASSERT(args.length() == 1); + Counters::math_tan.Increment(); CONVERT_DOUBLE_CHECKED(x, args[0]); return TranscendentalCache::Get(TranscendentalCache::TAN, x); } -// The NewArguments function is only used when constructing the -// arguments array when calling non-functions from JavaScript in -// runtime.js:CALL_NON_FUNCTION. -static Object* Runtime_NewArguments(Arguments args) { - NoHandleAllocation ha; - ASSERT(args.length() == 1); - - // ECMA-262, 3rd., 10.1.8, p.39 - CONVERT_CHECKED(JSFunction, callee, args[0]); - - // Compute the frame holding the arguments. - JavaScriptFrameIterator it; - it.AdvanceToArgumentsFrame(); - JavaScriptFrame* frame = it.frame(); - - const int length = frame->GetProvidedParametersCount(); - Object* result = Heap::AllocateArgumentsObject(callee, length); - if (result->IsFailure()) return result; - if (length > 0) { - Object* obj = Heap::AllocateFixedArray(length); - if (obj->IsFailure()) return obj; - FixedArray* array = FixedArray::cast(obj); - ASSERT(array->length() == length); - - AssertNoAllocation no_gc; - WriteBarrierMode mode = array->GetWriteBarrierMode(no_gc); - for (int i = 0; i < length; i++) { - array->set(i, frame->GetParameter(i), mode); - } - JSObject::cast(result)->set_elements(array); - } - return result; -} - - static Object* Runtime_NewArgumentsFast(Arguments args) { NoHandleAllocation ha; ASSERT(args.length() == 3); @@ -4849,21 +4908,21 @@ static Object* Runtime_NewClosure(Arguments args) { } -static Code* ComputeConstructStub(Handle<SharedFunctionInfo> shared) { - // TODO(385): Change this to create a construct stub specialized for - // the given map to make allocation of simple objects - and maybe - // arrays - much faster. - if (FLAG_inline_new - && shared->has_only_simple_this_property_assignments()) { +static Code* ComputeConstructStub(Handle<JSFunction> function) { + Handle<Object> prototype = Factory::null_value(); + if (function->has_instance_prototype()) { + prototype = Handle<Object>(function->instance_prototype()); + } + if (function->shared()->CanGenerateInlineConstructor(*prototype)) { ConstructStubCompiler compiler; - Object* code = compiler.CompileConstructStub(*shared); + Object* code = compiler.CompileConstructStub(function->shared()); if (code->IsFailure()) { return Builtins::builtin(Builtins::JSConstructStubGeneric); } return Code::cast(code); } - return shared->construct_stub(); + return function->shared()->construct_stub(); } @@ -4913,10 +4972,9 @@ static Object* Runtime_NewObject(Arguments args) { bool first_allocation = !function->has_initial_map(); Handle<JSObject> result = Factory::NewJSObject(function); if (first_allocation) { - Handle<Map> map = Handle<Map>(function->initial_map()); Handle<Code> stub = Handle<Code>( - ComputeConstructStub(Handle<SharedFunctionInfo>(function->shared()))); - function->shared()->set_construct_stub(*stub); + ComputeConstructStub(Handle<JSFunction>(function))); + shared->set_construct_stub(*stub); } Counters::constructed_objects.Increment(); @@ -4955,28 +5013,6 @@ static Object* Runtime_LazyCompile(Arguments args) { } -static Object* Runtime_GetCalledFunction(Arguments args) { - HandleScope scope; - ASSERT(args.length() == 0); - StackFrameIterator it; - // Get past the JS-to-C exit frame. - ASSERT(it.frame()->is_exit()); - it.Advance(); - // Get past the CALL_NON_FUNCTION activation frame. - ASSERT(it.frame()->is_java_script()); - it.Advance(); - // Argument adaptor frames do not copy the function; we have to skip - // past them to get to the real calling frame. - if (it.frame()->is_arguments_adaptor()) it.Advance(); - // Get the function from the top of the expression stack of the - // calling frame. - StandardFrame* frame = StandardFrame::cast(it.frame()); - int index = frame->ComputeExpressionsCount() - 1; - Object* result = frame->GetExpression(index); - return result; -} - - static Object* Runtime_GetFunctionDelegate(Arguments args) { HandleScope scope; ASSERT(args.length() == 1); @@ -7981,20 +8017,22 @@ static Object* Runtime_FunctionGetInferredName(Arguments args) { static Object* Runtime_ProfilerResume(Arguments args) { NoHandleAllocation ha; - ASSERT(args.length() == 1); + ASSERT(args.length() == 2); CONVERT_CHECKED(Smi, smi_modules, args[0]); - v8::V8::ResumeProfilerEx(smi_modules->value()); + CONVERT_CHECKED(Smi, smi_tag, args[1]); + v8::V8::ResumeProfilerEx(smi_modules->value(), smi_tag->value()); return Heap::undefined_value(); } static Object* Runtime_ProfilerPause(Arguments args) { NoHandleAllocation ha; - ASSERT(args.length() == 1); + ASSERT(args.length() == 2); CONVERT_CHECKED(Smi, smi_modules, args[0]); - v8::V8::PauseProfilerEx(smi_modules->value()); + CONVERT_CHECKED(Smi, smi_tag, args[1]); + v8::V8::PauseProfilerEx(smi_modules->value(), smi_tag->value()); return Heap::undefined_value(); } @@ -8159,15 +8197,6 @@ static Object* Runtime_DeleteHandleScopeExtensions(Arguments args) { } -static Object* Runtime_ProfileLogMarker(Arguments args) { - ASSERT(args.length() == 1); - CONVERT_CHECKED(String, format, args[0]); - Vector<const char> marker = format->ToAsciiVector(); - Logger::LogProfileMarker(marker); - return Heap::undefined_value(); -} - - #ifdef DEBUG // ListNatives is ONLY used by the fuzz-natives.js in debug mode // Exclude the code in release mode. diff --git a/src/runtime.h b/src/runtime.h index 12f969d4..e2e5c221 100644 --- a/src/runtime.h +++ b/src/runtime.h @@ -71,10 +71,8 @@ namespace internal { F(IsExtensible, 1, 1) \ \ /* Utilities */ \ - F(GetCalledFunction, 0, 1) \ F(GetFunctionDelegate, 1, 1) \ F(GetConstructorDelegate, 1, 1) \ - F(NewArguments, 1, 1) \ F(NewArgumentsFast, 3, 1) \ F(LazyCompile, 1, 1) \ F(SetNewFunctionAttributes, 1, 1) \ @@ -268,7 +266,6 @@ namespace internal { F(InitializeConstGlobal, 2, 1) \ F(InitializeConstContextSlot, 3, 1) \ F(OptimizeObjectForAddingMultipleProperties, 2, 1) \ - F(TransformToFastProperties, 1, 1) \ \ /* Debugging */ \ F(DebugPrint, 1, 1) \ @@ -284,11 +281,7 @@ namespace internal { F(DeleteHandleScopeExtensions, 0, 1) \ \ /* Pseudo functions - handled as macros by parser */ \ - F(IS_VAR, 1, 1) \ - \ - /* Profile marker support for more fine grained profiling of page cyclers. */ \ - /* Also known as a stupid hack. :) */ \ - F(ProfileLogMarker, 1, 1) + F(IS_VAR, 1, 1) #ifdef ENABLE_DEBUGGER_SUPPORT #define RUNTIME_FUNCTION_LIST_DEBUGGER_SUPPORT(F) \ @@ -335,8 +328,8 @@ namespace internal { #ifdef ENABLE_LOGGING_AND_PROFILING #define RUNTIME_FUNCTION_LIST_PROFILER_SUPPORT(F) \ - F(ProfilerResume, 1, 1) \ - F(ProfilerPause, 1, 1) + F(ProfilerResume, 2, 1) \ + F(ProfilerPause, 2, 1) #else #define RUNTIME_FUNCTION_LIST_PROFILER_SUPPORT(F) #endif diff --git a/src/runtime.js b/src/runtime.js index 10ef98eb..e9d98487 100644 --- a/src/runtime.js +++ b/src/runtime.js @@ -178,7 +178,7 @@ function STRING_ADD_LEFT(y) { y = %_ValueOf(y); } else { y = IS_NUMBER(y) - ? %NumberToString(y) + ? %_NumberToString(y) : %ToString(%ToPrimitive(y, NO_HINT)); } } @@ -194,7 +194,7 @@ function STRING_ADD_RIGHT(y) { x = %_ValueOf(x); } else { x = IS_NUMBER(x) - ? %NumberToString(x) + ? %_NumberToString(x) : %ToString(%ToPrimitive(x, NO_HINT)); } } @@ -395,26 +395,20 @@ function FILTER_KEY(key) { function CALL_NON_FUNCTION() { - var callee = %GetCalledFunction(); - var delegate = %GetFunctionDelegate(callee); + var delegate = %GetFunctionDelegate(this); if (!IS_FUNCTION(delegate)) { - throw %MakeTypeError('called_non_callable', [typeof callee]); + throw %MakeTypeError('called_non_callable', [typeof this]); } - - var parameters = %NewArguments(delegate); - return delegate.apply(callee, parameters); + return delegate.apply(this, arguments); } function CALL_NON_FUNCTION_AS_CONSTRUCTOR() { - var callee = %GetCalledFunction(); - var delegate = %GetConstructorDelegate(callee); + var delegate = %GetConstructorDelegate(this); if (!IS_FUNCTION(delegate)) { - throw %MakeTypeError('called_non_callable', [typeof callee]); + throw %MakeTypeError('called_non_callable', [typeof this]); } - - var parameters = %NewArguments(delegate); - return delegate.apply(callee, parameters); + return delegate.apply(this, arguments); } @@ -529,6 +523,13 @@ function ToNumber(x) { // ECMA-262, section 9.8, page 35. function ToString(x) { if (IS_STRING(x)) return x; + if (IS_NUMBER(x)) return %_NumberToString(x); + if (IS_BOOLEAN(x)) return x ? 'true' : 'false'; + if (IS_UNDEFINED(x)) return 'undefined'; + return (IS_NULL(x)) ? 'null' : %ToString(%DefaultString(x)); +} + +function NonStringToString(x) { if (IS_NUMBER(x)) return %NumberToString(x); if (IS_BOOLEAN(x)) return x ? 'true' : 'false'; if (IS_UNDEFINED(x)) return 'undefined'; diff --git a/src/scopeinfo.cc b/src/scopeinfo.cc index 8b989d7a..de1841b0 100644 --- a/src/scopeinfo.cc +++ b/src/scopeinfo.cc @@ -536,7 +536,7 @@ int ContextSlotCache::Hash(Code* code, String* name) { // Uses only lower 32 bits if pointers are larger. uintptr_t addr_hash = static_cast<uint32_t>(reinterpret_cast<uintptr_t>(code)) >> 2; - return (addr_hash ^ name->Hash()) % kLength; + return static_cast<int>((addr_hash ^ name->Hash()) % kLength); } diff --git a/src/serialize.cc b/src/serialize.cc index ad8e20f6..110e4619 100644 --- a/src/serialize.cc +++ b/src/serialize.cc @@ -359,79 +359,87 @@ void ExternalReferenceTable::PopulateTable() { UNCLASSIFIED, 7, "Heap::NewSpaceStart()"); - Add(ExternalReference::heap_always_allocate_scope_depth().address(), + Add(ExternalReference::new_space_mask().address(), UNCLASSIFIED, 8, + "Heap::NewSpaceMask()"); + Add(ExternalReference::heap_always_allocate_scope_depth().address(), + UNCLASSIFIED, + 9, "Heap::always_allocate_scope_depth()"); Add(ExternalReference::new_space_allocation_limit_address().address(), UNCLASSIFIED, - 9, + 10, "Heap::NewSpaceAllocationLimitAddress()"); Add(ExternalReference::new_space_allocation_top_address().address(), UNCLASSIFIED, - 10, + 11, "Heap::NewSpaceAllocationTopAddress()"); #ifdef ENABLE_DEBUGGER_SUPPORT Add(ExternalReference::debug_break().address(), UNCLASSIFIED, - 11, + 12, "Debug::Break()"); Add(ExternalReference::debug_step_in_fp_address().address(), UNCLASSIFIED, - 12, + 13, "Debug::step_in_fp_addr()"); #endif Add(ExternalReference::double_fp_operation(Token::ADD).address(), UNCLASSIFIED, - 13, + 14, "add_two_doubles"); Add(ExternalReference::double_fp_operation(Token::SUB).address(), UNCLASSIFIED, - 14, + 15, "sub_two_doubles"); Add(ExternalReference::double_fp_operation(Token::MUL).address(), UNCLASSIFIED, - 15, + 16, "mul_two_doubles"); Add(ExternalReference::double_fp_operation(Token::DIV).address(), UNCLASSIFIED, - 16, + 17, "div_two_doubles"); Add(ExternalReference::double_fp_operation(Token::MOD).address(), UNCLASSIFIED, - 17, + 18, "mod_two_doubles"); Add(ExternalReference::compare_doubles().address(), UNCLASSIFIED, - 18, + 19, "compare_doubles"); #ifdef V8_NATIVE_REGEXP Add(ExternalReference::re_case_insensitive_compare_uc16().address(), UNCLASSIFIED, - 19, + 20, "NativeRegExpMacroAssembler::CaseInsensitiveCompareUC16()"); Add(ExternalReference::re_check_stack_guard_state().address(), UNCLASSIFIED, - 20, + 21, "RegExpMacroAssembler*::CheckStackGuardState()"); Add(ExternalReference::re_grow_stack().address(), UNCLASSIFIED, - 21, + 22, "NativeRegExpMacroAssembler::GrowStack()"); Add(ExternalReference::re_word_character_map().address(), UNCLASSIFIED, - 22, + 23, "NativeRegExpMacroAssembler::word_character_map"); #endif // Keyed lookup cache. Add(ExternalReference::keyed_lookup_cache_keys().address(), UNCLASSIFIED, - 23, + 24, "KeyedLookupCache::keys()"); Add(ExternalReference::keyed_lookup_cache_field_offsets().address(), UNCLASSIFIED, - 24, + 25, "KeyedLookupCache::field_offsets()"); + Add(ExternalReference::transcendental_cache_array_address().address(), + UNCLASSIFIED, + 26, + "TranscendentalCache::caches()"); } @@ -852,10 +860,10 @@ void SnapshotByteSink::PutInt(uintptr_t integer, const char* description) { const int max_shift = ((kPointerSize * kBitsPerByte) / 7) * 7; for (int shift = max_shift; shift > 0; shift -= 7) { if (integer >= static_cast<uintptr_t>(1u) << shift) { - Put(((integer >> shift) & 0x7f) | 0x80, "IntPart"); + Put((static_cast<int>((integer >> shift)) & 0x7f) | 0x80, "IntPart"); } } - PutSection(integer & 0x7f, "IntLastPart"); + PutSection(static_cast<int>(integer & 0x7f), "IntLastPart"); } #ifdef DEBUG diff --git a/src/spaces-inl.h b/src/spaces-inl.h index 4fd8a6c8..72f83050 100644 --- a/src/spaces-inl.h +++ b/src/spaces-inl.h @@ -183,7 +183,7 @@ Page* MemoryAllocator::GetNextPage(Page* p) { int MemoryAllocator::GetChunkId(Page* p) { ASSERT(p->is_valid()); - return p->opaque_header & Page::kPageAlignmentMask; + return static_cast<int>(p->opaque_header & Page::kPageAlignmentMask); } diff --git a/src/string.js b/src/string.js index b2af0504..a8fc8d47 100644 --- a/src/string.js +++ b/src/string.js @@ -34,7 +34,7 @@ // Set the String function and constructor. %SetCode($String, function(x) { - var value = %_ArgumentsLength() == 0 ? '' : ToString(x); + var value = %_ArgumentsLength() == 0 ? '' : TO_STRING_INLINE(x); if (%_IsConstructCall()) { %_SetValueOf(this, value); } else { @@ -64,7 +64,7 @@ function StringValueOf() { function StringCharAt(pos) { var char_code = %_FastCharCodeAt(this, pos); if (!%_IsSmi(char_code)) { - var subject = ToString(this); + var subject = TO_STRING_INLINE(this); var index = TO_INTEGER(pos); if (index >= subject.length || index < 0) return ""; char_code = %StringCharCodeAt(subject, index); @@ -79,7 +79,7 @@ function StringCharCodeAt(pos) { if (%_IsSmi(fast_answer)) { return fast_answer; } - var subject = ToString(this); + var subject = TO_STRING_INLINE(this); var index = TO_INTEGER(pos); return %StringCharCodeAt(subject, index); } @@ -87,14 +87,18 @@ function StringCharCodeAt(pos) { // ECMA-262, section 15.5.4.6 function StringConcat() { - var len = %_ArgumentsLength() + 1; - var parts = new $Array(len); - parts[0] = IS_STRING(this) ? this : ToString(this); - for (var i = 1; i < len; i++) { - var part = %_Arguments(i - 1); - parts[i] = IS_STRING(part) ? part : ToString(part); + var len = %_ArgumentsLength(); + var this_as_string = TO_STRING_INLINE(this); + if (len === 1) { + return this_as_string + %_Arguments(0); } - return %StringBuilderConcat(parts, len, ""); + var parts = new $Array(len + 1); + parts[0] = this_as_string; + for (var i = 0; i < len; i++) { + var part = %_Arguments(i); + parts[i + 1] = TO_STRING_INLINE(part); + } + return %StringBuilderConcat(parts, len + 1, ""); } // Match ES3 and Safari @@ -103,8 +107,8 @@ function StringConcat() { // ECMA-262 section 15.5.4.7 function StringIndexOf(searchString /* position */) { // length == 1 - var subject_str = ToString(this); - var pattern_str = ToString(searchString); + var subject_str = TO_STRING_INLINE(this); + var pattern_str = TO_STRING_INLINE(searchString); var subject_str_len = subject_str.length; var pattern_str_len = pattern_str.length; var index = 0; @@ -121,9 +125,9 @@ function StringIndexOf(searchString /* position */) { // length == 1 // ECMA-262 section 15.5.4.8 function StringLastIndexOf(searchString /* position */) { // length == 1 - var sub = ToString(this); + var sub = TO_STRING_INLINE(this); var subLength = sub.length; - var pat = ToString(searchString); + var pat = TO_STRING_INLINE(searchString); var patLength = pat.length; var index = subLength - patLength; if (%_ArgumentsLength() > 1) { @@ -152,8 +156,8 @@ function StringLastIndexOf(searchString /* position */) { // length == 1 function StringLocaleCompare(other) { if (%_ArgumentsLength() === 0) return 0; - var this_str = ToString(this); - var other_str = ToString(other); + var this_str = TO_STRING_INLINE(this); + var other_str = TO_STRING_INLINE(other); return %StringLocaleCompare(this_str, other_str); } @@ -161,7 +165,7 @@ function StringLocaleCompare(other) { // ECMA-262 section 15.5.4.10 function StringMatch(regexp) { if (!IS_REGEXP(regexp)) regexp = new $RegExp(regexp); - var subject = ToString(this); + var subject = TO_STRING_INLINE(this); if (!regexp.global) return regexp.exec(subject); %_Log('regexp', 'regexp-match,%0S,%1r', [subject, regexp]); @@ -196,7 +200,7 @@ var reusableMatchInfo = [2, "", "", -1, -1]; // ECMA-262, section 15.5.4.11 function StringReplace(search, replace) { - var subject = IS_STRING(this) ? this : ToString(this); + var subject = TO_STRING_INLINE(this); // Delegate to one of the regular expression variants if necessary. if (IS_REGEXP(search)) { @@ -209,7 +213,7 @@ function StringReplace(search, replace) { } // Convert the search argument to a string and search for it. - search = IS_STRING(search) ? search : ToString(search); + search = TO_STRING_INLINE(search); var start = %StringIndexOf(subject, search, 0); if (start < 0) return subject; var end = start + search.length; @@ -224,7 +228,7 @@ function StringReplace(search, replace) { } else { reusableMatchInfo[CAPTURE0] = start; reusableMatchInfo[CAPTURE1] = end; - if (!IS_STRING(replace)) replace = ToString(replace); + replace = TO_STRING_INLINE(replace); ExpandReplacement(replace, subject, reusableMatchInfo, builder); } @@ -237,7 +241,7 @@ function StringReplace(search, replace) { // Helper function for regular expressions in String.prototype.replace. function StringReplaceRegExp(subject, regexp, replace) { - replace = ToString(replace); + replace = TO_STRING_INLINE(replace); return %StringReplaceRegExpWithString(subject, regexp, replace, @@ -458,7 +462,7 @@ function ApplyReplacementFunction(replace, matchInfo, subject) { // ECMA-262 section 15.5.4.12 function StringSearch(re) { var regexp = new $RegExp(re); - var s = ToString(this); + var s = TO_STRING_INLINE(this); var last_idx = regexp.lastIndex; // keep old lastIndex regexp.lastIndex = 0; // ignore re.global property var result = regexp.exec(s); @@ -472,7 +476,7 @@ function StringSearch(re) { // ECMA-262 section 15.5.4.13 function StringSlice(start, end) { - var s = ToString(this); + var s = TO_STRING_INLINE(this); var s_len = s.length; var start_i = TO_INTEGER(start); var end_i = s_len; @@ -507,7 +511,7 @@ function StringSlice(start, end) { // ECMA-262 section 15.5.4.14 function StringSplit(separator, limit) { - var subject = ToString(this); + var subject = TO_STRING_INLINE(this); limit = (IS_UNDEFINED(limit)) ? 0xffffffff : TO_UINT32(limit); if (limit === 0) return []; @@ -521,18 +525,35 @@ function StringSplit(separator, limit) { } var length = subject.length; - if (IS_REGEXP(separator)) { - %_Log('regexp', 'regexp-split,%0S,%1r', [subject, separator]); - } else { - separator = ToString(separator); + if (!IS_REGEXP(separator)) { + separator = TO_STRING_INLINE(separator); + var separator_length = separator.length; + // If the separator string is empty then return the elements in the subject. - if (separator.length == 0) { + if (separator_length === 0) { var result = $Array(length); for (var i = 0; i < length; i++) result[i] = subject[i]; return result; } + + var result = []; + var start_index = 0; + var index; + while (true) { + if (start_index + separator_length > length || + (index = %StringIndexOf(subject, separator, start_index)) === -1) { + result.push(SubString(subject, start_index, length)); + break; + } + if (result.push(SubString(subject, start_index, index)) === limit) break; + start_index = index + separator_length; + } + + return result; } + %_Log('regexp', 'regexp-split,%0S,%1r', [subject, separator]); + if (length === 0) { if (splitMatch(separator, subject, 0, 0) != null) return []; return [subject]; @@ -567,7 +588,8 @@ function StringSplit(separator, limit) { result[result.length] = SubString(subject, currentIndex, matchInfo[CAPTURE0]); if (result.length === limit) return result; - for (var i = 2; i < NUMBER_OF_CAPTURES(matchInfo); i += 2) { + var num_captures = NUMBER_OF_CAPTURES(matchInfo); + for (var i = 2; i < num_captures; i += 2) { var start = matchInfo[CAPTURE(i)]; var end = matchInfo[CAPTURE(i + 1)]; if (start != -1 && end != -1) { @@ -587,28 +609,18 @@ function StringSplit(separator, limit) { // Helper function used by split. This version returns the matchInfo // instead of allocating a new array with basically the same information. function splitMatch(separator, subject, current_index, start_index) { - if (IS_REGEXP(separator)) { - var matchInfo = DoRegExpExec(separator, subject, start_index); - if (matchInfo == null) return null; - // Section 15.5.4.14 paragraph two says that we do not allow zero length - // matches at the end of the string. - if (matchInfo[CAPTURE0] === subject.length) return null; - return matchInfo; - } - - var separatorIndex = subject.indexOf(separator, start_index); - if (separatorIndex === -1) return null; - - reusableMatchInfo[CAPTURE0] = separatorIndex; - reusableMatchInfo[CAPTURE1] = separatorIndex + separator.length; - return reusableMatchInfo; -}; + var matchInfo = DoRegExpExec(separator, subject, start_index); + if (matchInfo == null) return null; + // Section 15.5.4.14 paragraph two says that we do not allow zero length + // matches at the end of the string. + if (matchInfo[CAPTURE0] === subject.length) return null; + return matchInfo; +} // ECMA-262 section 15.5.4.15 function StringSubstring(start, end) { - var s = this; - if (!IS_STRING(s)) s = ToString(s); + var s = TO_STRING_INLINE(this); var s_len = s.length; var start_i = TO_INTEGER(start); @@ -639,7 +651,7 @@ function StringSubstring(start, end) { // This is not a part of ECMA-262. function StringSubstr(start, n) { - var s = ToString(this); + var s = TO_STRING_INLINE(this); var len; // Correct n: If not given, set to string length; if explicitly @@ -677,38 +689,38 @@ function StringSubstr(start, n) { // ECMA-262, 15.5.4.16 function StringToLowerCase() { - return %StringToLowerCase(ToString(this)); + return %StringToLowerCase(TO_STRING_INLINE(this)); } // ECMA-262, 15.5.4.17 function StringToLocaleLowerCase() { - return %StringToLowerCase(ToString(this)); + return %StringToLowerCase(TO_STRING_INLINE(this)); } // ECMA-262, 15.5.4.18 function StringToUpperCase() { - return %StringToUpperCase(ToString(this)); + return %StringToUpperCase(TO_STRING_INLINE(this)); } // ECMA-262, 15.5.4.19 function StringToLocaleUpperCase() { - return %StringToUpperCase(ToString(this)); + return %StringToUpperCase(TO_STRING_INLINE(this)); } // ES5, 15.5.4.20 function StringTrim() { - return %StringTrim(ToString(this), true, true); + return %StringTrim(TO_STRING_INLINE(this), true, true); } function StringTrimLeft() { - return %StringTrim(ToString(this), true, false); + return %StringTrim(TO_STRING_INLINE(this), true, false); } function StringTrimRight() { - return %StringTrim(ToString(this), false, true); + return %StringTrim(TO_STRING_INLINE(this), false, true); } // ECMA-262, section 15.5.3.2 @@ -727,10 +739,10 @@ function StringFromCharCode(code) { // Helper function for very basic XSS protection. function HtmlEscape(str) { - return ToString(str).replace(/</g, "<") - .replace(/>/g, ">") - .replace(/"/g, """) - .replace(/'/g, "'"); + return TO_STRING_INLINE(str).replace(/</g, "<") + .replace(/>/g, ">") + .replace(/"/g, """) + .replace(/'/g, "'"); }; @@ -809,7 +821,7 @@ function ReplaceResultBuilder(str) { ReplaceResultBuilder.prototype.add = function(str) { - if (!IS_STRING(str)) str = ToString(str); + str = TO_STRING_INLINE(str); if (str.length > 0) { var elements = this.elements; elements[elements.length] = str; diff --git a/src/stub-cache.cc b/src/stub-cache.cc index 81f89fd4..577c2d77 100644 --- a/src/stub-cache.cc +++ b/src/stub-cache.cc @@ -105,7 +105,7 @@ Object* StubCache::ComputeLoadField(String* name, if (code->IsFailure()) return code; LOG(CodeCreateEvent(Logger::LOAD_IC_TAG, Code::cast(code), name)); Object* result = receiver->map()->UpdateCodeCache(name, Code::cast(code)); - if (result->IsFailure()) return code; + if (result->IsFailure()) return result; } return Set(name, receiver->map(), Code::cast(code)); } @@ -124,7 +124,7 @@ Object* StubCache::ComputeLoadCallback(String* name, if (code->IsFailure()) return code; LOG(CodeCreateEvent(Logger::LOAD_IC_TAG, Code::cast(code), name)); Object* result = receiver->map()->UpdateCodeCache(name, Code::cast(code)); - if (result->IsFailure()) return code; + if (result->IsFailure()) return result; } return Set(name, receiver->map(), Code::cast(code)); } @@ -143,7 +143,7 @@ Object* StubCache::ComputeLoadConstant(String* name, if (code->IsFailure()) return code; LOG(CodeCreateEvent(Logger::LOAD_IC_TAG, Code::cast(code), name)); Object* result = receiver->map()->UpdateCodeCache(name, Code::cast(code)); - if (result->IsFailure()) return code; + if (result->IsFailure()) return result; } return Set(name, receiver->map(), Code::cast(code)); } @@ -160,7 +160,7 @@ Object* StubCache::ComputeLoadInterceptor(String* name, if (code->IsFailure()) return code; LOG(CodeCreateEvent(Logger::LOAD_IC_TAG, Code::cast(code), name)); Object* result = receiver->map()->UpdateCodeCache(name, Code::cast(code)); - if (result->IsFailure()) return code; + if (result->IsFailure()) return result; } return Set(name, receiver->map(), Code::cast(code)); } @@ -189,7 +189,7 @@ Object* StubCache::ComputeLoadGlobal(String* name, if (code->IsFailure()) return code; LOG(CodeCreateEvent(Logger::LOAD_IC_TAG, Code::cast(code), name)); Object* result = receiver->map()->UpdateCodeCache(name, Code::cast(code)); - if (result->IsFailure()) return code; + if (result->IsFailure()) return result; } return Set(name, receiver->map(), Code::cast(code)); } @@ -351,7 +351,7 @@ Object* StubCache::ComputeStoreGlobal(String* name, if (code->IsFailure()) return code; LOG(CodeCreateEvent(Logger::LOAD_IC_TAG, Code::cast(code), name)); Object* result = receiver->map()->UpdateCodeCache(name, Code::cast(code)); - if (result->IsFailure()) return code; + if (result->IsFailure()) return result; } return Set(name, receiver->map(), Code::cast(code)); } @@ -484,7 +484,10 @@ Object* StubCache::ComputeCallField(int argc, Object* code = map->FindInCodeCache(name, flags); if (code->IsUndefined()) { CallStubCompiler compiler(argc, in_loop); - code = compiler.CompileCallField(object, holder, index, name); + code = compiler.CompileCallField(JSObject::cast(object), + holder, + index, + name); if (code->IsFailure()) return code; ASSERT_EQ(flags, Code::cast(code)->flags()); LOG(CodeCreateEvent(Logger::CALL_IC_TAG, Code::cast(code), name)); @@ -518,7 +521,9 @@ Object* StubCache::ComputeCallInterceptor(int argc, Object* code = map->FindInCodeCache(name, flags); if (code->IsUndefined()) { CallStubCompiler compiler(argc, NOT_IN_LOOP); - code = compiler.CompileCallInterceptor(object, holder, name); + code = compiler.CompileCallInterceptor(JSObject::cast(object), + holder, + name); if (code->IsFailure()) return code; ASSERT_EQ(flags, Code::cast(code)->flags()); LOG(CodeCreateEvent(Logger::CALL_IC_TAG, Code::cast(code), name)); @@ -561,7 +566,7 @@ Object* StubCache::ComputeCallGlobal(int argc, ASSERT_EQ(flags, Code::cast(code)->flags()); LOG(CodeCreateEvent(Logger::CALL_IC_TAG, Code::cast(code), name)); Object* result = receiver->map()->UpdateCodeCache(name, Code::cast(code)); - if (result->IsFailure()) return code; + if (result->IsFailure()) return result; } return Set(name, receiver->map(), Code::cast(code)); } @@ -920,6 +925,13 @@ Object* StoreInterceptorProperty(Arguments args) { } +Object* KeyedLoadPropertyWithInterceptor(Arguments args) { + JSObject* receiver = JSObject::cast(args[0]); + uint32_t index = Smi::cast(args[1])->value(); + return receiver->GetElementWithInterceptor(receiver, index); +} + + Object* StubCompiler::CompileCallInitialize(Code::Flags flags) { HandleScope scope; int argc = Code::ExtractArgumentsCountFromFlags(flags); @@ -1058,11 +1070,13 @@ Object* StubCompiler::GetCodeWithFlags(Code::Flags flags, String* name) { return GetCodeWithFlags(flags, reinterpret_cast<char*>(NULL)); } + void StubCompiler::LookupPostInterceptor(JSObject* holder, String* name, LookupResult* lookup) { holder->LocalLookupRealNamedProperty(name, lookup); - if (lookup->IsNotFound()) { + if (!lookup->IsProperty()) { + lookup->NotFound(); Object* proto = holder->GetPrototype(); if (proto != Heap::null_value()) { proto->Lookup(name, lookup); diff --git a/src/stub-cache.h b/src/stub-cache.h index d97fe773..43354db1 100644 --- a/src/stub-cache.h +++ b/src/stub-cache.h @@ -312,6 +312,7 @@ Object* LoadPropertyWithInterceptorForLoad(Arguments args); Object* LoadPropertyWithInterceptorForCall(Arguments args); Object* StoreInterceptorProperty(Arguments args); Object* CallInterceptorProperty(Arguments args); +Object* KeyedLoadPropertyWithInterceptor(Arguments args); // Support function for computing call IC miss stubs. @@ -346,6 +347,7 @@ class StubCompiler BASE_EMBEDDED { static void GenerateLoadGlobalFunctionPrototype(MacroAssembler* masm, int index, Register prototype); + static void GenerateFastPropertyLoad(MacroAssembler* masm, Register dst, Register src, JSObject* holder, int index); @@ -354,22 +356,20 @@ class StubCompiler BASE_EMBEDDED { Register receiver, Register scratch, Label* miss_label); + static void GenerateLoadStringLength(MacroAssembler* masm, Register receiver, - Register scratch, + Register scratch1, + Register scratch2, Label* miss_label); - static void GenerateLoadStringLength2(MacroAssembler* masm, - Register receiver, - Register scratch1, - Register scratch2, - Label* miss_label); + static void GenerateLoadFunctionPrototype(MacroAssembler* masm, Register receiver, Register scratch1, Register scratch2, Label* miss_label); + static void GenerateStoreField(MacroAssembler* masm, - Builtins::Name storage_extend, JSObject* object, int index, Map* transition, @@ -377,16 +377,30 @@ class StubCompiler BASE_EMBEDDED { Register name_reg, Register scratch, Label* miss_label); + static void GenerateLoadMiss(MacroAssembler* masm, Code::Kind kind); // Check the integrity of the prototype chain to make sure that the // current IC is still valid. + + Register CheckPrototypes(JSObject* object, + Register object_reg, + JSObject* holder, + Register holder_reg, + Register scratch, + String* name, + Label* miss) { + return CheckPrototypes(object, object_reg, holder, holder_reg, scratch, + name, kInvalidProtoDepth, miss); + } + Register CheckPrototypes(JSObject* object, Register object_reg, JSObject* holder, Register holder_reg, Register scratch, String* name, + int save_at_depth, Label* miss); protected: @@ -538,7 +552,7 @@ class CallStubCompiler: public StubCompiler { explicit CallStubCompiler(int argc, InLoopFlag in_loop) : arguments_(argc), in_loop_(in_loop) { } - Object* CompileCallField(Object* object, + Object* CompileCallField(JSObject* object, JSObject* holder, int index, String* name); @@ -547,7 +561,7 @@ class CallStubCompiler: public StubCompiler { JSFunction* function, String* name, CheckType check); - Object* CompileCallInterceptor(Object* object, + Object* CompileCallInterceptor(JSObject* object, JSObject* holder, String* name); Object* CompileCallGlobal(JSObject* object, @@ -949,10 +949,15 @@ Handle<Context> Top::GetCallingGlobalContext() { } +bool Top::CanHaveSpecialFunctions(JSObject* object) { + return object->IsJSArray(); +} + + Object* Top::LookupSpecialFunction(JSObject* receiver, JSObject* prototype, JSFunction* function) { - if (receiver->IsJSArray()) { + if (CanHaveSpecialFunctions(receiver)) { FixedArray* table = context()->global_context()->special_function_table(); for (int index = 0; index < table->length(); index +=3) { if ((prototype == table->get(index)) && @@ -342,6 +342,7 @@ class Top { return Handle<JSBuiltinsObject>(thread_local_.context_->builtins()); } + static bool CanHaveSpecialFunctions(JSObject* object); static Object* LookupSpecialFunction(JSObject* receiver, JSObject* prototype, JSFunction* value); diff --git a/src/utils.cc b/src/utils.cc index 374385b6..45a4cd60 100644 --- a/src/utils.cc +++ b/src/utils.cc @@ -51,43 +51,6 @@ uint32_t RoundUpToPowerOf2(uint32_t x) { } -byte* EncodeInt(byte* p, int x) { - while (x < -64 || x >= 64) { - *p++ = static_cast<byte>(x & 127); - x = ArithmeticShiftRight(x, 7); - } - // -64 <= x && x < 64 - *p++ = static_cast<byte>(x + 192); - return p; -} - - -byte* DecodeInt(byte* p, int* x) { - int r = 0; - unsigned int s = 0; - byte b = *p++; - while (b < 128) { - r |= static_cast<int>(b) << s; - s += 7; - b = *p++; - } - // b >= 128 - *x = r | ((static_cast<int>(b) - 192) << s); - return p; -} - - -byte* EncodeUnsignedIntBackward(byte* p, unsigned int x) { - while (x >= 128) { - *--p = static_cast<byte>(x & 127); - x = x >> 7; - } - // x < 128 - *--p = static_cast<byte>(x + 128); - return p; -} - - // Thomas Wang, Integer Hash Functions. // http://www.concentric.net/~Ttwang/tech/inthash.htm uint32_t ComputeIntegerHash(uint32_t key) { diff --git a/src/utils.h b/src/utils.h index 0fd24ec9..2fcd241f 100644 --- a/src/utils.h +++ b/src/utils.h @@ -157,7 +157,9 @@ class BitField { // Returns a uint32_t mask of bit field. static uint32_t mask() { - return (1U << (size + shift)) - (1U << shift); + // To use all bits of a uint32 in a bitfield without compiler warnings we + // have to compute 2^32 without using a shift count of 32. + return ((1U << shift) << size) - (1U << shift); } // Returns a uint32_t with the bit field value encoded. @@ -168,54 +170,12 @@ class BitField { // Extracts the bit field from the value. static T decode(uint32_t value) { - return static_cast<T>((value >> shift) & ((1U << (size)) - 1)); + return static_cast<T>((value & mask()) >> shift); } }; // ---------------------------------------------------------------------------- -// Support for compressed, machine-independent encoding -// and decoding of integer values of arbitrary size. - -// Encoding and decoding from/to a buffer at position p; -// the result is the position after the encoded integer. -// Small signed integers in the range -64 <= x && x < 64 -// are encoded in 1 byte; larger values are encoded in 2 -// or more bytes. At most sizeof(int) + 1 bytes are used -// in the worst case. -byte* EncodeInt(byte* p, int x); -byte* DecodeInt(byte* p, int* x); - - -// Encoding and decoding from/to a buffer at position p - 1 -// moving backward; the result is the position of the last -// byte written. These routines are useful to read/write -// into a buffer starting at the end of the buffer. -byte* EncodeUnsignedIntBackward(byte* p, unsigned int x); - -// The decoding function is inlined since its performance is -// important to mark-sweep garbage collection. -inline byte* DecodeUnsignedIntBackward(byte* p, unsigned int* x) { - byte b = *--p; - if (b >= 128) { - *x = static_cast<unsigned int>(b) - 128; - return p; - } - unsigned int r = static_cast<unsigned int>(b); - unsigned int s = 7; - b = *--p; - while (b < 128) { - r |= static_cast<unsigned int>(b) << s; - s += 7; - b = *--p; - } - // b >= 128 - *x = r | ((static_cast<unsigned int>(b) - 128) << s); - return p; -} - - -// ---------------------------------------------------------------------------- // Hash function. uint32_t ComputeIntegerHash(uint32_t key); diff --git a/src/v8-counters.h b/src/v8-counters.h index 2d24765a..1ba20038 100644 --- a/src/v8-counters.h +++ b/src/v8-counters.h @@ -104,71 +104,94 @@ namespace internal { SC(contexts_created_by_snapshot, V8.ContextsCreatedBySnapshot) +#define STATS_COUNTER_LIST_2(SC) \ + /* Number of code stubs. */ \ + SC(code_stubs, V8.CodeStubs) \ + /* Amount of stub code. */ \ + SC(total_stubs_code_size, V8.TotalStubsCodeSize) \ + /* Amount of (JS) compiled code. */ \ + SC(total_compiled_code_size, V8.TotalCompiledCodeSize) \ + SC(gc_compactor_caused_by_request, V8.GCCompactorCausedByRequest) \ + SC(gc_compactor_caused_by_promoted_data, \ + V8.GCCompactorCausedByPromotedData) \ + SC(gc_compactor_caused_by_oldspace_exhaustion, \ + V8.GCCompactorCausedByOldspaceExhaustion) \ + SC(gc_compactor_caused_by_weak_handles, \ + V8.GCCompactorCausedByWeakHandles) \ + SC(gc_last_resort_from_js, V8.GCLastResortFromJS) \ + SC(gc_last_resort_from_handles, V8.GCLastResortFromHandles) \ + /* How is the generic keyed-load stub used? */ \ + SC(keyed_load_generic_smi, V8.KeyedLoadGenericSmi) \ + SC(keyed_load_generic_symbol, V8.KeyedLoadGenericSymbol) \ + SC(keyed_load_generic_slow, V8.KeyedLoadGenericSlow) \ + SC(keyed_load_external_array_slow, V8.KeyedLoadExternalArraySlow) \ + /* Count how much the monomorphic keyed-load stubs are hit. */ \ + SC(keyed_load_function_prototype, V8.KeyedLoadFunctionPrototype) \ + SC(keyed_load_string_length, V8.KeyedLoadStringLength) \ + SC(keyed_load_array_length, V8.KeyedLoadArrayLength) \ + SC(keyed_load_constant_function, V8.KeyedLoadConstantFunction) \ + SC(keyed_load_field, V8.KeyedLoadField) \ + SC(keyed_load_callback, V8.KeyedLoadCallback) \ + SC(keyed_load_interceptor, V8.KeyedLoadInterceptor) \ + SC(keyed_load_inline, V8.KeyedLoadInline) \ + SC(keyed_load_inline_miss, V8.KeyedLoadInlineMiss) \ + SC(named_load_inline, V8.NamedLoadInline) \ + SC(named_load_inline_miss, V8.NamedLoadInlineMiss) \ + SC(named_load_global_inline, V8.NamedLoadGlobalInline) \ + SC(named_load_global_inline_miss, V8.NamedLoadGlobalInlineMiss) \ + SC(keyed_store_field, V8.KeyedStoreField) \ + SC(keyed_store_inline, V8.KeyedStoreInline) \ + SC(keyed_store_inline_miss, V8.KeyedStoreInlineMiss) \ + SC(named_store_global_inline, V8.NamedStoreGlobalInline) \ + SC(named_store_global_inline_miss, V8.NamedStoreGlobalInlineMiss) \ + SC(call_const, V8.CallConst) \ + SC(call_const_fast_api, V8.CallConstFastApi) \ + SC(call_const_interceptor, V8.CallConstInterceptor) \ + SC(call_const_interceptor_fast_api, V8.CallConstInterceptorFastApi) \ + SC(call_global_inline, V8.CallGlobalInline) \ + SC(call_global_inline_miss, V8.CallGlobalInlineMiss) \ + SC(constructed_objects, V8.ConstructedObjects) \ + SC(constructed_objects_runtime, V8.ConstructedObjectsRuntime) \ + SC(constructed_objects_stub, V8.ConstructedObjectsStub) \ + SC(array_function_runtime, V8.ArrayFunctionRuntime) \ + SC(array_function_native, V8.ArrayFunctionNative) \ + SC(for_in, V8.ForIn) \ + SC(enum_cache_hits, V8.EnumCacheHits) \ + SC(enum_cache_misses, V8.EnumCacheMisses) \ + SC(reloc_info_count, V8.RelocInfoCount) \ + SC(reloc_info_size, V8.RelocInfoSize) \ + SC(zone_segment_bytes, V8.ZoneSegmentBytes) \ + SC(compute_entry_frame, V8.ComputeEntryFrame) \ + SC(generic_binary_stub_calls, V8.GenericBinaryStubCalls) \ + SC(generic_binary_stub_calls_regs, V8.GenericBinaryStubCallsRegs) \ + SC(string_add_runtime, V8.StringAddRuntime) \ + SC(string_add_native, V8.StringAddNative) \ + SC(sub_string_runtime, V8.SubStringRuntime) \ + SC(sub_string_native, V8.SubStringNative) \ + SC(string_compare_native, V8.StringCompareNative) \ + SC(string_compare_runtime, V8.StringCompareRuntime) \ + SC(regexp_entry_runtime, V8.RegExpEntryRuntime) \ + SC(regexp_entry_native, V8.RegExpEntryNative) \ + SC(number_to_string_native, V8.NumberToStringNative) \ + SC(number_to_string_runtime, V8.NumberToStringRuntime) \ + SC(math_abs, V8.MathAbs) \ + SC(math_acos, V8.MathAcos) \ + SC(math_asin, V8.MathAsin) \ + SC(math_atan, V8.MathAtan) \ + SC(math_atan2, V8.MathAtan2) \ + SC(math_ceil, V8.MathCeil) \ + SC(math_cos, V8.MathCos) \ + SC(math_exp, V8.MathExp) \ + SC(math_floor, V8.MathFloor) \ + SC(math_log, V8.MathLog) \ + SC(math_pow, V8.MathPow) \ + SC(math_round, V8.MathRound) \ + SC(math_sin, V8.MathSin) \ + SC(math_sqrt, V8.MathSqrt) \ + SC(math_tan, V8.MathTan) \ + SC(transcendental_cache_hit, V8.TranscendentalCacheHit) \ + SC(transcendental_cache_miss, V8.TranscendentalCacheMiss) -#define STATS_COUNTER_LIST_2(SC) \ - /* Number of code stubs. */ \ - SC(code_stubs, V8.CodeStubs) \ - /* Amount of stub code. */ \ - SC(total_stubs_code_size, V8.TotalStubsCodeSize) \ - /* Amount of (JS) compiled code. */ \ - SC(total_compiled_code_size, V8.TotalCompiledCodeSize) \ - SC(gc_compactor_caused_by_request, V8.GCCompactorCausedByRequest) \ - SC(gc_compactor_caused_by_promoted_data, \ - V8.GCCompactorCausedByPromotedData) \ - SC(gc_compactor_caused_by_oldspace_exhaustion, \ - V8.GCCompactorCausedByOldspaceExhaustion) \ - SC(gc_compactor_caused_by_weak_handles, \ - V8.GCCompactorCausedByWeakHandles) \ - SC(gc_last_resort_from_js, V8.GCLastResortFromJS) \ - SC(gc_last_resort_from_handles, V8.GCLastResortFromHandles) \ - /* How is the generic keyed-load stub used? */ \ - SC(keyed_load_generic_smi, V8.KeyedLoadGenericSmi) \ - SC(keyed_load_generic_symbol, V8.KeyedLoadGenericSymbol) \ - SC(keyed_load_generic_slow, V8.KeyedLoadGenericSlow) \ - SC(keyed_load_external_array_slow, V8.KeyedLoadExternalArraySlow) \ - /* Count how much the monomorphic keyed-load stubs are hit. */ \ - SC(keyed_load_function_prototype, V8.KeyedLoadFunctionPrototype) \ - SC(keyed_load_string_length, V8.KeyedLoadStringLength) \ - SC(keyed_load_array_length, V8.KeyedLoadArrayLength) \ - SC(keyed_load_constant_function, V8.KeyedLoadConstantFunction) \ - SC(keyed_load_field, V8.KeyedLoadField) \ - SC(keyed_load_callback, V8.KeyedLoadCallback) \ - SC(keyed_load_interceptor, V8.KeyedLoadInterceptor) \ - SC(keyed_load_inline, V8.KeyedLoadInline) \ - SC(keyed_load_inline_miss, V8.KeyedLoadInlineMiss) \ - SC(named_load_inline, V8.NamedLoadInline) \ - SC(named_load_inline_miss, V8.NamedLoadInlineMiss) \ - SC(named_load_global_inline, V8.NamedLoadGlobalInline) \ - SC(named_load_global_inline_miss, V8.NamedLoadGlobalInlineMiss) \ - SC(keyed_store_field, V8.KeyedStoreField) \ - SC(keyed_store_inline, V8.KeyedStoreInline) \ - SC(keyed_store_inline_miss, V8.KeyedStoreInlineMiss) \ - SC(named_store_global_inline, V8.NamedStoreGlobalInline) \ - SC(named_store_global_inline_miss, V8.NamedStoreGlobalInlineMiss) \ - SC(call_global_inline, V8.CallGlobalInline) \ - SC(call_global_inline_miss, V8.CallGlobalInlineMiss) \ - SC(constructed_objects, V8.ConstructedObjects) \ - SC(constructed_objects_runtime, V8.ConstructedObjectsRuntime) \ - SC(constructed_objects_stub, V8.ConstructedObjectsStub) \ - SC(array_function_runtime, V8.ArrayFunctionRuntime) \ - SC(array_function_native, V8.ArrayFunctionNative) \ - SC(for_in, V8.ForIn) \ - SC(enum_cache_hits, V8.EnumCacheHits) \ - SC(enum_cache_misses, V8.EnumCacheMisses) \ - SC(reloc_info_count, V8.RelocInfoCount) \ - SC(reloc_info_size, V8.RelocInfoSize) \ - SC(zone_segment_bytes, V8.ZoneSegmentBytes) \ - SC(compute_entry_frame, V8.ComputeEntryFrame) \ - SC(generic_binary_stub_calls, V8.GenericBinaryStubCalls) \ - SC(generic_binary_stub_calls_regs, V8.GenericBinaryStubCallsRegs) \ - SC(string_add_runtime, V8.StringAddRuntime) \ - SC(string_add_native, V8.StringAddNative) \ - SC(sub_string_runtime, V8.SubStringRuntime) \ - SC(sub_string_native, V8.SubStringNative) \ - SC(string_compare_native, V8.StringCompareNative) \ - SC(string_compare_runtime, V8.StringCompareRuntime) \ - SC(regexp_entry_runtime, V8.RegExpEntryRuntime) \ - SC(regexp_entry_native, V8.RegExpEntryNative) // This file contains all the v8 counters that are in use. class Counters : AllStatic { diff --git a/src/v8natives.js b/src/v8natives.js index 5a47211a..6a32d7bd 100644 --- a/src/v8natives.js +++ b/src/v8natives.js @@ -56,7 +56,7 @@ function InstallFunctions(object, attributes, functions) { %FunctionSetName(f, key); %SetProperty(object, key, f, attributes); } - %TransformToFastProperties(object); + %ToFastProperties(object); } // Emulates JSC by installing functions on a hidden prototype that @@ -623,9 +623,8 @@ function ObjectGetOwnPropertyNames(obj) { if (%GetInterceptorInfo(obj) & 1) { var indexedInterceptorNames = %GetIndexedInterceptorElementNames(obj); - if (indexedInterceptorNames) { + if (indexedInterceptorNames) propertyNames = propertyNames.concat(indexedInterceptorNames); - } } // Find all the named properties. @@ -643,6 +642,10 @@ function ObjectGetOwnPropertyNames(obj) { } } + // Property names are expected to be strings. + for (var i = 0; i < propertyNames.length; ++i) + propertyNames[i] = ToString(propertyNames[i]); + return propertyNames; } @@ -911,7 +914,7 @@ function SetupNumber() { "POSITIVE_INFINITY", 1/0, DONT_ENUM | DONT_DELETE | READ_ONLY); - %TransformToFastProperties($Number); + %ToFastProperties($Number); // Setup non-enumerable functions on the Number prototype object. InstallFunctions($Number.prototype, DONT_ENUM, $Array( diff --git a/src/version.cc b/src/version.cc index a2008616..f6d84f31 100644 --- a/src/version.cc +++ b/src/version.cc @@ -34,7 +34,7 @@ // cannot be changed without changing the SCons build script. #define MAJOR_VERSION 2 #define MINOR_VERSION 1 -#define BUILD_NUMBER 1 +#define BUILD_NUMBER 3 #define PATCH_LEVEL 0 #define CANDIDATE_VERSION true diff --git a/src/virtual-frame.cc b/src/virtual-frame.cc index 44e5fae4..3624e254 100644 --- a/src/virtual-frame.cc +++ b/src/virtual-frame.cc @@ -48,7 +48,13 @@ VirtualFrame::VirtualFrame(VirtualFrame* original) } -FrameElement VirtualFrame::CopyElementAt(int index) { +// Create a duplicate of an existing valid frame element. +// We can pass an optional number type information that will override the +// existing information about the backing element. The new information must +// not conflict with the existing type information and must be equally or +// more precise. The default parameter value kUninitialized means that there +// is no additional information. +FrameElement VirtualFrame::CopyElementAt(int index, NumberInfo::Type info) { ASSERT(index >= 0); ASSERT(index < element_count()); @@ -71,15 +77,26 @@ FrameElement VirtualFrame::CopyElementAt(int index) { // Fall through. case FrameElement::MEMORY: // Fall through. - case FrameElement::REGISTER: + case FrameElement::REGISTER: { // All copies are backed by memory or register locations. result.set_type(FrameElement::COPY); result.clear_copied(); result.clear_sync(); result.set_index(index); elements_[index].set_copied(); + // Update backing element's number information. + NumberInfo::Type existing = elements_[index].number_info(); + ASSERT(existing != NumberInfo::kUninitialized); + // Assert that the new type information (a) does not conflict with the + // existing one and (b) is equally or more precise. + ASSERT((info == NumberInfo::kUninitialized) || + (existing | info) != NumberInfo::kUninitialized); + ASSERT(existing <= info); + elements_[index].set_number_info(info != NumberInfo::kUninitialized + ? info + : existing); break; - + } case FrameElement::INVALID: // We should not try to copy invalid elements. UNREACHABLE(); @@ -98,7 +115,7 @@ void VirtualFrame::Adjust(int count) { ASSERT(stack_pointer_ == element_count() - 1); for (int i = 0; i < count; i++) { - elements_.Add(FrameElement::MemoryElement()); + elements_.Add(FrameElement::MemoryElement(NumberInfo::kUnknown)); } stack_pointer_ += count; } @@ -144,8 +161,16 @@ void VirtualFrame::SpillElementAt(int index) { if (!elements_[index].is_valid()) return; SyncElementAt(index); + // Number type information is preserved. + // Copies get their number information from their backing element. + NumberInfo::Type info; + if (!elements_[index].is_copy()) { + info = elements_[index].number_info(); + } else { + info = elements_[elements_[index].index()].number_info(); + } // The element is now in memory. Its copied flag is preserved. - FrameElement new_element = FrameElement::MemoryElement(); + FrameElement new_element = FrameElement::MemoryElement(info); if (elements_[index].is_copied()) { new_element.set_copied(); } @@ -268,7 +293,6 @@ void VirtualFrame::SetElementAt(int index, Result* value) { InvalidateFrameSlotAt(frame_index); - FrameElement new_element; if (value->is_register()) { if (is_used(value->reg())) { // The register already appears on the frame. Either the existing @@ -301,7 +325,8 @@ void VirtualFrame::SetElementAt(int index, Result* value) { Use(value->reg(), frame_index); elements_[frame_index] = FrameElement::RegisterElement(value->reg(), - FrameElement::NOT_SYNCED); + FrameElement::NOT_SYNCED, + value->number_info()); } } else { ASSERT(value->is_constant()); @@ -318,16 +343,15 @@ void VirtualFrame::PushFrameSlotAt(int index) { } -void VirtualFrame::Push(Register reg) { +void VirtualFrame::Push(Register reg, NumberInfo::Type info) { if (is_used(reg)) { int index = register_location(reg); - FrameElement element = CopyElementAt(index); + FrameElement element = CopyElementAt(index, info); elements_.Add(element); } else { Use(reg, element_count()); FrameElement element = - FrameElement::RegisterElement(reg, - FrameElement::NOT_SYNCED); + FrameElement::RegisterElement(reg, FrameElement::NOT_SYNCED, info); elements_.Add(element); } } diff --git a/src/x64/assembler-x64.cc b/src/x64/assembler-x64.cc index 1b6874e9..a994f45b 100644 --- a/src/x64/assembler-x64.cc +++ b/src/x64/assembler-x64.cc @@ -34,46 +34,6 @@ namespace v8 { namespace internal { // ----------------------------------------------------------------------------- -// Implementation of Register - -Register rax = { 0 }; -Register rcx = { 1 }; -Register rdx = { 2 }; -Register rbx = { 3 }; -Register rsp = { 4 }; -Register rbp = { 5 }; -Register rsi = { 6 }; -Register rdi = { 7 }; -Register r8 = { 8 }; -Register r9 = { 9 }; -Register r10 = { 10 }; -Register r11 = { 11 }; -Register r12 = { 12 }; -Register r13 = { 13 }; -Register r14 = { 14 }; -Register r15 = { 15 }; - -Register no_reg = { -1 }; - -XMMRegister xmm0 = { 0 }; -XMMRegister xmm1 = { 1 }; -XMMRegister xmm2 = { 2 }; -XMMRegister xmm3 = { 3 }; -XMMRegister xmm4 = { 4 }; -XMMRegister xmm5 = { 5 }; -XMMRegister xmm6 = { 6 }; -XMMRegister xmm7 = { 7 }; -XMMRegister xmm8 = { 8 }; -XMMRegister xmm9 = { 9 }; -XMMRegister xmm10 = { 10 }; -XMMRegister xmm11 = { 11 }; -XMMRegister xmm12 = { 12 }; -XMMRegister xmm13 = { 13 }; -XMMRegister xmm14 = { 14 }; -XMMRegister xmm15 = { 15 }; - - -// ----------------------------------------------------------------------------- // Implementation of CpuFeatures // The required user mode extensions in X64 are (from AMD64 ABI Table A.1): @@ -224,7 +184,7 @@ void RelocInfo::PatchCode(byte* instructions, int instruction_count) { // ----------------------------------------------------------------------------- // Implementation of Operand -Operand::Operand(Register base, int32_t disp): rex_(0) { +Operand::Operand(Register base, int32_t disp) : rex_(0) { len_ = 1; if (base.is(rsp) || base.is(r12)) { // SIB byte is needed to encode (rsp + offset) or (r12 + offset). @@ -246,7 +206,7 @@ Operand::Operand(Register base, int32_t disp): rex_(0) { Operand::Operand(Register base, Register index, ScaleFactor scale, - int32_t disp): rex_(0) { + int32_t disp) : rex_(0) { ASSERT(!index.is(rsp)); len_ = 1; set_sib(scale, index, base); @@ -264,6 +224,17 @@ Operand::Operand(Register base, } +Operand::Operand(Register index, + ScaleFactor scale, + int32_t disp) : rex_(0) { + ASSERT(!index.is(rsp)); + len_ = 1; + set_modrm(0, rsp); + set_sib(scale, index, rbp); + set_disp32(disp); +} + + // ----------------------------------------------------------------------------- // Implementation of Assembler. @@ -2508,6 +2479,38 @@ void Assembler::divsd(XMMRegister dst, XMMRegister src) { } +void Assembler::xorpd(XMMRegister dst, XMMRegister src) { + EnsureSpace ensure_space(this); + last_pc_ = pc_; + emit(0x66); + emit_optional_rex_32(dst, src); + emit(0x0f); + emit(0x57); + emit_sse_operand(dst, src); +} + + +void Assembler::comisd(XMMRegister dst, XMMRegister src) { + EnsureSpace ensure_space(this); + last_pc_ = pc_; + emit(0x66); + emit_optional_rex_32(dst, src); + emit(0x0f); + emit(0x2f); + emit_sse_operand(dst, src); +} + + +void Assembler::ucomisd(XMMRegister dst, XMMRegister src) { + EnsureSpace ensure_space(this); + last_pc_ = pc_; + emit(0x66); + emit_optional_rex_32(dst, src); + emit(0x0f); + emit(0x2e); + emit_sse_operand(dst, src); +} + void Assembler::emit_sse_operand(XMMRegister reg, const Operand& adr) { Register ireg = { reg.code() }; diff --git a/src/x64/assembler-x64.h b/src/x64/assembler-x64.h index 64fbd888..50195255 100644 --- a/src/x64/assembler-x64.h +++ b/src/x64/assembler-x64.h @@ -118,51 +118,23 @@ struct Register { int code_; }; -extern Register rax; -extern Register rcx; -extern Register rdx; -extern Register rbx; -extern Register rsp; -extern Register rbp; -extern Register rsi; -extern Register rdi; -extern Register r8; -extern Register r9; -extern Register r10; -extern Register r11; -extern Register r12; -extern Register r13; -extern Register r14; -extern Register r15; -extern Register no_reg; - - -struct MMXRegister { - bool is_valid() const { return 0 <= code_ && code_ < 2; } - int code() const { - ASSERT(is_valid()); - return code_; - } - - int code_; -}; - -extern MMXRegister mm0; -extern MMXRegister mm1; -extern MMXRegister mm2; -extern MMXRegister mm3; -extern MMXRegister mm4; -extern MMXRegister mm5; -extern MMXRegister mm6; -extern MMXRegister mm7; -extern MMXRegister mm8; -extern MMXRegister mm9; -extern MMXRegister mm10; -extern MMXRegister mm11; -extern MMXRegister mm12; -extern MMXRegister mm13; -extern MMXRegister mm14; -extern MMXRegister mm15; +const Register rax = { 0 }; +const Register rcx = { 1 }; +const Register rdx = { 2 }; +const Register rbx = { 3 }; +const Register rsp = { 4 }; +const Register rbp = { 5 }; +const Register rsi = { 6 }; +const Register rdi = { 7 }; +const Register r8 = { 8 }; +const Register r9 = { 9 }; +const Register r10 = { 10 }; +const Register r11 = { 11 }; +const Register r12 = { 12 }; +const Register r13 = { 13 }; +const Register r14 = { 14 }; +const Register r15 = { 15 }; +const Register no_reg = { -1 }; struct XMMRegister { @@ -186,22 +158,22 @@ struct XMMRegister { int code_; }; -extern XMMRegister xmm0; -extern XMMRegister xmm1; -extern XMMRegister xmm2; -extern XMMRegister xmm3; -extern XMMRegister xmm4; -extern XMMRegister xmm5; -extern XMMRegister xmm6; -extern XMMRegister xmm7; -extern XMMRegister xmm8; -extern XMMRegister xmm9; -extern XMMRegister xmm10; -extern XMMRegister xmm11; -extern XMMRegister xmm12; -extern XMMRegister xmm13; -extern XMMRegister xmm14; -extern XMMRegister xmm15; +const XMMRegister xmm0 = { 0 }; +const XMMRegister xmm1 = { 1 }; +const XMMRegister xmm2 = { 2 }; +const XMMRegister xmm3 = { 3 }; +const XMMRegister xmm4 = { 4 }; +const XMMRegister xmm5 = { 5 }; +const XMMRegister xmm6 = { 6 }; +const XMMRegister xmm7 = { 7 }; +const XMMRegister xmm8 = { 8 }; +const XMMRegister xmm9 = { 9 }; +const XMMRegister xmm10 = { 10 }; +const XMMRegister xmm11 = { 11 }; +const XMMRegister xmm12 = { 12 }; +const XMMRegister xmm13 = { 13 }; +const XMMRegister xmm14 = { 14 }; +const XMMRegister xmm15 = { 15 }; enum Condition { // any value < 0 is considered no_condition @@ -308,7 +280,6 @@ enum ScaleFactor { times_4 = 2, times_8 = 3, times_int_size = times_4, - times_half_pointer_size = times_4, times_pointer_size = times_8 }; @@ -1122,6 +1093,10 @@ class Assembler : public Malloced { void mulsd(XMMRegister dst, XMMRegister src); void divsd(XMMRegister dst, XMMRegister src); + void xorpd(XMMRegister dst, XMMRegister src); + + void comisd(XMMRegister dst, XMMRegister src); + void ucomisd(XMMRegister dst, XMMRegister src); void emit_sse_operand(XMMRegister dst, XMMRegister src); void emit_sse_operand(XMMRegister reg, const Operand& adr); @@ -1168,14 +1143,6 @@ class Assembler : public Malloced { static const int kMaximalBufferSize = 512*MB; static const int kMinimalBufferSize = 4*KB; - protected: - // void movsd(XMMRegister dst, const Operand& src); - // void movsd(const Operand& dst, XMMRegister src); - - // void emit_sse_operand(XMMRegister reg, const Operand& adr); - // void emit_sse_operand(XMMRegister dst, XMMRegister src); - - private: byte* addr_at(int pos) { return buffer_ + pos; } byte byte_at(int pos) { return buffer_[pos]; } diff --git a/src/x64/builtins-x64.cc b/src/x64/builtins-x64.cc index 0b95bba6..b3c5e33f 100644 --- a/src/x64/builtins-x64.cc +++ b/src/x64/builtins-x64.cc @@ -185,14 +185,14 @@ void Builtins::Generate_ArgumentsAdaptorTrampoline(MacroAssembler* masm) { void Builtins::Generate_FunctionCall(MacroAssembler* masm) { // Stack Layout: - // rsp: return address - // +1: Argument n - // +2: Argument n-1 + // rsp[0]: Return address + // rsp[1]: Argument n + // rsp[2]: Argument n-1 // ... - // +n: Argument 1 = receiver - // +n+1: Argument 0 = function to call + // rsp[n]: Argument 1 + // rsp[n+1]: Receiver (function to call) // - // rax contains the number of arguments, n, not counting the function. + // rax contains the number of arguments, n, not counting the receiver. // // 1. Make sure we have at least one argument. { Label done; @@ -205,31 +205,23 @@ void Builtins::Generate_FunctionCall(MacroAssembler* masm) { __ bind(&done); } - // 2. Get the function to call from the stack. - { Label done, non_function, function; - // The function to call is at position n+1 on the stack. - __ movq(rdi, Operand(rsp, rax, times_pointer_size, +1 * kPointerSize)); - __ JumpIfSmi(rdi, &non_function); - __ CmpObjectType(rdi, JS_FUNCTION_TYPE, rcx); - __ j(equal, &function); - - // Non-function called: Clear the function to force exception. - __ bind(&non_function); - __ xor_(rdi, rdi); - __ jmp(&done); + // 2. Get the function to call (passed as receiver) from the stack, check + // if it is a function. + Label non_function; + // The function to call is at position n+1 on the stack. + __ movq(rdi, Operand(rsp, rax, times_pointer_size, 1 * kPointerSize)); + __ JumpIfSmi(rdi, &non_function); + __ CmpObjectType(rdi, JS_FUNCTION_TYPE, rcx); + __ j(not_equal, &non_function); - // Function called: Change context eagerly to get the right global object. - __ bind(&function); + // 3a. Patch the first argument if necessary when calling a function. + Label shift_arguments; + { Label convert_to_object, use_global_receiver, patch_receiver; + // Change context eagerly in case we need the global receiver. __ movq(rsi, FieldOperand(rdi, JSFunction::kContextOffset)); - __ bind(&done); - } - - // 3. Make sure first argument is an object; convert if necessary. - { Label call_to_object, use_global_receiver, patch_receiver, done; __ movq(rbx, Operand(rsp, rax, times_pointer_size, 0)); - - __ JumpIfSmi(rbx, &call_to_object); + __ JumpIfSmi(rbx, &convert_to_object); __ CompareRoot(rbx, Heap::kNullValueRootIndex); __ j(equal, &use_global_receiver); @@ -237,31 +229,28 @@ void Builtins::Generate_FunctionCall(MacroAssembler* masm) { __ j(equal, &use_global_receiver); __ CmpObjectType(rbx, FIRST_JS_OBJECT_TYPE, rcx); - __ j(below, &call_to_object); + __ j(below, &convert_to_object); __ CmpInstanceType(rcx, LAST_JS_OBJECT_TYPE); - __ j(below_equal, &done); - - __ bind(&call_to_object); - __ EnterInternalFrame(); // preserves rax, rbx, rdi + __ j(below_equal, &shift_arguments); - // Store the arguments count on the stack (smi tagged). + __ bind(&convert_to_object); + __ EnterInternalFrame(); // In order to preserve argument count. __ Integer32ToSmi(rax, rax); __ push(rax); - __ push(rdi); // save edi across the call __ push(rbx); __ InvokeBuiltin(Builtins::TO_OBJECT, CALL_FUNCTION); __ movq(rbx, rax); - __ pop(rdi); // restore edi after the call - // Get the arguments count and untag it. __ pop(rax); __ SmiToInteger32(rax, rax); - __ LeaveInternalFrame(); + // Restore the function to rdi. + __ movq(rdi, Operand(rsp, rax, times_pointer_size, 1 * kPointerSize)); __ jmp(&patch_receiver); - // Use the global receiver object from the called function as the receiver. + // Use the global receiver object from the called function as the + // receiver. __ bind(&use_global_receiver); const int kGlobalIndex = Context::kHeaderSize + Context::GLOBAL_INDEX * kPointerSize; @@ -273,48 +262,57 @@ void Builtins::Generate_FunctionCall(MacroAssembler* masm) { __ bind(&patch_receiver); __ movq(Operand(rsp, rax, times_pointer_size, 0), rbx); - __ bind(&done); + __ jmp(&shift_arguments); } - // 4. Shift stuff one slot down the stack. + + // 3b. Patch the first argument when calling a non-function. The + // CALL_NON_FUNCTION builtin expects the non-function callee as + // receiver, so overwrite the first argument which will ultimately + // become the receiver. + __ bind(&non_function); + __ movq(Operand(rsp, rax, times_pointer_size, 0), rdi); + __ xor_(rdi, rdi); + + // 4. Shift arguments and return address one slot down on the stack + // (overwriting the original receiver). Adjust argument count to make + // the original first argument the new receiver. + __ bind(&shift_arguments); { Label loop; - __ lea(rcx, Operand(rax, +1)); // +1 ~ copy receiver too + __ movq(rcx, rax); __ bind(&loop); __ movq(rbx, Operand(rsp, rcx, times_pointer_size, 0)); __ movq(Operand(rsp, rcx, times_pointer_size, 1 * kPointerSize), rbx); __ decq(rcx); - __ j(not_zero, &loop); + __ j(not_sign, &loop); // While non-negative (to copy return address). + __ pop(rbx); // Discard copy of return address. + __ decq(rax); // One fewer argument (first argument is new receiver). } - // 5. Remove TOS (copy of last arguments), but keep return address. - __ pop(rbx); - __ pop(rcx); - __ push(rbx); - __ decq(rax); - - // 6. Check that function really was a function and get the code to - // call from the function and check that the number of expected - // arguments matches what we're providing. - { Label invoke, trampoline; + // 5a. Call non-function via tail call to CALL_NON_FUNCTION builtin. + { Label function; __ testq(rdi, rdi); - __ j(not_zero, &invoke); + __ j(not_zero, &function); __ xor_(rbx, rbx); __ GetBuiltinEntry(rdx, Builtins::CALL_NON_FUNCTION); - __ bind(&trampoline); __ Jump(Handle<Code>(builtin(ArgumentsAdaptorTrampoline)), RelocInfo::CODE_TARGET); - - __ bind(&invoke); - __ movq(rdx, FieldOperand(rdi, JSFunction::kSharedFunctionInfoOffset)); - __ movsxlq(rbx, - FieldOperand(rdx, SharedFunctionInfo::kFormalParameterCountOffset)); - __ movq(rdx, FieldOperand(rdx, SharedFunctionInfo::kCodeOffset)); - __ lea(rdx, FieldOperand(rdx, Code::kHeaderSize)); - __ cmpq(rax, rbx); - __ j(not_equal, &trampoline); + __ bind(&function); } - // 7. Jump (tail-call) to the code in register edx without checking arguments. + // 5b. Get the code to call from the function and check that the number of + // expected arguments matches what we're providing. If so, jump + // (tail-call) to the code in register edx without checking arguments. + __ movq(rdx, FieldOperand(rdi, JSFunction::kSharedFunctionInfoOffset)); + __ movsxlq(rbx, + FieldOperand(rdx, SharedFunctionInfo::kFormalParameterCountOffset)); + __ movq(rdx, FieldOperand(rdx, SharedFunctionInfo::kCodeOffset)); + __ lea(rdx, FieldOperand(rdx, Code::kHeaderSize)); + __ cmpq(rax, rbx); + __ j(not_equal, + Handle<Code>(builtin(ArgumentsAdaptorTrampoline)), + RelocInfo::CODE_TARGET); + ParameterCount expected(0); __ InvokeCode(rdx, expected, expected, JUMP_FUNCTION); } @@ -586,6 +584,7 @@ static void AllocateJSArray(MacroAssembler* masm, JSFunction::kPrototypeOrInitialMapOffset)); // Check whether an empty sized array is requested. + __ SmiToInteger64(array_size, array_size); __ testq(array_size, array_size); __ j(not_zero, ¬_empty); @@ -605,7 +604,7 @@ static void AllocateJSArray(MacroAssembler* masm, __ bind(¬_empty); ASSERT(kSmiTagSize == 1 && kSmiTag == 0); __ AllocateInNewSpace(JSArray::kSize + FixedArray::kHeaderSize, - times_half_pointer_size, // array_size is a smi. + times_pointer_size, array_size, result, elements_array_end, @@ -618,19 +617,20 @@ static void AllocateJSArray(MacroAssembler* masm, // result: JSObject // elements_array: initial map // elements_array_end: start of next object - // array_size: size of array (smi) + // array_size: size of array __ bind(&allocated); __ movq(FieldOperand(result, JSObject::kMapOffset), elements_array); __ Move(elements_array, Factory::empty_fixed_array()); __ movq(FieldOperand(result, JSArray::kPropertiesOffset), elements_array); // Field JSArray::kElementsOffset is initialized later. - __ movq(FieldOperand(result, JSArray::kLengthOffset), array_size); + __ Integer32ToSmi(scratch, array_size); + __ movq(FieldOperand(result, JSArray::kLengthOffset), scratch); // Calculate the location of the elements array and set elements array member // of the JSArray. // result: JSObject // elements_array_end: start of next object - // array_size: size of array (smi) + // array_size: size of array __ lea(elements_array, Operand(result, JSArray::kSize)); __ movq(FieldOperand(result, JSArray::kElementsOffset), elements_array); @@ -638,9 +638,8 @@ static void AllocateJSArray(MacroAssembler* masm, // result: JSObject // elements_array: elements array // elements_array_end: start of next object - // array_size: size of array (smi) + // array_size: size of array ASSERT(kSmiTag == 0); - __ SmiToInteger64(array_size, array_size); __ Move(FieldOperand(elements_array, JSObject::kMapOffset), Factory::fixed_array_map()); Label not_empty_2, fill_array; @@ -900,7 +899,11 @@ void Builtins::Generate_JSConstructCall(MacroAssembler* masm) { // edi: called object // eax: number of arguments __ bind(&non_function_call); - // Set expected number of arguments to zero (not changing eax). + // CALL_NON_FUNCTION expects the non-function constructor as receiver + // (instead of the original receiver from the call site). The receiver is + // stack element argc+1. + __ movq(Operand(rsp, rax, times_pointer_size, kPointerSize), rdi); + // Set expected number of arguments to zero (not changing rax). __ movq(rbx, Immediate(0)); __ GetBuiltinEntry(rdx, Builtins::CALL_NON_FUNCTION_AS_CONSTRUCTOR); __ Jump(Handle<Code>(builtin(ArgumentsAdaptorTrampoline)), diff --git a/src/x64/codegen-x64.cc b/src/x64/codegen-x64.cc index ee8f251b..e4188836 100644 --- a/src/x64/codegen-x64.cc +++ b/src/x64/codegen-x64.cc @@ -277,7 +277,7 @@ void CodeGenerator::DeclareGlobals(Handle<FixedArray> pairs) { } -void CodeGenerator::Generate(CompilationInfo* info, Mode mode) { +void CodeGenerator::Generate(CompilationInfo* info) { // Record the position for debugging purposes. CodeForFunctionPosition(info->function()); @@ -316,7 +316,7 @@ void CodeGenerator::Generate(CompilationInfo* info, Mode mode) { // rsi: callee's context allocator_->Initialize(); - if (mode == PRIMARY) { + if (info->mode() == CompilationInfo::PRIMARY) { frame_->Enter(); // Allocate space for locals and initialize them. @@ -407,6 +407,12 @@ void CodeGenerator::Generate(CompilationInfo* info, Mode mode) { // frame to match this state. frame_->Adjust(3); allocator_->Unuse(rdi); + + // Bind all the bailout labels to the beginning of the function. + List<CompilationInfo::Bailout*>* bailouts = info->bailouts(); + for (int i = 0; i < bailouts->length(); i++) { + __ bind(bailouts->at(i)->label()); + } } // Initialize the function return target after the locals are set @@ -1221,7 +1227,7 @@ void CodeGenerator::VisitSwitchStatement(SwitchStatement* node) { // Compare and branch to the body if true or the next test if // false. Prefer the next test as a fall through. ControlDestination dest(clause->body_target(), &next_test, false); - Comparison(equal, true, &dest); + Comparison(node, equal, true, &dest); // If the comparison fell through to the true target, jump to the // actual body. @@ -2218,8 +2224,7 @@ void CodeGenerator::VisitDebuggerStatement(DebuggerStatement* node) { // Spill everything, even constants, to the frame. frame_->SpillAll(); - DebuggerStatementStub ces; - frame_->CallStub(&ces, 0); + frame_->DebugBreak(); // Ignore the return value. #endif } @@ -2496,17 +2501,19 @@ void CodeGenerator::VisitArrayLiteral(ArrayLiteral* node) { // Load the literals array of the function. __ movq(literals.reg(), FieldOperand(literals.reg(), JSFunction::kLiteralsOffset)); - // Literal array. + frame_->Push(&literals); - // Literal index. frame_->Push(Smi::FromInt(node->literal_index())); - // Constant elements. frame_->Push(node->constant_elements()); + int length = node->values()->length(); Result clone; if (node->depth() > 1) { clone = frame_->CallRuntime(Runtime::kCreateArrayLiteral, 3); - } else { + } else if (length > FastCloneShallowArrayStub::kMaximumLength) { clone = frame_->CallRuntime(Runtime::kCreateArrayLiteralShallow, 3); + } else { + FastCloneShallowArrayStub stub(length); + clone = frame_->CallStub(&stub, 3); } frame_->Push(&clone); @@ -2756,9 +2763,6 @@ void CodeGenerator::VisitCall(Call* node) { // JavaScript example: 'foo(1, 2, 3)' // foo is global // ---------------------------------- - // Push the name of the function and the receiver onto the stack. - frame_->Push(var->name()); - // Pass the global object as the receiver and let the IC stub // patch the stack to use the global proxy as 'this' in the // invoked function. @@ -2770,6 +2774,9 @@ void CodeGenerator::VisitCall(Call* node) { Load(args->at(i)); } + // Push the name of the function on the frame. + frame_->Push(var->name()); + // Call the IC initialization code. CodeForSourcePosition(node->position()); Result result = frame_->CallCallIC(RelocInfo::CODE_TARGET_CONTEXT, @@ -2777,7 +2784,7 @@ void CodeGenerator::VisitCall(Call* node) { loop_nesting()); frame_->RestoreContextRegister(); // Replace the function on the stack with the result. - frame_->SetElementAt(0, &result); + frame_->Push(&result); } else if (var != NULL && var->slot() != NULL && var->slot()->type() == Slot::LOOKUP) { @@ -2830,8 +2837,7 @@ void CodeGenerator::VisitCall(Call* node) { node->position()); } else { - // Push the name of the function and the receiver onto the stack. - frame_->Push(name); + // Push the receiver onto the frame. Load(property->obj()); // Load the arguments. @@ -2840,14 +2846,16 @@ void CodeGenerator::VisitCall(Call* node) { Load(args->at(i)); } + // Push the name of the function onto the frame. + frame_->Push(name); + // Call the IC initialization code. CodeForSourcePosition(node->position()); Result result = frame_->CallCallIC(RelocInfo::CODE_TARGET, arg_count, loop_nesting()); frame_->RestoreContextRegister(); - // Replace the function on the stack with the result. - frame_->SetElementAt(0, &result); + frame_->Push(&result); } } else { @@ -2938,8 +2946,6 @@ void CodeGenerator::VisitCallRuntime(CallRuntime* node) { Runtime::Function* function = node->function(); if (function == NULL) { - // Prepare stack for calling JS runtime function. - frame_->Push(node->name()); // Push the builtins object found in the current global object. Result temp = allocator()->Allocate(); ASSERT(temp.is_valid()); @@ -2957,11 +2963,12 @@ void CodeGenerator::VisitCallRuntime(CallRuntime* node) { if (function == NULL) { // Call the JS runtime function. + frame_->Push(node->name()); Result answer = frame_->CallCallIC(RelocInfo::CODE_TARGET, arg_count, loop_nesting_); frame_->RestoreContextRegister(); - frame_->SetElementAt(0, &answer); + frame_->Push(&answer); } else { // Call the C runtime function. Result answer = frame_->CallRuntime(function, arg_count); @@ -3070,7 +3077,6 @@ void CodeGenerator::VisitUnaryOperation(UnaryOperation* node) { case Token::SUB: { GenericUnaryOpStub stub(Token::SUB, overwrite); - // TODO(1222589): remove dependency of TOS being cached inside stub Result operand = frame_->Pop(); Result answer = frame_->CallStub(&stub, &operand); frame_->Push(&answer); @@ -3586,7 +3592,7 @@ void CodeGenerator::VisitCompareOperation(CompareOperation* node) { } Load(left); Load(right); - Comparison(cc, strict, destination()); + Comparison(node, cc, strict, destination()); } @@ -3627,6 +3633,22 @@ void CodeGenerator::GenerateIsArray(ZoneList<Expression*>* args) { } +void CodeGenerator::GenerateIsRegExp(ZoneList<Expression*>* args) { + ASSERT(args->length() == 1); + Load(args->at(0)); + Result value = frame_->Pop(); + value.ToRegister(); + ASSERT(value.is_valid()); + Condition is_smi = masm_->CheckSmi(value.reg()); + destination()->false_target()->Branch(is_smi); + // It is a heap object - get map. + // Check if the object is a regexp. + __ CmpObjectType(value.reg(), JS_REGEXP_TYPE, kScratchRegister); + value.Unuse(); + destination()->Split(equal); +} + + void CodeGenerator::GenerateIsObject(ZoneList<Expression*>* args) { // This generates a fast version of: // (typeof(arg) === 'object' || %_ClassOf(arg) == 'RegExp') @@ -3971,6 +3993,35 @@ void CodeGenerator::GenerateRegExpExec(ZoneList<Expression*>* args) { } +void CodeGenerator::GenerateNumberToString(ZoneList<Expression*>* args) { + ASSERT_EQ(args->length(), 1); + + // Load the argument on the stack and jump to the runtime. + Load(args->at(0)); + + Result answer = frame_->CallRuntime(Runtime::kNumberToString, 1); + frame_->Push(&answer); +} + + +void CodeGenerator::GenerateMathSin(ZoneList<Expression*>* args) { + ASSERT_EQ(args->length(), 1); + // Load the argument on the stack and jump to the runtime. + Load(args->at(0)); + Result answer = frame_->CallRuntime(Runtime::kMath_sin, 1); + frame_->Push(&answer); +} + + +void CodeGenerator::GenerateMathCos(ZoneList<Expression*>* args) { + ASSERT_EQ(args->length(), 1); + // Load the argument on the stack and jump to the runtime. + Load(args->at(0)); + Result answer = frame_->CallRuntime(Runtime::kMath_cos, 1); + frame_->Push(&answer); +} + + void CodeGenerator::GenerateStringAdd(ZoneList<Expression*>* args) { ASSERT_EQ(2, args->length()); @@ -4260,34 +4311,52 @@ void CodeGenerator::ToBoolean(ControlDestination* dest) { // The value to convert should be popped from the frame. Result value = frame_->Pop(); value.ToRegister(); - // Fast case checks. - // 'false' => false. - __ CompareRoot(value.reg(), Heap::kFalseValueRootIndex); - dest->false_target()->Branch(equal); + if (value.is_number()) { + Comment cmnt(masm_, "ONLY_NUMBER"); + // Fast case if NumberInfo indicates only numbers. + if (FLAG_debug_code) { + __ AbortIfNotNumber(value.reg(), "ToBoolean operand is not a number."); + } + // Smi => false iff zero. + __ SmiCompare(value.reg(), Smi::FromInt(0)); + dest->false_target()->Branch(equal); + Condition is_smi = masm_->CheckSmi(value.reg()); + dest->true_target()->Branch(is_smi); + __ fldz(); + __ fld_d(FieldOperand(value.reg(), HeapNumber::kValueOffset)); + __ FCmp(); + value.Unuse(); + dest->Split(not_zero); + } else { + // Fast case checks. + // 'false' => false. + __ CompareRoot(value.reg(), Heap::kFalseValueRootIndex); + dest->false_target()->Branch(equal); - // 'true' => true. - __ CompareRoot(value.reg(), Heap::kTrueValueRootIndex); - dest->true_target()->Branch(equal); + // 'true' => true. + __ CompareRoot(value.reg(), Heap::kTrueValueRootIndex); + dest->true_target()->Branch(equal); - // 'undefined' => false. - __ CompareRoot(value.reg(), Heap::kUndefinedValueRootIndex); - dest->false_target()->Branch(equal); + // 'undefined' => false. + __ CompareRoot(value.reg(), Heap::kUndefinedValueRootIndex); + dest->false_target()->Branch(equal); - // Smi => false iff zero. - __ SmiCompare(value.reg(), Smi::FromInt(0)); - dest->false_target()->Branch(equal); - Condition is_smi = masm_->CheckSmi(value.reg()); - dest->true_target()->Branch(is_smi); + // Smi => false iff zero. + __ SmiCompare(value.reg(), Smi::FromInt(0)); + dest->false_target()->Branch(equal); + Condition is_smi = masm_->CheckSmi(value.reg()); + dest->true_target()->Branch(is_smi); - // Call the stub for all other cases. - frame_->Push(&value); // Undo the Pop() from above. - ToBooleanStub stub; - Result temp = frame_->CallStub(&stub, 1); - // Convert the result to a condition code. - __ testq(temp.reg(), temp.reg()); - temp.Unuse(); - dest->Split(not_equal); + // Call the stub for all other cases. + frame_->Push(&value); // Undo the Pop() from above. + ToBooleanStub stub; + Result temp = frame_->CallStub(&stub, 1); + // Convert the result to a condition code. + __ testq(temp.reg(), temp.reg()); + temp.Unuse(); + dest->Split(not_equal); + } } @@ -4868,7 +4937,8 @@ void CodeGenerator::LoadTypeofExpression(Expression* expr) { } -void CodeGenerator::Comparison(Condition cc, +void CodeGenerator::Comparison(AstNode* node, + Condition cc, bool strict, ControlDestination* dest) { // Strict only makes sense for equality comparisons. @@ -4915,7 +4985,8 @@ void CodeGenerator::Comparison(Condition cc, default: UNREACHABLE(); } - } else { // Only one side is a constant Smi. + } else { + // Only one side is a constant Smi. // If left side is a constant Smi, reverse the operands. // Since one side is a constant Smi, conversion order does not matter. if (left_side_constant_smi) { @@ -4929,6 +5000,8 @@ void CodeGenerator::Comparison(Condition cc, // Implement comparison against a constant Smi, inlining the case // where both sides are Smis. left_side.ToRegister(); + Register left_reg = left_side.reg(); + Handle<Object> right_val = right_side.handle(); // Here we split control flow to the stub call and inlined cases // before finally splitting it to the control destination. We use @@ -4936,12 +5009,48 @@ void CodeGenerator::Comparison(Condition cc, // the first split. We manually handle the off-frame references // by reconstituting them on the non-fall-through path. JumpTarget is_smi; - Register left_reg = left_side.reg(); - Handle<Object> right_val = right_side.handle(); Condition left_is_smi = masm_->CheckSmi(left_side.reg()); is_smi.Branch(left_is_smi); + bool is_for_loop_compare = (node->AsCompareOperation() != NULL) + && node->AsCompareOperation()->is_for_loop_condition(); + if (!is_for_loop_compare && right_val->IsSmi()) { + // Right side is a constant smi and left side has been checked + // not to be a smi. + JumpTarget not_number; + __ Cmp(FieldOperand(left_reg, HeapObject::kMapOffset), + Factory::heap_number_map()); + not_number.Branch(not_equal, &left_side); + __ movsd(xmm1, + FieldOperand(left_reg, HeapNumber::kValueOffset)); + int value = Smi::cast(*right_val)->value(); + if (value == 0) { + __ xorpd(xmm0, xmm0); + } else { + Result temp = allocator()->Allocate(); + __ movl(temp.reg(), Immediate(value)); + __ cvtlsi2sd(xmm0, temp.reg()); + temp.Unuse(); + } + __ ucomisd(xmm1, xmm0); + // Jump to builtin for NaN. + not_number.Branch(parity_even, &left_side); + left_side.Unuse(); + Condition double_cc = cc; + switch (cc) { + case less: double_cc = below; break; + case equal: double_cc = equal; break; + case less_equal: double_cc = below_equal; break; + case greater: double_cc = above; break; + case greater_equal: double_cc = above_equal; break; + default: UNREACHABLE(); + } + dest->true_target()->Branch(double_cc); + dest->false_target()->Jump(); + not_number.Bind(&left_side); + } + // Setup and call the compare stub. CompareStub stub(cc, strict); Result result = frame_->CallStub(&stub, &left_side, &right_side); @@ -5114,26 +5223,34 @@ void CodeGenerator::GenericBinaryOperation(Token::Value op, // Neither operand is known to be a string. } - bool left_is_smi = left.is_constant() && left.handle()->IsSmi(); - bool left_is_non_smi = left.is_constant() && !left.handle()->IsSmi(); - bool right_is_smi = right.is_constant() && right.handle()->IsSmi(); - bool right_is_non_smi = right.is_constant() && !right.handle()->IsSmi(); + bool left_is_smi_constant = left.is_constant() && left.handle()->IsSmi(); + bool left_is_non_smi_constant = left.is_constant() && !left.handle()->IsSmi(); + bool right_is_smi_constant = right.is_constant() && right.handle()->IsSmi(); + bool right_is_non_smi_constant = + right.is_constant() && !right.handle()->IsSmi(); - if (left_is_smi && right_is_smi) { + if (left_is_smi_constant && right_is_smi_constant) { // Compute the constant result at compile time, and leave it on the frame. int left_int = Smi::cast(*left.handle())->value(); int right_int = Smi::cast(*right.handle())->value(); if (FoldConstantSmis(op, left_int, right_int)) return; } + // Get number type of left and right sub-expressions. + NumberInfo::Type operands_type = + NumberInfo::Combine(left.number_info(), right.number_info()); + Result answer; - if (left_is_non_smi || right_is_non_smi) { - GenericBinaryOpStub stub(op, overwrite_mode, NO_SMI_CODE_IN_STUB); + if (left_is_non_smi_constant || right_is_non_smi_constant) { + GenericBinaryOpStub stub(op, + overwrite_mode, + NO_SMI_CODE_IN_STUB, + operands_type); answer = stub.GenerateCall(masm_, frame_, &left, &right); - } else if (right_is_smi) { + } else if (right_is_smi_constant) { answer = ConstantSmiBinaryOperation(op, &left, right.handle(), type, false, overwrite_mode); - } else if (left_is_smi) { + } else if (left_is_smi_constant) { answer = ConstantSmiBinaryOperation(op, &right, left.handle(), type, true, overwrite_mode); } else { @@ -5145,10 +5262,62 @@ void CodeGenerator::GenericBinaryOperation(Token::Value op, if (loop_nesting() > 0 && (Token::IsBitOp(op) || type->IsLikelySmi())) { answer = LikelySmiBinaryOperation(op, &left, &right, overwrite_mode); } else { - GenericBinaryOpStub stub(op, overwrite_mode, NO_GENERIC_BINARY_FLAGS); + GenericBinaryOpStub stub(op, + overwrite_mode, + NO_GENERIC_BINARY_FLAGS, + operands_type); answer = stub.GenerateCall(masm_, frame_, &left, &right); } } + + // Set NumberInfo of result according to the operation performed. + // We rely on the fact that smis have a 32 bit payload on x64. + ASSERT(kSmiValueSize == 32); + NumberInfo::Type result_type = NumberInfo::kUnknown; + switch (op) { + case Token::COMMA: + result_type = right.number_info(); + break; + case Token::OR: + case Token::AND: + // Result type can be either of the two input types. + result_type = operands_type; + break; + case Token::BIT_OR: + case Token::BIT_XOR: + case Token::BIT_AND: + // Result is always a smi. + result_type = NumberInfo::kSmi; + break; + case Token::SAR: + case Token::SHL: + // Result is always a smi. + result_type = NumberInfo::kSmi; + break; + case Token::SHR: + // Result of x >>> y is always a smi if y >= 1, otherwise a number. + result_type = (right.is_constant() && right.handle()->IsSmi() + && Smi::cast(*right.handle())->value() >= 1) + ? NumberInfo::kSmi + : NumberInfo::kNumber; + break; + case Token::ADD: + // Result could be a string or a number. Check types of inputs. + result_type = NumberInfo::IsNumber(operands_type) + ? NumberInfo::kNumber + : NumberInfo::kUnknown; + break; + case Token::SUB: + case Token::MUL: + case Token::DIV: + case Token::MOD: + // Result is always a number. + result_type = NumberInfo::kNumber; + break; + default: + UNREACHABLE(); + } + answer.set_number_info(result_type); frame_->Push(&answer); } @@ -6221,6 +6390,63 @@ void FastNewContextStub::Generate(MacroAssembler* masm) { } +void FastCloneShallowArrayStub::Generate(MacroAssembler* masm) { + // Stack layout on entry: + // + // [rsp + kPointerSize]: constant elements. + // [rsp + (2 * kPointerSize)]: literal index. + // [rsp + (3 * kPointerSize)]: literals array. + + // All sizes here are multiples of kPointerSize. + int elements_size = (length_ > 0) ? FixedArray::SizeFor(length_) : 0; + int size = JSArray::kSize + elements_size; + + // Load boilerplate object into rcx and check if we need to create a + // boilerplate. + Label slow_case; + __ movq(rcx, Operand(rsp, 3 * kPointerSize)); + __ movq(rax, Operand(rsp, 2 * kPointerSize)); + SmiIndex index = masm->SmiToIndex(rax, rax, kPointerSizeLog2); + __ movq(rcx, + FieldOperand(rcx, index.reg, index.scale, FixedArray::kHeaderSize)); + __ CompareRoot(rcx, Heap::kUndefinedValueRootIndex); + __ j(equal, &slow_case); + + // Allocate both the JS array and the elements array in one big + // allocation. This avoids multiple limit checks. + __ AllocateInNewSpace(size, rax, rbx, rdx, &slow_case, TAG_OBJECT); + + // Copy the JS array part. + for (int i = 0; i < JSArray::kSize; i += kPointerSize) { + if ((i != JSArray::kElementsOffset) || (length_ == 0)) { + __ movq(rbx, FieldOperand(rcx, i)); + __ movq(FieldOperand(rax, i), rbx); + } + } + + if (length_ > 0) { + // Get hold of the elements array of the boilerplate and setup the + // elements pointer in the resulting object. + __ movq(rcx, FieldOperand(rcx, JSArray::kElementsOffset)); + __ lea(rdx, Operand(rax, JSArray::kSize)); + __ movq(FieldOperand(rax, JSArray::kElementsOffset), rdx); + + // Copy the elements array. + for (int i = 0; i < elements_size; i += kPointerSize) { + __ movq(rbx, FieldOperand(rcx, i)); + __ movq(FieldOperand(rdx, i), rbx); + } + } + + // Return and remove the on-stack parameters. + __ ret(3 * kPointerSize); + + __ bind(&slow_case); + ExternalReference runtime(Runtime::kCreateArrayLiteralShallow); + __ TailCallRuntime(runtime, 3, 1); +} + + void ToBooleanStub::Generate(MacroAssembler* masm) { Label false_result, true_result, not_string; __ movq(rax, Operand(rsp, 1 * kPointerSize)); @@ -7234,30 +7460,107 @@ void InstanceofStub::Generate(MacroAssembler* masm) { void ArgumentsAccessStub::GenerateNewObject(MacroAssembler* masm) { + // rsp[0] : return address + // rsp[8] : number of parameters + // rsp[16] : receiver displacement + // rsp[24] : function + // The displacement is used for skipping the return address and the // frame pointer on the stack. It is the offset of the last // parameter (if any) relative to the frame pointer. static const int kDisplacement = 2 * kPointerSize; // Check if the calling frame is an arguments adaptor frame. - Label runtime; + Label adaptor_frame, try_allocate, runtime; __ movq(rdx, Operand(rbp, StandardFrameConstants::kCallerFPOffset)); __ SmiCompare(Operand(rdx, StandardFrameConstants::kContextOffset), Smi::FromInt(StackFrame::ARGUMENTS_ADAPTOR)); - __ j(not_equal, &runtime); - // Value in rcx is Smi encoded. + __ j(equal, &adaptor_frame); + + // Get the length from the frame. + __ movq(rcx, Operand(rsp, 1 * kPointerSize)); + __ jmp(&try_allocate); // Patch the arguments.length and the parameters pointer. + __ bind(&adaptor_frame); __ movq(rcx, Operand(rdx, ArgumentsAdaptorFrameConstants::kLengthOffset)); __ movq(Operand(rsp, 1 * kPointerSize), rcx); - SmiIndex index = masm->SmiToIndex(rcx, rcx, kPointerSizeLog2); + // Do not clobber the length index for the indexing operation since + // it is used compute the size for allocation later. + SmiIndex index = masm->SmiToIndex(rbx, rcx, kPointerSizeLog2); __ lea(rdx, Operand(rdx, index.reg, index.scale, kDisplacement)); __ movq(Operand(rsp, 2 * kPointerSize), rdx); + // Try the new space allocation. Start out with computing the size of + // the arguments object and the elements array. + Label add_arguments_object; + __ bind(&try_allocate); + __ testq(rcx, rcx); + __ j(zero, &add_arguments_object); + index = masm->SmiToIndex(rcx, rcx, kPointerSizeLog2); + __ lea(rcx, Operand(index.reg, index.scale, FixedArray::kHeaderSize)); + __ bind(&add_arguments_object); + __ addq(rcx, Immediate(Heap::kArgumentsObjectSize)); + + // Do the allocation of both objects in one go. + __ AllocateInNewSpace(rcx, rax, rdx, rbx, &runtime, TAG_OBJECT); + + // Get the arguments boilerplate from the current (global) context. + int offset = Context::SlotOffset(Context::ARGUMENTS_BOILERPLATE_INDEX); + __ movq(rdi, Operand(rsi, Context::SlotOffset(Context::GLOBAL_INDEX))); + __ movq(rdi, FieldOperand(rdi, GlobalObject::kGlobalContextOffset)); + __ movq(rdi, Operand(rdi, offset)); + + // Copy the JS object part. + for (int i = 0; i < JSObject::kHeaderSize; i += kPointerSize) { + __ movq(kScratchRegister, FieldOperand(rdi, i)); + __ movq(FieldOperand(rax, i), kScratchRegister); + } + + // Setup the callee in-object property. + ASSERT(Heap::arguments_callee_index == 0); + __ movq(kScratchRegister, Operand(rsp, 3 * kPointerSize)); + __ movq(FieldOperand(rax, JSObject::kHeaderSize), kScratchRegister); + + // Get the length (smi tagged) and set that as an in-object property too. + ASSERT(Heap::arguments_length_index == 1); + __ movq(rcx, Operand(rsp, 1 * kPointerSize)); + __ movq(FieldOperand(rax, JSObject::kHeaderSize + kPointerSize), rcx); + + // If there are no actual arguments, we're done. + Label done; + __ testq(rcx, rcx); + __ j(zero, &done); + + // Get the parameters pointer from the stack and untag the length. + __ movq(rdx, Operand(rsp, 2 * kPointerSize)); + __ SmiToInteger32(rcx, rcx); + + // Setup the elements pointer in the allocated arguments object and + // initialize the header in the elements fixed array. + __ lea(rdi, Operand(rax, Heap::kArgumentsObjectSize)); + __ movq(FieldOperand(rax, JSObject::kElementsOffset), rdi); + __ LoadRoot(kScratchRegister, Heap::kFixedArrayMapRootIndex); + __ movq(FieldOperand(rdi, FixedArray::kMapOffset), kScratchRegister); + __ movq(FieldOperand(rdi, FixedArray::kLengthOffset), rcx); + + // Copy the fixed array slots. + Label loop; + __ bind(&loop); + __ movq(kScratchRegister, Operand(rdx, -1 * kPointerSize)); // Skip receiver. + __ movq(FieldOperand(rdi, FixedArray::kHeaderSize), kScratchRegister); + __ addq(rdi, Immediate(kPointerSize)); + __ subq(rdx, Immediate(kPointerSize)); + __ decq(rcx); + __ j(not_zero, &loop); + + // Return and remove the on-stack parameters. + __ bind(&done); + __ ret(3 * kPointerSize); + // Do the runtime call to allocate the arguments object. __ bind(&runtime); - Runtime::Function* f = Runtime::FunctionForId(Runtime::kNewArgumentsFast); - __ TailCallRuntime(ExternalReference(f), 3, f->result_size); + __ TailCallRuntime(ExternalReference(Runtime::kNewArgumentsFast), 3, 1); } @@ -7592,6 +7895,9 @@ void CallFunctionStub::Generate(MacroAssembler* masm) { // Slow-case: Non-function called. __ bind(&slow); + // CALL_NON_FUNCTION expects the non-function callee as receiver (instead + // of the original receiver from the call site). + __ movq(Operand(rsp, (argc_ + 1) * kPointerSize), rdi); __ Set(rax, argc_); __ Set(rbx, 0); __ GetBuiltinEntry(rdx, Builtins::CALL_NON_FUNCTION); @@ -7980,13 +8286,14 @@ const char* GenericBinaryOpStub::GetName() { } OS::SNPrintF(Vector<char>(name_, len), - "GenericBinaryOpStub_%s_%s%s_%s%s_%s", + "GenericBinaryOpStub_%s_%s%s_%s%s_%s%s", op_name, overwrite_name, (flags_ & NO_SMI_CODE_IN_STUB) ? "_NoSmiInStub" : "", args_in_registers_ ? "RegArgs" : "StackArgs", args_reversed_ ? "_R" : "", - use_sse3_ ? "SSE3" : "SSE2"); + use_sse3_ ? "SSE3" : "SSE2", + NumberInfo::ToString(operands_type_)); return name_; } @@ -8012,6 +8319,8 @@ void GenericBinaryOpStub::GenerateCall( } } else if (left.is(left_arg)) { __ movq(right_arg, right); + } else if (right.is(right_arg)) { + __ movq(left_arg, left); } else if (left.is(right_arg)) { if (IsOperationCommutative()) { __ movq(left_arg, right); @@ -8030,8 +8339,6 @@ void GenericBinaryOpStub::GenerateCall( __ movq(right_arg, right); __ movq(left_arg, left); } - } else if (right.is(right_arg)) { - __ movq(left_arg, left); } else { // Order of moves is not important. __ movq(left_arg, left); @@ -8067,6 +8374,10 @@ void GenericBinaryOpStub::GenerateCall( __ Move(left_arg, right); SetArgsReversed(); } else { + // For non-commutative operations, left and right_arg might be + // the same register. Therefore, the order of the moves is + // important here in order to not overwrite left before moving + // it to left_arg. __ movq(left_arg, left); __ Move(right_arg, right); } @@ -8099,8 +8410,12 @@ void GenericBinaryOpStub::GenerateCall( __ Move(right_arg, left); SetArgsReversed(); } else { - __ Move(left_arg, left); + // For non-commutative operations, right and left_arg might be + // the same register. Therefore, the order of the moves is + // important here in order to not overwrite right before moving + // it to right_arg. __ movq(right_arg, right); + __ Move(left_arg, left); } // Update flags to indicate that arguments are in registers. SetArgsInRegisters(); @@ -8302,7 +8617,15 @@ void GenericBinaryOpStub::Generate(MacroAssembler* masm) { case Token::DIV: { // rax: y // rdx: x - FloatingPointHelper::CheckNumberOperands(masm, &call_runtime); + if (NumberInfo::IsNumber(operands_type_)) { + if (FLAG_debug_code) { + // Assert at runtime that inputs are only numbers. + __ AbortIfNotNumber(rdx, "GenericBinaryOpStub operand not a number."); + __ AbortIfNotNumber(rax, "GenericBinaryOpStub operand not a number."); + } + } else { + FloatingPointHelper::CheckNumberOperands(masm, &call_runtime); + } // Fast-case: Both operands are numbers. // xmm4 and xmm5 are volatile XMM registers. FloatingPointHelper::LoadFloatOperands(masm, xmm4, xmm5); diff --git a/src/x64/codegen-x64.h b/src/x64/codegen-x64.h index 8fbbe5a6..4b0c77d2 100644 --- a/src/x64/codegen-x64.h +++ b/src/x64/codegen-x64.h @@ -294,15 +294,6 @@ enum ArgumentsAllocationMode { class CodeGenerator: public AstVisitor { public: - // Compilation mode. Either the compiler is used as the primary - // compiler and needs to setup everything or the compiler is used as - // the secondary compiler for split compilation and has to handle - // bailouts. - enum Mode { - PRIMARY, - SECONDARY - }; - // Takes a function literal, generates code for it. This function should only // be called by compiler.cc. static Handle<Code> MakeCode(CompilationInfo* info); @@ -385,7 +376,7 @@ class CodeGenerator: public AstVisitor { void VisitStatementsAndSpill(ZoneList<Statement*>* statements); // Main code generation function - void Generate(CompilationInfo* info, Mode mode); + void Generate(CompilationInfo* info); // Generate the return sequence code. Should be called no more than // once per compiled function, immediately after binding the return @@ -484,7 +475,8 @@ class CodeGenerator: public AstVisitor { Result* right, OverwriteMode overwrite_mode); - void Comparison(Condition cc, + void Comparison(AstNode* node, + Condition cc, bool strict, ControlDestination* destination); @@ -535,6 +527,7 @@ class CodeGenerator: public AstVisitor { void GenerateIsSmi(ZoneList<Expression*>* args); void GenerateIsNonNegativeSmi(ZoneList<Expression*>* args); void GenerateIsArray(ZoneList<Expression*>* args); + void GenerateIsRegExp(ZoneList<Expression*>* args); void GenerateIsObject(ZoneList<Expression*>* args); void GenerateIsFunction(ZoneList<Expression*>* args); void GenerateIsUndetectableObject(ZoneList<Expression*>* args); @@ -576,7 +569,14 @@ class CodeGenerator: public AstVisitor { // Support for direct calls from JavaScript to native RegExp code. void GenerateRegExpExec(ZoneList<Expression*>* args); - // Simple condition analysis. + // Fast support for number to string. + void GenerateNumberToString(ZoneList<Expression*>* args); + + // Fast call to math functions. + void GenerateMathSin(ZoneList<Expression*>* args); + void GenerateMathCos(ZoneList<Expression*>* args); + +// Simple condition analysis. enum ConditionAnalysis { ALWAYS_TRUE, ALWAYS_FALSE, @@ -654,13 +654,15 @@ class GenericBinaryOpStub: public CodeStub { public: GenericBinaryOpStub(Token::Value op, OverwriteMode mode, - GenericBinaryFlags flags) + GenericBinaryFlags flags, + NumberInfo::Type operands_type = NumberInfo::kUnknown) : op_(op), mode_(mode), flags_(flags), args_in_registers_(false), args_reversed_(false), - name_(NULL) { + name_(NULL), + operands_type_(operands_type) { use_sse3_ = CpuFeatures::IsSupported(SSE3); ASSERT(OpBits::is_valid(Token::NUM_TOKENS)); } @@ -685,28 +687,32 @@ class GenericBinaryOpStub: public CodeStub { bool args_reversed_; // Left and right argument are swapped. bool use_sse3_; char* name_; + NumberInfo::Type operands_type_; const char* GetName(); #ifdef DEBUG void Print() { - PrintF("GenericBinaryOpStub (op %s), " - "(mode %d, flags %d, registers %d, reversed %d)\n", + PrintF("GenericBinaryOpStub %d (op %s), " + "(mode %d, flags %d, registers %d, reversed %d, only_numbers %s)\n", + MinorKey(), Token::String(op_), static_cast<int>(mode_), static_cast<int>(flags_), static_cast<int>(args_in_registers_), - static_cast<int>(args_reversed_)); + static_cast<int>(args_reversed_), + NumberInfo::ToString(operands_type_)); } #endif - // Minor key encoding in 16 bits FRASOOOOOOOOOOMM. + // Minor key encoding in 16 bits NNNFRASOOOOOOOMM. class ModeBits: public BitField<OverwriteMode, 0, 2> {}; - class OpBits: public BitField<Token::Value, 2, 10> {}; - class SSE3Bits: public BitField<bool, 12, 1> {}; - class ArgsInRegistersBits: public BitField<bool, 13, 1> {}; - class ArgsReversedBits: public BitField<bool, 14, 1> {}; - class FlagBits: public BitField<GenericBinaryFlags, 15, 1> {}; + class OpBits: public BitField<Token::Value, 2, 7> {}; + class SSE3Bits: public BitField<bool, 9, 1> {}; + class ArgsInRegistersBits: public BitField<bool, 10, 1> {}; + class ArgsReversedBits: public BitField<bool, 11, 1> {}; + class FlagBits: public BitField<GenericBinaryFlags, 12, 1> {}; + class NumberInfoBits: public BitField<NumberInfo::Type, 13, 3> {}; Major MajorKey() { return GenericBinaryOp; } int MinorKey() { @@ -716,7 +722,8 @@ class GenericBinaryOpStub: public CodeStub { | FlagBits::encode(flags_) | SSE3Bits::encode(use_sse3_) | ArgsInRegistersBits::encode(args_in_registers_) - | ArgsReversedBits::encode(args_reversed_); + | ArgsReversedBits::encode(args_reversed_) + | NumberInfoBits::encode(operands_type_); } void Generate(MacroAssembler* masm); diff --git a/src/x64/disasm-x64.cc b/src/x64/disasm-x64.cc index ce3aae8a..547daeeb 100644 --- a/src/x64/disasm-x64.cc +++ b/src/x64/disasm-x64.cc @@ -993,7 +993,60 @@ int DisassemblerX64::TwoByteOpcodeInstruction(byte* data) { byte* current = data + 2; // At return, "current" points to the start of the next instruction. const char* mnemonic = TwoByteMnemonic(opcode); - if (opcode == 0x1F) { + if (operand_size_ == 0x66) { + // 0x66 0x0F prefix. + int mod, regop, rm; + get_modrm(*current, &mod, ®op, &rm); + const char* mnemonic = "?"; + if (opcode == 0x57) { + mnemonic = "xorpd"; + } else if (opcode == 0x2E) { + mnemonic = "comisd"; + } else if (opcode == 0x2F) { + mnemonic = "ucomisd"; + } else { + UnimplementedInstruction(); + } + AppendToBuffer("%s %s,", mnemonic, NameOfXMMRegister(regop)); + current += PrintRightXMMOperand(current); + } else if (group_1_prefix_ == 0xF2) { + // Beginning of instructions with prefix 0xF2. + + if (opcode == 0x11 || opcode == 0x10) { + // MOVSD: Move scalar double-precision fp to/from/between XMM registers. + AppendToBuffer("movsd "); + int mod, regop, rm; + get_modrm(*current, &mod, ®op, &rm); + if (opcode == 0x11) { + current += PrintRightOperand(current); + AppendToBuffer(",%s", NameOfXMMRegister(regop)); + } else { + AppendToBuffer("%s,", NameOfXMMRegister(regop)); + current += PrintRightOperand(current); + } + } else if (opcode == 0x2A) { + // CVTSI2SD: integer to XMM double conversion. + int mod, regop, rm; + get_modrm(*current, &mod, ®op, &rm); + AppendToBuffer("%s %s,", mnemonic, NameOfXMMRegister(regop)); + current += PrintRightOperand(current); + } else if ((opcode & 0xF8) == 0x58) { + // XMM arithmetic. Mnemonic was retrieved at the start of this function. + int mod, regop, rm; + get_modrm(*current, &mod, ®op, &rm); + AppendToBuffer("%s %s,", mnemonic, NameOfXMMRegister(regop)); + current += PrintRightXMMOperand(current); + } else { + UnimplementedInstruction(); + } + } else if (opcode == 0x2C && group_1_prefix_ == 0xF3) { + // Instruction with prefix 0xF3. + + // CVTTSS2SI: Convert scalar single-precision FP to dword integer. + // Assert that mod is not 3, so source is memory, not an XMM register. + ASSERT_NE(0xC0, *current & 0xC0); + current += PrintOperands("cvttss2si", REG_OPER_OP_ORDER, current); + } else if (opcode == 0x1F) { // NOP int mod, regop, rm; get_modrm(*current, &mod, ®op, &rm); @@ -1007,8 +1060,7 @@ int DisassemblerX64::TwoByteOpcodeInstruction(byte* data) { current += 4; } // else no immediate displacement. AppendToBuffer("nop"); - - } else if (opcode == 0xA2 || opcode == 0x31) { + } else if (opcode == 0xA2 || opcode == 0x31) { // RDTSC or CPUID AppendToBuffer("%s", mnemonic); @@ -1043,43 +1095,6 @@ int DisassemblerX64::TwoByteOpcodeInstruction(byte* data) { } else { AppendToBuffer(",%s,cl", NameOfCPURegister(regop)); } - } else if (group_1_prefix_ == 0xF2) { - // Beginning of instructions with prefix 0xF2. - - if (opcode == 0x11 || opcode == 0x10) { - // MOVSD: Move scalar double-precision fp to/from/between XMM registers. - AppendToBuffer("movsd "); - int mod, regop, rm; - get_modrm(*current, &mod, ®op, &rm); - if (opcode == 0x11) { - current += PrintRightOperand(current); - AppendToBuffer(",%s", NameOfXMMRegister(regop)); - } else { - AppendToBuffer("%s,", NameOfXMMRegister(regop)); - current += PrintRightOperand(current); - } - } else if (opcode == 0x2A) { - // CVTSI2SD: integer to XMM double conversion. - int mod, regop, rm; - get_modrm(*current, &mod, ®op, &rm); - AppendToBuffer("%s %s,", mnemonic, NameOfXMMRegister(regop)); - current += PrintRightOperand(current); - } else if ((opcode & 0xF8) == 0x58) { - // XMM arithmetic. Mnemonic was retrieved at the start of this function. - int mod, regop, rm; - get_modrm(*current, &mod, ®op, &rm); - AppendToBuffer("%s %s,", mnemonic, NameOfXMMRegister(regop)); - current += PrintRightXMMOperand(current); - } else { - UnimplementedInstruction(); - } - } else if (opcode == 0x2C && group_1_prefix_ == 0xF3) { - // Instruction with prefix 0xF3. - - // CVTTSS2SI: Convert scalar single-precision FP to dword integer. - // Assert that mod is not 3, so source is memory, not an XMM register. - ASSERT_NE(0xC0, *current & 0xC0); - current += PrintOperands("cvttss2si", REG_OPER_OP_ORDER, current); } else { UnimplementedInstruction(); } diff --git a/src/x64/fast-codegen-x64.cc b/src/x64/fast-codegen-x64.cc index c6e7be20..4dbf26a4 100644 --- a/src/x64/fast-codegen-x64.cc +++ b/src/x64/fast-codegen-x64.cc @@ -35,54 +35,91 @@ namespace internal { #define __ ACCESS_MASM(masm()) -void FastCodeGenerator::EmitLoadReceiver(Register reg) { +Register FastCodeGenerator::accumulator0() { return rax; } +Register FastCodeGenerator::accumulator1() { return rdx; } +Register FastCodeGenerator::scratch0() { return rcx; } +Register FastCodeGenerator::scratch1() { return rdi; } +Register FastCodeGenerator::receiver_reg() { return rbx; } +Register FastCodeGenerator::context_reg() { return rsi; } + + +void FastCodeGenerator::EmitLoadReceiver() { // Offset 2 is due to return address and saved frame pointer. int index = 2 + scope()->num_parameters(); - __ movq(reg, Operand(rbp, index * kPointerSize)); + __ movq(receiver_reg(), Operand(rbp, index * kPointerSize)); } -void FastCodeGenerator::EmitReceiverMapCheck() { - Comment cmnt(masm(), ";; MapCheck(this)"); - if (FLAG_print_ir) { - PrintF("MapCheck(this)\n"); - } +void FastCodeGenerator::EmitGlobalVariableLoad(Handle<Object> cell) { + ASSERT(!destination().is(no_reg)); + ASSERT(cell->IsJSGlobalPropertyCell()); - ASSERT(info()->has_receiver() && info()->receiver()->IsHeapObject()); - Handle<HeapObject> object = Handle<HeapObject>::cast(info()->receiver()); - Handle<Map> map(object->map()); + __ Move(destination(), cell); + __ movq(destination(), + FieldOperand(destination(), JSGlobalPropertyCell::kValueOffset)); + if (FLAG_debug_code) { + __ Cmp(destination(), Factory::the_hole_value()); + __ Check(not_equal, "DontDelete cells can't contain the hole"); + } - EmitLoadReceiver(rdx); - __ CheckMap(rdx, map, bailout(), false); + // The loaded value is not known to be a smi. + clear_as_smi(destination()); } -void FastCodeGenerator::EmitGlobalMapCheck() { - Comment cmnt(masm(), ";; GlobalMapCheck"); - if (FLAG_print_ir) { - PrintF(";; GlobalMapCheck()"); - } +void FastCodeGenerator::EmitThisPropertyStore(Handle<String> name) { + LookupResult lookup; + info()->receiver()->Lookup(*name, &lookup); - ASSERT(info()->has_global_object()); - Handle<Map> map(info()->global_object()->map()); + ASSERT(lookup.holder() == *info()->receiver()); + ASSERT(lookup.type() == FIELD); + Handle<Map> map(Handle<HeapObject>::cast(info()->receiver())->map()); + int index = lookup.GetFieldIndex() - map->inobject_properties(); + int offset = index * kPointerSize; - __ movq(rbx, CodeGenerator::GlobalObject()); - __ CheckMap(rbx, map, bailout(), true); -} + // We will emit the write barrier unless the stored value is statically + // known to be a smi. + bool needs_write_barrier = !is_smi(accumulator0()); + // Perform the store. Negative offsets are inobject properties. + if (offset < 0) { + offset += map->instance_size(); + __ movq(FieldOperand(receiver_reg(), offset), accumulator0()); + if (needs_write_barrier) { + // Preserve receiver from write barrier. + __ movq(scratch0(), receiver_reg()); + } + } else { + offset += FixedArray::kHeaderSize; + __ movq(scratch0(), + FieldOperand(receiver_reg(), JSObject::kPropertiesOffset)); + __ movq(FieldOperand(scratch0(), offset), accumulator0()); + } -void FastCodeGenerator::EmitGlobalVariableLoad(Handle<Object> cell) { - ASSERT(cell->IsJSGlobalPropertyCell()); - __ Move(rax, cell); - __ movq(rax, FieldOperand(rax, JSGlobalPropertyCell::kValueOffset)); - if (FLAG_debug_code) { - __ Cmp(rax, Factory::the_hole_value()); - __ Check(not_equal, "DontDelete cells can't contain the hole"); + if (needs_write_barrier) { + if (destination().is(no_reg)) { + // After RecordWrite accumulator0 is only accidently a smi, but it is + // already marked as not known to be one. + __ RecordWrite(scratch0(), offset, accumulator0(), scratch1()); + } else { + // Copy the value to the other accumulator to preserve a copy from the + // write barrier. One of the accumulators is available as a scratch + // register. Neither is a smi. + __ movq(accumulator1(), accumulator0()); + clear_as_smi(accumulator1()); + Register value_scratch = other_accumulator(destination()); + __ RecordWrite(scratch0(), offset, value_scratch, scratch1()); + } + } else if (destination().is(accumulator1())) { + __ movq(accumulator1(), accumulator0()); + // Is a smi because we do not need the write barrier. + set_as_smi(accumulator1()); } } -void FastCodeGenerator::EmitThisPropertyStore(Handle<String> name) { +void FastCodeGenerator::EmitThisPropertyLoad(Handle<String> name) { + ASSERT(!destination().is(no_reg)); LookupResult lookup; info()->receiver()->Lookup(*name, &lookup); @@ -92,19 +129,59 @@ void FastCodeGenerator::EmitThisPropertyStore(Handle<String> name) { int index = lookup.GetFieldIndex() - map->inobject_properties(); int offset = index * kPointerSize; - // Negative offsets are inobject properties. + // Perform the load. Negative offsets are inobject properties. if (offset < 0) { offset += map->instance_size(); - __ movq(rcx, rdx); // Copy receiver for write barrier. + __ movq(destination(), FieldOperand(receiver_reg(), offset)); } else { offset += FixedArray::kHeaderSize; - __ movq(rcx, FieldOperand(rdx, JSObject::kPropertiesOffset)); + __ movq(scratch0(), + FieldOperand(receiver_reg(), JSObject::kPropertiesOffset)); + __ movq(destination(), FieldOperand(scratch0(), offset)); } - // Perform the store. - __ movq(FieldOperand(rcx, offset), rax); - // Preserve value from write barrier in case it's needed. - __ movq(rbx, rax); - __ RecordWrite(rcx, offset, rbx, rdi); + + // The loaded value is not known to be a smi. + clear_as_smi(destination()); +} + + +void FastCodeGenerator::EmitBitOr() { + if (is_smi(accumulator0()) && is_smi(accumulator1())) { + // If both operands are known to be a smi then there is no need to check + // the operands or result. + if (destination().is(no_reg)) { + __ or_(accumulator1(), accumulator0()); + } else { + // Leave the result in the destination register. Bitwise or is + // commutative. + __ or_(destination(), other_accumulator(destination())); + } + } else { + // Left is in accumulator1, right in accumulator0. + if (destination().is(accumulator0())) { + __ movq(scratch0(), accumulator0()); + __ or_(destination(), accumulator1()); // Or is commutative. + Label* bailout = + info()->AddBailout(accumulator1(), scratch0()); // Left, right. + __ JumpIfNotSmi(destination(), bailout); + } else if (destination().is(accumulator1())) { + __ movq(scratch0(), accumulator1()); + __ or_(destination(), accumulator0()); + Label* bailout = info()->AddBailout(scratch0(), accumulator0()); + __ JumpIfNotSmi(destination(), bailout); + } else { + ASSERT(destination().is(no_reg)); + __ movq(scratch0(), accumulator1()); + __ or_(scratch0(), accumulator0()); + Label* bailout = info()->AddBailout(accumulator1(), accumulator0()); + __ JumpIfNotSmi(scratch0(), bailout); + } + } + + // If we didn't bailout, the result (in fact, both inputs too) is known to + // be a smi. + set_as_smi(accumulator0()); + set_as_smi(accumulator1()); } @@ -121,24 +198,43 @@ void FastCodeGenerator::Generate(CompilationInfo* compilation_info) { // Note that we keep a live register reference to esi (context) at this // point. - // Receiver (this) is allocated to rdx if there are this properties. - if (info()->has_this_properties()) EmitReceiverMapCheck(); + Label* bailout_to_beginning = info()->AddBailout(); + // Receiver (this) is allocated to a fixed register. + if (info()->has_this_properties()) { + Comment cmnt(masm(), ";; MapCheck(this)"); + if (FLAG_print_ir) { + PrintF("MapCheck(this)\n"); + } + ASSERT(info()->has_receiver() && info()->receiver()->IsHeapObject()); + Handle<HeapObject> object = Handle<HeapObject>::cast(info()->receiver()); + Handle<Map> map(object->map()); + EmitLoadReceiver(); + __ CheckMap(receiver_reg(), map, bailout_to_beginning, false); + } - // If there is a global variable access check if the global object - // is the same as at lazy-compilation time. - if (info()->has_globals()) EmitGlobalMapCheck(); + // If there is a global variable access check if the global object is the + // same as at lazy-compilation time. + if (info()->has_globals()) { + Comment cmnt(masm(), ";; MapCheck(GLOBAL)"); + if (FLAG_print_ir) { + PrintF("MapCheck(GLOBAL)\n"); + } + ASSERT(info()->has_global_object()); + Handle<Map> map(info()->global_object()->map()); + __ movq(scratch0(), CodeGenerator::GlobalObject()); + __ CheckMap(scratch0(), map, bailout_to_beginning, true); + } VisitStatements(info()->function()->body()); Comment return_cmnt(masm(), ";; Return(<undefined>)"); + if (FLAG_print_ir) { + PrintF("Return(<undefined>)\n"); + } __ LoadRoot(rax, Heap::kUndefinedValueRootIndex); - - Comment epilogue_cmnt(masm(), ";; Epilogue"); __ movq(rsp, rbp); __ pop(rbp); __ ret((scope()->num_parameters() + 1) * kPointerSize); - - __ bind(&bailout_); } diff --git a/src/x64/full-codegen-x64.cc b/src/x64/full-codegen-x64.cc index 2e95c688..30db660d 100644 --- a/src/x64/full-codegen-x64.cc +++ b/src/x64/full-codegen-x64.cc @@ -1015,6 +1015,92 @@ void FullCodeGenerator::VisitArrayLiteral(ArrayLiteral* expr) { } +void FullCodeGenerator::VisitAssignment(Assignment* expr) { + Comment cmnt(masm_, "[ Assignment"); + ASSERT(expr->op() != Token::INIT_CONST); + // Left-hand side can only be a property, a global or a (parameter or local) + // slot. Variables with rewrite to .arguments are treated as KEYED_PROPERTY. + enum LhsKind { VARIABLE, NAMED_PROPERTY, KEYED_PROPERTY }; + LhsKind assign_type = VARIABLE; + Property* prop = expr->target()->AsProperty(); + if (prop != NULL) { + assign_type = + (prop->key()->IsPropertyName()) ? NAMED_PROPERTY : KEYED_PROPERTY; + } + + // Evaluate LHS expression. + switch (assign_type) { + case VARIABLE: + // Nothing to do here. + break; + case NAMED_PROPERTY: + if (expr->is_compound()) { + // We need the receiver both on the stack and in the accumulator. + VisitForValue(prop->obj(), kAccumulator); + __ push(result_register()); + } else { + VisitForValue(prop->obj(), kStack); + } + break; + case KEYED_PROPERTY: + VisitForValue(prop->obj(), kStack); + VisitForValue(prop->key(), kStack); + break; + } + + // If we have a compound assignment: Get value of LHS expression and + // store in on top of the stack. + if (expr->is_compound()) { + Location saved_location = location_; + location_ = kStack; + switch (assign_type) { + case VARIABLE: + EmitVariableLoad(expr->target()->AsVariableProxy()->var(), + Expression::kValue); + break; + case NAMED_PROPERTY: + EmitNamedPropertyLoad(prop); + __ push(result_register()); + break; + case KEYED_PROPERTY: + EmitKeyedPropertyLoad(prop); + __ push(result_register()); + break; + } + location_ = saved_location; + } + + // Evaluate RHS expression. + Expression* rhs = expr->value(); + VisitForValue(rhs, kAccumulator); + + // If we have a compound assignment: Apply operator. + if (expr->is_compound()) { + Location saved_location = location_; + location_ = kAccumulator; + EmitBinaryOp(expr->binary_op(), Expression::kValue); + location_ = saved_location; + } + + // Record source position before possible IC call. + SetSourcePosition(expr->position()); + + // Store the value. + switch (assign_type) { + case VARIABLE: + EmitVariableAssignment(expr->target()->AsVariableProxy()->var(), + context_); + break; + case NAMED_PROPERTY: + EmitNamedPropertyAssignment(expr); + break; + case KEYED_PROPERTY: + EmitKeyedPropertyAssignment(expr); + break; + } +} + + void FullCodeGenerator::EmitNamedPropertyLoad(Property* prop) { SetSourcePosition(prop->position()); Literal* key = prop->key()->AsLiteral(); @@ -1200,7 +1286,7 @@ void FullCodeGenerator::VisitProperty(Property* expr) { void FullCodeGenerator::EmitCallWithIC(Call* expr, - Handle<Object> ignored, + Handle<Object> name, RelocInfo::Mode mode) { // Code common for calls using the IC. ZoneList<Expression*>* args = expr->arguments(); @@ -1208,6 +1294,7 @@ void FullCodeGenerator::EmitCallWithIC(Call* expr, for (int i = 0; i < arg_count; i++) { VisitForValue(args->at(i), kStack); } + __ Move(rcx, name); // Record source position for debugger. SetSourcePosition(expr->position()); // Call the IC initialization code. @@ -1217,8 +1304,7 @@ void FullCodeGenerator::EmitCallWithIC(Call* expr, __ Call(ic, mode); // Restore context register. __ movq(rsi, Operand(rbp, StandardFrameConstants::kContextOffset)); - // Discard the function left on TOS. - DropAndApply(1, context_, rax); + Apply(context_, rax); } @@ -1250,7 +1336,6 @@ void FullCodeGenerator::VisitCall(Call* expr) { UNREACHABLE(); } else if (var != NULL && !var->is_this() && var->is_global()) { // Call to a global variable. - __ Push(var->name()); // Push global object as receiver for the call IC lookup. __ push(CodeGenerator::GlobalObject()); EmitCallWithIC(expr, var->name(), RelocInfo::CODE_TARGET_CONTEXT); @@ -1264,7 +1349,6 @@ void FullCodeGenerator::VisitCall(Call* expr) { Literal* key = prop->key()->AsLiteral(); if (key != NULL && key->handle()->IsSymbol()) { // Call to a named property, use call IC. - __ Push(key->handle()); VisitForValue(prop->obj(), kStack); EmitCallWithIC(expr, key->handle(), RelocInfo::CODE_TARGET); } else { @@ -1355,7 +1439,6 @@ void FullCodeGenerator::VisitCallRuntime(CallRuntime* expr) { if (expr->is_jsruntime()) { // Prepare for calling JS runtime function. - __ Push(expr->name()); __ movq(rax, CodeGenerator::GlobalObject()); __ push(FieldOperand(rax, GlobalObject::kBuiltinsOffset)); } @@ -1367,18 +1450,17 @@ void FullCodeGenerator::VisitCallRuntime(CallRuntime* expr) { } if (expr->is_jsruntime()) { - // Call the JS runtime function. - Handle<Code> ic = CodeGenerator::ComputeCallInitialize(arg_count, - NOT_IN_LOOP); + // Call the JS runtime function using a call IC. + __ Move(rcx, expr->name()); + InLoopFlag in_loop = (loop_depth() > 0) ? IN_LOOP : NOT_IN_LOOP; + Handle<Code> ic = CodeGenerator::ComputeCallInitialize(arg_count, in_loop); __ call(ic, RelocInfo::CODE_TARGET); // Restore context register. __ movq(rsi, Operand(rbp, StandardFrameConstants::kContextOffset)); - // Discard the function left on TOS. - DropAndApply(1, context_, rax); } else { __ CallRuntime(expr->function(), arg_count); - Apply(context_, rax); } + Apply(context_, rax); } diff --git a/src/x64/ic-x64.cc b/src/x64/ic-x64.cc index 99a8c7d4..8d43332e 100644 --- a/src/x64/ic-x64.cc +++ b/src/x64/ic-x64.cc @@ -212,23 +212,37 @@ void KeyedStoreIC::RestoreInlinedVersion(Address address) { } -void KeyedLoadIC::Generate(MacroAssembler* masm, - ExternalReference const& f) { +void KeyedLoadIC::GenerateMiss(MacroAssembler* masm) { // ----------- S t a t e ------------- // -- rsp[0] : return address // -- rsp[8] : name // -- rsp[16] : receiver // ----------------------------------- - __ movq(rax, Operand(rsp, kPointerSize)); - __ movq(rcx, Operand(rsp, 2 * kPointerSize)); __ pop(rbx); - __ push(rcx); // receiver - __ push(rax); // name + __ push(Operand(rsp, 1 * kPointerSize)); // receiver + __ push(Operand(rsp, 1 * kPointerSize)); // name __ push(rbx); // return address // Perform tail call to the entry. - __ TailCallRuntime(f, 2, 1); + __ TailCallRuntime(ExternalReference(IC_Utility(kKeyedLoadIC_Miss)), 2, 1); +} + + +void KeyedLoadIC::GenerateRuntimeGetProperty(MacroAssembler* masm) { + // ----------- S t a t e ------------- + // -- rsp[0] : return address + // -- rsp[8] : name + // -- rsp[16] : receiver + // ----------------------------------- + + __ pop(rbx); + __ push(Operand(rsp, 1 * kPointerSize)); // receiver + __ push(Operand(rsp, 1 * kPointerSize)); // name + __ push(rbx); // return address + + // Perform tail call to the entry. + __ TailCallRuntime(ExternalReference(Runtime::kKeyedGetProperty), 2, 1); } @@ -301,7 +315,7 @@ void KeyedLoadIC::GenerateGeneric(MacroAssembler* masm) { // Slow case: Load name and receiver from stack and jump to runtime. __ bind(&slow); __ IncrementCounter(&Counters::keyed_load_generic_slow, 1); - Generate(masm, ExternalReference(Runtime::kKeyedGetProperty)); + GenerateRuntimeGetProperty(masm); __ bind(&check_string); // The key is not a smi. // Is it a string? @@ -538,21 +552,54 @@ void KeyedLoadIC::GenerateExternalArray(MacroAssembler* masm, // Slow case: Load name and receiver from stack and jump to runtime. __ bind(&slow); __ IncrementCounter(&Counters::keyed_load_external_array_slow, 1); - Generate(masm, ExternalReference(Runtime::kKeyedGetProperty)); + GenerateRuntimeGetProperty(masm); } -void KeyedLoadIC::GenerateMiss(MacroAssembler* masm) { +void KeyedLoadIC::GenerateIndexedInterceptor(MacroAssembler* masm) { // ----------- S t a t e ------------- // -- rsp[0] : return address - // -- rsp[8] : name + // -- rsp[8] : key // -- rsp[16] : receiver // ----------------------------------- - Generate(masm, ExternalReference(IC_Utility(kKeyedLoadIC_Miss))); + Label slow; + + // Load key and receiver. + __ movq(rax, Operand(rsp, kPointerSize)); + __ movq(rcx, Operand(rsp, 2 * kPointerSize)); + + // Check that the receiver isn't a smi. + __ JumpIfSmi(rcx, &slow); + + // Check that the key is a smi. + __ JumpIfNotSmi(rax, &slow); + + // Get the map of the receiver. + __ movq(rdx, FieldOperand(rcx, HeapObject::kMapOffset)); + + // Check that it has indexed interceptor and access checks + // are not enabled for this object. + __ movb(rdx, FieldOperand(rdx, Map::kBitFieldOffset)); + __ andb(rdx, Immediate(kSlowCaseBitFieldMask)); + __ cmpb(rdx, Immediate(1 << Map::kHasIndexedInterceptor)); + __ j(not_zero, &slow); + + // Everything is fine, call runtime. + __ pop(rdx); + __ push(rcx); // receiver + __ push(rax); // key + __ push(rdx); // return address + + // Perform tail call to the entry. + __ TailCallRuntime(ExternalReference( + IC_Utility(kKeyedLoadPropertyWithInterceptor)), 2, 1); + + __ bind(&slow); + GenerateMiss(masm); } -void KeyedStoreIC::Generate(MacroAssembler* masm, ExternalReference const& f) { +void KeyedStoreIC::GenerateMiss(MacroAssembler* masm) { // ----------- S t a t e ------------- // -- rax : value // -- rsp[0] : return address @@ -567,28 +614,26 @@ void KeyedStoreIC::Generate(MacroAssembler* masm, ExternalReference const& f) { __ push(rcx); // return address // Do tail-call to runtime routine. - __ TailCallRuntime(f, 3, 1); + __ TailCallRuntime(ExternalReference(IC_Utility(kKeyedStoreIC_Miss)), 3, 1); } -void KeyedStoreIC::GenerateExtendStorage(MacroAssembler* masm) { +void KeyedStoreIC::GenerateRuntimeSetProperty(MacroAssembler* masm) { // ----------- S t a t e ------------- // -- rax : value - // -- rcx : transition map // -- rsp[0] : return address // -- rsp[8] : key // -- rsp[16] : receiver // ----------------------------------- - __ pop(rbx); + __ pop(rcx); __ push(Operand(rsp, 1 * kPointerSize)); // receiver - __ push(rcx); // transition map + __ push(Operand(rsp, 1 * kPointerSize)); // key __ push(rax); // value - __ push(rbx); // return address + __ push(rcx); // return address // Do tail-call to runtime routine. - __ TailCallRuntime( - ExternalReference(IC_Utility(kSharedStoreIC_ExtendStorage)), 3, 1); + __ TailCallRuntime(ExternalReference(Runtime::kSetProperty), 3, 1); } @@ -642,7 +687,7 @@ void KeyedStoreIC::GenerateGeneric(MacroAssembler* masm) { // Slow case: call runtime. __ bind(&slow); - Generate(masm, ExternalReference(Runtime::kSetProperty)); + GenerateRuntimeSetProperty(masm); // Check whether the elements is a pixel array. // rax: value @@ -906,23 +951,29 @@ void KeyedStoreIC::GenerateExternalArray(MacroAssembler* masm, // Slow case: call runtime. __ bind(&slow); - Generate(masm, ExternalReference(Runtime::kSetProperty)); + GenerateRuntimeSetProperty(masm); } void CallIC::GenerateMiss(MacroAssembler* masm, int argc) { + // ----------- S t a t e ------------- + // rcx : function name + // rsp[0] : return address + // rsp[8] : argument argc + // rsp[16] : argument argc - 1 + // ... + // rsp[argc * 8] : argument 1 + // rsp[(argc + 1) * 8] : argument 0 = receiver + // ----------------------------------- // Get the receiver of the function from the stack; 1 ~ return address. __ movq(rdx, Operand(rsp, (argc + 1) * kPointerSize)); - // Get the name of the function to call from the stack. - // 2 ~ receiver, return address. - __ movq(rbx, Operand(rsp, (argc + 2) * kPointerSize)); // Enter an internal frame. __ EnterInternalFrame(); // Push the receiver and the name of the function. __ push(rdx); - __ push(rbx); + __ push(rcx); // Call the entry. CEntryStub stub(1); @@ -960,20 +1011,18 @@ Object* CallIC_Miss(Arguments args); void CallIC::GenerateMegamorphic(MacroAssembler* masm, int argc) { // ----------- S t a t e ------------- - // rsp[0] return address - // rsp[8] argument argc - // rsp[16] argument argc - 1 + // rcx : function name + // rsp[0] : return address + // rsp[8] : argument argc + // rsp[16] : argument argc - 1 // ... - // rsp[argc * 8] argument 1 - // rsp[(argc + 1) * 8] argument 0 = receiver - // rsp[(argc + 2) * 8] function name + // rsp[argc * 8] : argument 1 + // rsp[(argc + 1) * 8] : argument 0 = receiver // ----------------------------------- Label number, non_number, non_string, boolean, probe, miss; // Get the receiver of the function from the stack; 1 ~ return address. __ movq(rdx, Operand(rsp, (argc + 1) * kPointerSize)); - // Get the name of the function from the stack; 2 ~ return address, receiver - __ movq(rcx, Operand(rsp, (argc + 2) * kPointerSize)); // Probe the stub cache. Code::Flags flags = @@ -1026,6 +1075,16 @@ static void GenerateNormalHelper(MacroAssembler* masm, int argc, bool is_global_object, Label* miss) { + // ----------- S t a t e ------------- + // rcx : function name + // rdx : receiver + // rsp[0] : return address + // rsp[8] : argument argc + // rsp[16] : argument argc - 1 + // ... + // rsp[argc * 8] : argument 1 + // rsp[(argc + 1) * 8] : argument 0 = receiver + // ----------------------------------- // Search dictionary - put result in register rdx. GenerateDictionaryLoad(masm, miss, rax, rdx, rbx, rcx, CHECK_DICTIONARY); @@ -1052,20 +1111,18 @@ static void GenerateNormalHelper(MacroAssembler* masm, void CallIC::GenerateNormal(MacroAssembler* masm, int argc) { // ----------- S t a t e ------------- - // rsp[0] return address - // rsp[8] argument argc - // rsp[16] argument argc - 1 + // rcx : function name + // rsp[0] : return address + // rsp[8] : argument argc + // rsp[16] : argument argc - 1 // ... - // rsp[argc * 8] argument 1 - // rsp[(argc + 1) * 8] argument 0 = receiver - // rsp[(argc + 2) * 8] function name + // rsp[argc * 8] : argument 1 + // rsp[(argc + 1) * 8] : argument 0 = receiver // ----------------------------------- Label miss, global_object, non_global_object; // Get the receiver of the function from the stack. __ movq(rdx, Operand(rsp, (argc + 1) * kPointerSize)); - // Get the name of the function from the stack. - __ movq(rcx, Operand(rsp, (argc + 2) * kPointerSize)); // Check that the receiver isn't a smi. __ JumpIfSmi(rdx, &miss); @@ -1132,22 +1189,20 @@ void LoadIC::ClearInlinedVersion(Address address) { } -void LoadIC::Generate(MacroAssembler* masm, ExternalReference const& f) { +void LoadIC::GenerateMiss(MacroAssembler* masm) { // ----------- S t a t e ------------- // -- rcx : name // -- rsp[0] : return address // -- rsp[8] : receiver // ----------------------------------- - __ movq(rax, Operand(rsp, kPointerSize)); - __ pop(rbx); - __ push(rax); // receiver + __ push(Operand(rsp, 0)); // receiver __ push(rcx); // name __ push(rbx); // return address // Perform tail call to the entry. - __ TailCallRuntime(f, 2, 1); + __ TailCallRuntime(ExternalReference(IC_Utility(kLoadIC_Miss)), 2, 1); } @@ -1203,17 +1258,6 @@ void LoadIC::GenerateMegamorphic(MacroAssembler* masm) { } -void LoadIC::GenerateMiss(MacroAssembler* masm) { - // ----------- S t a t e ------------- - // -- rcx : name - // -- rsp[0] : return address - // -- rsp[8] : receiver - // ----------------------------------- - - Generate(masm, ExternalReference(IC_Utility(kLoadIC_Miss))); -} - - void LoadIC::GenerateNormal(MacroAssembler* masm) { // ----------- S t a t e ------------- // -- rcx : name @@ -1256,7 +1300,7 @@ void LoadIC::GenerateNormal(MacroAssembler* masm) { // Cache miss: Restore receiver from stack and jump to runtime. __ bind(&miss); __ movq(rax, Operand(rsp, 1 * kPointerSize)); - Generate(masm, ExternalReference(IC_Utility(kLoadIC_Miss))); + GenerateMiss(masm); } @@ -1270,13 +1314,12 @@ void LoadIC::GenerateStringLength(MacroAssembler* masm) { __ movq(rax, Operand(rsp, kPointerSize)); - StubCompiler::GenerateLoadStringLength(masm, rax, rdx, &miss); + StubCompiler::GenerateLoadStringLength(masm, rax, rdx, rbx, &miss); __ bind(&miss); StubCompiler::GenerateLoadMiss(masm, Code::LOAD_IC); } - bool LoadIC::PatchInlinedLoad(Address address, Object* map, int offset) { // The address of the instruction following the call. Address test_instruction_address = @@ -1304,6 +1347,7 @@ bool LoadIC::PatchInlinedLoad(Address address, Object* map, int offset) { return true; } + void StoreIC::GenerateMiss(MacroAssembler* masm) { // ----------- S t a t e ------------- // -- rax : value @@ -1322,24 +1366,6 @@ void StoreIC::GenerateMiss(MacroAssembler* masm) { __ TailCallRuntime(ExternalReference(IC_Utility(kStoreIC_Miss)), 3, 1); } -void StoreIC::GenerateExtendStorage(MacroAssembler* masm) { - // ----------- S t a t e ------------- - // -- rax : value - // -- rcx : Map (target of map transition) - // -- rdx : receiver - // -- rsp[0] : return address - // ----------------------------------- - - __ pop(rbx); - __ push(rdx); // receiver - __ push(rcx); // transition map - __ push(rax); // value - __ push(rbx); // return address - - // Perform tail call to the entry. - __ TailCallRuntime( - ExternalReference(IC_Utility(kSharedStoreIC_ExtendStorage)), 3, 1); -} void StoreIC::GenerateMegamorphic(MacroAssembler* masm) { // ----------- S t a t e ------------- diff --git a/src/x64/macro-assembler-x64.cc b/src/x64/macro-assembler-x64.cc index 56bbc202..90a9c75d 100644 --- a/src/x64/macro-assembler-x64.cc +++ b/src/x64/macro-assembler-x64.cc @@ -39,7 +39,6 @@ namespace internal { MacroAssembler::MacroAssembler(void* buffer, int size) : Assembler(buffer, size), - unresolved_(0), generating_stub_(false), allow_stub_calls_(true), code_object_(Heap::undefined_value()) { @@ -387,6 +386,16 @@ void MacroAssembler::CallRuntime(Runtime::Function* f, int num_arguments) { } +void MacroAssembler::CallExternalReference(const ExternalReference& ext, + int num_arguments) { + movq(rax, Immediate(num_arguments)); + movq(rbx, ext); + + CEntryStub stub(1); + CallStub(&stub); +} + + void MacroAssembler::TailCallRuntime(ExternalReference const& ext, int num_arguments, int result_size) { @@ -415,38 +424,30 @@ void MacroAssembler::JumpToRuntime(const ExternalReference& ext, } -void MacroAssembler::GetBuiltinEntry(Register target, Builtins::JavaScript id) { - bool resolved; - Handle<Code> code = ResolveBuiltin(id, &resolved); - - const char* name = Builtins::GetName(id); - int argc = Builtins::GetArgumentsCount(id); +void MacroAssembler::InvokeBuiltin(Builtins::JavaScript id, InvokeFlag flag) { + // Calls are not allowed in some stubs. + ASSERT(flag == JUMP_FUNCTION || allow_stub_calls()); - movq(target, code, RelocInfo::EMBEDDED_OBJECT); - if (!resolved) { - uint32_t flags = - Bootstrapper::FixupFlagsArgumentsCount::encode(argc) | - Bootstrapper::FixupFlagsUseCodeObject::encode(true); - Unresolved entry = { pc_offset() - sizeof(intptr_t), flags, name }; - unresolved_.Add(entry); - } - addq(target, Immediate(Code::kHeaderSize - kHeapObjectTag)); + // Rely on the assertion to check that the number of provided + // arguments match the expected number of arguments. Fake a + // parameter count to avoid emitting code to do the check. + ParameterCount expected(0); + GetBuiltinEntry(rdx, id); + InvokeCode(rdx, expected, expected, flag); } -Handle<Code> MacroAssembler::ResolveBuiltin(Builtins::JavaScript id, - bool* resolved) { - // Move the builtin function into the temporary function slot by - // reading it from the builtins object. NOTE: We should be able to - // reduce this to two instructions by putting the function table in - // the global object instead of the "builtins" object and by using a - // real register for the function. - movq(rdx, Operand(rsi, Context::SlotOffset(Context::GLOBAL_INDEX))); - movq(rdx, FieldOperand(rdx, GlobalObject::kBuiltinsOffset)); + +void MacroAssembler::GetBuiltinEntry(Register target, Builtins::JavaScript id) { + // Load the JavaScript builtin function from the builtins object. + movq(rdi, Operand(rsi, Context::SlotOffset(Context::GLOBAL_INDEX))); + movq(rdi, FieldOperand(rdi, GlobalObject::kBuiltinsOffset)); int builtins_offset = JSBuiltinsObject::kJSBuiltinsOffset + (id * kPointerSize); - movq(rdi, FieldOperand(rdx, builtins_offset)); - - return Builtins::GetCode(id, resolved); + movq(rdi, FieldOperand(rdi, builtins_offset)); + // Load the code entry point from the function into the target register. + movq(target, FieldOperand(rdi, JSFunction::kSharedFunctionInfoOffset)); + movq(target, FieldOperand(target, SharedFunctionInfo::kCodeOffset)); + addq(target, Immediate(Code::kHeaderSize - kHeapObjectTag)); } @@ -1597,6 +1598,17 @@ void MacroAssembler::CheckMap(Register obj, } +void MacroAssembler::AbortIfNotNumber(Register object, const char* msg) { + Label ok; + Condition is_smi = CheckSmi(object); + j(is_smi, &ok); + Cmp(FieldOperand(object, HeapObject::kMapOffset), + Factory::heap_number_map()); + Assert(equal, msg); + bind(&ok); +} + + Condition MacroAssembler::IsObjectStringType(Register heap_object, Register map, Register instance_type) { @@ -1774,39 +1786,14 @@ void MacroAssembler::CopyRegistersFromStackToMemory(Register base, } } -#endif // ENABLE_DEBUGGER_SUPPORT - - -void MacroAssembler::InvokeBuiltin(Builtins::JavaScript id, InvokeFlag flag) { - bool resolved; - Handle<Code> code = ResolveBuiltin(id, &resolved); - - // Calls are not allowed in some stubs. - ASSERT(flag == JUMP_FUNCTION || allow_stub_calls()); - - // Rely on the assertion to check that the number of provided - // arguments match the expected number of arguments. Fake a - // parameter count to avoid emitting code to do the check. - ParameterCount expected(0); - InvokeCode(Handle<Code>(code), - expected, - expected, - RelocInfo::CODE_TARGET, - flag); - - const char* name = Builtins::GetName(id); - int argc = Builtins::GetArgumentsCount(id); - // The target address for the jump is stored as an immediate at offset - // kInvokeCodeAddressOffset. - if (!resolved) { - uint32_t flags = - Bootstrapper::FixupFlagsArgumentsCount::encode(argc) | - Bootstrapper::FixupFlagsUseCodeObject::encode(false); - Unresolved entry = - { pc_offset() - kCallTargetAddressOffset, flags, name }; - unresolved_.Add(entry); - } +void MacroAssembler::DebugBreak() { + ASSERT(allow_stub_calls()); + xor_(rax, rax); // no arguments + movq(rbx, ExternalReference(Runtime::kDebugBreak)); + CEntryStub ces(1); + Call(ces.GetCode(), RelocInfo::DEBUG_BREAK); } +#endif // ENABLE_DEBUGGER_SUPPORT void MacroAssembler::InvokePrologue(const ParameterCount& expected, @@ -1926,6 +1913,21 @@ void MacroAssembler::InvokeFunction(Register function, } +void MacroAssembler::InvokeFunction(JSFunction* function, + const ParameterCount& actual, + InvokeFlag flag) { + ASSERT(function->is_compiled()); + // Get the function and setup the context. + Move(rdi, Handle<JSFunction>(function)); + movq(rsi, FieldOperand(rdi, JSFunction::kContextOffset)); + + // Invoke the cached code. + Handle<Code> code(function->code()); + ParameterCount expected(function->shared()->formal_parameter_count()); + InvokeCode(code, expected, actual, RelocInfo::CODE_TARGET, flag); +} + + void MacroAssembler::EnterFrame(StackFrame::Type type) { push(rbp); movq(rbp, rsp); @@ -1965,13 +1967,9 @@ void MacroAssembler::EnterExitFrame(ExitFrame::Mode mode, int result_size) { // Reserve room for entry stack pointer and push the debug marker. ASSERT(ExitFrameConstants::kSPOffset == -1 * kPointerSize); - push(Immediate(0)); // saved entry sp, patched before call - if (mode == ExitFrame::MODE_DEBUG) { - push(Immediate(0)); - } else { - movq(kScratchRegister, CodeObject(), RelocInfo::EMBEDDED_OBJECT); - push(kScratchRegister); - } + push(Immediate(0)); // Saved entry sp, patched before call. + movq(kScratchRegister, CodeObject(), RelocInfo::EMBEDDED_OBJECT); + push(kScratchRegister); // Accessed from EditFrame::code_slot. // Save the frame pointer and the context in top. ExternalReference c_entry_fp_address(Top::k_c_entry_fp_address); diff --git a/src/x64/macro-assembler-x64.h b/src/x64/macro-assembler-x64.h index a975dcaf..2673086d 100644 --- a/src/x64/macro-assembler-x64.h +++ b/src/x64/macro-assembler-x64.h @@ -36,7 +36,7 @@ namespace internal { // Default scratch register used by MacroAssembler (and other code that needs // a spare register). The register isn't callee save, and not used by the // function calling convention. -static const Register kScratchRegister = r10; +static const Register kScratchRegister = { 10 }; // r10. // Convenience for platform-independent signatures. typedef Operand MemOperand; @@ -98,6 +98,7 @@ class MacroAssembler: public Assembler { void CopyRegistersFromStackToMemory(Register base, Register scratch, RegList regs); + void DebugBreak(); #endif // --------------------------------------------------------------------------- @@ -148,6 +149,10 @@ class MacroAssembler: public Assembler { const ParameterCount& actual, InvokeFlag flag); + void InvokeFunction(JSFunction* function, + const ParameterCount& actual, + InvokeFlag flag); + // Invoke specified builtin JavaScript function. Adds an entry to // the unresolved list if the name does not resolve. void InvokeBuiltin(Builtins::JavaScript id, InvokeFlag flag); @@ -481,6 +486,9 @@ class MacroAssembler: public Assembler { // jcc instructions (je, ja, jae, jb, jbe, je, and jz). void FCmp(); + // Abort execution if argument is not a number. Used in debug code. + void AbortIfNotNumber(Register object, const char* msg); + // --------------------------------------------------------------------------- // Exception handling @@ -643,6 +651,10 @@ class MacroAssembler: public Assembler { // Convenience function: Same as above, but takes the fid instead. void CallRuntime(Runtime::FunctionId id, int num_arguments); + // Convenience function: call an external reference. + void CallExternalReference(const ExternalReference& ext, + int num_arguments); + // Tail call of a runtime routine (jump). // Like JumpToRuntime, but also takes care of passing the number // of arguments. @@ -679,13 +691,6 @@ class MacroAssembler: public Assembler { void Ret(); - struct Unresolved { - int pc; - uint32_t flags; // see Bootstrapper::FixupFlags decoders/encoders. - const char* name; - }; - List<Unresolved>* unresolved() { return &unresolved_; } - Handle<Object> CodeObject() { return code_object_; } @@ -717,7 +722,6 @@ class MacroAssembler: public Assembler { bool allow_stub_calls() { return allow_stub_calls_; } private: - List<Unresolved> unresolved_; bool generating_stub_; bool allow_stub_calls_; // This handle will be patched with the code object on installation. @@ -731,18 +735,6 @@ class MacroAssembler: public Assembler { Label* done, InvokeFlag flag); - // Prepares for a call or jump to a builtin by doing two things: - // 1. Emits code that fetches the builtin's function object from the context - // at runtime, and puts it in the register rdi. - // 2. Fetches the builtin's code object, and returns it in a handle, at - // compile time, so that later code can emit instructions to jump or call - // the builtin directly. If the code object has not yet been created, it - // returns the builtin code object for IllegalFunction, and sets the - // output parameter "resolved" to false. Code that uses the return value - // should then add the address and the builtin name to the list of fixups - // called unresolved_, which is fixed up by the bootstrapper. - Handle<Code> ResolveBuiltin(Builtins::JavaScript id, bool* resolved); - // Activation support. void EnterFrame(StackFrame::Type type); void LeaveFrame(StackFrame::Type type); diff --git a/src/x64/stub-cache-x64.cc b/src/x64/stub-cache-x64.cc index 693447b5..9c8b4f75 100644 --- a/src/x64/stub-cache-x64.cc +++ b/src/x64/stub-cache-x64.cc @@ -133,11 +133,10 @@ void StubCompiler::GenerateFastPropertyLoad(MacroAssembler* masm, } -template <typename Pushable> static void PushInterceptorArguments(MacroAssembler* masm, Register receiver, Register holder, - Pushable name, + Register name, JSObject* holder_obj) { __ push(receiver); __ push(holder); @@ -201,8 +200,9 @@ void StubCache::GenerateProbe(MacroAssembler* masm, } +// Both name_reg and receiver_reg are preserved on jumps to miss_label, +// but may be destroyed if store is successful. void StubCompiler::GenerateStoreField(MacroAssembler* masm, - Builtins::Name storage_extend, JSObject* object, int index, Map* transition, @@ -231,9 +231,13 @@ void StubCompiler::GenerateStoreField(MacroAssembler* masm, if ((transition != NULL) && (object->map()->unused_property_fields() == 0)) { // The properties must be extended before we can store the value. // We jump to a runtime call that extends the properties array. - __ Move(rcx, Handle<Map>(transition)); - Handle<Code> ic(Builtins::builtin(storage_extend)); - __ Jump(ic, RelocInfo::CODE_TARGET); + __ pop(scratch); // Return address. + __ push(receiver_reg); + __ Push(Handle<Map>(transition)); + __ push(rax); + __ push(scratch); + __ TailCallRuntime( + ExternalReference(IC_Utility(IC::kSharedStoreIC_ExtendStorage)), 3, 1); return; } @@ -314,38 +318,39 @@ static void GenerateStringCheck(MacroAssembler* masm, void StubCompiler::GenerateLoadStringLength(MacroAssembler* masm, Register receiver, - Register scratch, + Register scratch1, + Register scratch2, Label* miss) { - Label load_length, check_wrapper; + Label check_wrapper; // Check if the object is a string leaving the instance type in the // scratch register. - GenerateStringCheck(masm, receiver, scratch, miss, &check_wrapper); + GenerateStringCheck(masm, receiver, scratch1, miss, &check_wrapper); // Load length directly from the string. - __ bind(&load_length); __ movl(rax, FieldOperand(receiver, String::kLengthOffset)); __ Integer32ToSmi(rax, rax); __ ret(0); // Check if the object is a JSValue wrapper. __ bind(&check_wrapper); - __ cmpl(scratch, Immediate(JS_VALUE_TYPE)); + __ cmpl(scratch1, Immediate(JS_VALUE_TYPE)); __ j(not_equal, miss); // Check if the wrapped value is a string and load the length // directly if it is. - __ movq(receiver, FieldOperand(receiver, JSValue::kValueOffset)); - GenerateStringCheck(masm, receiver, scratch, miss, miss); - __ jmp(&load_length); + __ movq(scratch2, FieldOperand(receiver, JSValue::kValueOffset)); + GenerateStringCheck(masm, scratch2, scratch1, miss, miss); + __ movl(rax, FieldOperand(scratch2, String::kLengthOffset)); + __ Integer32ToSmi(rax, rax); + __ ret(0); } -template <class Pushable> static void CompileCallLoadPropertyWithInterceptor(MacroAssembler* masm, Register receiver, Register holder, - Pushable name, + Register name, JSObject* holder_obj) { PushInterceptorArguments(masm, receiver, holder, name, holder_obj); @@ -394,7 +399,7 @@ static void CompileLoadInterceptor(Compiler* compiler, stub_compiler->CheckPrototypes(object, receiver, holder, scratch1, scratch2, name, miss); - if (lookup->IsValid() && lookup->IsCacheable()) { + if (lookup->IsProperty() && lookup->IsCacheable()) { compiler->CompileCacheable(masm, stub_compiler, receiver, @@ -430,7 +435,7 @@ class LoadInterceptorCompiler BASE_EMBEDDED { LookupResult* lookup, String* name, Label* miss_label) { - AccessorInfo* callback = 0; + AccessorInfo* callback = NULL; bool optimize = false; // So far the most popular follow ups for interceptor loads are FIELD // and CALLBACKS, so inline only them, other cases may be added @@ -553,8 +558,8 @@ class LoadInterceptorCompiler BASE_EMBEDDED { class CallInterceptorCompiler BASE_EMBEDDED { public: - explicit CallInterceptorCompiler(const ParameterCount& arguments) - : arguments_(arguments), argc_(arguments.immediate()) {} + CallInterceptorCompiler(const ParameterCount& arguments, Register name) + : arguments_(arguments), name_(name) {} void CompileCacheable(MacroAssembler* masm, StubCompiler* stub_compiler, @@ -584,18 +589,20 @@ class CallInterceptorCompiler BASE_EMBEDDED { return; } + ASSERT(!lookup->holder()->IsGlobalObject()); + __ EnterInternalFrame(); - __ push(holder); // save the holder + __ push(holder); // Save the holder. + __ push(name_); // Save the name. - CompileCallLoadPropertyWithInterceptor( - masm, - receiver, - holder, - // Under EnterInternalFrame this refers to name. - Operand(rbp, (argc_ + 3) * kPointerSize), - holder_obj); + CompileCallLoadPropertyWithInterceptor(masm, + receiver, + holder, + name_, + holder_obj); - __ pop(receiver); // restore holder + __ pop(name_); // Restore the name. + __ pop(receiver); // Restore the holder. __ LeaveInternalFrame(); __ CompareRoot(rax, Heap::kNoInterceptorResultSentinelRootIndex); @@ -607,22 +614,8 @@ class CallInterceptorCompiler BASE_EMBEDDED { scratch2, name, miss_label); - if (lookup->holder()->IsGlobalObject()) { - __ movq(rdx, Operand(rsp, (argc_ + 1) * kPointerSize)); - __ movq(rdx, FieldOperand(rdx, GlobalObject::kGlobalReceiverOffset)); - __ movq(Operand(rsp, (argc_ + 1) * kPointerSize), rdx); - } - ASSERT(function->is_compiled()); - // Get the function and setup the context. - __ Move(rdi, Handle<JSFunction>(function)); - __ movq(rsi, FieldOperand(rdi, JSFunction::kContextOffset)); - - // Jump to the cached code (tail call). - Handle<Code> code(function->code()); - ParameterCount expected(function->shared()->formal_parameter_count()); - __ InvokeCode(code, expected, arguments_, - RelocInfo::CODE_TARGET, JUMP_FUNCTION); + __ InvokeFunction(function, arguments_, JUMP_FUNCTION); __ bind(&invoke); } @@ -634,27 +627,26 @@ class CallInterceptorCompiler BASE_EMBEDDED { JSObject* holder_obj, Label* miss_label) { __ EnterInternalFrame(); + // Save the name_ register across the call. + __ push(name_); PushInterceptorArguments(masm, receiver, holder, - Operand(rbp, (argc_ + 3) * kPointerSize), + name_, holder_obj); - ExternalReference ref = ExternalReference( - IC_Utility(IC::kLoadPropertyWithInterceptorForCall)); - __ movq(rax, Immediate(5)); - __ movq(rbx, ref); - - CEntryStub stub(1); - __ CallStub(&stub); + __ CallExternalReference( + ExternalReference(IC_Utility(IC::kLoadPropertyWithInterceptorForCall)), + 5); + __ pop(name_); __ LeaveInternalFrame(); } private: const ParameterCount& arguments_; - int argc_; + Register name_; }; @@ -669,14 +661,14 @@ Object* CallStubCompiler::CompileCallConstant(Object* object, String* name, StubCompiler::CheckType check) { // ----------- S t a t e ------------- - // ----------------------------------- - // rsp[0] return address - // rsp[8] argument argc - // rsp[16] argument argc - 1 + // rcx : function name + // rsp[0] : return address + // rsp[8] : argument argc + // rsp[16] : argument argc - 1 // ... - // rsp[argc * 8] argument 1 - // rsp[(argc + 1) * 8] argument 0 = receiver - // rsp[(argc + 2) * 8] function name + // rsp[argc * 8] : argument 1 + // rsp[(argc + 1) * 8] : argument 0 = receiver + // ----------------------------------- Label miss; @@ -697,7 +689,7 @@ Object* CallStubCompiler::CompileCallConstant(Object* object, case RECEIVER_MAP_CHECK: // Check that the maps haven't changed. CheckPrototypes(JSObject::cast(object), rdx, holder, - rbx, rcx, name, &miss); + rbx, rax, name, &miss); // Patch the receiver on the stack with the global proxy if // necessary. @@ -713,13 +705,13 @@ Object* CallStubCompiler::CompileCallConstant(Object* object, __ jmp(&miss); } else { // Check that the object is a two-byte string or a symbol. - __ CmpObjectType(rdx, FIRST_NONSTRING_TYPE, rcx); + __ CmpObjectType(rdx, FIRST_NONSTRING_TYPE, rax); __ j(above_equal, &miss); // Check that the maps starting from the prototype haven't changed. GenerateLoadGlobalFunctionPrototype(masm(), Context::STRING_FUNCTION_INDEX, - rcx); - CheckPrototypes(JSObject::cast(object->GetPrototype()), rcx, holder, + rax); + CheckPrototypes(JSObject::cast(object->GetPrototype()), rax, holder, rbx, rdx, name, &miss); } break; @@ -732,14 +724,14 @@ Object* CallStubCompiler::CompileCallConstant(Object* object, Label fast; // Check that the object is a smi or a heap number. __ JumpIfSmi(rdx, &fast); - __ CmpObjectType(rdx, HEAP_NUMBER_TYPE, rcx); + __ CmpObjectType(rdx, HEAP_NUMBER_TYPE, rax); __ j(not_equal, &miss); __ bind(&fast); // Check that the maps starting from the prototype haven't changed. GenerateLoadGlobalFunctionPrototype(masm(), Context::NUMBER_FUNCTION_INDEX, - rcx); - CheckPrototypes(JSObject::cast(object->GetPrototype()), rcx, holder, + rax); + CheckPrototypes(JSObject::cast(object->GetPrototype()), rax, holder, rbx, rdx, name, &miss); } break; @@ -760,8 +752,8 @@ Object* CallStubCompiler::CompileCallConstant(Object* object, // Check that the maps starting from the prototype haven't changed. GenerateLoadGlobalFunctionPrototype(masm(), Context::BOOLEAN_FUNCTION_INDEX, - rcx); - CheckPrototypes(JSObject::cast(object->GetPrototype()), rcx, holder, + rax); + CheckPrototypes(JSObject::cast(object->GetPrototype()), rax, holder, rbx, rdx, name, &miss); } break; @@ -769,7 +761,7 @@ Object* CallStubCompiler::CompileCallConstant(Object* object, case JSARRAY_HAS_FAST_ELEMENTS_CHECK: CheckPrototypes(JSObject::cast(object), rdx, holder, - rbx, rcx, name, &miss); + rbx, rax, name, &miss); // Make sure object->HasFastElements(). // Get the elements array of the object. __ movq(rbx, FieldOperand(rdx, JSObject::kElementsOffset)); @@ -783,16 +775,7 @@ Object* CallStubCompiler::CompileCallConstant(Object* object, UNREACHABLE(); } - // Get the function and setup the context. - __ Move(rdi, Handle<JSFunction>(function)); - __ movq(rsi, FieldOperand(rdi, JSFunction::kContextOffset)); - - // Jump to the cached code (tail call). - ASSERT(function->is_compiled()); - Handle<Code> code(function->code()); - ParameterCount expected(function->shared()->formal_parameter_count()); - __ InvokeCode(code, expected, arguments(), - RelocInfo::CODE_TARGET, JUMP_FUNCTION); + __ InvokeFunction(function, arguments(), JUMP_FUNCTION); // Handle call cache miss. __ bind(&miss); @@ -808,19 +791,19 @@ Object* CallStubCompiler::CompileCallConstant(Object* object, } -Object* CallStubCompiler::CompileCallField(Object* object, +Object* CallStubCompiler::CompileCallField(JSObject* object, JSObject* holder, int index, String* name) { // ----------- S t a t e ------------- - // ----------------------------------- - // rsp[0] return address - // rsp[8] argument argc - // rsp[16] argument argc - 1 + // rcx : function name + // rsp[0] : return address + // rsp[8] : argument argc + // rsp[16] : argument argc - 1 // ... - // rsp[argc * 8] argument 1 - // rsp[(argc + 1) * 8] argument 0 = receiver - // rsp[(argc + 2) * 8] function name + // rsp[argc * 8] : argument 1 + // rsp[(argc + 1) * 8] : argument 0 = receiver + // ----------------------------------- Label miss; // Get the receiver from the stack. @@ -831,9 +814,7 @@ Object* CallStubCompiler::CompileCallField(Object* object, __ JumpIfSmi(rdx, &miss); // Do the right check and compute the holder register. - Register reg = - CheckPrototypes(JSObject::cast(object), rdx, holder, - rbx, rcx, name, &miss); + Register reg = CheckPrototypes(object, rdx, holder, rbx, rax, name, &miss); GenerateFastPropertyLoad(masm(), rdi, reg, holder, index); @@ -862,10 +843,17 @@ Object* CallStubCompiler::CompileCallField(Object* object, } -Object* CallStubCompiler::CompileCallInterceptor(Object* object, +Object* CallStubCompiler::CompileCallInterceptor(JSObject* object, JSObject* holder, String* name) { // ----------- S t a t e ------------- + // rcx : function name + // rsp[0] : return address + // rsp[8] : argument argc + // rsp[16] : argument argc - 1 + // ... + // rsp[argc * 8] : argument 1 + // rsp[(argc + 1) * 8] : argument 0 = receiver // ----------------------------------- Label miss; @@ -878,17 +866,17 @@ Object* CallStubCompiler::CompileCallInterceptor(Object* object, // Get the receiver from the stack. __ movq(rdx, Operand(rsp, (argc + 1) * kPointerSize)); - CallInterceptorCompiler compiler(arguments()); + CallInterceptorCompiler compiler(arguments(), rcx); CompileLoadInterceptor(&compiler, this, masm(), - JSObject::cast(object), + object, holder, name, &lookup, rdx, rbx, - rcx, + rdi, &miss); // Restore receiver. @@ -920,7 +908,6 @@ Object* CallStubCompiler::CompileCallInterceptor(Object* object, } - Object* CallStubCompiler::CompileCallGlobal(JSObject* object, GlobalObject* holder, JSGlobalPropertyCell* cell, @@ -928,13 +915,13 @@ Object* CallStubCompiler::CompileCallGlobal(JSObject* object, String* name) { // ----------- S t a t e ------------- // ----------------------------------- - // rsp[0] return address - // rsp[8] argument argc - // rsp[16] argument argc - 1 + // rcx : function name + // rsp[0] : return address + // rsp[8] : argument argc + // rsp[16] : argument argc - 1 // ... - // rsp[argc * 8] argument 1 - // rsp[(argc + 1) * 8] argument 0 = receiver - // rsp[(argc + 2) * 8] function name + // rsp[argc * 8] : argument 1 + // rsp[(argc + 1) * 8] : argument 0 = receiver Label miss; // Get the number of arguments. @@ -951,7 +938,7 @@ Object* CallStubCompiler::CompileCallGlobal(JSObject* object, } // Check that the maps haven't changed. - CheckPrototypes(object, rdx, holder, rbx, rcx, name, &miss); + CheckPrototypes(object, rdx, holder, rbx, rax, name, &miss); // Get the value from the cell. __ Move(rdi, Handle<JSGlobalPropertyCell>(cell)); @@ -965,12 +952,12 @@ Object* CallStubCompiler::CompileCallGlobal(JSObject* object, // function can all use this call IC. Before we load through the // function, we have to verify that it still is a function. __ JumpIfSmi(rdi, &miss); - __ CmpObjectType(rdi, JS_FUNCTION_TYPE, rcx); + __ CmpObjectType(rdi, JS_FUNCTION_TYPE, rax); __ j(not_equal, &miss); // Check the shared function info. Make sure it hasn't changed. - __ Move(rcx, Handle<SharedFunctionInfo>(function->shared())); - __ cmpq(FieldOperand(rdi, JSFunction::kSharedFunctionInfoOffset), rcx); + __ Move(rax, Handle<SharedFunctionInfo>(function->shared())); + __ cmpq(FieldOperand(rdi, JSFunction::kSharedFunctionInfoOffset), rax); __ j(not_equal, &miss); } else { __ Cmp(rdi, Handle<JSFunction>(function)); @@ -1325,7 +1312,7 @@ Object* KeyedLoadStubCompiler::CompileLoadStringLength(String* name) { __ Cmp(rax, Handle<String>(name)); __ j(not_equal, &miss); - GenerateLoadStringLength(masm(), rcx, rdx, &miss); + GenerateLoadStringLength(masm(), rcx, rdx, rbx, &miss); __ bind(&miss); __ DecrementCounter(&Counters::keyed_load_string_length, 1); GenerateLoadMiss(masm(), Code::KEYED_LOAD_IC); @@ -1397,9 +1384,8 @@ Object* StoreStubCompiler::CompileStoreField(JSObject* object, // ----------------------------------- Label miss; - // Generate store field code. Trashes the name register. + // Generate store field code. Preserves receiver and name on jump to miss. GenerateStoreField(masm(), - Builtins::StoreIC_ExtendStorage, object, index, transition, @@ -1408,7 +1394,6 @@ Object* StoreStubCompiler::CompileStoreField(JSObject* object, // Handle store cache miss. __ bind(&miss); - __ Move(rcx, Handle<String>(name)); // restore name Handle<Code> ic(Builtins::builtin(Builtins::StoreIC_Miss)); __ Jump(ic, RelocInfo::CODE_TARGET); @@ -1550,16 +1535,15 @@ Object* KeyedStoreStubCompiler::CompileStoreField(JSObject* object, __ Cmp(rcx, Handle<String>(name)); __ j(not_equal, &miss); - // Get the object from the stack. - __ movq(rbx, Operand(rsp, 2 * kPointerSize)); + // Get the receiver from the stack. + __ movq(rdx, Operand(rsp, 2 * kPointerSize)); - // Generate store field code. Trashes the name register. + // Generate store field code. Preserves receiver and name on jump to miss. GenerateStoreField(masm(), - Builtins::KeyedStoreIC_ExtendStorage, object, index, transition, - rbx, rcx, rdx, + rdx, rcx, rbx, &miss); // Handle store cache miss. @@ -1665,7 +1649,11 @@ Register StubCompiler::CheckPrototypes(JSObject* object, Register holder_reg, Register scratch, String* name, + int save_at_depth, Label* miss) { + // TODO(602): support object saving. + ASSERT(save_at_depth == kInvalidProtoDepth); + // Check that the maps haven't changed. Register result = __ CheckMaps(object, object_reg, holder, holder_reg, scratch, miss); diff --git a/src/x64/virtual-frame-x64.cc b/src/x64/virtual-frame-x64.cc index cb93d5d4..a0e883c8 100644 --- a/src/x64/virtual-frame-x64.cc +++ b/src/x64/virtual-frame-x64.cc @@ -45,7 +45,7 @@ VirtualFrame::VirtualFrame() : elements_(parameter_count() + local_count() + kPreallocatedElements), stack_pointer_(parameter_count() + 1) { // 0-based index of TOS. for (int i = 0; i <= stack_pointer_; i++) { - elements_.Add(FrameElement::MemoryElement()); + elements_.Add(FrameElement::MemoryElement(NumberInfo::kUnknown)); } for (int i = 0; i < RegisterAllocator::kNumRegisters; i++) { register_locations_[i] = kIllegalIndex; @@ -193,25 +193,25 @@ void VirtualFrame::EmitPop(const Operand& operand) { } -void VirtualFrame::EmitPush(Register reg) { +void VirtualFrame::EmitPush(Register reg, NumberInfo::Type info) { ASSERT(stack_pointer_ == element_count() - 1); - elements_.Add(FrameElement::MemoryElement()); + elements_.Add(FrameElement::MemoryElement(info)); stack_pointer_++; __ push(reg); } -void VirtualFrame::EmitPush(const Operand& operand) { +void VirtualFrame::EmitPush(const Operand& operand, NumberInfo::Type info) { ASSERT(stack_pointer_ == element_count() - 1); - elements_.Add(FrameElement::MemoryElement()); + elements_.Add(FrameElement::MemoryElement(info)); stack_pointer_++; __ push(operand); } -void VirtualFrame::EmitPush(Immediate immediate) { +void VirtualFrame::EmitPush(Immediate immediate, NumberInfo::Type info) { ASSERT(stack_pointer_ == element_count() - 1); - elements_.Add(FrameElement::MemoryElement()); + elements_.Add(FrameElement::MemoryElement(info)); stack_pointer_++; __ push(immediate); } @@ -219,7 +219,7 @@ void VirtualFrame::EmitPush(Immediate immediate) { void VirtualFrame::EmitPush(Smi* smi_value) { ASSERT(stack_pointer_ == element_count() - 1); - elements_.Add(FrameElement::MemoryElement()); + elements_.Add(FrameElement::MemoryElement(NumberInfo::kSmi)); stack_pointer_++; __ Push(smi_value); } @@ -227,15 +227,21 @@ void VirtualFrame::EmitPush(Smi* smi_value) { void VirtualFrame::EmitPush(Handle<Object> value) { ASSERT(stack_pointer_ == element_count() - 1); - elements_.Add(FrameElement::MemoryElement()); + NumberInfo::Type info = NumberInfo::kUnknown; + if (value->IsSmi()) { + info = NumberInfo::kSmi; + } else if (value->IsHeapNumber()) { + info = NumberInfo::kHeapNumber; + } + elements_.Add(FrameElement::MemoryElement(info)); stack_pointer_++; __ Push(value); } -void VirtualFrame::EmitPush(Heap::RootListIndex index) { +void VirtualFrame::EmitPush(Heap::RootListIndex index, NumberInfo::Type info) { ASSERT(stack_pointer_ == element_count() - 1); - elements_.Add(FrameElement::MemoryElement()); + elements_.Add(FrameElement::MemoryElement(info)); stack_pointer_++; __ PushRoot(index); } @@ -305,10 +311,14 @@ int VirtualFrame::InvalidateFrameSlotAt(int index) { // Set the new backing element. if (elements_[new_backing_index].is_synced()) { elements_[new_backing_index] = - FrameElement::RegisterElement(backing_reg, FrameElement::SYNCED); + FrameElement::RegisterElement(backing_reg, + FrameElement::SYNCED, + original.number_info()); } else { elements_[new_backing_index] = - FrameElement::RegisterElement(backing_reg, FrameElement::NOT_SYNCED); + FrameElement::RegisterElement(backing_reg, + FrameElement::NOT_SYNCED, + original.number_info()); } // Update the other copies. for (int i = new_backing_index + 1; i < element_count(); i++) { @@ -339,7 +349,8 @@ void VirtualFrame::TakeFrameSlotAt(int index) { ASSERT(fresh.is_valid()); FrameElement new_element = FrameElement::RegisterElement(fresh.reg(), - FrameElement::NOT_SYNCED); + FrameElement::NOT_SYNCED, + original.number_info()); Use(fresh.reg(), element_count()); elements_.Add(new_element); __ movq(fresh.reg(), Operand(rbp, fp_relative(index))); @@ -480,10 +491,12 @@ void VirtualFrame::MakeMergable() { for (int i = 0; i < element_count(); i++) { FrameElement element = elements_[i]; + // In all cases we have to reset the number type information + // to unknown for a mergable frame because of incoming back edges. if (element.is_constant() || element.is_copy()) { if (element.is_synced()) { // Just spill. - elements_[i] = FrameElement::MemoryElement(); + elements_[i] = FrameElement::MemoryElement(NumberInfo::kUnknown); } else { // Allocate to a register. FrameElement backing_element; // Invalid if not a copy. @@ -494,7 +507,8 @@ void VirtualFrame::MakeMergable() { ASSERT(fresh.is_valid()); // A register was spilled if all were in use. elements_[i] = FrameElement::RegisterElement(fresh.reg(), - FrameElement::NOT_SYNCED); + FrameElement::NOT_SYNCED, + NumberInfo::kUnknown); Use(fresh.reg(), i); // Emit a move. @@ -523,6 +537,7 @@ void VirtualFrame::MakeMergable() { // The copy flag is not relied on before the end of this loop, // including when registers are spilled. elements_[i].clear_copied(); + elements_[i].set_number_info(NumberInfo::kUnknown); } } } @@ -728,6 +743,14 @@ Result VirtualFrame::Pop() { int index = element_count(); ASSERT(element.is_valid()); + // Get number type information of the result. + NumberInfo::Type info; + if (!element.is_copy()) { + info = element.number_info(); + } else { + info = elements_[element.index()].number_info(); + } + bool pop_needed = (stack_pointer_ == index); if (pop_needed) { stack_pointer_--; @@ -735,6 +758,7 @@ Result VirtualFrame::Pop() { Result temp = cgen()->allocator()->Allocate(); ASSERT(temp.is_valid()); __ pop(temp.reg()); + temp.set_number_info(info); return temp; } @@ -762,14 +786,16 @@ Result VirtualFrame::Pop() { ASSERT(temp.is_valid()); Use(temp.reg(), index); FrameElement new_element = - FrameElement::RegisterElement(temp.reg(), FrameElement::SYNCED); + FrameElement::RegisterElement(temp.reg(), + FrameElement::SYNCED, + element.number_info()); // Preserve the copy flag on the element. if (element.is_copied()) new_element.set_copied(); elements_[index] = new_element; __ movq(temp.reg(), Operand(rbp, fp_relative(index))); - return Result(temp.reg()); + return Result(temp.reg(), info); } else if (element.is_register()) { - return Result(element.reg()); + return Result(element.reg(), info); } else { ASSERT(element.is_constant()); return Result(element.handle()); @@ -969,6 +995,17 @@ Result VirtualFrame::CallRuntime(Runtime::FunctionId id, int arg_count) { } +#ifdef ENABLE_DEBUGGER_SUPPORT +void VirtualFrame::DebugBreak() { + PrepareForCall(0, 0); + ASSERT(cgen()->HasValidEntryRegisters()); + __ DebugBreak(); + Result result = cgen()->allocator()->Allocate(rax); + ASSERT(result.is_valid()); +} +#endif + + Result VirtualFrame::CallLoadIC(RelocInfo::Mode mode) { // Name and receiver are on the top of the frame. The IC expects // name in rcx and receiver on the stack. It does not drop the @@ -996,7 +1033,6 @@ Result VirtualFrame::CallKeyedStoreIC() { // expects value in rax and key and receiver on the stack. It does // not drop the key and receiver. Handle<Code> ic(Builtins::builtin(Builtins::KeyedStoreIC_Initialize)); - // TODO(1222589): Make the IC grab the values from the stack. Result value = Pop(); PrepareForCall(2, 0); // Two stack args, neither callee-dropped. value.ToRegister(rax); @@ -1008,14 +1044,17 @@ Result VirtualFrame::CallKeyedStoreIC() { Result VirtualFrame::CallCallIC(RelocInfo::Mode mode, int arg_count, int loop_nesting) { - // Arguments, receiver, and function name are on top of the frame. - // The IC expects them on the stack. It does not drop the function - // name slot (but it does drop the rest). + // Function name, arguments, and receiver are found on top of the frame + // and dropped by the call. The IC expects the name in rcx and the rest + // on the stack, and drops them all. InLoopFlag in_loop = loop_nesting > 0 ? IN_LOOP : NOT_IN_LOOP; Handle<Code> ic = cgen()->ComputeCallInitialize(arg_count, in_loop); + Result name = Pop(); // Spill args, receiver, and function. The call will drop args and // receiver. - PrepareForCall(arg_count + 2, arg_count + 1); + PrepareForCall(arg_count + 1, arg_count + 1); + name.ToRegister(rcx); + name.Unuse(); return RawCallCodeObject(ic, mode); } diff --git a/src/x64/virtual-frame-x64.h b/src/x64/virtual-frame-x64.h index 8e3e40f0..c9aa7991 100644 --- a/src/x64/virtual-frame-x64.h +++ b/src/x64/virtual-frame-x64.h @@ -28,6 +28,7 @@ #ifndef V8_X64_VIRTUAL_FRAME_X64_H_ #define V8_X64_VIRTUAL_FRAME_X64_H_ +#include "number-info.h" #include "register-allocator.h" #include "scopes.h" @@ -81,7 +82,8 @@ class VirtualFrame : public ZoneObject { MacroAssembler* masm() { return cgen()->masm(); } // Create a duplicate of an existing valid frame element. - FrameElement CopyElementAt(int index); + FrameElement CopyElementAt(int index, + NumberInfo::Type info = NumberInfo::kUninitialized); // The number of elements on the virtual frame. int element_count() { return elements_.length(); } @@ -321,6 +323,10 @@ class VirtualFrame : public ZoneObject { Result CallRuntime(Runtime::Function* f, int arg_count); Result CallRuntime(Runtime::FunctionId id, int arg_count); +#ifdef ENABLE_DEBUGGER_SUPPORT + void DebugBreak(); +#endif + // Invoke builtin given the number of arguments it expects on (and // removes from) the stack. Result InvokeBuiltin(Builtins::JavaScript id, @@ -343,9 +349,9 @@ class VirtualFrame : public ZoneObject { // of the frame. Key and receiver are not dropped. Result CallKeyedStoreIC(); - // Call call IC. Arguments, receiver, and function name are found - // on top of the frame. Function name slot is not dropped. The - // argument count does not include the receiver. + // Call call IC. Function name, arguments, and receiver are found on top + // of the frame and dropped by the call. + // The argument count does not include the receiver. Result CallCallIC(RelocInfo::Mode mode, int arg_count, int loop_nesting); // Allocate and call JS function as constructor. Arguments, @@ -376,16 +382,20 @@ class VirtualFrame : public ZoneObject { // Push an element on top of the expression stack and emit a // corresponding push instruction. - void EmitPush(Register reg); - void EmitPush(const Operand& operand); - void EmitPush(Heap::RootListIndex index); - void EmitPush(Immediate immediate); + void EmitPush(Register reg, + NumberInfo::Type info = NumberInfo::kUnknown); + void EmitPush(const Operand& operand, + NumberInfo::Type info = NumberInfo::kUnknown); + void EmitPush(Heap::RootListIndex index, + NumberInfo::Type info = NumberInfo::kUnknown); + void EmitPush(Immediate immediate, + NumberInfo::Type info = NumberInfo::kUnknown); void EmitPush(Smi* value); // Uses kScratchRegister, emits appropriate relocation info. void EmitPush(Handle<Object> value); // Push an element on the virtual frame. - void Push(Register reg); + void Push(Register reg, NumberInfo::Type info = NumberInfo::kUnknown); void Push(Handle<Object> value); void Push(Smi* value) { Push(Handle<Object>(value)); } @@ -393,7 +403,7 @@ class VirtualFrame : public ZoneObject { // frame). void Push(Result* result) { if (result->is_register()) { - Push(result->reg()); + Push(result->reg(), result->number_info()); } else { ASSERT(result->is_constant()); Push(result->handle()); diff --git a/test/cctest/test-api.cc b/test/cctest/test-api.cc index 0a392eb6..db16bfc4 100644 --- a/test/cctest/test-api.cc +++ b/test/cctest/test-api.cc @@ -394,6 +394,9 @@ THREADED_TEST(ScriptMakingExternalString) { v8::HandleScope scope; LocalContext env; Local<String> source = String::New(two_byte_source); + // Trigger GCs so that the newly allocated string moves to old gen. + i::Heap::CollectGarbage(0, i::NEW_SPACE); // in survivor space now + i::Heap::CollectGarbage(0, i::NEW_SPACE); // in old gen now bool success = source->MakeExternal(new TestResource(two_byte_source)); CHECK(success); Local<Script> script = Script::Compile(source); @@ -416,6 +419,9 @@ THREADED_TEST(ScriptMakingExternalAsciiString) { v8::HandleScope scope; LocalContext env; Local<String> source = v8_str(c_source); + // Trigger GCs so that the newly allocated string moves to old gen. + i::Heap::CollectGarbage(0, i::NEW_SPACE); // in survivor space now + i::Heap::CollectGarbage(0, i::NEW_SPACE); // in old gen now bool success = source->MakeExternal( new TestAsciiResource(i::StrDup(c_source))); CHECK(success); @@ -432,6 +438,80 @@ THREADED_TEST(ScriptMakingExternalAsciiString) { } +TEST(MakingExternalStringConditions) { + v8::HandleScope scope; + LocalContext env; + + // Free some space in the new space so that we can check freshness. + i::Heap::CollectGarbage(0, i::NEW_SPACE); + i::Heap::CollectGarbage(0, i::NEW_SPACE); + + Local<String> small_string = String::New(AsciiToTwoByteString("small")); + // We should refuse to externalize newly created small string. + CHECK(!small_string->CanMakeExternal()); + // Trigger GCs so that the newly allocated string moves to old gen. + i::Heap::CollectGarbage(0, i::NEW_SPACE); // in survivor space now + i::Heap::CollectGarbage(0, i::NEW_SPACE); // in old gen now + // Old space strings should be accepted. + CHECK(small_string->CanMakeExternal()); + + small_string = String::New(AsciiToTwoByteString("small 2")); + // We should refuse externalizing newly created small string. + CHECK(!small_string->CanMakeExternal()); + for (int i = 0; i < 100; i++) { + String::Value value(small_string); + } + // Frequently used strings should be accepted. + CHECK(small_string->CanMakeExternal()); + + const int buf_size = 10 * 1024; + char* buf = i::NewArray<char>(buf_size); + memset(buf, 'a', buf_size); + buf[buf_size - 1] = '\0'; + Local<String> large_string = String::New(AsciiToTwoByteString(buf)); + i::DeleteArray(buf); + // Large strings should be immediately accepted. + CHECK(large_string->CanMakeExternal()); +} + + +TEST(MakingExternalAsciiStringConditions) { + v8::HandleScope scope; + LocalContext env; + + // Free some space in the new space so that we can check freshness. + i::Heap::CollectGarbage(0, i::NEW_SPACE); + i::Heap::CollectGarbage(0, i::NEW_SPACE); + + Local<String> small_string = String::New("small"); + // We should refuse to externalize newly created small string. + CHECK(!small_string->CanMakeExternal()); + // Trigger GCs so that the newly allocated string moves to old gen. + i::Heap::CollectGarbage(0, i::NEW_SPACE); // in survivor space now + i::Heap::CollectGarbage(0, i::NEW_SPACE); // in old gen now + // Old space strings should be accepted. + CHECK(small_string->CanMakeExternal()); + + small_string = String::New("small 2"); + // We should refuse externalizing newly created small string. + CHECK(!small_string->CanMakeExternal()); + for (int i = 0; i < 100; i++) { + String::Value value(small_string); + } + // Frequently used strings should be accepted. + CHECK(small_string->CanMakeExternal()); + + const int buf_size = 10 * 1024; + char* buf = i::NewArray<char>(buf_size); + memset(buf, 'a', buf_size); + buf[buf_size - 1] = '\0'; + Local<String> large_string = String::New(buf); + i::DeleteArray(buf); + // Large strings should be immediately accepted. + CHECK(large_string->CanMakeExternal()); +} + + THREADED_TEST(UsingExternalString) { { v8::HandleScope scope; @@ -2448,6 +2528,33 @@ THREADED_TEST(NamedInterceptorPropertyRead) { } +static v8::Handle<Value> SetXOnPrototypeGetter(Local<String> property, + const AccessorInfo& info) { + // Set x on the prototype object and do not handle the get request. + v8::Handle<v8::Value> proto = info.Holder()->GetPrototype(); + v8::Handle<v8::Object>::Cast(proto)->Set(v8_str("x"), v8::Integer::New(23)); + return v8::Handle<Value>(); +} + + +// This is a regression test for http://crbug.com/20104. Map +// transitions should not interfere with post interceptor lookup. +THREADED_TEST(NamedInterceptorMapTransitionRead) { + v8::HandleScope scope; + Local<v8::FunctionTemplate> function_template = v8::FunctionTemplate::New(); + Local<v8::ObjectTemplate> instance_template + = function_template->InstanceTemplate(); + instance_template->SetNamedPropertyHandler(SetXOnPrototypeGetter); + LocalContext context; + context->Global()->Set(v8_str("F"), function_template->GetFunction()); + // Create an instance of F and introduce a map transition for x. + CompileRun("var o = new F(); o.x = 23;"); + // Create an instance of F and invoke the getter. The result should be 23. + Local<Value> result = CompileRun("o = new F(); o.x"); + CHECK_EQ(result->Int32Value(), 23); +} + + static v8::Handle<Value> IndexedPropertyGetter(uint32_t index, const AccessorInfo& info) { ApiTestFuzzer::Fuzz(); @@ -2529,6 +2636,195 @@ THREADED_TEST(IndexedInterceptorWithNoSetter) { } +THREADED_TEST(IndexedInterceptorWithAccessorCheck) { + v8::HandleScope scope; + Local<ObjectTemplate> templ = ObjectTemplate::New(); + templ->SetIndexedPropertyHandler(IdentityIndexedPropertyGetter); + + LocalContext context; + Local<v8::Object> obj = templ->NewInstance(); + obj->TurnOnAccessCheck(); + context->Global()->Set(v8_str("obj"), obj); + + const char* code = + "try {" + " for (var i = 0; i < 100; i++) {" + " var v = obj[0];" + " if (v != undefined) throw 'Wrong value ' + v + ' at iteration ' + i;" + " }" + " 'PASSED'" + "} catch(e) {" + " e" + "}"; + ExpectString(code, "PASSED"); +} + + +THREADED_TEST(IndexedInterceptorWithAccessorCheckSwitchedOn) { + i::FLAG_allow_natives_syntax = true; + v8::HandleScope scope; + Local<ObjectTemplate> templ = ObjectTemplate::New(); + templ->SetIndexedPropertyHandler(IdentityIndexedPropertyGetter); + + LocalContext context; + Local<v8::Object> obj = templ->NewInstance(); + context->Global()->Set(v8_str("obj"), obj); + + const char* code = + "try {" + " for (var i = 0; i < 100; i++) {" + " var expected = i;" + " if (i == 5) {" + " %EnableAccessChecks(obj);" + " expected = undefined;" + " }" + " var v = obj[i];" + " if (v != expected) throw 'Wrong value ' + v + ' at iteration ' + i;" + " if (i == 5) %DisableAccessChecks(obj);" + " }" + " 'PASSED'" + "} catch(e) {" + " e" + "}"; + ExpectString(code, "PASSED"); +} + + +THREADED_TEST(IndexedInterceptorWithDifferentIndices) { + v8::HandleScope scope; + Local<ObjectTemplate> templ = ObjectTemplate::New(); + templ->SetIndexedPropertyHandler(IdentityIndexedPropertyGetter); + + LocalContext context; + Local<v8::Object> obj = templ->NewInstance(); + context->Global()->Set(v8_str("obj"), obj); + + const char* code = + "try {" + " for (var i = 0; i < 100; i++) {" + " var v = obj[i];" + " if (v != i) throw 'Wrong value ' + v + ' at iteration ' + i;" + " }" + " 'PASSED'" + "} catch(e) {" + " e" + "}"; + ExpectString(code, "PASSED"); +} + + +THREADED_TEST(IndexedInterceptorWithNotSmiLookup) { + v8::HandleScope scope; + Local<ObjectTemplate> templ = ObjectTemplate::New(); + templ->SetIndexedPropertyHandler(IdentityIndexedPropertyGetter); + + LocalContext context; + Local<v8::Object> obj = templ->NewInstance(); + context->Global()->Set(v8_str("obj"), obj); + + const char* code = + "try {" + " for (var i = 0; i < 100; i++) {" + " var expected = i;" + " if (i == 50) {" + " i = 'foobar';" + " expected = undefined;" + " }" + " var v = obj[i];" + " if (v != expected) throw 'Wrong value ' + v + ' at iteration ' + i;" + " }" + " 'PASSED'" + "} catch(e) {" + " e" + "}"; + ExpectString(code, "PASSED"); +} + + +THREADED_TEST(IndexedInterceptorGoingMegamorphic) { + v8::HandleScope scope; + Local<ObjectTemplate> templ = ObjectTemplate::New(); + templ->SetIndexedPropertyHandler(IdentityIndexedPropertyGetter); + + LocalContext context; + Local<v8::Object> obj = templ->NewInstance(); + context->Global()->Set(v8_str("obj"), obj); + + const char* code = + "var original = obj;" + "try {" + " for (var i = 0; i < 100; i++) {" + " var expected = i;" + " if (i == 50) {" + " obj = {50: 'foobar'};" + " expected = 'foobar';" + " }" + " var v = obj[i];" + " if (v != expected) throw 'Wrong value ' + v + ' at iteration ' + i;" + " if (i == 50) obj = original;" + " }" + " 'PASSED'" + "} catch(e) {" + " e" + "}"; + ExpectString(code, "PASSED"); +} + + +THREADED_TEST(IndexedInterceptorReceiverTurningSmi) { + v8::HandleScope scope; + Local<ObjectTemplate> templ = ObjectTemplate::New(); + templ->SetIndexedPropertyHandler(IdentityIndexedPropertyGetter); + + LocalContext context; + Local<v8::Object> obj = templ->NewInstance(); + context->Global()->Set(v8_str("obj"), obj); + + const char* code = + "var original = obj;" + "try {" + " for (var i = 0; i < 100; i++) {" + " var expected = i;" + " if (i == 5) {" + " obj = 239;" + " expected = undefined;" + " }" + " var v = obj[i];" + " if (v != expected) throw 'Wrong value ' + v + ' at iteration ' + i;" + " if (i == 5) obj = original;" + " }" + " 'PASSED'" + "} catch(e) {" + " e" + "}"; + ExpectString(code, "PASSED"); +} + + +THREADED_TEST(IndexedInterceptorOnProto) { + v8::HandleScope scope; + Local<ObjectTemplate> templ = ObjectTemplate::New(); + templ->SetIndexedPropertyHandler(IdentityIndexedPropertyGetter); + + LocalContext context; + Local<v8::Object> obj = templ->NewInstance(); + context->Global()->Set(v8_str("obj"), obj); + + const char* code = + "var o = {__proto__: obj};" + "try {" + " for (var i = 0; i < 100; i++) {" + " var v = o[i];" + " if (v != i) throw 'Wrong value ' + v + ' at iteration ' + i;" + " }" + " 'PASSED'" + "} catch(e) {" + " e" + "}"; + ExpectString(code, "PASSED"); +} + + THREADED_TEST(MultiContexts) { v8::HandleScope scope; v8::Handle<ObjectTemplate> templ = ObjectTemplate::New(); @@ -4844,6 +5140,84 @@ THREADED_TEST(HiddenPrototype) { } +THREADED_TEST(SetPrototype) { + v8::HandleScope handle_scope; + LocalContext context; + + Local<v8::FunctionTemplate> t0 = v8::FunctionTemplate::New(); + t0->InstanceTemplate()->Set(v8_str("x"), v8_num(0)); + Local<v8::FunctionTemplate> t1 = v8::FunctionTemplate::New(); + t1->SetHiddenPrototype(true); + t1->InstanceTemplate()->Set(v8_str("y"), v8_num(1)); + Local<v8::FunctionTemplate> t2 = v8::FunctionTemplate::New(); + t2->SetHiddenPrototype(true); + t2->InstanceTemplate()->Set(v8_str("z"), v8_num(2)); + Local<v8::FunctionTemplate> t3 = v8::FunctionTemplate::New(); + t3->InstanceTemplate()->Set(v8_str("u"), v8_num(3)); + + Local<v8::Object> o0 = t0->GetFunction()->NewInstance(); + Local<v8::Object> o1 = t1->GetFunction()->NewInstance(); + Local<v8::Object> o2 = t2->GetFunction()->NewInstance(); + Local<v8::Object> o3 = t3->GetFunction()->NewInstance(); + + // Setting the prototype on an object does not skip hidden prototypes. + CHECK_EQ(0, o0->Get(v8_str("x"))->Int32Value()); + CHECK(o0->SetPrototype(o1)); + CHECK_EQ(0, o0->Get(v8_str("x"))->Int32Value()); + CHECK_EQ(1, o0->Get(v8_str("y"))->Int32Value()); + CHECK(o1->SetPrototype(o2)); + CHECK_EQ(0, o0->Get(v8_str("x"))->Int32Value()); + CHECK_EQ(1, o0->Get(v8_str("y"))->Int32Value()); + CHECK_EQ(2, o0->Get(v8_str("z"))->Int32Value()); + CHECK(o2->SetPrototype(o3)); + CHECK_EQ(0, o0->Get(v8_str("x"))->Int32Value()); + CHECK_EQ(1, o0->Get(v8_str("y"))->Int32Value()); + CHECK_EQ(2, o0->Get(v8_str("z"))->Int32Value()); + CHECK_EQ(3, o0->Get(v8_str("u"))->Int32Value()); + + // Getting the prototype of o0 should get the first visible one + // which is o3. Therefore, z should not be defined on the prototype + // object. + Local<Value> proto = o0->Get(v8_str("__proto__")); + CHECK(proto->IsObject()); + CHECK_EQ(v8::Handle<v8::Object>::Cast(proto), o3); + + // However, Object::GetPrototype ignores hidden prototype. + Local<Value> proto0 = o0->GetPrototype(); + CHECK(proto0->IsObject()); + CHECK_EQ(v8::Handle<v8::Object>::Cast(proto0), o1); + + Local<Value> proto1 = o1->GetPrototype(); + CHECK(proto1->IsObject()); + CHECK_EQ(v8::Handle<v8::Object>::Cast(proto1), o2); + + Local<Value> proto2 = o2->GetPrototype(); + CHECK(proto2->IsObject()); + CHECK_EQ(v8::Handle<v8::Object>::Cast(proto2), o3); +} + + +THREADED_TEST(SetPrototypeThrows) { + v8::HandleScope handle_scope; + LocalContext context; + + Local<v8::FunctionTemplate> t = v8::FunctionTemplate::New(); + + Local<v8::Object> o0 = t->GetFunction()->NewInstance(); + Local<v8::Object> o1 = t->GetFunction()->NewInstance(); + + CHECK(o0->SetPrototype(o1)); + // If setting the prototype leads to the cycle, SetPrototype should + // return false and keep VM in sane state. + v8::TryCatch try_catch; + CHECK(!o1->SetPrototype(o0)); + CHECK(!try_catch.HasCaught()); + ASSERT(!i::Top::has_pending_exception()); + + CHECK_EQ(42, CompileRun("function f() { return 42; }; f()")->Int32Value()); +} + + THREADED_TEST(GetterSetterExceptions) { v8::HandleScope handle_scope; LocalContext context; @@ -5078,7 +5452,7 @@ THREADED_TEST(CrossLazyLoad) { static v8::Handle<Value> call_as_function(const v8::Arguments& args) { - //ApiTestFuzzer::Fuzz(); + ApiTestFuzzer::Fuzz(); if (args.IsConstructCall()) { if (args[0]->IsInt32()) { return v8_num(-args[0]->Int32Value()); @@ -5891,6 +6265,294 @@ THREADED_TEST(InterceptorCallICCachedFromGlobal) { CHECK_EQ(239 * 10, value->Int32Value()); } +static v8::Handle<Value> InterceptorCallICFastApi(Local<String> name, + const AccessorInfo& info) { + ApiTestFuzzer::Fuzz(); + int* call_count = reinterpret_cast<int*>(v8::External::Unwrap(info.Data())); + ++(*call_count); + if ((*call_count) % 20 == 0) { + v8::internal::Heap::CollectAllGarbage(true); + } + return v8::Handle<Value>(); +} + +static v8::Handle<Value> FastApiCallback_TrivialSignature( + const v8::Arguments& args) { + ApiTestFuzzer::Fuzz(); + CHECK_EQ(args.This(), args.Holder()); + CHECK(args.Data()->Equals(v8_str("method_data"))); + return v8::Integer::New(args[0]->Int32Value() + 1); +} + +static v8::Handle<Value> FastApiCallback_SimpleSignature( + const v8::Arguments& args) { + ApiTestFuzzer::Fuzz(); + CHECK_EQ(args.This()->GetPrototype(), args.Holder()); + CHECK(args.Data()->Equals(v8_str("method_data"))); + // Note, we're using HasRealNamedProperty instead of Has to avoid + // invoking the interceptor again. + CHECK(args.Holder()->HasRealNamedProperty(v8_str("foo"))); + return v8::Integer::New(args[0]->Int32Value() + 1); +} + +// Helper to maximize the odds of object moving. +static void GenerateSomeGarbage() { + CompileRun( + "var garbage;" + "for (var i = 0; i < 1000; i++) {" + " garbage = [1/i, \"garbage\" + i, garbage, {foo: garbage}];" + "}" + "garbage = undefined;"); +} + +THREADED_TEST(InterceptorCallICFastApi_TrivialSignature) { + int interceptor_call_count = 0; + v8::HandleScope scope; + v8::Handle<v8::FunctionTemplate> fun_templ = v8::FunctionTemplate::New(); + v8::Handle<v8::FunctionTemplate> method_templ = + v8::FunctionTemplate::New(FastApiCallback_TrivialSignature, + v8_str("method_data"), + v8::Handle<v8::Signature>()); + v8::Handle<v8::ObjectTemplate> proto_templ = fun_templ->PrototypeTemplate(); + proto_templ->Set(v8_str("method"), method_templ); + v8::Handle<v8::ObjectTemplate> templ = fun_templ->InstanceTemplate(); + templ->SetNamedPropertyHandler(InterceptorCallICFastApi, + NULL, NULL, NULL, NULL, + v8::External::Wrap(&interceptor_call_count)); + LocalContext context; + v8::Handle<v8::Function> fun = fun_templ->GetFunction(); + GenerateSomeGarbage(); + context->Global()->Set(v8_str("o"), fun->NewInstance()); + v8::Handle<Value> value = CompileRun( + "var result = 0;" + "for (var i = 0; i < 100; i++) {" + " result = o.method(41);" + "}"); + CHECK_EQ(42, context->Global()->Get(v8_str("result"))->Int32Value()); + CHECK_EQ(100, interceptor_call_count); +} + +THREADED_TEST(InterceptorCallICFastApi_SimpleSignature) { + int interceptor_call_count = 0; + v8::HandleScope scope; + v8::Handle<v8::FunctionTemplate> fun_templ = v8::FunctionTemplate::New(); + v8::Handle<v8::FunctionTemplate> method_templ = + v8::FunctionTemplate::New(FastApiCallback_SimpleSignature, + v8_str("method_data"), + v8::Signature::New(fun_templ)); + v8::Handle<v8::ObjectTemplate> proto_templ = fun_templ->PrototypeTemplate(); + proto_templ->Set(v8_str("method"), method_templ); + v8::Handle<v8::ObjectTemplate> templ = fun_templ->InstanceTemplate(); + templ->SetNamedPropertyHandler(InterceptorCallICFastApi, + NULL, NULL, NULL, NULL, + v8::External::Wrap(&interceptor_call_count)); + LocalContext context; + v8::Handle<v8::Function> fun = fun_templ->GetFunction(); + GenerateSomeGarbage(); + context->Global()->Set(v8_str("o"), fun->NewInstance()); + v8::Handle<Value> value = CompileRun( + "o.foo = 17;" + "var receiver = {};" + "receiver.__proto__ = o;" + "var result = 0;" + "for (var i = 0; i < 100; i++) {" + " result = receiver.method(41);" + "}"); + CHECK_EQ(42, context->Global()->Get(v8_str("result"))->Int32Value()); + CHECK_EQ(100, interceptor_call_count); +} + +THREADED_TEST(InterceptorCallICFastApi_SimpleSignature_Miss1) { + int interceptor_call_count = 0; + v8::HandleScope scope; + v8::Handle<v8::FunctionTemplate> fun_templ = v8::FunctionTemplate::New(); + v8::Handle<v8::FunctionTemplate> method_templ = + v8::FunctionTemplate::New(FastApiCallback_SimpleSignature, + v8_str("method_data"), + v8::Signature::New(fun_templ)); + v8::Handle<v8::ObjectTemplate> proto_templ = fun_templ->PrototypeTemplate(); + proto_templ->Set(v8_str("method"), method_templ); + v8::Handle<v8::ObjectTemplate> templ = fun_templ->InstanceTemplate(); + templ->SetNamedPropertyHandler(InterceptorCallICFastApi, + NULL, NULL, NULL, NULL, + v8::External::Wrap(&interceptor_call_count)); + LocalContext context; + v8::Handle<v8::Function> fun = fun_templ->GetFunction(); + GenerateSomeGarbage(); + context->Global()->Set(v8_str("o"), fun->NewInstance()); + v8::Handle<Value> value = CompileRun( + "o.foo = 17;" + "var receiver = {};" + "receiver.__proto__ = o;" + "var result = 0;" + "var saved_result = 0;" + "for (var i = 0; i < 100; i++) {" + " result = receiver.method(41);" + " if (i == 50) {" + " saved_result = result;" + " receiver = {method: function(x) { return x - 1 }};" + " }" + "}"); + CHECK_EQ(40, context->Global()->Get(v8_str("result"))->Int32Value()); + CHECK_EQ(42, context->Global()->Get(v8_str("saved_result"))->Int32Value()); + CHECK_GE(interceptor_call_count, 50); +} + +THREADED_TEST(InterceptorCallICFastApi_SimpleSignature_Miss2) { + int interceptor_call_count = 0; + v8::HandleScope scope; + v8::Handle<v8::FunctionTemplate> fun_templ = v8::FunctionTemplate::New(); + v8::Handle<v8::FunctionTemplate> method_templ = + v8::FunctionTemplate::New(FastApiCallback_SimpleSignature, + v8_str("method_data"), + v8::Signature::New(fun_templ)); + v8::Handle<v8::ObjectTemplate> proto_templ = fun_templ->PrototypeTemplate(); + proto_templ->Set(v8_str("method"), method_templ); + v8::Handle<v8::ObjectTemplate> templ = fun_templ->InstanceTemplate(); + templ->SetNamedPropertyHandler(InterceptorCallICFastApi, + NULL, NULL, NULL, NULL, + v8::External::Wrap(&interceptor_call_count)); + LocalContext context; + v8::Handle<v8::Function> fun = fun_templ->GetFunction(); + GenerateSomeGarbage(); + context->Global()->Set(v8_str("o"), fun->NewInstance()); + v8::Handle<Value> value = CompileRun( + "o.foo = 17;" + "var receiver = {};" + "receiver.__proto__ = o;" + "var result = 0;" + "var saved_result = 0;" + "for (var i = 0; i < 100; i++) {" + " result = receiver.method(41);" + " if (i == 50) {" + " saved_result = result;" + " o.method = function(x) { return x - 1 };" + " }" + "}"); + CHECK_EQ(40, context->Global()->Get(v8_str("result"))->Int32Value()); + CHECK_EQ(42, context->Global()->Get(v8_str("saved_result"))->Int32Value()); + CHECK_GE(interceptor_call_count, 50); +} + +THREADED_TEST(InterceptorCallICFastApi_SimpleSignature_TypeError) { + int interceptor_call_count = 0; + v8::HandleScope scope; + v8::Handle<v8::FunctionTemplate> fun_templ = v8::FunctionTemplate::New(); + v8::Handle<v8::FunctionTemplate> method_templ = + v8::FunctionTemplate::New(FastApiCallback_SimpleSignature, + v8_str("method_data"), + v8::Signature::New(fun_templ)); + v8::Handle<v8::ObjectTemplate> proto_templ = fun_templ->PrototypeTemplate(); + proto_templ->Set(v8_str("method"), method_templ); + v8::Handle<v8::ObjectTemplate> templ = fun_templ->InstanceTemplate(); + templ->SetNamedPropertyHandler(InterceptorCallICFastApi, + NULL, NULL, NULL, NULL, + v8::External::Wrap(&interceptor_call_count)); + LocalContext context; + v8::Handle<v8::Function> fun = fun_templ->GetFunction(); + GenerateSomeGarbage(); + context->Global()->Set(v8_str("o"), fun->NewInstance()); + v8::TryCatch try_catch; + v8::Handle<Value> value = CompileRun( + "o.foo = 17;" + "var receiver = {};" + "receiver.__proto__ = o;" + "var result = 0;" + "var saved_result = 0;" + "for (var i = 0; i < 100; i++) {" + " result = receiver.method(41);" + " if (i == 50) {" + " saved_result = result;" + " receiver = {method: receiver.method};" + " }" + "}"); + CHECK(try_catch.HasCaught()); + CHECK_EQ(v8_str("TypeError: Illegal invocation"), + try_catch.Exception()->ToString()); + CHECK_EQ(42, context->Global()->Get(v8_str("saved_result"))->Int32Value()); + CHECK_GE(interceptor_call_count, 50); +} + +THREADED_TEST(CallICFastApi_TrivialSignature) { + v8::HandleScope scope; + v8::Handle<v8::FunctionTemplate> fun_templ = v8::FunctionTemplate::New(); + v8::Handle<v8::FunctionTemplate> method_templ = + v8::FunctionTemplate::New(FastApiCallback_TrivialSignature, + v8_str("method_data"), + v8::Handle<v8::Signature>()); + v8::Handle<v8::ObjectTemplate> proto_templ = fun_templ->PrototypeTemplate(); + proto_templ->Set(v8_str("method"), method_templ); + v8::Handle<v8::ObjectTemplate> templ = fun_templ->InstanceTemplate(); + LocalContext context; + v8::Handle<v8::Function> fun = fun_templ->GetFunction(); + GenerateSomeGarbage(); + context->Global()->Set(v8_str("o"), fun->NewInstance()); + v8::Handle<Value> value = CompileRun( + "var result = 0;" + "for (var i = 0; i < 100; i++) {" + " result = o.method(41);" + "}"); + + CHECK_EQ(42, context->Global()->Get(v8_str("result"))->Int32Value()); +} + +THREADED_TEST(CallICFastApi_SimpleSignature) { + v8::HandleScope scope; + v8::Handle<v8::FunctionTemplate> fun_templ = v8::FunctionTemplate::New(); + v8::Handle<v8::FunctionTemplate> method_templ = + v8::FunctionTemplate::New(FastApiCallback_SimpleSignature, + v8_str("method_data"), + v8::Signature::New(fun_templ)); + v8::Handle<v8::ObjectTemplate> proto_templ = fun_templ->PrototypeTemplate(); + proto_templ->Set(v8_str("method"), method_templ); + v8::Handle<v8::ObjectTemplate> templ = fun_templ->InstanceTemplate(); + LocalContext context; + v8::Handle<v8::Function> fun = fun_templ->GetFunction(); + GenerateSomeGarbage(); + context->Global()->Set(v8_str("o"), fun->NewInstance()); + v8::Handle<Value> value = CompileRun( + "o.foo = 17;" + "var receiver = {};" + "receiver.__proto__ = o;" + "var result = 0;" + "for (var i = 0; i < 100; i++) {" + " result = receiver.method(41);" + "}"); + + CHECK_EQ(42, context->Global()->Get(v8_str("result"))->Int32Value()); +} + +THREADED_TEST(CallICFastApi_SimpleSignature_Miss) { + v8::HandleScope scope; + v8::Handle<v8::FunctionTemplate> fun_templ = v8::FunctionTemplate::New(); + v8::Handle<v8::FunctionTemplate> method_templ = + v8::FunctionTemplate::New(FastApiCallback_SimpleSignature, + v8_str("method_data"), + v8::Signature::New(fun_templ)); + v8::Handle<v8::ObjectTemplate> proto_templ = fun_templ->PrototypeTemplate(); + proto_templ->Set(v8_str("method"), method_templ); + v8::Handle<v8::ObjectTemplate> templ = fun_templ->InstanceTemplate(); + LocalContext context; + v8::Handle<v8::Function> fun = fun_templ->GetFunction(); + GenerateSomeGarbage(); + context->Global()->Set(v8_str("o"), fun->NewInstance()); + v8::Handle<Value> value = CompileRun( + "o.foo = 17;" + "var receiver = {};" + "receiver.__proto__ = o;" + "var result = 0;" + "var saved_result = 0;" + "for (var i = 0; i < 100; i++) {" + " result = receiver.method(41);" + " if (i == 50) {" + " saved_result = result;" + " receiver = {method: function(x) { return x - 1 }};" + " }" + "}"); + CHECK_EQ(40, context->Global()->Get(v8_str("result"))->Int32Value()); + CHECK_EQ(42, context->Global()->Get(v8_str("saved_result"))->Int32Value()); +} + static int interceptor_call_count = 0; @@ -8927,3 +9589,138 @@ TEST(Regress528) { other_context.Dispose(); } + + +THREADED_TEST(ScriptOrigin) { + v8::HandleScope scope; + LocalContext env; + v8::ScriptOrigin origin = v8::ScriptOrigin(v8::String::New("test")); + v8::Handle<v8::String> script = v8::String::New( + "function f() {}\n\nfunction g() {}"); + v8::Script::Compile(script, &origin)->Run(); + v8::Local<v8::Function> f = v8::Local<v8::Function>::Cast( + env->Global()->Get(v8::String::New("f"))); + v8::Local<v8::Function> g = v8::Local<v8::Function>::Cast( + env->Global()->Get(v8::String::New("g"))); + + v8::ScriptOrigin script_origin_f = f->GetScriptOrigin(); + CHECK_EQ("test", *v8::String::AsciiValue(script_origin_f.ResourceName())); + CHECK_EQ(0, script_origin_f.ResourceLineOffset()->Int32Value()); + + v8::ScriptOrigin script_origin_g = g->GetScriptOrigin(); + CHECK_EQ("test", *v8::String::AsciiValue(script_origin_g.ResourceName())); + CHECK_EQ(0, script_origin_g.ResourceLineOffset()->Int32Value()); +} + + +THREADED_TEST(ScriptLineNumber) { + v8::HandleScope scope; + LocalContext env; + v8::ScriptOrigin origin = v8::ScriptOrigin(v8::String::New("test")); + v8::Handle<v8::String> script = v8::String::New( + "function f() {}\n\nfunction g() {}"); + v8::Script::Compile(script, &origin)->Run(); + v8::Local<v8::Function> f = v8::Local<v8::Function>::Cast( + env->Global()->Get(v8::String::New("f"))); + v8::Local<v8::Function> g = v8::Local<v8::Function>::Cast( + env->Global()->Get(v8::String::New("g"))); + CHECK_EQ(0, f->GetScriptLineNumber()); + CHECK_EQ(2, g->GetScriptLineNumber()); +} + + +static v8::Handle<Value> GetterWhichReturns42(Local<String> name, + const AccessorInfo& info) { + return v8_num(42); +} + + +static void SetterWhichSetsYOnThisTo23(Local<String> name, + Local<Value> value, + const AccessorInfo& info) { + info.This()->Set(v8_str("y"), v8_num(23)); +} + + +THREADED_TEST(SetterOnConstructorPrototype) { + v8::HandleScope scope; + Local<ObjectTemplate> templ = ObjectTemplate::New(); + templ->SetAccessor(v8_str("x"), + GetterWhichReturns42, + SetterWhichSetsYOnThisTo23); + LocalContext context; + context->Global()->Set(v8_str("P"), templ->NewInstance()); + CompileRun("function C1() {" + " this.x = 23;" + "};" + "C1.prototype = P;" + "function C2() {" + " this.x = 23" + "};" + "C2.prototype = { };" + "C2.prototype.__proto__ = P;"); + + v8::Local<v8::Script> script; + script = v8::Script::Compile(v8_str("new C1();")); + for (int i = 0; i < 10; i++) { + v8::Handle<v8::Object> c1 = v8::Handle<v8::Object>::Cast(script->Run()); + CHECK_EQ(42, c1->Get(v8_str("x"))->Int32Value()); + CHECK_EQ(23, c1->Get(v8_str("y"))->Int32Value()); + } + + script = v8::Script::Compile(v8_str("new C2();")); + for (int i = 0; i < 10; i++) { + v8::Handle<v8::Object> c2 = v8::Handle<v8::Object>::Cast(script->Run()); + CHECK_EQ(42, c2->Get(v8_str("x"))->Int32Value()); + CHECK_EQ(23, c2->Get(v8_str("y"))->Int32Value()); + } +} + + +static v8::Handle<Value> NamedPropertyGetterWhichReturns42( + Local<String> name, const AccessorInfo& info) { + return v8_num(42); +} + + +static v8::Handle<Value> NamedPropertySetterWhichSetsYOnThisTo23( + Local<String> name, Local<Value> value, const AccessorInfo& info) { + if (name->Equals(v8_str("x"))) { + info.This()->Set(v8_str("y"), v8_num(23)); + } + return v8::Handle<Value>(); +} + + +THREADED_TEST(InterceptorOnConstructorPrototype) { + v8::HandleScope scope; + Local<ObjectTemplate> templ = ObjectTemplate::New(); + templ->SetNamedPropertyHandler(NamedPropertyGetterWhichReturns42, + NamedPropertySetterWhichSetsYOnThisTo23); + LocalContext context; + context->Global()->Set(v8_str("P"), templ->NewInstance()); + CompileRun("function C1() {" + " this.x = 23;" + "};" + "C1.prototype = P;" + "function C2() {" + " this.x = 23" + "};" + "C2.prototype = { };" + "C2.prototype.__proto__ = P;"); + + v8::Local<v8::Script> script; + script = v8::Script::Compile(v8_str("new C1();")); + for (int i = 0; i < 10; i++) { + v8::Handle<v8::Object> c1 = v8::Handle<v8::Object>::Cast(script->Run()); + CHECK_EQ(23, c1->Get(v8_str("x"))->Int32Value()); + CHECK_EQ(42, c1->Get(v8_str("y"))->Int32Value()); + } + + script = v8::Script::Compile(v8_str("new C2();")); + for (int i = 0; i < 10; i++) { + v8::Handle<v8::Object> c2 = v8::Handle<v8::Object>::Cast(script->Run()); + CHECK_EQ(23, c2->Get(v8_str("x"))->Int32Value()); + CHECK_EQ(42, c2->Get(v8_str("y"))->Int32Value()); + } +} diff --git a/test/cctest/test-assembler-mips.cc b/test/cctest/test-assembler-mips.cc new file mode 100644 index 00000000..ab011a73 --- /dev/null +++ b/test/cctest/test-assembler-mips.cc @@ -0,0 +1,257 @@ +// Copyright 2010 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 "v8.h" + +#include "disassembler.h" +#include "factory.h" +#include "macro-assembler.h" +#include "mips/macro-assembler-mips.h" +#include "mips/simulator-mips.h" + +#include "cctest.h" + +using namespace v8::internal; + + +// Define these function prototypes to match JSEntryFunction in execution.cc. +typedef Object* (*F1)(int x, int p1, int p2, int p3, int p4); +typedef Object* (*F2)(int x, int y, int p2, int p3, int p4); +typedef Object* (*F3)(void* p, int p1, int p2, int p3, int p4); + + +static v8::Persistent<v8::Context> env; + + +// The test framework does not accept flags on the command line, so we set them. +static void InitializeVM() { + // Disable compilation of natives by specifying an empty natives file. + FLAG_natives_file = ""; + + // Enable generation of comments. + FLAG_debug_code = true; + + if (env.IsEmpty()) { + env = v8::Context::New(); + } +} + + +#define __ assm. + +TEST(MIPS0) { + InitializeVM(); + v8::HandleScope scope; + + MacroAssembler assm(NULL, 0); + + // Addition. + __ addu(v0, a0, a1); + __ jr(ra); + __ nop(); + + CodeDesc desc; + assm.GetCode(&desc); + Object* code = Heap::CreateCode(desc, + NULL, + Code::ComputeFlags(Code::STUB), + Handle<Object>(Heap::undefined_value())); + CHECK(code->IsCode()); +#ifdef DEBUG + Code::cast(code)->Print(); +#endif + F2 f = FUNCTION_CAST<F2>(Code::cast(code)->entry()); + int res = reinterpret_cast<int>(CALL_GENERATED_CODE(f, 0xab0, 0xc, 0, 0, 0)); + ::printf("f() = %d\n", res); + CHECK_EQ(0xabc, res); +} + + +TEST(MIPS1) { + InitializeVM(); + v8::HandleScope scope; + + MacroAssembler assm(NULL, 0); + Label L, C; + + __ mov(a1, a0); + __ li(v0, 0); + __ b(&C); + __ nop(); + + __ bind(&L); + __ add(v0, v0, a1); + __ addiu(a1, a1, -1); + + __ bind(&C); + __ xori(v1, a1, 0); + __ Branch(ne, &L, v1, Operand(0)); + __ nop(); + + __ jr(ra); + __ nop(); + + CodeDesc desc; + assm.GetCode(&desc); + Object* code = Heap::CreateCode(desc, + NULL, + Code::ComputeFlags(Code::STUB), + Handle<Object>(Heap::undefined_value())); + CHECK(code->IsCode()); +#ifdef DEBUG + Code::cast(code)->Print(); +#endif + F1 f = FUNCTION_CAST<F1>(Code::cast(code)->entry()); + int res = reinterpret_cast<int>(CALL_GENERATED_CODE(f, 50, 0, 0, 0, 0)); + ::printf("f() = %d\n", res); + CHECK_EQ(1275, res); +} + + +TEST(MIPS2) { + InitializeVM(); + v8::HandleScope scope; + + MacroAssembler assm(NULL, 0); + + Label exit, error; + + // ----- Test all instructions. + + // Test lui, ori, and addiu, used in the li pseudo-instruction. + // This way we can then safely load registers with chosen values. + + __ ori(t0, zero_reg, 0); + __ lui(t0, 0x1234); + __ ori(t0, t0, 0); + __ ori(t0, t0, 0x0f0f); + __ ori(t0, t0, 0xf0f0); + __ addiu(t1, t0, 1); + __ addiu(t2, t1, -0x10); + + // Load values in temporary registers. + __ li(t0, 0x00000004); + __ li(t1, 0x00001234); + __ li(t2, 0x12345678); + __ li(t3, 0x7fffffff); + __ li(t4, 0xfffffffc); + __ li(t5, 0xffffedcc); + __ li(t6, 0xedcba988); + __ li(t7, 0x80000000); + + // SPECIAL class. + __ srl(v0, t2, 8); // 0x00123456 + __ sll(v0, v0, 11); // 0x91a2b000 + __ sra(v0, v0, 3); // 0xf2345600 + __ srav(v0, v0, t0); // 0xff234560 + __ sllv(v0, v0, t0); // 0xf2345600 + __ srlv(v0, v0, t0); // 0x0f234560 + __ Branch(ne, &error, v0, Operand(0x0f234560)); + __ nop(); + + __ add(v0, t0, t1); // 0x00001238 + __ sub(v0, v0, t0); // 0x00001234 + __ Branch(ne, &error, v0, Operand(0x00001234)); + __ nop(); + __ addu(v1, t3, t0); + __ Branch(ne, &error, v1, Operand(0x80000003)); + __ nop(); + __ subu(v1, t7, t0); // 0x7ffffffc + __ Branch(ne, &error, v1, Operand(0x7ffffffc)); + __ nop(); + + __ and_(v0, t1, t2); // 0x00001230 + __ or_(v0, v0, t1); // 0x00001234 + __ xor_(v0, v0, t2); // 0x1234444c + __ nor(v0, v0, t2); // 0xedcba987 + __ Branch(ne, &error, v0, Operand(0xedcba983)); + __ nop(); + + __ slt(v0, t7, t3); + __ Branch(ne, &error, v0, Operand(0x1)); + __ nop(); + __ sltu(v0, t7, t3); + __ Branch(ne, &error, v0, Operand(0x0)); + __ nop(); + // End of SPECIAL class. + + __ addi(v0, zero_reg, 0x7421); // 0x00007421 + __ addi(v0, v0, -0x1); // 0x00007420 + __ addiu(v0, v0, -0x20); // 0x00007400 + __ Branch(ne, &error, v0, Operand(0x00007400)); + __ nop(); + __ addiu(v1, t3, 0x1); // 0x80000000 + __ Branch(ne, &error, v1, Operand(0x80000000)); + __ nop(); + + __ slti(v0, t1, 0x00002000); // 0x1 + __ slti(v0, v0, 0xffff8000); // 0x0 + __ Branch(ne, &error, v0, Operand(0x0)); + __ nop(); + __ sltiu(v0, t1, 0x00002000); // 0x1 + __ sltiu(v0, v0, 0x00008000); // 0x1 + __ Branch(ne, &error, v0, Operand(0x1)); + __ nop(); + + __ andi(v0, t1, 0xf0f0); // 0x00001030 + __ ori(v0, v0, 0x8a00); // 0x00009a30 + __ xori(v0, v0, 0x83cc); // 0x000019fc + __ Branch(ne, &error, v0, Operand(0x000019fc)); + __ nop(); + __ lui(v1, 0x8123); // 0x81230000 + __ Branch(ne, &error, v1, Operand(0x81230000)); + __ nop(); + + // Everything was correctly executed. Load the expected result. + __ li(v0, 0x31415926); + __ b(&exit); + __ nop(); + + __ bind(&error); + // Got an error. Return a wrong result. + + __ bind(&exit); + __ jr(ra); + __ nop(); + + CodeDesc desc; + assm.GetCode(&desc); + Object* code = Heap::CreateCode(desc, + NULL, + Code::ComputeFlags(Code::STUB), + Handle<Object>(Heap::undefined_value())); + CHECK(code->IsCode()); +#ifdef DEBUG + Code::cast(code)->Print(); +#endif + F2 f = FUNCTION_CAST<F2>(Code::cast(code)->entry()); + int res = reinterpret_cast<int>(CALL_GENERATED_CODE(f, 0xab0, 0xc, 0, 0, 0)); + ::printf("f() = %d\n", res); + CHECK_EQ(0x31415926, res); +} + +#undef __ diff --git a/test/cctest/test-compiler.cc b/test/cctest/test-compiler.cc index b5f12b9c..471753c0 100644 --- a/test/cctest/test-compiler.cc +++ b/test/cctest/test-compiler.cc @@ -120,6 +120,7 @@ static Handle<JSFunction> Compile(const char* source) { 0, NULL, NULL, + Handle<String>::null(), NOT_NATIVES_CODE); return Factory::NewFunctionFromBoilerplate(boilerplate, Top::global_context()); @@ -322,3 +323,27 @@ TEST(Regression236) { CHECK_EQ(-1, GetScriptLineNumber(script, 100)); CHECK_EQ(-1, GetScriptLineNumber(script, -1)); } + + +TEST(GetScriptLineNumber) { + LocalContext env; + v8::HandleScope scope; + v8::ScriptOrigin origin = v8::ScriptOrigin(v8::String::New("test")); + const char function_f[] = "function f() {}"; + const int max_rows = 1000; + const int buffer_size = max_rows + sizeof(function_f); + ScopedVector<char> buffer(buffer_size); + memset(buffer.start(), '\n', buffer_size - 1); + buffer[buffer_size - 1] = '\0'; + + for (int i = 0; i < max_rows; ++i) { + if (i > 0) + buffer[i - 1] = '\n'; + memcpy(&buffer[i], function_f, sizeof(function_f) - 1); + v8::Handle<v8::String> script_body = v8::String::New(buffer.start()); + v8::Script::Compile(script_body, &origin)->Run(); + v8::Local<v8::Function> f = v8::Local<v8::Function>::Cast( + env->Global()->Get(v8::String::New("f"))); + CHECK_EQ(i, f->GetScriptLineNumber()); + } +} diff --git a/test/cctest/test-debug.cc b/test/cctest/test-debug.cc index 92e18e06..b7c39b22 100644 --- a/test/cctest/test-debug.cc +++ b/test/cctest/test-debug.cc @@ -550,6 +550,15 @@ const char* frame_script_data_source = v8::Local<v8::Function> frame_script_data; +// Source for The JavaScript function which picks out the script data from +// AfterCompile event +const char* compiled_script_data_source = + "function compiled_script_data(event_data) {" + " return event_data.script().data();" + "}"; +v8::Local<v8::Function> compiled_script_data; + + // Source for The JavaScript function which returns the number of frames. static const char* frame_count_source = "function frame_count(exec_state) {" @@ -647,6 +656,19 @@ static void DebugEventBreakPointHitCount(v8::DebugEvent event, script_data->WriteAscii(last_script_data_hit); } } + } else if (event == v8::AfterCompile && !compiled_script_data.IsEmpty()) { + const int argc = 1; + v8::Handle<v8::Value> argv[argc] = { event_data }; + v8::Handle<v8::Value> result = compiled_script_data->Call(exec_state, + argc, argv); + if (result->IsUndefined()) { + last_script_data_hit[0] = '\0'; + } else { + result = result->ToString(); + CHECK(result->IsString()); + v8::Handle<v8::String> script_data(result->ToString()); + script_data->WriteAscii(last_script_data_hit); + } } } @@ -3884,6 +3906,11 @@ bool IsEvaluateResponseMessage(char* message) { } +static int StringToInt(const char* s) { + return atoi(s); // NOLINT +} + + // We match parts of the message to get evaluate result int value. int GetEvaluateIntResult(char *message) { const char* value = "\"value\":"; @@ -3892,7 +3919,7 @@ int GetEvaluateIntResult(char *message) { return -1; } int res = -1; - res = atoi(pos + strlen(value)); + res = StringToInt(pos + strlen(value)); return res; } @@ -3905,7 +3932,7 @@ int GetBreakpointIdFromBreakEventMessage(char *message) { return -1; } int res = -1; - res = atoi(pos + strlen(breakpoints)); + res = StringToInt(pos + strlen(breakpoints)); return res; } @@ -3918,11 +3945,7 @@ int GetTotalFramesInt(char *message) { return -1; } pos += strlen(prefix); - char* pos_end = pos; - int res = static_cast<int>(strtol(pos, &pos_end, 10)); - if (pos_end == pos) { - return -1; - } + int res = StringToInt(pos); return res; } @@ -5231,6 +5254,9 @@ TEST(ScriptNameAndData) { frame_script_data = CompileFunction(&env, frame_script_data_source, "frame_script_data"); + compiled_script_data = CompileFunction(&env, + compiled_script_data_source, + "compiled_script_data"); v8::Debug::SetDebugEventListener(DebugEventBreakPointHitCount, v8::Undefined()); @@ -5277,6 +5303,16 @@ TEST(ScriptNameAndData) { CHECK_EQ(3, break_point_hit_count); CHECK_EQ("new name", last_script_name_hit); CHECK_EQ("abc 123", last_script_data_hit); + + v8::Handle<v8::Script> script3 = + v8::Script::Compile(script, &origin2, NULL, + v8::String::New("in compile")); + CHECK_EQ("in compile", last_script_data_hit); + script3->Run(); + f = v8::Local<v8::Function>::Cast(env->Global()->Get(v8::String::New("f"))); + f->Call(env->Global(), 0, NULL); + CHECK_EQ(4, break_point_hit_count); + CHECK_EQ("in compile", last_script_data_hit); } diff --git a/test/cctest/test-log.cc b/test/cctest/test-log.cc index eca2c2b6..9853af32 100644 --- a/test/cctest/test-log.cc +++ b/test/cctest/test-log.cc @@ -170,21 +170,110 @@ static void SigProfSignalHandler(int signal, siginfo_t* info, void* context) { #endif // __linux__ -static int CheckThatProfilerWorks(int log_pos) { - Logger::ResumeProfiler(v8::PROFILER_MODULE_CPU); +namespace { + +class ScopedLoggerInitializer { + public: + explicit ScopedLoggerInitializer(bool log, bool prof_lazy) + : saved_log_(i::FLAG_log), + saved_prof_lazy_(i::FLAG_prof_lazy), + saved_prof_(i::FLAG_prof), + saved_prof_auto_(i::FLAG_prof_auto), + trick_to_run_init_flags_(init_flags_(log, prof_lazy)), + need_to_set_up_logger_(i::V8::IsRunning()), + scope_(), + env_(v8::Context::New()) { + if (need_to_set_up_logger_) Logger::Setup(); + env_->Enter(); + } + + ~ScopedLoggerInitializer() { + env_->Exit(); + Logger::TearDown(); + i::FLAG_prof_lazy = saved_prof_lazy_; + i::FLAG_prof = saved_prof_; + i::FLAG_prof_auto = saved_prof_auto_; + i::FLAG_log = saved_log_; + } + + v8::Handle<v8::Context>& env() { return env_; } + + private: + static bool init_flags_(bool log, bool prof_lazy) { + i::FLAG_log = log; + i::FLAG_prof = true; + i::FLAG_prof_lazy = prof_lazy; + i::FLAG_prof_auto = false; + i::FLAG_logfile = "*"; + return prof_lazy; + } + + const bool saved_log_; + const bool saved_prof_lazy_; + const bool saved_prof_; + const bool saved_prof_auto_; + const bool trick_to_run_init_flags_; + const bool need_to_set_up_logger_; + v8::HandleScope scope_; + v8::Handle<v8::Context> env_; + + DISALLOW_COPY_AND_ASSIGN(ScopedLoggerInitializer); +}; + + +class LogBufferMatcher { + public: + LogBufferMatcher() { + // Skip all initially logged stuff. + log_pos_ = GetLogLines(0, &buffer_); + } + + int log_pos() { return log_pos_; } + + int GetNextChunk() { + int chunk_size = GetLogLines(log_pos_, &buffer_); + CHECK_GT(buffer_.length(), chunk_size); + buffer_[chunk_size] = '\0'; + log_pos_ += chunk_size; + return chunk_size; + } + + const char* Find(const char* substr) { + return strstr(buffer_.start(), substr); + } + + const char* Find(const i::Vector<char>& substr) { + return Find(substr.start()); + } + + bool IsInSequence(const char* s1, const char* s2) { + const char* s1_pos = Find(s1); + const char* s2_pos = Find(s2); + CHECK_NE(NULL, s1_pos); + CHECK_NE(NULL, s2_pos); + return s1_pos < s2_pos; + } + + void PrintBuffer() { + puts(buffer_.start()); + } + + private: + EmbeddedVector<char, 102400> buffer_; + int log_pos_; +}; + +} // namespace + + +static void CheckThatProfilerWorks(LogBufferMatcher* matcher) { + Logger::ResumeProfiler(v8::PROFILER_MODULE_CPU, 0); CHECK(LoggerTestHelper::IsSamplerActive()); // Verify that the current map of compiled functions has been logged. - EmbeddedVector<char, 102400> buffer; - int map_log_size = GetLogLines(log_pos, &buffer); - printf("map_log_size: %d\n", map_log_size); - CHECK_GT(map_log_size, 0); - CHECK_GT(buffer.length(), map_log_size); - log_pos += map_log_size; - // Check buffer contents. - buffer[map_log_size] = '\0'; + CHECK_GT(matcher->GetNextChunk(), 0); const char* code_creation = "\ncode-creation,"; // eq. to /^code-creation,/ - CHECK_NE(NULL, strstr(buffer.start(), code_creation)); + CHECK_NE(NULL, matcher->Find(code_creation)); #ifdef __linux__ // Intercept SIGPROF handler to make sure that the test process @@ -204,7 +293,7 @@ static int CheckThatProfilerWorks(int log_pos) { i::OS::SNPrintF(script_src, "function f%d(x) { return %d * x; }" "for (var i = 0; i < 10000; ++i) { f%d(i); }", - log_pos, log_pos, log_pos); + matcher->log_pos(), matcher->log_pos(), matcher->log_pos()); // Run code for 200 msecs to get some ticks. const double end_time = i::OS::TimeCurrentMillis() + 200; while (i::OS::TimeCurrentMillis() < end_time) { @@ -213,7 +302,7 @@ static int CheckThatProfilerWorks(int log_pos) { i::OS::Sleep(1); } - Logger::PauseProfiler(v8::PROFILER_MODULE_CPU); + Logger::PauseProfiler(v8::PROFILER_MODULE_CPU, 0); CHECK(!LoggerTestHelper::IsSamplerActive()); // Wait 50 msecs to allow Profiler thread to process the last @@ -221,68 +310,39 @@ static int CheckThatProfilerWorks(int log_pos) { i::OS::Sleep(50); // Now we must have compiler and tick records. - int log_size = GetLogLines(log_pos, &buffer); - printf("log_size: %d\n", log_size); - CHECK_GT(log_size, 0); - CHECK_GT(buffer.length(), log_size); - log_pos += log_size; - // Check buffer contents. - buffer[log_size] = '\0'; - printf("%s", buffer.start()); + CHECK_GT(matcher->GetNextChunk(), 0); + matcher->PrintBuffer(); + CHECK_NE(NULL, matcher->Find(code_creation)); const char* tick = "\ntick,"; - CHECK_NE(NULL, strstr(buffer.start(), code_creation)); - const bool ticks_found = strstr(buffer.start(), tick) != NULL; + const bool ticks_found = matcher->Find(tick) != NULL; CHECK_EQ(was_sigprof_received, ticks_found); - - return log_pos; } TEST(ProfLazyMode) { - const bool saved_prof_lazy = i::FLAG_prof_lazy; - const bool saved_prof = i::FLAG_prof; - const bool saved_prof_auto = i::FLAG_prof_auto; - i::FLAG_prof = true; - i::FLAG_prof_lazy = true; - i::FLAG_prof_auto = false; - i::FLAG_logfile = "*"; - - // If tests are being run manually, V8 will be already initialized - // by the bottom test. - const bool need_to_set_up_logger = i::V8::IsRunning(); - v8::HandleScope scope; - v8::Handle<v8::Context> env = v8::Context::New(); - if (need_to_set_up_logger) Logger::Setup(); - env->Enter(); + ScopedLoggerInitializer initialize_logger(false, true); // No sampling should happen prior to resuming profiler. CHECK(!LoggerTestHelper::IsSamplerActive()); - EmbeddedVector<char, 102400> buffer; + LogBufferMatcher matcher; // Nothing must be logged until profiling is resumed. - int log_pos = GetLogLines(0, &buffer); - CHECK_EQ(0, log_pos); + CHECK_EQ(0, matcher.log_pos()); CompileAndRunScript("var a = (function(x) { return x + 1; })(10);"); // Nothing must be logged while profiling is suspended. - CHECK_EQ(0, GetLogLines(log_pos, &buffer)); + CHECK_EQ(0, matcher.GetNextChunk()); - log_pos = CheckThatProfilerWorks(log_pos); + CheckThatProfilerWorks(&matcher); CompileAndRunScript("var a = (function(x) { return x + 1; })(10);"); // No new data beyond last retrieved position. - CHECK_EQ(0, GetLogLines(log_pos, &buffer)); + CHECK_EQ(0, matcher.GetNextChunk()); // Check that profiling can be resumed again. - CheckThatProfilerWorks(log_pos); - - env->Exit(); - Logger::TearDown(); - i::FLAG_prof_lazy = saved_prof_lazy; - i::FLAG_prof = saved_prof; - i::FLAG_prof_auto = saved_prof_auto; + CheckThatProfilerWorks(&matcher); } @@ -480,25 +540,8 @@ static v8::Handle<v8::Value> ObjMethod1(const v8::Arguments& args) { } TEST(LogCallbacks) { - const bool saved_prof_lazy = i::FLAG_prof_lazy; - const bool saved_prof = i::FLAG_prof; - const bool saved_prof_auto = i::FLAG_prof_auto; - i::FLAG_prof = true; - i::FLAG_prof_lazy = false; - i::FLAG_prof_auto = false; - i::FLAG_logfile = "*"; - - // If tests are being run manually, V8 will be already initialized - // by the bottom test. - const bool need_to_set_up_logger = i::V8::IsRunning(); - v8::HandleScope scope; - v8::Handle<v8::Context> env = v8::Context::New(); - if (need_to_set_up_logger) Logger::Setup(); - env->Enter(); - - // Skip all initially logged stuff. - EmbeddedVector<char, 102400> buffer; - int log_pos = GetLogLines(0, &buffer); + ScopedLoggerInitializer initialize_logger(false, false); + LogBufferMatcher matcher; v8::Persistent<v8::FunctionTemplate> obj = v8::Persistent<v8::FunctionTemplate>::New(v8::FunctionTemplate::New()); @@ -511,16 +554,14 @@ TEST(LogCallbacks) { signature), static_cast<v8::PropertyAttribute>(v8::DontDelete)); - env->Global()->Set(v8_str("Obj"), obj->GetFunction()); + initialize_logger.env()->Global()->Set(v8_str("Obj"), obj->GetFunction()); CompileAndRunScript("Obj.prototype.method1.toString();"); i::Logger::LogCompiledFunctions(); - log_pos = GetLogLines(log_pos, &buffer); - CHECK_GT(log_pos, 0); - buffer[log_pos] = 0; + CHECK_GT(matcher.GetNextChunk(), 0); const char* callback_rec = "code-creation,Callback,"; - char* pos = strstr(buffer.start(), callback_rec); + char* pos = const_cast<char*>(matcher.Find(callback_rec)); CHECK_NE(NULL, pos); pos += strlen(callback_rec); EmbeddedVector<char, 100> ref_data; @@ -530,12 +571,6 @@ TEST(LogCallbacks) { CHECK_EQ(ref_data.start(), pos); obj.Dispose(); - - env->Exit(); - Logger::TearDown(); - i::FLAG_prof_lazy = saved_prof_lazy; - i::FLAG_prof = saved_prof; - i::FLAG_prof_auto = saved_prof_auto; } @@ -555,25 +590,8 @@ static v8::Handle<v8::Value> Prop2Getter(v8::Local<v8::String> property, } TEST(LogAccessorCallbacks) { - const bool saved_prof_lazy = i::FLAG_prof_lazy; - const bool saved_prof = i::FLAG_prof; - const bool saved_prof_auto = i::FLAG_prof_auto; - i::FLAG_prof = true; - i::FLAG_prof_lazy = false; - i::FLAG_prof_auto = false; - i::FLAG_logfile = "*"; - - // If tests are being run manually, V8 will be already initialized - // by the bottom test. - const bool need_to_set_up_logger = i::V8::IsRunning(); - v8::HandleScope scope; - v8::Handle<v8::Context> env = v8::Context::New(); - if (need_to_set_up_logger) Logger::Setup(); - env->Enter(); - - // Skip all initially logged stuff. - EmbeddedVector<char, 102400> buffer; - int log_pos = GetLogLines(0, &buffer); + ScopedLoggerInitializer initialize_logger(false, false); + LogBufferMatcher matcher; v8::Persistent<v8::FunctionTemplate> obj = v8::Persistent<v8::FunctionTemplate>::New(v8::FunctionTemplate::New()); @@ -583,34 +601,112 @@ TEST(LogAccessorCallbacks) { inst->SetAccessor(v8::String::New("prop2"), Prop2Getter); i::Logger::LogAccessorCallbacks(); - log_pos = GetLogLines(log_pos, &buffer); - CHECK_GT(log_pos, 0); - buffer[log_pos] = 0; - printf("%s", buffer.start()); + CHECK_GT(matcher.GetNextChunk(), 0); + matcher.PrintBuffer(); EmbeddedVector<char, 100> prop1_getter_record; i::OS::SNPrintF(prop1_getter_record, "code-creation,Callback,0x%" V8PRIxPTR ",1,\"get prop1\"", Prop1Getter); - CHECK_NE(NULL, strstr(buffer.start(), prop1_getter_record.start())); + CHECK_NE(NULL, matcher.Find(prop1_getter_record)); EmbeddedVector<char, 100> prop1_setter_record; i::OS::SNPrintF(prop1_setter_record, "code-creation,Callback,0x%" V8PRIxPTR ",1,\"set prop1\"", Prop1Setter); - CHECK_NE(NULL, strstr(buffer.start(), prop1_setter_record.start())); + CHECK_NE(NULL, matcher.Find(prop1_setter_record)); EmbeddedVector<char, 100> prop2_getter_record; i::OS::SNPrintF(prop2_getter_record, "code-creation,Callback,0x%" V8PRIxPTR ",1,\"get prop2\"", Prop2Getter); - CHECK_NE(NULL, strstr(buffer.start(), prop2_getter_record.start())); + CHECK_NE(NULL, matcher.Find(prop2_getter_record)); obj.Dispose(); +} - env->Exit(); - Logger::TearDown(); - i::FLAG_prof_lazy = saved_prof_lazy; - i::FLAG_prof = saved_prof; - i::FLAG_prof_auto = saved_prof_auto; + +TEST(LogTags) { + ScopedLoggerInitializer initialize_logger(true, false); + LogBufferMatcher matcher; + + const char* open_tag = "open-tag,"; + const char* close_tag = "close-tag,"; + + // Check compatibility with the old style behavior. + CHECK_EQ(v8::PROFILER_MODULE_NONE, Logger::GetActiveProfilerModules()); + Logger::ResumeProfiler(v8::PROFILER_MODULE_CPU, 0); + CHECK_EQ(v8::PROFILER_MODULE_CPU, Logger::GetActiveProfilerModules()); + Logger::PauseProfiler(v8::PROFILER_MODULE_CPU, 0); + CHECK_EQ(v8::PROFILER_MODULE_NONE, Logger::GetActiveProfilerModules()); + CHECK_EQ(NULL, matcher.Find(open_tag)); + CHECK_EQ(NULL, matcher.Find(close_tag)); + + const char* open_tag1 = "open-tag,1\n"; + const char* close_tag1 = "close-tag,1\n"; + + // Check non-nested tag case. + CHECK_EQ(v8::PROFILER_MODULE_NONE, Logger::GetActiveProfilerModules()); + Logger::ResumeProfiler(v8::PROFILER_MODULE_CPU, 1); + CHECK_EQ(v8::PROFILER_MODULE_CPU, Logger::GetActiveProfilerModules()); + Logger::PauseProfiler(v8::PROFILER_MODULE_CPU, 1); + CHECK_EQ(v8::PROFILER_MODULE_NONE, Logger::GetActiveProfilerModules()); + CHECK_GT(matcher.GetNextChunk(), 0); + CHECK(matcher.IsInSequence(open_tag1, close_tag1)); + + const char* open_tag2 = "open-tag,2\n"; + const char* close_tag2 = "close-tag,2\n"; + + // Check nested tags case. + CHECK_EQ(v8::PROFILER_MODULE_NONE, Logger::GetActiveProfilerModules()); + Logger::ResumeProfiler(v8::PROFILER_MODULE_CPU, 1); + CHECK_EQ(v8::PROFILER_MODULE_CPU, Logger::GetActiveProfilerModules()); + Logger::ResumeProfiler(v8::PROFILER_MODULE_CPU, 2); + CHECK_EQ(v8::PROFILER_MODULE_CPU, Logger::GetActiveProfilerModules()); + Logger::PauseProfiler(v8::PROFILER_MODULE_CPU, 2); + CHECK_EQ(v8::PROFILER_MODULE_CPU, Logger::GetActiveProfilerModules()); + Logger::PauseProfiler(v8::PROFILER_MODULE_CPU, 1); + CHECK_EQ(v8::PROFILER_MODULE_NONE, Logger::GetActiveProfilerModules()); + CHECK_GT(matcher.GetNextChunk(), 0); + // open_tag1 < open_tag2 < close_tag2 < close_tag1 + CHECK(matcher.IsInSequence(open_tag1, open_tag2)); + CHECK(matcher.IsInSequence(open_tag2, close_tag2)); + CHECK(matcher.IsInSequence(close_tag2, close_tag1)); + + // Check overlapped tags case. + CHECK_EQ(v8::PROFILER_MODULE_NONE, Logger::GetActiveProfilerModules()); + Logger::ResumeProfiler(v8::PROFILER_MODULE_CPU, 1); + CHECK_EQ(v8::PROFILER_MODULE_CPU, Logger::GetActiveProfilerModules()); + Logger::ResumeProfiler(v8::PROFILER_MODULE_CPU, 2); + CHECK_EQ(v8::PROFILER_MODULE_CPU, Logger::GetActiveProfilerModules()); + Logger::PauseProfiler(v8::PROFILER_MODULE_CPU, 1); + CHECK_EQ(v8::PROFILER_MODULE_CPU, Logger::GetActiveProfilerModules()); + Logger::PauseProfiler(v8::PROFILER_MODULE_CPU, 2); + CHECK_EQ(v8::PROFILER_MODULE_NONE, Logger::GetActiveProfilerModules()); + CHECK_GT(matcher.GetNextChunk(), 0); + // open_tag1 < open_tag2 < close_tag1 < close_tag2 + CHECK(matcher.IsInSequence(open_tag1, open_tag2)); + CHECK(matcher.IsInSequence(open_tag2, close_tag1)); + CHECK(matcher.IsInSequence(close_tag1, close_tag2)); + + const char* open_tag3 = "open-tag,3\n"; + const char* close_tag3 = "close-tag,3\n"; + + // Check pausing overflow case. + CHECK_EQ(v8::PROFILER_MODULE_NONE, Logger::GetActiveProfilerModules()); + Logger::ResumeProfiler(v8::PROFILER_MODULE_CPU, 1); + CHECK_EQ(v8::PROFILER_MODULE_CPU, Logger::GetActiveProfilerModules()); + Logger::ResumeProfiler(v8::PROFILER_MODULE_CPU, 2); + CHECK_EQ(v8::PROFILER_MODULE_CPU, Logger::GetActiveProfilerModules()); + Logger::PauseProfiler(v8::PROFILER_MODULE_CPU, 2); + CHECK_EQ(v8::PROFILER_MODULE_CPU, Logger::GetActiveProfilerModules()); + Logger::PauseProfiler(v8::PROFILER_MODULE_CPU, 1); + CHECK_EQ(v8::PROFILER_MODULE_NONE, Logger::GetActiveProfilerModules()); + Logger::PauseProfiler(v8::PROFILER_MODULE_CPU, 3); + CHECK_EQ(v8::PROFILER_MODULE_NONE, Logger::GetActiveProfilerModules()); + Logger::ResumeProfiler(v8::PROFILER_MODULE_CPU, 3); + CHECK_EQ(v8::PROFILER_MODULE_NONE, Logger::GetActiveProfilerModules()); + // Must be no tags, because logging must be disabled. + CHECK_EQ(NULL, matcher.Find(open_tag3)); + CHECK_EQ(NULL, matcher.Find(close_tag3)); } diff --git a/test/cctest/test-serialize.cc b/test/cctest/test-serialize.cc index 18f69880..5335e9b6 100644 --- a/test/cctest/test-serialize.cc +++ b/test/cctest/test-serialize.cc @@ -131,7 +131,7 @@ TEST(ExternalReferenceEncoder) { ExternalReference::address_of_real_stack_limit(); CHECK_EQ(make_code(UNCLASSIFIED, 5), encoder.Encode(real_stack_limit_address.address())); - CHECK_EQ(make_code(UNCLASSIFIED, 11), + CHECK_EQ(make_code(UNCLASSIFIED, 12), encoder.Encode(ExternalReference::debug_break().address())); CHECK_EQ(make_code(UNCLASSIFIED, 7), encoder.Encode(ExternalReference::new_space_start().address())); @@ -165,7 +165,7 @@ TEST(ExternalReferenceDecoder) { CHECK_EQ(ExternalReference::address_of_real_stack_limit().address(), decoder.Decode(make_code(UNCLASSIFIED, 5))); CHECK_EQ(ExternalReference::debug_break().address(), - decoder.Decode(make_code(UNCLASSIFIED, 11))); + decoder.Decode(make_code(UNCLASSIFIED, 12))); CHECK_EQ(ExternalReference::new_space_start().address(), decoder.Decode(make_code(UNCLASSIFIED, 7))); } diff --git a/test/cctest/test-utils.cc b/test/cctest/test-utils.cc index 1d65e686..24b3c908 100644 --- a/test/cctest/test-utils.cc +++ b/test/cctest/test-utils.cc @@ -35,111 +35,6 @@ using namespace v8::internal; -enum Mode { - forward, - backward_unsigned -}; - - -static v8::internal::byte* Write(v8::internal::byte* p, Mode m, int x) { - v8::internal::byte* q = NULL; - switch (m) { - case forward: - q = EncodeInt(p, x); - CHECK(q <= p + sizeof(x) + 1); - break; - case backward_unsigned: - q = EncodeUnsignedIntBackward(p, x); - CHECK(q >= p - sizeof(x) - 1); - break; - } - return q; -} - - -static v8::internal::byte* Read(v8::internal::byte* p, Mode m, int x) { - v8::internal::byte* q = NULL; - int y; - switch (m) { - case forward: - q = DecodeInt(p, &y); - CHECK(q <= p + sizeof(y) + 1); - break; - case backward_unsigned: { - unsigned int uy; - q = DecodeUnsignedIntBackward(p, &uy); - y = uy; - CHECK(q >= p - sizeof(uy) - 1); - break; - } - } - CHECK(y == x); - return q; -} - - -static v8::internal::byte* WriteMany(v8::internal::byte* p, Mode m, int x) { - p = Write(p, m, x - 7); - p = Write(p, m, x - 1); - p = Write(p, m, x); - p = Write(p, m, x + 1); - p = Write(p, m, x + 2); - p = Write(p, m, -x - 5); - p = Write(p, m, -x - 1); - p = Write(p, m, -x); - p = Write(p, m, -x + 1); - p = Write(p, m, -x + 3); - - return p; -} - - -static v8::internal::byte* ReadMany(v8::internal::byte* p, Mode m, int x) { - p = Read(p, m, x - 7); - p = Read(p, m, x - 1); - p = Read(p, m, x); - p = Read(p, m, x + 1); - p = Read(p, m, x + 2); - p = Read(p, m, -x - 5); - p = Read(p, m, -x - 1); - p = Read(p, m, -x); - p = Read(p, m, -x + 1); - p = Read(p, m, -x + 3); - - return p; -} - - -void ProcessValues(int* values, int n, Mode m) { - v8::internal::byte buf[4 * KB]; // make this big enough - v8::internal::byte* p0 = (m == forward ? buf : buf + ARRAY_SIZE(buf)); - - v8::internal::byte* p = p0; - for (int i = 0; i < n; i++) { - p = WriteMany(p, m, values[i]); - } - - v8::internal::byte* q = p0; - for (int i = 0; i < n; i++) { - q = ReadMany(q, m, values[i]); - } - - CHECK(p == q); -} - - -TEST(Utils0) { - int values[] = { - 0, 1, 10, 16, 32, 64, 128, 256, 512, 1024, 1234, 5731, - 10000, 100000, 1000000, 10000000, 100000000, 1000000000 - }; - const int n = ARRAY_SIZE(values); - - ProcessValues(values, n, forward); - ProcessValues(values, n, backward_unsigned); -} - - TEST(Utils1) { CHECK_EQ(-1000000, FastD2I(-1000000.0)); CHECK_EQ(-1, FastD2I(-1.0)); diff --git a/test/mjsunit/array-functions-prototype-misc.js b/test/mjsunit/array-functions-prototype-misc.js new file mode 100644 index 00000000..0543c323 --- /dev/null +++ b/test/mjsunit/array-functions-prototype-misc.js @@ -0,0 +1,314 @@ +// Copyright 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. + +/** + * @fileoverview Test splice, shift, unshift, slice and join on small + * and large arrays. Some of these methods are specified such that they + * should work on other objects too, so we test that too. + */ + +var LARGE = 40000000; +var VERYLARGE = 4000000000; + +// Nicer for firefox 1.5. Unless you uncomment the following two lines, +// smjs will appear to hang on this file. +//var LARGE = 40000; +//var VERYLARGE = 40000; + +var fourhundredth = LARGE/400; + +function PseudoArray() { +}; + +for (var use_real_arrays = 0; use_real_arrays <= 1; use_real_arrays++) { + var poses = [0, 140, 20000, VERYLARGE]; + var the_prototype; + var new_function; + var push_function; + var concat_function; + var slice_function; + var splice_function; + var splice_function_2; + var unshift_function; + var unshift_function_2; + var shift_function; + if (use_real_arrays) { + new_function = function(length) { + return new Array(length); + }; + the_prototype = Array.prototype; + push_function = function(array, elt) { + return array.push(elt); + }; + concat_function = function(array, other) { + return array.concat(other); + }; + slice_function = function(array, start, len) { + return array.slice(start, len); + }; + splice_function = function(array, start, len) { + return array.splice(start, len); + }; + splice_function_2 = function(array, start, len, elt) { + return array.splice(start, len, elt); + }; + unshift_function = function(array, elt) { + return array.unshift(elt); + }; + unshift_function_2 = function(array, elt1, elt2) { + return array.unshift(elt1, elt2); + }; + shift_function = function(array) { + return array.shift(); + }; + } else { + // Don't run largest size on non-arrays or we'll be here for ever. + poses.pop(); + new_function = function(length) { + var obj = new PseudoArray(); + obj.length = length; + return obj; + }; + the_prototype = PseudoArray.prototype; + push_function = function(array, elt) { + array[array.length] = elt; + array.length++; + }; + concat_function = function(array, other) { + return Array.prototype.concat.call(array, other); + }; + slice_function = function(array, start, len) { + return Array.prototype.slice.call(array, start, len); + }; + splice_function = function(array, start, len) { + return Array.prototype.splice.call(array, start, len); + }; + splice_function_2 = function(array, start, len, elt) { + return Array.prototype.splice.call(array, start, len, elt); + }; + unshift_function = function(array, elt) { + return Array.prototype.unshift.call(array, elt); + }; + unshift_function_2 = function(array, elt1, elt2) { + return Array.prototype.unshift.call(array, elt1, elt2); + }; + shift_function = function(array) { + return Array.prototype.shift.call(array); + }; + } + + for (var pos_pos = 0; pos_pos < poses.length; pos_pos++) { + var pos = poses[pos_pos]; + if (pos > 100) { + var a = new_function(pos); + assertEquals(pos, a.length); + push_function(a, 'foo'); + assertEquals(pos + 1, a.length); + var b = ['bar']; + // Delete a huge number of holes. + var c = splice_function(a, 10, pos - 20); + assertEquals(pos - 20, c.length); + assertEquals(21, a.length); + } + + // Add a numeric property to the prototype of the array class. This + // allows us to test some borderline stuff relative to the standard. + the_prototype["" + (pos + 1)] = 'baz'; + + if (use_real_arrays) { + // It seems quite clear from ECMAScript spec 15.4.4.5. Just call Get on + // every integer in the range. + // IE, Safari get this right. + // FF, Opera get this wrong. + var a = ['zero', ,'two']; + if (pos == 0) { + assertEquals("zero,baz,two", a.join(",")); + } + + // Concat only applies to real arrays, unlike most of the other methods. + var a = new_function(pos); + push_function(a, "con"); + assertEquals("con", a[pos]); + assertEquals(pos + 1, a.length); + var b = new_function(0); + push_function(b, "cat"); + assertEquals("cat", b[0]); + var ab = concat_function(a, b); + assertEquals("con", ab[pos]); + assertEquals(pos + 2, ab.length); + assertEquals("cat", ab[pos + 1]); + var ba = concat_function(b, a); + assertEquals("con", ba[pos + 1]); + assertEquals(pos + 2, ba.length); + assertEquals("cat", ba[0]); + + // Join with '' as separator. + var join = a.join(''); + assertEquals("con", join); + join = b.join(''); + assertEquals("cat", join); + join = ab.join(''); + assertEquals("concat", join); + join = ba.join(''); + assertEquals("catcon", join); + + var sparse = []; + sparse[pos + 1000] = 'is '; + sparse[pos + 271828] = 'time '; + sparse[pos + 31415] = 'the '; + sparse[pos + 012260199] = 'all '; + sparse[-1] = 'foo'; + sparse[pos + 22591927] = 'good '; + sparse[pos + 1618033] = 'for '; + sparse[pos + 91] = ': Now '; + sparse[pos + 86720199] = 'men.'; + sparse.hest = 'fisk'; + + assertEquals("baz: Now is the time for all good men.", sparse.join('')); + } + + a = new_function(pos); + push_function(a, 'zero'); + push_function(a, void 0); + push_function(a, 'two'); + + // Splice works differently from join. + // IE, Safari get this wrong. + // FF, Opera get this right. + // 15.4.4.12 line 24 says the object itself has to have the property... + var zero = splice_function(a, pos, 1); + assertEquals("undefined", typeof(a[pos])); + assertEquals("two", a[pos+1], "pos1:" + pos); + assertEquals(pos + 2, a.length, "a length"); + assertEquals(1, zero.length, "zero length"); + assertEquals("zero", zero[0]); + + // 15.4.4.12 line 41 says the object itself has to have the property... + a = new_function(pos); + push_function(a, 'zero'); + push_function(a, void 0); + push_function(a, 'two'); + var nothing = splice_function_2(a, pos, 0, 'minus1'); + assertEquals("minus1", a[pos]); + assertEquals("zero", a[pos+1]); + assertEquals("undefined", typeof(a[pos+2]), "toot!"); + assertEquals("two", a[pos+3], "pos3"); + assertEquals(pos + 4, a.length); + assertEquals(1, zero.length); + assertEquals("zero", zero[0]); + + // 15.4.4.12 line 10 says the object itself has to have the property... + a = new_function(pos); + push_function(a, 'zero'); + push_function(a, void 0); + push_function(a, 'two'); + var one = splice_function(a, pos + 1, 1); + assertEquals("", one.join(",")); + assertEquals(pos + 2, a.length); + assertEquals("zero", a[pos]); + assertEquals("two", a[pos+1]); + + // Set things back to the way they were. + the_prototype[pos + 1] = undefined; + + // Unshift. + var a = new_function(pos); + push_function(a, "foo"); + assertEquals("foo", a[pos]); + assertEquals(pos + 1, a.length); + unshift_function(a, "bar"); + assertEquals("foo", a[pos+1]); + assertEquals(pos + 2, a.length); + assertEquals("bar", a[0]); + unshift_function_2(a, "baz", "boo"); + assertEquals("foo", a[pos+3]); + assertEquals(pos + 4, a.length); + assertEquals("baz", a[0]); + assertEquals("boo", a[1]); + assertEquals("bar", a[2]); + + // Shift. + var baz = shift_function(a); + assertEquals("baz", baz); + assertEquals("boo", a[0]); + assertEquals(pos + 3, a.length); + assertEquals("foo", a[pos + 2]); + + // Slice. + var bar = slice_function(a, 1, 0); // don't throw an exception please. + bar = slice_function(a, 1, 2); + assertEquals("bar", bar[0]); + assertEquals(1, bar.length); + assertEquals("bar", a[1]); + + } +} + +// Lets see if performance is reasonable. + +var a = new Array(LARGE + 10); +for (var i = 0; i < a.length; i += 1000) { + a[i] = i; +} + +// Take something near the end of the array. +for (var i = 0; i < 100; i++) { + var top = a.splice(LARGE, 5); + assertEquals(5, top.length); + assertEquals(LARGE, top[0]); + assertEquals("undefined", typeof(top[1])); + assertEquals(LARGE + 5, a.length); + a.splice(LARGE, 0, LARGE); + a.length = LARGE + 10; +} + +var a = new Array(LARGE + 10); +for (var i = 0; i < a.length; i += fourhundredth) { + a[i] = i; +} + +// Take something near the middle of the array. +for (var i = 0; i < 10; i++) { + var top = a.splice(LARGE >> 1, 5); + assertEquals(5, top.length); + assertEquals(LARGE >> 1, top[0]); + assertEquals("undefined", typeof(top[1])); + assertEquals(LARGE + 5, a.length); + a.splice(LARGE >> 1, 0, LARGE >> 1, void 0, void 0, void 0, void 0); +} + + +// Test http://b/issue?id=1202711 +arr = [0]; +arr.length = 2; +Array.prototype[1] = 1; +assertEquals(1, arr.pop()); +assertEquals(0, arr.pop()); +Array.prototype[1] = undefined; + +// Test http://code.google.com/p/chromium/issues/detail?id=21860 +Array.prototype.push.apply([], [1].splice(0, -(-1 % 5))); diff --git a/test/mjsunit/array-shift.js b/test/mjsunit/array-shift.js new file mode 100644 index 00000000..d985b31e --- /dev/null +++ b/test/mjsunit/array-shift.js @@ -0,0 +1,71 @@ +// Copyright 2010 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. + +// Check that shifting array of holes keeps it as array of holes +(function() { + var array = new Array(10); + array.shift(); + assertFalse(0 in array); +})(); + +// Now check the case with array of holes and some elements on prototype. +(function() { + var len = 9; + var array = new Array(len); + Array.prototype[3] = "@3"; + Array.prototype[7] = "@7"; + + assertEquals(len, array.length); + for (var i = 0; i < array.length; i++) { + assertEquals(array[i], Array.prototype[i]); + } + + array.shift(); + + assertEquals(len - 1, array.length); + // Note that shift copies values from prototype into the array. + assertEquals(array[2], Array.prototype[3]); + assertTrue(array.hasOwnProperty(2)); + + assertEquals(array[6], Array.prototype[7]); + assertTrue(array.hasOwnProperty(6)); + + // ... but keeps the rest as holes: + Array.prototype[5] = "@5"; + assertEquals(array[5], Array.prototype[5]); + assertFalse(array.hasOwnProperty(5)); + + assertEquals(array[3], Array.prototype[3]); + assertFalse(array.hasOwnProperty(3)); + + assertEquals(array[7], Array.prototype[7]); + assertFalse(array.hasOwnProperty(7)); + + assertTrue(delete Array.prototype[3]); + assertTrue(delete Array.prototype[5]); + assertTrue(delete Array.prototype[7]); +})(); diff --git a/test/mjsunit/array-slice.js b/test/mjsunit/array-slice.js new file mode 100644 index 00000000..c993a077 --- /dev/null +++ b/test/mjsunit/array-slice.js @@ -0,0 +1,162 @@ +// Copyright 2010 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. + +// Check that slicing array of holes keeps it as array of holes +(function() { + var array = new Array(10); + for (var i = 0; i < 7; i++) { + var sliced = array.slice(); + assertEquals(array.length, sliced.length); + assertFalse(0 in sliced); + } +})(); + + +// Check various forms of arguments omission. +(function() { + var array = new Array(7); + + for (var i = 0; i < 7; i++) { + assertEquals(array, array.slice()); + assertEquals(array, array.slice(0)); + assertEquals(array, array.slice(undefined)); + assertEquals(array, array.slice("foobar")); + assertEquals(array, array.slice(undefined, undefined)); + } +})(); + + +// Check variants of negatives and positive indices. +(function() { + var array = new Array(7); + + for (var i = 0; i < 7; i++) { + assertEquals(7, array.slice(-100).length); + assertEquals(3, array.slice(-3).length); + assertEquals(3, array.slice(4).length); + assertEquals(1, array.slice(6).length); + assertEquals(0, array.slice(7).length); + assertEquals(0, array.slice(8).length); + assertEquals(0, array.slice(100).length); + + assertEquals(0, array.slice(0, -100).length); + assertEquals(4, array.slice(0, -3).length); + assertEquals(4, array.slice(0, 4).length); + assertEquals(6, array.slice(0, 6).length); + assertEquals(7, array.slice(0, 7).length); + assertEquals(7, array.slice(0, 8).length); + assertEquals(7, array.slice(0, 100).length); + + // Some exotic cases. + + obj = { toString: function() { throw 'Exception'; } }; + + // More than 2 arguments: + assertEquals(7, array.slice(0, 7, obj, null, undefined).length); + + // Custom conversion: + assertEquals(1, array.slice({valueOf: function() { return 1; }}, + {toString: function() { return 2; }}).length); + + // Throwing an exception in conversion: + try { + assertEquals(7, array.slice(0, obj).length); + throw 'Should have thrown'; + } catch (e) { + assertEquals('Exception', e); + } + } +})(); + + +// Nasty: modify the array in ToInteger. +(function() { + var array = []; + var expected = [] + bad_guy = { valueOf: function() { array.push(array.length); return -1; } }; + + for (var i = 0; i < 13; i++) { + var sliced = array.slice(bad_guy); + expected.push(i); + assertEquals(expected, array); + // According to the spec (15.4.4.10), length is calculated before + // performing ToInteger on arguments. + if (i == 0) { + assertEquals([], sliced); // Length was 0, nothing to get. + } else { + // Actually out of array [0..i] we get [i - 1] as length is i. + assertEquals([i - 1], sliced); + } + } +})(); + + +// Now check the case with array of holes and some elements on prototype. +(function() { + var len = 9; + var array = new Array(len); + + var at3 = "@3"; + var at7 = "@7"; + + for (var i = 0; i < 7; i++) { + Array.prototype[3] = at3; + Array.prototype[7] = at7; + + assertEquals(len, array.length); + for (var i = 0; i < array.length; i++) { + assertEquals(array[i], Array.prototype[i]); + } + + var sliced = array.slice(); + + assertEquals(len, sliced.length); + + assertTrue(delete Array.prototype[3]); + assertTrue(delete Array.prototype[7]); + + // Note that slice copies values from prototype into the array. + assertEquals(array[3], undefined); + assertFalse(array.hasOwnProperty(3)); + assertEquals(sliced[3], at3); + assertTrue(sliced.hasOwnProperty(3)); + + assertEquals(array[7], undefined); + assertFalse(array.hasOwnProperty(7)); + assertEquals(sliced[7], at7); + assertTrue(sliced.hasOwnProperty(7)); + + // ... but keeps the rest as holes: + Array.prototype[5] = "@5"; + assertEquals(array[5], Array.prototype[5]); + assertFalse(array.hasOwnProperty(5)); + assertEquals(sliced[5], Array.prototype[5]); + assertFalse(sliced.hasOwnProperty(5)); + + assertTrue(delete Array.prototype[5]); + } +})(); diff --git a/test/mjsunit/array-splice.js b/test/mjsunit/array-splice.js index 0543c323..18f81fe8 100644 --- a/test/mjsunit/array-splice.js +++ b/test/mjsunit/array-splice.js @@ -1,4 +1,4 @@ -// Copyright 2008 the V8 project authors. All rights reserved. +// Copyright 2010 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: @@ -25,290 +25,265 @@ // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -/** - * @fileoverview Test splice, shift, unshift, slice and join on small - * and large arrays. Some of these methods are specified such that they - * should work on other objects too, so we test that too. - */ - -var LARGE = 40000000; -var VERYLARGE = 4000000000; - -// Nicer for firefox 1.5. Unless you uncomment the following two lines, -// smjs will appear to hang on this file. -//var LARGE = 40000; -//var VERYLARGE = 40000; - -var fourhundredth = LARGE/400; - -function PseudoArray() { -}; - -for (var use_real_arrays = 0; use_real_arrays <= 1; use_real_arrays++) { - var poses = [0, 140, 20000, VERYLARGE]; - var the_prototype; - var new_function; - var push_function; - var concat_function; - var slice_function; - var splice_function; - var splice_function_2; - var unshift_function; - var unshift_function_2; - var shift_function; - if (use_real_arrays) { - new_function = function(length) { - return new Array(length); - }; - the_prototype = Array.prototype; - push_function = function(array, elt) { - return array.push(elt); - }; - concat_function = function(array, other) { - return array.concat(other); - }; - slice_function = function(array, start, len) { - return array.slice(start, len); - }; - splice_function = function(array, start, len) { - return array.splice(start, len); - }; - splice_function_2 = function(array, start, len, elt) { - return array.splice(start, len, elt); - }; - unshift_function = function(array, elt) { - return array.unshift(elt); - }; - unshift_function_2 = function(array, elt1, elt2) { - return array.unshift(elt1, elt2); - }; - shift_function = function(array) { - return array.shift(); - }; - } else { - // Don't run largest size on non-arrays or we'll be here for ever. - poses.pop(); - new_function = function(length) { - var obj = new PseudoArray(); - obj.length = length; - return obj; - }; - the_prototype = PseudoArray.prototype; - push_function = function(array, elt) { - array[array.length] = elt; - array.length++; - }; - concat_function = function(array, other) { - return Array.prototype.concat.call(array, other); - }; - slice_function = function(array, start, len) { - return Array.prototype.slice.call(array, start, len); - }; - splice_function = function(array, start, len) { - return Array.prototype.splice.call(array, start, len); - }; - splice_function_2 = function(array, start, len, elt) { - return Array.prototype.splice.call(array, start, len, elt); - }; - unshift_function = function(array, elt) { - return Array.prototype.unshift.call(array, elt); - }; - unshift_function_2 = function(array, elt1, elt2) { - return Array.prototype.unshift.call(array, elt1, elt2); - }; - shift_function = function(array) { - return Array.prototype.shift.call(array); - }; +// Check that splicing array of holes keeps it as array of holes +(function() { + for (var i = 0; i < 7; i++) { + var array = new Array(10); + var spliced = array.splice(1, 1, 'one', 'two'); + assertEquals(1, spliced.length); + assertFalse(0 in spliced); + + assertEquals(11, array.length); + assertFalse(0 in array); + assertTrue(1 in array); + assertTrue(2 in array); + assertFalse(3 in array); + } +})(); + + +// Check various forms of arguments omission. +(function() { + var array; + for (var i = 0; i < 7; i++) { + // SpiderMonkey and JSC return undefined in the case where no + // arguments are given instead of using the implicit undefined + // arguments. This does not follow ECMA-262, but we do the same for + // compatibility. + // TraceMonkey follows ECMA-262 though. + array = [1, 2, 3] + assertEquals(undefined, array.splice()); + assertEquals([1, 2, 3], array); + + // SpiderMonkey, TraceMonkey and JSC treat the case where no delete count is + // given differently from when an undefined delete count is given. + // This does not follow ECMA-262, but we do the same for + // compatibility. + array = [1, 2, 3] + assertEquals([1, 2, 3], array.splice(0)); + assertEquals([], array); + + array = [1, 2, 3] + assertEquals([1, 2, 3], array.splice(undefined)); + assertEquals([], array); + + array = [1, 2, 3] + assertEquals([1, 2, 3], array.splice("foobar")); + assertEquals([], array); + + array = [1, 2, 3] + assertEquals([], array.splice(undefined, undefined)); + assertEquals([1, 2, 3], array); + + array = [1, 2, 3] + assertEquals([], array.splice("foobar", undefined)); + assertEquals([1, 2, 3], array); + + array = [1, 2, 3] + assertEquals([], array.splice(undefined, "foobar")); + assertEquals([1, 2, 3], array); + + array = [1, 2, 3] + assertEquals([], array.splice("foobar", "foobar")); + assertEquals([1, 2, 3], array); } +})(); + + +// Check variants of negatives and positive indices. +(function() { + var array, spliced; + for (var i = 0; i < 7; i++) { + array = [1, 2, 3, 4, 5, 6, 7]; + spliced = array.splice(-100); + assertEquals([], array); + assertEquals([1, 2, 3, 4, 5, 6, 7], spliced); + + array = [1, 2, 3, 4, 5, 6, 7]; + spliced = array.splice(-3); + assertEquals([1, 2, 3, 4], array); + assertEquals([5, 6, 7], spliced); + + array = [1, 2, 3, 4, 5, 6, 7]; + spliced = array.splice(4); + assertEquals([1, 2, 3, 4], array); + assertEquals([5, 6, 7], spliced); + + array = [1, 2, 3, 4, 5, 6, 7]; + spliced = array.splice(6); + assertEquals([1, 2, 3, 4, 5, 6], array); + assertEquals([7], spliced); + + array = [1, 2, 3, 4, 5, 6, 7]; + spliced = array.splice(7); + assertEquals([1, 2, 3, 4, 5, 6, 7], array); + assertEquals([], spliced); + + array = [1, 2, 3, 4, 5, 6, 7]; + spliced = array.splice(8); + assertEquals([1, 2, 3, 4, 5, 6, 7], array); + assertEquals([], spliced); + + array = [1, 2, 3, 4, 5, 6, 7]; + spliced = array.splice(100); + assertEquals([1, 2, 3, 4, 5, 6, 7], array); + assertEquals([], spliced); + + array = [1, 2, 3, 4, 5, 6, 7]; + spliced = array.splice(0, -100); + assertEquals([1, 2, 3, 4, 5, 6, 7], array); + assertEquals([], spliced); + + array = [1, 2, 3, 4, 5, 6, 7]; + spliced = array.splice(0, -3); + assertEquals([1, 2, 3, 4, 5, 6, 7], array); + assertEquals([], spliced); + + array = [1, 2, 3, 4, 5, 6, 7]; + spliced = array.splice(0, 4); + assertEquals([5, 6, 7], array); + assertEquals([1, 2, 3, 4], spliced); + + array = [1, 2, 3, 4, 5, 6, 7]; + spliced = array.splice(0, 6); + assertEquals([7], array); + assertEquals([1, 2, 3, 4, 5, 6], spliced); + + array = [1, 2, 3, 4, 5, 6, 7]; + spliced = array.splice(0, 7); + assertEquals([], array); + assertEquals([1, 2, 3, 4, 5, 6, 7], spliced); - for (var pos_pos = 0; pos_pos < poses.length; pos_pos++) { - var pos = poses[pos_pos]; - if (pos > 100) { - var a = new_function(pos); - assertEquals(pos, a.length); - push_function(a, 'foo'); - assertEquals(pos + 1, a.length); - var b = ['bar']; - // Delete a huge number of holes. - var c = splice_function(a, 10, pos - 20); - assertEquals(pos - 20, c.length); - assertEquals(21, a.length); + array = [1, 2, 3, 4, 5, 6, 7]; + spliced = array.splice(0, 8); + assertEquals([], array); + assertEquals([1, 2, 3, 4, 5, 6, 7], spliced); + + array = [1, 2, 3, 4, 5, 6, 7]; + spliced = array.splice(0, 100); + assertEquals([], array); + assertEquals([1, 2, 3, 4, 5, 6, 7], spliced); + + // Some exotic cases. + obj = { toString: function() { throw 'Exception'; } }; + + // Throwing an exception in conversion: + try { + [1, 2, 3].splice(obj, 3); + throw 'Should have thrown'; + } catch (e) { + assertEquals('Exception', e); } - // Add a numeric property to the prototype of the array class. This - // allows us to test some borderline stuff relative to the standard. - the_prototype["" + (pos + 1)] = 'baz'; - - if (use_real_arrays) { - // It seems quite clear from ECMAScript spec 15.4.4.5. Just call Get on - // every integer in the range. - // IE, Safari get this right. - // FF, Opera get this wrong. - var a = ['zero', ,'two']; - if (pos == 0) { - assertEquals("zero,baz,two", a.join(",")); - } - - // Concat only applies to real arrays, unlike most of the other methods. - var a = new_function(pos); - push_function(a, "con"); - assertEquals("con", a[pos]); - assertEquals(pos + 1, a.length); - var b = new_function(0); - push_function(b, "cat"); - assertEquals("cat", b[0]); - var ab = concat_function(a, b); - assertEquals("con", ab[pos]); - assertEquals(pos + 2, ab.length); - assertEquals("cat", ab[pos + 1]); - var ba = concat_function(b, a); - assertEquals("con", ba[pos + 1]); - assertEquals(pos + 2, ba.length); - assertEquals("cat", ba[0]); - - // Join with '' as separator. - var join = a.join(''); - assertEquals("con", join); - join = b.join(''); - assertEquals("cat", join); - join = ab.join(''); - assertEquals("concat", join); - join = ba.join(''); - assertEquals("catcon", join); - - var sparse = []; - sparse[pos + 1000] = 'is '; - sparse[pos + 271828] = 'time '; - sparse[pos + 31415] = 'the '; - sparse[pos + 012260199] = 'all '; - sparse[-1] = 'foo'; - sparse[pos + 22591927] = 'good '; - sparse[pos + 1618033] = 'for '; - sparse[pos + 91] = ': Now '; - sparse[pos + 86720199] = 'men.'; - sparse.hest = 'fisk'; - - assertEquals("baz: Now is the time for all good men.", sparse.join('')); + try { + [1, 2, 3].splice(0, obj, 3); + throw 'Should have thrown'; + } catch (e) { + assertEquals('Exception', e); + } + + array = [1, 2, 3]; + array.splice(0, 3, obj); + assertEquals(1, array.length); + + // Custom conversion: + array = [1, 2, 3]; + spliced = array.splice({valueOf: function() { return 1; }}, + {toString: function() { return 2; }}, + 'one', 'two'); + assertEquals([2, 3], spliced); + assertEquals([1, 'one', 'two'], array); + } +})(); + + +// Nasty: modify the array in ToInteger. +(function() { + var array = []; + var spliced; + + for (var i = 0; i < 13; i++) { + bad_start = { valueOf: function() { array.push(2*i); return -1; } }; + bad_count = { valueOf: function() { array.push(2*i + 1); return 1; } }; + spliced = array.splice(bad_start, bad_count); + // According to the spec (15.4.4.12), length is calculated before + // performing ToInteger on arguments. However, v8 ignores elements + // we add while converting, so we need corrective pushes. + array.push(2*i); array.push(2*i + 1); + if (i == 0) { + assertEquals([], spliced); // Length was 0, nothing to get. + assertEquals([0, 1], array); + } else { + // When we start splice, array is [0 .. 2*i - 1], so we get + // as a result [2*i], this element is removed from the array, + // but [2 * i, 2 * i + 1] are added. + assertEquals([2 * i - 1], spliced); + assertEquals(2 * i, array[i]); + assertEquals(2 * i + 1, array[i + 1]); } + } +})(); + - a = new_function(pos); - push_function(a, 'zero'); - push_function(a, void 0); - push_function(a, 'two'); - - // Splice works differently from join. - // IE, Safari get this wrong. - // FF, Opera get this right. - // 15.4.4.12 line 24 says the object itself has to have the property... - var zero = splice_function(a, pos, 1); - assertEquals("undefined", typeof(a[pos])); - assertEquals("two", a[pos+1], "pos1:" + pos); - assertEquals(pos + 2, a.length, "a length"); - assertEquals(1, zero.length, "zero length"); - assertEquals("zero", zero[0]); - - // 15.4.4.12 line 41 says the object itself has to have the property... - a = new_function(pos); - push_function(a, 'zero'); - push_function(a, void 0); - push_function(a, 'two'); - var nothing = splice_function_2(a, pos, 0, 'minus1'); - assertEquals("minus1", a[pos]); - assertEquals("zero", a[pos+1]); - assertEquals("undefined", typeof(a[pos+2]), "toot!"); - assertEquals("two", a[pos+3], "pos3"); - assertEquals(pos + 4, a.length); - assertEquals(1, zero.length); - assertEquals("zero", zero[0]); - - // 15.4.4.12 line 10 says the object itself has to have the property... - a = new_function(pos); - push_function(a, 'zero'); - push_function(a, void 0); - push_function(a, 'two'); - var one = splice_function(a, pos + 1, 1); - assertEquals("", one.join(",")); - assertEquals(pos + 2, a.length); - assertEquals("zero", a[pos]); - assertEquals("two", a[pos+1]); - - // Set things back to the way they were. - the_prototype[pos + 1] = undefined; - - // Unshift. - var a = new_function(pos); - push_function(a, "foo"); - assertEquals("foo", a[pos]); - assertEquals(pos + 1, a.length); - unshift_function(a, "bar"); - assertEquals("foo", a[pos+1]); - assertEquals(pos + 2, a.length); - assertEquals("bar", a[0]); - unshift_function_2(a, "baz", "boo"); - assertEquals("foo", a[pos+3]); - assertEquals(pos + 4, a.length); - assertEquals("baz", a[0]); - assertEquals("boo", a[1]); - assertEquals("bar", a[2]); - - // Shift. - var baz = shift_function(a); - assertEquals("baz", baz); - assertEquals("boo", a[0]); - assertEquals(pos + 3, a.length); - assertEquals("foo", a[pos + 2]); - - // Slice. - var bar = slice_function(a, 1, 0); // don't throw an exception please. - bar = slice_function(a, 1, 2); - assertEquals("bar", bar[0]); - assertEquals(1, bar.length); - assertEquals("bar", a[1]); +// Now check the case with array of holes and some elements on prototype. +(function() { + var len = 9; + + var at3 = "@3"; + var at7 = "@7"; + + for (var i = 0; i < 7; i++) { + var array = new Array(len); + Array.prototype[3] = at3; + Array.prototype[7] = at7; + + var spliced = array.splice(2, 2, 'one', undefined, 'two'); + + // Second hole (at index 3) of array turns into + // value of Array.prototype[3] while copying. + assertEquals([, at3], spliced); + assertEquals([, , 'one', undefined, 'two', , , at7, at7, ,], array); + + // ... but array[7] is actually a hole: + assertTrue(delete Array.prototype[7]); + assertEquals(undefined, array[7]); + + // and now check hasOwnProperty + assertFalse(array.hasOwnProperty(0)); + assertFalse(array.hasOwnProperty(1)); + assertTrue(array.hasOwnProperty(2)); + assertTrue(array.hasOwnProperty(3)); + assertTrue(array.hasOwnProperty(4)); + assertFalse(array.hasOwnProperty(5)); + assertFalse(array.hasOwnProperty(6)); + assertFalse(array.hasOwnProperty(7)); + assertTrue(array.hasOwnProperty(8)); + assertFalse(array.hasOwnProperty(9)); + + // and now check couple of indices above length. + assertFalse(array.hasOwnProperty(10)); + assertFalse(array.hasOwnProperty(15)); + assertFalse(array.hasOwnProperty(31)); + assertFalse(array.hasOwnProperty(63)); + assertFalse(array.hasOwnProperty(2 << 32 - 1)); + } +})(); + + +// Check the behaviour when approaching maximal values for length. +(function() { + for (var i = 0; i < 7; i++) { + try { + new Array((1 << 32) - 3).splice(-1, 0, 1, 2, 3, 4, 5); + throw 'Should have thrown RangeError'; + } catch (e) { + assertTrue(e instanceof RangeError); + } + // Check smi boundary + var bigNum = (1 << 30) - 3; + var array = new Array(bigNum); + array.splice(-1, 0, 1, 2, 3, 4, 5, 6, 7); + assertEquals(bigNum + 7, array.length); } -} - -// Lets see if performance is reasonable. - -var a = new Array(LARGE + 10); -for (var i = 0; i < a.length; i += 1000) { - a[i] = i; -} - -// Take something near the end of the array. -for (var i = 0; i < 100; i++) { - var top = a.splice(LARGE, 5); - assertEquals(5, top.length); - assertEquals(LARGE, top[0]); - assertEquals("undefined", typeof(top[1])); - assertEquals(LARGE + 5, a.length); - a.splice(LARGE, 0, LARGE); - a.length = LARGE + 10; -} - -var a = new Array(LARGE + 10); -for (var i = 0; i < a.length; i += fourhundredth) { - a[i] = i; -} - -// Take something near the middle of the array. -for (var i = 0; i < 10; i++) { - var top = a.splice(LARGE >> 1, 5); - assertEquals(5, top.length); - assertEquals(LARGE >> 1, top[0]); - assertEquals("undefined", typeof(top[1])); - assertEquals(LARGE + 5, a.length); - a.splice(LARGE >> 1, 0, LARGE >> 1, void 0, void 0, void 0, void 0); -} - - -// Test http://b/issue?id=1202711 -arr = [0]; -arr.length = 2; -Array.prototype[1] = 1; -assertEquals(1, arr.pop()); -assertEquals(0, arr.pop()); -Array.prototype[1] = undefined; - -// Test http://code.google.com/p/chromium/issues/detail?id=21860 -Array.prototype.push.apply([], [1].splice(0, -(-1 % 5))); +})(); diff --git a/test/mjsunit/array-unshift.js b/test/mjsunit/array-unshift.js new file mode 100644 index 00000000..06a78a7d --- /dev/null +++ b/test/mjsunit/array-unshift.js @@ -0,0 +1,132 @@ +// Copyright 2010 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. + +// Check that unshifting array of holes keeps the original array +// as array of holes +(function() { + var array = new Array(10); + assertEquals(13, array.unshift('1st', '2ns', '3rd')); + assertTrue(0 in array); + assertTrue(1 in array); + assertTrue(2 in array); + assertFalse(3 in array); +})(); + + +// Check that unshif with no args has a side-effect of +// feeling the holes with elements from the prototype +// (if present, of course) +(function() { + var len = 3; + var array = new Array(len); + + var at0 = '@0'; + var at2 = '@2'; + + Array.prototype[0] = at0; + Array.prototype[2] = at2; + + // array owns nothing... + assertFalse(array.hasOwnProperty(0)); + assertFalse(array.hasOwnProperty(1)); + assertFalse(array.hasOwnProperty(2)); + + // ... but sees values from Array.prototype + assertEquals(array[0], at0); + assertEquals(array[1], undefined); + assertEquals(array[2], at2); + + assertEquals(len, array.unshift()); + + assertTrue(delete Array.prototype[0]); + assertTrue(delete Array.prototype[2]); + + // unshift makes array own 0 and 2... + assertTrue(array.hasOwnProperty(0)); + assertFalse(array.hasOwnProperty(1)); + assertTrue(array.hasOwnProperty(2)); + + // ... so they are not affected be delete. + assertEquals(array[0], at0); + assertEquals(array[1], undefined); + assertEquals(array[2], at2); +})(); + + +// Now check the case with array of holes and some elements on prototype. +(function() { + var len = 9; + var array = new Array(len); + Array.prototype[3] = "@3"; + Array.prototype[7] = "@7"; + + assertEquals(len, array.length); + for (var i = 0; i < array.length; i++) { + assertEquals(array[i], Array.prototype[i]); + } + + assertEquals(len + 1, array.unshift('head')); + + assertEquals(len + 1, array.length); + // Note that unshift copies values from prototype into the array. + assertEquals(array[4], Array.prototype[3]); + assertTrue(array.hasOwnProperty(4)); + + assertEquals(array[8], Array.prototype[7]); + assertTrue(array.hasOwnProperty(8)); + + // ... but keeps the rest as holes: + Array.prototype[5] = "@5"; + assertEquals(array[5], Array.prototype[5]); + assertFalse(array.hasOwnProperty(5)); + + assertEquals(array[3], Array.prototype[3]); + assertFalse(array.hasOwnProperty(3)); + + assertEquals(array[7], Array.prototype[7]); + assertFalse(array.hasOwnProperty(7)); + + assertTrue(delete Array.prototype[3]); + assertTrue(delete Array.prototype[5]); + assertTrue(delete Array.prototype[7]); +})(); + +// Check the behaviour when approaching maximal values for length. +(function() { + for (var i = 0; i < 7; i++) { + try { + new Array((1 << 32) - 3).unshift(1, 2, 3, 4, 5); + throw 'Should have thrown RangeError'; + } catch (e) { + assertTrue(e instanceof RangeError); + } + + // Check smi boundary + var bigNum = (1 << 30) - 3; + assertEquals(bigNum + 7, new Array(bigNum).unshift(1, 2, 3, 4, 5, 6, 7)); + } +})(); diff --git a/test/mjsunit/bugs/618.js b/test/mjsunit/bugs/618.js new file mode 100644 index 00000000..afa9929a --- /dev/null +++ b/test/mjsunit/bugs/618.js @@ -0,0 +1,86 @@ +// Copyright 2010 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. + +// Simple class using inline constructor. +function C1() { + this.x = 23; +}; +var c1 = new C1(); +assertEquals(23, c1.x); +assertEquals("undefined", typeof c1.y); + +// Add setter somewhere on the prototype chain after having constructed the +// first instance. +C1.prototype = { set x(value) { this.y = 23; } }; +var c1 = new C1(); +assertEquals("undefined", typeof c1.x); +assertEquals(23, c1.y); + +// Simple class using inline constructor. +function C2() { + this.x = 23; +}; +var c2 = new C2(); +assertEquals(23, c2.x); +assertEquals("undefined", typeof c2.y); + +// Add setter somewhere on the prototype chain after having constructed the +// first instance. +C2.prototype.__proto__ = { set x(value) { this.y = 23; } }; +var c2 = new C2(); +assertEquals("undefined", typeof c2.x); +assertEquals(23, c2.y); + +// Simple class using inline constructor. +function C3() { + this.x = 23; +}; +var c3 = new C3(); +assertEquals(23, c3.x); +assertEquals("undefined", typeof c3.y); + +// Add setter somewhere on the prototype chain after having constructed the +// first instance. +C3.prototype.__defineSetter__('x', function(value) { this.y = 23; }); +var c3 = new C3(); +assertEquals("undefined", typeof c3.x); +assertEquals(23, c3.y); + +// Simple class using inline constructor. +function C4() { + this.x = 23; +}; +var c4 = new C4(); +assertEquals(23, c4.x); +assertEquals("undefined", typeof c4.y); + +// Add setter somewhere on the prototype chain after having constructed the +// first instance. +C4.prototype.__proto__.__defineSetter__('x', function(value) { this.y = 23; }); +var c4 = new C4(); +assertEquals("undefined", typeof c4.x); +assertEquals(23, c4.y); diff --git a/test/mjsunit/bugs/bug-619.js b/test/mjsunit/bugs/bug-619.js new file mode 100644 index 00000000..ef8ba80e --- /dev/null +++ b/test/mjsunit/bugs/bug-619.js @@ -0,0 +1,62 @@ +// Copyright 2010 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. + +// When this bug is corrected move to object-define-property and add +// additional tests for configurable in the same manner as existing tests +// there. + +var obj = {}; +obj[1] = 42; +assertEquals(42, obj[1]); +Object.defineProperty(obj, '1', {value:10, writable:false}); +assertEquals(10, obj[1]); + +// We should not be able to override obj[1]. +obj[1] = 5; +assertEquals(10, obj[1]); + +// Try on a range of numbers. +for(var i = 0; i < 1024; i++) { + obj[i] = 42; +} + +for(var i = 0; i < 1024; i++) { + Object.defineProperty(obj, i, {value: i, writable:false}); +} + +for(var i = 0; i < 1024; i++) { + assertEquals(i, obj[i]); +} + +for(var i = 0; i < 1024; i++) { + obj[1] = 5; +} + +for(var i = 0; i < 1024; i++) { + assertEquals(i, obj[i]); +} + diff --git a/test/mjsunit/codegen-coverage.js b/test/mjsunit/codegen-coverage.js index d5e7769d..42c371ba 100644 --- a/test/mjsunit/codegen-coverage.js +++ b/test/mjsunit/codegen-coverage.js @@ -25,64 +25,110 @@ // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -// Test the paths in the code generator where values in specific -// registers get moved around so that the shift operation can use -// register ECX on ia32 for the shift amount. Other codegen coverage -// tests should go here too. - - +// Flags: --nofull-compiler --nofast-compiler +// Test paths in the code generator where values in specific registers +// get moved around. function identity(x) { return x; } function cover_codegen_paths() { var x = 1; - var a; // Register EAX - var b; // Register EBX - var c; // Register ECX - var d; // Register EDX - // Register ESI is already used. - var di; // Register EDI + + // This test depends on the fixed order of register allocation. We try to + // get values in specific registers (ia32, x64): + var a; // Register eax, rax. + var b; // Register ebx, rbx. + var c; // Register ecx, rcx. + var d; // Register edx, rdx. + var di; // Register edi, rdi. while (x == 1) { + // The call will spill registers and leave x in {eax,rax}. x = identity(1); + // The add will spill x and reuse {eax,rax} for the result. a = x + 1; + // A fresh register {ebx,rbx} will be allocated for x, then reused for + // the result. + b = x + 1; + // Et cetera. c = x + 1; d = x + 1; - b = x + 1; di = x + 1; // Locals are in the corresponding registers here. - assertEquals(c << a, 8); + assertEquals(8, c << a); x = identity(1); a = x + 1; + b = x + 1; c = x + 1; d = x + 1; - b = x + 1; di = x + 1; - // Locals are in the corresponding registers here. - assertEquals(a << c, 8); + assertEquals(8, a << c); x = identity(1); a = x + 1; + b = x + 1; c = x + 1; d = x + 1; - b = x + 1; di = x + 1; - // Locals are in the corresponding registers here. c = 0; // Free register ecx. - assertEquals(a << d, 8); + assertEquals(8, a << d); x = identity(1); a = x + 1; + b = x + 1; c = x + 1; d = x + 1; - b = x + 1; di = x + 1; - // Locals are in the corresponding registers here. b = 0; // Free register ebx. - assertEquals(a << d, 8); + assertEquals(8, a << d); + + // Test the non-commutative subtraction operation with a smi on the + // left, all available registers on the right, and a non-smi result. + x = identity(-1073741824); // Least (31-bit) smi. + a = x + 1; // Still a smi, the greatest smi negated. + b = x + 1; + c = x + 1; + d = x + 1; + di = x + 1; + // Subtraction should overflow the 31-bit smi range. The result + // (1073741824) is outside the 31-bit smi range so it doesn't hit the + // "unsafe smi" code that spills a register. + assertEquals(1073741824, 1 - a); + + x = identity(-1073741824); + a = x + 1; + b = x + 1; + c = x + 1; + d = x + 1; + di = x + 1; + assertEquals(1073741824, 1 - b); + + x = identity(-1073741824); + a = x + 1; + b = x + 1; + c = x + 1; + d = x + 1; + di = x + 1; + assertEquals(1073741824, 1 - c); + + x = identity(-1073741824); + a = x + 1; + b = x + 1; + c = x + 1; + d = x + 1; + di = x + 1; + assertEquals(1073741824, 1 - d); + + x = identity(-1073741824); + a = x + 1; + b = x + 1; + c = x + 1; + d = x + 1; + di = x + 1; + assertEquals(1073741824, 1 - di); x = 3; } diff --git a/test/mjsunit/compiler/assignment.js b/test/mjsunit/compiler/assignment.js new file mode 100644 index 00000000..ee2d3237 --- /dev/null +++ b/test/mjsunit/compiler/assignment.js @@ -0,0 +1,264 @@ +// Copyright 2010 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. + +// Tests for compound assignments at the top level + +z = 2; +z += 4; + +assertEquals(z, 6); + +a = new Array(10); + +a[2] += 7; +a[2] = 15; +a[2] += 2; + +assertEquals(17, a[2]); + +b = new Object(); +b.foo = 5; +b.foo += 12; + +assertEquals(17, b.foo); + +// Test compound assignments in an anonymous function with local variables. +(function () { + var z = 2; + z += 4; + + assertEquals(z, 6); + + var a = new Array(10); + + a[2] += 7; + a[2] = 15; + a[2] += 2; + + assertEquals(17, a[2]); + + var b = new Object(); + b.foo = 5; + b.foo += 12; + + assertEquals(17, b.foo); +})(); + +// Test compound assignments in an anonymous function with global variables. +(function () { + z = 2; + z += 4; + + assertEquals(z, 6); + + a = new Array(10); + + a[2] += 7; + a[2] = 15; + a[2] += 2; + + assertEquals(17, a[2]); + + b = new Object(); + b.foo = 5; + b.foo += 12; + + assertEquals(17, b.foo); +})(); + +// Test compound assignments in a named function with local variables. +function foo() { + var z = 3; + z += 4; + + assertEquals(z, 7); + + var a = new Array(10); + + a[2] += 7; + a[2] = 15; + a[2] += 2; + + assertEquals(17, a[2]); + + var b = new Object(); + b.foo = 5; + b.foo += 12; + + assertEquals(17, b.foo); +} + +foo(); + +// Test compound assignments in a named function with global variables. +function bar() { + z = 2; + z += 5; + + assertEquals(z, 7); + + a = new Array(10); + + a[2] += 7; + a[2] = 15; + a[2] += 2; + + assertEquals(17, a[2]); + + b = new Object(); + b.foo = 5; + b.foo += 12; + + assertEquals(17, b.foo); +} + +bar(); + +// Entire series of tests repeated, in loops. +// ------------------------------------------- +// Tests for compound assignments in a loop at the top level + +for (i = 0; i < 5; ++i) { + z = 2; + z += 4; + + assertEquals(z, 6); + + a = new Array(10); + + a[2] += 7; + a[2] = 15; + a[2] += 2; + + assertEquals(17, a[2]); + + b = new Object(); + b.foo = 5; + b.foo += 12; + + assertEquals(17, b.foo); +} + +// Test compound assignments in an anonymous function with local variables. +(function () { + for (var i = 0; i < 5; ++i) { + var z = 2; + z += 4; + + assertEquals(z, 6); + + var a = new Array(10); + + a[2] += 7; + a[2] = 15; + a[2] += 2; + + assertEquals(17, a[2]); + + var b = new Object(); + b.foo = 5; + b.foo += 12; + + assertEquals(17, b.foo); + } +})(); + +// Test compound assignments in an anonymous function with global variables. +(function () { + for (i = 0; i < 5; ++i) { + z = 2; + z += 4; + + assertEquals(z, 6); + + a = new Array(10); + + a[2] += 7; + a[2] = 15; + a[2] += 2; + + assertEquals(17, a[2]); + + b = new Object(); + b.foo = 5; + b.foo += 12; + + assertEquals(17, b.foo); + } +})(); + +// Test compound assignments in a named function with local variables. +function foo_loop() { + for (i = 0; i < 5; ++i) { + var z = 3; + z += 4; + + assertEquals(z, 7); + + var a = new Array(10); + + a[2] += 7; + a[2] = 15; + a[2] += 2; + + assertEquals(17, a[2]); + + var b = new Object(); + b.foo = 5; + b.foo += 12; + + assertEquals(17, b.foo); + } +} + +foo_loop(); + +// Test compound assignments in a named function with global variables. +function bar_loop() { + for (i = 0; i < 5; ++i) { + z = 2; + z += 5; + + assertEquals(z, 7); + + a = new Array(10); + + a[2] += 7; + a[2] = 15; + a[2] += 2; + + assertEquals(17, a[2]); + + b = new Object(); + b.foo = 5; + b.foo += 12; + + assertEquals(17, b.foo); + } +} + +bar_loop(); diff --git a/test/mjsunit/compiler/simple-bailouts.js b/test/mjsunit/compiler/simple-bailouts.js new file mode 100644 index 00000000..af80b7f0 --- /dev/null +++ b/test/mjsunit/compiler/simple-bailouts.js @@ -0,0 +1,127 @@ +// Copyright 2010 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. + +// Flags: --fast-compiler + +function Test() { + this.result = 0; + this.x = 0; + this.y = 0; + this.z = 0; +} +var a = 1; +var b = 2; +var c = 4; +var d = 8; + +// Test operations expected to stay on the fast path. Enumerate all binary +// trees with <= 4 leaves. +Test.prototype.test0 = function () { + this.result = a | b; +}; + +Test.prototype.test1 = function() { + this.result = (a | b) | c; +}; + +Test.prototype.test2 = function() { + this.result = a | (b | c); +}; + +Test.prototype.test3 = function() { + this.result = ((a | b) | c) | d; +}; + +Test.prototype.test4 = function() { + this.result = (a | (b | c)) | d; +}; + +Test.prototype.test5 = function() { + this.result = (a | b) | (c | d); +}; + +Test.prototype.test6 = function() { + this.result = a | ((b | c) | d); +}; + +Test.prototype.test7 = function() { + this.result = a | (b | (c | d)); +}; + +// These tests should fail if we bailed out to the beginning of the full +// code. +Test.prototype.test8 = function () { + // If this.x = 1 and a = 1.1: + this.y = this.x | b; // Should be (1 | 2) == 3. + this.x = c; // Should be 4. + this.z = this.x | a; // Should be (4 | 1.1) == 5. +}; + +Test.prototype.test9 = function() { + // If this.x = 2 and a = 1.1: + this.z = // (14 | 1.1) == 15 + (this.x = // (6 | 8) == 14 + (this.y = // (2 | 4) == 6 + this.x // 2 + | c) // 4 + | d) // 8 + | a; // 1.1 +} + +var t = new Test(); + +t.test0(); +assertEquals(3, t.result); + +t.test1(); +assertEquals(7, t.result); +t.test2(); +assertEquals(7, t.result); + +t.test3(); +assertEquals(15, t.result); +t.test4(); +assertEquals(15, t.result); +t.test5(); +assertEquals(15, t.result); +t.test6(); +assertEquals(15, t.result); +t.test7(); +assertEquals(15, t.result); + +a = 1.1; +t.x = 1; +t.test8(); +assertEquals(4, t.x); +assertEquals(3, t.y); +assertEquals(5, t.z); + +t.x = 2; +t.test9(); +assertEquals(14, t.x); +assertEquals(6, t.y); +assertEquals(15, t.z); diff --git a/test/mjsunit/compiler/simple-binary-op.js b/test/mjsunit/compiler/simple-binary-op.js new file mode 100644 index 00000000..15e1a559 --- /dev/null +++ b/test/mjsunit/compiler/simple-binary-op.js @@ -0,0 +1,40 @@ +// Copyright 2010 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. + +// Flags: --fast-compiler + +var a = 1; +var b = 2; +var c = 4; + +function f() { this.x = this.x | a | b | c | a | c; } + +var o = {x:0, g:f} + +o.g(); + +assertEquals(7, o.x); diff --git a/test/mjsunit/compiler/simple-global-access.js b/test/mjsunit/compiler/simple-global-access.js new file mode 100644 index 00000000..35746ba8 --- /dev/null +++ b/test/mjsunit/compiler/simple-global-access.js @@ -0,0 +1,53 @@ +// Copyright 2010 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. + +// Flags: --fast-compiler + +// Test global variable loads with the fast compiler. +var g1 = 42; +var g2 = 43; +var g3 = 44; +this.__defineGetter__("g4", function () { return 45; }); + +function f1() { this.x = this.y = this.z = g1; } +function f2() { this.x = g1; this.y = g2; this.z = g3; } +function f3() { this.x = g4; } + +var o = { x:0, y:0, z:0, test1:f1, test2:f2, test3:f3 } + +o.test1(); +assertEquals(42, o.x); +assertEquals(42, o.y); +assertEquals(42, o.z); + +o.test2(); +assertEquals(42, o.x); +assertEquals(43, o.y); +assertEquals(44, o.z); + +o.test3(); +assertEquals(45, o.x); diff --git a/test/mjsunit/compiler/this-property-refs.js b/test/mjsunit/compiler/this-property-refs.js new file mode 100644 index 00000000..5e8ea596 --- /dev/null +++ b/test/mjsunit/compiler/this-property-refs.js @@ -0,0 +1,64 @@ +// Copyright 2010 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. + +// Flags: --fast-compiler + +// Test references to properties of this. +function Test() { + this.a = 0; + this.b = 1; + this.c = 2; + this.d = 3; +} + +Test.prototype.test0 = function () { + this.a = this.b; +}; + +Test.prototype.test1 = function() { + this.a = this.b = this.c; +}; + +Test.prototype.test2 = function() { + this.c = this.d; + this.b = this.c; + this.a = this.b; +}; + +var t = new Test(); + +t.test0(); +assertEquals(1, t.a); + +t.test1(); +assertEquals(2, t.a); +assertEquals(2, t.b); + +t.test2(); +assertEquals(3, t.a); +assertEquals(3, t.b); +assertEquals(3, t.c); diff --git a/test/mjsunit/debug-compile-event.js b/test/mjsunit/debug-compile-event.js index 071183bf..e7ecf47e 100644 --- a/test/mjsunit/debug-compile-event.js +++ b/test/mjsunit/debug-compile-event.js @@ -90,6 +90,11 @@ function listener(event, exec_state, event_data, data) { var json = event_data.toJSONProtocol(); var msg = eval('(' + json + ')'); assertTrue('context' in msg.body.script); + + // Check that we pick script name from //@ sourceURL, iff present + assertEquals(current_source.indexOf('sourceURL') >= 0 ? + 'myscript.js' : undefined, + event_data.script().name()); } } catch (e) { exception = e @@ -109,6 +114,7 @@ compileSource('eval("eval(\'(function(){return a;})\')")'); source_count += 2; // Using eval causes additional compilation event. compileSource('JSON.parse(\'{"a":1,"b":2}\')'); source_count++; // Using JSON.parse causes additional compilation event. +compileSource('x=1; //@ sourceURL=myscript.js'); // Make sure that the debug event listener was invoked. assertFalse(exception, "exception in listener") diff --git a/test/mjsunit/debug-evaluate.js b/test/mjsunit/debug-evaluate.js index c4779072..182e2ac9 100644 --- a/test/mjsunit/debug-evaluate.js +++ b/test/mjsunit/debug-evaluate.js @@ -87,6 +87,37 @@ function listener(event, exec_state, event_data, data) { testRequest(dcp, '{"expression":"a","global":true}', true, 1); testRequest(dcp, '{"expression":"this.a","global":true}', true, 1); + // Test that the whole string text is returned if maxStringLength + // parameter is passed. + testRequest( + dcp, + '{"expression":"this.longString","global":true,maxStringLength:-1}', + true, + longString); + testRequest( + dcp, + '{"expression":"this.longString","global":true,maxStringLength:' + + longString.length + '}', + true, + longString); + var truncatedStringSuffix = '... (length: ' + longString.length + ')'; + testRequest( + dcp, + '{"expression":"this.longString","global":true,maxStringLength:0}', + true, + truncatedStringSuffix); + testRequest( + dcp, + '{"expression":"this.longString","global":true,maxStringLength:1}', + true, + longString.charAt(0) + truncatedStringSuffix); + // Test that by default string is truncated to first 80 chars. + testRequest( + dcp, + '{"expression":"this.longString","global":true}', + true, + longString.substring(0, 80) + truncatedStringSuffix); + // Indicate that all was processed. listenerComplete = true; } @@ -109,6 +140,12 @@ function g() { a = 1; +// String which is longer than 80 chars. +var longString = "1234567890_"; +for (var i = 0; i < 4; i++) { + longString += longString; +} + // Set a break point at return in f and invoke g to hit the breakpoint. Debug.setBreakPoint(f, 2, 0); g(); diff --git a/test/mjsunit/div-mod.js b/test/mjsunit/div-mod.js index b3c77e1d..1d352b55 100644 --- a/test/mjsunit/div-mod.js +++ b/test/mjsunit/div-mod.js @@ -154,4 +154,18 @@ function compute_mod(dividend, divisor) { doTest(-a,-b); } } -})() +})(); + + +(function () { + // Edge cases + var zero = 0; + var minsmi32 = -0x40000000; + var minsmi64 = -0x80000000; + var somenum = 3532; + assertEquals(-0, zero / -1, "0 / -1"); + assertEquals(1, minsmi32 / -0x40000000, "minsmi/minsmi-32"); + assertEquals(1, minsmi64 / -0x80000000, "minsmi/minsmi-64"); + assertEquals(somenum, somenum % -0x40000000, "%minsmi-32"); + assertEquals(somenum, somenum % -0x80000000, "%minsmi-64"); +})(); diff --git a/test/mjsunit/fuzz-natives.js b/test/mjsunit/fuzz-natives.js index d906eb8a..e2f601eb 100644 --- a/test/mjsunit/fuzz-natives.js +++ b/test/mjsunit/fuzz-natives.js @@ -27,6 +27,9 @@ // Flags: --allow-natives-syntax +var RUN_WITH_ALL_ARGUMENT_ENTRIES = false; +var kOnManyArgumentsRemove = 5; + function makeArguments() { var result = [ ]; result.push(17); @@ -74,13 +77,23 @@ function testArgumentTypes(name, argc) { var func = makeFunction(name, argc); while (hasMore) { var argPool = makeArguments(); + // When we have 5 or more arguments we lower the amount of tests cases + // by randomly removing kOnManyArgumentsRemove entries + var numArguments = RUN_WITH_ALL_ARGUMENT_ENTRIES ? + kArgObjects : kArgObjects-kOnManyArgumentsRemove; + if (argc >= 5 && !RUN_WITH_ALL_ARGUMENT_ENTRIES) { + for (var i = 0; i < kOnManyArgumentsRemove; i++) { + var rand = Math.floor(Math.random() * (kArgObjects - i)); + argPool.splice(rand,1); + } + } var current = type; var hasMore = false; var argList = [ ]; for (var i = 0; i < argc; i++) { - var index = current % kArgObjects; - current = (current / kArgObjects) << 0; - if (index != (kArgObjects - 1)) + var index = current % numArguments; + current = (current / numArguments) << 0; + if (index != (numArguments - 1)) hasMore = true; argList.push(argPool[index]); } diff --git a/test/mjsunit/object-define-properties.js b/test/mjsunit/object-define-properties.js new file mode 100644 index 00000000..6b3725b3 --- /dev/null +++ b/test/mjsunit/object-define-properties.js @@ -0,0 +1,56 @@ +// Copyright 2010 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. + +// Tests the Object.defineProperties method - ES 15.2.3.7 +// Note that the internal DefineOwnProperty method is tested through +// object-define-property.js, this file only contains tests specific for +// Object.defineProperties. Also note that object-create.js contains +// a range of indirect tests on this method since Object.create uses +// Object.defineProperties as a step in setting up the object. + +// Try defining with null as descriptor: +try { + Object.defineProperties({}, null); +} catch(e) { + assertTrue(/null to object/.test(e)); +} + +// Try defining with null as object +try { + Object.defineProperties(null, {}); +} catch(e) { + assertTrue(/called on non-object/.test(e)); +} + + +var desc = {foo: {value: 10}, bar: {get: function() {return 42; }}}; +var obj = {}; +// Check that we actually get the object back as returnvalue +var x = Object.defineProperties(obj, desc); + +assertEquals(x.foo, 10); +assertEquals(x.bar, 42); diff --git a/test/mjsunit/object-define-property.js b/test/mjsunit/object-define-property.js new file mode 100644 index 00000000..43b1c7f0 --- /dev/null +++ b/test/mjsunit/object-define-property.js @@ -0,0 +1,499 @@ +// Copyright 2010 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. + +// Tests the object.defineProperty method - ES 15.2.3.6 + +// Flags: --allow-natives-syntax + +// Check that an exception is thrown when null is passed as object. +try { + Object.defineProperty(null, null, null); + assertTrue(false); +} catch (e) { + assertTrue(/called on non-object/.test(e)); +} + +// Check that an exception is thrown when undefined is passed as object. +try { + Object.defineProperty(undefined, undefined, undefined); + assertTrue(false); +} catch (e) { + assertTrue(/called on non-object/.test(e)); +} + +// Check that an exception is thrown when non-object is passed as object. +try { + Object.defineProperty(0, "foo", undefined); + assertTrue(false); +} catch (e) { + assertTrue(/called on non-object/.test(e)); +} + +// Object +var obj1 = {}; + +// Values +var val1 = 0; +var val2 = 0; +var val3 = 0; + +// Descriptors +var emptyDesc = {}; + +var accessorConfigurable = { + set: function() { val1++; }, + get: function() { return val1; }, + configurable: true +}; + +var accessorNoConfigurable = { + set: function() { val2++; }, + get: function() { return val2; }, + configurable: false +}; + +var accessorOnlySet = { + set: function() { val3++; }, + configurable: true +}; + +var accessorOnlyGet = { + get: function() { return val3; }, + configurable: true +}; + +var accessorDefault = {set: function(){} }; + +var dataConfigurable = { value: 1000, configurable: true }; + +var dataNoConfigurable = { value: 2000, configurable: false }; + +var dataWritable = { value: 3000, writable: true}; + + +// Check that we can't add property with undefined attributes. +try { + Object.defineProperty(obj1, "foo", undefined); + assertTrue(false); +} catch (e) { + assertTrue(/must be an object/.test(e)); +} + +// Make sure that we can add a property with an empty descriptor and +// that it has the default descriptor values. +Object.defineProperty(obj1, "foo", emptyDesc); + +// foo should be undefined as it has no get, set or value +assertEquals(undefined, obj1.foo); + +// We should, however, be able to retrieve the propertydescriptor which should +// have all default values (according to 8.6.1). +var desc = Object.getOwnPropertyDescriptor(obj1, "foo"); +assertFalse(desc.configurable); +assertFalse(desc.enumerable); +assertFalse(desc.writable); +assertEquals(desc.get, undefined); +assertEquals(desc.set, undefined); +assertEquals(desc.value, undefined); + +// Make sure that getOwnPropertyDescriptor does not return a descriptor +// with default values if called with non existing property (otherwise +// the test above is invalid). +desc = Object.getOwnPropertyDescriptor(obj1, "bar"); +assertEquals(desc, undefined); + +// Make sure that foo can't be reset (as configurable is false). +try { + Object.defineProperty(obj1, "foo", accessorConfigurable); +} catch (e) { + assertTrue(/Cannot redefine property/.test(e)); +} + + +// Accessor properties + +Object.defineProperty(obj1, "bar", accessorConfigurable); +desc = Object.getOwnPropertyDescriptor(obj1, "bar"); +assertTrue(desc.configurable); +assertFalse(desc.enumerable); +assertEquals(desc.writable, undefined); +assertEquals(desc.get, accessorConfigurable.get); +assertEquals(desc.set, accessorConfigurable.set); +assertEquals(desc.value, undefined); +assertEquals(1, obj1.bar = 1); +assertEquals(1, val1); +assertEquals(1, obj1.bar = 1); +assertEquals(2, val1); +assertEquals(2, obj1.bar); + +// Redefine bar with non configurable test +Object.defineProperty(obj1, "bar", accessorNoConfigurable); +desc = Object.getOwnPropertyDescriptor(obj1, "bar"); +assertFalse(desc.configurable); +assertFalse(desc.enumerable); +assertEquals(desc.writable, undefined); +assertEquals(desc.get, accessorNoConfigurable.get); +assertEquals(desc.set, accessorNoConfigurable.set); +assertEquals(desc.value, undefined); +assertEquals(1, obj1.bar = 1); +assertEquals(2, val1); +assertEquals(1, val2); +assertEquals(1, obj1.bar = 1) +assertEquals(2, val1); +assertEquals(2, val2); +assertEquals(2, obj1.bar); + +// Try to redefine bar again - should fail as configurable is false. +try { + Object.defineProperty(obj1, "bar", accessorConfigurable); + assertTrue(false); +} catch(e) { + assertTrue(/Cannot redefine property/.test(e)); +} + +// Try to redefine bar again using the data descriptor - should fail. +try { + Object.defineProperty(obj1, "bar", dataConfigurable); + assertTrue(false); +} catch(e) { + assertTrue(/Cannot redefine property/.test(e)); +} + +// Redefine using same descriptor - should succeed. +Object.defineProperty(obj1, "bar", accessorNoConfigurable); +desc = Object.getOwnPropertyDescriptor(obj1, "bar"); +assertFalse(desc.configurable); +assertFalse(desc.enumerable); +assertEquals(desc.writable, undefined); +assertEquals(desc.get, accessorNoConfigurable.get); +assertEquals(desc.set, accessorNoConfigurable.set); +assertEquals(desc.value, undefined); +assertEquals(1, obj1.bar = 1); +assertEquals(2, val1); +assertEquals(3, val2); +assertEquals(1, obj1.bar = 1) +assertEquals(2, val1); +assertEquals(4, val2); +assertEquals(4, obj1.bar); + +// Define an accessor that has only a setter +Object.defineProperty(obj1, "setOnly", accessorOnlySet); +desc = Object.getOwnPropertyDescriptor(obj1, "setOnly"); +assertTrue(desc.configurable); +assertFalse(desc.enumerable); +assertEquals(desc.set, accessorOnlySet.set); +assertEquals(desc.writable, undefined); +assertEquals(desc.value, undefined); +assertEquals(desc.get, undefined); +assertEquals(1, obj1.setOnly = 1); +assertEquals(1, val3); + +// Add a getter - should not touch the setter +Object.defineProperty(obj1, "setOnly", accessorOnlyGet); +desc = Object.getOwnPropertyDescriptor(obj1, "setOnly"); +assertTrue(desc.configurable); +assertFalse(desc.enumerable); +assertEquals(desc.get, accessorOnlyGet.get); +assertEquals(desc.set, accessorOnlySet.set); +assertEquals(desc.writable, undefined); +assertEquals(desc.value, undefined); +assertEquals(1, obj1.setOnly = 1); +assertEquals(2, val3); + +// The above should also work if redefining just a getter or setter on +// an existing property with both a getter and a setter. +Object.defineProperty(obj1, "both", accessorConfigurable); + +Object.defineProperty(obj1, "both", accessorOnlySet); +desc = Object.getOwnPropertyDescriptor(obj1, "both"); +assertTrue(desc.configurable); +assertFalse(desc.enumerable); +assertEquals(desc.set, accessorOnlySet.set); +assertEquals(desc.get, accessorConfigurable.get); +assertEquals(desc.writable, undefined); +assertEquals(desc.value, undefined); +assertEquals(1, obj1.both = 1); +assertEquals(3, val3); + + +// Data properties + +Object.defineProperty(obj1, "foobar", dataConfigurable); +desc = Object.getOwnPropertyDescriptor(obj1, "foobar"); +assertEquals(obj1.foobar, 1000); +assertEquals(desc.value, 1000); +assertTrue(desc.configurable); +assertFalse(desc.writable); +assertFalse(desc.enumerable); +assertEquals(desc.get, undefined); +assertEquals(desc.set, undefined); +//Try writing to non writable attribute - should remain 1000 +obj1.foobar = 1001; +assertEquals(obj1.foobar, 1000); + + +// Redefine to writable descriptor - now writing to foobar should be allowed +Object.defineProperty(obj1, "foobar", dataWritable); +desc = Object.getOwnPropertyDescriptor(obj1, "foobar"); +assertEquals(obj1.foobar, 3000); +assertEquals(desc.value, 3000); +// Note that since dataWritable does not define configurable the configurable +// setting from the redefined property (in this case true) is used. +assertTrue(desc.configurable); +assertTrue(desc.writable); +assertFalse(desc.enumerable); +assertEquals(desc.get, undefined); +assertEquals(desc.set, undefined); +// Writing to the property should now be allowed +obj1.foobar = 1001; +assertEquals(obj1.foobar, 1001); + + +// Redefine with non configurable data property. +Object.defineProperty(obj1, "foobar", dataNoConfigurable); +desc = Object.getOwnPropertyDescriptor(obj1, "foobar"); +assertEquals(obj1.foobar, 2000); +assertEquals(desc.value, 2000); +assertFalse(desc.configurable); +assertFalse(desc.writable); +assertFalse(desc.enumerable); +assertEquals(desc.get, undefined); +assertEquals(desc.set, undefined); + +// Try redefine again - shold fail because configurable is now false. +try { + Object.defineProperty(obj1, "foobar", dataConfigurable); + assertTrue(false); +} catch (e) { + assertTrue(/Cannot redefine property/.test(e)); +} + +// Try redefine again with accessor property - shold also fail. +try { + Object.defineProperty(obj1, "foobar", dataConfigurable); + assertTrue(false); +} catch (e) { + assertTrue(/Cannot redefine property/.test(e)); +} + + +// Redifine with the same descriptor - should succeed (step 6). +Object.defineProperty(obj1, "foobar", dataNoConfigurable); +desc = Object.getOwnPropertyDescriptor(obj1, "foobar"); +assertEquals(obj1.foobar, 2000); +assertEquals(desc.value, 2000); +assertFalse(desc.configurable); +assertFalse(desc.writable); +assertFalse(desc.enumerable); +assertEquals(desc.get, undefined); +assertEquals(desc.set, undefined); + + +// New object +var obj2 = {}; + +// Make accessor - redefine to data +Object.defineProperty(obj2, "foo", accessorConfigurable); + +// Redefine to data property +Object.defineProperty(obj2, "foo", dataConfigurable); +desc = Object.getOwnPropertyDescriptor(obj2, "foo"); +assertEquals(obj2.foo, 1000); +assertEquals(desc.value, 1000); +assertTrue(desc.configurable); +assertFalse(desc.writable); +assertFalse(desc.enumerable); +assertEquals(desc.get, undefined); +assertEquals(desc.set, undefined); + + +// Redefine back to accessor +Object.defineProperty(obj2, "foo", accessorConfigurable); +desc = Object.getOwnPropertyDescriptor(obj2, "foo"); +assertTrue(desc.configurable); +assertFalse(desc.enumerable); +assertEquals(desc.writable, undefined); +assertEquals(desc.get, accessorConfigurable.get); +assertEquals(desc.set, accessorConfigurable.set); +assertEquals(desc.value, undefined); +assertEquals(1, obj2.foo = 1); +assertEquals(3, val1); +assertEquals(4, val2); +assertEquals(3, obj2.foo); + +// Make data - redefine to accessor +Object.defineProperty(obj2, "bar", dataConfigurable) + +// Redefine to accessor property +Object.defineProperty(obj2, "bar", accessorConfigurable); +desc = Object.getOwnPropertyDescriptor(obj2, "bar"); +assertTrue(desc.configurable); +assertFalse(desc.enumerable); +assertEquals(desc.writable, undefined); +assertEquals(desc.get, accessorConfigurable.get); +assertEquals(desc.set, accessorConfigurable.set); +assertEquals(desc.value, undefined); +assertEquals(1, obj2.bar = 1); +assertEquals(4, val1); +assertEquals(4, val2); +assertEquals(4, obj2.foo); + +// Redefine back to data property +Object.defineProperty(obj2, "bar", dataConfigurable); +desc = Object.getOwnPropertyDescriptor(obj2, "bar"); +assertEquals(obj2.bar, 1000); +assertEquals(desc.value, 1000); +assertTrue(desc.configurable); +assertFalse(desc.writable); +assertFalse(desc.enumerable); +assertEquals(desc.get, undefined); +assertEquals(desc.set, undefined); + + +// Redefinition of an accessor defined using __defineGetter__ and +// __defineSetter__ +function get(){return this.x} +function set(x){this.x=x}; + +var obj3 = {x:1000}; +obj3.__defineGetter__("foo", get); +obj3.__defineSetter__("foo", set); + +desc = Object.getOwnPropertyDescriptor(obj3, "foo"); +assertTrue(desc.configurable); +assertTrue(desc.enumerable); +assertEquals(desc.writable, undefined); +assertEquals(desc.get, get); +assertEquals(desc.set, set); +assertEquals(desc.value, undefined); +assertEquals(1, obj3.foo = 1); +assertEquals(1, obj3.x); +assertEquals(1, obj3.foo); + +// Redefine to accessor property (non configurable) - note that enumerable +// which we do not redefine should remain the same (true). +Object.defineProperty(obj3, "foo", accessorNoConfigurable); +desc = Object.getOwnPropertyDescriptor(obj3, "foo"); +assertFalse(desc.configurable); +assertTrue(desc.enumerable); +assertEquals(desc.writable, undefined); +assertEquals(desc.get, accessorNoConfigurable.get); +assertEquals(desc.set, accessorNoConfigurable.set); +assertEquals(desc.value, undefined); +assertEquals(1, obj3.foo = 1); +assertEquals(5, val2); +assertEquals(5, obj3.foo); + + +obj3.__defineGetter__("bar", get); +obj3.__defineSetter__("bar", set); + + +// Redefine back to data property +Object.defineProperty(obj3, "bar", dataConfigurable); +desc = Object.getOwnPropertyDescriptor(obj3, "bar"); +assertEquals(obj3.bar, 1000); +assertEquals(desc.value, 1000); +assertTrue(desc.configurable); +assertFalse(desc.writable); +assertTrue(desc.enumerable); +assertEquals(desc.get, undefined); +assertEquals(desc.set, undefined); + + +var obj4 = {}; +var func = function (){return 42;}; +obj4.bar = func; +assertEquals(42, obj4.bar()); + +Object.defineProperty(obj4, "bar", accessorConfigurable); +desc = Object.getOwnPropertyDescriptor(obj4, "bar"); +assertTrue(desc.configurable); +assertTrue(desc.enumerable); +assertEquals(desc.writable, undefined); +assertEquals(desc.get, accessorConfigurable.get); +assertEquals(desc.set, accessorConfigurable.set); +assertEquals(desc.value, undefined); +assertEquals(1, obj4.bar = 1); +assertEquals(5, val1); +assertEquals(5, obj4.bar); + +// Make sure an error is thrown when trying to access to redefined function +try { + obj4.bar(); + assertTrue(false); +} catch (e) { + assertTrue(/is not a function/.test(e)); +} + + +// Test runtime calls to DefineOrRedefineDataProperty and +// DefineOrRedefineAccessorProperty - make sure we don't +// crash +try { + %DefineOrRedefineAccessorProperty(0, 0, 0, 0, 0); +} catch (e) { + assertTrue(/illegal access/.test(e)); +} + +try { + %DefineOrRedefineDataProperty(0, 0, 0, 0); +} catch (e) { + assertTrue(/illegal access/.test(e)); +} + +try { + %DefineOrRedefineDataProperty(null, null, null, null); +} catch (e) { + assertTrue(/illegal access/.test(e)); +} + +try { + %DefineOrRedefineAccessorProperty(null, null, null, null, null); +} catch (e) { + assertTrue(/illegal access/.test(e)); +} + +try { + %DefineOrRedefineDataProperty({}, null, null, null); +} catch (e) { + assertTrue(/illegal access/.test(e)); +} + +// Defining properties null should fail even when we have +// other allowed values +try { + %DefineOrRedefineAccessorProperty(null, 'foo', 0, func, 0); +} catch (e) { + assertTrue(/illegal access/.test(e)); +} + +try { + %DefineOrRedefineDataProperty(null, 'foo', 0, 0); +} catch (e) { + assertTrue(/illegal access/.test(e)); +} diff --git a/test/mjsunit/object-get-own-property-names.js b/test/mjsunit/object-get-own-property-names.js index f52cee2f..33aa85ef 100644 --- a/test/mjsunit/object-get-own-property-names.js +++ b/test/mjsunit/object-get-own-property-names.js @@ -57,6 +57,8 @@ propertyNames.sort(); assertEquals(3, propertyNames.length); assertEquals("0", propertyNames[0]); assertEquals("1", propertyNames[1]); +assertEquals("string", typeof propertyNames[0]); +assertEquals("string", typeof propertyNames[1]); assertEquals("length", propertyNames[2]); // Check that no proto properties are returned. diff --git a/test/mjsunit/regress/regress-603.js b/test/mjsunit/regress/regress-603.js new file mode 100644 index 00000000..7d4c3229 --- /dev/null +++ b/test/mjsunit/regress/regress-603.js @@ -0,0 +1,49 @@ +// Copyright 2010 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. + +// Calling non-objects directly or via Function.prototype.call should +// not mess up the stack. +// http://code.google.com/p/v8/issues/detail?id=603 + +function test0() { + var re = /b../; + return re('abcdefghijklm') + 'z'; +} +assertEquals('bcdz', test0()); + +var re1 = /c../; +re1.call = Function.prototype.call; +var test1 = re1.call(null, 'abcdefghijklm') + 'z'; +assertEquals('cdez', test1); + +var re2 = /d../; +var test2 = Function.prototype.call.call(re2, null, 'abcdefghijklm') + 'z'; +assertEquals('defz', test2); + +var re3 = /e../; +var test3 = Function.prototype.call.apply(re3, [null, 'abcdefghijklm']) + 'z'; +assertEquals('efgz', test3); diff --git a/test/mjsunit/regress/regress-612.js b/test/mjsunit/regress/regress-612.js new file mode 100644 index 00000000..aee6d530 --- /dev/null +++ b/test/mjsunit/regress/regress-612.js @@ -0,0 +1,44 @@ +// Copyright 2010 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. + +// Tests intercation between __defineGetter__/__defineSetter and fast and slow +// mode of the objects due to series of assignments optimization. +// (See http://code.google.com/p/v8/issues/detail?id=612) + +obj = {} + +// Define getter which currently moves object into slow mode. +obj.__defineGetter__('foobar', function() { return 42; }) + +// Starts initialization block mode. And turns object into slow mode. +obj.a = 1 +obj.b = 2; +obj.c = 3; +// Now object is turned into fast mode, but it has getter defined above... + +// Now assert is triggered. +obj.__defineGetter__('foobar', function() { return 42; }) diff --git a/test/mjsunit/setter-on-constructor-prototype.js b/test/mjsunit/setter-on-constructor-prototype.js new file mode 100644 index 00000000..d5718f9c --- /dev/null +++ b/test/mjsunit/setter-on-constructor-prototype.js @@ -0,0 +1,111 @@ +// Copyright 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. + +// Flags: --allow-natives-syntax + +function RunTest(ensure_fast_case) { + function C1() { + this.x = 23; + }; + C1.prototype = { set x(value) { this.y = 23; } }; + if (ensure_fast_case) { + %ToFastProperties(C1.prototype); + } + + for (var i = 0; i < 10; i++) { + var c1 = new C1(); + assertEquals("undefined", typeof c1.x); + assertEquals(23, c1.y); + } + + + function C2() { + this.x = 23; + }; + C2.prototype = { }; + C2.prototype.__proto__ = { set x(value) { this.y = 23; } }; + if (ensure_fast_case) { + %ToFastProperties(C2.prototype.__proto__) + } + + for (var i = 0; i < 10; i++) { + var c2 = new C2(); + assertEquals("undefined", typeof c2.x); + assertEquals(23, c2.y); + } + + + function C3() { + this.x = 23; + }; + C3.prototype = { }; + C3.prototype.__defineSetter__('x', function(value) { this.y = 23; }); + if (ensure_fast_case) { + %ToFastProperties(C3.prototype); + } + + for (var i = 0; i < 10; i++) { + var c3 = new C3(); + assertEquals("undefined", typeof c3.x); + assertEquals(23, c3.y); + } + + + function C4() { + this.x = 23; + }; + C4.prototype = { }; + C4.prototype.__proto__ = { }; + C4.prototype.__proto__.__defineSetter__('x', function(value) { this.y = 23; }); + if (ensure_fast_case) { + %ToFastProperties(C4.prototype.__proto__); + } + + for (var i = 0; i < 10; i++) { + var c4 = new C4(); + assertEquals("undefined", typeof c4.x); + assertEquals(23, c4.y); + } + + + function D() { + this.x = 23; + }; + D.prototype = 1; + if (ensure_fast_case) { + %ToFastProperties(D.prototype); + } + + for (var i = 0; i < 10; i++) { + var d = new D(); + assertEquals(23, d.x); + assertEquals("undefined", typeof d.y); + } +} + +RunTest(false); +RunTest(true); diff --git a/test/mjsunit/substr.js b/test/mjsunit/substr.js index f69a9c04..f69a9c04 100644..100755 --- a/test/mjsunit/substr.js +++ b/test/mjsunit/substr.js diff --git a/test/mjsunit/tools/tickprocessor-test-func-info.log b/test/mjsunit/tools/tickprocessor-test-func-info.log new file mode 100644 index 00000000..29a12f6f --- /dev/null +++ b/test/mjsunit/tools/tickprocessor-test-func-info.log @@ -0,0 +1,13 @@ +shared-library,"shell",0x08048000,0x081ee000 +shared-library,"/lib32/libm-2.7.so",0xf7db6000,0xf7dd9000 +shared-library,"ffffe000-fffff000",0xffffe000,0xfffff000 +profiler,"begin",1 +code-creation,Stub,0x424260,348,"CompareStub_GE" +code-creation,LazyCompile,0x2a8100,18535,"DrawQube 3d-cube.js:188" +function-creation,0x2d11b8,0x2a8100 +code-creation,LazyCompile,0x480100,3908,"DrawLine 3d-cube.js:17" +function-creation,0x2d0f7c,0x480100 +tick,0x424284,0xbfffeea0,0x2d0f7c,0,0x2aaaa5 +tick,0x42429f,0xbfffed88,0x2d0f7c,0,0x2aacb4 +tick,0x48063d,0xbfffec7c,0x2d0f7c,0,0x2aaec6 +profiler,"end" diff --git a/tools/gyp/v8.gyp b/tools/gyp/v8.gyp index f2d1b98e..f190598c 100644 --- a/tools/gyp/v8.gyp +++ b/tools/gyp/v8.gyp @@ -269,7 +269,6 @@ '../../src/execution.h', '../../src/factory.cc', '../../src/factory.h', - '../../src/fast-codegen.cc', '../../src/fast-codegen.h', '../../src/flag-definitions.h', '../../src/flags.cc', @@ -308,6 +307,8 @@ '../../src/jsregexp.h', '../../src/list-inl.h', '../../src/list.h', + '../../src/liveedit.cc', + '../../src/liveedit.h', '../../src/log-inl.h', '../../src/log-utils.cc', '../../src/log-utils.h', @@ -320,6 +321,7 @@ '../../src/messages.cc', '../../src/messages.h', '../../src/natives.h', + '../../src/number-info.h', '../../src/objects-debug.cc', '../../src/objects-inl.h', '../../src/objects.cc', @@ -401,6 +403,7 @@ '../../src/arm', ], 'sources': [ + '../../src/fast-codegen.cc', '../../src/arm/assembler-arm-inl.h', '../../src/arm/assembler-arm.cc', '../../src/arm/assembler-arm.h', @@ -452,6 +455,7 @@ '../../src/ia32/debug-ia32.cc', '../../src/ia32/disasm-ia32.cc', '../../src/ia32/fast-codegen-ia32.cc', + '../../src/ia32/fast-codegen-ia32.h', '../../src/ia32/frames-ia32.cc', '../../src/ia32/frames-ia32.h', '../../src/ia32/full-codegen-ia32.cc', @@ -472,6 +476,7 @@ '../../src/x64', ], 'sources': [ + '../../src/fast-codegen.cc', '../../src/x64/assembler-x64-inl.h', '../../src/x64/assembler-x64.cc', '../../src/x64/assembler-x64.h', @@ -556,11 +561,11 @@ '../../src/math.js', '../../src/messages.js', '../../src/apinatives.js', - '../../src/debug-delay.js', - '../../src/mirror-delay.js', - '../../src/date-delay.js', - '../../src/json-delay.js', - '../../src/regexp-delay.js', + '../../src/debug-debugger.js', + '../../src/mirror-debugger.js', + '../../src/date.js', + '../../src/json.js', + '../../src/regexp.js', '../../src/macros.py', ], }, diff --git a/tools/tickprocessor.js b/tools/tickprocessor.js index 40cee8a0..a3e14c3a 100644 --- a/tools/tickprocessor.js +++ b/tools/tickprocessor.js @@ -160,10 +160,6 @@ function TickProcessor( processor: this.processHeapSampleEnd }, 'heap-js-prod-item': { parsers: [null, 'var-args'], processor: this.processJSProducer, backrefs: true }, - 'PAGE-LOAD-START': { parsers: [null, null], - processor: this.processPageLoadStart }, - 'PAGE-LOAD-END': { parsers: [null, null], - processor: this.processPageLoadEnd }, // Ignored events. 'profiler': null, 'heap-sample-stats': null, @@ -180,7 +176,6 @@ function TickProcessor( this.stateFilter_ = stateFilter; this.snapshotLogProcessor_ = snapshotLogProcessor; this.deserializedEntriesNames_ = []; - this.handle_ticks_ = false; var ticks = this.ticks_ = { total: 0, unaccounted: 0, excluded: 0, gc: 0 }; @@ -344,7 +339,6 @@ TickProcessor.prototype.includeTick = function(vmState) { TickProcessor.prototype.processTick = function(pc, sp, func, vmState, stack) { - if (!this.handle_ticks_) return; this.ticks_.total++; if (vmState == TickProcessor.VmStates.GC) this.ticks_.gc++; if (!this.includeTick(vmState)) { @@ -392,16 +386,6 @@ TickProcessor.prototype.processHeapSampleEnd = function(space, state) { }; -TickProcessor.prototype.processPageLoadStart = function() { - this.handle_ticks_ = true; -}; - - -TickProcessor.prototype.processPageLoadEnd = function() { - this.handle_ticks_ = false; -}; - - TickProcessor.prototype.processJSProducer = function(constructor, stack) { if (!this.currentProducerProfile_) return; if (stack.length == 0) return; diff --git a/tools/visual_studio/v8_base.vcproj b/tools/visual_studio/v8_base.vcproj index e58e8ff3..85935288 100644 --- a/tools/visual_studio/v8_base.vcproj +++ b/tools/visual_studio/v8_base.vcproj @@ -401,7 +401,7 @@ > </File> <File - RelativePath="..\..\src\fast-codegen.cc" + RelativePath="..\..\src\ia32\fast-codegen-ia32.h" > </File> <File @@ -577,6 +577,14 @@ > </File> <File + RelativePath="..\..\src\liveedit.cc" + > + </File> + <File + RelativePath="..\..\src\liveedit.h" + > + </File> + <File RelativePath="..\..\src\log.cc" > </File> @@ -633,6 +641,10 @@ > </File> <File + RelativePath="..\..\src\number-info.h" + > + </File> + <File RelativePath="..\..\src\objects-debug.cc" > <FileConfiguration diff --git a/tools/visual_studio/v8_base_arm.vcproj b/tools/visual_studio/v8_base_arm.vcproj index 4b37b538..2602be45 100644 --- a/tools/visual_studio/v8_base_arm.vcproj +++ b/tools/visual_studio/v8_base_arm.vcproj @@ -581,6 +581,14 @@ > </File> <File + RelativePath="..\..\src\liveedit.cc" + > + </File> + <File + RelativePath="..\..\src\liveedit.h" + > + </File> + <File RelativePath="..\..\src\log.cc" > </File> @@ -637,6 +645,10 @@ > </File> <File + RelativePath="..\..\src\number-info.h" + > + </File> + <File RelativePath="..\..\src\objects-debug.cc" > <FileConfiguration diff --git a/tools/visual_studio/v8_base_x64.vcproj b/tools/visual_studio/v8_base_x64.vcproj index b6d5c7d8..d3f55c6a 100644 --- a/tools/visual_studio/v8_base_x64.vcproj +++ b/tools/visual_studio/v8_base_x64.vcproj @@ -578,6 +578,14 @@ > </File> <File + RelativePath="..\..\src\liveedit.cc" + > + </File> + <File + RelativePath="..\..\src\liveedit.h" + > + </File> + <File RelativePath="..\..\src\log.cc" > </File> @@ -634,6 +642,10 @@ > </File> <File + RelativePath="..\..\src\number-info.h" + > + </File> + <File RelativePath="..\..\src\objects-debug.cc" > <FileConfiguration |