/** * Copyright 2013 Douglas Campos, and individual contributors * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.dynjs.runtime; import org.dynjs.exception.ThrowException; public class DynArray extends DynObject { public DynArray(GlobalContext globalContext) { super(globalContext); setClassName("Array"); super.defineOwnProperty(null, "length", PropertyDescriptor.newDataPropertyDescriptor(0L, true, true, true), false); setPrototype(globalContext.getPrototypeFor("Array")); } @Override public boolean defineOwnProperty(ExecutionContext context, String name, PropertyDescriptor desc, boolean shouldThrow) { // 15.4.5.1 PropertyDescriptor oldLenDesc = (PropertyDescriptor) getOwnProperty(context, "length"); long oldLen = (long) oldLenDesc.getValue(); if (name.equals("length")) { if (desc.getValue() == null ) { return super.defineOwnProperty(context, "length", desc, shouldThrow); } PropertyDescriptor newLenDesc = desc; Long newLen = Types.toUint32(context, desc.getValue()); if (!Types.compareEquality(context, newLen, Types.toNumber(context, desc.getValue()))) { throw new ThrowException(context, context.createRangeError("invalid length: " + newLen)); } newLenDesc.setValue(newLen); if (newLen >= oldLen) { return super.defineOwnProperty(context, "length", newLenDesc, shouldThrow); } if (!oldLenDesc.hasWritable() && !oldLenDesc.isWritable()) { return reject(context, shouldThrow); } boolean newWritable = false; if ((!newLenDesc.hasWritable()) || newLenDesc.isWritable()) { newWritable = true; } else { newWritable = false; newLenDesc.setWritable(true); } boolean succeeded = super.defineOwnProperty(context, "length", newLenDesc, shouldThrow); if (!succeeded) { return false; } while (newLen < oldLen) { oldLen = oldLen - 1; boolean deleteSucceeded = delete(context, "" + oldLen, false); if (!deleteSucceeded) { newLenDesc.setValue(oldLen + 1); if (!newWritable) { newLenDesc.setWritable(false); } super.defineOwnProperty(context, "length", newLenDesc, false); return reject(context, shouldThrow); } } if (newWritable == false) { PropertyDescriptor lengthDesc = new PropertyDescriptor(); lengthDesc.setWritable(false); super.defineOwnProperty(context, "length", lengthDesc, false); } return true; } // 'length' if (isArrayIndex(context, name)) { Long index = Types.toUint32(context, name); if (index.longValue() >= oldLen && oldLenDesc.hasWritable() && !oldLenDesc.isWritable()) { return reject(context, shouldThrow); } boolean succeeded = super.defineOwnProperty(context, name, desc, shouldThrow); if (!succeeded) { return reject(context, shouldThrow); } if (index.longValue() >= oldLen && index.longValue() < 4294967295L) { oldLenDesc.setValue(index.longValue() + 1); super.defineOwnProperty(context, "length", oldLenDesc, false); } return true; } return super.defineOwnProperty(context, name, desc, shouldThrow); } public long length() { return Types.toInt32(null, this.get(null, "length")); } protected boolean isArrayIndex(ExecutionContext context, String name) { return name.equals(Types.toUint32(context, name).toString()); } }