// ECMA 15.4.5.1 void JSArray::put(JSCell* cell, ExecState* exec, PropertyName propertyName, JSValue value, PutPropertySlot& slot) { JSArray* thisObject = jsCast<JSArray*>(cell); if (propertyName == exec->propertyNames().length) { unsigned newLength = value.toUInt32(exec); if (value.toNumber(exec) != static_cast<double>(newLength)) { exec->vm().throwException(exec, createRangeError(exec, ASCIILiteral("Invalid array length"))); return; } thisObject->setLength(exec, newLength, slot.isStrictMode()); return; } JSObject::put(thisObject, exec, propertyName, value, slot); }
// Defined in ES5.1 15.4.5.1 bool JSArray::defineOwnProperty(JSObject* object, ExecState* exec, PropertyName propertyName, const PropertyDescriptor& descriptor, bool throwException) { JSArray* array = jsCast<JSArray*>(object); // 3. If P is "length", then if (propertyName == exec->propertyNames().length) { // All paths through length definition call the default [[DefineOwnProperty]], hence: // from ES5.1 8.12.9 7.a. if (descriptor.configurablePresent() && descriptor.configurable()) return reject(exec, throwException, "Attempting to change configurable attribute of unconfigurable property."); // from ES5.1 8.12.9 7.b. if (descriptor.enumerablePresent() && descriptor.enumerable()) return reject(exec, throwException, "Attempting to change enumerable attribute of unconfigurable property."); // a. If the [[Value]] field of Desc is absent, then // a.i. Return the result of calling the default [[DefineOwnProperty]] internal method (8.12.9) on A passing "length", Desc, and Throw as arguments. if (descriptor.isAccessorDescriptor()) return reject(exec, throwException, "Attempting to change access mechanism for an unconfigurable property."); // from ES5.1 8.12.9 10.a. if (!array->isLengthWritable() && descriptor.writablePresent() && descriptor.writable()) return reject(exec, throwException, "Attempting to change writable attribute of unconfigurable property."); // This descriptor is either just making length read-only, or changing nothing! if (!descriptor.value()) { if (descriptor.writablePresent()) array->setLengthWritable(exec, descriptor.writable()); return true; } // b. Let newLenDesc be a copy of Desc. // c. Let newLen be ToUint32(Desc.[[Value]]). unsigned newLen = descriptor.value().toUInt32(exec); // d. If newLen is not equal to ToNumber( Desc.[[Value]]), throw a RangeError exception. if (newLen != descriptor.value().toNumber(exec)) { exec->vm().throwException(exec, createRangeError(exec, ASCIILiteral("Invalid array length"))); return false; } // Based on SameValue check in 8.12.9, this is always okay. // FIXME: Nothing prevents this from being called on a RuntimeArray, and the length function will always return 0 in that case. if (newLen == array->length()) { if (descriptor.writablePresent()) array->setLengthWritable(exec, descriptor.writable()); return true; } // e. Set newLenDesc.[[Value] to newLen. // f. If newLen >= oldLen, then // f.i. Return the result of calling the default [[DefineOwnProperty]] internal method (8.12.9) on A passing "length", newLenDesc, and Throw as arguments. // g. Reject if oldLenDesc.[[Writable]] is false. if (!array->isLengthWritable()) return reject(exec, throwException, "Attempting to change value of a readonly property."); // h. If newLenDesc.[[Writable]] is absent or has the value true, let newWritable be true. // i. Else, // i.i. Need to defer setting the [[Writable]] attribute to false in case any elements cannot be deleted. // i.ii. Let newWritable be false. // i.iii. Set newLenDesc.[[Writable] to true. // j. Let succeeded be the result of calling the default [[DefineOwnProperty]] internal method (8.12.9) on A passing "length", newLenDesc, and Throw as arguments. // k. If succeeded is false, return false. // l. While newLen < oldLen repeat, // l.i. Set oldLen to oldLen – 1. // l.ii. Let deleteSucceeded be the result of calling the [[Delete]] internal method of A passing ToString(oldLen) and false as arguments. // l.iii. If deleteSucceeded is false, then if (!array->setLength(exec, newLen, throwException)) { // 1. Set newLenDesc.[[Value] to oldLen+1. // 2. If newWritable is false, set newLenDesc.[[Writable] to false. // 3. Call the default [[DefineOwnProperty]] internal method (8.12.9) on A passing "length", newLenDesc, and false as arguments. // 4. Reject. if (descriptor.writablePresent()) array->setLengthWritable(exec, descriptor.writable()); return false; } // m. If newWritable is false, then // i. Call the default [[DefineOwnProperty]] internal method (8.12.9) on A passing "length", // Property Descriptor{[[Writable]]: false}, and false as arguments. This call will always // return true. if (descriptor.writablePresent()) array->setLengthWritable(exec, descriptor.writable()); // n. Return true. return true; } // 4. Else if P is an array index (15.4), then // a. Let index be ToUint32(P). if (Optional<uint32_t> optionalIndex = parseIndex(propertyName)) { // b. Reject if index >= oldLen and oldLenDesc.[[Writable]] is false. uint32_t index = optionalIndex.value(); // FIXME: Nothing prevents this from being called on a RuntimeArray, and the length function will always return 0 in that case. if (index >= array->length() && !array->isLengthWritable()) return reject(exec, throwException, "Attempting to define numeric property on array with non-writable length property."); // c. Let succeeded be the result of calling the default [[DefineOwnProperty]] internal method (8.12.9) on A passing P, Desc, and false as arguments. // d. Reject if succeeded is false. // e. If index >= oldLen // e.i. Set oldLenDesc.[[Value]] to index + 1. // e.ii. Call the default [[DefineOwnProperty]] internal method (8.12.9) on A passing "length", oldLenDesc, and false as arguments. This call will always return true. // f. Return true. return array->defineOwnIndexedProperty(exec, index, descriptor, throwException); } return array->JSObject::defineOwnNonIndexProperty(exec, propertyName, descriptor, throwException); }
DeserializationResult CloneDeserializer::deserialize() { Vector<uint32_t, 16> indexStack; Vector<Identifier, 16> propertyNameStack; Vector<JSObject*, 16> outputObjectStack; Vector<JSArray*, 16> outputArrayStack; Vector<WalkerState, 16> stateStack; WalkerState state = StateUnknown; JSValue outValue; unsigned tickCount = ticksUntilNextCheck(); while (1) { switch (state) { arrayStartState: case ArrayStartState: { uint32_t length; if (!read(length)) { fail(); goto error; } JSArray* outArray = constructEmptyArray(m_exec, m_globalObject); outArray->setLength(length); m_gcBuffer.append(outArray); outputArrayStack.append(outArray); // fallthrough } arrayStartVisitMember: case ArrayStartVisitMember: { if (!--tickCount) { if (didTimeOut()) return make_pair(JSValue(), InterruptedExecutionError); tickCount = ticksUntilNextCheck(); } uint32_t index; if (!read(index)) { fail(); goto error; } if (index == TerminatorTag) { JSArray* outArray = outputArrayStack.last(); outValue = outArray; outputArrayStack.removeLast(); break; } if (JSValue terminal = readTerminal()) { putProperty(outputArrayStack.last(), index, terminal); goto arrayStartVisitMember; } if (m_failed) goto error; indexStack.append(index); stateStack.append(ArrayEndVisitMember); goto stateUnknown; } case ArrayEndVisitMember: { JSArray* outArray = outputArrayStack.last(); putProperty(outArray, indexStack.last(), outValue); indexStack.removeLast(); goto arrayStartVisitMember; } objectStartState: case ObjectStartState: { if (outputObjectStack.size() + outputArrayStack.size() > maximumFilterRecursion) return make_pair(JSValue(), StackOverflowError); JSObject* outObject = constructEmptyObject(m_exec, m_globalObject); m_gcBuffer.append(outObject); outputObjectStack.append(outObject); // fallthrough } objectStartVisitMember: case ObjectStartVisitMember: { if (!--tickCount) { if (didTimeOut()) return make_pair(JSValue(), InterruptedExecutionError); tickCount = ticksUntilNextCheck(); } CachedStringRef cachedString; bool wasTerminator = false; if (!readStringData(cachedString, wasTerminator)) { if (!wasTerminator) goto error; JSObject* outObject = outputObjectStack.last(); outValue = outObject; outputObjectStack.removeLast(); break; } if (JSValue terminal = readTerminal()) { putProperty(outputObjectStack.last(), Identifier(m_exec, cachedString->ustring()), terminal); goto objectStartVisitMember; } stateStack.append(ObjectEndVisitMember); propertyNameStack.append(Identifier(m_exec, cachedString->ustring())); goto stateUnknown; } case ObjectEndVisitMember: { putProperty(outputObjectStack.last(), propertyNameStack.last(), outValue); propertyNameStack.removeLast(); goto objectStartVisitMember; } stateUnknown: case StateUnknown: if (JSValue terminal = readTerminal()) { outValue = terminal; break; } SerializationTag tag = readTag(); if (tag == ArrayTag) goto arrayStartState; if (tag == ObjectTag) goto objectStartState; goto error; } if (stateStack.isEmpty()) break; state = stateStack.last(); stateStack.removeLast(); if (!--tickCount) { if (didTimeOut()) return make_pair(JSValue(), InterruptedExecutionError); tickCount = ticksUntilNextCheck(); } } ASSERT(outValue); ASSERT(!m_failed); return make_pair(outValue, SuccessfullyCompleted); error: fail(); return make_pair(JSValue(), ValidationError); }