Index: src/runtime.cc |
=================================================================== |
--- src/runtime.cc (revision 9808) |
+++ src/runtime.cc (working copy) |
@@ -432,64 +432,77 @@ |
// Create the JSArray. |
Handle<JSFunction> constructor( |
JSFunction::GlobalContextFromLiterals(*literals)->array_function()); |
- Handle<Object> object = isolate->factory()->NewJSObject(constructor); |
+ Handle<JSArray> object = |
+ Handle<JSArray>::cast(isolate->factory()->NewJSObject(constructor)); |
- if (elements->length() > kSmiOnlyLiteralMinimumLength) { |
- Handle<Map> smi_array_map = isolate->factory()->GetElementsTransitionMap( |
- Handle<JSObject>::cast(object), |
- FAST_SMI_ONLY_ELEMENTS); |
- HeapObject::cast(*object)->set_map(*smi_array_map); |
+ ElementsKind constant_elements_kind = |
+ static_cast<ElementsKind>(Smi::cast(elements->get(0))->value()); |
+ Handle<FixedArrayBase> constant_elements_values( |
+ FixedArrayBase::cast(elements->get(1))); |
+ |
+ ASSERT(FLAG_smi_only_arrays || constant_elements_kind == FAST_ELEMENTS || |
+ constant_elements_kind == FAST_SMI_ONLY_ELEMENTS); |
+ bool allow_literal_kind_transition = FLAG_smi_only_arrays && |
+ constant_elements_kind > object->GetElementsKind(); |
+ |
+ if (!FLAG_smi_only_arrays && |
+ constant_elements_values->length() > kSmiOnlyLiteralMinimumLength && |
+ constant_elements_kind != object->GetElementsKind()) { |
+ allow_literal_kind_transition = true; |
} |
- const bool is_cow = |
- (elements->map() == isolate->heap()->fixed_cow_array_map()); |
- Handle<FixedArray> copied_elements = |
- is_cow ? elements : isolate->factory()->CopyFixedArray(elements); |
+ // If the ElementsKind of the constant values of the array literal are less |
+ // specific than the ElementsKind of the boilerplate array object, change the |
+ // boilerplate array object's map to reflect that kind. |
+ if (allow_literal_kind_transition) { |
+ Handle<Map> transitioned_array_map = |
+ isolate->factory()->GetElementsTransitionMap(object, |
+ constant_elements_kind); |
+ object->set_map(*transitioned_array_map); |
+ } |
- Handle<FixedArray> content = Handle<FixedArray>::cast(copied_elements); |
- bool has_non_smi = false; |
- if (is_cow) { |
- // Copy-on-write arrays must be shallow (and simple). |
- for (int i = 0; i < content->length(); i++) { |
- Object* current = content->get(i); |
- ASSERT(!current->IsFixedArray()); |
- if (!current->IsSmi() && !current->IsTheHole()) { |
- has_non_smi = true; |
+ Handle<FixedArrayBase> copied_elements_values; |
+ if (constant_elements_kind == FAST_DOUBLE_ELEMENTS) { |
+ ASSERT(FLAG_smi_only_arrays); |
+ copied_elements_values = isolate->factory()->CopyFixedDoubleArray( |
+ Handle<FixedDoubleArray>::cast(constant_elements_values)); |
+ } else { |
+ ASSERT(constant_elements_kind == FAST_SMI_ONLY_ELEMENTS || |
+ constant_elements_kind == FAST_ELEMENTS); |
+ const bool is_cow = |
+ (constant_elements_values->map() == |
+ isolate->heap()->fixed_cow_array_map()); |
+ if (is_cow) { |
+ copied_elements_values = constant_elements_values; |
+#if DEBUG |
+ Handle<FixedArray> fixed_array_values = |
+ Handle<FixedArray>::cast(copied_elements_values); |
+ for (int i = 0; i < fixed_array_values->length(); i++) { |
+ ASSERT(!fixed_array_values->get(i)->IsFixedArray()); |
} |
- } |
-#if DEBUG |
- for (int i = 0; i < content->length(); i++) { |
- ASSERT(!content->get(i)->IsFixedArray()); |
- } |
#endif |
- } else { |
- for (int i = 0; i < content->length(); i++) { |
- Object* current = content->get(i); |
- if (current->IsFixedArray()) { |
- // The value contains the constant_properties of a |
- // simple object or array literal. |
- Handle<FixedArray> fa(FixedArray::cast(content->get(i))); |
- Handle<Object> result = |
- CreateLiteralBoilerplate(isolate, literals, fa); |
- if (result.is_null()) return result; |
- content->set(i, *result); |
- has_non_smi = true; |
- } else { |
- if (!current->IsSmi() && !current->IsTheHole()) { |
- has_non_smi = true; |
+ } else { |
+ Handle<FixedArray> fixed_array_values = |
+ Handle<FixedArray>::cast(constant_elements_values); |
+ Handle<FixedArray> fixed_array_values_copy = |
+ isolate->factory()->CopyFixedArray(fixed_array_values); |
+ copied_elements_values = fixed_array_values_copy; |
+ for (int i = 0; i < fixed_array_values->length(); i++) { |
+ Object* current = fixed_array_values->get(i); |
+ if (current->IsFixedArray()) { |
+ // The value contains the constant_properties of a |
+ // simple object or array literal. |
+ Handle<FixedArray> fa(FixedArray::cast(fixed_array_values->get(i))); |
+ Handle<Object> result = |
+ CreateLiteralBoilerplate(isolate, literals, fa); |
+ if (result.is_null()) return result; |
+ fixed_array_values_copy->set(i, *result); |
} |
} |
} |
} |
- |
- // Set the elements. |
- Handle<JSArray> js_object(Handle<JSArray>::cast(object)); |
- isolate->factory()->SetContent(js_object, content); |
- |
- if (has_non_smi && js_object->HasFastSmiOnlyElements()) { |
- isolate->factory()->EnsureCanContainNonSmiElements(js_object); |
- } |
- |
+ object->set_elements(*copied_elements_values); |
+ object->set_length(Smi::FromInt(copied_elements_values->length())); |
return object; |
} |
@@ -704,6 +717,82 @@ |
} |
+RUNTIME_FUNCTION(MaybeObject*, Runtime_SetInitialize) { |
+ HandleScope scope(isolate); |
+ ASSERT(args.length() == 1); |
+ CONVERT_ARG_CHECKED(JSSet, holder, 0); |
+ Handle<ObjectHashSet> table = isolate->factory()->NewObjectHashSet(0); |
+ holder->set_table(*table); |
+ return *holder; |
+} |
+ |
+ |
+RUNTIME_FUNCTION(MaybeObject*, Runtime_SetAdd) { |
+ HandleScope scope(isolate); |
+ ASSERT(args.length() == 2); |
+ CONVERT_ARG_CHECKED(JSSet, holder, 0); |
+ Handle<Object> key(args[1]); |
+ Handle<ObjectHashSet> table(ObjectHashSet::cast(holder->table())); |
+ table = ObjectHashSetAdd(table, key); |
+ holder->set_table(*table); |
+ return isolate->heap()->undefined_symbol(); |
+} |
+ |
+ |
+RUNTIME_FUNCTION(MaybeObject*, Runtime_SetHas) { |
+ HandleScope scope(isolate); |
+ ASSERT(args.length() == 2); |
+ CONVERT_ARG_CHECKED(JSSet, holder, 0); |
+ Handle<Object> key(args[1]); |
+ Handle<ObjectHashSet> table(ObjectHashSet::cast(holder->table())); |
+ return isolate->heap()->ToBoolean(table->Contains(*key)); |
+} |
+ |
+ |
+RUNTIME_FUNCTION(MaybeObject*, Runtime_SetDelete) { |
+ HandleScope scope(isolate); |
+ ASSERT(args.length() == 2); |
+ CONVERT_ARG_CHECKED(JSSet, holder, 0); |
+ Handle<Object> key(args[1]); |
+ Handle<ObjectHashSet> table(ObjectHashSet::cast(holder->table())); |
+ table = ObjectHashSetRemove(table, key); |
+ holder->set_table(*table); |
+ return isolate->heap()->undefined_symbol(); |
+} |
+ |
+ |
+RUNTIME_FUNCTION(MaybeObject*, Runtime_MapInitialize) { |
+ HandleScope scope(isolate); |
+ ASSERT(args.length() == 1); |
+ CONVERT_ARG_CHECKED(JSMap, holder, 0); |
+ Handle<ObjectHashTable> table = isolate->factory()->NewObjectHashTable(0); |
+ holder->set_table(*table); |
+ return *holder; |
+} |
+ |
+ |
+RUNTIME_FUNCTION(MaybeObject*, Runtime_MapGet) { |
+ HandleScope scope(isolate); |
+ ASSERT(args.length() == 2); |
+ CONVERT_ARG_CHECKED(JSMap, holder, 0); |
+ Handle<Object> key(args[1]); |
+ return ObjectHashTable::cast(holder->table())->Lookup(*key); |
+} |
+ |
+ |
+RUNTIME_FUNCTION(MaybeObject*, Runtime_MapSet) { |
+ HandleScope scope(isolate); |
+ ASSERT(args.length() == 3); |
+ CONVERT_ARG_CHECKED(JSMap, holder, 0); |
+ Handle<Object> key(args[1]); |
+ Handle<Object> value(args[2]); |
+ Handle<ObjectHashTable> table(ObjectHashTable::cast(holder->table())); |
+ Handle<ObjectHashTable> new_table = PutIntoObjectHashTable(table, key, value); |
+ holder->set_table(*new_table); |
+ return *value; |
+} |
+ |
+ |
RUNTIME_FUNCTION(MaybeObject*, Runtime_WeakMapInitialize) { |
HandleScope scope(isolate); |
ASSERT(args.length() == 1); |
@@ -961,7 +1050,7 @@ |
HandleScope scope(isolate); |
Handle<FixedArray> elms = isolate->factory()->NewFixedArray(DESCRIPTOR_SIZE); |
Handle<JSArray> desc = isolate->factory()->NewJSArrayWithElements(elms); |
- LookupResult result; |
+ LookupResult result(isolate); |
CONVERT_ARG_CHECKED(JSObject, obj, 0); |
CONVERT_ARG_CHECKED(String, name, 1); |
@@ -992,7 +1081,7 @@ |
case JSObject::INTERCEPTED_ELEMENT: |
case JSObject::FAST_ELEMENT: { |
elms->set(IS_ACCESSOR_INDEX, heap->false_value()); |
- Handle<Object> value = GetElement(obj, index); |
+ Handle<Object> value = Object::GetElement(obj, index); |
RETURN_IF_EMPTY_HANDLE(isolate, value); |
elms->set(VALUE_INDEX, *value); |
elms->set(WRITABLE_INDEX, heap->true_value()); |
@@ -1036,7 +1125,7 @@ |
case NORMAL: { |
// This is a data property. |
elms->set(IS_ACCESSOR_INDEX, heap->false_value()); |
- Handle<Object> value = GetElement(obj, index); |
+ Handle<Object> value = Object::GetElement(obj, index); |
ASSERT(!value.is_null()); |
elms->set(VALUE_INDEX, *value); |
elms->set(WRITABLE_INDEX, heap->ToBoolean(!details.IsReadOnly())); |
@@ -1240,7 +1329,7 @@ |
if (value->IsUndefined() || is_const_property) { |
// Lookup the property in the global object, and don't set the |
// value of the variable if the property is already there. |
- LookupResult lookup; |
+ LookupResult lookup(isolate); |
global->Lookup(*name, &lookup); |
if (lookup.IsProperty()) { |
// We found an existing property. Unless it was an interceptor |
@@ -1267,7 +1356,7 @@ |
value = function; |
} |
- LookupResult lookup; |
+ LookupResult lookup(isolate); |
global->LocalLookup(*name, &lookup); |
// Compute the property attributes. According to ECMA-262, section |
@@ -1275,10 +1364,10 @@ |
// non-deletable. However, neither SpiderMonkey nor KJS creates the |
// property as read-only, so we don't either. |
int attr = NONE; |
- if ((flags & kDeclareGlobalsEvalFlag) == 0) { |
+ if (!DeclareGlobalsEvalFlag::decode(flags)) { |
attr |= DONT_DELETE; |
} |
- bool is_native = (flags & kDeclareGlobalsNativeFlag) != 0; |
+ bool is_native = DeclareGlobalsNativeFlag::decode(flags); |
if (is_const_property || (is_native && is_function_declaration)) { |
attr |= READ_ONLY; |
} |
@@ -1303,9 +1392,7 @@ |
value, |
attributes)); |
} else { |
- StrictModeFlag strict_mode = |
- ((flags & kDeclareGlobalsStrictModeFlag) != 0) ? kStrictMode |
- : kNonStrictMode; |
+ StrictModeFlag strict_mode = DeclareGlobalsStrictModeFlag::decode(flags); |
RETURN_IF_EMPTY_HANDLE(isolate, |
SetProperty(global, |
name, |
@@ -1399,7 +1486,7 @@ |
// not real JSObjects. |
if (initial_value->IsTheHole() && |
!object->IsJSContextExtensionObject()) { |
- LookupResult lookup; |
+ LookupResult lookup(isolate); |
object->Lookup(*name, &lookup); |
if (lookup.IsProperty() && (lookup.type() == CALLBACKS)) { |
return ThrowRedeclarationError(isolate, "const", name); |
@@ -1443,7 +1530,7 @@ |
// Note that objects can have hidden prototypes, so we need to traverse |
// the whole chain of hidden prototypes to do a 'local' lookup. |
Object* object = global; |
- LookupResult lookup; |
+ LookupResult lookup(isolate); |
while (object->IsJSObject() && |
JSObject::cast(object)->map()->is_hidden_prototype()) { |
JSObject* raw_holder = JSObject::cast(object); |
@@ -1497,7 +1584,7 @@ |
// add it as a local property even in case of callbacks in the |
// prototype chain (this rules out using SetProperty). |
// We use SetLocalPropertyIgnoreAttributes instead |
- LookupResult lookup; |
+ LookupResult lookup(isolate); |
global->LocalLookup(*name, &lookup); |
if (!lookup.IsProperty()) { |
return global->SetLocalPropertyIgnoreAttributes(*name, |
@@ -1614,7 +1701,7 @@ |
// This is the property that was introduced by the const declaration. |
// Set it if it hasn't been set before. NOTE: We cannot use |
// GetProperty() to get the current value as it 'unholes' the value. |
- LookupResult lookup; |
+ LookupResult lookup(isolate); |
object->LocalLookupRealNamedProperty(*name, &lookup); |
ASSERT(lookup.IsProperty()); // the property was declared |
ASSERT(lookup.IsReadOnly()); // and it was declared as read-only |
@@ -1663,19 +1750,6 @@ |
} |
-RUNTIME_FUNCTION(MaybeObject*, Runtime_NonSmiElementStored) { |
- ASSERT(args.length() == 1); |
- CONVERT_ARG_CHECKED(JSObject, object, 0); |
- if (object->HasFastSmiOnlyElements()) { |
- MaybeObject* maybe_map = object->GetElementsTransitionMap(FAST_ELEMENTS); |
- Map* map; |
- if (!maybe_map->To<Map>(&map)) return maybe_map; |
- object->set_map(Map::cast(map)); |
- } |
- return *object; |
-} |
- |
- |
RUNTIME_FUNCTION(MaybeObject*, Runtime_RegExpExec) { |
HandleScope scope(isolate); |
ASSERT(args.length() == 4); |
@@ -1930,15 +2004,6 @@ |
} |
-RUNTIME_FUNCTION(MaybeObject*, Runtime_FunctionSetBound) { |
- HandleScope scope(isolate); |
- ASSERT(args.length() == 1); |
- |
- CONVERT_CHECKED(JSFunction, fun, args[0]); |
- fun->shared()->set_bound(true); |
- return isolate->heap()->undefined_value(); |
-} |
- |
RUNTIME_FUNCTION(MaybeObject*, Runtime_FunctionRemovePrototype) { |
NoHandleAllocation ha; |
ASSERT(args.length() == 1); |
@@ -2017,24 +2082,6 @@ |
} |
-// Creates a local, readonly, property called length with the correct |
-// length (when read by the user). This effectively overwrites the |
-// interceptor used to normally provide the length. |
-RUNTIME_FUNCTION(MaybeObject*, Runtime_BoundFunctionSetLength) { |
- NoHandleAllocation ha; |
- ASSERT(args.length() == 2); |
- CONVERT_CHECKED(JSFunction, fun, args[0]); |
- CONVERT_CHECKED(Smi, length, args[1]); |
- MaybeObject* maybe_name = |
- isolate->heap()->AllocateStringFromAscii(CStrVector("length")); |
- String* name; |
- if (!maybe_name->To(&name)) return maybe_name; |
- PropertyAttributes attr = |
- static_cast<PropertyAttributes>(DONT_DELETE | DONT_ENUM | READ_ONLY); |
- return fun->AddProperty(name, length, attr, kNonStrictMode); |
-} |
- |
- |
RUNTIME_FUNCTION(MaybeObject*, Runtime_FunctionSetPrototype) { |
NoHandleAllocation ha; |
ASSERT(args.length() == 2); |
@@ -2137,13 +2184,12 @@ |
Handle<JSFunction> fun = Handle<JSFunction>::cast(code); |
Handle<SharedFunctionInfo> shared(fun->shared()); |
- if (!EnsureCompiled(shared, KEEP_EXCEPTION)) { |
+ if (!SharedFunctionInfo::EnsureCompiled(shared, KEEP_EXCEPTION)) { |
return Failure::Exception(); |
} |
// Since we don't store the source for this we should never |
// optimize this. |
shared->code()->set_optimizable(false); |
- |
// Set the code, scope info, formal parameter count, |
// and the length of the target function. |
target->shared()->set_code(shared->code()); |
@@ -4069,11 +4115,6 @@ |
return prototype->GetElement(index); |
} |
- return GetElement(object, index); |
-} |
- |
- |
-MaybeObject* Runtime::GetElement(Handle<Object> object, uint32_t index) { |
return object->GetElement(index); |
} |
@@ -4162,7 +4203,7 @@ |
return value->IsTheHole() ? isolate->heap()->undefined_value() : value; |
} |
// Lookup cache miss. Perform lookup and update the cache if appropriate. |
- LookupResult result; |
+ LookupResult result(isolate); |
receiver->LocalLookup(key, &result); |
if (result.IsProperty() && result.type() == FIELD) { |
int offset = result.GetFieldIndex(); |
@@ -4217,7 +4258,7 @@ |
int unchecked = flag_attr->value(); |
RUNTIME_ASSERT((unchecked & ~(READ_ONLY | DONT_ENUM | DONT_DELETE)) == 0); |
RUNTIME_ASSERT(!obj->IsNull()); |
- LookupResult result; |
+ LookupResult result(isolate); |
obj->LocalLookupRealNamedProperty(name, &result); |
PropertyAttributes attr = static_cast<PropertyAttributes>(unchecked); |
@@ -4259,11 +4300,11 @@ |
uint32_t index; |
bool is_element = name->AsArrayIndex(&index); |
- // Special case for elements if any of the flags are true. |
+ // Special case for elements if any of the flags might be involved. |
// If elements are in fast case we always implicitly assume that: |
// DONT_DELETE: false, DONT_ENUM: false, READ_ONLY: false. |
- if (((unchecked & (DONT_DELETE | DONT_ENUM | READ_ONLY)) != 0) && |
- is_element) { |
+ if (is_element && (attr != NONE || |
+ js_object->HasLocalElement(index) == JSObject::DICTIONARY_ELEMENT)) { |
// Normalize the elements to enable attributes on the property. |
if (js_object->IsJSGlobalProxy()) { |
// We do not need to do access checks here since these has already |
@@ -4301,7 +4342,7 @@ |
return *obj_value; |
} |
- LookupResult result; |
+ LookupResult result(isolate); |
js_object->LocalLookupRealNamedProperty(*name, &result); |
// To be compatible with safari we do not change the value on API objects |
@@ -4568,6 +4609,39 @@ |
} |
+MaybeObject* TransitionElements(Handle<Object> object, |
+ ElementsKind to_kind, |
+ Isolate* isolate) { |
+ HandleScope scope(isolate); |
+ if (!object->IsJSObject()) return isolate->ThrowIllegalOperation(); |
+ ElementsKind from_kind = |
+ Handle<JSObject>::cast(object)->map()->elements_kind(); |
+ if (Map::IsValidElementsTransition(from_kind, to_kind)) { |
+ Handle<Object> result = |
+ TransitionElementsKind(Handle<JSObject>::cast(object), to_kind); |
+ if (result.is_null()) return isolate->ThrowIllegalOperation(); |
+ return *result; |
+ } |
+ return isolate->ThrowIllegalOperation(); |
+} |
+ |
+ |
+RUNTIME_FUNCTION(MaybeObject*, Runtime_TransitionElementsSmiToDouble) { |
+ NoHandleAllocation ha; |
+ RUNTIME_ASSERT(args.length() == 1); |
+ Handle<Object> object = args.at<Object>(0); |
+ return TransitionElements(object, FAST_DOUBLE_ELEMENTS, isolate); |
+} |
+ |
+ |
+RUNTIME_FUNCTION(MaybeObject*, Runtime_TransitionElementsDoubleToObject) { |
+ NoHandleAllocation ha; |
+ RUNTIME_ASSERT(args.length() == 1); |
+ Handle<Object> object = args.at<Object>(0); |
+ return TransitionElements(object, FAST_ELEMENTS, isolate); |
+} |
+ |
+ |
// Set the native flag on the function. |
// This is used to decide if we should transform null and undefined |
// into the global object when doing call and apply. |
@@ -4751,8 +4825,11 @@ |
RUNTIME_FUNCTION(MaybeObject*, Runtime_GetPropertyNames) { |
HandleScope scope(isolate); |
ASSERT(args.length() == 1); |
- CONVERT_ARG_CHECKED(JSObject, object, 0); |
- return *GetKeysFor(object); |
+ CONVERT_ARG_CHECKED(JSReceiver, object, 0); |
+ bool threw = false; |
+ Handle<JSArray> result = GetKeysFor(object, &threw); |
+ if (threw) return Failure::Exception(); |
+ return *result; |
} |
@@ -4764,14 +4841,16 @@ |
RUNTIME_FUNCTION(MaybeObject*, Runtime_GetPropertyNamesFast) { |
ASSERT(args.length() == 1); |
- CONVERT_CHECKED(JSObject, raw_object, args[0]); |
+ CONVERT_CHECKED(JSReceiver, raw_object, args[0]); |
if (raw_object->IsSimpleEnum()) return raw_object->map(); |
HandleScope scope(isolate); |
- Handle<JSObject> object(raw_object); |
- Handle<FixedArray> content = GetKeysInFixedArrayFor(object, |
- INCLUDE_PROTOS); |
+ Handle<JSReceiver> object(raw_object); |
+ bool threw = false; |
+ Handle<FixedArray> content = |
+ GetKeysInFixedArrayFor(object, INCLUDE_PROTOS, &threw); |
+ if (threw) return Failure::Exception(); |
// Test again, since cache may have been built by preceding call. |
if (object->IsSimpleEnum()) return object->map(); |
@@ -4968,8 +5047,11 @@ |
object = Handle<JSObject>::cast(proto); |
} |
- Handle<FixedArray> contents = GetKeysInFixedArrayFor(object, |
- LOCAL_ONLY); |
+ bool threw = false; |
+ Handle<FixedArray> contents = |
+ GetKeysInFixedArrayFor(object, LOCAL_ONLY, &threw); |
+ if (threw) return Failure::Exception(); |
+ |
// Some fast paths through GetKeysInFixedArrayFor reuse a cached |
// property array and since the result is mutable we have to create |
// a fresh clone on each invocation. |
@@ -7762,14 +7844,21 @@ |
int year, month, day; |
DateYMDFromTime(static_cast<int>(floor(t / 86400000)), year, month, day); |
- RUNTIME_ASSERT(res_array->elements()->map() == |
- isolate->heap()->fixed_array_map()); |
- FixedArray* elms = FixedArray::cast(res_array->elements()); |
- RUNTIME_ASSERT(elms->length() == 3); |
+ FixedArrayBase* elms_base = FixedArrayBase::cast(res_array->elements()); |
+ RUNTIME_ASSERT(elms_base->length() == 3); |
+ RUNTIME_ASSERT(res_array->GetElementsKind() <= FAST_DOUBLE_ELEMENTS); |
- elms->set(0, Smi::FromInt(year)); |
- elms->set(1, Smi::FromInt(month)); |
- elms->set(2, Smi::FromInt(day)); |
+ if (res_array->HasFastDoubleElements()) { |
+ FixedDoubleArray* elms = FixedDoubleArray::cast(res_array->elements()); |
+ elms->set(0, year); |
+ elms->set(1, month); |
+ elms->set(2, day); |
+ } else { |
+ FixedArray* elms = FixedArray::cast(res_array->elements()); |
+ elms->set(0, Smi::FromInt(year)); |
+ elms->set(1, Smi::FromInt(month)); |
+ elms->set(2, Smi::FromInt(day)); |
+ } |
return isolate->heap()->undefined_value(); |
} |
@@ -7926,8 +8015,11 @@ |
} |
-static SmartArrayPointer<Handle<Object> > GetNonBoundArguments( |
- int bound_argc, |
+// Find the arguments of the JavaScript function invocation that called |
+// into C++ code. Collect these in a newly allocated array of handles (possibly |
+// prefixed by a number of empty handles). |
+static SmartArrayPointer<Handle<Object> > GetCallerArguments( |
+ int prefix_argc, |
int* total_argc) { |
// Find frame containing arguments passed to the caller. |
JavaScriptFrameIterator it; |
@@ -7943,12 +8035,12 @@ |
inlined_frame_index, |
&args_slots); |
- *total_argc = bound_argc + args_count; |
+ *total_argc = prefix_argc + args_count; |
SmartArrayPointer<Handle<Object> > param_data( |
NewArray<Handle<Object> >(*total_argc)); |
for (int i = 0; i < args_count; i++) { |
Handle<Object> val = args_slots[i].GetValue(); |
- param_data[bound_argc + i] = val; |
+ param_data[prefix_argc + i] = val; |
} |
return param_data; |
} else { |
@@ -7956,49 +8048,131 @@ |
frame = it.frame(); |
int args_count = frame->ComputeParametersCount(); |
- *total_argc = bound_argc + args_count; |
+ *total_argc = prefix_argc + args_count; |
SmartArrayPointer<Handle<Object> > param_data( |
NewArray<Handle<Object> >(*total_argc)); |
for (int i = 0; i < args_count; i++) { |
Handle<Object> val = Handle<Object>(frame->GetParameter(i)); |
- param_data[bound_argc + i] = val; |
+ param_data[prefix_argc + i] = val; |
} |
return param_data; |
} |
} |
+RUNTIME_FUNCTION(MaybeObject*, Runtime_FunctionBindArguments) { |
+ HandleScope scope(isolate); |
+ ASSERT(args.length() == 4); |
+ CONVERT_ARG_CHECKED(JSFunction, bound_function, 0); |
+ RUNTIME_ASSERT(args[3]->IsNumber()); |
+ Handle<Object> bindee = args.at<Object>(1); |
+ |
+ // TODO(lrn): Create bound function in C++ code from premade shared info. |
+ bound_function->shared()->set_bound(true); |
+ // Get all arguments of calling function (Function.prototype.bind). |
+ int argc = 0; |
+ SmartArrayPointer<Handle<Object> > arguments = GetCallerArguments(0, &argc); |
+ // Don't count the this-arg. |
+ if (argc > 0) { |
+ ASSERT(*arguments[0] == args[2]); |
+ argc--; |
+ } else { |
+ ASSERT(args[2]->IsUndefined()); |
+ } |
+ // Initialize array of bindings (function, this, and any existing arguments |
+ // if the function was already bound). |
+ Handle<FixedArray> new_bindings; |
+ int i; |
+ if (bindee->IsJSFunction() && JSFunction::cast(*bindee)->shared()->bound()) { |
+ Handle<FixedArray> old_bindings( |
+ JSFunction::cast(*bindee)->function_bindings()); |
+ new_bindings = |
+ isolate->factory()->NewFixedArray(old_bindings->length() + argc); |
+ bindee = Handle<Object>(old_bindings->get(JSFunction::kBoundFunctionIndex)); |
+ i = 0; |
+ for (int n = old_bindings->length(); i < n; i++) { |
+ new_bindings->set(i, old_bindings->get(i)); |
+ } |
+ } else { |
+ int array_size = JSFunction::kBoundArgumentsStartIndex + argc; |
+ new_bindings = isolate->factory()->NewFixedArray(array_size); |
+ new_bindings->set(JSFunction::kBoundFunctionIndex, *bindee); |
+ new_bindings->set(JSFunction::kBoundThisIndex, args[2]); |
+ i = 2; |
+ } |
+ // Copy arguments, skipping the first which is "this_arg". |
+ for (int j = 0; j < argc; j++, i++) { |
+ new_bindings->set(i, *arguments[j + 1]); |
+ } |
+ new_bindings->set_map(isolate->heap()->fixed_cow_array_map()); |
+ bound_function->set_function_bindings(*new_bindings); |
+ |
+ // Update length. |
+ Handle<String> length_symbol = isolate->factory()->length_symbol(); |
+ Handle<Object> new_length(args.at<Object>(3)); |
+ PropertyAttributes attr = |
+ static_cast<PropertyAttributes>(DONT_DELETE | DONT_ENUM | READ_ONLY); |
+ ForceSetProperty(bound_function, length_symbol, new_length, attr); |
+ return *bound_function; |
+} |
+ |
+ |
+RUNTIME_FUNCTION(MaybeObject*, Runtime_BoundFunctionGetBindings) { |
+ HandleScope handles(isolate); |
+ ASSERT(args.length() == 1); |
+ CONVERT_ARG_CHECKED(JSObject, callable, 0); |
+ if (callable->IsJSFunction()) { |
+ Handle<JSFunction> function = Handle<JSFunction>::cast(callable); |
+ if (function->shared()->bound()) { |
+ Handle<FixedArray> bindings(function->function_bindings()); |
+ ASSERT(bindings->map() == isolate->heap()->fixed_cow_array_map()); |
+ return *isolate->factory()->NewJSArrayWithElements(bindings); |
+ } |
+ } |
+ return isolate->heap()->undefined_value(); |
+} |
+ |
+ |
RUNTIME_FUNCTION(MaybeObject*, Runtime_NewObjectFromBound) { |
HandleScope scope(isolate); |
- ASSERT(args.length() == 2); |
+ ASSERT(args.length() == 1); |
// First argument is a function to use as a constructor. |
CONVERT_ARG_CHECKED(JSFunction, function, 0); |
+ RUNTIME_ASSERT(function->shared()->bound()); |
- // Second argument is either null or an array of bound arguments. |
- Handle<FixedArray> bound_args; |
- int bound_argc = 0; |
- if (!args[1]->IsNull()) { |
- CONVERT_ARG_CHECKED(JSArray, params, 1); |
- RUNTIME_ASSERT(params->HasFastTypeElements()); |
- bound_args = Handle<FixedArray>(FixedArray::cast(params->elements())); |
- bound_argc = Smi::cast(params->length())->value(); |
- } |
+ // The argument is a bound function. Extract its bound arguments |
+ // and callable. |
+ Handle<FixedArray> bound_args = |
+ Handle<FixedArray>(FixedArray::cast(function->function_bindings())); |
+ int bound_argc = bound_args->length() - JSFunction::kBoundArgumentsStartIndex; |
+ Handle<Object> bound_function( |
+ JSReceiver::cast(bound_args->get(JSFunction::kBoundFunctionIndex))); |
+ ASSERT(!bound_function->IsJSFunction() || |
+ !Handle<JSFunction>::cast(bound_function)->shared()->bound()); |
int total_argc = 0; |
SmartArrayPointer<Handle<Object> > param_data = |
- GetNonBoundArguments(bound_argc, &total_argc); |
+ GetCallerArguments(bound_argc, &total_argc); |
for (int i = 0; i < bound_argc; i++) { |
- Handle<Object> val = Handle<Object>(bound_args->get(i)); |
- param_data[i] = val; |
+ param_data[i] = Handle<Object>(bound_args->get( |
+ JSFunction::kBoundArgumentsStartIndex + i)); |
} |
+ if (!bound_function->IsJSFunction()) { |
+ bool exception_thrown; |
+ bound_function = Execution::TryGetConstructorDelegate(bound_function, |
+ &exception_thrown); |
+ if (exception_thrown) return Failure::Exception(); |
+ } |
+ ASSERT(bound_function->IsJSFunction()); |
+ |
bool exception = false; |
Handle<Object> result = |
- Execution::New(function, total_argc, *param_data, &exception); |
+ Execution::New(Handle<JSFunction>::cast(bound_function), |
+ total_argc, *param_data, &exception); |
if (exception) { |
- return Failure::Exception(); |
+ return Failure::Exception(); |
} |
- |
ASSERT(!result.is_null()); |
return *result; |
} |
@@ -8011,7 +8185,8 @@ |
prototype = Handle<Object>(function->instance_prototype(), isolate); |
} |
if (function->shared()->CanGenerateInlineConstructor(*prototype)) { |
- ConstructStubCompiler compiler; |
+ HandleScope scope(isolate); |
+ ConstructStubCompiler compiler(isolate); |
MaybeObject* code = compiler.CompileConstructStub(*function); |
if (!code->IsFailure()) { |
function->shared()->set_construct_stub( |
@@ -8075,9 +8250,11 @@ |
// available. We cannot use EnsureCompiled because that forces a |
// compilation through the shared function info which makes it |
// impossible for us to optimize. |
+ if (!function->is_compiled()) { |
+ JSFunction::CompileLazy(function, CLEAR_EXCEPTION); |
+ } |
+ |
Handle<SharedFunctionInfo> shared(function->shared(), isolate); |
- if (!function->is_compiled()) CompileLazy(function, CLEAR_EXCEPTION); |
- |
if (!function->has_initial_map() && |
shared->IsInobjectSlackTrackingInProgress()) { |
// The tracking is already in progress for another function. We can only |
@@ -8128,7 +8305,7 @@ |
// Compile the target function. |
ASSERT(!function->is_compiled()); |
- if (!CompileLazy(function, KEEP_EXCEPTION)) { |
+ if (!JSFunction::CompileLazy(function, KEEP_EXCEPTION)) { |
return Failure::Exception(); |
} |
@@ -8165,7 +8342,9 @@ |
function->ReplaceCode(function->shared()->code()); |
return function->code(); |
} |
- if (CompileOptimized(function, AstNode::kNoNumber, CLEAR_EXCEPTION)) { |
+ if (JSFunction::CompileOptimized(function, |
+ AstNode::kNoNumber, |
+ CLEAR_EXCEPTION)) { |
return function->code(); |
} |
if (FLAG_trace_opt) { |
@@ -8406,7 +8585,7 @@ |
// Try to compile the optimized code. A true return value from |
// CompileOptimized means that compilation succeeded, not necessarily |
// that optimization succeeded. |
- if (CompileOptimized(function, ast_id, CLEAR_EXCEPTION) && |
+ if (JSFunction::CompileOptimized(function, ast_id, CLEAR_EXCEPTION) && |
function->IsOptimized()) { |
DeoptimizationInputData* data = DeoptimizationInputData::cast( |
function->code()->deoptimization_data()); |
@@ -8762,13 +8941,26 @@ |
Handle<Object> receiver = isolate->factory()->the_hole_value(); |
Object* value = Context::cast(*holder)->get(index); |
// Check for uninitialized bindings. |
- if (binding_flags == MUTABLE_CHECK_INITIALIZED && value->IsTheHole()) { |
- Handle<Object> reference_error = |
- isolate->factory()->NewReferenceError("not_defined", |
- HandleVector(&name, 1)); |
- return MakePair(isolate->Throw(*reference_error), NULL); |
- } else { |
- return MakePair(Unhole(isolate->heap(), value, attributes), *receiver); |
+ switch (binding_flags) { |
+ case MUTABLE_CHECK_INITIALIZED: |
+ case IMMUTABLE_CHECK_INITIALIZED_HARMONY: |
+ if (value->IsTheHole()) { |
+ Handle<Object> reference_error = |
+ isolate->factory()->NewReferenceError("not_defined", |
+ HandleVector(&name, 1)); |
+ return MakePair(isolate->Throw(*reference_error), NULL); |
+ } |
+ // FALLTHROUGH |
+ case MUTABLE_IS_INITIALIZED: |
+ case IMMUTABLE_IS_INITIALIZED: |
+ case IMMUTABLE_IS_INITIALIZED_HARMONY: |
+ ASSERT(!value->IsTheHole()); |
+ return MakePair(value, *receiver); |
+ case IMMUTABLE_CHECK_INITIALIZED: |
+ return MakePair(Unhole(isolate->heap(), value, attributes), *receiver); |
+ case MISSING_BINDING: |
+ UNREACHABLE(); |
+ return MakePair(NULL, NULL); |
} |
} |
@@ -8947,42 +9139,6 @@ |
} |
-// NOTE: These PrintXXX functions are defined for all builds (not just |
-// DEBUG builds) because we may want to be able to trace function |
-// calls in all modes. |
-static void PrintString(String* str) { |
- // not uncommon to have empty strings |
- if (str->length() > 0) { |
- SmartArrayPointer<char> s = |
- str->ToCString(DISALLOW_NULLS, ROBUST_STRING_TRAVERSAL); |
- PrintF("%s", *s); |
- } |
-} |
- |
- |
-static void PrintObject(Object* obj) { |
- if (obj->IsSmi()) { |
- PrintF("%d", Smi::cast(obj)->value()); |
- } else if (obj->IsString() || obj->IsSymbol()) { |
- PrintString(String::cast(obj)); |
- } else if (obj->IsNumber()) { |
- PrintF("%g", obj->Number()); |
- } else if (obj->IsFailure()) { |
- PrintF("<failure>"); |
- } else if (obj->IsUndefined()) { |
- PrintF("<undefined>"); |
- } else if (obj->IsNull()) { |
- PrintF("<null>"); |
- } else if (obj->IsTrue()) { |
- PrintF("<true>"); |
- } else if (obj->IsFalse()) { |
- PrintF("<false>"); |
- } else { |
- PrintF("%p", reinterpret_cast<void*>(obj)); |
- } |
-} |
- |
- |
static int StackSize() { |
int n = 0; |
for (JavaScriptFrameIterator it; !it.done(); it.Advance()) n++; |
@@ -9001,38 +9157,33 @@ |
} |
if (result == NULL) { |
- // constructor calls |
- JavaScriptFrameIterator it; |
- JavaScriptFrame* frame = it.frame(); |
- if (frame->IsConstructor()) PrintF("new "); |
- // function name |
- Object* fun = frame->function(); |
- if (fun->IsJSFunction()) { |
- PrintObject(JSFunction::cast(fun)->shared()->name()); |
- } else { |
- PrintObject(fun); |
- } |
- // function arguments |
- // (we are intentionally only printing the actually |
- // supplied parameters, not all parameters required) |
- PrintF("(this="); |
- PrintObject(frame->receiver()); |
- const int length = frame->ComputeParametersCount(); |
- for (int i = 0; i < length; i++) { |
- PrintF(", "); |
- PrintObject(frame->GetParameter(i)); |
- } |
- PrintF(") {\n"); |
- |
+ JavaScriptFrame::PrintTop(stdout, true, false); |
+ PrintF(" {\n"); |
} else { |
// function result |
PrintF("} -> "); |
- PrintObject(result); |
+ result->ShortPrint(); |
PrintF("\n"); |
} |
} |
+RUNTIME_FUNCTION(MaybeObject*, Runtime_TraceElementsKindTransition) { |
+ ASSERT(args.length() == 5); |
+ CONVERT_ARG_CHECKED(JSObject, obj, 0); |
+ CONVERT_SMI_ARG_CHECKED(from_kind, 1); |
+ CONVERT_ARG_CHECKED(FixedArrayBase, from_elements, 2); |
+ CONVERT_SMI_ARG_CHECKED(to_kind, 3); |
+ CONVERT_ARG_CHECKED(FixedArrayBase, to_elements, 4); |
+ NoHandleAllocation ha; |
+ PrintF("*"); |
+ obj->PrintElementsTransition(stdout, |
+ static_cast<ElementsKind>(from_kind), *from_elements, |
+ static_cast<ElementsKind>(to_kind), *to_elements); |
+ return isolate->heap()->undefined_value(); |
+} |
+ |
+ |
RUNTIME_FUNCTION(MaybeObject*, Runtime_TraceEnter) { |
ASSERT(args.length() == 0); |
NoHandleAllocation ha; |
@@ -9781,8 +9932,8 @@ |
} else if (receiver->HasElement(j)) { |
// Call GetElement on receiver, not its prototype, or getters won't |
// have the correct receiver. |
- element_value = GetElement(receiver, j); |
- if (element_value.is_null()) return false; |
+ element_value = Object::GetElement(receiver, j); |
+ RETURN_IF_EMPTY_HANDLE_VALUE(isolate, element_value, false); |
visitor->visit(j, element_value); |
} |
} |
@@ -9800,8 +9951,8 @@ |
while (j < n) { |
HandleScope loop_scope; |
uint32_t index = indices[j]; |
- Handle<Object> element = GetElement(receiver, index); |
- if (element.is_null()) return false; |
+ Handle<Object> element = Object::GetElement(receiver, index); |
+ RETURN_IF_EMPTY_HANDLE_VALUE(isolate, element, false); |
visitor->visit(index, element); |
// Skip to next different index (i.e., omit duplicates). |
do { |
@@ -10051,9 +10202,9 @@ |
} |
Handle<JSObject> jsobject = Handle<JSObject>::cast(object); |
- Handle<Object> tmp1 = GetElement(jsobject, index1); |
+ Handle<Object> tmp1 = Object::GetElement(jsobject, index1); |
RETURN_IF_EMPTY_HANDLE(isolate, tmp1); |
- Handle<Object> tmp2 = GetElement(jsobject, index2); |
+ Handle<Object> tmp2 = Object::GetElement(jsobject, index2); |
RETURN_IF_EMPTY_HANDLE(isolate, tmp2); |
RETURN_IF_EMPTY_HANDLE(isolate, |
@@ -10078,7 +10229,11 @@ |
if (array->elements()->IsDictionary()) { |
// Create an array and get all the keys into it, then remove all the |
// keys that are not integers in the range 0 to length-1. |
- Handle<FixedArray> keys = GetKeysInFixedArrayFor(array, INCLUDE_PROTOS); |
+ bool threw = false; |
+ Handle<FixedArray> keys = |
+ GetKeysInFixedArrayFor(array, INCLUDE_PROTOS, &threw); |
+ if (threw) return Failure::Exception(); |
+ |
int keys_length = keys->length(); |
for (int i = 0; i < keys_length; i++) { |
Object* key = keys->get(i); |
@@ -10303,7 +10458,7 @@ |
// Try local lookup on each of the objects. |
Handle<JSObject> jsproto = obj; |
for (int i = 0; i < length; i++) { |
- LookupResult result; |
+ LookupResult result(isolate); |
jsproto->LocalLookup(*name, &result); |
if (result.IsProperty()) { |
// LookupResult is not GC safe as it holds raw object pointers. |
@@ -10360,7 +10515,7 @@ |
CONVERT_ARG_CHECKED(JSObject, obj, 0); |
CONVERT_ARG_CHECKED(String, name, 1); |
- LookupResult result; |
+ LookupResult result(isolate); |
obj->Lookup(*name, &result); |
if (result.IsProperty()) { |
return DebugLookupResultValue(isolate->heap(), *obj, *name, &result, NULL); |
@@ -10897,7 +11052,11 @@ |
if (function_context->has_extension() && |
!function_context->IsGlobalContext()) { |
Handle<JSObject> ext(JSObject::cast(function_context->extension())); |
- Handle<FixedArray> keys = GetKeysInFixedArrayFor(ext, INCLUDE_PROTOS); |
+ bool threw = false; |
+ Handle<FixedArray> keys = |
+ GetKeysInFixedArrayFor(ext, INCLUDE_PROTOS, &threw); |
+ if (threw) return Handle<JSObject>(); |
+ |
for (int i = 0; i < keys->length(); i++) { |
// Names of variables introduced by eval are strings. |
ASSERT(keys->get(i)->IsString()); |
@@ -10945,7 +11104,11 @@ |
// be variables introduced by eval. |
if (context->has_extension()) { |
Handle<JSObject> ext(JSObject::cast(context->extension())); |
- Handle<FixedArray> keys = GetKeysInFixedArrayFor(ext, INCLUDE_PROTOS); |
+ bool threw = false; |
+ Handle<FixedArray> keys = |
+ GetKeysInFixedArrayFor(ext, INCLUDE_PROTOS, &threw); |
+ if (threw) return Handle<JSObject>(); |
+ |
for (int i = 0; i < keys->length(); i++) { |
// Names of variables introduced by eval are strings. |
ASSERT(keys->get(i)->IsString()); |
@@ -11010,9 +11173,10 @@ |
} |
-// Iterate over the actual scopes visible from a stack frame. All scopes are |
+// Iterate over the actual scopes visible from a stack frame. The iteration |
+// proceeds from the innermost visible nested scope outwards. All scopes are |
// backed by an actual context except the local scope, which is inserted |
-// "artifically" in the context chain. |
+// "artificially" in the context chain. |
class ScopeIterator { |
public: |
enum ScopeType { |
@@ -11032,28 +11196,52 @@ |
inlined_frame_index_(inlined_frame_index), |
function_(JSFunction::cast(frame->function())), |
context_(Context::cast(frame->context())), |
- local_done_(false), |
- at_local_(false) { |
+ nested_scope_chain_(4) { |
- // Check whether the first scope is actually a local scope. |
- // If there is a stack slot for .result then this local scope has been |
- // created for evaluating top level code and it is not a real local scope. |
+ // Catch the case when the debugger stops in an internal function. |
+ Handle<SharedFunctionInfo> shared_info(function_->shared()); |
+ if (shared_info->script() == isolate->heap()->undefined_value()) { |
+ while (context_->closure() == *function_) { |
+ context_ = Handle<Context>(context_->previous(), isolate_); |
+ } |
+ return; |
+ } |
+ |
+ // Check whether we are in global code or function code. If there is a stack |
+ // slot for .result then this function has been created for evaluating |
+ // global code and it is not a real function. |
// Checking for the existence of .result seems fragile, but the scope info |
// saved with the code object does not otherwise have that information. |
- int index = function_->shared()->scope_info()-> |
+ int index = shared_info->scope_info()-> |
StackSlotIndex(isolate_->heap()->result_symbol()); |
+ |
+ // Reparse the code and analyze the scopes. |
+ ZoneScope zone_scope(isolate, DELETE_ON_EXIT); |
+ Handle<Script> script(Script::cast(shared_info->script())); |
+ Scope* scope; |
if (index >= 0) { |
- local_done_ = true; |
- } else if (context_->IsGlobalContext() || |
- context_->IsFunctionContext()) { |
- at_local_ = true; |
- } else if (context_->closure() != *function_) { |
- // The context_ is a block or with or catch block from the outer function. |
- ASSERT(context_->IsWithContext() || |
- context_->IsCatchContext() || |
- context_->IsBlockContext()); |
- at_local_ = true; |
+ // Global code |
+ CompilationInfo info(script); |
+ info.MarkAsGlobal(); |
+ bool result = ParserApi::Parse(&info); |
+ ASSERT(result); |
+ result = Scope::Analyze(&info); |
+ ASSERT(result); |
+ scope = info.function()->scope(); |
+ } else { |
+ // Function code |
+ CompilationInfo info(shared_info); |
+ bool result = ParserApi::Parse(&info); |
+ ASSERT(result); |
+ result = Scope::Analyze(&info); |
+ ASSERT(result); |
+ scope = info.function()->scope(); |
} |
+ |
+ // Retrieve the scope chain for the current position. |
+ int statement_position = |
+ shared_info->code()->SourceStatementPosition(frame_->pc()); |
+ scope->GetNestedScopeChain(&nested_scope_chain_, statement_position); |
} |
// More scopes? |
@@ -11061,40 +11249,48 @@ |
// Move to the next scope. |
void Next() { |
- // If at a local scope mark the local scope as passed. |
- if (at_local_) { |
- at_local_ = false; |
- local_done_ = true; |
- |
- // If the current context is not associated with the local scope the |
- // current context is the next real scope, so don't move to the next |
- // context in this case. |
- if (context_->closure() != *function_) { |
- return; |
- } |
- } |
- |
- // The global scope is always the last in the chain. |
- if (context_->IsGlobalContext()) { |
+ ScopeType scope_type = Type(); |
+ if (scope_type == ScopeTypeGlobal) { |
+ // The global scope is always the last in the chain. |
+ ASSERT(context_->IsGlobalContext()); |
context_ = Handle<Context>(); |
return; |
} |
- |
- // Move to the next context. |
- context_ = Handle<Context>(context_->previous(), isolate_); |
- |
- // If passing the local scope indicate that the current scope is now the |
- // local scope. |
- if (!local_done_ && |
- (context_->IsGlobalContext() || context_->IsFunctionContext())) { |
- at_local_ = true; |
+ if (nested_scope_chain_.is_empty()) { |
+ context_ = Handle<Context>(context_->previous(), isolate_); |
+ } else { |
+ if (nested_scope_chain_.last()->HasContext()) { |
+ context_ = Handle<Context>(context_->previous(), isolate_); |
+ } |
+ nested_scope_chain_.RemoveLast(); |
} |
} |
// Return the type of the current scope. |
ScopeType Type() { |
- if (at_local_) { |
- return ScopeTypeLocal; |
+ if (!nested_scope_chain_.is_empty()) { |
+ Handle<SerializedScopeInfo> scope_info = nested_scope_chain_.last(); |
+ switch (scope_info->Type()) { |
+ case FUNCTION_SCOPE: |
+ ASSERT(context_->IsFunctionContext() || |
+ !scope_info->HasContext()); |
+ return ScopeTypeLocal; |
+ case GLOBAL_SCOPE: |
+ ASSERT(context_->IsGlobalContext()); |
+ return ScopeTypeGlobal; |
+ case WITH_SCOPE: |
+ ASSERT(context_->IsWithContext()); |
+ return ScopeTypeWith; |
+ case CATCH_SCOPE: |
+ ASSERT(context_->IsCatchContext()); |
+ return ScopeTypeCatch; |
+ case BLOCK_SCOPE: |
+ ASSERT(!scope_info->HasContext() || |
+ context_->IsBlockContext()); |
+ return ScopeTypeBlock; |
+ case EVAL_SCOPE: |
+ UNREACHABLE(); |
+ } |
} |
if (context_->IsGlobalContext()) { |
ASSERT(context_->global()->IsGlobalObject()); |
@@ -11120,6 +11316,7 @@ |
return Handle<JSObject>(CurrentContext()->global()); |
case ScopeIterator::ScopeTypeLocal: |
// Materialize the content of the local scope into a JSObject. |
+ ASSERT(nested_scope_chain_.length() == 1); |
return MaterializeLocalScope(isolate_, frame_, inlined_frame_index_); |
case ScopeIterator::ScopeTypeWith: |
// Return the with object. |
@@ -11136,13 +11333,30 @@ |
return Handle<JSObject>(); |
} |
+ Handle<SerializedScopeInfo> CurrentScopeInfo() { |
+ if (!nested_scope_chain_.is_empty()) { |
+ return nested_scope_chain_.last(); |
+ } else if (context_->IsBlockContext()) { |
+ return Handle<SerializedScopeInfo>( |
+ SerializedScopeInfo::cast(context_->extension())); |
+ } else if (context_->IsFunctionContext()) { |
+ return Handle<SerializedScopeInfo>( |
+ context_->closure()->shared()->scope_info()); |
+ } |
+ return Handle<SerializedScopeInfo>::null(); |
+ } |
+ |
// Return the context for this scope. For the local context there might not |
// be an actual context. |
Handle<Context> CurrentContext() { |
- if (at_local_ && context_->closure() != *function_) { |
+ if (Type() == ScopeTypeGlobal || |
+ nested_scope_chain_.is_empty()) { |
+ return context_; |
+ } else if (nested_scope_chain_.last()->HasContext()) { |
+ return context_; |
+ } else { |
return Handle<Context>(); |
} |
- return context_; |
} |
#ifdef DEBUG |
@@ -11205,8 +11419,7 @@ |
int inlined_frame_index_; |
Handle<JSFunction> function_; |
Handle<Context> context_; |
- bool local_done_; |
- bool at_local_; |
+ List<Handle<SerializedScopeInfo> > nested_scope_chain_; |
DISALLOW_IMPLICIT_CONSTRUCTORS(ScopeIterator); |
}; |
@@ -11521,7 +11734,7 @@ |
if (!done) { |
// If the candidate is not compiled compile it to reveal any inner |
// functions which might contain the requested source position. |
- CompileLazyShared(target, KEEP_EXCEPTION); |
+ SharedFunctionInfo::CompileLazy(target, KEEP_EXCEPTION); |
} |
} // End while loop. |
@@ -11669,46 +11882,65 @@ |
// Creates a copy of the with context chain. The copy of the context chain is |
// is linked to the function context supplied. |
-static Handle<Context> CopyWithContextChain(Isolate* isolate, |
- Handle<JSFunction> function, |
- Handle<Context> current, |
- Handle<Context> base) { |
- // At the end of the chain. Return the base context to link to. |
- if (current->IsFunctionContext() || current->IsGlobalContext()) { |
- return base; |
+static Handle<Context> CopyNestedScopeContextChain(Isolate* isolate, |
+ Handle<JSFunction> function, |
+ Handle<Context> base, |
+ JavaScriptFrame* frame, |
+ int inlined_frame_index) { |
+ HandleScope scope(isolate); |
+ List<Handle<SerializedScopeInfo> > scope_chain; |
+ List<Handle<Context> > context_chain; |
+ |
+ ScopeIterator it(isolate, frame, inlined_frame_index); |
+ for (; it.Type() != ScopeIterator::ScopeTypeGlobal && |
+ it.Type() != ScopeIterator::ScopeTypeLocal ; it.Next()) { |
+ ASSERT(!it.Done()); |
+ scope_chain.Add(it.CurrentScopeInfo()); |
+ context_chain.Add(it.CurrentContext()); |
} |
- // Recursively copy the with and catch contexts. |
- HandleScope scope(isolate); |
- Handle<Context> previous(current->previous()); |
- Handle<Context> new_previous = |
- CopyWithContextChain(isolate, function, previous, base); |
- Handle<Context> new_current; |
- if (current->IsCatchContext()) { |
- Handle<String> name(String::cast(current->extension())); |
- Handle<Object> thrown_object(current->get(Context::THROWN_OBJECT_INDEX)); |
- new_current = |
- isolate->factory()->NewCatchContext(function, |
- new_previous, |
- name, |
- thrown_object); |
- } else if (current->IsBlockContext()) { |
- Handle<SerializedScopeInfo> scope_info( |
- SerializedScopeInfo::cast(current->extension())); |
- new_current = |
- isolate->factory()->NewBlockContext(function, new_previous, scope_info); |
- // Copy context slots. |
- int num_context_slots = scope_info->NumberOfContextSlots(); |
- for (int i = Context::MIN_CONTEXT_SLOTS; i < num_context_slots; ++i) { |
- new_current->set(i, current->get(i)); |
+ // At the end of the chain. Return the base context to link to. |
+ Handle<Context> context = base; |
+ |
+ // Iteratively copy and or materialize the nested contexts. |
+ while (!scope_chain.is_empty()) { |
+ Handle<SerializedScopeInfo> scope_info = scope_chain.RemoveLast(); |
+ Handle<Context> current = context_chain.RemoveLast(); |
+ ASSERT(!(scope_info->HasContext() & current.is_null())); |
+ |
+ if (scope_info->Type() == CATCH_SCOPE) { |
+ Handle<String> name(String::cast(current->extension())); |
+ Handle<Object> thrown_object(current->get(Context::THROWN_OBJECT_INDEX)); |
+ context = |
+ isolate->factory()->NewCatchContext(function, |
+ context, |
+ name, |
+ thrown_object); |
+ } else if (scope_info->Type() == BLOCK_SCOPE) { |
+ // Materialize the contents of the block scope into a JSObject. |
+ Handle<JSObject> block_scope_object = |
+ MaterializeBlockScope(isolate, current); |
+ if (block_scope_object.is_null()) { |
+ return Handle<Context>::null(); |
+ } |
+ // Allocate a new function context for the debug evaluation and set the |
+ // extension object. |
+ Handle<Context> new_context = |
+ isolate->factory()->NewFunctionContext(Context::MIN_CONTEXT_SLOTS, |
+ function); |
+ new_context->set_extension(*block_scope_object); |
+ new_context->set_previous(*context); |
+ context = new_context; |
+ } else { |
+ ASSERT(scope_info->Type() == WITH_SCOPE); |
+ ASSERT(current->IsWithContext()); |
+ Handle<JSObject> extension(JSObject::cast(current->extension())); |
+ context = |
+ isolate->factory()->NewWithContext(function, context, extension); |
} |
- } else { |
- ASSERT(current->IsWithContext()); |
- Handle<JSObject> extension(JSObject::cast(current->extension())); |
- new_current = |
- isolate->factory()->NewWithContext(function, new_previous, extension); |
} |
- return scope.CloseAndEscape(new_current); |
+ |
+ return scope.CloseAndEscape(context); |
} |
@@ -11846,7 +12078,11 @@ |
if (scope_info->HasHeapAllocatedLocals()) { |
function_context = Handle<Context>(frame_context->declaration_context()); |
} |
- context = CopyWithContextChain(isolate, go_between, frame_context, context); |
+ context = CopyNestedScopeContextChain(isolate, |
+ go_between, |
+ context, |
+ frame, |
+ inlined_frame_index); |
if (additional_context->IsJSObject()) { |
Handle<JSObject> extension = Handle<JSObject>::cast(additional_context); |
@@ -12245,7 +12481,7 @@ |
// Get the function and make sure it is compiled. |
CONVERT_ARG_CHECKED(JSFunction, func, 0); |
Handle<SharedFunctionInfo> shared(func->shared()); |
- if (!EnsureCompiled(shared, KEEP_EXCEPTION)) { |
+ if (!SharedFunctionInfo::EnsureCompiled(shared, KEEP_EXCEPTION)) { |
return Failure::Exception(); |
} |
func->code()->PrintLn(); |
@@ -12261,7 +12497,7 @@ |
// Get the function and make sure it is compiled. |
CONVERT_ARG_CHECKED(JSFunction, func, 0); |
Handle<SharedFunctionInfo> shared(func->shared()); |
- if (!EnsureCompiled(shared, KEEP_EXCEPTION)) { |
+ if (!SharedFunctionInfo::EnsureCompiled(shared, KEEP_EXCEPTION)) { |
return Failure::Exception(); |
} |
shared->construct_stub()->PrintLn(); |
@@ -12867,35 +13103,33 @@ |
Object* caller, |
bool* seen_caller) { |
// Only display JS frames. |
- if (!raw_frame->is_java_script()) |
+ if (!raw_frame->is_java_script()) { |
return false; |
+ } |
JavaScriptFrame* frame = JavaScriptFrame::cast(raw_frame); |
Object* raw_fun = frame->function(); |
// Not sure when this can happen but skip it just in case. |
- if (!raw_fun->IsJSFunction()) |
+ if (!raw_fun->IsJSFunction()) { |
return false; |
+ } |
if ((raw_fun == caller) && !(*seen_caller)) { |
*seen_caller = true; |
return false; |
} |
// Skip all frames until we've seen the caller. |
if (!(*seen_caller)) return false; |
- // Also, skip the most obvious builtin calls. We recognize builtins |
- // as (1) functions called with the builtins object as the receiver and |
- // as (2) functions from native scripts called with undefined as the |
- // receiver (direct calls to helper functions in the builtins |
- // code). Some builtin calls (such as Number.ADD which is invoked |
- // using 'call') are very difficult to recognize so we're leaving |
- // them in for now. |
- if (frame->receiver()->IsJSBuiltinsObject()) { |
- return false; |
+ // Also, skip non-visible built-in functions and any call with the builtins |
+ // object as receiver, so as to not reveal either the builtins object or |
+ // an internal function. |
+ // The --builtins-in-stack-traces command line flag allows including |
+ // internal call sites in the stack trace for debugging purposes. |
+ if (!FLAG_builtins_in_stack_traces) { |
+ JSFunction* fun = JSFunction::cast(raw_fun); |
+ if (frame->receiver()->IsJSBuiltinsObject() || |
+ (fun->IsBuiltin() && !fun->shared()->native())) { |
+ return false; |
+ } |
} |
- JSFunction* fun = JSFunction::cast(raw_fun); |
- Object* raw_script = fun->shared()->script(); |
- if (frame->receiver()->IsUndefined() && raw_script->IsScript()) { |
- int script_type = Script::cast(raw_script)->type()->value(); |
- return script_type != Script::TYPE_NATIVE; |
- } |
return true; |
} |
@@ -13041,7 +13275,9 @@ |
} |
#ifdef DEBUG |
- cache_handle->JSFunctionResultCacheVerify(); |
+ if (FLAG_verify_heap) { |
+ cache_handle->JSFunctionResultCacheVerify(); |
+ } |
#endif |
// Function invocation may have cleared the cache. Reread all the data. |
@@ -13070,7 +13306,9 @@ |
cache_handle->set_finger_index(index); |
#ifdef DEBUG |
- cache_handle->JSFunctionResultCacheVerify(); |
+ if (FLAG_verify_heap) { |
+ cache_handle->JSFunctionResultCacheVerify(); |
+ } |
#endif |
return *value; |