SmallMapList* map_set = instr->hydrogen()->map_set();
for (int i = 0; i < map_set->length() - 1; i++) {
Handle<Map> map = map_set->at(i);
- __ CompareMap(reg, scratch, map, &success, REQUIRE_EXACT_MAP);
+ __ CompareMap(reg, scratch, map, &success, instr->hydrogen()->mode());
__ b(eq, &success);
}
Handle<Map> map = map_set->last();
- DoCheckMapCommon(reg, scratch, map, REQUIRE_EXACT_MAP, instr->environment());
+ DoCheckMapCommon(reg, scratch, map, instr->hydrogen()->mode(),
+ instr->environment());
__ bind(&success);
}
class HCheckMaps: public HTemplateInstruction<2> {
public:
HCheckMaps(HValue* value, Handle<Map> map, Zone* zone,
- HValue* typecheck = NULL) {
+ HValue* typecheck = NULL,
+ CompareMapMode mode = REQUIRE_EXACT_MAP) : mode_(mode) {
SetOperandAt(0, value);
// If callers don't depend on a typecheck, they can pass in NULL. In that
// case we use a copy of the |value| argument as a dummy value.
SetGVNFlag(kDependsOnElementsKind);
map_set()->Add(map, zone);
}
- HCheckMaps(HValue* value, SmallMapList* maps, Zone* zone) {
+ HCheckMaps(HValue* value, SmallMapList* maps, Zone* zone,
+ CompareMapMode mode = REQUIRE_EXACT_MAP) : mode_(mode) {
SetOperandAt(0, value);
SetOperandAt(1, value);
set_representation(Representation::Tagged());
HValue* value() { return OperandAt(0); }
SmallMapList* map_set() { return &map_set_; }
+ CompareMapMode mode() { return mode_; }
DECLARE_CONCRETE_INSTRUCTION(CheckMaps)
private:
SmallMapList map_set_;
+ CompareMapMode mode_;
};
HInstruction* instr = NULL;
if (expr->AsProperty()->IsArrayLength()) {
+ // Note that in the monomorphic case IsArrayLength() is false because we
+ // handle that it with a regular property load IC.
HValue* array = Pop();
AddInstruction(new(zone()) HCheckNonSmi(array));
HInstruction* mapcheck =
map = types->first();
if (map->is_dictionary_map()) monomorphic = false;
}
- if (monomorphic) {
- Handle<JSFunction> getter;
- Handle<JSObject> holder;
- if (LookupGetter(map, name, &getter, &holder)) {
- AddCheckConstantFunction(holder, Top(), map);
- if (FLAG_inline_accessors && TryInlineGetter(getter, expr)) return;
- AddInstruction(new(zone()) HPushArgument(Pop()));
- instr = new(zone()) HCallConstantFunction(getter, 1);
+
+ // Try to see if this is an array length access.
+ if (name->Equals(isolate()->heap()->length_symbol())) {
+ bool is_array = false;
+ bool fast_mode = false;
+ bool map_mode = false;
+ HInstruction* mapcheck = NULL;
+
+ if (expr->IsMonomorphic()) {
+ // Even if there is more than one map they all must be element
+ // transition maps, so checking just one is ok.
+ if (map->instance_type() == JS_ARRAY_TYPE) {
+ is_array = true;
+ map_mode = true;
+ fast_mode = IsFastElementsKind(map->elements_kind());
+ }
} else {
- instr = BuildLoadNamedMonomorphic(Pop(), name, expr, map);
+ // Since we will emit an instance check we have to conservatively
+ // assume that some arrays will contain slow elements, so we set
+ // fast_mode to false.
+ map_mode = false;
+ fast_mode = false;
+ is_array = true;
+ for (int i = 0; i < types->length(); i++) {
+ Handle<Map> current_map = types->at(i);
+ if (!current_map->instance_type() == JS_ARRAY_TYPE) {
+ is_array = false;
+ break;
+ }
+ }
+ }
+
+ // It is an array length access so we produce a HJSArrayLength.
+ if (is_array) {
+ HValue* array = Pop();
+ AddInstruction(new(zone()) HCheckNonSmi(array));
+ mapcheck = map_mode ?
+ HInstruction::cast(
+ new(zone()) HCheckMaps(array, map, zone(), NULL,
+ ALLOW_ELEMENT_TRANSITION_MAPS)) :
+ HInstruction::cast(
+ HCheckInstanceType::NewIsJSArray(array, zone()));
+ AddInstruction(mapcheck);
+ instr = new(zone()) HJSArrayLength(
+ array, mapcheck, fast_mode ? HType::Smi() : HType::Tagged());
}
- } else if (types != NULL && types->length() > 1) {
- return HandlePolymorphicLoadNamedField(expr, Pop(), types, name);
- } else {
- instr = BuildLoadNamedGeneric(Pop(), name, expr);
}
+ // We cannot know if it was an array length access so we handle it as
+ // a regular property access.
+ if (instr == NULL) {
+ if (monomorphic) {
+ Handle<JSFunction> getter;
+ Handle<JSObject> holder;
+ if (LookupGetter(map, name, &getter, &holder)) {
+ AddCheckConstantFunction(holder, Top(), map);
+ if (FLAG_inline_accessors && TryInlineGetter(getter, expr)) return;
+ AddInstruction(new(zone()) HPushArgument(Pop()));
+ instr = new(zone()) HCallConstantFunction(getter, 1);
+ } else {
+ instr = BuildLoadNamedMonomorphic(Pop(), name, expr, map);
+ }
+ } else if (types != NULL && types->length() > 1) {
+ return HandlePolymorphicLoadNamedField(expr, Pop(), types, name);
+ } else {
+ instr = BuildLoadNamedGeneric(Pop(), name, expr);
+ }
+ }
} else {
CHECK_ALIVE(VisitForValue(expr->key()));
SmallMapList* map_set = instr->hydrogen()->map_set();
for (int i = 0; i < map_set->length() - 1; i++) {
Handle<Map> map = map_set->at(i);
- __ CompareMap(reg, map, &success, REQUIRE_EXACT_MAP);
+ __ CompareMap(reg, map, &success, instr->hydrogen()->mode());
__ j(equal, &success);
}
Handle<Map> map = map_set->last();
- DoCheckMapCommon(reg, map, REQUIRE_EXACT_MAP, instr->environment());
+ DoCheckMapCommon(reg, map, instr->hydrogen()->mode(), instr->environment());
__ bind(&success);
}
return Smi::FromInt(String::cast(*string)->length());
}
- // Use specialized code for getting the length of arrays.
- if (object->IsJSArray() &&
- name->Equals(isolate()->heap()->length_symbol())) {
- Handle<Code> stub;
- if (state == UNINITIALIZED) {
- stub = pre_monomorphic_stub();
- } else if (state == PREMONOMORPHIC) {
- stub = isolate()->builtins()->LoadIC_ArrayLength();
- } else if (state != MEGAMORPHIC) {
- stub = megamorphic_stub();
- }
- if (!stub.is_null()) {
- set_target(*stub);
-#ifdef DEBUG
- if (FLAG_trace_ic) PrintF("[LoadIC : +#length /array]\n");
-#endif
- }
- return JSArray::cast(*object)->length();
- }
-
// Use specialized code for getting prototype of functions.
if (object->IsJSFunction() &&
name->Equals(isolate()->heap()->prototype_symbol()) &&
if (!holder->HasFastProperties()) return;
code = isolate()->stub_cache()->ComputeLoadViaGetter(
name, receiver, holder, Handle<JSFunction>::cast(getter));
+ } else if (holder->IsJSArray() &&
+ name->Equals(isolate()->heap()->length_symbol())) {
+ ASSERT(callback->IsForeign());
+ ASSERT(reinterpret_cast<AccessorDescriptor*>(
+ Handle<Foreign>::cast(callback)->foreign_address())
+ == &Accessors::ArrayLength);
+ // Use a "load field" IC for getting the array length.
+ // Note that the resulting code object is marked as "Code::FIELD"
+ // and not as "Code::CALLBACKS".
+ code = isolate()->stub_cache()->ComputeLoadField(
+ name, receiver, holder, JSArray::ArrayLengthIndex());
} else {
ASSERT(callback->IsForeign());
// No IC support for old-style native accessors.
for (int i = 0; i < map_set->length() - 1; i++) {
Handle<Map> map = map_set->at(i);
__ CompareMapAndBranch(
- reg, scratch, map, &success, eq, &success, REQUIRE_EXACT_MAP);
+ reg, scratch, map, &success, eq, &success, instr->hydrogen()->mode());
}
Handle<Map> map = map_set->last();
- DoCheckMapCommon(reg, scratch, map, REQUIRE_EXACT_MAP, instr->environment());
+ DoCheckMapCommon(reg, scratch, map, instr->hydrogen()->mode(),
+ instr->environment());
__ bind(&success);
}
ACCESSORS(JSArray, length, Object, kLengthOffset)
+PropertyIndex JSArray::ArrayLengthIndex() {
+ return PropertyIndex::NewHeaderIndex(kLengthOffset / kPointerSize);
+}
+
+
ACCESSORS(JSRegExp, data, Object, kDataOffset)
};
+class PropertyIndex;
+
+
// The JSArray describes JavaScript Arrays
// Such an array can be in one of two modes:
// - fast, backing storage is a FixedArray and length <= elements.length();
static const int kLengthOffset = JSObject::kHeaderSize;
static const int kSize = kLengthOffset + kPointerSize;
+ static inline PropertyIndex ArrayLengthIndex();
+ STATIC_ASSERT(kLengthOffset % kPointerSize == 0);
+
private:
// Expand the fixed array backing of a fast-case JSArray to at least
// the requested size.
SmallMapList* map_set = instr->hydrogen()->map_set();
for (int i = 0; i < map_set->length() - 1; i++) {
Handle<Map> map = map_set->at(i);
- __ CompareMap(reg, map, &success, REQUIRE_EXACT_MAP);
+ __ CompareMap(reg, map, &success, instr->hydrogen()->mode());
__ j(equal, &success);
}
Handle<Map> map = map_set->last();
- DoCheckMapCommon(reg, map, REQUIRE_EXACT_MAP, instr->environment());
+ DoCheckMapCommon(reg, map, instr->hydrogen()->mode(), instr->environment());
__ bind(&success);
}
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+// Flags: --allow-natives-syntax
+
var a = [0,1,2,3];
assertEquals(0, a.length = 0);
t = a.length = 7;
assertEquals(7, t);
}
+
+function TestLengthAccess() {
+ var TestGetLength = function(a, l, getLength, getKeyedLength) {
+ assertEquals(l, getLength(a));
+ assertEquals(l, getKeyedLength(a));
+ }
+
+ var Test = function(a, l, getLength, getKeyedLength) {
+ // Make the ICs reach the state we want.
+ TestGetLength(a, l, getLength, getKeyedLength);
+ TestGetLength(a, l, getLength, getKeyedLength);
+ TestGetLength(a, l, getLength, getKeyedLength);
+ // Also test crankshaft.
+ %OptimizeFunctionOnNextCall(getLength);
+ %OptimizeFunctionOnNextCall(getKeyedLength);
+ TestGetLength(a, l, getLength, getKeyedLength);
+ }
+
+ var getLength = function(a) { return a.length; }
+ var getKeyedLength = function(a) { return a['length']; }
+ Test(new Array(4), 4, getLength, getKeyedLength);
+
+ getLength = function(a) { return a.length; }
+ getKeyedLength = function(a) { return a['length']; }
+ Test([1, 2, 3, 4], 4, getLength, getKeyedLength);
+
+ getLength = function(a) { return a.length; }
+ getKeyedLength = function(a) { return a['length']; }
+ Test(Object.create(new Array(4)), 4, getLength, getKeyedLength);
+
+ getLength = function(a) { return a.length; }
+ getKeyedLength = function(a) { return a['length']; }
+ // Set the 'length' of the global object.
+ length = 4;
+ var globalObject = (1,eval)("this");
+ Test(globalObject, 4, getLength, getKeyedLength);
+}
+TestLengthAccess();
+