1 /* 2 * Copyright (c) 2010, 2013, Oracle and/or its affiliates. All rights reserved. 3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 4 * 5 * This code is free software; you can redistribute it and/or modify it 6 * under the terms of the GNU General Public License version 2 only, as 7 * published by the Free Software Foundation. Oracle designates this 8 * particular file as subject to the "Classpath" exception as provided 9 * by Oracle in the LICENSE file that accompanied this code. 10 * 11 * This code is distributed in the hope that it will be useful, but WITHOUT 12 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 13 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 14 * version 2 for more details (a copy is included in the LICENSE file that 15 * accompanied this code). 16 * 17 * You should have received a copy of the GNU General Public License version 18 * 2 along with this work; if not, write to the Free Software Foundation, 19 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 20 * 21 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA 22 * or visit www.oracle.com if you need additional information or have any 23 * questions. 24 */ 25 26 package jdk.nashorn.internal.objects; 27 28 import static jdk.nashorn.internal.runtime.ECMAErrors.rangeError; 29 30 import jdk.nashorn.internal.objects.annotations.Attribute; 31 import jdk.nashorn.internal.objects.annotations.Getter; 32 import jdk.nashorn.internal.objects.annotations.ScriptClass; 33 import jdk.nashorn.internal.runtime.JSType; 34 import jdk.nashorn.internal.runtime.PropertyMap; 35 import jdk.nashorn.internal.runtime.ScriptObject; 36 import jdk.nashorn.internal.runtime.ScriptRuntime; 37 import jdk.nashorn.internal.runtime.arrays.ArrayData; 38 39 @ScriptClass("ArrayBufferView") 40 abstract class ArrayBufferView extends ScriptObject { 41 42 // initialized by nasgen 43 private static PropertyMap $nasgenmap$; 44 45 private ArrayBufferView(final NativeArrayBuffer buffer, final int byteOffset, final int elementLength, final Global global) { 46 super($nasgenmap$); 47 checkConstructorArgs(buffer, byteOffset, elementLength); 48 this.setProto(getPrototype(global)); 49 this.setArray(factory().createArrayData(buffer, byteOffset, elementLength)); 50 } 51 52 ArrayBufferView(final NativeArrayBuffer buffer, final int byteOffset, final int elementLength) { 53 this(buffer, byteOffset, elementLength, Global.instance()); 54 } 55 56 private void checkConstructorArgs(final NativeArrayBuffer buffer, final int byteOffset, final int elementLength) { 57 if (byteOffset < 0 || elementLength < 0) { 58 throw new RuntimeException("byteOffset or length must not be negative"); 59 } 60 if (byteOffset + elementLength * bytesPerElement() > buffer.getByteLength()) { 61 throw new RuntimeException("byteOffset + byteLength out of range"); 62 } 63 if (byteOffset % bytesPerElement() != 0) { 64 throw new RuntimeException("byteOffset must be a multiple of the element size"); 65 } 66 } 67 68 private int bytesPerElement() { 69 return factory().bytesPerElement; 70 } 71 72 @Getter(attributes = Attribute.NOT_ENUMERABLE | Attribute.NOT_WRITABLE | Attribute.NOT_CONFIGURABLE) 73 public static Object buffer(final Object self) { 74 return ((ArrayDataImpl)((ArrayBufferView)self).getArray()).buffer; 75 } 76 77 @Getter(attributes = Attribute.NOT_ENUMERABLE | Attribute.NOT_WRITABLE | Attribute.NOT_CONFIGURABLE) 78 public static Object byteOffset(final Object self) { 79 return ((ArrayDataImpl)((ArrayBufferView)self).getArray()).byteOffset; 80 } 81 82 @Getter(attributes = Attribute.NOT_ENUMERABLE | Attribute.NOT_WRITABLE | Attribute.NOT_CONFIGURABLE) 83 public static Object byteLength(final Object self) { 84 final ArrayBufferView view = (ArrayBufferView)self; 85 return ((ArrayDataImpl)view.getArray()).elementLength * view.bytesPerElement(); 86 } 87 88 @Getter(attributes = Attribute.NOT_ENUMERABLE | Attribute.NOT_WRITABLE | Attribute.NOT_CONFIGURABLE) 89 public static Object length(final Object self) { 90 return ((ArrayBufferView)self).elementLength(); 91 } 92 93 @Override 94 public final Object getLength() { 95 return elementLength(); 96 } 97 98 private int elementLength() { 99 return ((ArrayDataImpl)getArray()).elementLength; 100 } 101 102 protected static abstract class ArrayDataImpl extends ArrayData { 103 protected final NativeArrayBuffer buffer; 104 protected final int byteOffset; 105 private final int elementLength; 106 107 protected ArrayDataImpl(final NativeArrayBuffer buffer, final int byteOffset, final int elementLength) { 108 super(elementLength); 109 this.buffer = buffer; 110 this.byteOffset = byteOffset; 111 this.elementLength = elementLength; 112 } 113 114 @Override 115 public ArrayData copy() { 116 throw new UnsupportedOperationException(); // Not used for ArrayBuffers 117 } 118 119 @Override 120 public Object[] asObjectArray() { 121 final Object[] array = new Object[elementLength]; 122 for (int i = 0; i < elementLength; i++) { 123 array[i] = getObjectImpl(i); 124 } 125 return array; 126 } 127 128 @Override 129 public ArrayData ensure(final long safeIndex) { 130 return this; 131 } 132 133 @Override 134 public void setLength(final long length) { 135 //empty? 136 //TODO is this right? 137 } 138 139 @Override 140 public ArrayData shrink(final long newLength) { 141 return this; 142 } 143 144 @Override 145 public ArrayData set(final int index, final Object value, final boolean strict) { 146 if (has(index)) { 147 setImpl(index, value); 148 } 149 return this; 150 } 151 152 @Override 153 public ArrayData set(final int index, final int value, final boolean strict) { 154 if (has(index)) { 155 setImpl(index, value); 156 } 157 return this; 158 } 159 160 @Override 161 public ArrayData set(final int index, final long value, final boolean strict) { 162 if (has(index)) { 163 setImpl(index, value); 164 } 165 return this; 166 } 167 168 @Override 169 public ArrayData set(final int index, final double value, final boolean strict) { 170 if (has(index)) { 171 setImpl(index, value); 172 } 173 return this; 174 } 175 176 @Override 177 public int getInt(final int index) { 178 return getIntImpl(index); 179 } 180 181 @Override 182 public long getLong(final int index) { 183 return getLongImpl(index); 184 } 185 186 @Override 187 public double getDouble(final int index) { 188 return getDoubleImpl(index); 189 } 190 191 @Override 192 public Object getObject(final int index) { 193 return getObjectImpl(index); 194 } 195 196 @Override 197 public boolean has(final int index) { 198 return index >= 0 && index < elementLength; 199 } 200 201 @Override 202 public boolean canDelete(final int index, final boolean strict) { 203 return false; 204 } 205 206 @Override 207 public boolean canDelete(final long fromIndex, final long toIndex, final boolean strict) { 208 return false; 209 } 210 211 @Override 212 public ArrayData delete(final int index) { 213 return this; 214 } 215 216 @Override 217 public ArrayData delete(final long fromIndex, final long toIndex) { 218 return this; 219 } 220 221 @Override 222 protected ArrayData convert(final Class<?> type) { 223 return this; 224 } 225 226 @Override 227 public void shiftLeft(final int by) { 228 throw new UnsupportedOperationException(); 229 } 230 231 @Override 232 public ArrayData shiftRight(final int by) { 233 throw new UnsupportedOperationException(); 234 } 235 236 @Override 237 public Object pop() { 238 throw new UnsupportedOperationException(); 239 } 240 241 @Override 242 public ArrayData slice(final long from, final long to) { 243 throw new UnsupportedOperationException(); 244 } 245 246 protected abstract int getIntImpl(int key); 247 248 protected long getLongImpl(final int key) { 249 return getIntImpl(key); 250 } 251 252 protected double getDoubleImpl(final int key) { 253 return getIntImpl(key); 254 } 255 256 protected Object getObjectImpl(final int key) { 257 return getIntImpl(key); 258 } 259 260 protected abstract void setImpl(int key, int value); 261 262 protected void setImpl(final int key, final long value) { 263 setImpl(key, (int)value); 264 } 265 266 protected void setImpl(final int key, final double value) { 267 setImpl(key, JSType.toInt32(value)); 268 } 269 270 protected void setImpl(final int key, final Object value) { 271 setImpl(key, JSType.toInt32(value)); 272 } 273 274 protected abstract int byteIndex(int index); 275 } 276 277 protected static abstract class Factory { 278 final int bytesPerElement; 279 final int maxElementLength; 280 281 public Factory(final int bytesPerElement) { 282 this.bytesPerElement = bytesPerElement; 283 this.maxElementLength = Integer.MAX_VALUE / bytesPerElement; 284 } 285 286 public final ArrayBufferView construct(final int elementLength) { 287 if(elementLength > maxElementLength) { 288 throw rangeError("inappropriate.array.buffer.length", JSType.toString(elementLength)); 289 } 290 return construct(new NativeArrayBuffer(elementLength * bytesPerElement), 0, elementLength); 291 } 292 293 public abstract ArrayBufferView construct(NativeArrayBuffer buffer, int byteOffset, int elementLength); 294 295 public abstract ArrayData createArrayData(NativeArrayBuffer buffer, int byteOffset, int elementLength); 296 } 297 298 protected abstract Factory factory(); 299 300 protected abstract ScriptObject getPrototype(final Global global); 301 302 protected boolean isFloatArray() { 303 return false; 304 } 305 306 protected static ArrayBufferView constructorImpl(final Object[] args, final Factory factory) { 307 final Object arg0 = args.length != 0 ? args[0] : 0; 308 final ArrayBufferView dst; 309 final int length; 310 if (arg0 instanceof NativeArrayBuffer) { 311 // Constructor(ArrayBuffer buffer, optional unsigned long byteOffset, optional unsigned long length) 312 final NativeArrayBuffer buffer = (NativeArrayBuffer) arg0; 313 final int byteOffset = args.length > 1 ? JSType.toInt32(args[1]) : 0; 314 if (args.length > 2) { 315 length = JSType.toInt32(args[2]); 316 } else { 317 if ((buffer.getByteLength() - byteOffset) % factory.bytesPerElement != 0) { 318 throw new RuntimeException("buffer.byteLength - byteOffset must be a multiple of the element size"); 319 } 320 length = (buffer.getByteLength() - byteOffset) / factory.bytesPerElement; 321 } 322 return factory.construct(buffer, byteOffset, length); 323 } else if (arg0 instanceof ArrayBufferView) { 324 // Constructor(TypedArray array) 325 length = ((ArrayBufferView)arg0).elementLength(); 326 dst = factory.construct(length); 327 } else if (arg0 instanceof NativeArray) { 328 // Constructor(type[] array) 329 length = lengthToInt(((NativeArray) arg0).getArray().length()); 330 dst = factory.construct(length); 331 } else { 332 // Constructor(unsigned long length) 333 length = lengthToInt(JSType.toInt64(arg0)); 334 return factory.construct(length); 335 } 336 337 copyElements(dst, length, (ScriptObject)arg0, 0); 338 return dst; 339 } 340 341 protected static Object setImpl(final Object self, final Object array, final Object offset0) { 342 final ArrayBufferView dest = ((ArrayBufferView)self); 343 final int length; 344 if (array instanceof ArrayBufferView) { 345 // void set(TypedArray array, optional unsigned long offset) 346 length = ((ArrayBufferView)array).elementLength(); 347 } else if (array instanceof NativeArray) { 348 // void set(type[] array, optional unsigned long offset) 349 length = (int) (((NativeArray) array).getArray().length() & 0x7fff_ffff); 350 } else { 351 throw new RuntimeException("argument is not of array type"); 352 } 353 354 final ScriptObject source = (ScriptObject) array; 355 final int offset = JSType.toInt32(offset0); // default=0 356 357 if (dest.elementLength() < length + offset || offset < 0) { 358 throw new RuntimeException("offset or array length out of bounds"); 359 } 360 361 copyElements(dest, length, source, offset); 362 363 return ScriptRuntime.UNDEFINED; 364 } 365 366 private static void copyElements(final ArrayBufferView dest, final int length, final ScriptObject source, final int offset) { 367 if (!dest.isFloatArray()) { 368 for (int i = 0, j = offset; i < length; i++, j++) { 369 dest.set(j, source.getInt(i), false); 370 } 371 } else { 372 for (int i = 0, j = offset; i < length; i++, j++) { 373 dest.set(j, source.getDouble(i), false); 374 } 375 } 376 } 377 378 private static int lengthToInt(final long length) { 379 if (length > Integer.MAX_VALUE || length < 0) { 380 throw rangeError("inappropriate.array.buffer.length", JSType.toString(length)); 381 } 382 return (int) (length & Integer.MAX_VALUE); 383 } 384 385 protected static Object subarrayImpl(final Object self, final Object begin0, final Object end0) { 386 final ArrayBufferView arrayView = ((ArrayBufferView)self); 387 final int elementLength = arrayView.elementLength(); 388 final int begin = NativeArrayBuffer.adjustIndex(JSType.toInt32(begin0), elementLength); 389 final int end = NativeArrayBuffer.adjustIndex(end0 != ScriptRuntime.UNDEFINED ? JSType.toInt32(end0) : elementLength, elementLength); 390 final ArrayDataImpl arrayData = (ArrayDataImpl)arrayView.getArray(); 391 return arrayView.factory().construct(arrayData.buffer, arrayData.byteIndex(begin), Math.max(end - begin, 0)); 392 } 393 }