| Index: src/objects.cc
|
| ===================================================================
|
| --- src/objects.cc (revision 6800)
|
| +++ src/objects.cc (working copy)
|
| @@ -980,6 +980,9 @@
|
| case SHARED_FUNCTION_INFO_TYPE:
|
| accumulator->Add("<SharedFunctionInfo>");
|
| break;
|
| + case JS_MESSAGE_OBJECT_TYPE:
|
| + accumulator->Add("<JSMessageObject>");
|
| + break;
|
| #define MAKE_STRUCT_CASE(NAME, Name, name) \
|
| case NAME##_TYPE: \
|
| accumulator->Put('<'); \
|
| @@ -1070,6 +1073,7 @@
|
| case JS_GLOBAL_PROXY_TYPE:
|
| case JS_GLOBAL_OBJECT_TYPE:
|
| case JS_BUILTINS_OBJECT_TYPE:
|
| + case JS_MESSAGE_OBJECT_TYPE:
|
| JSObject::BodyDescriptor::IterateBody(this, object_size, v);
|
| break;
|
| case JS_FUNCTION_TYPE:
|
| @@ -1210,6 +1214,8 @@
|
| MaybeObject* JSObject::AddFastProperty(String* name,
|
| Object* value,
|
| PropertyAttributes attributes) {
|
| + ASSERT(!IsJSGlobalProxy());
|
| +
|
| // Normalize the object if the name is an actual string (not the
|
| // hidden symbols) and is not a real identifier.
|
| StringInputBuffer buffer(name);
|
| @@ -1394,7 +1400,7 @@
|
| if (!map()->is_extensible()) {
|
| Handle<Object> args[1] = {Handle<String>(name)};
|
| return Top::Throw(*Factory::NewTypeError("object_not_extensible",
|
| - HandleVector(args, 1)));
|
| + HandleVector(args, 1)));
|
| }
|
| if (HasFastProperties()) {
|
| // Ensure the descriptor array does not get too big.
|
| @@ -1702,8 +1708,9 @@
|
| }
|
|
|
|
|
| -bool JSObject::SetElementWithCallbackSetterInPrototypes(uint32_t index,
|
| - Object* value) {
|
| +MaybeObject* JSObject::SetElementWithCallbackSetterInPrototypes(uint32_t index,
|
| + Object* value,
|
| + bool* found) {
|
| for (Object* pt = GetPrototype();
|
| pt != Heap::null_value();
|
| pt = pt->GetPrototype()) {
|
| @@ -1713,15 +1720,16 @@
|
| NumberDictionary* dictionary = JSObject::cast(pt)->element_dictionary();
|
| int entry = dictionary->FindEntry(index);
|
| if (entry != NumberDictionary::kNotFound) {
|
| - Object* element = dictionary->ValueAt(entry);
|
| PropertyDetails details = dictionary->DetailsAt(entry);
|
| if (details.type() == CALLBACKS) {
|
| - SetElementWithCallback(element, index, value, JSObject::cast(pt));
|
| - return true;
|
| + *found = true;
|
| + return SetElementWithCallback(
|
| + dictionary->ValueAt(entry), index, value, JSObject::cast(pt));
|
| }
|
| }
|
| }
|
| - return false;
|
| + *found = false;
|
| + return Heap::the_hole_value();
|
| }
|
|
|
|
|
| @@ -1824,8 +1832,9 @@
|
| // We only need to deal with CALLBACKS and INTERCEPTORS
|
| MaybeObject* JSObject::SetPropertyWithFailedAccessCheck(LookupResult* result,
|
| String* name,
|
| - Object* value) {
|
| - if (!result->IsProperty()) {
|
| + Object* value,
|
| + bool check_prototype) {
|
| + if (check_prototype && !result->IsProperty()) {
|
| LookupCallbackSetterInPrototypes(name, result);
|
| }
|
|
|
| @@ -1851,7 +1860,8 @@
|
| LookupResult r;
|
| LookupRealNamedProperty(name, &r);
|
| if (r.IsProperty()) {
|
| - return SetPropertyWithFailedAccessCheck(&r, name, value);
|
| + return SetPropertyWithFailedAccessCheck(&r, name, value,
|
| + check_prototype);
|
| }
|
| break;
|
| }
|
| @@ -1892,7 +1902,7 @@
|
| // Check access rights if needed.
|
| if (IsAccessCheckNeeded()
|
| && !Top::MayNamedAccess(this, name, v8::ACCESS_SET)) {
|
| - return SetPropertyWithFailedAccessCheck(result, name, value);
|
| + return SetPropertyWithFailedAccessCheck(result, name, value, true);
|
| }
|
|
|
| if (IsJSGlobalProxy()) {
|
| @@ -1982,7 +1992,7 @@
|
| // callback setter removed. The two lines looking up the LookupResult
|
| // result are also added. If one of the functions is changed, the other
|
| // should be.
|
| -MaybeObject* JSObject::IgnoreAttributesAndSetLocalProperty(
|
| +MaybeObject* JSObject::SetLocalPropertyIgnoreAttributes(
|
| String* name,
|
| Object* value,
|
| PropertyAttributes attributes) {
|
| @@ -1994,14 +2004,14 @@
|
| // Check access rights if needed.
|
| if (IsAccessCheckNeeded()
|
| && !Top::MayNamedAccess(this, name, v8::ACCESS_SET)) {
|
| - return SetPropertyWithFailedAccessCheck(&result, name, value);
|
| + return SetPropertyWithFailedAccessCheck(&result, name, value, false);
|
| }
|
|
|
| if (IsJSGlobalProxy()) {
|
| Object* proto = GetPrototype();
|
| if (proto->IsNull()) return value;
|
| ASSERT(proto->IsJSGlobalObject());
|
| - return JSObject::cast(proto)->IgnoreAttributesAndSetLocalProperty(
|
| + return JSObject::cast(proto)->SetLocalPropertyIgnoreAttributes(
|
| name,
|
| value,
|
| attributes);
|
| @@ -2283,6 +2293,9 @@
|
| // The global object is always normalized.
|
| ASSERT(!IsGlobalObject());
|
|
|
| + // JSGlobalProxy must never be normalized
|
| + ASSERT(!IsJSGlobalProxy());
|
| +
|
| // Allocate new content.
|
| int property_count = map()->NumberOfDescribedProperties();
|
| if (expected_additional_properties > 0) {
|
| @@ -2608,7 +2621,17 @@
|
| NumberDictionary* dictionary = element_dictionary();
|
| int entry = dictionary->FindEntry(index);
|
| if (entry != NumberDictionary::kNotFound) {
|
| - return dictionary->DeleteProperty(entry, mode);
|
| + Object* result = dictionary->DeleteProperty(entry, mode);
|
| + if (mode == STRICT_DELETION && result == Heap::false_value()) {
|
| + // In strict mode, deleting a non-configurable property throws
|
| + // exception. dictionary->DeleteProperty will return false_value()
|
| + // if a non-configurable property is being deleted.
|
| + HandleScope scope;
|
| + Handle<Object> i = Factory::NewNumberFromUint(index);
|
| + Handle<Object> args[2] = { i, Handle<Object>(this) };
|
| + return Top::Throw(*Factory::NewTypeError("strict_delete_property",
|
| + HandleVector(args, 2)));
|
| + }
|
| }
|
| break;
|
| }
|
| @@ -2647,6 +2670,13 @@
|
| if (!result.IsProperty()) return Heap::true_value();
|
| // Ignore attributes if forcing a deletion.
|
| if (result.IsDontDelete() && mode != FORCE_DELETION) {
|
| + if (mode == STRICT_DELETION) {
|
| + // Deleting a non-configurable property in strict mode.
|
| + HandleScope scope;
|
| + Handle<Object> args[2] = { Handle<Object>(name), Handle<Object>(this) };
|
| + return Top::Throw(*Factory::NewTypeError("strict_delete_property",
|
| + HandleVector(args, 2)));
|
| + }
|
| return Heap::false_value();
|
| }
|
| // Check for interceptor.
|
| @@ -2769,6 +2799,13 @@
|
|
|
|
|
| MaybeObject* JSObject::PreventExtensions() {
|
| + if (IsJSGlobalProxy()) {
|
| + Object* proto = GetPrototype();
|
| + if (proto->IsNull()) return this;
|
| + ASSERT(proto->IsJSGlobalObject());
|
| + return JSObject::cast(proto)->PreventExtensions();
|
| + }
|
| +
|
| // If there are fast elements we normalize.
|
| if (HasFastElements()) {
|
| Object* ok;
|
| @@ -5398,7 +5435,8 @@
|
|
|
| void JSFunction::MarkForLazyRecompilation() {
|
| ASSERT(is_compiled() && !IsOptimized());
|
| - ASSERT(shared()->allows_lazy_compilation());
|
| + ASSERT(shared()->allows_lazy_compilation() ||
|
| + code()->optimizable());
|
| ReplaceCode(Builtins::builtin(Builtins::LazyRecompile));
|
| }
|
|
|
| @@ -5914,7 +5952,7 @@
|
| Handle<Object> p = it.rinfo()->target_object_handle(origin);
|
| it.rinfo()->set_target_object(*p);
|
| } else if (mode == RelocInfo::GLOBAL_PROPERTY_CELL) {
|
| - Handle<JSGlobalPropertyCell> cell = it.rinfo()->target_cell_handle();
|
| + Handle<JSGlobalPropertyCell> cell = it.rinfo()->target_cell_handle();
|
| it.rinfo()->set_target_cell(*cell);
|
| } else if (RelocInfo::IsCodeTarget(mode)) {
|
| // rewrite code handles in inline cache targets to direct
|
| @@ -5986,21 +6024,16 @@
|
| }
|
|
|
|
|
| -uint8_t* Code::GetSafepointEntry(Address pc) {
|
| +SafepointEntry Code::GetSafepointEntry(Address pc) {
|
| SafepointTable table(this);
|
| - unsigned pc_offset = static_cast<unsigned>(pc - instruction_start());
|
| - for (unsigned i = 0; i < table.length(); i++) {
|
| - // TODO(kasperl): Replace the linear search with binary search.
|
| - if (table.GetPcOffset(i) == pc_offset) return table.GetEntry(i);
|
| - }
|
| - return NULL;
|
| + return table.FindEntry(pc);
|
| }
|
|
|
|
|
| void Code::SetNoStackCheckTable() {
|
| // Indicate the absence of a stack-check table by a table start after the
|
| // end of the instructions. Table start must be aligned, so round up.
|
| - set_stack_check_table_start(RoundUp(instruction_size(), kIntSize));
|
| + set_stack_check_table_offset(RoundUp(instruction_size(), kIntSize));
|
| }
|
|
|
|
|
| @@ -6223,10 +6256,35 @@
|
| }
|
|
|
|
|
| +void Code::PrintExtraICState(FILE* out, Kind kind, ExtraICState extra) {
|
| + const char* name = NULL;
|
| + switch (kind) {
|
| + case CALL_IC:
|
| + if (extra == STRING_INDEX_OUT_OF_BOUNDS) {
|
| + name = "STRING_INDEX_OUT_OF_BOUNDS";
|
| + }
|
| + break;
|
| + case STORE_IC:
|
| + if (extra == StoreIC::kStoreICStrict) {
|
| + name = "STRICT";
|
| + }
|
| + break;
|
| + default:
|
| + break;
|
| + }
|
| + if (name != NULL) {
|
| + PrintF(out, "extra_ic_state = %s\n", name);
|
| + } else {
|
| + PrintF(out, "etra_ic_state = %d\n", extra);
|
| + }
|
| +}
|
| +
|
| +
|
| void Code::Disassemble(const char* name, FILE* out) {
|
| PrintF(out, "kind = %s\n", Kind2String(kind()));
|
| if (is_inline_cache_stub()) {
|
| PrintF(out, "ic_state = %s\n", ICState2String(ic_state()));
|
| + PrintExtraICState(out, kind(), extra_ic_state());
|
| PrintF(out, "ic_in_loop = %d\n", ic_in_loop() == IN_LOOP);
|
| if (ic_state() == MONOMORPHIC) {
|
| PrintF(out, "type = %s\n", PropertyType2String(type()));
|
| @@ -6264,17 +6322,20 @@
|
| PrintF(out, "%p %4d ", (instruction_start() + pc_offset), pc_offset);
|
| table.PrintEntry(i);
|
| PrintF(out, " (sp -> fp)");
|
| - int deoptimization_index = table.GetDeoptimizationIndex(i);
|
| - if (deoptimization_index != Safepoint::kNoDeoptimizationIndex) {
|
| - PrintF(out, " %6d", deoptimization_index);
|
| + SafepointEntry entry = table.GetEntry(i);
|
| + if (entry.deoptimization_index() != Safepoint::kNoDeoptimizationIndex) {
|
| + PrintF(out, " %6d", entry.deoptimization_index());
|
| } else {
|
| PrintF(out, " <none>");
|
| }
|
| + if (entry.argument_count() > 0) {
|
| + PrintF(out, " argc: %d", entry.argument_count());
|
| + }
|
| PrintF(out, "\n");
|
| }
|
| PrintF(out, "\n");
|
| } else if (kind() == FUNCTION) {
|
| - unsigned offset = stack_check_table_start();
|
| + unsigned offset = stack_check_table_offset();
|
| // If there is no stack check table, the "table start" will at or after
|
| // (due to alignment) the end of the instruction stream.
|
| if (static_cast<int>(offset) < instruction_size()) {
|
| @@ -6675,6 +6736,13 @@
|
| return UNDEFINED_ELEMENT;
|
| }
|
|
|
| + if (IsJSGlobalProxy()) {
|
| + Object* proto = GetPrototype();
|
| + if (proto->IsNull()) return UNDEFINED_ELEMENT;
|
| + ASSERT(proto->IsJSGlobalObject());
|
| + return JSObject::cast(proto)->HasLocalElement(index);
|
| + }
|
| +
|
| // Check for lookup interceptor
|
| if (HasIndexedInterceptor()) {
|
| return HasElementWithInterceptor(this, index) ? INTERCEPTED_ELEMENT
|
| @@ -6946,9 +7014,11 @@
|
| uint32_t elms_length = static_cast<uint32_t>(elms->length());
|
|
|
| if (check_prototype &&
|
| - (index >= elms_length || elms->get(index)->IsTheHole()) &&
|
| - SetElementWithCallbackSetterInPrototypes(index, value)) {
|
| - return value;
|
| + (index >= elms_length || elms->get(index)->IsTheHole())) {
|
| + bool found;
|
| + MaybeObject* result =
|
| + SetElementWithCallbackSetterInPrototypes(index, value, &found);
|
| + if (found) return result;
|
| }
|
|
|
|
|
| @@ -7080,9 +7150,11 @@
|
| }
|
| } else {
|
| // Index not already used. Look for an accessor in the prototype chain.
|
| - if (check_prototype &&
|
| - SetElementWithCallbackSetterInPrototypes(index, value)) {
|
| - return value;
|
| + if (check_prototype) {
|
| + bool found;
|
| + MaybeObject* result =
|
| + SetElementWithCallbackSetterInPrototypes(index, value, &found);
|
| + if (found) return result;
|
| }
|
| // When we set the is_extensible flag to false we always force
|
| // the element into dictionary mode (and force them to stay there).
|
| @@ -7982,20 +8054,28 @@
|
| // StringSharedKeys are used as keys in the eval cache.
|
| class StringSharedKey : public HashTableKey {
|
| public:
|
| - StringSharedKey(String* source, SharedFunctionInfo* shared)
|
| - : source_(source), shared_(shared) { }
|
| + StringSharedKey(String* source,
|
| + SharedFunctionInfo* shared,
|
| + StrictModeFlag strict_mode)
|
| + : source_(source),
|
| + shared_(shared),
|
| + strict_mode_(strict_mode) { }
|
|
|
| bool IsMatch(Object* other) {
|
| if (!other->IsFixedArray()) return false;
|
| FixedArray* pair = FixedArray::cast(other);
|
| SharedFunctionInfo* shared = SharedFunctionInfo::cast(pair->get(0));
|
| if (shared != shared_) return false;
|
| + StrictModeFlag strict_mode = static_cast<StrictModeFlag>(
|
| + Smi::cast(pair->get(2))->value());
|
| + if (strict_mode != strict_mode_) return false;
|
| String* source = String::cast(pair->get(1));
|
| return source->Equals(source_);
|
| }
|
|
|
| static uint32_t StringSharedHashHelper(String* source,
|
| - SharedFunctionInfo* shared) {
|
| + SharedFunctionInfo* shared,
|
| + StrictModeFlag strict_mode) {
|
| uint32_t hash = source->Hash();
|
| if (shared->HasSourceCode()) {
|
| // Instead of using the SharedFunctionInfo pointer in the hash
|
| @@ -8005,36 +8085,41 @@
|
| // collection.
|
| Script* script = Script::cast(shared->script());
|
| hash ^= String::cast(script->source())->Hash();
|
| + if (strict_mode == kStrictMode) hash ^= 0x8000;
|
| hash += shared->start_position();
|
| }
|
| return hash;
|
| }
|
|
|
| uint32_t Hash() {
|
| - return StringSharedHashHelper(source_, shared_);
|
| + return StringSharedHashHelper(source_, shared_, strict_mode_);
|
| }
|
|
|
| uint32_t HashForObject(Object* obj) {
|
| FixedArray* pair = FixedArray::cast(obj);
|
| SharedFunctionInfo* shared = SharedFunctionInfo::cast(pair->get(0));
|
| String* source = String::cast(pair->get(1));
|
| - return StringSharedHashHelper(source, shared);
|
| + StrictModeFlag strict_mode = static_cast<StrictModeFlag>(
|
| + Smi::cast(pair->get(2))->value());
|
| + return StringSharedHashHelper(source, shared, strict_mode);
|
| }
|
|
|
| MUST_USE_RESULT MaybeObject* AsObject() {
|
| Object* obj;
|
| - { MaybeObject* maybe_obj = Heap::AllocateFixedArray(2);
|
| + { MaybeObject* maybe_obj = Heap::AllocateFixedArray(3);
|
| if (!maybe_obj->ToObject(&obj)) return maybe_obj;
|
| }
|
| FixedArray* pair = FixedArray::cast(obj);
|
| pair->set(0, shared_);
|
| pair->set(1, source_);
|
| + pair->set(2, Smi::FromInt(strict_mode_));
|
| return pair;
|
| }
|
|
|
| private:
|
| String* source_;
|
| SharedFunctionInfo* shared_;
|
| + StrictModeFlag strict_mode_;
|
| };
|
|
|
|
|
| @@ -8523,10 +8608,20 @@
|
| if (value->IsUndefined()) {
|
| undefs++;
|
| } else {
|
| + if (pos > static_cast<uint32_t>(Smi::kMaxValue)) {
|
| + // Adding an entry with the key beyond smi-range requires
|
| + // allocation. Bailout.
|
| + return Smi::FromInt(-1);
|
| + }
|
| new_dict->AddNumberEntry(pos, value, details)->ToObjectUnchecked();
|
| pos++;
|
| }
|
| } else {
|
| + if (key > static_cast<uint32_t>(Smi::kMaxValue)) {
|
| + // Adding an entry with the key beyond smi-range requires
|
| + // allocation. Bailout.
|
| + return Smi::FromInt(-1);
|
| + }
|
| new_dict->AddNumberEntry(key, value, details)->ToObjectUnchecked();
|
| }
|
| }
|
| @@ -8535,6 +8630,11 @@
|
| uint32_t result = pos;
|
| PropertyDetails no_details = PropertyDetails(NONE, NORMAL);
|
| while (undefs > 0) {
|
| + if (pos > static_cast<uint32_t>(Smi::kMaxValue)) {
|
| + // Adding an entry with the key beyond smi-range requires
|
| + // allocation. Bailout.
|
| + return Smi::FromInt(-1);
|
| + }
|
| new_dict->AddNumberEntry(pos, Heap::undefined_value(), no_details)->
|
| ToObjectUnchecked();
|
| pos++;
|
| @@ -8993,8 +9093,10 @@
|
| }
|
|
|
|
|
| -Object* CompilationCacheTable::LookupEval(String* src, Context* context) {
|
| - StringSharedKey key(src, context->closure()->shared());
|
| +Object* CompilationCacheTable::LookupEval(String* src,
|
| + Context* context,
|
| + StrictModeFlag strict_mode) {
|
| + StringSharedKey key(src, context->closure()->shared(), strict_mode);
|
| int entry = FindEntry(&key);
|
| if (entry == kNotFound) return Heap::undefined_value();
|
| return get(EntryToIndex(entry) + 1);
|
| @@ -9029,8 +9131,10 @@
|
|
|
| MaybeObject* CompilationCacheTable::PutEval(String* src,
|
| Context* context,
|
| - Object* value) {
|
| - StringSharedKey key(src, context->closure()->shared());
|
| + SharedFunctionInfo* value) {
|
| + StringSharedKey key(src,
|
| + context->closure()->shared(),
|
| + value->strict_mode() ? kStrictMode : kNonStrictMode);
|
| Object* obj;
|
| { MaybeObject* maybe_obj = EnsureCapacity(1, &key);
|
| if (!maybe_obj->ToObject(&obj)) return maybe_obj;
|
| @@ -9259,7 +9363,7 @@
|
| JSObject::DeleteMode mode) {
|
| PropertyDetails details = DetailsAt(entry);
|
| // Ignore attributes if forcing a deletion.
|
| - if (details.IsDontDelete() && mode == JSObject::NORMAL_DELETION) {
|
| + if (details.IsDontDelete() && mode != JSObject::FORCE_DELETION) {
|
| return Heap::false_value();
|
| }
|
| SetEntry(entry, Heap::null_value(), Heap::null_value(), Smi::FromInt(0));
|
|
|