| Index: src/runtime.cc
|
| ===================================================================
|
| --- src/runtime.cc (revision 6800)
|
| +++ src/runtime.cc (working copy)
|
| @@ -330,13 +330,18 @@
|
| Handle<Object> result;
|
| uint32_t element_index = 0;
|
| if (key->IsSymbol()) {
|
| - // If key is a symbol it is not an array element.
|
| - Handle<String> name(String::cast(*key));
|
| - ASSERT(!name->AsArrayIndex(&element_index));
|
| - result = SetProperty(boilerplate, name, value, NONE);
|
| + if (Handle<String>::cast(key)->AsArrayIndex(&element_index)) {
|
| + // Array index as string (uint32).
|
| + result = SetOwnElement(boilerplate, element_index, value);
|
| + } else {
|
| + Handle<String> name(String::cast(*key));
|
| + ASSERT(!name->AsArrayIndex(&element_index));
|
| + result = SetLocalPropertyIgnoreAttributes(boilerplate, name,
|
| + value, NONE);
|
| + }
|
| } else if (key->ToArrayIndex(&element_index)) {
|
| // Array index (uint32).
|
| - result = SetElement(boilerplate, element_index, value);
|
| + result = SetOwnElement(boilerplate, element_index, value);
|
| } else {
|
| // Non-uint32 number.
|
| ASSERT(key->IsNumber());
|
| @@ -345,7 +350,8 @@
|
| Vector<char> buffer(arr, ARRAY_SIZE(arr));
|
| const char* str = DoubleToCString(num, buffer);
|
| Handle<String> name = Factory::NewStringFromAscii(CStrVector(str));
|
| - result = SetProperty(boilerplate, name, value, NONE);
|
| + result = SetLocalPropertyIgnoreAttributes(boilerplate, name,
|
| + value, NONE);
|
| }
|
| // If setting the property on the boilerplate throws an
|
| // exception, the exception is converted to an empty handle in
|
| @@ -638,6 +644,90 @@
|
| }
|
|
|
|
|
| +static bool CheckAccessException(LookupResult* result,
|
| + v8::AccessType access_type) {
|
| + if (result->type() == CALLBACKS) {
|
| + Object* callback = result->GetCallbackObject();
|
| + if (callback->IsAccessorInfo()) {
|
| + AccessorInfo* info = AccessorInfo::cast(callback);
|
| + bool can_access =
|
| + (access_type == v8::ACCESS_HAS &&
|
| + (info->all_can_read() || info->all_can_write())) ||
|
| + (access_type == v8::ACCESS_GET && info->all_can_read()) ||
|
| + (access_type == v8::ACCESS_SET && info->all_can_write());
|
| + return can_access;
|
| + }
|
| + }
|
| +
|
| + return false;
|
| +}
|
| +
|
| +
|
| +static bool CheckAccess(JSObject* obj,
|
| + String* name,
|
| + LookupResult* result,
|
| + v8::AccessType access_type) {
|
| + ASSERT(result->IsProperty());
|
| +
|
| + JSObject* holder = result->holder();
|
| + JSObject* current = obj;
|
| + while (true) {
|
| + if (current->IsAccessCheckNeeded() &&
|
| + !Top::MayNamedAccess(current, name, access_type)) {
|
| + // Access check callback denied the access, but some properties
|
| + // can have a special permissions which override callbacks descision
|
| + // (currently see v8::AccessControl).
|
| + break;
|
| + }
|
| +
|
| + if (current == holder) {
|
| + return true;
|
| + }
|
| +
|
| + current = JSObject::cast(current->GetPrototype());
|
| + }
|
| +
|
| + // API callbacks can have per callback access exceptions.
|
| + switch (result->type()) {
|
| + case CALLBACKS: {
|
| + if (CheckAccessException(result, access_type)) {
|
| + return true;
|
| + }
|
| + break;
|
| + }
|
| + case INTERCEPTOR: {
|
| + // If the object has an interceptor, try real named properties.
|
| + // Overwrite the result to fetch the correct property later.
|
| + holder->LookupRealNamedProperty(name, result);
|
| + if (result->IsProperty()) {
|
| + if (CheckAccessException(result, access_type)) {
|
| + return true;
|
| + }
|
| + }
|
| + break;
|
| + }
|
| + default:
|
| + break;
|
| + }
|
| +
|
| + Top::ReportFailedAccessCheck(current, access_type);
|
| + return false;
|
| +}
|
| +
|
| +
|
| +// TODO(1095): we should traverse hidden prototype hierachy as well.
|
| +static bool CheckElementAccess(JSObject* obj,
|
| + uint32_t index,
|
| + v8::AccessType access_type) {
|
| + if (obj->IsAccessCheckNeeded() &&
|
| + !Top::MayIndexedAccess(obj, index, access_type)) {
|
| + return false;
|
| + }
|
| +
|
| + return true;
|
| +}
|
| +
|
| +
|
| // Enumerator used as indices into the array returned from GetOwnProperty
|
| enum PropertyDescriptorIndices {
|
| IS_ACCESSOR_INDEX,
|
| @@ -680,7 +770,7 @@
|
| // subsequent cases.
|
| Handle<JSValue> js_value = Handle<JSValue>::cast(obj);
|
| Handle<String> str(String::cast(js_value->value()));
|
| - Handle<String> substr = SubString(str, index, index+1, NOT_TENURED);
|
| + Handle<String> substr = SubString(str, index, index + 1, NOT_TENURED);
|
|
|
| elms->set(IS_ACCESSOR_INDEX, Heap::false_value());
|
| elms->set(VALUE_INDEX, *substr);
|
| @@ -693,8 +783,7 @@
|
| case JSObject::INTERCEPTED_ELEMENT:
|
| case JSObject::FAST_ELEMENT: {
|
| elms->set(IS_ACCESSOR_INDEX, Heap::false_value());
|
| - Handle<Object> element = GetElement(Handle<Object>(obj), index);
|
| - elms->set(VALUE_INDEX, *element);
|
| + elms->set(VALUE_INDEX, *GetElement(obj, index));
|
| elms->set(WRITABLE_INDEX, Heap::true_value());
|
| elms->set(ENUMERABLE_INDEX, Heap::true_value());
|
| elms->set(CONFIGURABLE_INDEX, Heap::true_value());
|
| @@ -702,7 +791,14 @@
|
| }
|
|
|
| case JSObject::DICTIONARY_ELEMENT: {
|
| - NumberDictionary* dictionary = obj->element_dictionary();
|
| + Handle<JSObject> holder = obj;
|
| + if (obj->IsJSGlobalProxy()) {
|
| + Object* proto = obj->GetPrototype();
|
| + if (proto->IsNull()) return Heap::undefined_value();
|
| + ASSERT(proto->IsJSGlobalObject());
|
| + holder = Handle<JSObject>(JSObject::cast(proto));
|
| + }
|
| + NumberDictionary* dictionary = holder->element_dictionary();
|
| int entry = dictionary->FindEntry(index);
|
| ASSERT(entry != NumberDictionary::kNotFound);
|
| PropertyDetails details = dictionary->DetailsAt(entry);
|
| @@ -712,14 +808,18 @@
|
| FixedArray* callbacks =
|
| FixedArray::cast(dictionary->ValueAt(entry));
|
| elms->set(IS_ACCESSOR_INDEX, Heap::true_value());
|
| - elms->set(GETTER_INDEX, callbacks->get(0));
|
| - elms->set(SETTER_INDEX, callbacks->get(1));
|
| + if (CheckElementAccess(*obj, index, v8::ACCESS_GET)) {
|
| + elms->set(GETTER_INDEX, callbacks->get(0));
|
| + }
|
| + if (CheckElementAccess(*obj, index, v8::ACCESS_SET)) {
|
| + elms->set(SETTER_INDEX, callbacks->get(1));
|
| + }
|
| break;
|
| }
|
| case NORMAL:
|
| // This is a data property.
|
| elms->set(IS_ACCESSOR_INDEX, Heap::false_value());
|
| - elms->set(VALUE_INDEX, dictionary->ValueAt(entry));
|
| + elms->set(VALUE_INDEX, *GetElement(obj, index));
|
| elms->set(WRITABLE_INDEX, Heap::ToBoolean(!details.IsReadOnly()));
|
| break;
|
| default:
|
| @@ -739,35 +839,41 @@
|
| if (!result.IsProperty()) {
|
| return Heap::undefined_value();
|
| }
|
| - if (result.type() == CALLBACKS) {
|
| - Object* structure = result.GetCallbackObject();
|
| - if (structure->IsProxy() || structure->IsAccessorInfo()) {
|
| - // Property that is internally implemented as a callback or
|
| - // an API defined callback.
|
| - Object* value;
|
| - { MaybeObject* maybe_value = obj->GetPropertyWithCallback(
|
| - *obj, structure, *name, result.holder());
|
| - if (!maybe_value->ToObject(&value)) return maybe_value;
|
| - }
|
| - elms->set(IS_ACCESSOR_INDEX, Heap::false_value());
|
| - elms->set(VALUE_INDEX, value);
|
| - elms->set(WRITABLE_INDEX, Heap::ToBoolean(!result.IsReadOnly()));
|
| - } else if (structure->IsFixedArray()) {
|
| - // __defineGetter__/__defineSetter__ callback.
|
| - elms->set(IS_ACCESSOR_INDEX, Heap::true_value());
|
| - elms->set(GETTER_INDEX, FixedArray::cast(structure)->get(0));
|
| - elms->set(SETTER_INDEX, FixedArray::cast(structure)->get(1));
|
| - } else {
|
| - return Heap::undefined_value();
|
| +
|
| + if (!CheckAccess(*obj, *name, &result, v8::ACCESS_HAS)) {
|
| + return Heap::false_value();
|
| + }
|
| +
|
| + elms->set(ENUMERABLE_INDEX, Heap::ToBoolean(!result.IsDontEnum()));
|
| + elms->set(CONFIGURABLE_INDEX, Heap::ToBoolean(!result.IsDontDelete()));
|
| +
|
| + bool is_js_accessor = (result.type() == CALLBACKS) &&
|
| + (result.GetCallbackObject()->IsFixedArray());
|
| +
|
| + if (is_js_accessor) {
|
| + // __defineGetter__/__defineSetter__ callback.
|
| + elms->set(IS_ACCESSOR_INDEX, Heap::true_value());
|
| +
|
| + FixedArray* structure = FixedArray::cast(result.GetCallbackObject());
|
| + if (CheckAccess(*obj, *name, &result, v8::ACCESS_GET)) {
|
| + elms->set(GETTER_INDEX, structure->get(0));
|
| }
|
| + if (CheckAccess(*obj, *name, &result, v8::ACCESS_SET)) {
|
| + elms->set(SETTER_INDEX, structure->get(1));
|
| + }
|
| } else {
|
| elms->set(IS_ACCESSOR_INDEX, Heap::false_value());
|
| - elms->set(VALUE_INDEX, result.GetLazyValue());
|
| elms->set(WRITABLE_INDEX, Heap::ToBoolean(!result.IsReadOnly()));
|
| +
|
| + PropertyAttributes attrs;
|
| + Object* value;
|
| + // GetProperty will check access and report any violations.
|
| + { MaybeObject* maybe_value = obj->GetProperty(*obj, &result, *name, &attrs);
|
| + if (!maybe_value->ToObject(&value)) return maybe_value;
|
| + }
|
| + elms->set(VALUE_INDEX, value);
|
| }
|
|
|
| - elms->set(ENUMERABLE_INDEX, Heap::ToBoolean(!result.IsDontEnum()));
|
| - elms->set(CONFIGURABLE_INDEX, Heap::ToBoolean(!result.IsDontDelete()));
|
| return *desc;
|
| }
|
|
|
| @@ -778,10 +884,17 @@
|
| return obj->PreventExtensions();
|
| }
|
|
|
| +
|
| static MaybeObject* Runtime_IsExtensible(Arguments args) {
|
| ASSERT(args.length() == 1);
|
| CONVERT_CHECKED(JSObject, obj, args[0]);
|
| - return obj->map()->is_extensible() ? Heap::true_value()
|
| + if (obj->IsJSGlobalProxy()) {
|
| + Object* proto = obj->GetPrototype();
|
| + if (proto->IsNull()) return Heap::false_value();
|
| + ASSERT(proto->IsJSGlobalObject());
|
| + obj = JSObject::cast(proto);
|
| + }
|
| + return obj->map()->is_extensible() ? Heap::true_value()
|
| : Heap::false_value();
|
| }
|
|
|
| @@ -976,7 +1089,7 @@
|
| const char* type = (lookup.IsReadOnly()) ? "const" : "var";
|
| return ThrowRedeclarationError(type, name);
|
| }
|
| - SetProperty(global, name, value, attributes);
|
| + RETURN_IF_EMPTY_HANDLE(SetProperty(global, name, value, attributes));
|
| } else {
|
| // If a property with this name does not already exist on the
|
| // global object add the property locally. We take special
|
| @@ -984,10 +1097,12 @@
|
| // of callbacks in the prototype chain (this rules out using
|
| // SetProperty). Also, we must use the handle-based version to
|
| // avoid GC issues.
|
| - IgnoreAttributesAndSetLocalProperty(global, name, value, attributes);
|
| + RETURN_IF_EMPTY_HANDLE(
|
| + SetLocalPropertyIgnoreAttributes(global, name, value, attributes));
|
| }
|
| }
|
|
|
| + ASSERT(!Top::has_pending_exception());
|
| return Heap::undefined_value();
|
| }
|
|
|
| @@ -1037,12 +1152,14 @@
|
| } else {
|
| // The holder is an arguments object.
|
| Handle<JSObject> arguments(Handle<JSObject>::cast(holder));
|
| - SetElement(arguments, index, initial_value);
|
| + Handle<Object> result = SetElement(arguments, index, initial_value);
|
| + if (result.is_null()) return Failure::Exception();
|
| }
|
| } else {
|
| // Slow case: The property is not in the FixedArray part of the context.
|
| Handle<JSObject> context_ext = Handle<JSObject>::cast(holder);
|
| - SetProperty(context_ext, name, initial_value, mode);
|
| + RETURN_IF_EMPTY_HANDLE(
|
| + SetProperty(context_ext, name, initial_value, mode));
|
| }
|
| }
|
|
|
| @@ -1069,8 +1186,7 @@
|
| ASSERT(!context_ext->HasLocalProperty(*name));
|
| Handle<Object> value(Heap::undefined_value());
|
| if (*initial_value != NULL) value = initial_value;
|
| - SetProperty(context_ext, name, value, mode);
|
| - ASSERT(context_ext->GetLocalPropertyAttribute(*name) == mode);
|
| + RETURN_IF_EMPTY_HANDLE(SetProperty(context_ext, name, value, mode));
|
| }
|
|
|
| return Heap::undefined_value();
|
| @@ -1099,7 +1215,7 @@
|
| // to assign to the property. When adding the property we take
|
| // special precautions to always add it as a local property even in
|
| // case of callbacks in the prototype chain (this rules out using
|
| - // SetProperty). We have IgnoreAttributesAndSetLocalProperty for
|
| + // SetProperty). We have SetLocalPropertyIgnoreAttributes for
|
| // this.
|
| // Note that objects can have hidden prototypes, so we need to traverse
|
| // the whole chain of hidden prototypes to do a 'local' lookup.
|
| @@ -1162,9 +1278,9 @@
|
|
|
| global = Top::context()->global();
|
| if (assign) {
|
| - return global->IgnoreAttributesAndSetLocalProperty(*name,
|
| - args[1],
|
| - attributes);
|
| + return global->SetLocalPropertyIgnoreAttributes(*name,
|
| + args[1],
|
| + attributes);
|
| }
|
| return Heap::undefined_value();
|
| }
|
| @@ -1190,13 +1306,13 @@
|
| // there, we add the property and take special precautions to always
|
| // add it as a local property even in case of callbacks in the
|
| // prototype chain (this rules out using SetProperty).
|
| - // We use IgnoreAttributesAndSetLocalProperty instead
|
| + // We use SetLocalPropertyIgnoreAttributes instead
|
| LookupResult lookup;
|
| global->LocalLookup(*name, &lookup);
|
| if (!lookup.IsProperty()) {
|
| - return global->IgnoreAttributesAndSetLocalProperty(*name,
|
| - *value,
|
| - attributes);
|
| + return global->SetLocalPropertyIgnoreAttributes(*name,
|
| + *value,
|
| + attributes);
|
| }
|
|
|
| // Determine if this is a redeclaration of something not
|
| @@ -1219,12 +1335,12 @@
|
| // with setting the value because the property is either absent or
|
| // read-only. We also have to do redo the lookup.
|
| HandleScope handle_scope;
|
| - Handle<GlobalObject>global(Top::context()->global());
|
| + Handle<GlobalObject> global(Top::context()->global());
|
|
|
| - // BUG 1213579: Handle the case where we have to set a read-only
|
| + // BUG 1213575: Handle the case where we have to set a read-only
|
| // property through an interceptor and only do it if it's
|
| // uninitialized, e.g. the hole. Nirk...
|
| - SetProperty(global, name, value, attributes);
|
| + RETURN_IF_EMPTY_HANDLE(SetProperty(global, name, value, attributes));
|
| return *value;
|
| }
|
|
|
| @@ -1306,7 +1422,7 @@
|
| // context.
|
| if (attributes == ABSENT) {
|
| Handle<JSObject> global = Handle<JSObject>(Top::context()->global());
|
| - SetProperty(global, name, value, NONE);
|
| + RETURN_IF_EMPTY_HANDLE(SetProperty(global, name, value, NONE));
|
| return *value;
|
| }
|
|
|
| @@ -1343,14 +1459,8 @@
|
| // The property was found in a different context extension object.
|
| // Set it if it is not a read-only property.
|
| if ((attributes & READ_ONLY) == 0) {
|
| - Handle<Object> set = SetProperty(context_ext, name, value, attributes);
|
| - // Setting a property might throw an exception. Exceptions
|
| - // are converted to empty handles in handle operations. We
|
| - // need to convert back to exceptions here.
|
| - if (set.is_null()) {
|
| - ASSERT(Top::has_pending_exception());
|
| - return Failure::Exception();
|
| - }
|
| + RETURN_IF_EMPTY_HANDLE(
|
| + SetProperty(context_ext, name, value, attributes));
|
| }
|
| }
|
|
|
| @@ -1467,27 +1577,27 @@
|
| PropertyAttributes writable =
|
| static_cast<PropertyAttributes>(DONT_ENUM | DONT_DELETE);
|
| MaybeObject* result;
|
| - result = regexp->IgnoreAttributesAndSetLocalProperty(Heap::source_symbol(),
|
| - source,
|
| - final);
|
| + result = regexp->SetLocalPropertyIgnoreAttributes(Heap::source_symbol(),
|
| + source,
|
| + final);
|
| ASSERT(!result->IsFailure());
|
| - result = regexp->IgnoreAttributesAndSetLocalProperty(Heap::global_symbol(),
|
| - global,
|
| - final);
|
| + result = regexp->SetLocalPropertyIgnoreAttributes(Heap::global_symbol(),
|
| + global,
|
| + final);
|
| ASSERT(!result->IsFailure());
|
| result =
|
| - regexp->IgnoreAttributesAndSetLocalProperty(Heap::ignore_case_symbol(),
|
| - ignoreCase,
|
| - final);
|
| + regexp->SetLocalPropertyIgnoreAttributes(Heap::ignore_case_symbol(),
|
| + ignoreCase,
|
| + final);
|
| ASSERT(!result->IsFailure());
|
| - result = regexp->IgnoreAttributesAndSetLocalProperty(Heap::multiline_symbol(),
|
| - multiline,
|
| - final);
|
| + result = regexp->SetLocalPropertyIgnoreAttributes(Heap::multiline_symbol(),
|
| + multiline,
|
| + final);
|
| ASSERT(!result->IsFailure());
|
| result =
|
| - regexp->IgnoreAttributesAndSetLocalProperty(Heap::last_index_symbol(),
|
| - Smi::FromInt(0),
|
| - writable);
|
| + regexp->SetLocalPropertyIgnoreAttributes(Heap::last_index_symbol(),
|
| + Smi::FromInt(0),
|
| + writable);
|
| ASSERT(!result->IsFailure());
|
| USE(result);
|
| return regexp;
|
| @@ -1743,6 +1853,7 @@
|
| // Array, and Object, and some web code
|
| // doesn't like seeing source code for constructors.
|
| target->shared()->set_script(Heap::undefined_value());
|
| + target->shared()->code()->set_optimizable(false);
|
| // Clear the optimization hints related to the compiled code as these are no
|
| // longer valid when the code is overwritten.
|
| target->shared()->ClearThisPropertyAssignmentsInfo();
|
| @@ -3484,8 +3595,10 @@
|
| HandleScope scope;
|
| Handle<String> str = args.at<String>(0);
|
| int index = Smi::cast(args[1])->value();
|
| - Handle<Object> result = GetCharAt(str, index);
|
| - return *result;
|
| + if (index >= 0 && index < str->length()) {
|
| + Handle<Object> result = GetCharAt(str, index);
|
| + return *result;
|
| + }
|
| }
|
|
|
| // Fall back to GetObjectProperty.
|
| @@ -3493,7 +3606,12 @@
|
| args.at<Object>(1));
|
| }
|
|
|
| -
|
| +// Implements part of 8.12.9 DefineOwnProperty.
|
| +// There are 3 cases that lead here:
|
| +// Step 4b - define a new accessor property.
|
| +// Steps 9c & 12 - replace an existing data property with an accessor property.
|
| +// Step 12 - update an existing accessor property with an accessor or generic
|
| +// descriptor.
|
| static MaybeObject* Runtime_DefineOrRedefineAccessorProperty(Arguments args) {
|
| ASSERT(args.length() == 5);
|
| HandleScope scope;
|
| @@ -3525,6 +3643,12 @@
|
| return obj->DefineAccessor(name, flag_setter->value() == 0, fun, attr);
|
| }
|
|
|
| +// Implements part of 8.12.9 DefineOwnProperty.
|
| +// There are 3 cases that lead here:
|
| +// Step 4a - define a new data property.
|
| +// Steps 9b & 12 - replace an existing accessor property with a data property.
|
| +// Step 12 - update an existing data property with a data or generic
|
| +// descriptor.
|
| static MaybeObject* Runtime_DefineOrRedefineDataProperty(Arguments args) {
|
| ASSERT(args.length() == 4);
|
| HandleScope scope;
|
| @@ -3548,12 +3672,20 @@
|
| if (((unchecked & (DONT_DELETE | DONT_ENUM | READ_ONLY)) != 0) &&
|
| is_element) {
|
| // Normalize the elements to enable attributes on the property.
|
| + if (js_object->IsJSGlobalProxy()) {
|
| + Handle<Object> proto(js_object->GetPrototype());
|
| + // If proxy is detached, ignore the assignment. Alternatively,
|
| + // we could throw an exception.
|
| + if (proto->IsNull()) return *obj_value;
|
| + js_object = Handle<JSObject>::cast(proto);
|
| + }
|
| NormalizeElements(js_object);
|
| Handle<NumberDictionary> dictionary(js_object->element_dictionary());
|
| // Make sure that we never go back to fast case.
|
| dictionary->set_requires_slow_elements();
|
| PropertyDetails details = PropertyDetails(attr, NORMAL);
|
| NumberDictionarySet(dictionary, index, obj_value, details);
|
| + return *obj_value;
|
| }
|
|
|
| LookupResult result;
|
| @@ -3568,15 +3700,20 @@
|
| if (result.IsProperty() &&
|
| (attr != result.GetAttributes() || result.type() == CALLBACKS)) {
|
| // New attributes - normalize to avoid writing to instance descriptor
|
| + if (js_object->IsJSGlobalProxy()) {
|
| + // Since the result is a property, the prototype will exist so
|
| + // we don't have to check for null.
|
| + js_object = Handle<JSObject>(JSObject::cast(js_object->GetPrototype()));
|
| + }
|
| NormalizeProperties(js_object, 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 js_object->SetLocalPropertyIgnoreAttributes(*name,
|
| + *obj_value,
|
| + attr);
|
| }
|
|
|
| - return Runtime::SetObjectProperty(js_object, name, obj_value, attr);
|
| + return Runtime::ForceSetObjectProperty(js_object, name, obj_value, attr);
|
| }
|
|
|
|
|
| @@ -3674,9 +3811,9 @@
|
| } else {
|
| Handle<String> key_string = Handle<String>::cast(key);
|
| key_string->TryFlatten();
|
| - return js_object->IgnoreAttributesAndSetLocalProperty(*key_string,
|
| - *value,
|
| - attr);
|
| + return js_object->SetLocalPropertyIgnoreAttributes(*key_string,
|
| + *value,
|
| + attr);
|
| }
|
| }
|
|
|
| @@ -3689,7 +3826,7 @@
|
| if (name->AsArrayIndex(&index)) {
|
| return js_object->SetElement(index, *value);
|
| } else {
|
| - return js_object->IgnoreAttributesAndSetLocalProperty(*name, *value, attr);
|
| + return js_object->SetLocalPropertyIgnoreAttributes(*name, *value, attr);
|
| }
|
| }
|
|
|
| @@ -3771,17 +3908,20 @@
|
| }
|
|
|
| return object->
|
| - IgnoreAttributesAndSetLocalProperty(name, args[2], attributes);
|
| + SetLocalPropertyIgnoreAttributes(name, args[2], attributes);
|
| }
|
|
|
|
|
| static MaybeObject* Runtime_DeleteProperty(Arguments args) {
|
| NoHandleAllocation ha;
|
| - ASSERT(args.length() == 2);
|
| + ASSERT(args.length() == 3);
|
|
|
| CONVERT_CHECKED(JSObject, object, args[0]);
|
| CONVERT_CHECKED(String, key, args[1]);
|
| - return object->DeleteProperty(key, JSObject::NORMAL_DELETION);
|
| + CONVERT_SMI_CHECKED(strict, args[2]);
|
| + return object->DeleteProperty(key, strict == kStrictMode
|
| + ? JSObject::STRICT_DELETION
|
| + : JSObject::NORMAL_DELETION);
|
| }
|
|
|
|
|
| @@ -4075,6 +4215,22 @@
|
| CONVERT_CHECKED(JSObject, raw_object, args[0]);
|
| HandleScope scope;
|
| Handle<JSObject> object(raw_object);
|
| +
|
| + if (object->IsJSGlobalProxy()) {
|
| + // Do access checks before going to the global object.
|
| + if (object->IsAccessCheckNeeded() &&
|
| + !Top::MayNamedAccess(*object, Heap::undefined_value(),
|
| + v8::ACCESS_KEYS)) {
|
| + Top::ReportFailedAccessCheck(*object, v8::ACCESS_KEYS);
|
| + return *Factory::NewJSArray(0);
|
| + }
|
| +
|
| + Handle<Object> proto(object->GetPrototype());
|
| + // If proxy is detached we simply return an empty array.
|
| + if (proto->IsNull()) return *Factory::NewJSArray(0);
|
| + object = Handle<JSObject>::cast(proto);
|
| + }
|
| +
|
| Handle<FixedArray> contents = GetKeysInFixedArrayFor(object,
|
| LOCAL_ONLY);
|
| // Some fast paths through GetKeysInFixedArrayFor reuse a cached
|
| @@ -4164,7 +4320,7 @@
|
|
|
| ASSERT(args.length() == 1);
|
| Handle<Object> object = args.at<Object>(0);
|
| - if (object->IsJSObject()) {
|
| + if (object->IsJSObject() && !object->IsJSGlobalProxy()) {
|
| Handle<JSObject> js_object = Handle<JSObject>::cast(object);
|
| NormalizeProperties(js_object, CLEAR_INOBJECT_PROPERTIES, 0);
|
| }
|
| @@ -4615,12 +4771,12 @@
|
| }
|
|
|
|
|
| -template <typename Char, typename StringType>
|
| +template <typename Char, typename StringType, bool comma>
|
| static MaybeObject* SlowQuoteJsonString(Vector<const Char> characters) {
|
| int length = characters.length();
|
| const Char* read_cursor = characters.start();
|
| const Char* end = read_cursor + length;
|
| - const int kSpaceForQuotes = 2;
|
| + const int kSpaceForQuotes = 2 + (comma ? 1 :0);
|
| int quoted_length = kSpaceForQuotes;
|
| while (read_cursor < end) {
|
| Char c = *(read_cursor++);
|
| @@ -4639,6 +4795,7 @@
|
|
|
| Char* write_cursor = reinterpret_cast<Char*>(
|
| new_string->address() + SeqAsciiString::kHeaderSize);
|
| + if (comma) *(write_cursor++) = ',';
|
| *(write_cursor++) = '"';
|
|
|
| read_cursor = characters.start();
|
| @@ -4660,14 +4817,14 @@
|
| }
|
|
|
|
|
| -template <typename Char, typename StringType>
|
| +template <typename Char, typename StringType, bool comma>
|
| static MaybeObject* QuoteJsonString(Vector<const Char> characters) {
|
| int length = characters.length();
|
| Counters::quote_json_char_count.Increment(length);
|
| - const int kSpaceForQuotes = 2;
|
| + const int kSpaceForQuotes = 2 + (comma ? 1 :0);
|
| int worst_case_length = length * kJsonQuoteWorstCaseBlowup + kSpaceForQuotes;
|
| if (worst_case_length > kMaxGuaranteedNewSpaceString) {
|
| - return SlowQuoteJsonString<Char, StringType>(characters);
|
| + return SlowQuoteJsonString<Char, StringType, comma>(characters);
|
| }
|
|
|
| MaybeObject* new_alloc = AllocateRawString<StringType>(worst_case_length);
|
| @@ -4680,7 +4837,7 @@
|
| // handle it being allocated in old space as may happen in the third
|
| // attempt. See CALL_AND_RETRY in heap-inl.h and similar code in
|
| // CEntryStub::GenerateCore.
|
| - return SlowQuoteJsonString<Char, StringType>(characters);
|
| + return SlowQuoteJsonString<Char, StringType, comma>(characters);
|
| }
|
| StringType* new_string = StringType::cast(new_object);
|
| ASSERT(Heap::new_space()->Contains(new_string));
|
| @@ -4688,6 +4845,7 @@
|
| STATIC_ASSERT(SeqTwoByteString::kHeaderSize == SeqAsciiString::kHeaderSize);
|
| Char* write_cursor = reinterpret_cast<Char*>(
|
| new_string->address() + SeqAsciiString::kHeaderSize);
|
| + if (comma) *(write_cursor++) = ',';
|
| *(write_cursor++) = '"';
|
|
|
| const Char* read_cursor = characters.start();
|
| @@ -4738,14 +4896,33 @@
|
| ASSERT(str->IsFlat());
|
| }
|
| if (str->IsTwoByteRepresentation()) {
|
| - return QuoteJsonString<uc16, SeqTwoByteString>(str->ToUC16Vector());
|
| + return QuoteJsonString<uc16, SeqTwoByteString, false>(str->ToUC16Vector());
|
| } else {
|
| - return QuoteJsonString<char, SeqAsciiString>(str->ToAsciiVector());
|
| + return QuoteJsonString<char, SeqAsciiString, false>(str->ToAsciiVector());
|
| }
|
| }
|
|
|
|
|
| +static MaybeObject* Runtime_QuoteJSONStringComma(Arguments args) {
|
| + NoHandleAllocation ha;
|
| + CONVERT_CHECKED(String, str, args[0]);
|
| + if (!str->IsFlat()) {
|
| + MaybeObject* try_flatten = str->TryFlatten();
|
| + Object* flat;
|
| + if (!try_flatten->ToObject(&flat)) {
|
| + return try_flatten;
|
| + }
|
| + str = String::cast(flat);
|
| + ASSERT(str->IsFlat());
|
| + }
|
| + if (str->IsTwoByteRepresentation()) {
|
| + return QuoteJsonString<uc16, SeqTwoByteString, true>(str->ToUC16Vector());
|
| + } else {
|
| + return QuoteJsonString<char, SeqAsciiString, true>(str->ToAsciiVector());
|
| + }
|
| +}
|
|
|
| +
|
| static MaybeObject* Runtime_StringParseInt(Arguments args) {
|
| NoHandleAllocation ha;
|
|
|
| @@ -5643,6 +5820,89 @@
|
| }
|
|
|
|
|
| +static MaybeObject* Runtime_StringBuilderJoin(Arguments args) {
|
| + NoHandleAllocation ha;
|
| + ASSERT(args.length() == 3);
|
| + CONVERT_CHECKED(JSArray, array, args[0]);
|
| + if (!args[1]->IsSmi()) {
|
| + Top::context()->mark_out_of_memory();
|
| + return Failure::OutOfMemoryException();
|
| + }
|
| + int array_length = Smi::cast(args[1])->value();
|
| + CONVERT_CHECKED(String, separator, args[2]);
|
| +
|
| + if (!array->HasFastElements()) {
|
| + return Top::Throw(Heap::illegal_argument_symbol());
|
| + }
|
| + FixedArray* fixed_array = FixedArray::cast(array->elements());
|
| + if (fixed_array->length() < array_length) {
|
| + array_length = fixed_array->length();
|
| + }
|
| +
|
| + if (array_length == 0) {
|
| + return Heap::empty_string();
|
| + } else if (array_length == 1) {
|
| + Object* first = fixed_array->get(0);
|
| + if (first->IsString()) return first;
|
| + }
|
| +
|
| + int separator_length = separator->length();
|
| + int max_nof_separators =
|
| + (String::kMaxLength + separator_length - 1) / separator_length;
|
| + if (max_nof_separators < (array_length - 1)) {
|
| + Top::context()->mark_out_of_memory();
|
| + return Failure::OutOfMemoryException();
|
| + }
|
| + int length = (array_length - 1) * separator_length;
|
| + for (int i = 0; i < array_length; i++) {
|
| + Object* element_obj = fixed_array->get(i);
|
| + if (!element_obj->IsString()) {
|
| + // TODO(1161): handle this case.
|
| + return Top::Throw(Heap::illegal_argument_symbol());
|
| + }
|
| + String* element = String::cast(element_obj);
|
| + int increment = element->length();
|
| + if (increment > String::kMaxLength - length) {
|
| + Top::context()->mark_out_of_memory();
|
| + return Failure::OutOfMemoryException();
|
| + }
|
| + length += increment;
|
| + }
|
| +
|
| + Object* object;
|
| + { MaybeObject* maybe_object = Heap::AllocateRawTwoByteString(length);
|
| + if (!maybe_object->ToObject(&object)) return maybe_object;
|
| + }
|
| + SeqTwoByteString* answer = SeqTwoByteString::cast(object);
|
| +
|
| + uc16* sink = answer->GetChars();
|
| +#ifdef DEBUG
|
| + uc16* end = sink + length;
|
| +#endif
|
| +
|
| + String* first = String::cast(fixed_array->get(0));
|
| + int first_length = first->length();
|
| + String::WriteToFlat(first, sink, 0, first_length);
|
| + sink += first_length;
|
| +
|
| + for (int i = 1; i < array_length; i++) {
|
| + ASSERT(sink + separator_length <= end);
|
| + String::WriteToFlat(separator, sink, 0, separator_length);
|
| + sink += separator_length;
|
| +
|
| + String* element = String::cast(fixed_array->get(i));
|
| + int element_length = element->length();
|
| + ASSERT(sink + element_length <= end);
|
| + String::WriteToFlat(element, sink, 0, element_length);
|
| + sink += element_length;
|
| + }
|
| + ASSERT(sink == end);
|
| +
|
| + ASSERT(!answer->HasOnlyAsciiChars()); // Use %_FastAsciiArrayJoin instead.
|
| + return answer;
|
| +}
|
| +
|
| +
|
| static MaybeObject* Runtime_NumberOr(Arguments args) {
|
| NoHandleAllocation ha;
|
| ASSERT(args.length() == 2);
|
| @@ -6542,28 +6802,50 @@
|
| return *result;
|
| }
|
|
|
| +
|
| static MaybeObject* Runtime_NewObjectFromBound(Arguments args) {
|
| HandleScope scope;
|
| ASSERT(args.length() == 2);
|
| + // First argument is a function to use as a constructor.
|
| CONVERT_ARG_CHECKED(JSFunction, function, 0);
|
| - CONVERT_ARG_CHECKED(JSArray, params, 1);
|
|
|
| - RUNTIME_ASSERT(params->HasFastElements());
|
| - FixedArray* fixed = FixedArray::cast(params->elements());
|
| + // Second argument is either null or an array of bound arguments.
|
| + FixedArray* bound_args = NULL;
|
| + int bound_argc = 0;
|
| + if (!args[1]->IsNull()) {
|
| + CONVERT_ARG_CHECKED(JSArray, params, 1);
|
| + RUNTIME_ASSERT(params->HasFastElements());
|
| + bound_args = FixedArray::cast(params->elements());
|
| + bound_argc = Smi::cast(params->length())->value();
|
| + }
|
|
|
| - int fixed_length = Smi::cast(params->length())->value();
|
| - SmartPointer<Object**> param_data(NewArray<Object**>(fixed_length));
|
| - for (int i = 0; i < fixed_length; i++) {
|
| - Handle<Object> val = Handle<Object>(fixed->get(i));
|
| + // Find frame containing arguments passed to the caller.
|
| + JavaScriptFrameIterator it;
|
| + JavaScriptFrame* frame = it.frame();
|
| + ASSERT(!frame->is_optimized());
|
| + it.AdvanceToArgumentsFrame();
|
| + frame = it.frame();
|
| + int argc = frame->GetProvidedParametersCount();
|
| +
|
| + // Prepend bound arguments to caller's arguments.
|
| + int total_argc = bound_argc + argc;
|
| + SmartPointer<Object**> param_data(NewArray<Object**>(total_argc));
|
| + for (int i = 0; i < bound_argc; i++) {
|
| + Handle<Object> val = Handle<Object>(bound_args->get(i));
|
| param_data[i] = val.location();
|
| }
|
| + for (int i = 0; i < argc; i++) {
|
| + Handle<Object> val = Handle<Object>(frame->GetParameter(i));
|
| + param_data[bound_argc + i] = val.location();
|
| + }
|
|
|
| bool exception = false;
|
| - Handle<Object> result = Execution::New(
|
| - function, fixed_length, *param_data, &exception);
|
| + Handle<Object> result =
|
| + Execution::New(function, total_argc, *param_data, &exception);
|
| if (exception) {
|
| return Failure::Exception();
|
| }
|
| +
|
| ASSERT(!result.is_null());
|
| return *result;
|
| }
|
| @@ -6714,12 +6996,24 @@
|
| // code from the full compiler.
|
| if (!function->shared()->code()->optimizable() ||
|
| Debug::has_break_points()) {
|
| + if (FLAG_trace_opt) {
|
| + PrintF("[failed to optimize ");
|
| + function->PrintName();
|
| + PrintF(": is code optimizable: %s, is debugger enabled: %s]\n",
|
| + function->shared()->code()->optimizable() ? "T" : "F",
|
| + Debug::has_break_points() ? "T" : "F");
|
| + }
|
| function->ReplaceCode(function->shared()->code());
|
| return function->code();
|
| }
|
| if (CompileOptimized(function, AstNode::kNoNumber)) {
|
| return function->code();
|
| }
|
| + if (FLAG_trace_opt) {
|
| + PrintF("[failed to optimize ");
|
| + function->PrintName();
|
| + PrintF(": optimized compilation failed]\n");
|
| + }
|
| function->ReplaceCode(function->shared()->code());
|
| return Failure::Exception();
|
| }
|
| @@ -6748,7 +7042,7 @@
|
| Handle<JSFunction> function(JSFunction::cast(frame->function()));
|
| Handle<Object> arguments;
|
| for (int i = frame->ComputeExpressionsCount() - 1; i >= 0; --i) {
|
| - if (frame->GetExpression(i) == Heap::the_hole_value()) {
|
| + if (frame->GetExpression(i) == Heap::arguments_marker()) {
|
| if (arguments.is_null()) {
|
| // FunctionGetArguments can't throw an exception, so cast away the
|
| // doubt with an assert.
|
| @@ -6859,7 +7153,7 @@
|
| // the AST id matching the PC.
|
| Address start = unoptimized->instruction_start();
|
| unsigned target_pc_offset = static_cast<unsigned>(frame->pc() - start);
|
| - Address table_cursor = start + unoptimized->stack_check_table_start();
|
| + Address table_cursor = start + unoptimized->stack_check_table_offset();
|
| uint32_t table_length = Memory::uint32_at(table_cursor);
|
| table_cursor += kIntSize;
|
| for (unsigned i = 0; i < table_length; ++i) {
|
| @@ -6910,15 +7204,9 @@
|
| Handle<Code> check_code = check_stub.GetCode();
|
| Handle<Code> replacement_code(
|
| Builtins::builtin(Builtins::OnStackReplacement));
|
| - // Iterate the unoptimized code and revert all the patched stack checks.
|
| - for (RelocIterator it(*unoptimized, RelocInfo::kCodeTargetMask);
|
| - !it.done();
|
| - it.next()) {
|
| - RelocInfo* rinfo = it.rinfo();
|
| - if (rinfo->target_address() == replacement_code->entry()) {
|
| - Deoptimizer::RevertStackCheckCode(rinfo, *check_code);
|
| - }
|
| - }
|
| + Deoptimizer::RevertStackCheckCode(*unoptimized,
|
| + *check_code,
|
| + *replacement_code);
|
|
|
| // Allow OSR only at nesting level zero again.
|
| unoptimized->set_allow_osr_at_loop_nesting_level(0);
|
| @@ -7015,7 +7303,7 @@
|
| }
|
|
|
|
|
| -static MaybeObject* Runtime_LookupContext(Arguments args) {
|
| +static MaybeObject* Runtime_DeleteContextSlot(Arguments args) {
|
| HandleScope scope;
|
| ASSERT(args.length() == 2);
|
|
|
| @@ -7025,16 +7313,31 @@
|
| int index;
|
| PropertyAttributes attributes;
|
| ContextLookupFlags flags = FOLLOW_CHAINS;
|
| - Handle<Object> holder =
|
| - context->Lookup(name, flags, &index, &attributes);
|
| + Handle<Object> holder = context->Lookup(name, flags, &index, &attributes);
|
|
|
| - if (index < 0 && !holder.is_null()) {
|
| - ASSERT(holder->IsJSObject());
|
| - return *holder;
|
| + // If the slot was not found the result is true.
|
| + if (holder.is_null()) {
|
| + return Heap::true_value();
|
| }
|
|
|
| - // No intermediate context found. Use global object by default.
|
| - return Top::context()->global();
|
| + // If the slot was found in a context, it should be DONT_DELETE.
|
| + if (holder->IsContext()) {
|
| + return Heap::false_value();
|
| + }
|
| +
|
| + // The slot was found in a JSObject, either a context extension object,
|
| + // the global object, or an arguments object. Try to delete it
|
| + // (respecting DONT_DELETE). For consistency with V8's usual behavior,
|
| + // which allows deleting all parameters in functions that mention
|
| + // 'arguments', we do this even for the case of slots found on an
|
| + // arguments object. The slot was found on an arguments object if the
|
| + // index is non-negative.
|
| + Handle<JSObject> object = Handle<JSObject>::cast(holder);
|
| + if (index >= 0) {
|
| + return object->DeleteElement(index, JSObject::NORMAL_DELETION);
|
| + } else {
|
| + return object->DeleteProperty(*name, JSObject::NORMAL_DELETION);
|
| + }
|
| }
|
|
|
|
|
| @@ -7107,8 +7410,7 @@
|
| int index;
|
| PropertyAttributes attributes;
|
| ContextLookupFlags flags = FOLLOW_CHAINS;
|
| - Handle<Object> holder =
|
| - context->Lookup(name, flags, &index, &attributes);
|
| + Handle<Object> holder = context->Lookup(name, flags, &index, &attributes);
|
|
|
| // If the index is non-negative, the slot has been found in a local
|
| // variable or a parameter. Read it from the context object or the
|
| @@ -7175,19 +7477,23 @@
|
| int index;
|
| PropertyAttributes attributes;
|
| ContextLookupFlags flags = FOLLOW_CHAINS;
|
| - Handle<Object> holder =
|
| - context->Lookup(name, flags, &index, &attributes);
|
| + Handle<Object> holder = context->Lookup(name, flags, &index, &attributes);
|
|
|
| if (index >= 0) {
|
| if (holder->IsContext()) {
|
| // Ignore if read_only variable.
|
| if ((attributes & READ_ONLY) == 0) {
|
| - Handle<Context>::cast(holder)->set(index, *value);
|
| + // Context is a fixed array and set cannot fail.
|
| + Context::cast(*holder)->set(index, *value);
|
| }
|
| } else {
|
| ASSERT((attributes & READ_ONLY) == 0);
|
| - Handle<JSObject>::cast(holder)->SetElement(index, *value)->
|
| - ToObjectUnchecked();
|
| + Handle<Object> result =
|
| + SetElement(Handle<JSObject>::cast(holder), index, value);
|
| + if (result.is_null()) {
|
| + ASSERT(Top::has_pending_exception());
|
| + return Failure::Exception();
|
| + }
|
| }
|
| return *value;
|
| }
|
| @@ -7210,14 +7516,7 @@
|
| // extension object itself.
|
| if ((attributes & READ_ONLY) == 0 ||
|
| (context_ext->GetLocalPropertyAttribute(*name) == ABSENT)) {
|
| - Handle<Object> set = SetProperty(context_ext, name, value, NONE);
|
| - if (set.is_null()) {
|
| - // Failure::Exception is converted to a null handle in the
|
| - // handle-based methods such as SetProperty. We therefore need
|
| - // to convert null handles back to exceptions.
|
| - ASSERT(Top::has_pending_exception());
|
| - return Failure::Exception();
|
| - }
|
| + RETURN_IF_EMPTY_HANDLE(SetProperty(context_ext, name, value, NONE));
|
| }
|
| return *value;
|
| }
|
| @@ -7516,7 +7815,8 @@
|
| Handle<Context> context(Top::context()->global_context());
|
| Handle<SharedFunctionInfo> shared = Compiler::CompileEval(source,
|
| context,
|
| - true);
|
| + true,
|
| + kNonStrictMode);
|
| if (shared.is_null()) return Failure::Exception();
|
| Handle<JSFunction> fun =
|
| Factory::NewFunctionFromSharedFunctionInfo(shared, context, NOT_TENURED);
|
| @@ -7525,13 +7825,15 @@
|
|
|
|
|
| static ObjectPair CompileGlobalEval(Handle<String> source,
|
| - Handle<Object> receiver) {
|
| + Handle<Object> receiver,
|
| + StrictModeFlag mode) {
|
| // Deal with a normal eval call with a string argument. Compile it
|
| // and return the compiled function bound in the local context.
|
| Handle<SharedFunctionInfo> shared = Compiler::CompileEval(
|
| source,
|
| Handle<Context>(Top::context()),
|
| - Top::context()->IsGlobalContext());
|
| + Top::context()->IsGlobalContext(),
|
| + mode);
|
| if (shared.is_null()) return MakePair(Failure::Exception(), NULL);
|
| Handle<JSFunction> compiled = Factory::NewFunctionFromSharedFunctionInfo(
|
| shared,
|
| @@ -7542,7 +7844,7 @@
|
|
|
|
|
| static ObjectPair Runtime_ResolvePossiblyDirectEval(Arguments args) {
|
| - ASSERT(args.length() == 3);
|
| + ASSERT(args.length() == 4);
|
| if (!args[0]->IsJSFunction()) {
|
| return MakePair(Top::ThrowIllegalOperation(), NULL);
|
| }
|
| @@ -7606,12 +7908,16 @@
|
| return MakePair(*callee, Top::context()->global()->global_receiver());
|
| }
|
|
|
| - return CompileGlobalEval(args.at<String>(1), args.at<Object>(2));
|
| + ASSERT(args[3]->IsSmi());
|
| + return CompileGlobalEval(args.at<String>(1),
|
| + args.at<Object>(2),
|
| + static_cast<StrictModeFlag>(
|
| + Smi::cast(args[3])->value()));
|
| }
|
|
|
|
|
| static ObjectPair Runtime_ResolvePossiblyDirectEvalNoLookup(Arguments args) {
|
| - ASSERT(args.length() == 3);
|
| + ASSERT(args.length() == 4);
|
| if (!args[0]->IsJSFunction()) {
|
| return MakePair(Top::ThrowIllegalOperation(), NULL);
|
| }
|
| @@ -7626,7 +7932,11 @@
|
| return MakePair(*callee, Top::context()->global()->global_receiver());
|
| }
|
|
|
| - return CompileGlobalEval(args.at<String>(1), args.at<Object>(2));
|
| + ASSERT(args[3]->IsSmi());
|
| + return CompileGlobalEval(args.at<String>(1),
|
| + args.at<Object>(2),
|
| + static_cast<StrictModeFlag>(
|
| + Smi::cast(args[3])->value()));
|
| }
|
|
|
|
|
| @@ -8057,7 +8367,7 @@
|
| }
|
| }
|
|
|
| - // Allocate an empty array, will set length and content later.
|
| + // Allocate an empty array, will set map, length, and content later.
|
| Handle<JSArray> result = Factory::NewJSArray(0);
|
|
|
| uint32_t estimate_nof_elements = IterateArguments(arguments, NULL);
|
| @@ -8066,23 +8376,20 @@
|
| // dictionary.
|
| bool fast_case = (estimate_nof_elements * 2) >= result_length;
|
|
|
| + Handle<Map> map;
|
| Handle<FixedArray> storage;
|
| if (fast_case) {
|
| // The backing storage array must have non-existing elements to
|
| // preserve holes across concat operations.
|
| + map = Factory::GetFastElementsMap(Handle<Map>(result->map()));
|
| storage = Factory::NewFixedArrayWithHoles(result_length);
|
| - Handle<Map> fast_map =
|
| - Factory::GetFastElementsMap(Handle<Map>(result->map()));
|
| - result->set_map(*fast_map);
|
| } else {
|
| + map = Factory::GetSlowElementsMap(Handle<Map>(result->map()));
|
| // TODO(126): move 25% pre-allocation logic into Dictionary::Allocate
|
| uint32_t at_least_space_for = estimate_nof_elements +
|
| (estimate_nof_elements >> 2);
|
| storage = Handle<FixedArray>::cast(
|
| - Factory::NewNumberDictionary(at_least_space_for));
|
| - Handle<Map> slow_map =
|
| - Factory::GetSlowElementsMap(Handle<Map>(result->map()));
|
| - result->set_map(*slow_map);
|
| + Factory::NewNumberDictionary(at_least_space_for));
|
| }
|
|
|
| Handle<Object> len = Factory::NewNumber(static_cast<double>(result_length));
|
| @@ -8091,8 +8398,12 @@
|
|
|
| IterateArguments(arguments, &visitor);
|
|
|
| + // Please note:
|
| + // - the storage might have been changed in the visitor;
|
| + // - the map and the storage must be set together to avoid breaking
|
| + // the invariant that the map describes the array's elements.
|
| + result->set_map(*map);
|
| result->set_length(*len);
|
| - // Please note the storage might have changed in the visitor.
|
| result->set_elements(*visitor.storage());
|
|
|
| return *result;
|
| @@ -8686,7 +8997,7 @@
|
| // If we are inspecting an optimized frame use undefined as the
|
| // value for all locals.
|
| //
|
| - // TODO(3141533): We should be able to get the correct values
|
| + // TODO(1140): We should be able to get the correct values
|
| // for locals in optimized frames.
|
| locals->set(i * 2 + 1, Heap::undefined_value());
|
| } else if (i < info.number_of_stack_slots()) {
|
| @@ -8850,7 +9161,7 @@
|
|
|
|
|
| // Copy all the context locals into an object used to materialize a scope.
|
| -static void CopyContextLocalsToScopeObject(
|
| +static bool CopyContextLocalsToScopeObject(
|
| Handle<SerializedScopeInfo> serialized_scope_info,
|
| ScopeInfo<>& scope_info,
|
| Handle<Context> context,
|
| @@ -8864,11 +9175,15 @@
|
|
|
| // Don't include the arguments shadow (.arguments) context variable.
|
| if (*scope_info.context_slot_name(i) != Heap::arguments_shadow_symbol()) {
|
| - SetProperty(scope_object,
|
| - scope_info.context_slot_name(i),
|
| - Handle<Object>(context->get(context_index)), NONE);
|
| + RETURN_IF_EMPTY_HANDLE_VALUE(
|
| + SetProperty(scope_object,
|
| + scope_info.context_slot_name(i),
|
| + Handle<Object>(context->get(context_index)), NONE),
|
| + false);
|
| }
|
| }
|
| +
|
| + return true;
|
| }
|
|
|
|
|
| @@ -8886,23 +9201,29 @@
|
|
|
| // First fill all parameters.
|
| for (int i = 0; i < scope_info.number_of_parameters(); ++i) {
|
| - SetProperty(local_scope,
|
| - scope_info.parameter_name(i),
|
| - Handle<Object>(frame->GetParameter(i)), NONE);
|
| + RETURN_IF_EMPTY_HANDLE_VALUE(
|
| + SetProperty(local_scope,
|
| + scope_info.parameter_name(i),
|
| + Handle<Object>(frame->GetParameter(i)), NONE),
|
| + Handle<JSObject>());
|
| }
|
|
|
| // Second fill all stack locals.
|
| for (int i = 0; i < scope_info.number_of_stack_slots(); i++) {
|
| - SetProperty(local_scope,
|
| - scope_info.stack_slot_name(i),
|
| - Handle<Object>(frame->GetExpression(i)), NONE);
|
| + RETURN_IF_EMPTY_HANDLE_VALUE(
|
| + SetProperty(local_scope,
|
| + scope_info.stack_slot_name(i),
|
| + Handle<Object>(frame->GetExpression(i)), NONE),
|
| + Handle<JSObject>());
|
| }
|
|
|
| // Third fill all context locals.
|
| Handle<Context> frame_context(Context::cast(frame->context()));
|
| Handle<Context> function_context(frame_context->fcontext());
|
| - CopyContextLocalsToScopeObject(serialized_scope_info, scope_info,
|
| - function_context, local_scope);
|
| + if (!CopyContextLocalsToScopeObject(serialized_scope_info, scope_info,
|
| + function_context, local_scope)) {
|
| + return Handle<JSObject>();
|
| + }
|
|
|
| // Finally copy any properties from the function context extension. This will
|
| // be variables introduced by eval.
|
| @@ -8915,7 +9236,9 @@
|
| // Names of variables introduced by eval are strings.
|
| ASSERT(keys->get(i)->IsString());
|
| Handle<String> key(String::cast(keys->get(i)));
|
| - SetProperty(local_scope, key, GetProperty(ext, key), NONE);
|
| + RETURN_IF_EMPTY_HANDLE_VALUE(
|
| + SetProperty(local_scope, key, GetProperty(ext, key), NONE),
|
| + Handle<JSObject>());
|
| }
|
| }
|
| }
|
| @@ -8948,16 +9271,20 @@
|
| for (int i = 0; i < scope_info.number_of_parameters(); ++i) {
|
| // We don't expect exception-throwing getters on the arguments shadow.
|
| Object* element = arguments_shadow->GetElement(i)->ToObjectUnchecked();
|
| - SetProperty(closure_scope,
|
| - scope_info.parameter_name(i),
|
| - Handle<Object>(element),
|
| - NONE);
|
| + RETURN_IF_EMPTY_HANDLE_VALUE(
|
| + SetProperty(closure_scope,
|
| + scope_info.parameter_name(i),
|
| + Handle<Object>(element),
|
| + NONE),
|
| + Handle<JSObject>());
|
| }
|
| }
|
|
|
| // Fill all context locals to the context extension.
|
| - CopyContextLocalsToScopeObject(serialized_scope_info, scope_info,
|
| - context, closure_scope);
|
| + if (!CopyContextLocalsToScopeObject(serialized_scope_info, scope_info,
|
| + context, closure_scope)) {
|
| + return Handle<JSObject>();
|
| + }
|
|
|
| // Finally copy any properties from the function context extension. This will
|
| // be variables introduced by eval.
|
| @@ -8968,7 +9295,9 @@
|
| // Names of variables introduced by eval are strings.
|
| ASSERT(keys->get(i)->IsString());
|
| Handle<String> key(String::cast(keys->get(i)));
|
| - SetProperty(closure_scope, key, GetProperty(ext, key), NONE);
|
| + RETURN_IF_EMPTY_HANDLE_VALUE(
|
| + SetProperty(closure_scope, key, GetProperty(ext, key), NONE),
|
| + Handle<JSObject>());
|
| }
|
| }
|
|
|
| @@ -9255,6 +9584,7 @@
|
| // Fill in scope details.
|
| details->set(kScopeDetailsTypeIndex, Smi::FromInt(it.Type()));
|
| Handle<JSObject> scope_object = it.ScopeObject();
|
| + RETURN_IF_EMPTY_HANDLE(scope_object);
|
| details->set(kScopeDetailsObjectIndex, *scope_object);
|
|
|
| return *Factory::NewJSArrayWithElements(details);
|
| @@ -9736,6 +10066,7 @@
|
|
|
| // Materialize the content of the local scope into a JSObject.
|
| Handle<JSObject> local_scope = MaterializeLocalScope(frame);
|
| + RETURN_IF_EMPTY_HANDLE(local_scope);
|
|
|
| // Allocate a new context for the debug evaluation and set the extension
|
| // object build.
|
| @@ -9763,10 +10094,14 @@
|
| Handle<String> function_source =
|
| Factory::NewStringFromAscii(Vector<const char>(source_str,
|
| source_str_length));
|
| +
|
| + // Currently, the eval code will be executed in non-strict mode,
|
| + // even in the strict code context.
|
| Handle<SharedFunctionInfo> shared =
|
| Compiler::CompileEval(function_source,
|
| context,
|
| - context->IsGlobalContext());
|
| + context->IsGlobalContext(),
|
| + kNonStrictMode);
|
| if (shared.is_null()) return Failure::Exception();
|
| Handle<JSFunction> compiled_function =
|
| Factory::NewFunctionFromSharedFunctionInfo(shared, context);
|
| @@ -9848,10 +10183,10 @@
|
| }
|
|
|
| // Compile the source to be evaluated.
|
| + // Currently, the eval code will be executed in non-strict mode,
|
| + // even in the strict code context.
|
| Handle<SharedFunctionInfo> shared =
|
| - Compiler::CompileEval(source,
|
| - context,
|
| - is_global);
|
| + Compiler::CompileEval(source, context, is_global, kNonStrictMode);
|
| if (shared.is_null()) return Failure::Exception();
|
| Handle<JSFunction> compiled_function =
|
| Handle<JSFunction>(Factory::NewFunctionFromSharedFunctionInfo(shared,
|
| @@ -10334,15 +10669,16 @@
|
| return *LiveEdit::CheckAndDropActivations(shared_array, do_drop);
|
| }
|
|
|
| -// Compares 2 strings line-by-line and returns diff in form of JSArray of
|
| -// triplets (pos1, pos1_end, pos2_end) describing list of diff chunks.
|
| -static MaybeObject* Runtime_LiveEditCompareStringsLinewise(Arguments args) {
|
| +// Compares 2 strings line-by-line, then token-wise and returns diff in form
|
| +// of JSArray of triplets (pos1, pos1_end, pos2_end) describing list
|
| +// of diff chunks.
|
| +static MaybeObject* Runtime_LiveEditCompareStrings(Arguments args) {
|
| ASSERT(args.length() == 2);
|
| HandleScope scope;
|
| CONVERT_ARG_CHECKED(String, s1, 0);
|
| CONVERT_ARG_CHECKED(String, s2, 1);
|
|
|
| - return *LiveEdit::CompareStringsLinewise(s1, s2);
|
| + return *LiveEdit::CompareStrings(s1, s2);
|
| }
|
|
|
|
|
| @@ -10412,10 +10748,36 @@
|
| }
|
|
|
|
|
| +// Sets a v8 flag.
|
| +static MaybeObject* Runtime_SetFlags(Arguments args) {
|
| + CONVERT_CHECKED(String, arg, args[0]);
|
| + SmartPointer<char> flags =
|
| + arg->ToCString(DISALLOW_NULLS, ROBUST_STRING_TRAVERSAL);
|
| + FlagList::SetFlagsFromString(*flags, StrLength(*flags));
|
| + return Heap::undefined_value();
|
| +}
|
| +
|
| +
|
| +// Performs a GC.
|
| +// Presently, it only does a full GC.
|
| +static MaybeObject* Runtime_CollectGarbage(Arguments args) {
|
| + Heap::CollectAllGarbage(true);
|
| + return Heap::undefined_value();
|
| +}
|
| +
|
| +
|
| +// Gets the current heap usage.
|
| +static MaybeObject* Runtime_GetHeapUsage(Arguments args) {
|
| + int usage = static_cast<int>(Heap::SizeOfObjects());
|
| + if (!Smi::IsValid(usage)) {
|
| + return *Factory::NewNumberFromInt(usage);
|
| + }
|
| + return Smi::FromInt(usage);
|
| +}
|
| #endif // ENABLE_DEBUGGER_SUPPORT
|
|
|
| +
|
| #ifdef ENABLE_LOGGING_AND_PROFILING
|
| -
|
| static MaybeObject* Runtime_ProfilerResume(Arguments args) {
|
| NoHandleAllocation ha;
|
| ASSERT(args.length() == 2);
|
| @@ -10593,51 +10955,12 @@
|
| }
|
|
|
|
|
| -MUST_USE_RESULT static MaybeObject* CacheMiss(FixedArray* cache_obj,
|
| - int index,
|
| - Object* key_obj) {
|
| - ASSERT(index % 2 == 0); // index of the key
|
| - ASSERT(index >= JSFunctionResultCache::kEntriesIndex);
|
| - ASSERT(index < cache_obj->length());
|
| -
|
| - HandleScope scope;
|
| -
|
| - Handle<FixedArray> cache(cache_obj);
|
| - Handle<Object> key(key_obj);
|
| - Handle<JSFunction> factory(JSFunction::cast(
|
| - cache->get(JSFunctionResultCache::kFactoryIndex)));
|
| - // TODO(antonm): consider passing a receiver when constructing a cache.
|
| - Handle<Object> receiver(Top::global_context()->global());
|
| -
|
| - Handle<Object> value;
|
| - {
|
| - // This handle is nor shared, nor used later, so it's safe.
|
| - Object** argv[] = { key.location() };
|
| - bool pending_exception = false;
|
| - value = Execution::Call(factory,
|
| - receiver,
|
| - 1,
|
| - argv,
|
| - &pending_exception);
|
| - if (pending_exception) return Failure::Exception();
|
| - }
|
| -
|
| - cache->set(index, *key);
|
| - cache->set(index + 1, *value);
|
| - cache->set(JSFunctionResultCache::kFingerIndex, Smi::FromInt(index));
|
| -
|
| - return *value;
|
| -}
|
| -
|
| -
|
| static MaybeObject* Runtime_GetFromCache(Arguments args) {
|
| // This is only called from codegen, so checks might be more lax.
|
| - CONVERT_CHECKED(FixedArray, cache, args[0]);
|
| + CONVERT_CHECKED(JSFunctionResultCache, cache, args[0]);
|
| Object* key = args[1];
|
|
|
| - const int finger_index =
|
| - Smi::cast(cache->get(JSFunctionResultCache::kFingerIndex))->value();
|
| -
|
| + int finger_index = cache->finger_index();
|
| Object* o = cache->get(finger_index);
|
| if (o == key) {
|
| // The fastest case: hit the same place again.
|
| @@ -10649,37 +10972,119 @@
|
| i -= 2) {
|
| o = cache->get(i);
|
| if (o == key) {
|
| - cache->set(JSFunctionResultCache::kFingerIndex, Smi::FromInt(i));
|
| + cache->set_finger_index(i);
|
| return cache->get(i + 1);
|
| }
|
| }
|
|
|
| - const int size =
|
| - Smi::cast(cache->get(JSFunctionResultCache::kCacheSizeIndex))->value();
|
| + int size = cache->size();
|
| ASSERT(size <= cache->length());
|
|
|
| for (int i = size - 2; i > finger_index; i -= 2) {
|
| o = cache->get(i);
|
| if (o == key) {
|
| - cache->set(JSFunctionResultCache::kFingerIndex, Smi::FromInt(i));
|
| + cache->set_finger_index(i);
|
| return cache->get(i + 1);
|
| }
|
| }
|
|
|
| - // Cache miss. If we have spare room, put new data into it, otherwise
|
| - // evict post finger entry which must be least recently used.
|
| - if (size < cache->length()) {
|
| - cache->set(JSFunctionResultCache::kCacheSizeIndex, Smi::FromInt(size + 2));
|
| - return CacheMiss(cache, size, key);
|
| + // There is no value in the cache. Invoke the function and cache result.
|
| + HandleScope scope;
|
| +
|
| + Handle<JSFunctionResultCache> cache_handle(cache);
|
| + Handle<Object> key_handle(key);
|
| + Handle<Object> value;
|
| + {
|
| + Handle<JSFunction> factory(JSFunction::cast(
|
| + cache_handle->get(JSFunctionResultCache::kFactoryIndex)));
|
| + // TODO(antonm): consider passing a receiver when constructing a cache.
|
| + Handle<Object> receiver(Top::global_context()->global());
|
| + // This handle is nor shared, nor used later, so it's safe.
|
| + Object** argv[] = { key_handle.location() };
|
| + bool pending_exception = false;
|
| + value = Execution::Call(factory,
|
| + receiver,
|
| + 1,
|
| + argv,
|
| + &pending_exception);
|
| + if (pending_exception) return Failure::Exception();
|
| + }
|
| +
|
| +#ifdef DEBUG
|
| + cache_handle->JSFunctionResultCacheVerify();
|
| +#endif
|
| +
|
| + // Function invocation may have cleared the cache. Reread all the data.
|
| + finger_index = cache_handle->finger_index();
|
| + size = cache_handle->size();
|
| +
|
| + // If we have spare room, put new data into it, otherwise evict post finger
|
| + // entry which is likely to be the least recently used.
|
| + int index = -1;
|
| + if (size < cache_handle->length()) {
|
| + cache_handle->set_size(size + JSFunctionResultCache::kEntrySize);
|
| + index = size;
|
| } else {
|
| - int target_index = finger_index + JSFunctionResultCache::kEntrySize;
|
| - if (target_index == cache->length()) {
|
| - target_index = JSFunctionResultCache::kEntriesIndex;
|
| + index = finger_index + JSFunctionResultCache::kEntrySize;
|
| + if (index == cache_handle->length()) {
|
| + index = JSFunctionResultCache::kEntriesIndex;
|
| }
|
| - return CacheMiss(cache, target_index, key);
|
| }
|
| +
|
| + ASSERT(index % 2 == 0);
|
| + ASSERT(index >= JSFunctionResultCache::kEntriesIndex);
|
| + ASSERT(index < cache_handle->length());
|
| +
|
| + cache_handle->set(index, *key_handle);
|
| + cache_handle->set(index + 1, *value);
|
| + cache_handle->set_finger_index(index);
|
| +
|
| +#ifdef DEBUG
|
| + cache_handle->JSFunctionResultCacheVerify();
|
| +#endif
|
| +
|
| + return *value;
|
| }
|
|
|
| +
|
| +static MaybeObject* Runtime_NewMessageObject(Arguments args) {
|
| + HandleScope scope;
|
| + CONVERT_ARG_CHECKED(String, type, 0);
|
| + CONVERT_ARG_CHECKED(JSArray, arguments, 1);
|
| + return *Factory::NewJSMessageObject(type,
|
| + arguments,
|
| + 0,
|
| + 0,
|
| + Factory::undefined_value(),
|
| + Factory::undefined_value(),
|
| + Factory::undefined_value());
|
| +}
|
| +
|
| +
|
| +static MaybeObject* Runtime_MessageGetType(Arguments args) {
|
| + CONVERT_CHECKED(JSMessageObject, message, args[0]);
|
| + return message->type();
|
| +}
|
| +
|
| +
|
| +static MaybeObject* Runtime_MessageGetArguments(Arguments args) {
|
| + CONVERT_CHECKED(JSMessageObject, message, args[0]);
|
| + return message->arguments();
|
| +}
|
| +
|
| +
|
| +static MaybeObject* Runtime_MessageGetStartPosition(Arguments args) {
|
| + CONVERT_CHECKED(JSMessageObject, message, args[0]);
|
| + return Smi::FromInt(message->start_position());
|
| +}
|
| +
|
| +
|
| +static MaybeObject* Runtime_MessageGetScript(Arguments args) {
|
| + CONVERT_CHECKED(JSMessageObject, message, args[0]);
|
| + return message->script();
|
| +}
|
| +
|
| +
|
| #ifdef DEBUG
|
| // ListNatives is ONLY used by the fuzz-natives.js in debug mode
|
| // Exclude the code in release mode.
|
|
|